| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --scrub-attributes --version 6 |
| ; RUN: opt -passes=always-inline -S < %s | FileCheck %s --check-prefixes=CHECK,ALWAYS |
| ; RUN: opt -passes=inline -S < %s | FileCheck %s --check-prefixes=CHECK,INLINE |
| ; RUN: opt -passes='cgscc(inline<only-mandatory>)' -S < %s | FileCheck %s --check-prefixes=CHECK,MANDATORY |
| ; RUN: opt -passes=always-inline -pass-remarks-missed=inline -S %s -o /dev/null 2>&1 | FileCheck --check-prefix=REMARK %s |
| |
| ; Test that the flatten attribute recursively inlines all calls. |
| |
| ; Multiple levels are inlined. |
| define internal i32 @leaf() { |
| ; ALWAYS-LABEL: define internal i32 @leaf() { |
| ; ALWAYS-NEXT: ret i32 42 |
| ; |
| ret i32 42 |
| } |
| |
| define internal i32 @middle() { |
| ; ALWAYS-LABEL: define internal i32 @middle() { |
| ; ALWAYS-NEXT: [[R:%.*]] = call i32 @leaf() |
| ; ALWAYS-NEXT: ret i32 [[R]] |
| ; |
| %r = call i32 @leaf() |
| ret i32 %r |
| } |
| |
| define i32 @test_multilevel() flatten { |
| ; CHECK-LABEL: define i32 @test_multilevel( |
| ; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { |
| ; CHECK-NEXT: ret i32 42 |
| ; |
| %r = call i32 @middle() |
| ret i32 %r |
| } |
| |
| ; Functions with invoke are inlined. |
| declare i32 @__gxx_personality_v0(...) |
| declare void @may_throw() |
| |
| define internal i32 @callee_with_invoke() personality ptr @__gxx_personality_v0 { |
| ; ALWAYS-LABEL: define internal i32 @callee_with_invoke() personality ptr @__gxx_personality_v0 { |
| ; ALWAYS-NEXT: [[ENTRY:.*:]] |
| ; ALWAYS-NEXT: invoke void @may_throw() |
| ; ALWAYS-NEXT: to label %[[CONT:.*]] unwind label %[[LPAD:.*]] |
| ; ALWAYS: [[CONT]]: |
| ; ALWAYS-NEXT: ret i32 100 |
| ; ALWAYS: [[LPAD]]: |
| ; ALWAYS-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } |
| ; ALWAYS-NEXT: cleanup |
| ; ALWAYS-NEXT: resume { ptr, i32 } [[LP]] |
| ; |
| entry: |
| invoke void @may_throw() to label %cont unwind label %lpad |
| cont: |
| ret i32 100 |
| lpad: |
| %lp = landingpad { ptr, i32 } cleanup |
| resume { ptr, i32 } %lp |
| } |
| |
| define i32 @test_invoke() flatten personality ptr @__gxx_personality_v0 { |
| ; CHECK-LABEL: define i32 @test_invoke( |
| ; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: invoke void @may_throw() |
| ; CHECK-NEXT: to label %[[CALLEE_WITH_INVOKE_EXIT:.*]] unwind label %[[LPAD_I:.*]] |
| ; CHECK: [[LPAD_I]]: |
| ; CHECK-NEXT: [[LP_I:%.*]] = landingpad { ptr, i32 } |
| ; CHECK-NEXT: cleanup |
| ; CHECK-NEXT: resume { ptr, i32 } [[LP_I]] |
| ; CHECK: [[CALLEE_WITH_INVOKE_EXIT]]: |
| ; CHECK-NEXT: ret i32 100 |
| ; |
| entry: |
| %r = call i32 @callee_with_invoke() |
| ret i32 %r |
| } |
| |
| ; Declaration without definition is not inlined. |
| declare i32 @external_func() |
| |
| define i32 @test_declaration() flatten { |
| ; CHECK-LABEL: define i32 @test_declaration( |
| ; CHECK-SAME: ) #[[ATTR0]] { |
| ; CHECK-NEXT: [[R:%.*]] = call i32 @external_func() |
| ; CHECK-NEXT: ret i32 [[R]] |
| ; |
| %r = call i32 @external_func() |
| ret i32 %r |
| } |
| |
| ; Inlined callee that calls a declaration - the declaration should remain after flattening. |
| define internal i32 @calls_external() { |
| ; ALWAYS-LABEL: define internal i32 @calls_external() { |
| ; ALWAYS-NEXT: [[R:%.*]] = call i32 @external_func() |
| ; ALWAYS-NEXT: ret i32 [[R]] |
| ; |
| %r = call i32 @external_func() |
| ret i32 %r |
| } |
| |
| define i32 @test_inline_then_declaration() flatten { |
| ; CHECK-LABEL: define i32 @test_inline_then_declaration( |
| ; CHECK-SAME: ) #[[ATTR0]] { |
| ; CHECK-NEXT: [[R_I:%.*]] = call i32 @external_func() |
| ; CHECK-NEXT: ret i32 [[R_I]] |
| ; |
| %r = call i32 @calls_external() |
| ret i32 %r |
| } |
| |
| ; Indirect calls are not inlined. |
| define internal i32 @target_func() { |
| ; CHECK-LABEL: define internal i32 @target_func() { |
| ; CHECK-NEXT: ret i32 99 |
| ; |
| ret i32 99 |
| } |
| |
| define i32 @test_indirect(ptr %func_ptr) flatten { |
| ; CHECK-LABEL: define i32 @test_indirect( |
| ; CHECK-SAME: ptr [[FUNC_PTR:%.*]]) #[[ATTR0]] { |
| ; CHECK-NEXT: [[R:%.*]] = call i32 [[FUNC_PTR]]() |
| ; CHECK-NEXT: ret i32 [[R]] |
| ; |
| %r = call i32 %func_ptr() |
| ret i32 %r |
| } |
| |
| ; Direct recursion back to flattened function. |
| ; The callee calls the flattened function - should not cause infinite inlining. |
| define internal i32 @calls_flattened_func() { |
| ; ALWAYS-LABEL: define internal i32 @calls_flattened_func() { |
| ; ALWAYS-NEXT: [[R:%.*]] = call i32 @test_direct_recursion() |
| ; ALWAYS-NEXT: ret i32 [[R]] |
| ; |
| %r = call i32 @test_direct_recursion() |
| ret i32 %r |
| } |
| |
| define i32 @test_direct_recursion() flatten { |
| ; The call to calls_flattened_func should be inlined, but the recursive call back |
| ; to test_direct_recursion should remain. |
| ; CHECK-LABEL: define i32 @test_direct_recursion( |
| ; CHECK-SAME: ) #[[ATTR0]] { |
| ; CHECK-NEXT: [[R_I:%.*]] = call i32 @test_direct_recursion() |
| ; CHECK-NEXT: ret i32 [[R_I]] |
| ; |
| %r = call i32 @calls_flattened_func() |
| ret i32 %r |
| } |
| |
| ; Mutual recursion (A calls B, B calls A). |
| ; Should inline once but not infinitely. |
| define internal i32 @mutual_a() { |
| ; ALWAYS-LABEL: define internal i32 @mutual_a() { |
| ; ALWAYS-NEXT: [[R:%.*]] = call i32 @mutual_b() |
| ; ALWAYS-NEXT: ret i32 [[R]] |
| ; |
| ; INLINE-LABEL: define internal i32 @mutual_a() { |
| ; INLINE-NEXT: [[R_I:%.*]] = call i32 @mutual_a() |
| ; INLINE-NEXT: ret i32 [[R_I]] |
| ; |
| ; MANDATORY-LABEL: define internal i32 @mutual_a() { |
| ; MANDATORY-NEXT: [[R:%.*]] = call i32 @mutual_b() |
| ; MANDATORY-NEXT: ret i32 [[R]] |
| ; |
| %r = call i32 @mutual_b() |
| ret i32 %r |
| } |
| |
| define internal i32 @mutual_b() { |
| ; ALWAYS-LABEL: define internal i32 @mutual_b() { |
| ; ALWAYS-NEXT: [[R:%.*]] = call i32 @mutual_a() |
| ; ALWAYS-NEXT: ret i32 [[R]] |
| ; |
| ; MANDATORY-LABEL: define internal i32 @mutual_b() { |
| ; MANDATORY-NEXT: [[R:%.*]] = call i32 @mutual_a() |
| ; MANDATORY-NEXT: ret i32 [[R]] |
| ; |
| %r = call i32 @mutual_a() |
| ret i32 %r |
| } |
| |
| define i32 @test_mutual_recursion() flatten { |
| ; After inlining mutual_a, we get call to mutual_b. |
| ; After inlining mutual_b, we get call to mutual_a which should remain (skipped due to inline history). |
| ; ALWAYS-LABEL: define i32 @test_mutual_recursion( |
| ; ALWAYS-SAME: ) #[[ATTR0]] { |
| ; ALWAYS-NEXT: [[R_I1:%.*]] = call i32 @mutual_a() |
| ; ALWAYS-NEXT: ret i32 [[R_I1]] |
| ; |
| ; INLINE-LABEL: define i32 @test_mutual_recursion( |
| ; INLINE-SAME: ) #[[ATTR0]] { |
| ; INLINE-NEXT: [[R:%.*]] = call i32 @mutual_a() |
| ; INLINE-NEXT: ret i32 [[R]] |
| ; |
| ; MANDATORY-LABEL: define i32 @test_mutual_recursion( |
| ; MANDATORY-SAME: ) #[[ATTR0]] { |
| ; MANDATORY-NEXT: [[R_I1:%.*]] = call i32 @mutual_a() |
| ; MANDATORY-NEXT: ret i32 [[R_I1]] |
| ; |
| %r = call i32 @mutual_a() |
| ret i32 %r |
| } |
| |
| ; Check that optimization remark is emitted for recursive calls during flattening. |
| ; REMARK: remark: {{.*}} 'test_direct_recursion' is not inlined into 'test_direct_recursion': recursive call during flattening |