codegen-tester.h 15.3 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
#define V8_CCTEST_COMPILER_CODEGEN_TESTER_H_

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 15 16 17 18 19
#include "test/cctest/compiler/call-tester.h"

namespace v8 {
namespace internal {
namespace compiler {

20 21 22 23
template <typename ReturnType>
class RawMachineAssemblerTester : public HandleAndZoneScope,
                                  public CallHelper<ReturnType>,
                                  public RawMachineAssembler {
24
 public:
25 26
  template <typename... ParamMachTypes>
  explicit RawMachineAssemblerTester(ParamMachTypes... p)
27
      : HandleAndZoneScope(),
28
        CallHelper<ReturnType>(
29
            main_isolate(),
30
            CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p...)),
31
        RawMachineAssembler(
32
            main_isolate(), new (main_zone()) Graph(main_zone()),
33 34
            Linkage::GetSimplifiedCDescriptor(
                main_zone(),
35 36
                CSignature::New(main_zone(), MachineTypeForC<ReturnType>(),
                                p...),
37
                true),
38
            MachineType::PointerRepresentation(),
39 40
            InstructionSelector::SupportedMachineOperatorFlags(),
            InstructionSelector::AlignmentRequirements()) {}
41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
  template <typename... ParamMachTypes>
  RawMachineAssemblerTester(Code::Kind kind, ParamMachTypes... p)
      : HandleAndZoneScope(),
        CallHelper<ReturnType>(
            main_isolate(),
            CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p...)),
        RawMachineAssembler(
            main_isolate(), new (main_zone()) Graph(main_zone()),
            Linkage::GetSimplifiedCDescriptor(
                main_zone(),
                CSignature::New(main_zone(), MachineTypeForC<ReturnType>(),
                                p...),
                true),
            MachineType::PointerRepresentation(),
            InstructionSelector::SupportedMachineOperatorFlags(),
            InstructionSelector::AlignmentRequirements()),
        kind_(kind) {}

60
  ~RawMachineAssemblerTester() override = default;
61

62
  void CheckNumber(double expected, Object number) {
63 64 65
    CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
  }

66
  void CheckString(const char* expected, Object string) {
67 68 69 70 71 72 73
    CHECK(
        this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue(
            string));
  }

  void GenerateCode() { Generate(); }

74 75 76 77 78
  Handle<Code> GetCode() {
    Generate();
    return code_.ToHandleChecked();
  }

79
 protected:
80
  Address Generate() override {
81 82
    if (code_.is_null()) {
      Schedule* schedule = this->Export();
83
      auto call_descriptor = this->call_descriptor();
84
      Graph* graph = this->graph();
85
      OptimizedCompilationInfo info(ArrayVector("testing"), main_zone(), kind_);
86
      code_ = Pipeline::GenerateCodeForTesting(
87 88
          &info, main_isolate(), call_descriptor, graph,
          AssemblerOptions::Default(main_isolate()), schedule);
89 90 91 92 93
    }
    return this->code_.ToHandleChecked()->entry();
  }

