LCOV - code coverage report
Current view: top level - test/cctest/compiler - codegen-tester.h (source / functions) Hit Total Coverage
Test: app.info Lines: 104 107 97.2 %
Date: 2019-04-18 Functions: 141 189 74.6 %

          Line data    Source code
       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/backend/instruction-selector.h"
       9             : #include "src/compiler/pipeline.h"
      10             : #include "src/compiler/raw-machine-assembler.h"
      11             : #include "src/optimized-compilation-info.h"
      12             : #include "src/simulator.h"
      13             : #include "test/cctest/cctest.h"
      14             : #include "test/cctest/compiler/call-tester.h"
      15             : 
      16             : namespace v8 {
      17             : namespace internal {
      18             : namespace compiler {
      19             : 
      20             : template <typename ReturnType>
      21             : class RawMachineAssemblerTester : public HandleAndZoneScope,
      22             :                                   public CallHelper<ReturnType>,
      23             :                                   public RawMachineAssembler {
      24             :  public:
      25             :   template <typename... ParamMachTypes>
      26      163868 :   explicit RawMachineAssemblerTester(ParamMachTypes... p)
      27             :       : HandleAndZoneScope(),
      28             :         CallHelper<ReturnType>(
      29             :             main_isolate(),
      30      163868 :             CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p...)),
      31             :         RawMachineAssembler(
      32             :             main_isolate(), new (main_zone()) Graph(main_zone()),
      33             :             Linkage::GetSimplifiedCDescriptor(
      34             :                 main_zone(),
      35      163868 :                 CSignature::New(main_zone(), MachineTypeForC<ReturnType>(),
      36             :                                 p...),
      37             :                 true),
      38             :             MachineType::PointerRepresentation(),
      39             :             InstructionSelector::SupportedMachineOperatorFlags(),
      40      819340 :             InstructionSelector::AlignmentRequirements()) {}
      41             : 
      42             :   template <typename... ParamMachTypes>
      43         640 :   RawMachineAssemblerTester(Code::Kind kind, ParamMachTypes... p)
      44             :       : HandleAndZoneScope(),
      45             :         CallHelper<ReturnType>(
      46             :             main_isolate(),
      47         640 :             CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p...)),
      48             :         RawMachineAssembler(
      49             :             main_isolate(), new (main_zone()) Graph(main_zone()),
      50             :             Linkage::GetSimplifiedCDescriptor(
      51             :                 main_zone(),
      52         640 :                 CSignature::New(main_zone(), MachineTypeForC<ReturnType>(),
      53             :                                 p...),
      54             :                 true),
      55             :             MachineType::PointerRepresentation(),
      56             :             InstructionSelector::SupportedMachineOperatorFlags(),
      57             :             InstructionSelector::AlignmentRequirements()),
      58        3200 :         kind_(kind) {}
      59             : 
      60      328188 :   ~RawMachineAssemblerTester() override = default;
      61             : 
      62         168 :   void CheckNumber(double expected, Object number) {
      63         336 :     CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
      64         168 :   }
      65             : 
      66          16 :   void CheckString(const char* expected, Object string) {
      67          32 :     CHECK(
      68             :         this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue(
      69             :             string));
      70          16 :   }
      71             : 
      72        9740 :   void GenerateCode() { Generate(); }
      73             : 
      74             :   Handle<Code> GetCode() {
      75             :     Generate();
      76             :     return code_.ToHandleChecked();
      77             :   }
      78             : 
      79             :  protected:
      80    35282852 :   Address Generate() override {
      81    35282852 :     if (code_.is_null()) {
      82      163612 :       Schedule* schedule = this->Export();
      83             :       auto call_descriptor = this->call_descriptor();
      84             :       Graph* graph = this->graph();
      85      490836 :       OptimizedCompilationInfo info(ArrayVector("testing"), main_zone(), kind_);
      86      163612 :       code_ = Pipeline::GenerateCodeForTesting(
      87             :           &info, main_isolate(), call_descriptor, graph,
      88             :           AssemblerOptions::Default(main_isolate()), schedule);
      89             :     }
      90    35282852 :     return this->code_.ToHandleChecked()->entry();
      91             :   }
      92             : 
      93             :  private:
      94             :   Code::Kind kind_ = Code::Kind::STUB;
      95             :   MaybeHandle<Code> code_;
      96             : };
      97             : 
      98             : template <typename ReturnType>
      99       33016 : class BufferedRawMachineAssemblerTester
     100             :     : public RawMachineAssemblerTester<int32_t> {
     101             :  public:
     102             :   template <typename... ParamMachTypes>
     103       16512 :   explicit BufferedRawMachineAssemblerTester(ParamMachTypes... p)
     104             :       : RawMachineAssemblerTester<int32_t>(
     105             :             MachineType::Pointer(), ((void)p, MachineType::Pointer())...),
     106             :         test_graph_signature_(
     107       16512 :             CSignature::New(this->main_zone(), MachineType::Int32(), p...)),
     108       33024 :         return_parameter_index_(sizeof...(p)) {
     109             :     static_assert(sizeof...(p) <= arraysize(parameter_nodes_),
     110             :                   "increase parameter_nodes_ array");
     111        2088 :     std::array<MachineType, sizeof...(p)> p_arr{{p...}};
     112        6472 :     for (size_t i = 0; i < p_arr.size(); ++i) {
     113        4384 :       parameter_nodes_[i] = Load(p_arr[i], RawMachineAssembler::Parameter(i));
     114             :     }
     115       16512 :   }
     116             : 
     117     2707520 :   Address Generate() override { return RawMachineAssemblerTester::Generate(); }
     118             : 
     119             :   // The BufferedRawMachineAssemblerTester does not pass parameters directly
     120             :   // to the constructed IR graph. Instead it passes a pointer to the parameter
     121             :   // to the IR graph, and adds Load nodes to the IR graph to load the
     122             :   // parameters from memory. Thereby it is possible to pass 64 bit parameters
     123             :   // to the IR graph.
     124             :   Node* Parameter(size_t index) {
     125             :     CHECK_GT(arraysize(parameter_nodes_), index);
     126        2096 :     return parameter_nodes_[index];
     127             :   }
     128             : 
     129             :   // The BufferedRawMachineAssemblerTester adds a Store node to the IR graph
     130             :   // to store the graph's return value in memory. The memory address for the
     131             :   // Store node is provided as a parameter. By storing the return value in
     132             :   // memory it is possible to return 64 bit values.
     133       16504 :   void Return(Node* input) {
     134             :     if (COMPRESS_POINTERS_BOOL && MachineTypeForC<ReturnType>().IsTagged()) {
     135             :       // Since we are returning values via storing to off-heap location
     136             :       // generate full-word store here.
     137             :       Store(MachineType::PointerRepresentation(),
     138             :             RawMachineAssembler::Parameter(return_parameter_index_),
     139             :             BitcastTaggedToWord(input), kNoWriteBarrier);
     140             : 
     141             :     } else {
     142       16504 :       Store(MachineTypeForC<ReturnType>().representation(),
     143       16504 :             RawMachineAssembler::Parameter(return_parameter_index_), input,
     144             :             kNoWriteBarrier);
     145             :     }
     146       16504 :     RawMachineAssembler::Return(Int32Constant(1234));
     147       16504 :   }
     148             : 
     149             :   template <typename... Params>
     150     2572272 :   ReturnType Call(Params... p) {
     151             :     uintptr_t zap_data[] = {kZapValue, kZapValue};
     152        3456 :     ReturnType return_value;
     153             :     STATIC_ASSERT(sizeof(return_value) <= sizeof(zap_data));
     154             :     MemCopy(&return_value, &zap_data, sizeof(return_value));
     155     2572272 :     CSignature::VerifyParams<Params...>(test_graph_signature_);
     156     2707520 :     CallHelper<int32_t>::Call(reinterpret_cast<void*>(&p)...,
     157             :                               reinterpret_cast<void*>(&return_value));
     158     2707520 :     return return_value;
     159             :   }
     160             : 
     161             :  private:
     162             :   CSignature* test_graph_signature_;
     163             :   Node* parameter_nodes_[4];
     164             :   uint32_t return_parameter_index_;
     165             : };
     166             : 
     167             : template <>
     168          40 : class BufferedRawMachineAssemblerTester<void>
     169             :     : public RawMachineAssemblerTester<void> {
     170             :  public:
     171             :   template <typename... ParamMachTypes>
     172          20 :   explicit BufferedRawMachineAssemblerTester(ParamMachTypes... p)
     173             :       : RawMachineAssemblerTester<void>(((void)p, MachineType::Pointer())...),
     174             :         test_graph_signature_(
     175          20 :             CSignature::New(RawMachineAssemblerTester<void>::main_zone(),
     176          40 :                             MachineType::None(), p...)) {
     177             :     static_assert(sizeof...(p) <= arraysize(parameter_nodes_),
     178             :                   "increase parameter_nodes_ array");
     179          16 :     std::array<MachineType, sizeof...(p)> p_arr{{p...}};
     180          96 :     for (size_t i = 0; i < p_arr.size(); ++i) {
     181          80 :       parameter_nodes_[i] = Load(p_arr[i], RawMachineAssembler::Parameter(i));
     182             :     }
     183          20 :   }
     184             : 
     185      236396 :   Address Generate() override { return RawMachineAssemblerTester::Generate(); }
     186             : 
     187             :   // The BufferedRawMachineAssemblerTester does not pass parameters directly
     188             :   // to the constructed IR graph. Instead it passes a pointer to the parameter
     189             :   // to the IR graph, and adds Load nodes to the IR graph to load the
     190             :   // parameters from memory. Thereby it is possible to pass 64 bit parameters
     191             :   // to the IR graph.
     192             :   Node* Parameter(size_t index) {
     193             :     CHECK_GT(arraysize(parameter_nodes_), index);
     194          16 :     return parameter_nodes_[index];
     195             :   }
     196             : 
     197             :   template <typename... Params>
     198             :   void Call(Params... p) {
     199      236392 :     CSignature::VerifyParams<Params...>(test_graph_signature_);
     200      236392 :     CallHelper<void>::Call(reinterpret_cast<void*>(&p)...);
     201             :   }
     202             : 
     203             :  private:
     204             :   CSignature* test_graph_signature_;
     205             :   Node* parameter_nodes_[4];
     206             : };
     207             : 
     208             : static const bool USE_RESULT_BUFFER = true;
     209             : static const bool USE_RETURN_REGISTER = false;
     210             : static const int32_t CHECK_VALUE = 0x99BEEDCE;
     211             : 
     212             : 
     213             : // TODO(titzer): use the C-style calling convention, or any register-based
     214             : // calling convention for binop tests.
     215             : template <typename CType, bool use_result_buffer>
     216             : class BinopTester {
     217             :  public:
     218         832 :   explicit BinopTester(RawMachineAssemblerTester<int32_t>* tester,
     219             :                        MachineType rep)
     220             :       : T(tester),
     221             :         param0(T->LoadFromPointer(&p0, rep)),
     222             :         param1(T->LoadFromPointer(&p1, rep)),
     223             :         rep(rep),
     224             :         p0(static_cast<CType>(0)),
     225             :         p1(static_cast<CType>(0)),
     226         832 :         result(static_cast<CType>(0)) {}
     227             : 
     228             :   RawMachineAssemblerTester<int32_t>* T;
     229             :   Node* param0;
     230             :   Node* param1;
     231             : 
     232      404228 :   CType call(CType a0, CType a1) {
     233     2535536 :     p0 = a0;
     234     2535536 :     p1 = a1;
     235             :     if (use_result_buffer) {
     236      808456 :       CHECK_EQ(CHECK_VALUE, T->Call());
     237      404228 :       return result;
     238             :     } else {
     239     2210040 :       return static_cast<CType>(T->Call());
     240             :     }
     241             :   }
     242             : 
     243          68 :   void AddReturn(Node* val) {
     244             :     if (use_result_buffer) {
     245         136 :       T->Store(rep.representation(), T->PointerConstant(&result),
     246          68 :                T->Int32Constant(0), val, kNoWriteBarrier);
     247          68 :       T->Return(T->Int32Constant(CHECK_VALUE));
     248             :     } else {
     249         820 :       T->Return(val);
     250             :     }
     251          68 :   }
     252             : 
     253             :   template <typename Ci, typename Cj, typename Fn>
     254             :   void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
     255             :     typename Ci::const_iterator i;
     256             :     typename Cj::const_iterator j;
     257             :     for (i = ci.begin(); i != ci.end(); ++i) {
     258             :       for (j = cj.begin(); j != cj.end(); ++j) {
     259             :         CHECK_EQ(fn(*i, *j), this->call(*i, *j));
     260             :       }
     261             :     }
     262             :   }
     263             : 
     264             :  protected:
     265             :   MachineType rep;
     266             :   CType p0;
     267             :   CType p1;
     268             :   CType result;
     269             : };
     270             : 
     271             : 
     272             : // A helper class for testing code sequences that take two int parameters and
     273             : // return an int value.
     274             : class Int32BinopTester : public BinopTester<int32_t, USE_RETURN_REGISTER> {
     275             :  public:
     276             :   explicit Int32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
     277             :       : BinopTester<int32_t, USE_RETURN_REGISTER>(tester,
     278         632 :                                                   MachineType::Int32()) {}
     279             : };
     280             : 
     281             : 
     282             : // A helper class for testing code sequences that take two int parameters and
     283             : // return an int value.
     284             : class Int64BinopTester : public BinopTester<int64_t, USE_RETURN_REGISTER> {
     285             :  public:
     286             :   explicit Int64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
     287             :       : BinopTester<int64_t, USE_RETURN_REGISTER>(tester,
     288          16 :                                                   MachineType::Int64()) {}
     289             : };
     290             : 
     291             : 
     292             : // A helper class for testing code sequences that take two uint parameters and
     293             : // return an uint value.
     294             : class Uint32BinopTester : public BinopTester<uint32_t, USE_RETURN_REGISTER> {
     295             :  public:
     296             :   explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
     297             :       : BinopTester<uint32_t, USE_RETURN_REGISTER>(tester,
     298         116 :                                                    MachineType::Uint32()) {}
     299             : 
     300             :   uint32_t call(uint32_t a0, uint32_t a1) {
     301      335476 :     p0 = a0;
     302      335476 :     p1 = a1;
     303      670952 :     return static_cast<uint32_t>(T->Call());
     304             :   }
     305             : };
     306             : 
     307             : 
     308             : // A helper class for testing code sequences that take two float parameters and
     309             : // return a float value.
     310             : class Float32BinopTester : public BinopTester<float, USE_RESULT_BUFFER> {
     311             :  public:
     312             :   explicit Float32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
     313          24 :       : BinopTester<float, USE_RESULT_BUFFER>(tester, MachineType::Float32()) {}
     314             : };
     315             : 
     316             : 
     317             : // A helper class for testing code sequences that take two double parameters and
     318             : // return a double value.
     319             : class Float64BinopTester : public BinopTester<double, USE_RESULT_BUFFER> {
     320             :  public:
     321             :   explicit Float64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
     322          44 :       : BinopTester<double, USE_RESULT_BUFFER>(tester, MachineType::Float64()) {
     323             :   }
     324             : };
     325             : 
     326             : 
     327             : // A helper class for testing code sequences that take two pointer parameters
     328             : // and return a pointer value.
     329             : // TODO(titzer): pick word size of pointers based on V8_TARGET.
     330             : template <typename Type>
     331             : class PointerBinopTester : public BinopTester<Type, USE_RETURN_REGISTER> {
     332             :  public:
     333             :   explicit PointerBinopTester(RawMachineAssemblerTester<int32_t>* tester)
     334             :       : BinopTester<Type, USE_RETURN_REGISTER>(tester, MachineType::Pointer()) {
     335             :   }
     336             : };
     337             : 
     338             : 
     339             : // A helper class for testing code sequences that take two tagged parameters and
     340             : // return a tagged value.
     341             : template <typename Type>
     342             : class TaggedBinopTester : public BinopTester<Type, USE_RETURN_REGISTER> {
     343             :  public:
     344             :   explicit TaggedBinopTester(RawMachineAssemblerTester<int32_t>* tester)
     345             :       : BinopTester<Type, USE_RETURN_REGISTER>(tester,
     346             :                                                MachineType::AnyTagged()) {}
     347             : };
     348             : 
     349             : // A helper class for testing compares. Wraps a machine opcode and provides
     350             : // evaluation routines and the operators.
     351             : class CompareWrapper {
     352             :  public:
     353         140 :   explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {}
     354             : 
     355        8184 :   Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
     356       16368 :     return m->AddNode(op(m->machine()), a, b);
     357             :   }
     358             : 
     359        8184 :   const Operator* op(MachineOperatorBuilder* machine) {
     360        8184 :     switch (opcode) {
     361             :       case IrOpcode::kWord32Equal:
     362        1632 :         return machine->Word32Equal();
     363             :       case IrOpcode::kInt32LessThan:
     364        1632 :         return machine->Int32LessThan();
     365             :       case IrOpcode::kInt32LessThanOrEqual:
     366        1632 :         return machine->Int32LessThanOrEqual();
     367             :       case IrOpcode::kUint32LessThan:
     368        1632 :         return machine->Uint32LessThan();
     369             :       case IrOpcode::kUint32LessThanOrEqual:
     370        1632 :         return machine->Uint32LessThanOrEqual();
     371             :       case IrOpcode::kFloat64Equal:
     372           8 :         return machine->Float64Equal();
     373             :       case IrOpcode::kFloat64LessThan:
     374           8 :         return machine->Float64LessThan();
     375             :       case IrOpcode::kFloat64LessThanOrEqual:
     376           8 :         return machine->Float64LessThanOrEqual();
     377             :       default:
     378           0 :         UNREACHABLE();
     379             :     }
     380             :     return nullptr;
     381             :   }
     382             : 
     383     2141164 :   bool Int32Compare(int32_t a, int32_t b) {
     384     2141164 :     switch (opcode) {
     385             :       case IrOpcode::kWord32Equal:
     386      438976 :         return a == b;
     387             :       case IrOpcode::kInt32LessThan:
     388      425536 :         return a < b;
     389             :       case IrOpcode::kInt32LessThanOrEqual:
     390      425536 :         return a <= b;
     391             :       case IrOpcode::kUint32LessThan:
     392      425560 :         return static_cast<uint32_t>(a) < static_cast<uint32_t>(b);
     393             :       case IrOpcode::kUint32LessThanOrEqual:
     394      425556 :         return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b);
     395             :       default:
     396           0 :         UNREACHABLE();
     397             :     }
     398             :     return false;
     399             :   }
     400             : 
     401       20452 :   bool Float64Compare(double a, double b) {
     402       20452 :     switch (opcode) {
     403             :       case IrOpcode::kFloat64Equal:
     404       10008 :         return a == b;
     405             :       case IrOpcode::kFloat64LessThan:
     406       10024 :         return a < b;
     407             :       case IrOpcode::kFloat64LessThanOrEqual:
     408         420 :         return a <= b;
     409             :       default:
     410           0 :         UNREACHABLE();
     411             :     }
     412             :     return false;
     413             :   }
     414             : 
     415             :   IrOpcode::Value opcode;
     416             : };
     417             : 
     418             : 
     419             : // A small closure class to generate code for a function of two inputs that
     420             : // produces a single output so that it can be used in many different contexts.
     421             : // The {expected()} method should compute the expected output for a given
     422             : // pair of inputs.
     423             : template <typename T>
     424         120 : class BinopGen {
     425             :  public:
     426             :   virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) = 0;
     427             :   virtual T expected(T a, T b) = 0;
     428         120 :   virtual ~BinopGen() = default;
     429             : };
     430             : 
     431             : // A helper class to generate various combination of input shape combinations
     432             : // and run the generated code to ensure it produces the correct results.
     433             : class Int32BinopInputShapeTester {
     434             :  public:
     435             :   explicit Int32BinopInputShapeTester(BinopGen<int32_t>* g)
     436         120 :       : gen(g), input_a(0), input_b(0) {}
     437             : 
     438             :   void TestAllInputShapes();
     439             : 
     440             :  private:
     441             :   BinopGen<int32_t>* gen;
     442             :   int32_t input_a;
     443             :   int32_t input_b;
     444             : 
     445             :   void Run(RawMachineAssemblerTester<int32_t>* m);
     446             :   void RunLeft(RawMachineAssemblerTester<int32_t>* m);
     447             :   void RunRight(RawMachineAssemblerTester<int32_t>* m);
     448             : };
     449             : }  // namespace compiler
     450             : }  // namespace internal
     451             : }  // namespace v8
     452             : 
     453             : #endif  // V8_CCTEST_COMPILER_CODEGEN_TESTER_H_

Generated by: LCOV version 1.10