1 // Copyright 2016 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 WASM_RUN_UTILS_H 6 #define WASM_RUN_UTILS_H 7 8 #include <stdint.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include "src/base/accounting-allocator.h" 13 #include "src/base/utils/random-number-generator.h" 14 15 #include "src/compiler/graph-visualizer.h" 16 #include "src/compiler/int64-lowering.h" 17 #include "src/compiler/js-graph.h" 18 #include "src/compiler/node.h" 19 #include "src/compiler/pipeline.h" 20 #include "src/compiler/wasm-compiler.h" 21 #include "src/compiler/zone-pool.h" 22 23 #include "src/wasm/ast-decoder.h" 24 #include "src/wasm/wasm-interpreter.h" 25 #include "src/wasm/wasm-js.h" 26 #include "src/wasm/wasm-macro-gen.h" 27 #include "src/wasm/wasm-module.h" 28 #include "src/wasm/wasm-opcodes.h" 29 30 #include "src/zone.h" 31 32 #include "test/cctest/cctest.h" 33 #include "test/cctest/compiler/call-tester.h" 34 #include "test/cctest/compiler/graph-builder-tester.h" 35 36 static const uint32_t kMaxFunctions = 10; 37 38 enum WasmExecutionMode { kExecuteInterpreted, kExecuteCompiled }; 39 40 // TODO(titzer): check traps more robustly in tests. 41 // Currently, in tests, we just return 0xdeadbeef from the function in which 42 // the trap occurs if the runtime context is not available to throw a JavaScript 43 // exception. 44 #define CHECK_TRAP32(x) \ 45 CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF) 46 #define CHECK_TRAP64(x) \ 47 CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF) 48 #define CHECK_TRAP(x) CHECK_TRAP32(x) 49 50 #define WASM_RUNNER_MAX_NUM_PARAMETERS 4 51 #define WASM_WRAPPER_RETURN_VALUE 8754 52 53 #define BUILD(r, ...) \ 54 do { \ 55 byte code[] = {__VA_ARGS__}; \ 56 r.Build(code, code + arraysize(code)); \ 57 } while (false) 58 59 namespace { 60 using namespace v8::base; 61 using namespace v8::internal; 62 using namespace v8::internal::compiler; 63 using namespace v8::internal::wasm; 64 65 const uint32_t kMaxGlobalsSize = 128; 66 67 // A helper for module environments that adds the ability to allocate memory 68 // and global variables. Contains a built-in {WasmModule} and 69 // {WasmModuleInstance}. 70 class TestingModule : public ModuleEnv { 71 public: 72 explicit TestingModule(WasmExecutionMode mode = kExecuteCompiled) 73 : execution_mode_(mode), 74 instance_(&module_), 75 isolate_(CcTest::InitIsolateOnce()), 76 global_offset(0), 77 interpreter_(mode == kExecuteInterpreted 78 ? new WasmInterpreter(&instance_, &allocator_) 79 : nullptr) { 80 module = &module_; 81 instance = &instance_; 82 instance->module = &module_; 83 instance->globals_start = global_data; 84 module_.globals_size = kMaxGlobalsSize; 85 instance->mem_start = nullptr; 86 instance->mem_size = 0; 87 origin = kWasmOrigin; 88 memset(global_data, 0, sizeof(global_data)); 89 } 90 91 ~TestingModule() { 92 if (instance->mem_start) { 93 free(instance->mem_start); 94 } 95 if (interpreter_) delete interpreter_; 96 } 97 98 byte* AddMemory(uint32_t size) { 99 CHECK_NULL(instance->mem_start); 100 CHECK_EQ(0, instance->mem_size); 101 instance->mem_start = reinterpret_cast<byte*>(malloc(size)); 102 CHECK(instance->mem_start); 103 memset(instance->mem_start, 0, size); 104 instance->mem_size = size; 105 return raw_mem_start<byte>(); 106 } 107 108 template <typename T> 109 T* AddMemoryElems(uint32_t count) { 110 AddMemory(count * sizeof(T)); 111 return raw_mem_start<T>(); 112 } 113 114 template <typename T> 115 T* AddGlobal(MachineType mem_type) { 116 const WasmGlobal* global = AddGlobal(mem_type); 117 return reinterpret_cast<T*>(instance->globals_start + global->offset); 118 } 119 120 byte AddSignature(FunctionSig* sig) { 121 module_.signatures.push_back(sig); 122 size_t size = module->signatures.size(); 123 CHECK(size < 127); 124 return static_cast<byte>(size - 1); 125 } 126 127 template <typename T> 128 T* raw_mem_start() { 129 DCHECK(instance->mem_start); 130 return reinterpret_cast<T*>(instance->mem_start); 131 } 132 133 template <typename T> 134 T* raw_mem_end() { 135 DCHECK(instance->mem_start); 136 return reinterpret_cast<T*>(instance->mem_start + instance->mem_size); 137 } 138 139 template <typename T> 140 T raw_mem_at(int i) { 141 DCHECK(instance->mem_start); 142 return reinterpret_cast<T*>(instance->mem_start)[i]; 143 } 144 145 template <typename T> 146 T raw_val_at(int i) { 147 T val; 148 memcpy(&val, reinterpret_cast<void*>(instance->mem_start + i), sizeof(T)); 149 return val; 150 } 151 152 // Zero-initialize the memory. 153 void BlankMemory() { 154 byte* raw = raw_mem_start<byte>(); 155 memset(raw, 0, instance->mem_size); 156 } 157 158 // Pseudo-randomly intialize the memory. 159 void RandomizeMemory(unsigned int seed = 88) { 160 byte* raw = raw_mem_start<byte>(); 161 byte* end = raw_mem_end<byte>(); 162 v8::base::RandomNumberGenerator rng; 163 rng.SetSeed(seed); 164 rng.NextBytes(raw, end - raw); 165 } 166 167 uint32_t AddFunction(FunctionSig* sig, Handle<Code> code) { 168 if (module->functions.size() == 0) { 169 // TODO(titzer): Reserving space here to avoid the underlying WasmFunction 170 // structs from moving. 171 module_.functions.reserve(kMaxFunctions); 172 } 173 uint32_t index = static_cast<uint32_t>(module->functions.size()); 174 module_.functions.push_back({sig, index, 0, 0, 0, 0, 0}); 175 instance->function_code.push_back(code); 176 if (interpreter_) { 177 const WasmFunction* function = &module->functions.back(); 178 int interpreter_index = interpreter_->AddFunctionForTesting(function); 179 CHECK_EQ(index, static_cast<uint32_t>(interpreter_index)); 180 } 181 DCHECK_LT(index, kMaxFunctions); // limited for testing. 182 return index; 183 } 184 185 uint32_t AddJsFunction(FunctionSig* sig, const char* source) { 186 Handle<JSFunction> jsfunc = Handle<JSFunction>::cast(v8::Utils::OpenHandle( 187 *v8::Local<v8::Function>::Cast(CompileRun(source)))); 188 uint32_t index = AddFunction(sig, Handle<Code>::null()); 189 WasmName module_name = ArrayVector("test"); 190 WasmName function_name; 191 Handle<Code> code = CompileWasmToJSWrapper(isolate_, jsfunc, sig, 192 module_name, function_name); 193 instance->function_code[index] = code; 194 return index; 195 } 196 197 Handle<JSFunction> WrapCode(uint32_t index) { 198 // Wrap the code so it can be called as a JS function. 199 Handle<String> name = isolate_->factory()->NewStringFromStaticChars("main"); 200 Handle<JSObject> module_object = Handle<JSObject>(0, isolate_); 201 Handle<Code> code = instance->function_code[index]; 202 WasmJs::InstallWasmFunctionMap(isolate_, isolate_->native_context()); 203 return compiler::CompileJSToWasmWrapper(isolate_, this, name, code, 204 module_object, index); 205 } 206 207 void SetFunctionCode(uint32_t index, Handle<Code> code) { 208 instance->function_code[index] = code; 209 } 210 211 void AddIndirectFunctionTable(int* functions, int table_size) { 212 Handle<FixedArray> fixed = 213 isolate_->factory()->NewFixedArray(2 * table_size); 214 instance->function_table = fixed; 215 DCHECK_EQ(0u, module->function_table.size()); 216 for (int i = 0; i < table_size; i++) { 217 module_.function_table.push_back(functions[i]); 218 } 219 } 220 221 void PopulateIndirectFunctionTable() { 222 if (instance->function_table.is_null()) return; 223 int table_size = static_cast<int>(module->function_table.size()); 224 for (int i = 0; i < table_size; i++) { 225 int function_index = module->function_table[i]; 226 const WasmFunction* function = &module->functions[function_index]; 227 instance->function_table->set(i, Smi::FromInt(function->sig_index)); 228 instance->function_table->set(i + table_size, 229 *instance->function_code[function_index]); 230 } 231 } 232 WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; } 233 234 WasmInterpreter* interpreter() { return interpreter_; } 235 WasmExecutionMode execution_mode() { return execution_mode_; } 236 237 private: 238 WasmExecutionMode execution_mode_; 239 WasmModule module_; 240 WasmModuleInstance instance_; 241 Isolate* isolate_; 242 v8::base::AccountingAllocator allocator_; 243 uint32_t global_offset; 244 V8_ALIGNED(8) byte global_data[kMaxGlobalsSize]; // preallocated global data. 245 WasmInterpreter* interpreter_; 246 247 const WasmGlobal* AddGlobal(MachineType mem_type) { 248 byte size = WasmOpcodes::MemSize(mem_type); 249 global_offset = (global_offset + size - 1) & ~(size - 1); // align 250 module_.globals.push_back({0, 0, mem_type, global_offset, false}); 251 global_offset += size; 252 // limit number of globals. 253 CHECK_LT(global_offset, kMaxGlobalsSize); 254 return &module->globals.back(); 255 } 256 }; 257 258 inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module, 259 FunctionSig* sig, 260 SourcePositionTable* source_position_table, 261 const byte* start, const byte* end) { 262 compiler::WasmGraphBuilder builder(zone, jsgraph, sig, source_position_table); 263 TreeResult result = 264 BuildTFGraph(zone->allocator(), &builder, module, sig, start, end); 265 if (result.failed()) { 266 ptrdiff_t pc = result.error_pc - result.start; 267 ptrdiff_t pt = result.error_pt - result.start; 268 std::ostringstream str; 269 str << "Verification failed: " << result.error_code << " pc = +" << pc; 270 if (result.error_pt) str << ", pt = +" << pt; 271 str << ", msg = " << result.error_msg.get(); 272 FATAL(str.str().c_str()); 273 } 274 builder.Int64LoweringForTesting(); 275 if (FLAG_trace_turbo_graph) { 276 OFStream os(stdout); 277 os << AsRPO(*jsgraph->graph()); 278 } 279 } 280 281 template <typename ReturnType> 282 class WasmFunctionWrapper : public HandleAndZoneScope, 283 private GraphAndBuilders { 284 public: 285 WasmFunctionWrapper() 286 : GraphAndBuilders(main_zone()), 287 inner_code_node_(nullptr), 288 signature_(nullptr) { 289 // One additional parameter for the pointer to the return value memory. 290 Signature<MachineType>::Builder sig_builder( 291 zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1); 292 293 sig_builder.AddReturn(MachineType::Int32()); 294 for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) { 295 sig_builder.AddParam(MachineType::Pointer()); 296 } 297 signature_ = sig_builder.Build(); 298 } 299 300 void Init(CallDescriptor* descriptor, MachineType p0 = MachineType::None(), 301 MachineType p1 = MachineType::None(), 302 MachineType p2 = MachineType::None(), 303 MachineType p3 = MachineType::None()) { 304 // Create the TF graph for the wrapper. The wrapper always takes four 305 // pointers as parameters, but may not pass the values of all pointers to 306 // the actual test function. 307 308 // Function, effect, and control. 309 Node** parameters = 310 zone()->template NewArray<Node*>(WASM_RUNNER_MAX_NUM_PARAMETERS + 3); 311 graph()->SetStart(graph()->NewNode(common()->Start(6))); 312 Node* effect = graph()->start(); 313 int parameter_count = 0; 314 315 // Dummy node which gets replaced in SetInnerCode. 316 inner_code_node_ = graph()->NewNode(common()->Int32Constant(0)); 317 parameters[parameter_count++] = inner_code_node_; 318 319 if (p0 != MachineType::None()) { 320 parameters[parameter_count] = graph()->NewNode( 321 machine()->Load(p0), 322 graph()->NewNode(common()->Parameter(0), graph()->start()), 323 graph()->NewNode(common()->Int32Constant(0)), effect, 324 graph()->start()); 325 effect = parameters[parameter_count++]; 326 } 327 if (p1 != MachineType::None()) { 328 parameters[parameter_count] = graph()->NewNode( 329 machine()->Load(p0), 330 graph()->NewNode(common()->Parameter(1), graph()->start()), 331 graph()->NewNode(common()->Int32Constant(0)), effect, 332 graph()->start()); 333 effect = parameters[parameter_count++]; 334 } 335 if (p2 != MachineType::None()) { 336 parameters[parameter_count] = graph()->NewNode( 337 machine()->Load(p0), 338 graph()->NewNode(common()->Parameter(2), graph()->start()), 339 graph()->NewNode(common()->Int32Constant(0)), effect, 340 graph()->start()); 341 effect = parameters[parameter_count++]; 342 } 343 if (p3 != MachineType::None()) { 344 parameters[parameter_count] = graph()->NewNode( 345 machine()->Load(p0), 346 graph()->NewNode(common()->Parameter(3), graph()->start()), 347 graph()->NewNode(common()->Int32Constant(0)), effect, 348 graph()->start()); 349 effect = parameters[parameter_count++]; 350 } 351 352 parameters[parameter_count++] = effect; 353 parameters[parameter_count++] = graph()->start(); 354 Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count, 355 parameters); 356 357 effect = graph()->NewNode( 358 machine()->Store( 359 StoreRepresentation(MachineTypeForC<ReturnType>().representation(), 360 WriteBarrierKind::kNoWriteBarrier)), 361 graph()->NewNode(common()->Parameter(WASM_RUNNER_MAX_NUM_PARAMETERS), 362 graph()->start()), 363 graph()->NewNode(common()->Int32Constant(0)), call, effect, 364 graph()->start()); 365 Node* r = graph()->NewNode( 366 common()->Return(), 367 graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)), 368 effect, graph()->start()); 369 graph()->SetEnd(graph()->NewNode(common()->End(2), r, graph()->start())); 370 } 371 372 void SetInnerCode(Handle<Code> code_handle) { 373 NodeProperties::ChangeOp(inner_code_node_, 374 common()->HeapConstant(code_handle)); 375 } 376 377 Handle<Code> GetWrapperCode() { 378 if (code_.is_null()) { 379 Isolate* isolate = CcTest::InitIsolateOnce(); 380 381 CallDescriptor* descriptor = 382 Linkage::GetSimplifiedCDescriptor(zone(), signature_, true); 383 384 if (kPointerSize == 4) { 385 // One additional parameter for the pointer of the return value. 386 Signature<MachineRepresentation>::Builder rep_builder( 387 zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1); 388 389 rep_builder.AddReturn(MachineRepresentation::kWord32); 390 for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) { 391 rep_builder.AddParam(MachineRepresentation::kWord32); 392 } 393 Int64Lowering r(graph(), machine(), common(), zone(), 394 rep_builder.Build()); 395 r.LowerGraph(); 396 } 397 398 CompilationInfo info(ArrayVector("testing"), isolate, graph()->zone()); 399 code_ = 400 Pipeline::GenerateCodeForTesting(&info, descriptor, graph(), nullptr); 401 CHECK(!code_.is_null()); 402 #ifdef ENABLE_DISASSEMBLER 403 if (FLAG_print_opt_code) { 404 OFStream os(stdout); 405 code_->Disassemble("wasm wrapper", os); 406 } 407 #endif 408 } 409 410 return code_; 411 } 412 413 Signature<MachineType>* signature() const { return signature_; } 414 415 private: 416 Node* inner_code_node_; 417 Handle<Code> code_; 418 Signature<MachineType>* signature_; 419 }; 420 421 // A helper for compiling WASM functions for testing. This class can create a 422 // standalone function if {module} is NULL or a function within a 423 // {TestingModule}. It contains the internal state for compilation (i.e. 424 // TurboFan graph) and interpretation (by adding to the interpreter manually). 425 class WasmFunctionCompiler : public HandleAndZoneScope, 426 private GraphAndBuilders { 427 public: 428 explicit WasmFunctionCompiler( 429 FunctionSig* sig, WasmExecutionMode mode, 430 Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>")) 431 : GraphAndBuilders(main_zone()), 432 execution_mode_(mode), 433 jsgraph(this->isolate(), this->graph(), this->common(), nullptr, 434 nullptr, this->machine()), 435 sig(sig), 436 descriptor_(nullptr), 437 testing_module_(nullptr), 438 debug_name_(debug_name), 439 local_decls(main_zone(), sig), 440 source_position_table_(this->graph()), 441 interpreter_(nullptr) { 442 // Create our own function. 443 function_ = new WasmFunction(); 444 function_->sig = sig; 445 function_->func_index = 0; 446 function_->sig_index = 0; 447 if (mode == kExecuteInterpreted) { 448 interpreter_ = new WasmInterpreter(nullptr, zone()->allocator()); 449 int index = interpreter_->AddFunctionForTesting(function_); 450 CHECK_EQ(0, index); 451 } 452 } 453 454 explicit WasmFunctionCompiler( 455 FunctionSig* sig, TestingModule* module, 456 Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>")) 457 : GraphAndBuilders(main_zone()), 458 execution_mode_(module->execution_mode()), 459 jsgraph(this->isolate(), this->graph(), this->common(), nullptr, 460 nullptr, this->machine()), 461 sig(sig), 462 descriptor_(nullptr), 463 testing_module_(module), 464 debug_name_(debug_name), 465 local_decls(main_zone(), sig), 466 source_position_table_(this->graph()), 467 interpreter_(module->interpreter()) { 468 // Get a new function from the testing module. 469 int index = module->AddFunction(sig, Handle<Code>::null()); 470 function_ = testing_module_->GetFunctionAt(index); 471 } 472 473 ~WasmFunctionCompiler() { 474 if (testing_module_) return; // testing module owns the below things. 475 delete function_; 476 if (interpreter_) delete interpreter_; 477 } 478 479 WasmExecutionMode execution_mode_; 480 JSGraph jsgraph; 481 FunctionSig* sig; 482 // The call descriptor is initialized when the function is compiled. 483 CallDescriptor* descriptor_; 484 TestingModule* testing_module_; 485 Vector<const char> debug_name_; 486 WasmFunction* function_; 487 LocalDeclEncoder local_decls; 488 SourcePositionTable source_position_table_; 489 WasmInterpreter* interpreter_; 490 491 Isolate* isolate() { return main_isolate(); } 492 Graph* graph() const { return main_graph_; } 493 Zone* zone() const { return graph()->zone(); } 494 CommonOperatorBuilder* common() { return &main_common_; } 495 MachineOperatorBuilder* machine() { return &main_machine_; } 496 void InitializeDescriptor() { 497 if (descriptor_ == nullptr) { 498 descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig); 499 } 500 } 501 CallDescriptor* descriptor() { return descriptor_; } 502 uint32_t function_index() { return function_->func_index; } 503 504 void Build(const byte* start, const byte* end) { 505 // Build the TurboFan graph. 506 local_decls.Prepend(main_zone(), &start, &end); 507 TestBuildingGraph(main_zone(), &jsgraph, testing_module_, sig, 508 &source_position_table_, start, end); 509 if (interpreter_) { 510 // Add the code to the interpreter. 511 CHECK(interpreter_->SetFunctionCodeForTesting(function_, start, end)); 512 } 513 } 514 515 byte AllocateLocal(LocalType type) { 516 uint32_t index = local_decls.AddLocals(1, type); 517 byte result = static_cast<byte>(index); 518 DCHECK_EQ(index, result); 519 return result; 520 } 521 522 Handle<Code> Compile() { 523 InitializeDescriptor(); 524 CallDescriptor* desc = descriptor_; 525 if (kPointerSize == 4) { 526 desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc); 527 } 528 CompilationInfo info(debug_name_, this->isolate(), this->zone(), 529 Code::ComputeFlags(Code::WASM_FUNCTION)); 530 v8::base::SmartPointer<CompilationJob> job(Pipeline::NewWasmCompilationJob( 531 &info, graph(), desc, &source_position_table_)); 532 if (job->OptimizeGraph() != CompilationJob::SUCCEEDED || 533 job->GenerateCode() != CompilationJob::SUCCEEDED) 534 return Handle<Code>::null(); 535 536 Handle<Code> code = info.code(); 537 538 // Length is always 2, since usually <wasm_obj, func_index> is stored in 539 // the deopt data. Here, we only store the function index. 540 DCHECK(code->deoptimization_data() == nullptr || 541 code->deoptimization_data()->length() == 0); 542 Handle<FixedArray> deopt_data = 543 isolate()->factory()->NewFixedArray(2, TENURED); 544 deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index()))); 545 deopt_data->set_length(2); 546 code->set_deoptimization_data(*deopt_data); 547 548 #ifdef ENABLE_DISASSEMBLER 549 if (FLAG_print_opt_code) { 550 OFStream os(stdout); 551 code->Disassemble("wasm code", os); 552 } 553 #endif 554 555 return code; 556 } 557 558 uint32_t CompileAndAdd(uint16_t sig_index = 0) { 559 CHECK(testing_module_); 560 function_->sig_index = sig_index; 561 Handle<Code> code = Compile(); 562 testing_module_->SetFunctionCode(function_index(), code); 563 return function_index(); 564 } 565 566 // Set the context, such that e.g. runtime functions can be called. 567 void SetModuleContext() { 568 if (!testing_module_->instance->context.is_null()) { 569 CHECK(testing_module_->instance->context.is_identical_to( 570 main_isolate()->native_context())); 571 return; 572 } 573 testing_module_->instance->context = main_isolate()->native_context(); 574 } 575 }; 576 577 // A helper class to build graphs from Wasm bytecode, generate machine 578 // code, and run that code. 579 template <typename ReturnType> 580 class WasmRunner { 581 public: 582 WasmRunner(WasmExecutionMode execution_mode, 583 MachineType p0 = MachineType::None(), 584 MachineType p1 = MachineType::None(), 585 MachineType p2 = MachineType::None(), 586 MachineType p3 = MachineType::None()) 587 : zone(&allocator_), 588 compiled_(false), 589 signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, 590 GetParameterCount(p0, p1, p2, p3), storage_), 591 compiler_(&signature_, execution_mode) { 592 InitSigStorage(p0, p1, p2, p3); 593 } 594 595 WasmRunner(TestingModule* module, MachineType p0 = MachineType::None(), 596 MachineType p1 = MachineType::None(), 597 MachineType p2 = MachineType::None(), 598 MachineType p3 = MachineType::None()) 599 : zone(&allocator_), 600 compiled_(false), 601 signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, 602 GetParameterCount(p0, p1, p2, p3), storage_), 603 compiler_(&signature_, module) { 604 DCHECK(module); 605 InitSigStorage(p0, p1, p2, p3); 606 } 607 608 void InitSigStorage(MachineType p0, MachineType p1, MachineType p2, 609 MachineType p3) { 610 int index = 0; 611 MachineType ret = MachineTypeForC<ReturnType>(); 612 if (ret != MachineType::None()) { 613 storage_[index++] = WasmOpcodes::LocalTypeFor(ret); 614 } 615 if (p0 != MachineType::None()) 616 storage_[index++] = WasmOpcodes::LocalTypeFor(p0); 617 if (p1 != MachineType::None()) 618 storage_[index++] = WasmOpcodes::LocalTypeFor(p1); 619 if (p2 != MachineType::None()) 620 storage_[index++] = WasmOpcodes::LocalTypeFor(p2); 621 if (p3 != MachineType::None()) 622 storage_[index++] = WasmOpcodes::LocalTypeFor(p3); 623 624 compiler_.InitializeDescriptor(); 625 wrapper_.Init(compiler_.descriptor(), p0, p1, p2, p3); 626 } 627 628 // Builds a graph from the given Wasm code and generates the machine 629 // code and call wrapper for that graph. This method must not be called 630 // more than once. 631 void Build(const byte* start, const byte* end) { 632 CHECK(!compiled_); 633 compiled_ = true; 634 compiler_.Build(start, end); 635 636 if (!interpret()) { 637 // Compile machine code and install it into the module. 638 Handle<Code> code = compiler_.Compile(); 639 640 if (compiler_.testing_module_) { 641 // Update the table of function code in the module. 642 compiler_.testing_module_->SetFunctionCode( 643 compiler_.function_->func_index, code); 644 } 645 646 wrapper_.SetInnerCode(code); 647 } 648 } 649 650 ReturnType Call() { 651 if (interpret()) { 652 return CallInterpreter(Vector<WasmVal>(nullptr, 0)); 653 } else { 654 return Call(0, 0, 0, 0); 655 } 656 } 657 658 template <typename P0> 659 ReturnType Call(P0 p0) { 660 if (interpret()) { 661 WasmVal args[] = {WasmVal(p0)}; 662 return CallInterpreter(ArrayVector(args)); 663 } else { 664 return Call(p0, 0, 0, 0); 665 } 666 } 667 668 template <typename P0, typename P1> 669 ReturnType Call(P0 p0, P1 p1) { 670 if (interpret()) { 671 WasmVal args[] = {WasmVal(p0), WasmVal(p1)}; 672 return CallInterpreter(ArrayVector(args)); 673 } else { 674 return Call(p0, p1, 0, 0); 675 } 676 } 677 678 template <typename P0, typename P1, typename P2> 679 ReturnType Call(P0 p0, P1 p1, P2 p2) { 680 if (interpret()) { 681 WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2)}; 682 return CallInterpreter(ArrayVector(args)); 683 } else { 684 return Call(p0, p1, p2, 0); 685 } 686 } 687 688 template <typename P0, typename P1, typename P2, typename P3> 689 ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) { 690 if (interpret()) { 691 WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2), WasmVal(p3)}; 692 return CallInterpreter(ArrayVector(args)); 693 } else { 694 CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(), 695 wrapper_.GetWrapperCode(), 696 wrapper_.signature()); 697 ReturnType return_value; 698 int32_t result = runner.Call<void*, void*, void*, void*, void*>( 699 &p0, &p1, &p2, &p3, &return_value); 700 CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result); 701 return return_value; 702 } 703 } 704 705 ReturnType CallInterpreter(Vector<WasmVal> args) { 706 CHECK_EQ(args.length(), 707 static_cast<int>(compiler_.function_->sig->parameter_count())); 708 WasmInterpreter::Thread* thread = interpreter()->GetThread(0); 709 thread->Reset(); 710 thread->PushFrame(compiler_.function_, args.start()); 711 if (thread->Run() == WasmInterpreter::FINISHED) { 712 WasmVal val = thread->GetReturnValue(); 713 return val.to<ReturnType>(); 714 } else if (thread->state() == WasmInterpreter::TRAPPED) { 715 // TODO(titzer): return the correct trap code 716 int64_t result = 0xdeadbeefdeadbeef; 717 return static_cast<ReturnType>(result); 718 } else { 719 // TODO(titzer): falling off end 720 ReturnType val = 0; 721 return val; 722 } 723 } 724 725 byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); } 726 727 WasmFunction* function() { return compiler_.function_; } 728 WasmInterpreter* interpreter() { return compiler_.interpreter_; } 729 730 protected: 731 v8::base::AccountingAllocator allocator_; 732 Zone zone; 733 bool compiled_; 734 LocalType storage_[WASM_RUNNER_MAX_NUM_PARAMETERS]; 735 FunctionSig signature_; 736 WasmFunctionCompiler compiler_; 737 WasmFunctionWrapper<ReturnType> wrapper_; 738 739 bool interpret() { return compiler_.execution_mode_ == kExecuteInterpreted; } 740 741 static size_t GetParameterCount(MachineType p0, MachineType p1, 742 MachineType p2, MachineType p3) { 743 if (p0 == MachineType::None()) return 0; 744 if (p1 == MachineType::None()) return 1; 745 if (p2 == MachineType::None()) return 2; 746 if (p3 == MachineType::None()) return 3; 747 return 4; 748 } 749 }; 750 751 // A macro to define tests that run in different engine configurations. 752 // Currently only supports compiled tests, but a future 753 // RunWasmInterpreted_##name version will allow each test to also run in the 754 // interpreter. 755 #define WASM_EXEC_TEST(name) \ 756 void RunWasm_##name(WasmExecutionMode execution_mode); \ 757 TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \ 758 TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \ 759 void RunWasm_##name(WasmExecutionMode execution_mode) 760 761 } // namespace 762 763 #endif 764