 private:
94
  Code::Kind kind_ = Code::Kind::STUB;
95 96 97
  MaybeHandle<Code> code_;
};

98 99 100 101
template <typename ReturnType>
class BufferedRawMachineAssemblerTester
    : public RawMachineAssemblerTester<int32_t> {
 public:
102 103 104 105 106 107 108 109 110
  template <typename... ParamMachTypes>
  explicit BufferedRawMachineAssemblerTester(ParamMachTypes... p)
      : RawMachineAssemblerTester<int32_t>(
            MachineType::Pointer(), ((void)p, MachineType::Pointer())...),
        test_graph_signature_(
            CSignature::New(this->main_zone(), MachineType::Int32(), p...)),
        return_parameter_index_(sizeof...(p)) {
    static_assert(sizeof...(p) <= arraysize(parameter_nodes_),
                  "increase parameter_nodes_ array");
111
    std::array<MachineType, sizeof...(p)> p_arr{{p...}};
112 113 114 115
    for (size_t i = 0; i < p_arr.size(); ++i) {
      parameter_nodes_[i] = Load(p_arr[i], RawMachineAssembler::Parameter(i));
    }
  }
116

117
  Address Generate() override { return RawMachineAssemblerTester::Generate(); }
118 119 120 121 122 123 124

  // The BufferedRawMachineAssemblerTester does not pass parameters directly
  // to the constructed IR graph. Instead it passes a pointer to the parameter
  // to the IR graph, and adds Load nodes to the IR graph to load the
  // parameters from memory. Thereby it is possible to pass 64 bit parameters
  // to the IR graph.
  Node* Parameter(size_t index) {
125
    CHECK_GT(arraysize(parameter_nodes_), index);
126 127 128 129 130 131 132 133
    return parameter_nodes_[index];
  }

  // The BufferedRawMachineAssemblerTester adds a Store node to the IR graph
  // to store the graph's return value in memory. The memory address for the
  // Store node is provided as a parameter. By storing the return value in
  // memory it is possible to return 64 bit values.
  void Return(Node* input) {
134 135 136 137 138 139 140 141 142 143 144 145
    if (COMPRESS_POINTERS_BOOL && MachineTypeForC<ReturnType>().IsTagged()) {
      // Since we are returning values via storing to off-heap location
      // generate full-word store here.
      Store(MachineType::PointerRepresentation(),
            RawMachineAssembler::Parameter(return_parameter_index_),
            BitcastTaggedToWord(input), kNoWriteBarrier);

    } else {
      Store(MachineTypeForC<ReturnType>().representation(),
            RawMachineAssembler::Parameter(return_parameter_index_), input,
            kNoWriteBarrier);
    }
146 147 148
    RawMachineAssembler::Return(Int32Constant(1234));
  }

149 150
  template <typename... Params>
  ReturnType Call(Params... p) {
151
    uintptr_t zap_data[] = {kZapValue, kZapValue};
152
    ReturnType return_value;
153 154
    STATIC_ASSERT(sizeof(return_value) <= sizeof(zap_data));
    MemCopy(&return_value, &zap_data, sizeof(return_value));
155 156
    CSignature::VerifyParams<Params...>(test_graph_signature_);
    CallHelper<int32_t>::Call(reinterpret_cast<void*>(&p)...,
157 158 159 160 161 162 163 164 165 166 167 168 169 170
                              reinterpret_cast<void*>(&return_value));
    return return_value;
  }

 private:
  CSignature* test_graph_signature_;
  Node* parameter_nodes_[4];
  uint32_t return_parameter_index_;
};

template <>
class BufferedRawMachineAssemblerTester<void>
    : public RawMachineAssemblerTester<void> {
 public:
171 172 173
  template <typename... ParamMachTypes>
  explicit BufferedRawMachineAssemblerTester(ParamMachTypes... p)
      : RawMachineAssemblerTester<void>(((void)p, MachineType::Pointer())...),
174 175
        test_graph_signature_(
            CSignature::New(RawMachineAssemblerTester<void>::main_zone(),
176 177 178
                            MachineType::None(), p...)) {
    static_assert(sizeof...(p) <= arraysize(parameter_nodes_),
                  "increase parameter_nodes_ array");
179
    std::array<MachineType, sizeof...(p)> p_arr{{p...}};
180 181 182
    for (size_t i = 0; i < p_arr.size(); ++i) {
      parameter_nodes_[i] = Load(p_arr[i], RawMachineAssembler::Parameter(i));
    }
183 184
  }

185
  Address Generate() override { return RawMachineAssemblerTester::Generate(); }
186

187 188 189 190 191 192
  // The BufferedRawMachineAssemblerTester does not pass parameters directly
  // to the constructed IR graph. Instead it passes a pointer to the parameter
  // to the IR graph, and adds Load nodes to the IR graph to load the
  // parameters from memory. Thereby it is possible to pass 64 bit parameters
  // to the IR graph.
  Node* Parameter(size_t index) {
193
    CHECK_GT(arraysize(parameter_nodes_), index);
194 195 196
    return parameter_nodes_[index];
  }

197 198 199 200
  template <typename... Params>
  void Call(Params... p) {
    CSignature::VerifyParams<Params...>(test_graph_signature_);
    CallHelper<void>::Call(reinterpret_cast<void*>(&p)...);
201 202 203 204 205 206
  }

 private:
  CSignature* test_graph_signature_;
  Node* parameter_nodes_[4];
};
207

208 209
static const bool USE_RESULT_BUFFER = true;
static const bool USE_RETURN_REGISTER = false;
210 211
static const int32_t CHECK_VALUE = 0x99BEEDCE;

212 213 214

// TODO(titzer): use the C-style calling convention, or any register-based
// calling convention for binop tests.
215
template <typename CType, bool use_result_buffer>
216 217
class BinopTester {
 public:
218 219
  explicit BinopTester(RawMachineAssemblerTester<int32_t>* tester,
                       MachineType rep)
220 221 222
      : T(tester),
        param0(T->LoadFromPointer(&p0, rep)),
        param1(T->LoadFromPointer(&p1, rep)),
223
        rep(rep),
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
        p0(static_cast<CType>(0)),
        p1(static_cast<CType>(0)),
        result(static_cast<CType>(0)) {}

