aboutsummaryrefslogtreecommitdiffstats
path: root/test/Transforms/DeadArgElim/aggregates.ll
blob: 68d253425587faa99ece7c8cc63032ee28aaf782 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
; 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
}