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/v8.h"
9 
10 #include "src/compiler/pipeline.h"
11 #include "src/compiler/raw-machine-assembler.h"
12 #include "src/simulator.h"
13 #include "test/cctest/compiler/call-tester.h"
14 
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18 
19 template <typename MachineAssembler>
20 class MachineAssemblerTester : public HandleAndZoneScope,
21                                public CallHelper,
22                                public MachineAssembler {
23  public:
MachineAssemblerTester(MachineType return_type,MachineType p0,MachineType p1,MachineType p2,MachineType p3,MachineType p4)24   MachineAssemblerTester(MachineType return_type, MachineType p0,
25                          MachineType p1, MachineType p2, MachineType p3,
26                          MachineType p4)
27       : HandleAndZoneScope(),
28         CallHelper(
29             main_isolate(),
30             MakeMachineSignature(main_zone(), return_type, p0, p1, p2, p3, p4)),
31         MachineAssembler(
32             new (main_zone()) Graph(main_zone()),
33             MakeMachineSignature(main_zone(), return_type, p0, p1, p2, p3, p4),
34             kMachPtr) {}
35 
36   Node* LoadFromPointer(void* address, MachineType rep, int32_t offset = 0) {
37     return this->Load(rep, this->PointerConstant(address),
38                       this->Int32Constant(offset));
39   }
40 
StoreToPointer(void * address,MachineType rep,Node * node)41   void StoreToPointer(void* address, MachineType rep, Node* node) {
42     this->Store(rep, this->PointerConstant(address), node);
43   }
44 
StringConstant(const char * string)45   Node* StringConstant(const char* string) {
46     return this->HeapConstant(
47         this->isolate()->factory()->InternalizeUtf8String(string));
48   }
49 
CheckNumber(double expected,Object * number)50   void CheckNumber(double expected, Object* number) {
51     CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
52   }
53 
CheckString(const char * expected,Object * string)54   void CheckString(const char* expected, Object* string) {
55     CHECK(
56         this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue(
57             string));
58   }
59 
GenerateCode()60   void GenerateCode() { Generate(); }
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(graph->zone()->isolate(), graph->zone());
69       Linkage linkage(&info, call_descriptor);
70       Pipeline pipeline(&info);
71       code_ = pipeline.GenerateCodeForMachineGraph(&linkage, graph, schedule);
72     }
73     return this->code_.ToHandleChecked()->entry();
74   }
75 
76  private:
77   MaybeHandle<Code> code_;
78 };
79 
80 
81 template <typename ReturnType>
82 class RawMachineAssemblerTester
83     : public MachineAssemblerTester<RawMachineAssembler>,
84       public CallHelper2<ReturnType, RawMachineAssemblerTester<ReturnType> > {
85  public:
86   RawMachineAssemblerTester(MachineType p0 = kMachNone,
87                             MachineType p1 = kMachNone,
88                             MachineType p2 = kMachNone,
89                             MachineType p3 = kMachNone,
90                             MachineType p4 = kMachNone)
91       : MachineAssemblerTester<RawMachineAssembler>(
92             ReturnValueTraits<ReturnType>::Representation(), p0, p1, p2, p3,
93             p4) {}
94 
95   template <typename Ci, typename Fn>
Run(const Ci & ci,const Fn & fn)96   void Run(const Ci& ci, const Fn& fn) {
97     typename Ci::const_iterator i;
98     for (i = ci.begin(); i != ci.end(); ++i) {
99       CHECK_EQ(fn(*i), this->Call(*i));
100     }
101   }
102 
103   template <typename Ci, typename Cj, typename Fn>
Run(const Ci & ci,const Cj & cj,const Fn & fn)104   void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
105     typename Ci::const_iterator i;
106     typename Cj::const_iterator j;
107     for (i = ci.begin(); i != ci.end(); ++i) {
108       for (j = cj.begin(); j != cj.end(); ++j) {
109         CHECK_EQ(fn(*i, *j), this->Call(*i, *j));
110       }
111     }
112   }
113 };
114 
115 
116 static const bool USE_RESULT_BUFFER = true;
117 static const bool USE_RETURN_REGISTER = false;
118 static const int32_t CHECK_VALUE = 0x99BEEDCE;
119 
120 
121 // TODO(titzer): use the C-style calling convention, or any register-based
122 // calling convention for binop tests.
123 template <typename CType, MachineType rep, bool use_result_buffer>
124 class BinopTester {
125  public:
BinopTester(RawMachineAssemblerTester<int32_t> * tester)126   explicit BinopTester(RawMachineAssemblerTester<int32_t>* tester)
127       : T(tester),
128         param0(T->LoadFromPointer(&p0, rep)),
129         param1(T->LoadFromPointer(&p1, rep)),
130         p0(static_cast<CType>(0)),
131         p1(static_cast<CType>(0)),
132         result(static_cast<CType>(0)) {}
133 
134   RawMachineAssemblerTester<int32_t>* T;
135   Node* param0;
136   Node* param1;
137 
call(CType a0,CType a1)138   CType call(CType a0, CType a1) {
139     p0 = a0;
140     p1 = a1;
141     if (use_result_buffer) {
142       CHECK_EQ(CHECK_VALUE, T->Call());
143       return result;
144     } else {
145       return T->Call();
146     }
147   }
148 
AddReturn(Node * val)149   void AddReturn(Node* val) {
150     if (use_result_buffer) {
151       T->Store(rep, T->PointerConstant(&result), T->Int32Constant(0), val);
152       T->Return(T->Int32Constant(CHECK_VALUE));
153     } else {
154       T->Return(val);
155     }
156   }
157 
158   template <typename Ci, typename Cj, typename Fn>
Run(const Ci & ci,const Cj & cj,const Fn & fn)159   void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
160     typename Ci::const_iterator i;
161     typename Cj::const_iterator j;
162     for (i = ci.begin(); i != ci.end(); ++i) {
163       for (j = cj.begin(); j != cj.end(); ++j) {
164         CHECK_EQ(fn(*i, *j), this->call(*i, *j));
165       }
166     }
167   }
168 
169  protected:
170   CType p0;
171   CType p1;
172   CType result;
173 };
174 
175 
176 // A helper class for testing code sequences that take two int parameters and
177 // return an int value.
178 class Int32BinopTester
179     : public BinopTester<int32_t, kMachInt32, USE_RETURN_REGISTER> {
180  public:
Int32BinopTester(RawMachineAssemblerTester<int32_t> * tester)181   explicit Int32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
182       : BinopTester<int32_t, kMachInt32, USE_RETURN_REGISTER>(tester) {}
183 };
184 
185 
186 // A helper class for testing code sequences that take two uint parameters and
187 // return an uint value.
188 class Uint32BinopTester
189     : public BinopTester<uint32_t, kMachUint32, USE_RETURN_REGISTER> {
190  public:
Uint32BinopTester(RawMachineAssemblerTester<int32_t> * tester)191   explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
192       : BinopTester<uint32_t, kMachUint32, USE_RETURN_REGISTER>(tester) {}
193 
call(uint32_t a0,uint32_t a1)194   uint32_t call(uint32_t a0, uint32_t a1) {
195     p0 = a0;
196     p1 = a1;
197     return static_cast<uint32_t>(T->Call());
198   }
199 };
200 
201 
202 // A helper class for testing code sequences that take two double parameters and
203 // return a double value.
204 // TODO(titzer): figure out how to return doubles correctly on ia32.
205 class Float64BinopTester
206     : public BinopTester<double, kMachFloat64, USE_RESULT_BUFFER> {
207  public:
Float64BinopTester(RawMachineAssemblerTester<int32_t> * tester)208   explicit Float64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
209       : BinopTester<double, kMachFloat64, USE_RESULT_BUFFER>(tester) {}
210 };
211 
212 
213 // A helper class for testing code sequences that take two pointer parameters
214 // and return a pointer value.
215 // TODO(titzer): pick word size of pointers based on V8_TARGET.
216 template <typename Type>
217 class PointerBinopTester
218     : public BinopTester<Type*, kMachPtr, USE_RETURN_REGISTER> {
219  public:
PointerBinopTester(RawMachineAssemblerTester<int32_t> * tester)220   explicit PointerBinopTester(RawMachineAssemblerTester<int32_t>* tester)
221       : BinopTester<Type*, kMachPtr, USE_RETURN_REGISTER>(tester) {}
222 };
223 
224 
225 // A helper class for testing code sequences that take two tagged parameters and
226 // return a tagged value.
227 template <typename Type>
228 class TaggedBinopTester
229     : public BinopTester<Type*, kMachAnyTagged, USE_RETURN_REGISTER> {
230  public:
TaggedBinopTester(RawMachineAssemblerTester<int32_t> * tester)231   explicit TaggedBinopTester(RawMachineAssemblerTester<int32_t>* tester)
232       : BinopTester<Type*, kMachAnyTagged, USE_RETURN_REGISTER>(tester) {}
233 };
234 
235 // A helper class for testing compares. Wraps a machine opcode and provides
236 // evaluation routines and the operators.
237 class CompareWrapper {
238  public:
CompareWrapper(IrOpcode::Value op)239   explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {}
240 
MakeNode(RawMachineAssemblerTester<int32_t> * m,Node * a,Node * b)241   Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
242     return m->NewNode(op(m->machine()), a, b);
243   }
244 
op(MachineOperatorBuilder * machine)245   const Operator* op(MachineOperatorBuilder* machine) {
246     switch (opcode) {
247       case IrOpcode::kWord32Equal:
248         return machine->Word32Equal();
249       case IrOpcode::kInt32LessThan:
250         return machine->Int32LessThan();
251       case IrOpcode::kInt32LessThanOrEqual:
252         return machine->Int32LessThanOrEqual();
253       case IrOpcode::kUint32LessThan:
254         return machine->Uint32LessThan();
255       case IrOpcode::kUint32LessThanOrEqual:
256         return machine->Uint32LessThanOrEqual();
257       case IrOpcode::kFloat64Equal:
258         return machine->Float64Equal();
259       case IrOpcode::kFloat64LessThan:
260         return machine->Float64LessThan();
261       case IrOpcode::kFloat64LessThanOrEqual:
262         return machine->Float64LessThanOrEqual();
263       default:
264         UNREACHABLE();
265     }
266     return NULL;
267   }
268 
Int32Compare(int32_t a,int32_t b)269   bool Int32Compare(int32_t a, int32_t b) {
270     switch (opcode) {
271       case IrOpcode::kWord32Equal:
272         return a == b;
273       case IrOpcode::kInt32LessThan:
274         return a < b;
275       case IrOpcode::kInt32LessThanOrEqual:
276         return a <= b;
277       case IrOpcode::kUint32LessThan:
278         return static_cast<uint32_t>(a) < static_cast<uint32_t>(b);
279       case IrOpcode::kUint32LessThanOrEqual:
280         return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b);
281       default:
282         UNREACHABLE();
283     }
284     return false;
285   }
286 
Float64Compare(double a,double b)287   bool Float64Compare(double a, double b) {
288     switch (opcode) {
289       case IrOpcode::kFloat64Equal:
290         return a == b;
291       case IrOpcode::kFloat64LessThan:
292         return a < b;
293       case IrOpcode::kFloat64LessThanOrEqual:
294         return a <= b;
295       default:
296         UNREACHABLE();
297     }
298     return false;
299   }
300 
301   IrOpcode::Value opcode;
302 };
303 
304 
305 // A small closure class to generate code for a function of two inputs that
306 // produces a single output so that it can be used in many different contexts.
307 // The {expected()} method should compute the expected output for a given
308 // pair of inputs.
309 template <typename T>
310 class BinopGen {
311  public:
312   virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) = 0;
313   virtual T expected(T a, T b) = 0;
~BinopGen()314   virtual ~BinopGen() {}
315 };
316 
317 // A helper class to generate various combination of input shape combinations
318 // and run the generated code to ensure it produces the correct results.
319 class Int32BinopInputShapeTester {
320  public:
Int32BinopInputShapeTester(BinopGen<int32_t> * g)321   explicit Int32BinopInputShapeTester(BinopGen<int32_t>* g) : gen(g) {}
322 
323   void TestAllInputShapes();
324 
325  private:
326   BinopGen<int32_t>* gen;
327   int32_t input_a;
328   int32_t input_b;
329 
330   void Run(RawMachineAssemblerTester<int32_t>* m);
331   void RunLeft(RawMachineAssemblerTester<int32_t>* m);
332   void RunRight(RawMachineAssemblerTester<int32_t>* m);
333 };
334 }  // namespace compiler
335 }  // namespace internal
336 }  // namespace v8
337 
338 #endif  // V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
339