  RawMachineAssemblerTester<int32_t>* T;
  Node* param0;
  Node* param1;

  CType call(CType a0, CType a1) {
    p0 = a0;
    p1 = a1;
    if (use_result_buffer) {
      CHECK_EQ(CHECK_VALUE, T->Call());
      return result;
    } else {
239
      return static_cast<CType>(T->Call());
240 241 242 243 244
    }
  }

  void AddReturn(Node* val) {
    if (use_result_buffer) {
245 246
      T->Store(rep.representation(), T->PointerConstant(&result),
               T->Int32Constant(0), val, kNoWriteBarrier);
247 248 249 250 251 252
      T->Return(T->Int32Constant(CHECK_VALUE));
    } else {
      T->Return(val);
    }
  }

253 254 255 256 257 258 259 260 261 262 263
  template <typename Ci, typename Cj, typename Fn>
  void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
    typename Ci::const_iterator i;
    typename Cj::const_iterator j;
    for (i = ci.begin(); i != ci.end(); ++i) {
      for (j = cj.begin(); j != cj.end(); ++j) {
        CHECK_EQ(fn(*i, *j), this->call(*i, *j));
      }
    }
  }

264
 protected:
265
  MachineType rep;
266 267 268 269 270 271 272 273
  CType p0;
  CType p1;
  CType result;
};


// A helper class for testing code sequences that take two int parameters and
// return an int value.
274
class Int32BinopTester : public BinopTester<int32_t, USE_RETURN_REGISTER> {
275 276
 public:
  explicit Int32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
277 278
      : BinopTester<int32_t, USE_RETURN_REGISTER>(tester,
                                                  MachineType::Int32()) {}
279 280 281
};


282 283 284 285 286 287 288 289 290 291
// A helper class for testing code sequences that take two int parameters and
// return an int value.
class Int64BinopTester : public BinopTester<int64_t, USE_RETURN_REGISTER> {
 public:
  explicit Int64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
      : BinopTester<int64_t, USE_RETURN_REGISTER>(tester,
                                                  MachineType::Int64()) {}
};


292 293
// A helper class for testing code sequences that take two uint parameters and
// return an uint value.
294
class Uint32BinopTester : public BinopTester<uint32_t, USE_RETURN_REGISTER> {
295 296
 public:
  explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
297 298
      : BinopTester<uint32_t, USE_RETURN_REGISTER>(tester,
                                                   MachineType::Uint32()) {}
299

300 301 302 303
  uint32_t call(uint32_t a0, uint32_t a1) {
    p0 = a0;
    p1 = a1;
    return static_cast<uint32_t>(T->Call());
304 305 306 307
  }
};


308 309
// A helper class for testing code sequences that take two float parameters and
// return a float value.
310
class Float32BinopTester : public BinopTester<float, USE_RESULT_BUFFER> {
311 312
 public:
  explicit Float32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
313
      : BinopTester<float, USE_RESULT_BUFFER>(tester, MachineType::Float32()) {}
314 315 316
};


317 318
// A helper class for testing code sequences that take two double parameters and
// return a double value.
319
class Float64BinopTester : public BinopTester<double, USE_RESULT_BUFFER> {
320 321
 public:
  explicit Float64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
322 323
      : BinopTester<double, USE_RESULT_BUFFER>(tester, MachineType::Float64()) {
  }
324 325 326 327 328 329 330
};


