111 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			LLVM
		
	
	
	
			
		
		
	
	
			111 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			LLVM
		
	
	
	
| ; FIXME: convert CHECK-INDIRECT into CHECK (and remove -check-prefixes) as soon
 | |
| ; FIXME: as new-pass-manager's handling of indirect_non_convergent_call is fixed
 | |
| ;
 | |
| ; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-INDIRECT
 | |
| ; RUN: opt -passes=function-attrs -S < %s | FileCheck %s
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-NOT: convergent
 | |
| ; CHECK-NEXT: define i32 @nonleaf()
 | |
| define i32 @nonleaf() convergent {
 | |
|   %a = call i32 @leaf()
 | |
|   ret i32 %a
 | |
| }
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-NOT: convergent
 | |
| ; CHECK-NEXT: define i32 @leaf()
 | |
| define i32 @leaf() convergent {
 | |
|   ret i32 0
 | |
| }
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-SAME: convergent
 | |
| ; CHECK-NEXT: declare i32 @k()
 | |
| declare i32 @k() convergent
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-SAME: convergent
 | |
| ; CHECK-NEXT: define i32 @extern()
 | |
| define i32 @extern() convergent {
 | |
|   %a = call i32 @k() convergent
 | |
|   ret i32 %a
 | |
| }
 | |
| 
 | |
| ; Convergent should not be removed on the function here.  Although the call is
 | |
| ; not explicitly convergent, it picks up the convergent attr from the callee.
 | |
| ;
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-SAME: convergent
 | |
| ; CHECK-NEXT: define i32 @extern_non_convergent_call()
 | |
| define i32 @extern_non_convergent_call() convergent {
 | |
|   %a = call i32 @k()
 | |
|   ret i32 %a
 | |
| }
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-SAME: convergent
 | |
| ; CHECK-NEXT: define i32 @indirect_convergent_call(
 | |
| define i32 @indirect_convergent_call(i32 ()* %f) convergent {
 | |
|    %a = call i32 %f() convergent
 | |
|    ret i32 %a
 | |
| }
 | |
| ; Give indirect_non_convergent_call the norecurse attribute so we get a
 | |
| ; "Function Attrs" comment in the output.
 | |
| ;
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-INDIRECT-NOT: convergent
 | |
| ; CHECK-INDIRECT-NEXT: define i32 @indirect_non_convergent_call(
 | |
| define i32 @indirect_non_convergent_call(i32 ()* %f) convergent norecurse {
 | |
|    %a = call i32 %f()
 | |
|    ret i32 %a
 | |
| }
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-SAME: convergent
 | |
| ; CHECK-NEXT: declare void @llvm.nvvm.barrier0()
 | |
| declare void @llvm.nvvm.barrier0() convergent
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-SAME: convergent
 | |
| ; CHECK-NEXT: define i32 @intrinsic()
 | |
| define i32 @intrinsic() convergent {
 | |
|   ; Implicitly convergent, because the intrinsic is convergent.
 | |
|   call void @llvm.nvvm.barrier0()
 | |
|   ret i32 0
 | |
| }
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-NOT: convergent
 | |
| ; CHECK-NEXT: define i32 @recursive1()
 | |
| define i32 @recursive1() convergent {
 | |
|   %a = call i32 @recursive2() convergent
 | |
|   ret i32 %a
 | |
| }
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-NOT: convergent
 | |
| ; CHECK-NEXT: define i32 @recursive2()
 | |
| define i32 @recursive2() convergent {
 | |
|   %a = call i32 @recursive1() convergent
 | |
|   ret i32 %a
 | |
| }
 | |
| 
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-SAME: convergent
 | |
| ; CHECK-NEXT: define i32 @noopt()
 | |
| define i32 @noopt() convergent optnone noinline {
 | |
|   %a = call i32 @noopt_friend() convergent
 | |
|   ret i32 0
 | |
| }
 | |
| 
 | |
| ; A function which is mutually-recursive with a convergent, optnone function
 | |
| ; shouldn't have its convergent attribute stripped.
 | |
| ; CHECK: Function Attrs
 | |
| ; CHECK-SAME: convergent
 | |
| ; CHECK-NEXT: define i32 @noopt_friend()
 | |
| define i32 @noopt_friend() convergent {
 | |
|   %a = call i32 @noopt()
 | |
|   ret i32 0
 | |
| }
 |