| ; 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 |
| } |