Line data Source code
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 : #ifndef V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
6 : #define V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
7 :
8 : #include "src/compilation-info.h"
9 : #include "src/compiler/instruction-selector.h"
10 : #include "src/compiler/pipeline.h"
11 : #include "src/compiler/raw-machine-assembler.h"
12 : #include "src/simulator.h"
13 : #include "test/cctest/cctest.h"
14 : #include "test/cctest/compiler/call-tester.h"
15 :
16 : namespace v8 {
17 : namespace internal {
18 : namespace compiler {
19 :
20 : template <typename ReturnType>
21 : class RawMachineAssemblerTester : public HandleAndZoneScope,
22 : public CallHelper<ReturnType>,
23 : public RawMachineAssembler {
24 : public:
25 190099 : RawMachineAssemblerTester(MachineType p0 = MachineType::None(),
26 : MachineType p1 = MachineType::None(),
27 : MachineType p2 = MachineType::None(),
28 : MachineType p3 = MachineType::None(),
29 : MachineType p4 = MachineType::None())
30 : : HandleAndZoneScope(),
31 : CallHelper<ReturnType>(
32 : main_isolate(),
33 : CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p0, p1,
34 190099 : p2, p3, p4)),
35 : RawMachineAssembler(
36 : main_isolate(), new (main_zone()) Graph(main_zone()),
37 : Linkage::GetSimplifiedCDescriptor(
38 : main_zone(),
39 : CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p0,
40 190099 : p1, p2, p3, p4),
41 : true),
42 : MachineType::PointerRepresentation(),
43 : InstructionSelector::SupportedMachineOperatorFlags(),
44 1140594 : InstructionSelector::AlignmentRequirements()) {}
45 :
46 378999 : virtual ~RawMachineAssemblerTester() {}
47 :
48 250 : void CheckNumber(double expected, Object* number) {
49 500 : CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
50 250 : }
51 :
52 22 : void CheckString(const char* expected, Object* string) {
53 44 : CHECK(
54 : this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue(
55 : string));
56 22 : }
57 :
58 12183 : void GenerateCode() { Generate(); }
59 :
60 11 : Handle<Code> GetCode() {
61 11 : Generate();
62 11 : return code_.ToHandleChecked();
63 : }
64 :
65 : protected:
66 44668040 : virtual byte* Generate() {
67 44668040 : if (code_.is_null()) {
68 188750 : Schedule* schedule = this->Export();
69 : CallDescriptor* call_descriptor = this->call_descriptor();
70 : Graph* graph = this->graph();
71 : CompilationInfo info(ArrayVector("testing"), main_isolate(), main_zone(),
72 188750 : Code::STUB);
73 188750 : code_ = Pipeline::GenerateCodeForTesting(&info, call_descriptor, graph,
74 188750 : schedule);
75 : }
76 44668040 : return this->code_.ToHandleChecked()->entry();
77 : }
78 :
79 : private:
80 : MaybeHandle<Code> code_;
81 : };
82 :
83 :
84 : template <typename ReturnType>
85 6300 : class BufferedRawMachineAssemblerTester
86 : : public RawMachineAssemblerTester<int32_t> {
87 : public:
88 6300 : BufferedRawMachineAssemblerTester(MachineType p0 = MachineType::None(),
89 : MachineType p1 = MachineType::None(),
90 : MachineType p2 = MachineType::None(),
91 : MachineType p3 = MachineType::None())
92 : : BufferedRawMachineAssemblerTester(ComputeParameterCount(p0, p1, p2, p3),
93 6300 : p0, p1, p2, p3) {}
94 :
95 3277404 : virtual byte* Generate() { return RawMachineAssemblerTester::Generate(); }
96 :
97 : // The BufferedRawMachineAssemblerTester does not pass parameters directly
98 : // to the constructed IR graph. Instead it passes a pointer to the parameter
99 : // to the IR graph, and adds Load nodes to the IR graph to load the
100 : // parameters from memory. Thereby it is possible to pass 64 bit parameters
101 : // to the IR graph.
102 2734 : Node* Parameter(size_t index) {
103 2734 : CHECK_GT(4, index);
104 2734 : return parameter_nodes_[index];
105 : }
106 :
107 : // The BufferedRawMachineAssemblerTester adds a Store node to the IR graph
108 : // to store the graph's return value in memory. The memory address for the
109 : // Store node is provided as a parameter. By storing the return value in
110 : // memory it is possible to return 64 bit values.
111 6280 : void Return(Node* input) {
112 6280 : Store(MachineTypeForC<ReturnType>().representation(),
113 : RawMachineAssembler::Parameter(return_parameter_index_), input,
114 : kNoWriteBarrier);
115 6280 : RawMachineAssembler::Return(Int32Constant(1234));
116 6280 : }
117 :
118 3692 : ReturnType Call() {
119 : ReturnType return_value;
120 3692 : CSignature::VerifyParams(test_graph_signature_);
121 3692 : CallHelper<int32_t>::Call(reinterpret_cast<void*>(&return_value));
122 3692 : return return_value;
123 : }
124 :
125 : template <typename P0>
126 201488 : ReturnType Call(P0 p0) {
127 : ReturnType return_value;
128 201488 : CSignature::VerifyParams<P0>(test_graph_signature_);
129 201488 : CallHelper<int32_t>::Call(reinterpret_cast<void*>(&p0),
130 201488 : reinterpret_cast<void*>(&return_value));
131 201488 : return return_value;
132 : }
133 :
134 : template <typename P0, typename P1>
135 443682 : ReturnType Call(P0 p0, P1 p1) {
136 : ReturnType return_value;
137 443682 : CSignature::VerifyParams<P0, P1>(test_graph_signature_);
138 443682 : CallHelper<int32_t>::Call(reinterpret_cast<void*>(&p0),
139 : reinterpret_cast<void*>(&p1),
140 443682 : reinterpret_cast<void*>(&return_value));
141 443682 : return return_value;
142 : }
143 :
144 : template <typename P0, typename P1, typename P2>
145 2471078 : ReturnType Call(P0 p0, P1 p1, P2 p2) {
146 : ReturnType return_value;
147 2471078 : CSignature::VerifyParams<P0, P1, P2>(test_graph_signature_);
148 2471078 : CallHelper<int32_t>::Call(
149 : reinterpret_cast<void*>(&p0), reinterpret_cast<void*>(&p1),
150 2471078 : reinterpret_cast<void*>(&p2), reinterpret_cast<void*>(&return_value));
151 2471078 : return return_value;
152 : }
153 :
154 : template <typename P0, typename P1, typename P2, typename P3>
155 157464 : ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) {
156 : ReturnType return_value;
157 157464 : CSignature::VerifyParams<P0, P1, P2, P3>(test_graph_signature_);
158 157464 : CallHelper<int32_t>::Call(
159 : reinterpret_cast<void*>(&p0), reinterpret_cast<void*>(&p1),
160 : reinterpret_cast<void*>(&p2), reinterpret_cast<void*>(&p3),
161 157464 : reinterpret_cast<void*>(&return_value));
162 157464 : return return_value;
163 : }
164 :
165 : private:
166 6300 : BufferedRawMachineAssemblerTester(uint32_t return_parameter_index,
167 : MachineType p0, MachineType p1,
168 : MachineType p2, MachineType p3)
169 : : RawMachineAssemblerTester<int32_t>(
170 : MachineType::Pointer(),
171 : p0 == MachineType::None() ? MachineType::None()
172 : : MachineType::Pointer(),
173 : p1 == MachineType::None() ? MachineType::None()
174 : : MachineType::Pointer(),
175 : p2 == MachineType::None() ? MachineType::None()
176 : : MachineType::Pointer(),
177 : p3 == MachineType::None() ? MachineType::None()
178 : : MachineType::Pointer()),
179 : test_graph_signature_(
180 6300 : CSignature::New(main_zone(), MachineType::Int32(), p0, p1, p2, p3)),
181 31500 : return_parameter_index_(return_parameter_index) {
182 8908 : parameter_nodes_[0] = p0 == MachineType::None()
183 2608 : ? nullptr
184 : : Load(p0, RawMachineAssembler::Parameter(0));
185 6398 : parameter_nodes_[1] = p1 == MachineType::None()
186 98 : ? nullptr
187 : : Load(p1, RawMachineAssembler::Parameter(1));
188 6332 : parameter_nodes_[2] = p2 == MachineType::None()
189 32 : ? nullptr
190 : : Load(p2, RawMachineAssembler::Parameter(2));
191 6306 : parameter_nodes_[3] = p3 == MachineType::None()
192 6 : ? nullptr
193 : : Load(p3, RawMachineAssembler::Parameter(3));
194 6300 : }
195 :
196 :
197 6300 : static uint32_t ComputeParameterCount(MachineType p0, MachineType p1,
198 : MachineType p2, MachineType p3) {
199 6300 : if (p0 == MachineType::None()) {
200 : return 0;
201 : }
202 2608 : if (p1 == MachineType::None()) {
203 : return 1;
204 : }
205 98 : if (p2 == MachineType::None()) {
206 : return 2;
207 : }
208 32 : if (p3 == MachineType::None()) {
209 : return 3;
210 : }
211 6 : return 4;
212 : }
213 :
214 :
215 : CSignature* test_graph_signature_;
216 : Node* parameter_nodes_[4];
217 : uint32_t return_parameter_index_;
218 : };
219 :
220 :
221 : template <>
222 30 : class BufferedRawMachineAssemblerTester<void>
223 : : public RawMachineAssemblerTester<void> {
224 : public:
225 30 : BufferedRawMachineAssemblerTester(MachineType p0 = MachineType::None(),
226 : MachineType p1 = MachineType::None(),
227 : MachineType p2 = MachineType::None(),
228 : MachineType p3 = MachineType::None())
229 : : RawMachineAssemblerTester<void>(
230 : p0 == MachineType::None() ? MachineType::None()
231 : : MachineType::Pointer(),
232 : p1 == MachineType::None() ? MachineType::None()
233 : : MachineType::Pointer(),
234 : p2 == MachineType::None() ? MachineType::None()
235 : : MachineType::Pointer(),
236 : p3 == MachineType::None() ? MachineType::None()
237 : : MachineType::Pointer()),
238 : test_graph_signature_(
239 : CSignature::New(RawMachineAssemblerTester<void>::main_zone(),
240 150 : MachineType::None(), p0, p1, p2, p3)) {
241 : parameter_nodes_[0] = p0 == MachineType::None()
242 : ? nullptr
243 30 : : Load(p0, RawMachineAssembler::Parameter(0));
244 : parameter_nodes_[1] = p1 == MachineType::None()
245 : ? nullptr
246 30 : : Load(p1, RawMachineAssembler::Parameter(1));
247 : parameter_nodes_[2] = p2 == MachineType::None()
248 : ? nullptr
249 30 : : Load(p2, RawMachineAssembler::Parameter(2));
250 : parameter_nodes_[3] = p3 == MachineType::None()
251 : ? nullptr
252 30 : : Load(p3, RawMachineAssembler::Parameter(3));
253 30 : }
254 :
255 354594 : virtual byte* Generate() { return RawMachineAssemblerTester::Generate(); }
256 :
257 : // The BufferedRawMachineAssemblerTester does not pass parameters directly
258 : // to the constructed IR graph. Instead it passes a pointer to the parameter
259 : // to the IR graph, and adds Load nodes to the IR graph to load the
260 : // parameters from memory. Thereby it is possible to pass 64 bit parameters
261 : // to the IR graph.
262 60 : Node* Parameter(size_t index) {
263 60 : CHECK_GT(4, index);
264 60 : return parameter_nodes_[index];
265 : }
266 :
267 :
268 : void Call() {
269 6 : CSignature::VerifyParams(test_graph_signature_);
270 6 : CallHelper<void>::Call();
271 : }
272 :
273 : template <typename P0>
274 : void Call(P0 p0) {
275 294 : CSignature::VerifyParams<P0>(test_graph_signature_);
276 294 : CallHelper<void>::Call(reinterpret_cast<void*>(&p0));
277 : }
278 :
279 : template <typename P0, typename P1>
280 : void Call(P0 p0, P1 p1) {
281 78732 : CSignature::VerifyParams<P0, P1>(test_graph_signature_);
282 78732 : CallHelper<void>::Call(reinterpret_cast<void*>(&p0),
283 : reinterpret_cast<void*>(&p1));
284 : }
285 :
286 : template <typename P0, typename P1, typename P2>
287 : void Call(P0 p0, P1 p1, P2 p2) {
288 118098 : CSignature::VerifyParams<P0, P1, P2>(test_graph_signature_);
289 118098 : CallHelper<void>::Call(reinterpret_cast<void*>(&p0),
290 : reinterpret_cast<void*>(&p1),
291 : reinterpret_cast<void*>(&p2));
292 : }
293 :
294 : template <typename P0, typename P1, typename P2, typename P3>
295 : void Call(P0 p0, P1 p1, P2 p2, P3 p3) {
296 157464 : CSignature::VerifyParams<P0, P1, P2, P3>(test_graph_signature_);
297 157464 : CallHelper<void>::Call(
298 : reinterpret_cast<void*>(&p0), reinterpret_cast<void*>(&p1),
299 : reinterpret_cast<void*>(&p2), reinterpret_cast<void*>(&p3));
300 : }
301 :
302 : private:
303 : CSignature* test_graph_signature_;
304 : Node* parameter_nodes_[4];
305 : };
306 : static const bool USE_RESULT_BUFFER = true;
307 : static const bool USE_RETURN_REGISTER = false;
308 : static const int32_t CHECK_VALUE = 0x99BEEDCE;
309 :
310 :
311 : // TODO(titzer): use the C-style calling convention, or any register-based
312 : // calling convention for binop tests.
313 : template <typename CType, bool use_result_buffer>
314 : class BinopTester {
315 : public:
316 1044 : explicit BinopTester(RawMachineAssemblerTester<int32_t>* tester,
317 : MachineType rep)
318 : : T(tester),
319 : param0(T->LoadFromPointer(&p0, rep)),
320 : param1(T->LoadFromPointer(&p1, rep)),
321 : rep(rep),
322 : p0(static_cast<CType>(0)),
323 : p1(static_cast<CType>(0)),
324 1044 : result(static_cast<CType>(0)) {}
325 :
326 : RawMachineAssemblerTester<int32_t>* T;
327 : Node* param0;
328 : Node* param1;
329 :
330 505383 : CType call(CType a0, CType a1) {
331 3169634 : p0 = a0;
332 3169634 : p1 = a1;
333 : if (use_result_buffer) {
334 505383 : CHECK_EQ(CHECK_VALUE, T->Call());
335 505383 : return result;
336 : } else {
337 2664251 : return static_cast<CType>(T->Call());
338 : }
339 : }
340 :
341 87 : void AddReturn(Node* val) {
342 : if (use_result_buffer) {
343 174 : T->Store(rep.representation(), T->PointerConstant(&result),
344 : T->Int32Constant(0), val, kNoWriteBarrier);
345 87 : T->Return(T->Int32Constant(CHECK_VALUE));
346 : } else {
347 1027 : T->Return(val);
348 : }
349 87 : }
350 :
351 : template <typename Ci, typename Cj, typename Fn>
352 : void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
353 : typename Ci::const_iterator i;
354 : typename Cj::const_iterator j;
355 : for (i = ci.begin(); i != ci.end(); ++i) {
356 : for (j = cj.begin(); j != cj.end(); ++j) {
357 : CHECK_EQ(fn(*i, *j), this->call(*i, *j));
358 : }
359 : }
360 : }
361 :
362 : protected:
363 : MachineType rep;
364 : CType p0;
365 : CType p1;
366 : CType result;
367 : };
368 :
369 :
370 : // A helper class for testing code sequences that take two int parameters and
371 : // return an int value.
372 : class Int32BinopTester : public BinopTester<int32_t, USE_RETURN_REGISTER> {
373 : public:
374 : explicit Int32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
375 : : BinopTester<int32_t, USE_RETURN_REGISTER>(tester,
376 792 : MachineType::Int32()) {}
377 : };
378 :
379 :
380 : // A helper class for testing code sequences that take two int parameters and
381 : // return an int value.
382 : class Int64BinopTester : public BinopTester<int64_t, USE_RETURN_REGISTER> {
383 : public:
384 : explicit Int64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
385 : : BinopTester<int64_t, USE_RETURN_REGISTER>(tester,
386 20 : MachineType::Int64()) {}
387 : };
388 :
389 :
390 : // A helper class for testing code sequences that take two uint parameters and
391 : // return an uint value.
392 : class Uint32BinopTester : public BinopTester<uint32_t, USE_RETURN_REGISTER> {
393 : public:
394 : explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
395 : : BinopTester<uint32_t, USE_RETURN_REGISTER>(tester,
396 145 : MachineType::Uint32()) {}
397 :
398 : uint32_t call(uint32_t a0, uint32_t a1) {
399 419345 : p0 = a0;
400 419345 : p1 = a1;
401 419345 : return static_cast<uint32_t>(T->Call());
402 : }
403 : };
404 :
405 :
406 : // A helper class for testing code sequences that take two float parameters and
407 : // return a float value.
408 : class Float32BinopTester : public BinopTester<float, USE_RESULT_BUFFER> {
409 : public:
410 : explicit Float32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
411 30 : : BinopTester<float, USE_RESULT_BUFFER>(tester, MachineType::Float32()) {}
412 : };
413 :
414 :
415 : // A helper class for testing code sequences that take two double parameters and
416 : // return a double value.
417 : class Float64BinopTester : public BinopTester<double, USE_RESULT_BUFFER> {
418 : public:
419 : explicit Float64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
420 57 : : BinopTester<double, USE_RESULT_BUFFER>(tester, MachineType::Float64()) {
421 : }
422 : };
423 :
424 :
425 : // A helper class for testing code sequences that take two pointer parameters
426 : // and return a pointer value.
427 : // TODO(titzer): pick word size of pointers based on V8_TARGET.
428 : template <typename Type>
429 : class PointerBinopTester : public BinopTester<Type*, USE_RETURN_REGISTER> {
430 : public:
431 : explicit PointerBinopTester(RawMachineAssemblerTester<int32_t>* tester)
432 : : BinopTester<Type*, USE_RETURN_REGISTER>(tester,
433 : MachineType::Pointer()) {}
434 : };
435 :
436 :
437 : // A helper class for testing code sequences that take two tagged parameters and
438 : // return a tagged value.
439 : template <typename Type>
440 : class TaggedBinopTester : public BinopTester<Type*, USE_RETURN_REGISTER> {
441 : public:
442 : explicit TaggedBinopTester(RawMachineAssemblerTester<int32_t>* tester)
443 : : BinopTester<Type*, USE_RETURN_REGISTER>(tester,
444 : MachineType::AnyTagged()) {}
445 : };
446 :
447 : // A helper class for testing compares. Wraps a machine opcode and provides
448 : // evaluation routines and the operators.
449 : class CompareWrapper {
450 : public:
451 208 : explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {}
452 :
453 12276 : Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
454 24552 : return m->AddNode(op(m->machine()), a, b);
455 : }
456 :
457 12276 : const Operator* op(MachineOperatorBuilder* machine) {
458 12276 : switch (opcode) {
459 : case IrOpcode::kWord32Equal:
460 2448 : return machine->Word32Equal();
461 : case IrOpcode::kInt32LessThan:
462 2448 : return machine->Int32LessThan();
463 : case IrOpcode::kInt32LessThanOrEqual:
464 2448 : return machine->Int32LessThanOrEqual();
465 : case IrOpcode::kUint32LessThan:
466 2448 : return machine->Uint32LessThan();
467 : case IrOpcode::kUint32LessThanOrEqual:
468 2448 : return machine->Uint32LessThanOrEqual();
469 : case IrOpcode::kFloat64Equal:
470 12 : return machine->Float64Equal();
471 : case IrOpcode::kFloat64LessThan:
472 12 : return machine->Float64LessThan();
473 : case IrOpcode::kFloat64LessThanOrEqual:
474 12 : return machine->Float64LessThanOrEqual();
475 : default:
476 0 : UNREACHABLE();
477 : }
478 : return nullptr;
479 : }
480 :
481 3211746 : bool Int32Compare(int32_t a, int32_t b) {
482 3211746 : switch (opcode) {
483 : case IrOpcode::kWord32Equal:
484 658464 : return a == b;
485 : case IrOpcode::kInt32LessThan:
486 638304 : return a < b;
487 : case IrOpcode::kInt32LessThanOrEqual:
488 638304 : return a <= b;
489 : case IrOpcode::kUint32LessThan:
490 638340 : return static_cast<uint32_t>(a) < static_cast<uint32_t>(b);
491 : case IrOpcode::kUint32LessThanOrEqual:
492 638334 : return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b);
493 : default:
494 0 : UNREACHABLE();
495 : }
496 : return false;
497 : }
498 :
499 25876 : bool Float64Compare(double a, double b) {
500 25876 : switch (opcode) {
501 : case IrOpcode::kFloat64Equal:
502 12611 : return a == b;
503 : case IrOpcode::kFloat64LessThan:
504 12635 : return a < b;
505 : case IrOpcode::kFloat64LessThanOrEqual:
506 630 : return a <= b;
507 : default:
508 0 : UNREACHABLE();
509 : }
510 : return false;
511 : }
512 :
513 : IrOpcode::Value opcode;
514 : };
515 :
516 :
517 : // A small closure class to generate code for a function of two inputs that
518 : // produces a single output so that it can be used in many different contexts.
519 : // The {expected()} method should compute the expected output for a given
520 : // pair of inputs.
521 : template <typename T>
522 180 : class BinopGen {
523 : public:
524 : virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) = 0;
525 : virtual T expected(T a, T b) = 0;
526 180 : virtual ~BinopGen() {}
527 : };
528 :
529 : // A helper class to generate various combination of input shape combinations
530 : // and run the generated code to ensure it produces the correct results.
531 : class Int32BinopInputShapeTester {
532 : public:
533 : explicit Int32BinopInputShapeTester(BinopGen<int32_t>* g)
534 180 : : gen(g), input_a(0), input_b(0) {}
535 :
536 : void TestAllInputShapes();
537 :
538 : private:
539 : BinopGen<int32_t>* gen;
540 : int32_t input_a;
541 : int32_t input_b;
542 :
543 : void Run(RawMachineAssemblerTester<int32_t>* m);
544 : void RunLeft(RawMachineAssemblerTester<int32_t>* m);
545 : void RunRight(RawMachineAssemblerTester<int32_t>* m);
546 : };
547 : } // namespace compiler
548 : } // namespace internal
549 : } // namespace v8
550 :
551 : #endif // V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
|