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: MachineAssemblerTester(MachineType return_type,MachineType p0,MachineType p1,MachineType p2,MachineType p3,MachineType p4)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 StoreToPointer(void * address,MachineType rep,Node * node)41 void StoreToPointer(void* address, MachineType rep, Node* node) { 42 this->Store(rep, this->PointerConstant(address), node); 43 } 44 StringConstant(const char * string)45 Node* StringConstant(const char* string) { 46 return this->HeapConstant( 47 this->isolate()->factory()->InternalizeUtf8String(string)); 48 } 49 CheckNumber(double expected,Object * number)50 void CheckNumber(double expected, Object* number) { 51 CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number)); 52 } 53 CheckString(const char * expected,Object * string)54 void CheckString(const char* expected, Object* string) { 55 CHECK( 56 this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue( 57 string)); 58 } 59 GenerateCode()60 void GenerateCode() { Generate(); } 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(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> Run(const Ci & ci,const Fn & 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> Run(const Ci & ci,const Cj & cj,const Fn & 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: BinopTester(RawMachineAssemblerTester<int32_t> * tester)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 call(CType a0,CType a1)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 AddReturn(Node * val)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> Run(const Ci & ci,const Cj & cj,const Fn & 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: Int32BinopTester(RawMachineAssemblerTester<int32_t> * tester)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: Uint32BinopTester(RawMachineAssemblerTester<int32_t> * tester)191 explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester) 192 : BinopTester<uint32_t, kMachUint32, USE_RETURN_REGISTER>(tester) {} 193 call(uint32_t a0,uint32_t a1)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: Float64BinopTester(RawMachineAssemblerTester<int32_t> * tester)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: PointerBinopTester(RawMachineAssemblerTester<int32_t> * tester)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: TaggedBinopTester(RawMachineAssemblerTester<int32_t> * tester)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: CompareWrapper(IrOpcode::Value op)239 explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {} 240 MakeNode(RawMachineAssemblerTester<int32_t> * m,Node * a,Node * b)241 Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) { 242 return m->NewNode(op(m->machine()), a, b); 243 } 244 op(MachineOperatorBuilder * machine)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 Int32Compare(int32_t a,int32_t b)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 Float64Compare(double a,double b)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; ~BinopGen()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: Int32BinopInputShapeTester(BinopGen<int32_t> * g)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