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/instruction-selector.h" 9 #include "src/compiler/pipeline.h" 10 #include "src/compiler/raw-machine-assembler.h" 11 #include "src/simulator.h" 12 #include "test/cctest/compiler/call-tester.h" 13 14 namespace v8 { 15 namespace internal { 16 namespace compiler { 17 18 template <typename ReturnType> 19 class RawMachineAssemblerTester : public HandleAndZoneScope, 20 public CallHelper<ReturnType>, 21 public RawMachineAssembler { 22 public: 23 RawMachineAssemblerTester(MachineType p0 = MachineType::None(), 24 MachineType p1 = MachineType::None(), 25 MachineType p2 = MachineType::None(), 26 MachineType p3 = MachineType::None(), 27 MachineType p4 = MachineType::None()) HandleAndZoneScope()28 : HandleAndZoneScope(), 29 CallHelper<ReturnType>( 30 main_isolate(), 31 CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p0, p1, 32 p2, p3, p4)), 33 RawMachineAssembler( 34 main_isolate(), new (main_zone()) Graph(main_zone()), 35 Linkage::GetSimplifiedCDescriptor( 36 main_zone(), 37 CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p0, 38 p1, p2, p3, p4), 39 true), 40 MachineType::PointerRepresentation(), 41 InstructionSelector::SupportedMachineOperatorFlags()) {} 42 ~RawMachineAssemblerTester()43 virtual ~RawMachineAssemblerTester() {} 44 CheckNumber(double expected,Object * number)45 void CheckNumber(double expected, Object* number) { 46 CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number)); 47 } 48 CheckString(const char * expected,Object * string)49 void CheckString(const char* expected, Object* string) { 50 CHECK( 51 this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue( 52 string)); 53 } 54 GenerateCode()55 void GenerateCode() { Generate(); } 56 GetCode()57 Handle<Code> GetCode() { 58 Generate(); 59 return code_.ToHandleChecked(); 60 } 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(ArrayVector("testing"), main_isolate(), main_zone()); 69 code_ = Pipeline::GenerateCodeForTesting(&info, call_descriptor, graph, 70 schedule); 71 } 72 return this->code_.ToHandleChecked()->entry(); 73 } 74 75 private: 76 MaybeHandle<Code> code_; 77 }; 78 79 80 template <typename ReturnType> 81 class BufferedRawMachineAssemblerTester 82 : public RawMachineAssemblerTester<int32_t> { 83 public: 84 BufferedRawMachineAssemblerTester(MachineType p0 = MachineType::None(), 85 MachineType p1 = MachineType::None(), 86 MachineType p2 = MachineType::None(), 87 MachineType p3 = MachineType::None()) BufferedRawMachineAssemblerTester(ComputeParameterCount (p0,p1,p2,p3),p0,p1,p2,p3)88 : BufferedRawMachineAssemblerTester(ComputeParameterCount(p0, p1, p2, p3), 89 p0, p1, p2, p3) {} 90 Generate()91 virtual byte* Generate() { return RawMachineAssemblerTester::Generate(); } 92 93 // The BufferedRawMachineAssemblerTester does not pass parameters directly 94 // to the constructed IR graph. Instead it passes a pointer to the parameter 95 // to the IR graph, and adds Load nodes to the IR graph to load the 96 // parameters from memory. Thereby it is possible to pass 64 bit parameters 97 // to the IR graph. Parameter(size_t index)98 Node* Parameter(size_t index) { 99 CHECK(index < 4); 100 return parameter_nodes_[index]; 101 } 102 103 // The BufferedRawMachineAssemblerTester adds a Store node to the IR graph 104 // to store the graph's return value in memory. The memory address for the 105 // Store node is provided as a parameter. By storing the return value in 106 // memory it is possible to return 64 bit values. Return(Node * input)107 void Return(Node* input) { 108 Store(MachineTypeForC<ReturnType>().representation(), 109 RawMachineAssembler::Parameter(return_parameter_index_), input, 110 kNoWriteBarrier); 111 RawMachineAssembler::Return(Int32Constant(1234)); 112 } 113 Call()114 ReturnType Call() { 115 ReturnType return_value; 116 CSignature::VerifyParams(test_graph_signature_); 117 CallHelper<int32_t>::Call(reinterpret_cast<void*>(&return_value)); 118 return return_value; 119 } 120 121 template <typename P0> Call(P0 p0)122 ReturnType Call(P0 p0) { 123 ReturnType return_value; 124 CSignature::VerifyParams<P0>(test_graph_signature_); 125 CallHelper<int32_t>::Call(reinterpret_cast<void*>(&p0), 126 reinterpret_cast<void*>(&return_value)); 127 return return_value; 128 } 129 130 template <typename P0, typename P1> Call(P0 p0,P1 p1)131 ReturnType Call(P0 p0, P1 p1) { 132 ReturnType return_value; 133 CSignature::VerifyParams<P0, P1>(test_graph_signature_); 134 CallHelper<int32_t>::Call(reinterpret_cast<void*>(&p0), 135 reinterpret_cast<void*>(&p1), 136 reinterpret_cast<void*>(&return_value)); 137 return return_value; 138 } 139 140 template <typename P0, typename P1, typename P2> Call(P0 p0,P1 p1,P2 p2)141 ReturnType Call(P0 p0, P1 p1, P2 p2) { 142 ReturnType return_value; 143 CSignature::VerifyParams<P0, P1, P2>(test_graph_signature_); 144 CallHelper<int32_t>::Call( 145 reinterpret_cast<void*>(&p0), reinterpret_cast<void*>(&p1), 146 reinterpret_cast<void*>(&p2), reinterpret_cast<void*>(&return_value)); 147 return return_value; 148 } 149 150 template <typename P0, typename P1, typename P2, typename P3> Call(P0 p0,P1 p1,P2 p2,P3 p3)151 ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) { 152 ReturnType return_value; 153 CSignature::VerifyParams<P0, P1, P2, P3>(test_graph_signature_); 154 CallHelper<int32_t>::Call( 155 reinterpret_cast<void*>(&p0), reinterpret_cast<void*>(&p1), 156 reinterpret_cast<void*>(&p2), reinterpret_cast<void*>(&p3), 157 reinterpret_cast<void*>(&return_value)); 158 return return_value; 159 } 160 161 private: BufferedRawMachineAssemblerTester(uint32_t return_parameter_index,MachineType p0,MachineType p1,MachineType p2,MachineType p3)162 BufferedRawMachineAssemblerTester(uint32_t return_parameter_index, 163 MachineType p0, MachineType p1, 164 MachineType p2, MachineType p3) 165 : RawMachineAssemblerTester<int32_t>( 166 MachineType::Pointer(), 167 p0 == MachineType::None() ? MachineType::None() 168 : MachineType::Pointer(), 169 p1 == MachineType::None() ? MachineType::None() 170 : MachineType::Pointer(), 171 p2 == MachineType::None() ? MachineType::None() 172 : MachineType::Pointer(), 173 p3 == MachineType::None() ? MachineType::None() 174 : MachineType::Pointer()), 175 test_graph_signature_( 176 CSignature::New(main_zone(), MachineType::Int32(), p0, p1, p2, p3)), 177 return_parameter_index_(return_parameter_index) { 178 parameter_nodes_[0] = p0 == MachineType::None() 179 ? nullptr 180 : Load(p0, RawMachineAssembler::Parameter(0)); 181 parameter_nodes_[1] = p1 == MachineType::None() 182 ? nullptr 183 : Load(p1, RawMachineAssembler::Parameter(1)); 184 parameter_nodes_[2] = p2 == MachineType::None() 185 ? nullptr 186 : Load(p2, RawMachineAssembler::Parameter(2)); 187 parameter_nodes_[3] = p3 == MachineType::None() 188 ? nullptr 189 : Load(p3, RawMachineAssembler::Parameter(3)); 190 } 191 192 ComputeParameterCount(MachineType p0,MachineType p1,MachineType p2,MachineType p3)193 static uint32_t ComputeParameterCount(MachineType p0, MachineType p1, 194 MachineType p2, MachineType p3) { 195 if (p0 == MachineType::None()) { 196 return 0; 197 } 198 if (p1 == MachineType::None()) { 199 return 1; 200 } 201 if (p2 == MachineType::None()) { 202 return 2; 203 } 204 if (p3 == MachineType::None()) { 205 return 3; 206 } 207 return 4; 208 } 209 210 211 CSignature* test_graph_signature_; 212 Node* parameter_nodes_[4]; 213 uint32_t return_parameter_index_; 214 }; 215 216 217 template <> 218 class BufferedRawMachineAssemblerTester<void> 219 : public RawMachineAssemblerTester<void> { 220 public: 221 BufferedRawMachineAssemblerTester(MachineType p0 = MachineType::None(), 222 MachineType p1 = MachineType::None(), 223 MachineType p2 = MachineType::None(), 224 MachineType p3 = MachineType::None()) 225 : RawMachineAssemblerTester<void>( 226 p0 == MachineType::None() ? MachineType::None() 227 : MachineType::Pointer(), 228 p1 == MachineType::None() ? MachineType::None() 229 : MachineType::Pointer(), 230 p2 == MachineType::None() ? MachineType::None() 231 : MachineType::Pointer(), 232 p3 == MachineType::None() ? MachineType::None() 233 : MachineType::Pointer()), 234 test_graph_signature_( 235 CSignature::New(RawMachineAssemblerTester<void>::main_zone(), 236 MachineType::None(), p0, p1, p2, p3)) { 237 parameter_nodes_[0] = p0 == MachineType::None() 238 ? nullptr 239 : Load(p0, RawMachineAssembler::Parameter(0)); 240 parameter_nodes_[1] = p1 == MachineType::None() 241 ? nullptr 242 : Load(p1, RawMachineAssembler::Parameter(1)); 243 parameter_nodes_[2] = p2 == MachineType::None() 244 ? nullptr 245 : Load(p2, RawMachineAssembler::Parameter(2)); 246 parameter_nodes_[3] = p3 == MachineType::None() 247 ? nullptr 248 : Load(p3, RawMachineAssembler::Parameter(3)); 249 } 250 Generate()251 virtual byte* Generate() { return RawMachineAssemblerTester::Generate(); } 252 253 // The BufferedRawMachineAssemblerTester does not pass parameters directly 254 // to the constructed IR graph. Instead it passes a pointer to the parameter 255 // to the IR graph, and adds Load nodes to the IR graph to load the 256 // parameters from memory. Thereby it is possible to pass 64 bit parameters 257 // to the IR graph. Parameter(size_t index)258 Node* Parameter(size_t index) { 259 CHECK(index < 4); 260 return parameter_nodes_[index]; 261 } 262 263 Call()264 void Call() { 265 CSignature::VerifyParams(test_graph_signature_); 266 CallHelper<void>::Call(); 267 } 268 269 template <typename P0> Call(P0 p0)270 void Call(P0 p0) { 271 CSignature::VerifyParams<P0>(test_graph_signature_); 272 CallHelper<void>::Call(reinterpret_cast<void*>(&p0)); 273 } 274 275 template <typename P0, typename P1> Call(P0 p0,P1 p1)276 void Call(P0 p0, P1 p1) { 277 CSignature::VerifyParams<P0, P1>(test_graph_signature_); 278 CallHelper<void>::Call(reinterpret_cast<void*>(&p0), 279 reinterpret_cast<void*>(&p1)); 280 } 281 282 template <typename P0, typename P1, typename P2> Call(P0 p0,P1 p1,P2 p2)283 void Call(P0 p0, P1 p1, P2 p2) { 284 CSignature::VerifyParams<P0, P1, P2>(test_graph_signature_); 285 CallHelper<void>::Call(reinterpret_cast<void*>(&p0), 286 reinterpret_cast<void*>(&p1), 287 reinterpret_cast<void*>(&p2)); 288 } 289 290 template <typename P0, typename P1, typename P2, typename P3> Call(P0 p0,P1 p1,P2 p2,P3 p3)291 void Call(P0 p0, P1 p1, P2 p2, P3 p3) { 292 CSignature::VerifyParams<P0, P1, P2, P3>(test_graph_signature_); 293 CallHelper<void>::Call( 294 reinterpret_cast<void*>(&p0), reinterpret_cast<void*>(&p1), 295 reinterpret_cast<void*>(&p2), reinterpret_cast<void*>(&p3)); 296 } 297 298 private: 299 CSignature* test_graph_signature_; 300 Node* parameter_nodes_[4]; 301 }; 302 static const bool USE_RESULT_BUFFER = true; 303 static const bool USE_RETURN_REGISTER = false; 304 static const int32_t CHECK_VALUE = 0x99BEEDCE; 305 306 307 // TODO(titzer): use the C-style calling convention, or any register-based 308 // calling convention for binop tests. 309 template <typename CType, bool use_result_buffer> 310 class BinopTester { 311 public: BinopTester(RawMachineAssemblerTester<int32_t> * tester,MachineType rep)312 explicit BinopTester(RawMachineAssemblerTester<int32_t>* tester, 313 MachineType rep) 314 : T(tester), 315 param0(T->LoadFromPointer(&p0, rep)), 316 param1(T->LoadFromPointer(&p1, rep)), 317 rep(rep), 318 p0(static_cast<CType>(0)), 319 p1(static_cast<CType>(0)), 320 result(static_cast<CType>(0)) {} 321 322 RawMachineAssemblerTester<int32_t>* T; 323 Node* param0; 324 Node* param1; 325 call(CType a0,CType a1)326 CType call(CType a0, CType a1) { 327 p0 = a0; 328 p1 = a1; 329 if (use_result_buffer) { 330 CHECK_EQ(CHECK_VALUE, T->Call()); 331 return result; 332 } else { 333 return static_cast<CType>(T->Call()); 334 } 335 } 336 AddReturn(Node * val)337 void AddReturn(Node* val) { 338 if (use_result_buffer) { 339 T->Store(rep.representation(), T->PointerConstant(&result), 340 T->Int32Constant(0), val, kNoWriteBarrier); 341 T->Return(T->Int32Constant(CHECK_VALUE)); 342 } else { 343 T->Return(val); 344 } 345 } 346 347 template <typename Ci, typename Cj, typename Fn> Run(const Ci & ci,const Cj & cj,const Fn & fn)348 void Run(const Ci& ci, const Cj& cj, const Fn& fn) { 349 typename Ci::const_iterator i; 350 typename Cj::const_iterator j; 351 for (i = ci.begin(); i != ci.end(); ++i) { 352 for (j = cj.begin(); j != cj.end(); ++j) { 353 CHECK_EQ(fn(*i, *j), this->call(*i, *j)); 354 } 355 } 356 } 357 358 protected: 359 MachineType rep; 360 CType p0; 361 CType p1; 362 CType result; 363 }; 364 365 366 // A helper class for testing code sequences that take two int parameters and 367 // return an int value. 368 class Int32BinopTester : public BinopTester<int32_t, USE_RETURN_REGISTER> { 369 public: Int32BinopTester(RawMachineAssemblerTester<int32_t> * tester)370 explicit Int32BinopTester(RawMachineAssemblerTester<int32_t>* tester) 371 : BinopTester<int32_t, USE_RETURN_REGISTER>(tester, 372 MachineType::Int32()) {} 373 }; 374 375 376 // A helper class for testing code sequences that take two int parameters and 377 // return an int value. 378 class Int64BinopTester : public BinopTester<int64_t, USE_RETURN_REGISTER> { 379 public: Int64BinopTester(RawMachineAssemblerTester<int32_t> * tester)380 explicit Int64BinopTester(RawMachineAssemblerTester<int32_t>* tester) 381 : BinopTester<int64_t, USE_RETURN_REGISTER>(tester, 382 MachineType::Int64()) {} 383 }; 384 385 386 // A helper class for testing code sequences that take two uint parameters and 387 // return an uint value. 388 class Uint32BinopTester : public BinopTester<uint32_t, USE_RETURN_REGISTER> { 389 public: Uint32BinopTester(RawMachineAssemblerTester<int32_t> * tester)390 explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester) 391 : BinopTester<uint32_t, USE_RETURN_REGISTER>(tester, 392 MachineType::Uint32()) {} 393 call(uint32_t a0,uint32_t a1)394 uint32_t call(uint32_t a0, uint32_t a1) { 395 p0 = a0; 396 p1 = a1; 397 return static_cast<uint32_t>(T->Call()); 398 } 399 }; 400 401 402 // A helper class for testing code sequences that take two float parameters and 403 // return a float value. 404 class Float32BinopTester : public BinopTester<float, USE_RESULT_BUFFER> { 405 public: Float32BinopTester(RawMachineAssemblerTester<int32_t> * tester)406 explicit Float32BinopTester(RawMachineAssemblerTester<int32_t>* tester) 407 : BinopTester<float, USE_RESULT_BUFFER>(tester, MachineType::Float32()) {} 408 }; 409 410 411 // A helper class for testing code sequences that take two double parameters and 412 // return a double value. 413 class Float64BinopTester : public BinopTester<double, USE_RESULT_BUFFER> { 414 public: Float64BinopTester(RawMachineAssemblerTester<int32_t> * tester)415 explicit Float64BinopTester(RawMachineAssemblerTester<int32_t>* tester) 416 : BinopTester<double, USE_RESULT_BUFFER>(tester, MachineType::Float64()) { 417 } 418 }; 419 420 421 // A helper class for testing code sequences that take two pointer parameters 422 // and return a pointer value. 423 // TODO(titzer): pick word size of pointers based on V8_TARGET. 424 template <typename Type> 425 class PointerBinopTester : public BinopTester<Type*, USE_RETURN_REGISTER> { 426 public: PointerBinopTester(RawMachineAssemblerTester<int32_t> * tester)427 explicit PointerBinopTester(RawMachineAssemblerTester<int32_t>* tester) 428 : BinopTester<Type*, USE_RETURN_REGISTER>(tester, 429 MachineType::Pointer()) {} 430 }; 431 432 433 // A helper class for testing code sequences that take two tagged parameters and 434 // return a tagged value. 435 template <typename Type> 436 class TaggedBinopTester : public BinopTester<Type*, USE_RETURN_REGISTER> { 437 public: TaggedBinopTester(RawMachineAssemblerTester<int32_t> * tester)438 explicit TaggedBinopTester(RawMachineAssemblerTester<int32_t>* tester) 439 : BinopTester<Type*, USE_RETURN_REGISTER>(tester, 440 MachineType::AnyTagged()) {} 441 }; 442 443 // A helper class for testing compares. Wraps a machine opcode and provides 444 // evaluation routines and the operators. 445 class CompareWrapper { 446 public: CompareWrapper(IrOpcode::Value op)447 explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {} 448 MakeNode(RawMachineAssemblerTester<int32_t> * m,Node * a,Node * b)449 Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) { 450 return m->AddNode(op(m->machine()), a, b); 451 } 452 op(MachineOperatorBuilder * machine)453 const Operator* op(MachineOperatorBuilder* machine) { 454 switch (opcode) { 455 case IrOpcode::kWord32Equal: 456 return machine->Word32Equal(); 457 case IrOpcode::kInt32LessThan: 458 return machine->Int32LessThan(); 459 case IrOpcode::kInt32LessThanOrEqual: 460 return machine->Int32LessThanOrEqual(); 461 case IrOpcode::kUint32LessThan: 462 return machine->Uint32LessThan(); 463 case IrOpcode::kUint32LessThanOrEqual: 464 return machine->Uint32LessThanOrEqual(); 465 case IrOpcode::kFloat64Equal: 466 return machine->Float64Equal(); 467 case IrOpcode::kFloat64LessThan: 468 return machine->Float64LessThan(); 469 case IrOpcode::kFloat64LessThanOrEqual: 470 return machine->Float64LessThanOrEqual(); 471 default: 472 UNREACHABLE(); 473 } 474 return NULL; 475 } 476 Int32Compare(int32_t a,int32_t b)477 bool Int32Compare(int32_t a, int32_t b) { 478 switch (opcode) { 479 case IrOpcode::kWord32Equal: 480 return a == b; 481 case IrOpcode::kInt32LessThan: 482 return a < b; 483 case IrOpcode::kInt32LessThanOrEqual: 484 return a <= b; 485 case IrOpcode::kUint32LessThan: 486 return static_cast<uint32_t>(a) < static_cast<uint32_t>(b); 487 case IrOpcode::kUint32LessThanOrEqual: 488 return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b); 489 default: 490 UNREACHABLE(); 491 } 492 return false; 493 } 494 Float64Compare(double a,double b)495 bool Float64Compare(double a, double b) { 496 switch (opcode) { 497 case IrOpcode::kFloat64Equal: 498 return a == b; 499 case IrOpcode::kFloat64LessThan: 500 return a < b; 501 case IrOpcode::kFloat64LessThanOrEqual: 502 return a <= b; 503 default: 504 UNREACHABLE(); 505 } 506 return false; 507 } 508 509 IrOpcode::Value opcode; 510 }; 511 512 513 // A small closure class to generate code for a function of two inputs that 514 // produces a single output so that it can be used in many different contexts. 515 // The {expected()} method should compute the expected output for a given 516 // pair of inputs. 517 template <typename T> 518 class BinopGen { 519 public: 520 virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) = 0; 521 virtual T expected(T a, T b) = 0; ~BinopGen()522 virtual ~BinopGen() {} 523 }; 524 525 // A helper class to generate various combination of input shape combinations 526 // and run the generated code to ensure it produces the correct results. 527 class Int32BinopInputShapeTester { 528 public: Int32BinopInputShapeTester(BinopGen<int32_t> * g)529 explicit Int32BinopInputShapeTester(BinopGen<int32_t>* g) 530 : gen(g), input_a(0), input_b(0) {} 531 532 void TestAllInputShapes(); 533 534 private: 535 BinopGen<int32_t>* gen; 536 int32_t input_a; 537 int32_t input_b; 538 539 void Run(RawMachineAssemblerTester<int32_t>* m); 540 void RunLeft(RawMachineAssemblerTester<int32_t>* m); 541 void RunRight(RawMachineAssemblerTester<int32_t>* m); 542 }; 543 } // namespace compiler 544 } // namespace internal 545 } // namespace v8 546 547 #endif // V8_CCTEST_COMPILER_CODEGEN_TESTER_H_ 548