mirror of
				https://github.com/c64scene-ar/llvm-6502.git
				synced 2025-11-03 14:21:30 +00:00 
			
		
		
		
	Now that `Metadata` is typeless, reflect that in the assembly.  These
are the matching assembly changes for the metadata/value split in
r223802.
  - Only use the `metadata` type when referencing metadata from a call
    intrinsic -- i.e., only when it's used as a `Value`.
  - Stop pretending that `ValueAsMetadata` is wrapped in an `MDNode`
    when referencing it from call intrinsics.
So, assembly like this:
    define @foo(i32 %v) {
      call void @llvm.foo(metadata !{i32 %v}, metadata !0)
      call void @llvm.foo(metadata !{i32 7}, metadata !0)
      call void @llvm.foo(metadata !1, metadata !0)
      call void @llvm.foo(metadata !3, metadata !0)
      call void @llvm.foo(metadata !{metadata !3}, metadata !0)
      ret void, !bar !2
    }
    !0 = metadata !{metadata !2}
    !1 = metadata !{i32* @global}
    !2 = metadata !{metadata !3}
    !3 = metadata !{}
turns into this:
    define @foo(i32 %v) {
      call void @llvm.foo(metadata i32 %v, metadata !0)
      call void @llvm.foo(metadata i32 7, metadata !0)
      call void @llvm.foo(metadata i32* @global, metadata !0)
      call void @llvm.foo(metadata !3, metadata !0)
      call void @llvm.foo(metadata !{!3}, metadata !0)
      ret void, !bar !2
    }
    !0 = !{!2}
    !1 = !{i32* @global}
    !2 = !{!3}
    !3 = !{}
