You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			436 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			LLVM
		
	
	
	
	
	
			
		
		
	
	
			436 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			LLVM
		
	
	
	
	
	
| ; RUN: opt -S -objc-arc < %s | FileCheck %s
 | |
| ; rdar://9503416
 | |
| 
 | |
| ; Detect loop boundaries and don't move retains and releases
 | |
| ; across them.
 | |
| 
 | |
| declare void @use_pointer(i8*)
 | |
| declare i8* @objc_retain(i8*)
 | |
| declare void @objc_release(i8*)
 | |
| declare void @callee()
 | |
| declare void @block_callee(void ()*)
 | |
| 
 | |
| ; CHECK-LABEL: define void @test0(
 | |
| ; CHECK:   call i8* @objc_retain(
 | |
| ; CHECK: for.body:
 | |
| ; CHECK-NOT: @objc
 | |
| ; CHECK: for.end:
 | |
| ; CHECK:   call void @objc_release(
 | |
| ; CHECK: }
 | |
| define void @test0(i8* %digits) {
 | |
| entry:
 | |
|   %tmp1 = call i8* @objc_retain(i8* %digits) nounwind
 | |
|   call void @use_pointer(i8* %digits)
 | |
|   br label %for.body
 | |
| 
 | |
| for.body:                                         ; preds = %for.body, %entry
 | |
|   %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ]
 | |
|   call void @use_pointer(i8* %digits)
 | |
|   %inc = add i64 %upcDigitIndex.01, 1
 | |
|   %cmp = icmp ult i64 %inc, 12
 | |
|   br i1 %cmp, label %for.body, label %for.end
 | |
| 
 | |
| for.end:                                          ; preds = %for.body
 | |
|   call void @objc_release(i8* %digits) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ; CHECK-LABEL: define void @test1(
 | |
| ; CHECK:   call i8* @objc_retain(
 | |
| ; CHECK: for.body:
 | |
| ; CHECK-NOT: @objc
 | |
| ; CHECK: for.end:
 | |
| ; CHECK:   void @objc_release(
 | |
| ; CHECK: }
 | |
| define void @test1(i8* %digits) {
 | |
| entry:
 | |
|   %tmp1 = call i8* @objc_retain(i8* %digits) nounwind
 | |
|   br label %for.body
 | |
| 
 | |
| for.body:                                         ; preds = %for.body, %entry
 | |
|   %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ]
 | |
|   call void @use_pointer(i8* %digits)
 | |
|   call void @use_pointer(i8* %digits)
 | |
|   %inc = add i64 %upcDigitIndex.01, 1
 | |
|   %cmp = icmp ult i64 %inc, 12
 | |
|   br i1 %cmp, label %for.body, label %for.end
 | |
| 
 | |
| for.end:                                          ; preds = %for.body
 | |
|   call void @objc_release(i8* %digits) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ; CHECK-LABEL: define void @test2(
 | |
| ; CHECK:   call i8* @objc_retain(
 | |
| ; CHECK: for.body:
 | |
| ; CHECK-NOT: @objc
 | |
| ; CHECK: for.end:
 | |
| ; CHECK:   void @objc_release(
 | |
| ; CHECK: }
 | |
| define void @test2(i8* %digits) {
 | |
| entry:
 | |
|   %tmp1 = call i8* @objc_retain(i8* %digits) nounwind
 | |
|   br label %for.body
 | |
| 
 | |
| for.body:                                         ; preds = %for.body, %entry
 | |
|   %upcDigitIndex.01 = phi i64 [ 2, %entry ], [ %inc, %for.body ]
 | |
|   call void @use_pointer(i8* %digits)
 | |
|   %inc = add i64 %upcDigitIndex.01, 1
 | |
|   %cmp = icmp ult i64 %inc, 12
 | |
|   br i1 %cmp, label %for.body, label %for.end
 | |
| 
 | |
| for.end:                                          ; preds = %for.body
 | |
|   call void @use_pointer(i8* %digits)
 | |
|   call void @objc_release(i8* %digits) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ; Delete nested retain+release pairs around loops.
 | |
| 
 | |
| ;      CHECK: define void @test3(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   tail call i8* @objc_retain(i8* %a) [[NUW:#[0-9]+]]
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   call void @objc_release(i8* %a)
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test3(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   call void @callee()
 | |
|   store i8 0, i8* %a
 | |
|   br i1 undef, label %loop, label %exit
 | |
| 
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ;      CHECK: define void @test4(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   call void @objc_release(i8* %a)
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test4(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br label %more
 | |
| 
 | |
| more:
 | |
|   call void @callee()
 | |
|   call void @callee()
 | |
|   store i8 0, i8* %a
 | |
|   br i1 undef, label %loop, label %exit
 | |
| 
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ;      CHECK: define void @test5(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK-NEXT:   call void @callee()
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   call void @use_pointer(i8* %a)
 | |
| ; CHECK-NEXT:   call void @objc_release(i8* %a)
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test5(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   call void @callee()
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br i1 undef, label %true, label %more
 | |
| 
 | |
| true:
 | |
|   br label %more
 | |
| 
 | |
| more:
 | |
|   br i1 undef, label %exit, label %loop
 | |
| 
 | |
| exit:
 | |
|   call void @use_pointer(i8* %a)
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ;      CHECK: define void @test6(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   call void @use_pointer(i8* %a)
 | |
| ; CHECK-NEXT:   call void @objc_release(i8* %a)
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test6(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br i1 undef, label %true, label %more
 | |
| 
 | |
| true:
 | |
|   call void @callee()
 | |
|   br label %more
 | |
| 
 | |
| more:
 | |
|   br i1 undef, label %exit, label %loop
 | |
| 
 | |
| exit:
 | |
|   call void @use_pointer(i8* %a)
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ;      CHECK: define void @test7(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK-NEXT:   call void @callee()
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   call void @objc_release(i8* %a)
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test7(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   call void @callee()
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br i1 undef, label %true, label %more
 | |
| 
 | |
| true:
 | |
|   call void @use_pointer(i8* %a)
 | |
|   br label %more
 | |
| 
 | |
| more:
 | |
|   br i1 undef, label %exit, label %loop
 | |
| 
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ;      CHECK: define void @test8(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   call void @objc_release(i8* %a)
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test8(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br i1 undef, label %true, label %more
 | |
| 
 | |
| true:
 | |
|   call void @callee()
 | |
|   call void @use_pointer(i8* %a)
 | |
|   br label %more
 | |
| 
 | |
| more:
 | |
|   br i1 undef, label %exit, label %loop
 | |
| 
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ;      CHECK: define void @test9(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test9(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br i1 undef, label %true, label %more
 | |
| 
 | |
| true:
 | |
|   call void @use_pointer(i8* %a)
 | |
|   br label %more
 | |
| 
 | |
| more:
 | |
|   br i1 undef, label %exit, label %loop
 | |
| 
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ;      CHECK: define void @test10(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test10(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br i1 undef, label %true, label %more
 | |
| 
 | |
| true:
 | |
|   call void @callee()
 | |
|   br label %more
 | |
| 
 | |
| more:
 | |
|   br i1 undef, label %exit, label %loop
 | |
| 
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ;      CHECK: define void @test11(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test11(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br i1 undef, label %true, label %more
 | |
| 
 | |
| true:
 | |
|   br label %more
 | |
| 
 | |
| more:
 | |
|   br i1 undef, label %exit, label %loop
 | |
| 
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ; Don't delete anything if they're not balanced.
 | |
| 
 | |
| ;      CHECK: define void @test12(i8* %a) #0 {
 | |
| ; CHECK-NEXT: entry:
 | |
| ; CHECK-NEXT:   %outer = tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK-NEXT:   %inner = tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK-NEXT:   br label %loop
 | |
| ;  CHECK-NOT:   @objc_
 | |
| ;      CHECK: exit:
 | |
| ; CHECK-NEXT: call void @objc_release(i8* %a) [[NUW]]
 | |
| ; CHECK-NEXT: call void @objc_release(i8* %a) [[NUW]], !clang.imprecise_release !0
 | |
| ; CHECK-NEXT:   ret void
 | |
| ; CHECK-NEXT: }
 | |
| define void @test12(i8* %a) nounwind {
 | |
| entry:
 | |
|   %outer = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   %inner = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   br i1 undef, label %true, label %more
 | |
| 
 | |
| true:
 | |
|   ret void
 | |
| 
 | |
| more:
 | |
|   br i1 undef, label %exit, label %loop
 | |
| 
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ; Do not improperly pair retains in a for loop with releases outside of a for
 | |
| ; loop when the proper pairing is disguised by a separate provenance represented
 | |
| ; by an alloca.
 | |
| ; rdar://12969722
 | |
| 
 | |
| ; CHECK: define void @test13(i8* %a) [[NUW]] {
 | |
| ; CHECK: entry:
 | |
| ; CHECK:   tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK: loop:
 | |
| ; CHECK:   tail call i8* @objc_retain(i8* %a) [[NUW]]
 | |
| ; CHECK:   call void @block_callee
 | |
| ; CHECK:   call void @objc_release(i8* %reloaded_a) [[NUW]]
 | |
| ; CHECK: exit:
 | |
| ; CHECK:   call void @objc_release(i8* %a) [[NUW]]
 | |
| ; CHECK: }
 | |
| define void @test13(i8* %a) nounwind {
 | |
| entry:
 | |
|   %block = alloca i8*
 | |
|   %a1 = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   br label %loop
 | |
| 
 | |
| loop:
 | |
|   %a2 = tail call i8* @objc_retain(i8* %a) nounwind
 | |
|   store i8* %a, i8** %block, align 8
 | |
|   %casted_block = bitcast i8** %block to void ()*
 | |
|   call void @block_callee(void ()* %casted_block)
 | |
|   %reloaded_a = load i8*, i8** %block, align 8
 | |
|   call void @objc_release(i8* %reloaded_a) nounwind, !clang.imprecise_release !0
 | |
|   br i1 undef, label %loop, label %exit
 | |
|   
 | |
| exit:
 | |
|   call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
 | |
|   ret void
 | |
| }
 | |
| 
 | |
| ; CHECK: attributes [[NUW]] = { nounwind }
 | |
| 
 | |
| !0 = !{}
 |