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
14using testing::_;
15using testing::AllOf;
16using testing::Capture;
17using testing::CaptureEq;
18
19namespace v8 {
20namespace internal {
21namespace compiler {
22
23// TODO(bmeurer): Find a new home for these functions.
24inline std::ostream& operator<<(std::ostream& os, const MachineType& type) {
25  OStringStream ost;
26  ost << type;
27  return os << ost.c_str();
28}
29
30
31class ChangeLoweringTest : public GraphTest {
32 public:
33  ChangeLoweringTest() : simplified_(zone()) {}
34  virtual ~ChangeLoweringTest() {}
35
36  virtual MachineType WordRepresentation() const = 0;
37
38 protected:
39  int HeapNumberValueOffset() const {
40    STATIC_ASSERT(HeapNumber::kValueOffset % kApiPointerSize == 0);
41    return (HeapNumber::kValueOffset / kApiPointerSize) * PointerSize() -
42           kHeapObjectTag;
43  }
44  bool Is32() const { return WordRepresentation() == kRepWord32; }
45  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  }
57  int SmiMaxValue() const { return -(SmiMinValue() + 1); }
58  int SmiMinValue() const {
59    return static_cast<int>(0xffffffffu << (SmiValueSize() - 1));
60  }
61  int SmiShiftAmount() const { return kSmiTagSize + SmiShiftSize(); }
62  int SmiShiftSize() const {
63    return Is32() ? SmiTagging<4>::SmiShiftSize()
64                  : SmiTagging<8>::SmiShiftSize();
65  }
66  int SmiValueSize() const {
67    return Is32() ? SmiTagging<4>::SmiValueSize()
68                  : SmiTagging<8>::SmiValueSize();
69  }
70
71  Node* Parameter(int32_t index = 0) {
72    return graph()->NewNode(common()->Parameter(index), graph()->start());
73  }
74
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
86  SimplifiedOperatorBuilder* simplified() { return &simplified_; }
87
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  }
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
113class ChangeLoweringCommonTest
114    : public ChangeLoweringTest,
115      public ::testing::WithParamInterface<MachineType> {
116 public:
117  virtual ~ChangeLoweringCommonTest() {}
118
119  virtual MachineType WordRepresentation() const FINAL OVERRIDE {
120    return GetParam();
121  }
122};
123
124
125TARGET_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
142TARGET_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
152TARGET_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
171TARGET_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
179INSTANTIATE_TEST_CASE_P(ChangeLoweringTest, ChangeLoweringCommonTest,
180                        ::testing::Values(kRepWord32, kRepWord64));
181
182
183// -----------------------------------------------------------------------------
184// 32-bit
185
186
187class ChangeLowering32Test : public ChangeLoweringTest {
188 public:
189  virtual ~ChangeLowering32Test() {}
190  virtual MachineType WordRepresentation() const FINAL OVERRIDE {
191    return kRepWord32;
192  }
193};
194
195
196TARGET_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
223TARGET_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
252TARGET_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
278TARGET_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
304TARGET_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
339class ChangeLowering64Test : public ChangeLoweringTest {
340 public:
341  virtual ~ChangeLowering64Test() {}
342  virtual MachineType WordRepresentation() const FINAL OVERRIDE {
343    return kRepWord64;
344  }
345};
346
347
348TARGET_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
360TARGET_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
389TARGET_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
416TARGET_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
443TARGET_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