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