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/compiler/change-lowering.h"
6 #include "src/compiler/compiler-test-utils.h"
7 #include "src/compiler/graph-unittest.h"
8 #include "src/compiler/js-graph.h"
9 #include "src/compiler/node-properties-inl.h"
10 #include "src/compiler/simplified-operator.h"
11 #include "src/compiler/typer.h"
12 #include "testing/gmock-support.h"
13
14 using testing::_;
15 using testing::AllOf;
16 using testing::Capture;
17 using testing::CaptureEq;
18
19 namespace v8 {
20 namespace internal {
21 namespace compiler {
22
23 // TODO(bmeurer): Find a new home for these functions.
operator <<(std::ostream & os,const MachineType & type)24 inline std::ostream& operator<<(std::ostream& os, const MachineType& type) {
25 OStringStream ost;
26 ost << type;
27 return os << ost.c_str();
28 }
29
30
31 class ChangeLoweringTest : public GraphTest {
32 public:
ChangeLoweringTest()33 ChangeLoweringTest() : simplified_(zone()) {}
~ChangeLoweringTest()34 virtual ~ChangeLoweringTest() {}
35
36 virtual MachineType WordRepresentation() const = 0;
37
38 protected:
HeapNumberValueOffset() const39 int HeapNumberValueOffset() const {
40 STATIC_ASSERT(HeapNumber::kValueOffset % kApiPointerSize == 0);
41 return (HeapNumber::kValueOffset / kApiPointerSize) * PointerSize() -
42 kHeapObjectTag;
43 }
Is32() const44 bool Is32() const { return WordRepresentation() == kRepWord32; }
PointerSize() const45 int PointerSize() const {
46 switch (WordRepresentation()) {
47 case kRepWord32:
48 return 4;
49 case kRepWord64:
50 return 8;
51 default:
52 break;
53 }
54 UNREACHABLE();
55 return 0;
56 }
SmiMaxValue() const57 int SmiMaxValue() const { return -(SmiMinValue() + 1); }
SmiMinValue() const58 int SmiMinValue() const {
59 return static_cast<int>(0xffffffffu << (SmiValueSize() - 1));
60 }
SmiShiftAmount() const61 int SmiShiftAmount() const { return kSmiTagSize + SmiShiftSize(); }
SmiShiftSize() const62 int SmiShiftSize() const {
63 return Is32() ? SmiTagging<4>::SmiShiftSize()
64 : SmiTagging<8>::SmiShiftSize();
65 }
SmiValueSize() const66 int SmiValueSize() const {
67 return Is32() ? SmiTagging<4>::SmiValueSize()
68 : SmiTagging<8>::SmiValueSize();
69 }
70
Parameter(int32_t index=0)71 Node* Parameter(int32_t index = 0) {
72 return graph()->NewNode(common()->Parameter(index), graph()->start());
73 }
74
Reduce(Node * node)75 Reduction Reduce(Node* node) {
76 Typer typer(zone());
77 MachineOperatorBuilder machine(WordRepresentation());
78 JSOperatorBuilder javascript(zone());
79 JSGraph jsgraph(graph(), common(), &javascript, &typer, &machine);
80 CompilationInfo info(isolate(), zone());
81 Linkage linkage(&info);
82 ChangeLowering reducer(&jsgraph, &linkage);
83 return reducer.Reduce(node);
84 }
85
simplified()86 SimplifiedOperatorBuilder* simplified() { return &simplified_; }
87
IsAllocateHeapNumber(const Matcher<Node * > & effect_matcher,const Matcher<Node * > & control_matcher)88 Matcher<Node*> IsAllocateHeapNumber(const Matcher<Node*>& effect_matcher,
89 const Matcher<Node*>& control_matcher) {
90 return IsCall(
91 _, IsHeapConstant(Unique<HeapObject>::CreateImmovable(
92 CEntryStub(isolate(), 1).GetCode())),
93 IsExternalConstant(ExternalReference(
94 Runtime::FunctionForId(Runtime::kAllocateHeapNumber), isolate())),
95 IsInt32Constant(0), IsNumberConstant(0.0), effect_matcher,
96 control_matcher);
97 }
IsWordEqual(const Matcher<Node * > & lhs_matcher,const Matcher<Node * > & rhs_matcher)98 Matcher<Node*> IsWordEqual(const Matcher<Node*>& lhs_matcher,
99 const Matcher<Node*>& rhs_matcher) {
100 return Is32() ? IsWord32Equal(lhs_matcher, rhs_matcher)
101 : IsWord64Equal(lhs_matcher, rhs_matcher);
102 }
103
104 private:
105 SimplifiedOperatorBuilder simplified_;
106 };
107
108
109 // -----------------------------------------------------------------------------
110 // Common.
111
112
113 class ChangeLoweringCommonTest
114 : public ChangeLoweringTest,
115 public ::testing::WithParamInterface<MachineType> {
116 public:
~ChangeLoweringCommonTest()117 virtual ~ChangeLoweringCommonTest() {}
118
WordRepresentation() const119 virtual MachineType WordRepresentation() const FINAL OVERRIDE {
120 return GetParam();
121 }
122 };
123
124
TARGET_TEST_P(ChangeLoweringCommonTest,ChangeBitToBool)125 TARGET_TEST_P(ChangeLoweringCommonTest, ChangeBitToBool) {
126 Node* val = Parameter(0);
127 Node* node = graph()->NewNode(simplified()->ChangeBitToBool(), val);
128 Reduction reduction = Reduce(node);
129 ASSERT_TRUE(reduction.Changed());
130
131 Node* phi = reduction.replacement();
132 Capture<Node*> branch;
133 EXPECT_THAT(phi,
134 IsPhi(static_cast<MachineType>(kTypeBool | kRepTagged),
135 IsTrueConstant(), IsFalseConstant(),
136 IsMerge(IsIfTrue(AllOf(CaptureEq(&branch),
137 IsBranch(val, graph()->start()))),
138 IsIfFalse(CaptureEq(&branch)))));
139 }
140
141
TARGET_TEST_P(ChangeLoweringCommonTest,ChangeBoolToBit)142 TARGET_TEST_P(ChangeLoweringCommonTest, ChangeBoolToBit) {
143 Node* val = Parameter(0);
144 Node* node = graph()->NewNode(simplified()->ChangeBoolToBit(), val);
145 Reduction reduction = Reduce(node);
146 ASSERT_TRUE(reduction.Changed());
147
148 EXPECT_THAT(reduction.replacement(), IsWordEqual(val, IsTrueConstant()));
149 }
150
151
TARGET_TEST_P(ChangeLoweringCommonTest,ChangeFloat64ToTagged)152 TARGET_TEST_P(ChangeLoweringCommonTest, ChangeFloat64ToTagged) {
153 Node* val = Parameter(0);
154 Node* node = graph()->NewNode(simplified()->ChangeFloat64ToTagged(), val);
155 Reduction reduction = Reduce(node);
156 ASSERT_TRUE(reduction.Changed());
157
158 Node* finish = reduction.replacement();
159 Capture<Node*> heap_number;
160 EXPECT_THAT(
161 finish,
162 IsFinish(
163 AllOf(CaptureEq(&heap_number),
164 IsAllocateHeapNumber(IsValueEffect(val), graph()->start())),
165 IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number),
166 IsInt32Constant(HeapNumberValueOffset()), val,
167 CaptureEq(&heap_number), graph()->start())));
168 }
169
170
TARGET_TEST_P(ChangeLoweringCommonTest,StringAdd)171 TARGET_TEST_P(ChangeLoweringCommonTest, StringAdd) {
172 Node* node =
173 graph()->NewNode(simplified()->StringAdd(), Parameter(0), Parameter(1));
174 Reduction reduction = Reduce(node);
175 EXPECT_FALSE(reduction.Changed());
176 }
177
178
179 INSTANTIATE_TEST_CASE_P(ChangeLoweringTest, ChangeLoweringCommonTest,
180 ::testing::Values(kRepWord32, kRepWord64));
181
182
183 // -----------------------------------------------------------------------------
184 // 32-bit
185
186
187 class ChangeLowering32Test : public ChangeLoweringTest {
188 public:
~ChangeLowering32Test()189 virtual ~ChangeLowering32Test() {}
WordRepresentation() const190 virtual MachineType WordRepresentation() const FINAL OVERRIDE {
191 return kRepWord32;
192 }
193 };
194
195
TARGET_TEST_F(ChangeLowering32Test,ChangeInt32ToTagged)196 TARGET_TEST_F(ChangeLowering32Test, ChangeInt32ToTagged) {
197 Node* val = Parameter(0);
198 Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), val);
199 Reduction reduction = Reduce(node);
200 ASSERT_TRUE(reduction.Changed());
201
202 Node* phi = reduction.replacement();
203 Capture<Node*> add, branch, heap_number, if_true;
204 EXPECT_THAT(
205 phi,
206 IsPhi(kMachAnyTagged,
207 IsFinish(
208 AllOf(CaptureEq(&heap_number),
209 IsAllocateHeapNumber(_, CaptureEq(&if_true))),
210 IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number),
211 IsInt32Constant(HeapNumberValueOffset()),
212 IsChangeInt32ToFloat64(val), CaptureEq(&heap_number),
213 CaptureEq(&if_true))),
214 IsProjection(
215 0, AllOf(CaptureEq(&add), IsInt32AddWithOverflow(val, val))),
216 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
217 IsIfFalse(AllOf(CaptureEq(&branch),
218 IsBranch(IsProjection(1, CaptureEq(&add)),
219 graph()->start()))))));
220 }
221
222
TARGET_TEST_F(ChangeLowering32Test,ChangeTaggedToFloat64)223 TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToFloat64) {
224 STATIC_ASSERT(kSmiTag == 0);
225 STATIC_ASSERT(kSmiTagSize == 1);
226
227 Node* val = Parameter(0);
228 Node* node = graph()->NewNode(simplified()->ChangeTaggedToFloat64(), val);
229 Reduction reduction = Reduce(node);
230 ASSERT_TRUE(reduction.Changed());
231
232 Node* phi = reduction.replacement();
233 Capture<Node*> branch, if_true;
234 EXPECT_THAT(
235 phi,
236 IsPhi(
237 kMachFloat64,
238 IsLoad(kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()),
239 IsControlEffect(CaptureEq(&if_true))),
240 IsChangeInt32ToFloat64(
241 IsWord32Sar(val, IsInt32Constant(SmiShiftAmount()))),
242 IsMerge(
243 AllOf(CaptureEq(&if_true),
244 IsIfTrue(AllOf(
245 CaptureEq(&branch),
246 IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)),
247 graph()->start())))),
248 IsIfFalse(CaptureEq(&branch)))));
249 }
250
251
TARGET_TEST_F(ChangeLowering32Test,ChangeTaggedToInt32)252 TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToInt32) {
253 STATIC_ASSERT(kSmiTag == 0);
254 STATIC_ASSERT(kSmiTagSize == 1);
255
256 Node* val = Parameter(0);
257 Node* node = graph()->NewNode(simplified()->ChangeTaggedToInt32(), val);
258 Reduction reduction = Reduce(node);
259 ASSERT_TRUE(reduction.Changed());
260
261 Node* phi = reduction.replacement();
262 Capture<Node*> branch, if_true;
263 EXPECT_THAT(
264 phi,
265 IsPhi(kMachInt32,
266 IsChangeFloat64ToInt32(IsLoad(
267 kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()),
268 IsControlEffect(CaptureEq(&if_true)))),
269 IsWord32Sar(val, IsInt32Constant(SmiShiftAmount())),
270 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
271 IsIfFalse(AllOf(
272 CaptureEq(&branch),
273 IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)),
274 graph()->start()))))));
275 }
276
277
TARGET_TEST_F(ChangeLowering32Test,ChangeTaggedToUint32)278 TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToUint32) {
279 STATIC_ASSERT(kSmiTag == 0);
280 STATIC_ASSERT(kSmiTagSize == 1);
281
282 Node* val = Parameter(0);
283 Node* node = graph()->NewNode(simplified()->ChangeTaggedToUint32(), val);
284 Reduction reduction = Reduce(node);
285 ASSERT_TRUE(reduction.Changed());
286
287 Node* phi = reduction.replacement();
288 Capture<Node*> branch, if_true;
289 EXPECT_THAT(
290 phi,
291 IsPhi(kMachUint32,
292 IsChangeFloat64ToUint32(IsLoad(
293 kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()),
294 IsControlEffect(CaptureEq(&if_true)))),
295 IsWord32Sar(val, IsInt32Constant(SmiShiftAmount())),
296 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
297 IsIfFalse(AllOf(
298 CaptureEq(&branch),
299 IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)),
300 graph()->start()))))));
301 }
302
303
TARGET_TEST_F(ChangeLowering32Test,ChangeUint32ToTagged)304 TARGET_TEST_F(ChangeLowering32Test, ChangeUint32ToTagged) {
305 STATIC_ASSERT(kSmiTag == 0);
306 STATIC_ASSERT(kSmiTagSize == 1);
307
308 Node* val = Parameter(0);
309 Node* node = graph()->NewNode(simplified()->ChangeUint32ToTagged(), val);
310 Reduction reduction = Reduce(node);
311 ASSERT_TRUE(reduction.Changed());
312
313 Node* phi = reduction.replacement();
314 Capture<Node*> branch, heap_number, if_false;
315 EXPECT_THAT(
316 phi,
317 IsPhi(
318 kMachAnyTagged, IsWord32Shl(val, IsInt32Constant(SmiShiftAmount())),
319 IsFinish(
320 AllOf(CaptureEq(&heap_number),
321 IsAllocateHeapNumber(_, CaptureEq(&if_false))),
322 IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number),
323 IsInt32Constant(HeapNumberValueOffset()),
324 IsChangeUint32ToFloat64(val), CaptureEq(&heap_number),
325 CaptureEq(&if_false))),
326 IsMerge(
327 IsIfTrue(AllOf(CaptureEq(&branch),
328 IsBranch(IsUint32LessThanOrEqual(
329 val, IsInt32Constant(SmiMaxValue())),
330 graph()->start()))),
331 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch))))));
332 }
333
334
335 // -----------------------------------------------------------------------------
336 // 64-bit
337
338
339 class ChangeLowering64Test : public ChangeLoweringTest {
340 public:
~ChangeLowering64Test()341 virtual ~ChangeLowering64Test() {}
WordRepresentation() const342 virtual MachineType WordRepresentation() const FINAL OVERRIDE {
343 return kRepWord64;
344 }
345 };
346
347
TARGET_TEST_F(ChangeLowering64Test,ChangeInt32ToTagged)348 TARGET_TEST_F(ChangeLowering64Test, ChangeInt32ToTagged) {
349 Node* val = Parameter(0);
350 Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), val);
351 Reduction reduction = Reduce(node);
352 ASSERT_TRUE(reduction.Changed());
353
354 EXPECT_THAT(reduction.replacement(),
355 IsWord64Shl(IsChangeInt32ToInt64(val),
356 IsInt32Constant(SmiShiftAmount())));
357 }
358
359
TARGET_TEST_F(ChangeLowering64Test,ChangeTaggedToFloat64)360 TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToFloat64) {
361 STATIC_ASSERT(kSmiTag == 0);
362 STATIC_ASSERT(kSmiTagSize == 1);
363
364 Node* val = Parameter(0);
365 Node* node = graph()->NewNode(simplified()->ChangeTaggedToFloat64(), val);
366 Reduction reduction = Reduce(node);
367 ASSERT_TRUE(reduction.Changed());
368
369 Node* phi = reduction.replacement();
370 Capture<Node*> branch, if_true;
371 EXPECT_THAT(
372 phi,
373 IsPhi(
374 kMachFloat64,
375 IsLoad(kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()),
376 IsControlEffect(CaptureEq(&if_true))),
377 IsChangeInt32ToFloat64(IsTruncateInt64ToInt32(
378 IsWord64Sar(val, IsInt32Constant(SmiShiftAmount())))),
379 IsMerge(
380 AllOf(CaptureEq(&if_true),
381 IsIfTrue(AllOf(
382 CaptureEq(&branch),
383 IsBranch(IsWord64And(val, IsInt32Constant(kSmiTagMask)),
384 graph()->start())))),
385 IsIfFalse(CaptureEq(&branch)))));
386 }
387
388
TARGET_TEST_F(ChangeLowering64Test,ChangeTaggedToInt32)389 TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToInt32) {
390 STATIC_ASSERT(kSmiTag == 0);
391 STATIC_ASSERT(kSmiTagSize == 1);
392
393 Node* val = Parameter(0);
394 Node* node = graph()->NewNode(simplified()->ChangeTaggedToInt32(), val);
395 Reduction reduction = Reduce(node);
396 ASSERT_TRUE(reduction.Changed());
397
398 Node* phi = reduction.replacement();
399 Capture<Node*> branch, if_true;
400 EXPECT_THAT(
401 phi,
402 IsPhi(kMachInt32,
403 IsChangeFloat64ToInt32(IsLoad(
404 kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()),
405 IsControlEffect(CaptureEq(&if_true)))),
406 IsTruncateInt64ToInt32(
407 IsWord64Sar(val, IsInt32Constant(SmiShiftAmount()))),
408 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
409 IsIfFalse(AllOf(
410 CaptureEq(&branch),
411 IsBranch(IsWord64And(val, IsInt32Constant(kSmiTagMask)),
412 graph()->start()))))));
413 }
414
415
TARGET_TEST_F(ChangeLowering64Test,ChangeTaggedToUint32)416 TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToUint32) {
417 STATIC_ASSERT(kSmiTag == 0);
418 STATIC_ASSERT(kSmiTagSize == 1);
419
420 Node* val = Parameter(0);
421 Node* node = graph()->NewNode(simplified()->ChangeTaggedToUint32(), val);
422 Reduction reduction = Reduce(node);
423 ASSERT_TRUE(reduction.Changed());
424
425 Node* phi = reduction.replacement();
426 Capture<Node*> branch, if_true;
427 EXPECT_THAT(
428 phi,
429 IsPhi(kMachUint32,
430 IsChangeFloat64ToUint32(IsLoad(
431 kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()),
432 IsControlEffect(CaptureEq(&if_true)))),
433 IsTruncateInt64ToInt32(
434 IsWord64Sar(val, IsInt32Constant(SmiShiftAmount()))),
435 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
436 IsIfFalse(AllOf(
437 CaptureEq(&branch),
438 IsBranch(IsWord64And(val, IsInt32Constant(kSmiTagMask)),
439 graph()->start()))))));
440 }
441
442
TARGET_TEST_F(ChangeLowering64Test,ChangeUint32ToTagged)443 TARGET_TEST_F(ChangeLowering64Test, ChangeUint32ToTagged) {
444 STATIC_ASSERT(kSmiTag == 0);
445 STATIC_ASSERT(kSmiTagSize == 1);
446
447 Node* val = Parameter(0);
448 Node* node = graph()->NewNode(simplified()->ChangeUint32ToTagged(), val);
449 Reduction reduction = Reduce(node);
450 ASSERT_TRUE(reduction.Changed());
451
452 Node* phi = reduction.replacement();
453 Capture<Node*> branch, heap_number, if_false;
454 EXPECT_THAT(
455 phi,
456 IsPhi(
457 kMachAnyTagged, IsWord64Shl(IsChangeUint32ToUint64(val),
458 IsInt32Constant(SmiShiftAmount())),
459 IsFinish(
460 AllOf(CaptureEq(&heap_number),
461 IsAllocateHeapNumber(_, CaptureEq(&if_false))),
462 IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number),
463 IsInt32Constant(HeapNumberValueOffset()),
464 IsChangeUint32ToFloat64(val), CaptureEq(&heap_number),
465 CaptureEq(&if_false))),
466 IsMerge(
467 IsIfTrue(AllOf(CaptureEq(&branch),
468 IsBranch(IsUint32LessThanOrEqual(
469 val, IsInt32Constant(SmiMaxValue())),
470 graph()->start()))),
471 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch))))));
472 }
473
474 } // namespace compiler
475 } // namespace internal
476 } // namespace v8
477