int NewHistoryID = InlineHistory.size();
InlineHistory.push_back(std::make_pair(Callee, InlineHistoryID));
- for (Value *Ptr : InlineInfo.InlinedCalls)
+#ifndef NDEBUG
+ // Make sure no dupplicates in the inline candidates. This could
+ // happen when a callsite is simpilfied to reusing the return value
+ // of another callsite during function cloning, thus the other
+ // callsite will be reconsidered here.
+ DenseSet<CallSite> DbgCallSites;
+ for (auto &II : CallSites)
+ DbgCallSites.insert(II.first);
+#endif
+
+ for (Value *Ptr : InlineInfo.InlinedCalls) {
+#ifndef NDEBUG
+ assert(DbgCallSites.count(CallSite(Ptr)) == 0);
+#endif
CallSites.push_back(std::make_pair(CallSite(Ptr), NewHistoryID));
+ }
}
}
--- /dev/null
+; Check the optimizer doesn't crash at inlining the function top and all of its callees are inlined.
+; RUN: opt < %s -O3 -S | FileCheck %s
+
+define dso_local void (...)* @second(i8** %p) {
+entry:
+ %p.addr = alloca i8**, align 8
+ store i8** %p, i8*** %p.addr, align 8
+ %tmp = load i8**, i8*** %p.addr, align 8
+ %tmp1 = load i8*, i8** %tmp, align 8
+ %tmp2 = bitcast i8* %tmp1 to void (...)*
+ ret void (...)* %tmp2
+}
+
+define dso_local void @top() {
+entry:
+ ; CHECK: {{.*}} = {{.*}} call {{.*}} @ext
+ ; CHECK-NOT: {{.*}} = {{.*}} call {{.*}} @third
+ ; CHECK-NOT: {{.*}} = {{.*}} call {{.*}} @second
+ ; CHECK-NOT: {{.*}} = {{.*}} call {{.*}} @wrapper
+ %q = alloca i8*, align 8
+ store i8* bitcast (void ()* @third to i8*), i8** %q, align 8
+ %tmp = call void (...)* @second(i8** %q)
+ ; The call to 'wrapper' here is to ensure that its function attributes
+ ; i.e., returning its parameter and having no side effect, will be decuded
+ ; before the next round of inlining happens to 'top' to expose the bug.
+ %call = call void (...)* @wrapper(void (...)* %tmp)
+ ; The indirect call here is to confuse the alias analyzer so that
+ ; an incomplete graph will be built during the first round of inlining.
+ ; This allows the current function to be processed before the actual
+ ; callee, i.e., the function 'run', is processed. Once it's simplified to
+ ; a direct call, it also enables an additional round of inlining with all
+ ; function attributes deduced.
+ call void (...) %call()
+ ret void
+}
+
+define dso_local void (...)* @gen() {
+entry:
+ %call = call void (...)* (...) @ext()
+ ret void (...)* %call
+}
+
+declare dso_local void (...)* @ext(...)
+
+define dso_local void (...)* @wrapper(void (...)* %fn) {
+entry:
+ ret void (...)* %fn
+}
+
+define dso_local void @run(void (...)* %fn) {
+entry:
+ %fn.addr = alloca void (...)*, align 8
+ %f = alloca void (...)*, align 8
+ store void (...)* %fn, void (...)** %fn.addr, align 8
+ %tmp = load void (...)*, void (...)** %fn.addr, align 8
+ %call = call void (...)* @wrapper(void (...)* %tmp)
+ store void (...)* %call, void (...)** %f, align 8
+ %tmp1 = load void (...)*, void (...)** %f, align 8
+ call void (...) %tmp1()
+ ret void
+}
+
+define dso_local void @third() {
+entry:
+ %f = alloca void (...)*, align 8
+ %call = call void (...)* @gen()
+ store void (...)* %call, void (...)** %f, align 8
+ %tmp = load void (...)*, void (...)** %f, align 8
+ call void @run(void (...)* %tmp)
+ ret void
+}
\ No newline at end of file