You've already forked linux-packaging-mono
							
							
		
			
	
	
		
			187 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			LLVM
		
	
	
	
	
	
		
		
			
		
	
	
			187 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			LLVM
		
	
	
	
	
	
|   | ; RUN: opt -S -deadargelim %s | FileCheck %s
 | ||
|  | 
 | ||
|  | ; Case 0: the basic example: an entire aggregate use is returned, but it's
 | ||
|  | ; actually only used in ways we can eliminate. We gain benefit from analysing
 | ||
|  | ; the "use" and applying its results to all sub-values.
 | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define internal void @agguse_dead()
 | ||
|  | 
 | ||
|  | define internal { i32, i32 } @agguse_dead() { | ||
|  |   ret { i32, i32 } { i32 0, i32 1 } | ||
|  | } | ||
|  | 
 | ||
|  | define internal { i32, i32 } @test_agguse_dead() { | ||
|  |   %val = call { i32, i32 } @agguse_dead() | ||
|  |   ret { i32, i32 } %val | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | ; Case 1: an opaque use of the aggregate exists (in this case dead). Otherwise
 | ||
|  | ; only one value is used, so function can be simplified.
 | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define internal i32 @rets_independent_if_agguse_dead()
 | ||
|  | ; CHECK: [[RET:%.*]] = extractvalue { i32, i32 } { i32 0, i32 1 }, 1
 | ||
|  | ; CHECK: ret i32 [[RET]]
 | ||
|  | 
 | ||
|  | define internal { i32, i32 } @rets_independent_if_agguse_dead() { | ||
|  |   ret { i32, i32 } { i32 0, i32 1 } | ||
|  | } | ||
|  | 
 | ||
|  | define internal { i32, i32 } @test_rets_independent_if_agguse_dead(i1 %tst) { | ||
|  |   %val = call { i32, i32 } @rets_independent_if_agguse_dead() | ||
|  |   br i1 %tst, label %use_1, label %use_aggregate | ||
|  | 
 | ||
|  | use_1: | ||
|  |   ; This use can be classified as applying only to ret 1.
 | ||
|  |   %val0 = extractvalue { i32, i32 } %val, 1 | ||
|  |   call void @callee(i32 %val0) | ||
|  |   ret { i32, i32 } undef | ||
|  | 
 | ||
|  | use_aggregate: | ||
|  |   ; This use is assumed to apply to both 0 and 1.
 | ||
|  |   ret { i32, i32 } %val | ||
|  | } | ||
|  | 
 | ||
|  | ; Case 2: an opaque use of the aggregate exists (in this case *live*). Other
 | ||
|  | ; uses shouldn't matter.
 | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define internal { i32, i32 } @rets_live_agguse()
 | ||
|  | ; CHECK: ret { i32, i32 } { i32 0, i32 1 }
 | ||
|  | 
 | ||
|  | define internal { i32, i32 } @rets_live_agguse() { | ||
|  |   ret { i32, i32} { i32 0, i32 1 } | ||
|  | } | ||
|  | 
 | ||
|  | define { i32, i32 } @test_rets_live_aggues(i1 %tst) { | ||
|  |   %val = call { i32, i32 } @rets_live_agguse() | ||
|  |   br i1 %tst, label %use_1, label %use_aggregate | ||
|  | 
 | ||
|  | use_1: | ||
|  |   ; This use can be classified as applying only to ret 1.
 | ||
|  |   %val0 = extractvalue { i32, i32 } %val, 1 | ||
|  |   call void @callee(i32 %val0) | ||
|  |   ret { i32, i32 } undef | ||
|  | 
 | ||
|  | use_aggregate: | ||
|  |   ; This use is assumed to apply to both 0 and 1.
 | ||
|  |   ret { i32, i32 } %val | ||
|  | } | ||
|  | 
 | ||
|  | declare void @callee(i32) | ||
|  | 
 | ||
|  | ; Case 3: the insertvalue meant %in was live if ret-slot-1 was, but we were only
 | ||
|  | ; tracking multiple ret-slots for struct types. So %in was eliminated
 | ||
|  | ; incorrectly.
 | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define internal [2 x i32] @array_rets_have_multiple_slots(i32 %in)
 | ||
|  | 
 | ||
|  | define internal [2 x i32] @array_rets_have_multiple_slots(i32 %in) { | ||
|  |   %ret = insertvalue [2 x i32] undef, i32 %in, 1 | ||
|  |   ret [2 x i32] %ret | ||
|  | } | ||
|  | 
 | ||
|  | define [2 x i32] @test_array_rets_have_multiple_slots() { | ||
|  |   %res = call [2 x i32] @array_rets_have_multiple_slots(i32 42) | ||
|  |   ret [2 x i32] %res | ||
|  | } | ||
|  | 
 | ||
|  | ; Case 4: we can remove some retvals from the array. It's nice to produce an
 | ||
|  | ; array again having done so (rather than converting it to a struct).
 | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define internal [2 x i32] @can_shrink_arrays()
 | ||
|  | ; CHECK: [[VAL0:%.*]] = extractvalue [3 x i32] [i32 42, i32 43, i32 44], 0
 | ||
|  | ; CHECK: [[RESTMP:%.*]] = insertvalue [2 x i32] undef, i32 [[VAL0]], 0
 | ||
|  | ; CHECK: [[VAL2:%.*]] = extractvalue [3 x i32] [i32 42, i32 43, i32 44], 2
 | ||
|  | ; CHECK: [[RES:%.*]] = insertvalue [2 x i32] [[RESTMP]], i32 [[VAL2]], 1
 | ||
|  | ; CHECK: ret [2 x i32] [[RES]]
 | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define void @test_can_shrink_arrays()
 | ||
|  | 
 | ||
|  | define internal [3 x i32] @can_shrink_arrays() { | ||
|  |   ret [3 x i32] [i32 42, i32 43, i32 44] | ||
|  | } | ||
|  | 
 | ||
|  | define void @test_can_shrink_arrays() { | ||
|  |   %res = call [3 x i32] @can_shrink_arrays() | ||
|  | 
 | ||
|  |   %res.0 = extractvalue [3 x i32] %res, 0 | ||
|  |   call void @callee(i32 %res.0) | ||
|  | 
 | ||
|  |   %res.2 = extractvalue [3 x i32] %res, 2 | ||
|  |   call void @callee(i32 %res.2) | ||
|  | 
 | ||
|  |   ret void | ||
|  | } | ||
|  | 
 | ||
|  | ; Case 5: %in gets passed directly to the return. It should mark be marked as
 | ||
|  | ; used if *any* of the return values are, not just if value 0 is.
 | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define internal i32 @ret_applies_to_all({ i32, i32 } %in)
 | ||
|  | ; CHECK: [[RET:%.*]] = extractvalue { i32, i32 } %in, 1
 | ||
|  | ; CHECK: ret i32 [[RET]]
 | ||
|  | 
 | ||
|  | define internal {i32, i32} @ret_applies_to_all({i32, i32} %in) { | ||
|  |   ret {i32, i32} %in | ||
|  | } | ||
|  | 
 | ||
|  | define i32 @test_ret_applies_to_all() { | ||
|  |   %val = call {i32, i32} @ret_applies_to_all({i32, i32} {i32 42, i32 43}) | ||
|  |   %ret = extractvalue {i32, i32} %val, 1 | ||
|  |   ret i32 %ret | ||
|  | } | ||
|  | 
 | ||
|  | ; Case 6: When considering @mid, the return instruciton has sub-value 0
 | ||
|  | ; unconditionally live, but 1 only conditionally live. Since at that level we're
 | ||
|  | ; applying the results to the whole of %res, this means %res is live and cannot
 | ||
|  | ; be reduced. There is scope for further optimisation here (though not visible
 | ||
|  | ; in this test-case).
 | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define internal { i8*, i32 } @inner()
 | ||
|  | 
 | ||
|  | define internal {i8*, i32} @mid() { | ||
|  |   %res = call {i8*, i32} @inner() | ||
|  |   %intval = extractvalue {i8*, i32} %res, 1 | ||
|  |   %tst = icmp eq i32 %intval, 42 | ||
|  |   br i1 %tst, label %true, label %true | ||
|  | 
 | ||
|  | true: | ||
|  |   ret {i8*, i32} %res | ||
|  | } | ||
|  | 
 | ||
|  | define internal {i8*, i32} @inner() { | ||
|  |   ret {i8*, i32} {i8* null, i32 42} | ||
|  | } | ||
|  | 
 | ||
|  | define internal i8 @outer() { | ||
|  |   %res = call {i8*, i32} @mid() | ||
|  |   %resptr = extractvalue {i8*, i32} %res, 0 | ||
|  | 
 | ||
|  |   %val = load i8, i8* %resptr | ||
|  |   ret i8 %val | ||
|  | } | ||
|  | 
 | ||
|  | define internal { i32 } @agg_ret() { | ||
|  | entry: | ||
|  |   unreachable | ||
|  | } | ||
|  | 
 | ||
|  | ; CHECK-LABEL: define void @PR24906
 | ||
|  | ; CHECK: %[[invoke:.*]] = invoke i32 @agg_ret()
 | ||
|  | ; CHECK: %[[oldret:.*]] = insertvalue { i32 } undef, i32 %[[invoke]], 0
 | ||
|  | ; CHECK: phi { i32 } [ %[[oldret]],
 | ||
|  | define void @PR24906() personality i32 (i32)* undef { | ||
|  | entry: | ||
|  |   %tmp2 = invoke { i32 } @agg_ret() | ||
|  |           to label %bb3 unwind label %bb4 | ||
|  | 
 | ||
|  | bb3: | ||
|  |   %tmp3 = phi { i32 } [ %tmp2, %entry ] | ||
|  |   unreachable | ||
|  | 
 | ||
|  | bb4: | ||
|  |   %tmp4 = landingpad { i8*, i32 } | ||
|  |           cleanup | ||
|  |   unreachable | ||
|  | } |