Home | History | Annotate | Download | only in compiler
      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:
     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 
     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>
     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 
    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:
    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.
    178 class 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.
    188 class 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.
    205 class 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.
    216 template <typename Type>
    217 class 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.
    227 template <typename Type>
    228 class 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.
    237 class 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.
    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;
    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:
    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