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
15namespace v8 {
16namespace internal {
17namespace compiler {
18
19template <typename MachineAssembler>
20class MachineAssemblerTester : public HandleAndZoneScope,
21                               public CallHelper,
22                               public MachineAssembler {
23 public:
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
41  void StoreToPointer(void* address, MachineType rep, Node* node) {
42    this->Store(rep, this->PointerConstant(address), node);
43  }
44
45  Node* StringConstant(const char* string) {
46    return this->HeapConstant(
47        this->isolate()->factory()->InternalizeUtf8String(string));
48  }
49
50  void CheckNumber(double expected, Object* number) {
51    CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
52  }
53
54  void CheckString(const char* expected, Object* string) {
55    CHECK(
56        this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue(
57            string));
58  }
59
60  void GenerateCode() { Generate(); }
61
62 protected:
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
81template <typename ReturnType>
82class 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>
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>
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
116static const bool USE_RESULT_BUFFER = true;
117static const bool USE_RETURN_REGISTER = false;
118static 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.
123template <typename CType, MachineType rep, bool use_result_buffer>
124class BinopTester {
125 public:
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
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
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>
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.
178class Int32BinopTester
179    : public BinopTester<int32_t, kMachInt32, USE_RETURN_REGISTER> {
180 public:
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.
188class Uint32BinopTester
189    : public BinopTester<uint32_t, kMachUint32, USE_RETURN_REGISTER> {
190 public:
191  explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
192      : BinopTester<uint32_t, kMachUint32, USE_RETURN_REGISTER>(tester) {}
193
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.
205class Float64BinopTester
206    : public BinopTester<double, kMachFloat64, USE_RESULT_BUFFER> {
207 public:
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.
216template <typename Type>
217class PointerBinopTester
218    : public BinopTester<Type*, kMachPtr, USE_RETURN_REGISTER> {
219 public:
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.
227template <typename Type>
228class TaggedBinopTester
229    : public BinopTester<Type*, kMachAnyTagged, USE_RETURN_REGISTER> {
230 public:
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.
237class CompareWrapper {
238 public:
239  explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {}
240
241  Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
242    return m->NewNode(op(m->machine()), a, b);
243  }
244
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
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
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.
309template <typename T>
310class 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;
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.
319class Int32BinopInputShapeTester {
320 public:
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