1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/v8.h"
6
7 #include "test/cctest/cctest.h"
8 #include "test/cctest/compiler/codegen-tester.h"
9 #include "test/cctest/compiler/value-helper.h"
10
11 #if V8_TURBOFAN_TARGET
12
13 using namespace v8::internal;
14 using namespace v8::internal::compiler;
15
16 typedef RawMachineAssembler::Label MLabel;
17
18 static IrOpcode::Value int32cmp_opcodes[] = {
19 IrOpcode::kWord32Equal, IrOpcode::kInt32LessThan,
20 IrOpcode::kInt32LessThanOrEqual, IrOpcode::kUint32LessThan,
21 IrOpcode::kUint32LessThanOrEqual};
22
23
TEST(BranchCombineWord32EqualZero_1)24 TEST(BranchCombineWord32EqualZero_1) {
25 // Test combining a branch with x == 0
26 RawMachineAssemblerTester<int32_t> m(kMachInt32);
27 int32_t eq_constant = -1033;
28 int32_t ne_constant = 825118;
29 Node* p0 = m.Parameter(0);
30
31 MLabel blocka, blockb;
32 m.Branch(m.Word32Equal(p0, m.Int32Constant(0)), &blocka, &blockb);
33 m.Bind(&blocka);
34 m.Return(m.Int32Constant(eq_constant));
35 m.Bind(&blockb);
36 m.Return(m.Int32Constant(ne_constant));
37
38 FOR_INT32_INPUTS(i) {
39 int32_t a = *i;
40 int32_t expect = a == 0 ? eq_constant : ne_constant;
41 CHECK_EQ(expect, m.Call(a));
42 }
43 }
44
45
TEST(BranchCombineWord32EqualZero_chain)46 TEST(BranchCombineWord32EqualZero_chain) {
47 // Test combining a branch with a chain of x == 0 == 0 == 0 ...
48 int32_t eq_constant = -1133;
49 int32_t ne_constant = 815118;
50
51 for (int k = 0; k < 6; k++) {
52 RawMachineAssemblerTester<int32_t> m(kMachInt32);
53 Node* p0 = m.Parameter(0);
54 MLabel blocka, blockb;
55 Node* cond = p0;
56 for (int j = 0; j < k; j++) {
57 cond = m.Word32Equal(cond, m.Int32Constant(0));
58 }
59 m.Branch(cond, &blocka, &blockb);
60 m.Bind(&blocka);
61 m.Return(m.Int32Constant(eq_constant));
62 m.Bind(&blockb);
63 m.Return(m.Int32Constant(ne_constant));
64
65 FOR_INT32_INPUTS(i) {
66 int32_t a = *i;
67 int32_t expect = (k & 1) == 1 ? (a == 0 ? eq_constant : ne_constant)
68 : (a == 0 ? ne_constant : eq_constant);
69 CHECK_EQ(expect, m.Call(a));
70 }
71 }
72 }
73
74
TEST(BranchCombineInt32LessThanZero_1)75 TEST(BranchCombineInt32LessThanZero_1) {
76 // Test combining a branch with x < 0
77 RawMachineAssemblerTester<int32_t> m(kMachInt32);
78 int32_t eq_constant = -1433;
79 int32_t ne_constant = 845118;
80 Node* p0 = m.Parameter(0);
81
82 MLabel blocka, blockb;
83 m.Branch(m.Int32LessThan(p0, m.Int32Constant(0)), &blocka, &blockb);
84 m.Bind(&blocka);
85 m.Return(m.Int32Constant(eq_constant));
86 m.Bind(&blockb);
87 m.Return(m.Int32Constant(ne_constant));
88
89 FOR_INT32_INPUTS(i) {
90 int32_t a = *i;
91 int32_t expect = a < 0 ? eq_constant : ne_constant;
92 CHECK_EQ(expect, m.Call(a));
93 }
94 }
95
96
TEST(BranchCombineUint32LessThan100_1)97 TEST(BranchCombineUint32LessThan100_1) {
98 // Test combining a branch with x < 100
99 RawMachineAssemblerTester<int32_t> m(kMachUint32);
100 int32_t eq_constant = 1471;
101 int32_t ne_constant = 88845718;
102 Node* p0 = m.Parameter(0);
103
104 MLabel blocka, blockb;
105 m.Branch(m.Uint32LessThan(p0, m.Int32Constant(100)), &blocka, &blockb);
106 m.Bind(&blocka);
107 m.Return(m.Int32Constant(eq_constant));
108 m.Bind(&blockb);
109 m.Return(m.Int32Constant(ne_constant));
110
111 FOR_UINT32_INPUTS(i) {
112 uint32_t a = *i;
113 int32_t expect = a < 100 ? eq_constant : ne_constant;
114 CHECK_EQ(expect, m.Call(a));
115 }
116 }
117
118
TEST(BranchCombineUint32LessThanOrEqual100_1)119 TEST(BranchCombineUint32LessThanOrEqual100_1) {
120 // Test combining a branch with x <= 100
121 RawMachineAssemblerTester<int32_t> m(kMachUint32);
122 int32_t eq_constant = 1479;
123 int32_t ne_constant = 77845719;
124 Node* p0 = m.Parameter(0);
125
126 MLabel blocka, blockb;
127 m.Branch(m.Uint32LessThanOrEqual(p0, m.Int32Constant(100)), &blocka, &blockb);
128 m.Bind(&blocka);
129 m.Return(m.Int32Constant(eq_constant));
130 m.Bind(&blockb);
131 m.Return(m.Int32Constant(ne_constant));
132
133 FOR_UINT32_INPUTS(i) {
134 uint32_t a = *i;
135 int32_t expect = a <= 100 ? eq_constant : ne_constant;
136 CHECK_EQ(expect, m.Call(a));
137 }
138 }
139
140
TEST(BranchCombineZeroLessThanInt32_1)141 TEST(BranchCombineZeroLessThanInt32_1) {
142 // Test combining a branch with 0 < x
143 RawMachineAssemblerTester<int32_t> m(kMachInt32);
144 int32_t eq_constant = -2033;
145 int32_t ne_constant = 225118;
146 Node* p0 = m.Parameter(0);
147
148 MLabel blocka, blockb;
149 m.Branch(m.Int32LessThan(m.Int32Constant(0), p0), &blocka, &blockb);
150 m.Bind(&blocka);
151 m.Return(m.Int32Constant(eq_constant));
152 m.Bind(&blockb);
153 m.Return(m.Int32Constant(ne_constant));
154
155 FOR_INT32_INPUTS(i) {
156 int32_t a = *i;
157 int32_t expect = 0 < a ? eq_constant : ne_constant;
158 CHECK_EQ(expect, m.Call(a));
159 }
160 }
161
162
TEST(BranchCombineInt32GreaterThanZero_1)163 TEST(BranchCombineInt32GreaterThanZero_1) {
164 // Test combining a branch with x > 0
165 RawMachineAssemblerTester<int32_t> m(kMachInt32);
166 int32_t eq_constant = -1073;
167 int32_t ne_constant = 825178;
168 Node* p0 = m.Parameter(0);
169
170 MLabel blocka, blockb;
171 m.Branch(m.Int32GreaterThan(p0, m.Int32Constant(0)), &blocka, &blockb);
172 m.Bind(&blocka);
173 m.Return(m.Int32Constant(eq_constant));
174 m.Bind(&blockb);
175 m.Return(m.Int32Constant(ne_constant));
176
177 FOR_INT32_INPUTS(i) {
178 int32_t a = *i;
179 int32_t expect = a > 0 ? eq_constant : ne_constant;
180 CHECK_EQ(expect, m.Call(a));
181 }
182 }
183
184
TEST(BranchCombineWord32EqualP)185 TEST(BranchCombineWord32EqualP) {
186 // Test combining a branch with an Word32Equal.
187 RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
188 int32_t eq_constant = -1035;
189 int32_t ne_constant = 825018;
190 Node* p0 = m.Parameter(0);
191 Node* p1 = m.Parameter(1);
192
193 MLabel blocka, blockb;
194 m.Branch(m.Word32Equal(p0, p1), &blocka, &blockb);
195 m.Bind(&blocka);
196 m.Return(m.Int32Constant(eq_constant));
197 m.Bind(&blockb);
198 m.Return(m.Int32Constant(ne_constant));
199
200 FOR_INT32_INPUTS(i) {
201 FOR_INT32_INPUTS(j) {
202 int32_t a = *i;
203 int32_t b = *j;
204 int32_t expect = a == b ? eq_constant : ne_constant;
205 CHECK_EQ(expect, m.Call(a, b));
206 }
207 }
208 }
209
210
TEST(BranchCombineWord32EqualI)211 TEST(BranchCombineWord32EqualI) {
212 int32_t eq_constant = -1135;
213 int32_t ne_constant = 925718;
214
215 for (int left = 0; left < 2; left++) {
216 FOR_INT32_INPUTS(i) {
217 RawMachineAssemblerTester<int32_t> m(kMachInt32);
218 int32_t a = *i;
219
220 Node* p0 = m.Int32Constant(a);
221 Node* p1 = m.Parameter(0);
222
223 MLabel blocka, blockb;
224 if (left == 1) m.Branch(m.Word32Equal(p0, p1), &blocka, &blockb);
225 if (left == 0) m.Branch(m.Word32Equal(p1, p0), &blocka, &blockb);
226 m.Bind(&blocka);
227 m.Return(m.Int32Constant(eq_constant));
228 m.Bind(&blockb);
229 m.Return(m.Int32Constant(ne_constant));
230
231 FOR_INT32_INPUTS(j) {
232 int32_t b = *j;
233 int32_t expect = a == b ? eq_constant : ne_constant;
234 CHECK_EQ(expect, m.Call(b));
235 }
236 }
237 }
238 }
239
240
TEST(BranchCombineInt32CmpP)241 TEST(BranchCombineInt32CmpP) {
242 int32_t eq_constant = -1235;
243 int32_t ne_constant = 725018;
244
245 for (int op = 0; op < 2; op++) {
246 RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
247 Node* p0 = m.Parameter(0);
248 Node* p1 = m.Parameter(1);
249
250 MLabel blocka, blockb;
251 if (op == 0) m.Branch(m.Int32LessThan(p0, p1), &blocka, &blockb);
252 if (op == 1) m.Branch(m.Int32LessThanOrEqual(p0, p1), &blocka, &blockb);
253 m.Bind(&blocka);
254 m.Return(m.Int32Constant(eq_constant));
255 m.Bind(&blockb);
256 m.Return(m.Int32Constant(ne_constant));
257
258 FOR_INT32_INPUTS(i) {
259 FOR_INT32_INPUTS(j) {
260 int32_t a = *i;
261 int32_t b = *j;
262 int32_t expect = 0;
263 if (op == 0) expect = a < b ? eq_constant : ne_constant;
264 if (op == 1) expect = a <= b ? eq_constant : ne_constant;
265 CHECK_EQ(expect, m.Call(a, b));
266 }
267 }
268 }
269 }
270
271
TEST(BranchCombineInt32CmpI)272 TEST(BranchCombineInt32CmpI) {
273 int32_t eq_constant = -1175;
274 int32_t ne_constant = 927711;
275
276 for (int op = 0; op < 2; op++) {
277 FOR_INT32_INPUTS(i) {
278 RawMachineAssemblerTester<int32_t> m(kMachInt32);
279 int32_t a = *i;
280 Node* p0 = m.Int32Constant(a);
281 Node* p1 = m.Parameter(0);
282
283 MLabel blocka, blockb;
284 if (op == 0) m.Branch(m.Int32LessThan(p0, p1), &blocka, &blockb);
285 if (op == 1) m.Branch(m.Int32LessThanOrEqual(p0, p1), &blocka, &blockb);
286 m.Bind(&blocka);
287 m.Return(m.Int32Constant(eq_constant));
288 m.Bind(&blockb);
289 m.Return(m.Int32Constant(ne_constant));
290
291 FOR_INT32_INPUTS(j) {
292 int32_t b = *j;
293 int32_t expect = 0;
294 if (op == 0) expect = a < b ? eq_constant : ne_constant;
295 if (op == 1) expect = a <= b ? eq_constant : ne_constant;
296 CHECK_EQ(expect, m.Call(b));
297 }
298 }
299 }
300 }
301
302
303 // Now come the sophisticated tests for many input shape combinations.
304
305 // Materializes a boolean (1 or 0) from a comparison.
306 class CmpMaterializeBoolGen : public BinopGen<int32_t> {
307 public:
308 CompareWrapper w;
309 bool invert;
310
CmpMaterializeBoolGen(IrOpcode::Value opcode,bool i)311 CmpMaterializeBoolGen(IrOpcode::Value opcode, bool i)
312 : w(opcode), invert(i) {}
313
gen(RawMachineAssemblerTester<int32_t> * m,Node * a,Node * b)314 virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
315 Node* cond = w.MakeNode(m, a, b);
316 if (invert) cond = m->Word32Equal(cond, m->Int32Constant(0));
317 m->Return(cond);
318 }
expected(int32_t a,int32_t b)319 virtual int32_t expected(int32_t a, int32_t b) {
320 if (invert) return !w.Int32Compare(a, b) ? 1 : 0;
321 return w.Int32Compare(a, b) ? 1 : 0;
322 }
323 };
324
325
326 // Generates a branch and return one of two values from a comparison.
327 class CmpBranchGen : public BinopGen<int32_t> {
328 public:
329 CompareWrapper w;
330 bool invert;
331 bool true_first;
332 int32_t eq_constant;
333 int32_t ne_constant;
334
CmpBranchGen(IrOpcode::Value opcode,bool i,bool t,int32_t eq,int32_t ne)335 CmpBranchGen(IrOpcode::Value opcode, bool i, bool t, int32_t eq, int32_t ne)
336 : w(opcode), invert(i), true_first(t), eq_constant(eq), ne_constant(ne) {}
337
gen(RawMachineAssemblerTester<int32_t> * m,Node * a,Node * b)338 virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
339 MLabel blocka, blockb;
340 Node* cond = w.MakeNode(m, a, b);
341 if (invert) cond = m->Word32Equal(cond, m->Int32Constant(0));
342 m->Branch(cond, &blocka, &blockb);
343 if (true_first) {
344 m->Bind(&blocka);
345 m->Return(m->Int32Constant(eq_constant));
346 m->Bind(&blockb);
347 m->Return(m->Int32Constant(ne_constant));
348 } else {
349 m->Bind(&blockb);
350 m->Return(m->Int32Constant(ne_constant));
351 m->Bind(&blocka);
352 m->Return(m->Int32Constant(eq_constant));
353 }
354 }
expected(int32_t a,int32_t b)355 virtual int32_t expected(int32_t a, int32_t b) {
356 if (invert) return !w.Int32Compare(a, b) ? eq_constant : ne_constant;
357 return w.Int32Compare(a, b) ? eq_constant : ne_constant;
358 }
359 };
360
361
TEST(BranchCombineInt32CmpAllInputShapes_materialized)362 TEST(BranchCombineInt32CmpAllInputShapes_materialized) {
363 for (size_t i = 0; i < arraysize(int32cmp_opcodes); i++) {
364 CmpMaterializeBoolGen gen(int32cmp_opcodes[i], false);
365 Int32BinopInputShapeTester tester(&gen);
366 tester.TestAllInputShapes();
367 }
368 }
369
370
TEST(BranchCombineInt32CmpAllInputShapes_inverted_materialized)371 TEST(BranchCombineInt32CmpAllInputShapes_inverted_materialized) {
372 for (size_t i = 0; i < arraysize(int32cmp_opcodes); i++) {
373 CmpMaterializeBoolGen gen(int32cmp_opcodes[i], true);
374 Int32BinopInputShapeTester tester(&gen);
375 tester.TestAllInputShapes();
376 }
377 }
378
379
TEST(BranchCombineInt32CmpAllInputShapes_branch_true)380 TEST(BranchCombineInt32CmpAllInputShapes_branch_true) {
381 for (int i = 0; i < static_cast<int>(arraysize(int32cmp_opcodes)); i++) {
382 CmpBranchGen gen(int32cmp_opcodes[i], false, false, 995 + i, -1011 - i);
383 Int32BinopInputShapeTester tester(&gen);
384 tester.TestAllInputShapes();
385 }
386 }
387
388
TEST(BranchCombineInt32CmpAllInputShapes_branch_false)389 TEST(BranchCombineInt32CmpAllInputShapes_branch_false) {
390 for (int i = 0; i < static_cast<int>(arraysize(int32cmp_opcodes)); i++) {
391 CmpBranchGen gen(int32cmp_opcodes[i], false, true, 795 + i, -2011 - i);
392 Int32BinopInputShapeTester tester(&gen);
393 tester.TestAllInputShapes();
394 }
395 }
396
397
TEST(BranchCombineInt32CmpAllInputShapes_inverse_branch_true)398 TEST(BranchCombineInt32CmpAllInputShapes_inverse_branch_true) {
399 for (int i = 0; i < static_cast<int>(arraysize(int32cmp_opcodes)); i++) {
400 CmpBranchGen gen(int32cmp_opcodes[i], true, false, 695 + i, -3011 - i);
401 Int32BinopInputShapeTester tester(&gen);
402 tester.TestAllInputShapes();
403 }
404 }
405
406
TEST(BranchCombineInt32CmpAllInputShapes_inverse_branch_false)407 TEST(BranchCombineInt32CmpAllInputShapes_inverse_branch_false) {
408 for (int i = 0; i < static_cast<int>(arraysize(int32cmp_opcodes)); i++) {
409 CmpBranchGen gen(int32cmp_opcodes[i], true, true, 595 + i, -4011 - i);
410 Int32BinopInputShapeTester tester(&gen);
411 tester.TestAllInputShapes();
412 }
413 }
414
415
TEST(BranchCombineFloat64Compares)416 TEST(BranchCombineFloat64Compares) {
417 double inf = V8_INFINITY;
418 double nan = v8::base::OS::nan_value();
419 double inputs[] = {0.0, 1.0, -1.0, -inf, inf, nan};
420
421 int32_t eq_constant = -1733;
422 int32_t ne_constant = 915118;
423
424 double input_a = 0.0;
425 double input_b = 0.0;
426
427 CompareWrapper cmps[] = {CompareWrapper(IrOpcode::kFloat64Equal),
428 CompareWrapper(IrOpcode::kFloat64LessThan),
429 CompareWrapper(IrOpcode::kFloat64LessThanOrEqual)};
430
431 for (size_t c = 0; c < arraysize(cmps); c++) {
432 CompareWrapper cmp = cmps[c];
433 for (int invert = 0; invert < 2; invert++) {
434 RawMachineAssemblerTester<int32_t> m;
435 Node* a = m.LoadFromPointer(&input_a, kMachFloat64);
436 Node* b = m.LoadFromPointer(&input_b, kMachFloat64);
437
438 MLabel blocka, blockb;
439 Node* cond = cmp.MakeNode(&m, a, b);
440 if (invert) cond = m.Word32Equal(cond, m.Int32Constant(0));
441 m.Branch(cond, &blocka, &blockb);
442 m.Bind(&blocka);
443 m.Return(m.Int32Constant(eq_constant));
444 m.Bind(&blockb);
445 m.Return(m.Int32Constant(ne_constant));
446
447 for (size_t i = 0; i < arraysize(inputs); i++) {
448 for (size_t j = 0; j < arraysize(inputs); j += 2) {
449 input_a = inputs[i];
450 input_b = inputs[i];
451 int32_t expected =
452 invert ? (cmp.Float64Compare(input_a, input_b) ? ne_constant
453 : eq_constant)
454 : (cmp.Float64Compare(input_a, input_b) ? eq_constant
455 : ne_constant);
456 CHECK_EQ(expected, m.Call());
457 }
458 }
459 }
460 }
461 }
462 #endif // V8_TURBOFAN_TARGET
463