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