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