I wrote an upgrade script that handled almost all of the tests in llvm
and many of the tests in cfe (even handling many `CHECK` lines).  I've
attached it (or will attach it in a moment if you're speedy) to PR21532
to help everyone update their out-of-tree testcases.
This is part of PR21532.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@224257 91177308-0d34-0410-b5e6-96231b3b80d8
		
	
		
			
				
	
	
		
			501 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			LLVM
		
	
	
	
	
	
			
		
		
	
	
			501 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			LLVM
		
	
	
	
	
	
; RUN: opt -objc-arc -S < %s | FileCheck %s
 | 
						|
 | 
						|
declare i8* @objc_retain(i8*)
 | 
						|
declare i8* @objc_retainAutoreleasedReturnValue(i8*)
 | 
						|
declare void @objc_release(i8*)
 | 
						|
declare i8* @objc_autorelease(i8*)
 | 
						|
declare i8* @objc_autoreleaseReturnValue(i8*)
 | 
						|
declare void @objc_autoreleasePoolPop(i8*)
 | 
						|
declare i8* @objc_autoreleasePoolPush()
 | 
						|
declare i8* @objc_retainBlock(i8*)
 | 
						|
 | 
						|
declare i8* @objc_retainedObject(i8*)
 | 
						|
declare i8* @objc_unretainedObject(i8*)
 | 
						|
declare i8* @objc_unretainedPointer(i8*)
 | 
						|
 | 
						|
declare void @use_pointer(i8*)
 | 
						|
declare void @callee()
 | 
						|
declare void @callee_fnptr(void ()*)
 | 
						|
declare void @invokee()
 | 
						|
declare i8* @returner()
 | 
						|
declare i8* @returner1()
 | 
						|
declare i8* @returner2()
 | 
						|
declare void @bar(i32 ()*)
 | 
						|
declare void @use_alloca(i8**)
 | 
						|
 | 
						|
declare void @llvm.dbg.value(metadata, i64, metadata, metadata)
 | 
						|
 | 
						|
declare i8* @objc_msgSend(i8*, i8*, ...)
 | 
						|
 | 
						|
 | 
						|
; In the presence of allocas, unconditionally remove retain/release pairs only
 | 
						|
; if they are known safe in both directions. This prevents matching up an inner
 | 
						|
; retain with the boundary guarding release in the following situation:
 | 
						|
; 
 | 
						|
; %A = alloca
 | 
						|
; retain(%x)
 | 
						|
; retain(%x) <--- Inner Retain
 | 
						|
; store %x, %A
 | 
						|
; %y = load %A
 | 
						|
; ... DO STUFF ...
 | 
						|
; release(%y)
 | 
						|
; release(%x) <--- Guarding Release
 | 
						|
;
 | 
						|
; rdar://13750319
 | 
						|
 | 
						|
; CHECK: define void @test1a(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test1a(i8* %x) {
 | 
						|
entry:
 | 
						|
  %A = alloca i8*
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  store i8* %x, i8** %A, align 8
 | 
						|
  %y = load i8** %A
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; CHECK: define void @test1b(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test1b(i8* %x) {
 | 
						|
entry:
 | 
						|
  %A = alloca i8*
 | 
						|
  %gep = getelementptr i8** %A, i32 0
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  store i8* %x, i8** %gep, align 8
 | 
						|
  %y = load i8** %A
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
; CHECK: define void @test1c(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test1c(i8* %x) {
 | 
						|
entry:
 | 
						|
  %A = alloca i8*, i32 3
 | 
						|
  %gep = getelementptr i8** %A, i32 2
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  store i8* %x, i8** %gep, align 8
 | 
						|
  %y = load i8** %gep
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
; CHECK: define void @test1d(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test1d(i8* %x) {
 | 
						|
entry:
 | 
						|
  br i1 undef, label %use_allocaA, label %use_allocaB
 | 
						|
 | 
						|
use_allocaA:
 | 
						|
  %allocaA = alloca i8*
 | 
						|
  br label %exit
 | 
						|
 | 
						|
use_allocaB:
 | 
						|
  %allocaB = alloca i8*
 | 
						|
  br label %exit
 | 
						|
 | 
						|
exit:
 | 
						|
  %A = phi i8** [ %allocaA, %use_allocaA ], [ %allocaB, %use_allocaB ]
 | 
						|
  %gep = getelementptr i8** %A, i32 0
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  store i8* %x, i8** %gep, align 8
 | 
						|
  %y = load i8** %gep
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; CHECK: define void @test1e(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test1e(i8* %x) {
 | 
						|
entry:
 | 
						|
  br i1 undef, label %use_allocaA, label %use_allocaB
 | 
						|
 | 
						|
use_allocaA:
 | 
						|
  %allocaA = alloca i8*, i32 4
 | 
						|
  br label %exit
 | 
						|
 | 
						|
use_allocaB:
 | 
						|
  %allocaB = alloca i8*, i32 4
 | 
						|
  br label %exit
 | 
						|
 | 
						|
exit:
 | 
						|
  %A = phi i8** [ %allocaA, %use_allocaA ], [ %allocaB, %use_allocaB ]
 | 
						|
  %gep = getelementptr i8** %A, i32 2
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  store i8* %x, i8** %gep, align 8
 | 
						|
  %y = load i8** %gep
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; CHECK: define void @test1f(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test1f(i8* %x) {
 | 
						|
entry:
 | 
						|
  %allocaOne = alloca i8*
 | 
						|
  %allocaTwo = alloca i8*
 | 
						|
  %A = select i1 undef, i8** %allocaOne, i8** %allocaTwo
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  store i8* %x, i8** %A, align 8
 | 
						|
  %y = load i8** %A
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; Make sure that if a store is in a different basic block we handle known safe
 | 
						|
; conservatively.
 | 
						|
 | 
						|
 | 
						|
; CHECK: define void @test2a(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test2a(i8* %x) {
 | 
						|
entry:
 | 
						|
  %A = alloca i8*
 | 
						|
  store i8* %x, i8** %A, align 8
 | 
						|
  %y = load i8** %A
 | 
						|
  br label %bb1
 | 
						|
 | 
						|
bb1:
 | 
						|
  br label %bb2
 | 
						|
 | 
						|
bb2:
 | 
						|
  br label %bb3
 | 
						|
 | 
						|
bb3:
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; CHECK: define void @test2b(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test2b(i8* %x) {
 | 
						|
entry:
 | 
						|
  %A = alloca i8*
 | 
						|
  %gep1 = getelementptr i8** %A, i32 0
 | 
						|
  store i8* %x, i8** %gep1, align 8
 | 
						|
  %gep2 = getelementptr i8** %A, i32 0
 | 
						|
  %y = load i8** %gep2
 | 
						|
  br label %bb1
 | 
						|
 | 
						|
bb1:
 | 
						|
  br label %bb2
 | 
						|
 | 
						|
bb2:
 | 
						|
  br label %bb3
 | 
						|
 | 
						|
bb3:
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; CHECK: define void @test2c(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test2c(i8* %x) {
 | 
						|
entry:
 | 
						|
  %A = alloca i8*, i32 3
 | 
						|
  %gep1 = getelementptr i8** %A, i32 2
 | 
						|
  store i8* %x, i8** %gep1, align 8
 | 
						|
  %gep2 = getelementptr i8** %A, i32 2
 | 
						|
  %y = load i8** %gep2
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  br label %bb1
 | 
						|
 | 
						|
bb1:
 | 
						|
  br label %bb2
 | 
						|
 | 
						|
bb2:
 | 
						|
  br label %bb3
 | 
						|
 | 
						|
bb3:
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; CHECK: define void @test2d(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_retain(i8* %x)
 | 
						|
; CHECK: @objc_release(i8* %y)
 | 
						|
; CHECK: @objc_release(i8* %x)
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test2d(i8* %x) {
 | 
						|
entry:
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  br label %bb1
 | 
						|
 | 
						|
bb1:
 | 
						|
  %Abb1 = alloca i8*, i32 3
 | 
						|
  %gepbb11 = getelementptr i8** %Abb1, i32 2
 | 
						|
  store i8* %x, i8** %gepbb11, align 8
 | 
						|
  %gepbb12 = getelementptr i8** %Abb1, i32 2
 | 
						|
  %ybb1 = load i8** %gepbb12
 | 
						|
  br label %bb3
 | 
						|
 | 
						|
bb2:
 | 
						|
  %Abb2 = alloca i8*, i32 4
 | 
						|
  %gepbb21 = getelementptr i8** %Abb2, i32 2
 | 
						|
  store i8* %x, i8** %gepbb21, align 8
 | 
						|
  %gepbb22 = getelementptr i8** %Abb2, i32 2
 | 
						|
  %ybb2 = load i8** %gepbb22
 | 
						|
  br label %bb3
 | 
						|
 | 
						|
bb3:
 | 
						|
  %A = phi i8** [ %Abb1, %bb1 ], [ %Abb2, %bb2 ]
 | 
						|
  %y = phi i8* [ %ybb1, %bb1 ], [ %ybb2, %bb2 ]
 | 
						|
  tail call i8* @objc_retain(i8* %x)
 | 
						|
  call void @use_alloca(i8** %A)
 | 
						|
  call void @objc_release(i8* %y), !clang.imprecise_release !0
 | 
						|
  call void @use_pointer(i8* %x)
 | 
						|
  call void @objc_release(i8* %x), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; Make sure in the presence of allocas, if we find a cfghazard we do not perform
 | 
						|
; code motion even if we are known safe. These two concepts are separate and
 | 
						|
; should be treated as such.
 | 
						|
;
 | 
						|
; rdar://13949644
 | 
						|
 | 
						|
; CHECK: define void @test3a() {
 | 
						|
; CHECK: entry:
 | 
						|
; CHECK:   @objc_retainAutoreleasedReturnValue
 | 
						|
; CHECK:   @objc_retain
 | 
						|
; CHECK:   @objc_retain
 | 
						|
; CHECK:   @objc_retain
 | 
						|
; CHECK:   @objc_retain
 | 
						|
; CHECK: arraydestroy.body:
 | 
						|
; CHECK:   @objc_release
 | 
						|
; CHECK-NOT: @objc_release
 | 
						|
; CHECK: arraydestroy.done:
 | 
						|
; CHECK-NOT: @objc_release
 | 
						|
; CHECK: arraydestroy.body1:
 | 
						|
; CHECK:   @objc_release
 | 
						|
; CHECK-NOT: @objc_release
 | 
						|
; CHECK: arraydestroy.done1:
 | 
						|
; CHECK: @objc_release
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test3a() {
 | 
						|
entry:
 | 
						|
  %keys = alloca [2 x i8*], align 16
 | 
						|
  %objs = alloca [2 x i8*], align 16
 | 
						|
  
 | 
						|
  %call1 = call i8* @returner()
 | 
						|
  %tmp0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %call1)
 | 
						|
 | 
						|
  %objs.begin = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 0
 | 
						|
  tail call i8* @objc_retain(i8* %call1)
 | 
						|
  store i8* %call1, i8** %objs.begin, align 8
 | 
						|
  %objs.elt = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 1
 | 
						|
  tail call i8* @objc_retain(i8* %call1)
 | 
						|
  store i8* %call1, i8** %objs.elt
 | 
						|
 | 
						|
  %call2 = call i8* @returner1()
 | 
						|
  %call3 = call i8* @returner2()
 | 
						|
  %keys.begin = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 0
 | 
						|
  tail call i8* @objc_retain(i8* %call2)
 | 
						|
  store i8* %call2, i8** %keys.begin, align 8
 | 
						|
  %keys.elt = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 1
 | 
						|
  tail call i8* @objc_retain(i8* %call3)
 | 
						|
  store i8* %call3, i8** %keys.elt  
 | 
						|
  
 | 
						|
  %gep = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 2
 | 
						|
  br label %arraydestroy.body
 | 
						|
 | 
						|
arraydestroy.body:
 | 
						|
  %arraydestroy.elementPast = phi i8** [ %gep, %entry ], [ %arraydestroy.element, %arraydestroy.body ]
 | 
						|
  %arraydestroy.element = getelementptr inbounds i8** %arraydestroy.elementPast, i64 -1
 | 
						|
  %destroy_tmp = load i8** %arraydestroy.element, align 8
 | 
						|
  call void @objc_release(i8* %destroy_tmp), !clang.imprecise_release !0
 | 
						|
  %objs_ptr = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 0
 | 
						|
  %arraydestroy.cmp = icmp eq i8** %arraydestroy.element, %objs_ptr
 | 
						|
  br i1 %arraydestroy.cmp, label %arraydestroy.done, label %arraydestroy.body
 | 
						|
 | 
						|
arraydestroy.done:
 | 
						|
  %gep1 = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 2
 | 
						|
  br label %arraydestroy.body1
 | 
						|
 | 
						|
arraydestroy.body1:
 | 
						|
  %arraydestroy.elementPast1 = phi i8** [ %gep1, %arraydestroy.done ], [ %arraydestroy.element1, %arraydestroy.body1 ]
 | 
						|
  %arraydestroy.element1 = getelementptr inbounds i8** %arraydestroy.elementPast1, i64 -1
 | 
						|
  %destroy_tmp1 = load i8** %arraydestroy.element1, align 8
 | 
						|
  call void @objc_release(i8* %destroy_tmp1), !clang.imprecise_release !0
 | 
						|
  %keys_ptr = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 0
 | 
						|
  %arraydestroy.cmp1 = icmp eq i8** %arraydestroy.element1, %keys_ptr
 | 
						|
  br i1 %arraydestroy.cmp1, label %arraydestroy.done1, label %arraydestroy.body1
 | 
						|
 | 
						|
arraydestroy.done1:
 | 
						|
  call void @objc_release(i8* %call1), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
; Make sure that even though we stop said code motion we still allow for
 | 
						|
; pointers to be removed if we are known safe in both directions.
 | 
						|
;
 | 
						|
; rdar://13949644
 | 
						|
 | 
						|
; CHECK: define void @test3b() {
 | 
						|
; CHECK: entry:
 | 
						|
; CHECK:   @objc_retainAutoreleasedReturnValue
 | 
						|
; CHECK:   @objc_retain
 | 
						|
; CHECK:   @objc_retain
 | 
						|
; CHECK:   @objc_retain
 | 
						|
; CHECK:   @objc_retain
 | 
						|
; CHECK: arraydestroy.body:
 | 
						|
; CHECK:   @objc_release
 | 
						|
; CHECK-NOT: @objc_release
 | 
						|
; CHECK: arraydestroy.done:
 | 
						|
; CHECK-NOT: @objc_release
 | 
						|
; CHECK: arraydestroy.body1:
 | 
						|
; CHECK:   @objc_release
 | 
						|
; CHECK-NOT: @objc_release
 | 
						|
; CHECK: arraydestroy.done1:
 | 
						|
; CHECK: @objc_release
 | 
						|
; CHECK: ret void
 | 
						|
; CHECK: }
 | 
						|
define void @test3b() {
 | 
						|
entry:
 | 
						|
  %keys = alloca [2 x i8*], align 16
 | 
						|
  %objs = alloca [2 x i8*], align 16
 | 
						|
  
 | 
						|
  %call1 = call i8* @returner()
 | 
						|
  %tmp0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %call1)
 | 
						|
  %tmp1 = tail call i8* @objc_retain(i8* %call1)
 | 
						|
 | 
						|
  %objs.begin = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 0
 | 
						|
  tail call i8* @objc_retain(i8* %call1)
 | 
						|
  store i8* %call1, i8** %objs.begin, align 8
 | 
						|
  %objs.elt = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 1
 | 
						|
  tail call i8* @objc_retain(i8* %call1)
 | 
						|
  store i8* %call1, i8** %objs.elt
 | 
						|
 | 
						|
  %call2 = call i8* @returner1()
 | 
						|
  %call3 = call i8* @returner2()
 | 
						|
  %keys.begin = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 0
 | 
						|
  tail call i8* @objc_retain(i8* %call2)
 | 
						|
  store i8* %call2, i8** %keys.begin, align 8
 | 
						|
  %keys.elt = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 1
 | 
						|
  tail call i8* @objc_retain(i8* %call3)
 | 
						|
  store i8* %call3, i8** %keys.elt  
 | 
						|
  
 | 
						|
  %gep = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 2
 | 
						|
  br label %arraydestroy.body
 | 
						|
 | 
						|
arraydestroy.body:
 | 
						|
  %arraydestroy.elementPast = phi i8** [ %gep, %entry ], [ %arraydestroy.element, %arraydestroy.body ]
 | 
						|
  %arraydestroy.element = getelementptr inbounds i8** %arraydestroy.elementPast, i64 -1
 | 
						|
  %destroy_tmp = load i8** %arraydestroy.element, align 8
 | 
						|
  call void @objc_release(i8* %destroy_tmp), !clang.imprecise_release !0
 | 
						|
  %objs_ptr = getelementptr inbounds [2 x i8*]* %objs, i64 0, i64 0
 | 
						|
  %arraydestroy.cmp = icmp eq i8** %arraydestroy.element, %objs_ptr
 | 
						|
  br i1 %arraydestroy.cmp, label %arraydestroy.done, label %arraydestroy.body
 | 
						|
 | 
						|
arraydestroy.done:
 | 
						|
  %gep1 = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 2
 | 
						|
  br label %arraydestroy.body1
 | 
						|
 | 
						|
arraydestroy.body1:
 | 
						|
  %arraydestroy.elementPast1 = phi i8** [ %gep1, %arraydestroy.done ], [ %arraydestroy.element1, %arraydestroy.body1 ]
 | 
						|
  %arraydestroy.element1 = getelementptr inbounds i8** %arraydestroy.elementPast1, i64 -1
 | 
						|
  %destroy_tmp1 = load i8** %arraydestroy.element1, align 8
 | 
						|
  call void @objc_release(i8* %destroy_tmp1), !clang.imprecise_release !0
 | 
						|
  %keys_ptr = getelementptr inbounds [2 x i8*]* %keys, i64 0, i64 0
 | 
						|
  %arraydestroy.cmp1 = icmp eq i8** %arraydestroy.element1, %keys_ptr
 | 
						|
  br i1 %arraydestroy.cmp1, label %arraydestroy.done1, label %arraydestroy.body1
 | 
						|
 | 
						|
arraydestroy.done1:
 | 
						|
  call void @objc_release(i8* %call1), !clang.imprecise_release !0
 | 
						|
  call void @objc_release(i8* %call1), !clang.imprecise_release !0
 | 
						|
  ret void
 | 
						|
}
 | 
						|
 | 
						|
!0 = !{}
 | 
						|
 | 
						|
declare i32 @__gxx_personality_v0(...)
 |