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