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 <limits>
6 
7 #include "src/compiler/change-lowering.h"
8 #include "src/compiler/control-builders.h"
9 #include "src/compiler/generic-node-inl.h"
10 #include "src/compiler/js-graph.h"
11 #include "src/compiler/node-properties-inl.h"
12 #include "src/compiler/pipeline.h"
13 #include "src/compiler/typer.h"
14 #include "src/compiler/verifier.h"
15 #include "src/execution.h"
16 #include "src/globals.h"
17 #include "src/parser.h"
18 #include "src/rewriter.h"
19 #include "src/scopes.h"
20 #include "test/cctest/cctest.h"
21 #include "test/cctest/compiler/codegen-tester.h"
22 #include "test/cctest/compiler/graph-builder-tester.h"
23 #include "test/cctest/compiler/value-helper.h"
24 
25 using namespace v8::internal;
26 using namespace v8::internal::compiler;
27 
28 template <typename ReturnType>
29 class ChangesLoweringTester : public GraphBuilderTester<ReturnType> {
30  public:
ChangesLoweringTester(MachineType p0=kMachNone)31   explicit ChangesLoweringTester(MachineType p0 = kMachNone)
32       : GraphBuilderTester<ReturnType>(p0),
33         typer(this->zone()),
34         javascript(this->zone()),
35         jsgraph(this->graph(), this->common(), &javascript, &typer,
36                 this->machine()),
37         function(Handle<JSFunction>::null()) {}
38 
39   Typer typer;
40   JSOperatorBuilder javascript;
41   JSGraph jsgraph;
42   Handle<JSFunction> function;
43 
start()44   Node* start() { return this->graph()->start(); }
45 
46   template <typename T>
CallWithPotentialGC()47   T* CallWithPotentialGC() {
48     // TODO(titzer): we need to wrap the code in a JSFunction and call it via
49     // Execution::Call() so that the GC knows about the frame, can walk it,
50     // relocate the code object if necessary, etc.
51     // This is pretty ugly and at the least should be moved up to helpers.
52     if (function.is_null()) {
53       function =
54           v8::Utils::OpenHandle(*v8::Handle<v8::Function>::Cast(CompileRun(
55               "(function() { 'use strict'; return 2.7123; })")));
56       CompilationInfoWithZone info(function);
57       CHECK(Parser::Parse(&info));
58       info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
59       CHECK(Rewriter::Rewrite(&info));
60       CHECK(Scope::Analyze(&info));
61       CHECK_NE(NULL, info.scope());
62       Handle<ScopeInfo> scope_info =
63           ScopeInfo::Create(info.scope(), info.zone());
64       info.shared_info()->set_scope_info(*scope_info);
65       Pipeline pipeline(&info);
66       Linkage linkage(&info);
67       Handle<Code> code =
68           pipeline.GenerateCodeForMachineGraph(&linkage, this->graph());
69       CHECK(!code.is_null());
70       function->ReplaceCode(*code);
71     }
72     Handle<Object>* args = NULL;
73     MaybeHandle<Object> result =
74         Execution::Call(this->isolate(), function, factory()->undefined_value(),
75                         0, args, false);
76     return T::cast(*result.ToHandleChecked());
77   }
78 
StoreFloat64(Node * node,double * ptr)79   void StoreFloat64(Node* node, double* ptr) {
80     Node* ptr_node = this->PointerConstant(ptr);
81     this->Store(kMachFloat64, ptr_node, node);
82   }
83 
LoadInt32(int32_t * ptr)84   Node* LoadInt32(int32_t* ptr) {
85     Node* ptr_node = this->PointerConstant(ptr);
86     return this->Load(kMachInt32, ptr_node);
87   }
88 
LoadUint32(uint32_t * ptr)89   Node* LoadUint32(uint32_t* ptr) {
90     Node* ptr_node = this->PointerConstant(ptr);
91     return this->Load(kMachUint32, ptr_node);
92   }
93 
LoadFloat64(double * ptr)94   Node* LoadFloat64(double* ptr) {
95     Node* ptr_node = this->PointerConstant(ptr);
96     return this->Load(kMachFloat64, ptr_node);
97   }
98 
CheckNumber(double expected,Object * number)99   void CheckNumber(double expected, Object* number) {
100     CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
101   }
102 
BuildAndLower(const Operator * op)103   void BuildAndLower(const Operator* op) {
104     // We build a graph by hand here, because the raw machine assembler
105     // does not add the correct control and effect nodes.
106     Node* p0 = this->Parameter(0);
107     Node* change = this->graph()->NewNode(op, p0);
108     Node* ret = this->graph()->NewNode(this->common()->Return(), change,
109                                        this->start(), this->start());
110     Node* end = this->graph()->NewNode(this->common()->End(), ret);
111     this->graph()->SetEnd(end);
112     LowerChange(change);
113   }
114 
BuildStoreAndLower(const Operator * op,const Operator * store_op,void * location)115   void BuildStoreAndLower(const Operator* op, const Operator* store_op,
116                           void* location) {
117     // We build a graph by hand here, because the raw machine assembler
118     // does not add the correct control and effect nodes.
119     Node* p0 = this->Parameter(0);
120     Node* change = this->graph()->NewNode(op, p0);
121     Node* store = this->graph()->NewNode(
122         store_op, this->PointerConstant(location), this->Int32Constant(0),
123         change, this->start(), this->start());
124     Node* ret = this->graph()->NewNode(
125         this->common()->Return(), this->Int32Constant(0), store, this->start());
126     Node* end = this->graph()->NewNode(this->common()->End(), ret);
127     this->graph()->SetEnd(end);
128     LowerChange(change);
129   }
130 
BuildLoadAndLower(const Operator * op,const Operator * load_op,void * location)131   void BuildLoadAndLower(const Operator* op, const Operator* load_op,
132                          void* location) {
133     // We build a graph by hand here, because the raw machine assembler
134     // does not add the correct control and effect nodes.
135     Node* load =
136         this->graph()->NewNode(load_op, this->PointerConstant(location),
137                                this->Int32Constant(0), this->start());
138     Node* change = this->graph()->NewNode(op, load);
139     Node* ret = this->graph()->NewNode(this->common()->Return(), change,
140                                        this->start(), this->start());
141     Node* end = this->graph()->NewNode(this->common()->End(), ret);
142     this->graph()->SetEnd(end);
143     LowerChange(change);
144   }
145 
LowerChange(Node * change)146   void LowerChange(Node* change) {
147     // Run the graph reducer with changes lowering on a single node.
148     CompilationInfo info(this->isolate(), this->zone());
149     Linkage linkage(&info);
150     ChangeLowering lowering(&jsgraph, &linkage);
151     GraphReducer reducer(this->graph());
152     reducer.AddReducer(&lowering);
153     reducer.ReduceNode(change);
154     Verifier::Run(this->graph());
155   }
156 
factory()157   Factory* factory() { return this->isolate()->factory(); }
heap()158   Heap* heap() { return this->isolate()->heap(); }
159 };
160 
161 
TEST(RunChangeTaggedToInt32)162 TEST(RunChangeTaggedToInt32) {
163   // Build and lower a graph by hand.
164   ChangesLoweringTester<int32_t> t(kMachAnyTagged);
165   t.BuildAndLower(t.simplified()->ChangeTaggedToInt32());
166 
167   if (Pipeline::SupportedTarget()) {
168     FOR_INT32_INPUTS(i) {
169       int32_t input = *i;
170 
171       if (Smi::IsValid(input)) {
172         int32_t result = t.Call(Smi::FromInt(input));
173         CHECK_EQ(input, result);
174       }
175 
176       {
177         Handle<Object> number = t.factory()->NewNumber(input);
178         int32_t result = t.Call(*number);
179         CHECK_EQ(input, result);
180       }
181 
182       {
183         Handle<HeapNumber> number = t.factory()->NewHeapNumber(input);
184         int32_t result = t.Call(*number);
185         CHECK_EQ(input, result);
186       }
187     }
188   }
189 }
190 
191 
TEST(RunChangeTaggedToUint32)192 TEST(RunChangeTaggedToUint32) {
193   // Build and lower a graph by hand.
194   ChangesLoweringTester<uint32_t> t(kMachAnyTagged);
195   t.BuildAndLower(t.simplified()->ChangeTaggedToUint32());
196 
197   if (Pipeline::SupportedTarget()) {
198     FOR_UINT32_INPUTS(i) {
199       uint32_t input = *i;
200 
201       if (Smi::IsValid(input)) {
202         uint32_t result = t.Call(Smi::FromInt(input));
203         CHECK_EQ(static_cast<int32_t>(input), static_cast<int32_t>(result));
204       }
205 
206       {
207         Handle<Object> number = t.factory()->NewNumber(input);
208         uint32_t result = t.Call(*number);
209         CHECK_EQ(static_cast<int32_t>(input), static_cast<int32_t>(result));
210       }
211 
212       {
213         Handle<HeapNumber> number = t.factory()->NewHeapNumber(input);
214         uint32_t result = t.Call(*number);
215         CHECK_EQ(static_cast<int32_t>(input), static_cast<int32_t>(result));
216       }
217     }
218   }
219 }
220 
221 
TEST(RunChangeTaggedToFloat64)222 TEST(RunChangeTaggedToFloat64) {
223   ChangesLoweringTester<int32_t> t(kMachAnyTagged);
224   double result;
225 
226   t.BuildStoreAndLower(
227       t.simplified()->ChangeTaggedToFloat64(),
228       t.machine()->Store(StoreRepresentation(kMachFloat64, kNoWriteBarrier)),
229       &result);
230 
231   if (Pipeline::SupportedTarget()) {
232     FOR_INT32_INPUTS(i) {
233       int32_t input = *i;
234 
235       if (Smi::IsValid(input)) {
236         t.Call(Smi::FromInt(input));
237         CHECK_EQ(input, static_cast<int32_t>(result));
238       }
239 
240       {
241         Handle<Object> number = t.factory()->NewNumber(input);
242         t.Call(*number);
243         CHECK_EQ(input, static_cast<int32_t>(result));
244       }
245 
246       {
247         Handle<HeapNumber> number = t.factory()->NewHeapNumber(input);
248         t.Call(*number);
249         CHECK_EQ(input, static_cast<int32_t>(result));
250       }
251     }
252   }
253 
254   if (Pipeline::SupportedTarget()) {
255     FOR_FLOAT64_INPUTS(i) {
256       double input = *i;
257       {
258         Handle<Object> number = t.factory()->NewNumber(input);
259         t.Call(*number);
260         CHECK_EQ(input, result);
261       }
262 
263       {
264         Handle<HeapNumber> number = t.factory()->NewHeapNumber(input);
265         t.Call(*number);
266         CHECK_EQ(input, result);
267       }
268     }
269   }
270 }
271 
272 
TEST(RunChangeBoolToBit)273 TEST(RunChangeBoolToBit) {
274   ChangesLoweringTester<int32_t> t(kMachAnyTagged);
275   t.BuildAndLower(t.simplified()->ChangeBoolToBit());
276 
277   if (Pipeline::SupportedTarget()) {
278     Object* true_obj = t.heap()->true_value();
279     int32_t result = t.Call(true_obj);
280     CHECK_EQ(1, result);
281   }
282 
283   if (Pipeline::SupportedTarget()) {
284     Object* false_obj = t.heap()->false_value();
285     int32_t result = t.Call(false_obj);
286     CHECK_EQ(0, result);
287   }
288 }
289 
290 
TEST(RunChangeBitToBool)291 TEST(RunChangeBitToBool) {
292   ChangesLoweringTester<Object*> t(kMachInt32);
293   t.BuildAndLower(t.simplified()->ChangeBitToBool());
294 
295   if (Pipeline::SupportedTarget()) {
296     Object* result = t.Call(1);
297     Object* true_obj = t.heap()->true_value();
298     CHECK_EQ(true_obj, result);
299   }
300 
301   if (Pipeline::SupportedTarget()) {
302     Object* result = t.Call(0);
303     Object* false_obj = t.heap()->false_value();
304     CHECK_EQ(false_obj, result);
305   }
306 }
307 
308 
309 #if V8_TURBOFAN_BACKEND
310 // TODO(titzer): disabled on ARM
311 
TEST(RunChangeInt32ToTaggedSmi)312 TEST(RunChangeInt32ToTaggedSmi) {
313   ChangesLoweringTester<Object*> t;
314   int32_t input;
315   t.BuildLoadAndLower(t.simplified()->ChangeInt32ToTagged(),
316                       t.machine()->Load(kMachInt32), &input);
317 
318   if (Pipeline::SupportedTarget()) {
319     FOR_INT32_INPUTS(i) {
320       input = *i;
321       if (!Smi::IsValid(input)) continue;
322       Object* result = t.Call();
323       t.CheckNumber(static_cast<double>(input), result);
324     }
325   }
326 }
327 
328 
TEST(RunChangeUint32ToTaggedSmi)329 TEST(RunChangeUint32ToTaggedSmi) {
330   ChangesLoweringTester<Object*> t;
331   uint32_t input;
332   t.BuildLoadAndLower(t.simplified()->ChangeUint32ToTagged(),
333                       t.machine()->Load(kMachUint32), &input);
334 
335   if (Pipeline::SupportedTarget()) {
336     FOR_UINT32_INPUTS(i) {
337       input = *i;
338       if (input > static_cast<uint32_t>(Smi::kMaxValue)) continue;
339       Object* result = t.Call();
340       double expected = static_cast<double>(input);
341       t.CheckNumber(expected, result);
342     }
343   }
344 }
345 
346 
TEST(RunChangeInt32ToTagged)347 TEST(RunChangeInt32ToTagged) {
348   ChangesLoweringTester<Object*> t;
349   int32_t input;
350   t.BuildLoadAndLower(t.simplified()->ChangeInt32ToTagged(),
351                       t.machine()->Load(kMachInt32), &input);
352 
353   if (Pipeline::SupportedTarget()) {
354     for (int m = 0; m < 3; m++) {  // Try 3 GC modes.
355       FOR_INT32_INPUTS(i) {
356         if (m == 0) CcTest::heap()->EnableInlineAllocation();
357         if (m == 1) CcTest::heap()->DisableInlineAllocation();
358         if (m == 2) SimulateFullSpace(CcTest::heap()->new_space());
359 
360         input = *i;
361         Object* result = t.CallWithPotentialGC<Object>();
362         t.CheckNumber(static_cast<double>(input), result);
363       }
364     }
365   }
366 }
367 
368 
TEST(RunChangeUint32ToTagged)369 TEST(RunChangeUint32ToTagged) {
370   ChangesLoweringTester<Object*> t;
371   uint32_t input;
372   t.BuildLoadAndLower(t.simplified()->ChangeUint32ToTagged(),
373                       t.machine()->Load(kMachUint32), &input);
374 
375   if (Pipeline::SupportedTarget()) {
376     for (int m = 0; m < 3; m++) {  // Try 3 GC modes.
377       FOR_UINT32_INPUTS(i) {
378         if (m == 0) CcTest::heap()->EnableInlineAllocation();
379         if (m == 1) CcTest::heap()->DisableInlineAllocation();
380         if (m == 2) SimulateFullSpace(CcTest::heap()->new_space());
381 
382         input = *i;
383         Object* result = t.CallWithPotentialGC<Object>();
384         double expected = static_cast<double>(input);
385         t.CheckNumber(expected, result);
386       }
387     }
388   }
389 }
390 
391 
TEST(RunChangeFloat64ToTagged)392 TEST(RunChangeFloat64ToTagged) {
393   ChangesLoweringTester<Object*> t;
394   double input;
395   t.BuildLoadAndLower(t.simplified()->ChangeFloat64ToTagged(),
396                       t.machine()->Load(kMachFloat64), &input);
397 
398   if (Pipeline::SupportedTarget()) {
399     for (int m = 0; m < 3; m++) {  // Try 3 GC modes.
400       FOR_FLOAT64_INPUTS(i) {
401         if (m == 0) CcTest::heap()->EnableInlineAllocation();
402         if (m == 1) CcTest::heap()->DisableInlineAllocation();
403         if (m == 2) SimulateFullSpace(CcTest::heap()->new_space());
404 
405         input = *i;
406         Object* result = t.CallWithPotentialGC<Object>();
407         t.CheckNumber(input, result);
408       }
409     }
410   }
411 }
412 
413 #endif  // V8_TURBOFAN_BACKEND
414