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