; 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)' -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