1
2
3
4
5 package ssacompile
6
7 import (
8 "testing"
9
10 "cmd/compile/internal/ssa"
11 "cmd/compile/internal/ssa/block"
12 "cmd/compile/internal/ssa/ssaop"
13 "cmd/compile/internal/types"
14 )
15
16
17 func ARM64Lt(cond, sub, alt string) ctrl {
18 return ctrl{block.BlockARM64LT, cond, []string{sub, alt}}
19 }
20
21
22 func ARM64Gt(cond, sub, alt string) ctrl {
23 return ctrl{block.BlockARM64GT, cond, []string{sub, alt}}
24 }
25
26
27 func ARM64Ne(cond, sub, alt string) ctrl {
28 return ctrl{block.BlockARM64NE, cond, []string{sub, alt}}
29 }
30
31
32 func ARM64Eq(cond, sub, alt string) ctrl {
33 return ctrl{block.BlockARM64EQ, cond, []string{sub, alt}}
34 }
35
36
37
38
39
40
41
42
43
44
45 func isNewConditionCorrect(b *ssa.Block) bool {
46 if b.Kind != block.BlockARM64LT {
47 return false
48 }
49
50 v := b.Controls[0]
51 if v.Op != ssaop.OpARM64CCMPconst {
52 return false
53 }
54
55 params := v.AuxArm64ConditionalParams()
56 if params.Cond != ssaop.OpARM64GreaterThan {
57 return false
58 }
59 if params.Nzcv() != 1 {
60
61 return false
62 }
63 if imm, ok := params.ConstValue(); !ok || imm != 4 {
64 return false
65 }
66
67 return true
68 }
69
70
71
72
73
74 func containsOpARM64CCMP(b *ssa.Block) bool {
75 for _, v := range b.Values {
76 if v.Op == ssaop.OpARM64CCMP || v.Op == ssaop.OpARM64CCMPconst {
77 return true
78 }
79 }
80 return false
81 }
82
83
84
85
86
87
88
89
90
91 func TestMergeConditionalBranchesWithoutPointers(t *testing.T) {
92 t.Run("arm64", func(t *testing.T) {
93 c := testConfigArch(t, "arm64")
94 intType := c.config.Types.Int64
95 fun := c.Fun("entry",
96 Bloc("entry",
97 Valu("mem",
98 ssaop.OpInitMem,
99 types.TypeMem,
100 0, nil,
101 ),
102 Valu("a",
103 ssaop.OpArg,
104 intType,
105 0, c.Temp(intType),
106 ),
107 Valu("b",
108 ssaop.OpArg,
109 intType,
110 1, c.Temp(intType),
111 ),
112 Valu("cond1",
113 ssaop.OpARM64CMPconst,
114 types.TypeFlags,
115 1, nil,
116 "a",
117 ),
118 ARM64Gt("cond1", "second_comparison", "ret_false"),
119 ),
120 Bloc("second_comparison",
121 Valu("cond2",
122 ssaop.OpARM64CMPconst,
123 types.TypeFlags,
124 4, nil,
125 "b",
126 ),
127 ARM64Lt("cond2", "ret_false", "ret_true"),
128 ),
129 Bloc("ret_true",
130 Valu("const1",
131 ssaop.OpARM64MOVDconst,
132 intType,
133 1, nil,
134 ),
135 Valu("true_result",
136 ssaop.OpMakeResult,
137 types.TypeMem,
138 0, nil,
139 "const1", "mem",
140 ),
141 Ret("true_result"),
142 ),
143 Bloc("ret_false",
144 Valu("const0",
145 ssaop.OpARM64MOVDconst,
146 intType,
147 0, nil,
148 ),
149 Valu("false_result",
150 ssaop.OpMakeResult,
151 types.TypeMem,
152 0, nil,
153 "const0", "mem",
154 ),
155 Ret("false_result"),
156 ),
157 )
158
159 CheckFunc(fun.f)
160 mergeConditionalBranches(fun.f)
161 CheckFunc(fun.f)
162
163 if len(fun.blocks) != 4 {
164 t.Errorf("Important block was deleted")
165 }
166
167 entryBlock := fun.blocks["entry"]
168 secondBlock := fun.blocks["second_comparison"]
169
170 if secondBlock.Kind != block.BlockPlain || len(secondBlock.Values) != 0 {
171 t.Errorf("Block with second condition wasn't cleaned")
172 }
173
174 if !isNewConditionCorrect(entryBlock) {
175 t.Errorf("Entry block doesn't contain CCMP opertation")
176 }
177 })
178 }
179
180
181 func TestNoCCMPWithPointerAndMemoryLoad(t *testing.T) {
182 t.Run("arm64", func(t *testing.T) {
183 c := testConfigArch(t, "arm64")
184 intType := c.config.Types.Int64
185 ptrType := c.config.Types.BytePtr
186
187 fun := c.Fun("entry",
188 Bloc("entry",
189 Valu("mem",
190 ssaop.OpInitMem,
191 types.TypeMem,
192 0, nil,
193 ),
194 Valu("ptr",
195 ssaop.OpArg,
196 ptrType,
197 0, c.Temp(ptrType),
198 ),
199 Valu("cond1",
200 ssaop.OpARM64CMPconst,
201 types.TypeFlags,
202 0, nil,
203 "ptr",
204 ),
205 ARM64Ne("cond1", "second_comparison", "ret_false"),
206 ),
207 Bloc("second_comparison",
208 Valu("load",
209 ssaop.OpLoad,
210 intType,
211 0, nil,
212 "ptr", "mem",
213 ),
214 Valu("cond2",
215 ssaop.OpARM64CMPconst,
216 types.TypeFlags,
217 3, nil,
218 "load",
219 ),
220 ARM64Eq("cond2", "ret_true", "ret_false"),
221 ),
222 Bloc("ret_true",
223 Valu("const1",
224 ssaop.OpARM64MOVDconst,
225 intType,
226 1, nil,
227 ),
228 Valu("true_result",
229 ssaop.OpMakeResult,
230 types.TypeMem,
231 0, nil,
232 "const1", "mem",
233 ),
234 Ret("true_result"),
235 ),
236 Bloc("ret_false",
237 Valu("const0",
238 ssaop.OpARM64MOVDconst,
239 intType,
240 0, nil,
241 ),
242 Valu("false_result",
243 ssaop.OpMakeResult,
244 types.TypeMem,
245 0, nil,
246 "const0", "mem",
247 ),
248 Ret("false_result"),
249 ),
250 )
251
252 CheckFunc(fun.f)
253 mergeConditionalBranches(fun.f)
254 CheckFunc(fun.f)
255
256
257 if fun.blocks["second_comparison"] == nil {
258 t.Errorf("Second comparison block was incorrectly removed")
259 }
260
261 entryBlock := fun.blocks["entry"]
262 secondBlock := fun.blocks["second_comparison"]
263
264
265 if containsOpARM64CCMP(entryBlock) {
266 t.Errorf("Entry block contains CCMP operation, but shouldn't due to memory load")
267 }
268
269
270 hasLoad := false
271 for _, v := range secondBlock.Values {
272 if v.Op == ssaop.OpLoad {
273 hasLoad = true
274 break
275 }
276 }
277 if !hasLoad {
278 t.Errorf("Second comparison block should contain load operation")
279 }
280
281
282 if secondBlock.Kind == block.BlockPlain {
283 t.Errorf("Block with memory load was incorrectly cleaned")
284 }
285 })
286 }
287
View as plain text