// A helper class for testing code sequences that take two pointer parameters
// and return a pointer value.
// TODO(titzer): pick word size of pointers based on V8_TARGET.
template <typename Type>
331
class PointerBinopTester : public BinopTester<Type, USE_RETURN_REGISTER> {
332 333
 public:
  explicit PointerBinopTester(RawMachineAssemblerTester<int32_t>* tester)
334 335
      : BinopTester<Type, USE_RETURN_REGISTER>(tester, MachineType::Pointer()) {
  }
336 337 338 339 340 341
};


// A helper class for testing code sequences that take two tagged parameters and
// return a tagged value.
template <typename Type>
342
class TaggedBinopTester : public BinopTester<Type, USE_RETURN_REGISTER> {
343 344
 public:
  explicit TaggedBinopTester(RawMachineAssemblerTester<int32_t>* tester)
345 346
      : BinopTester<Type, USE_RETURN_REGISTER>(tester,
                                               MachineType::AnyTagged()) {}
347 348 349 350 351 352 353 354 355
};

// A helper class for testing compares. Wraps a machine opcode and provides
// evaluation routines and the operators.
class CompareWrapper {
 public:
  explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {}

  Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
356
    return m->AddNode(op(m->machine()), a, b);
357 358
  }

359
  const Operator* op(MachineOperatorBuilder* machine) {
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
    switch (opcode) {
      case IrOpcode::kWord32Equal:
        return machine->Word32Equal();
      case IrOpcode::kInt32LessThan:
        return machine->Int32LessThan();
      case IrOpcode::kInt32LessThanOrEqual:
        return machine->Int32LessThanOrEqual();
      case IrOpcode::kUint32LessThan:
        return machine->Uint32LessThan();
      case IrOpcode::kUint32LessThanOrEqual:
        return machine->Uint32LessThanOrEqual();
      case IrOpcode::kFloat64Equal:
        return machine->Float64Equal();
      case IrOpcode::kFloat64LessThan:
        return machine->Float64LessThan();
      case IrOpcode::kFloat64LessThanOrEqual:
        return machine->Float64LessThanOrEqual();
      default:
        UNREACHABLE();
    }
380
    return nullptr;
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
  }

  bool Int32Compare(int32_t a, int32_t b) {
    switch (opcode) {
      case IrOpcode::kWord32Equal:
        return a == b;
      case IrOpcode::kInt32LessThan:
        return a < b;
      case IrOpcode::kInt32LessThanOrEqual:
        return a <= b;
      case IrOpcode::kUint32LessThan:
        return static_cast<uint32_t>(a) < static_cast<uint32_t>(b);
      case IrOpcode::kUint32LessThanOrEqual:
        return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b);
      default:
        UNREACHABLE();
    }
    return false;
  }

  bool Float64Compare(double a, double b) {
    switch (opcode) {
      case IrOpcode::kFloat64Equal:
        return a == b;
      case IrOpcode::kFloat64LessThan:
        return a < b;
      case IrOpcode::kFloat64LessThanOrEqual:
        return a <= b;
      default:
        UNREACHABLE();
    }
    return false;
  }

  IrOpcode::Value opcode;
};


// A small closure class to generate code for a function of two inputs that
// produces a single output so that it can be used in many different contexts.
// The {expected()} method should compute the expected output for a given
// pair of inputs.
template <typename T>
class BinopGen {
 public:
  virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) = 0;
  virtual T expected(T a, T b) = 0;
428
  virtual ~BinopGen() = default;
429 430 431 432 433 434
};

// A helper class to generate various combination of input shape combinations
// and run the generated code to ensure it produces the correct results.
class Int32BinopInputShapeTester {
 public:
435 436
  explicit Int32BinopInputShapeTester(BinopGen<int32_t>* g)
      : gen(g), input_a(0), input_b(0) {}
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

  void TestAllInputShapes();

 private:
  BinopGen<int32_t>* gen;
  int32_t input_a;
  int32_t input_b;

  void Run(RawMachineAssemblerTester<int32_t>* m);
  void RunLeft(RawMachineAssemblerTester<int32_t>* m);
  void RunRight(RawMachineAssemblerTester<int32_t>* m);
};
}  // namespace compiler
}  // namespace internal
}  // namespace v8

#endif  // V8_CCTEST_COMPILER_CODEGEN_TESTER_H_