; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs,inline)' -S | FileCheck %s ; This test runs the inliner and the function attribute deduction. It ensures ; that when the inliner mutates the call graph it correctly updates the CGSCC ; iteration so that we can compute refined function attributes. In this way it ; is leveraging function attribute computation to observe correct call graph ; updates. ; Boring unknown external function call. ; CHECK: declare void @unknown() declare void @unknown() ; Basic correctness check: this should get annotated as readnone. ; CHECK: Function Attrs: nounwind readnone ; CHECK-NEXT: declare void @readnone() declare void @readnone() readnone nounwind ; The 'test1_' prefixed functions are designed to trigger forming a new direct ; call in the inlined body of the function. After that, we form a new SCC and ; using that can deduce precise function attrs. ; This function should no longer exist. ; CHECK-NOT: @test1_f() define internal void @test1_f(void()* %p) { entry: call void %p() ret void } ; This function should have had 'readnone' deduced for its SCC. ; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test1_g() define void @test1_g() noinline { entry: call void @test1_f(void()* @test1_h) ret void } ; This function should have had 'readnone' deduced for its SCC. ; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test1_h() define void @test1_h() noinline { entry: call void @test1_g() call void @readnone() ret void } ; The 'test2_' prefixed functions are designed to trigger forming a new direct ; call due to RAUW-ing the returned value of a called function into the caller. ; This too should form a new SCC which can then be reasoned about to compute ; precise function attrs. ; This function should no longer exist. ; CHECK-NOT: @test2_f() define internal void()* @test2_f() { entry: ret void()* @test2_h } ; This function should have had 'readnone' deduced for its SCC. ; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test2_g() define void @test2_g() noinline { entry: %p = call void()* @test2_f() call void %p() ret void } ; This function should have had 'readnone' deduced for its SCC. ; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test2_h() define void @test2_h() noinline { entry: call void @test2_g() call void @readnone() ret void } ; The 'test3_' prefixed functions are designed to inline in a way that causes ; call sites to become trivially dead during the middle of inlining callsites of ; a single function to make sure that the inliner does not get confused by this ; pattern. ; CHECK-NOT: @test3_maybe_unknown( define internal void @test3_maybe_unknown(i1 %b) { entry: br i1 %b, label %then, label %exit then: call void @unknown() br label %exit exit: ret void } ; CHECK-NOT: @test3_f( define internal i1 @test3_f() { entry: ret i1 false } ; CHECK-NOT: @test3_g( define internal i1 @test3_g(i1 %b) { entry: br i1 %b, label %then1, label %if2 then1: call void @test3_maybe_unknown(i1 true) br label %if2 if2: %f = call i1 @test3_f() br i1 %f, label %then2, label %exit then2: call void @test3_maybe_unknown(i1 true) br label %exit exit: ret i1 false } ; FIXME: Currently the inliner doesn't successfully mark this as readnone ; because while it simplifies trivially dead CFGs when inlining callees it ; doesn't simplify the caller's trivially dead CFG and so we end with a dead ; block calling @unknown. ; CHECK-NOT: Function Attrs: readnone ; CHECK: define void @test3_h() define void @test3_h() { entry: %g = call i1 @test3_g(i1 false) br i1 %g, label %then, label %exit then: call void @test3_maybe_unknown(i1 true) br label %exit exit: call void @test3_maybe_unknown(i1 false) ret void } ; The 'test4_' prefixed functions are designed to trigger forming a new direct ; call in the inlined body of the function similar to 'test1_'. However, after ; that we continue to inline another edge of the graph forcing us to do a more ; interesting call graph update for the new call edge. Eventually, we still ; form a new SCC and should use that can deduce precise function attrs. ; This function should have had 'readnone' deduced for its SCC. ; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test4_f1() define void @test4_f1() noinline { entry: call void @test4_h() ret void } ; CHECK-NOT: @test4_f2 define internal void @test4_f2() { entry: call void @test4_f1() ret void } ; CHECK-NOT: @test4_g define internal void @test4_g(void()* %p) { entry: call void %p() ret void } ; This function should have had 'readnone' deduced for its SCC. ; CHECK: Function Attrs: nofree noinline nosync nounwind readnone ; CHECK-NEXT: define void @test4_h() define void @test4_h() noinline { entry: call void @test4_g(void()* @test4_f2) ret void }