OLD | NEW |
---|---|
1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef WASM_RUN_UTILS_H | 5 #ifndef WASM_RUN_UTILS_H |
6 #define WASM_RUN_UTILS_H | 6 #define WASM_RUN_UTILS_H |
7 | 7 |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 #include <stdlib.h> | 9 #include <stdlib.h> |
10 #include <string.h> | 10 #include <string.h> |
11 #include <array> | |
11 #include <memory> | 12 #include <memory> |
12 | 13 |
13 #include "src/base/utils/random-number-generator.h" | 14 #include "src/base/utils/random-number-generator.h" |
14 #include "src/zone/accounting-allocator.h" | 15 #include "src/zone/accounting-allocator.h" |
15 | 16 |
16 #include "src/compiler/compiler-source-position-table.h" | 17 #include "src/compiler/compiler-source-position-table.h" |
17 #include "src/compiler/graph-visualizer.h" | 18 #include "src/compiler/graph-visualizer.h" |
18 #include "src/compiler/int64-lowering.h" | 19 #include "src/compiler/int64-lowering.h" |
19 #include "src/compiler/js-graph.h" | 20 #include "src/compiler/js-graph.h" |
20 #include "src/compiler/node.h" | 21 #include "src/compiler/node.h" |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
64 using namespace v8::internal::compiler; | 65 using namespace v8::internal::compiler; |
65 using namespace v8::internal::wasm; | 66 using namespace v8::internal::wasm; |
66 | 67 |
67 const uint32_t kMaxGlobalsSize = 128; | 68 const uint32_t kMaxGlobalsSize = 128; |
68 | 69 |
69 // A helper for module environments that adds the ability to allocate memory | 70 // A helper for module environments that adds the ability to allocate memory |
70 // and global variables. Contains a built-in {WasmModule} and | 71 // and global variables. Contains a built-in {WasmModule} and |
71 // {WasmInstance}. | 72 // {WasmInstance}. |
72 class TestingModule : public ModuleEnv { | 73 class TestingModule : public ModuleEnv { |
73 public: | 74 public: |
74 explicit TestingModule(WasmExecutionMode mode = kExecuteCompiled) | 75 explicit TestingModule(Zone* zone, WasmExecutionMode mode = kExecuteCompiled) |
75 : ModuleEnv(&module_, &instance_), | 76 : ModuleEnv(&module_, &instance_), |
76 execution_mode_(mode), | 77 execution_mode_(mode), |
77 instance_(&module_), | 78 instance_(&module_), |
78 isolate_(CcTest::InitIsolateOnce()), | 79 isolate_(CcTest::InitIsolateOnce()), |
79 global_offset(0), | 80 global_offset(0), |
80 interpreter_(mode == kExecuteInterpreted | 81 interpreter_(mode == kExecuteInterpreted |
81 ? new WasmInterpreter( | 82 ? new WasmInterpreter( |
82 ModuleBytesEnv(&module_, &instance_, | 83 ModuleBytesEnv(&module_, &instance_, |
83 Vector<const byte>::empty()), | 84 Vector<const byte>::empty()), |
84 &allocator_) | 85 zone->allocator()) |
85 : nullptr) { | 86 : nullptr) { |
86 instance->module = &module_; | 87 instance->module = &module_; |
87 instance->globals_start = global_data; | 88 instance->globals_start = global_data; |
88 module_.globals_size = kMaxGlobalsSize; | 89 module_.globals_size = kMaxGlobalsSize; |
89 instance->mem_start = nullptr; | 90 instance->mem_start = nullptr; |
90 instance->mem_size = 0; | 91 instance->mem_size = 0; |
91 memset(global_data, 0, sizeof(global_data)); | 92 memset(global_data, 0, sizeof(global_data)); |
92 } | 93 } |
93 | 94 |
94 ~TestingModule() { | 95 ~TestingModule() { |
95 if (instance->mem_start) { | 96 if (instance->mem_start) { |
96 free(instance->mem_start); | 97 free(instance->mem_start); |
97 } | 98 } |
98 if (interpreter_) delete interpreter_; | 99 if (interpreter_) delete interpreter_; |
99 } | 100 } |
100 | 101 |
101 void ChangeOriginToAsmjs() { module_.origin = kAsmJsOrigin; } | 102 void ChangeOriginToAsmjs() { module_.origin = kAsmJsOrigin; } |
102 | 103 |
103 byte* AddMemory(uint32_t size) { | 104 byte* AddMemory(uint32_t size) { |
104 CHECK_NULL(instance->mem_start); | 105 CHECK_NULL(instance->mem_start); |
105 CHECK_EQ(0u, instance->mem_size); | 106 CHECK_EQ(0, instance->mem_size); |
106 instance->mem_start = reinterpret_cast<byte*>(malloc(size)); | 107 instance->mem_start = reinterpret_cast<byte*>(malloc(size)); |
107 CHECK(instance->mem_start); | 108 CHECK(instance->mem_start); |
108 memset(instance->mem_start, 0, size); | 109 memset(instance->mem_start, 0, size); |
109 instance->mem_size = size; | 110 instance->mem_size = size; |
110 return raw_mem_start<byte>(); | 111 return raw_mem_start<byte>(); |
111 } | 112 } |
112 | 113 |
113 template <typename T> | 114 template <typename T> |
114 T* AddMemoryElems(uint32_t count) { | 115 T* AddMemoryElems(uint32_t count) { |
115 AddMemory(count * sizeof(T)); | 116 AddMemory(count * sizeof(T)); |
116 return raw_mem_start<T>(); | 117 return raw_mem_start<T>(); |
117 } | 118 } |
118 | 119 |
119 template <typename T> | 120 template <typename T> |
120 T* AddGlobal(LocalType type) { | 121 T* AddGlobal( |
122 LocalType type = WasmOpcodes::LocalTypeFor(MachineTypeForC<T>())) { | |
121 const WasmGlobal* global = AddGlobal(type); | 123 const WasmGlobal* global = AddGlobal(type); |
122 return reinterpret_cast<T*>(instance->globals_start + global->offset); | 124 return reinterpret_cast<T*>(instance->globals_start + global->offset); |
123 } | 125 } |
124 | 126 |
125 byte AddSignature(FunctionSig* sig) { | 127 byte AddSignature(FunctionSig* sig) { |
126 module_.signatures.push_back(sig); | 128 module_.signatures.push_back(sig); |
127 size_t size = module->signatures.size(); | 129 size_t size = module->signatures.size(); |
128 CHECK(size < 127); | 130 CHECK(size < 127); |
129 return static_cast<byte>(size - 1); | 131 return static_cast<byte>(size - 1); |
130 } | 132 } |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
236 for (uint32_t i = 0; i < table_size; ++i) { | 238 for (uint32_t i = 0; i < table_size; ++i) { |
237 table.values.push_back(function_indexes[i]); | 239 table.values.push_back(function_indexes[i]); |
238 table.map.FindOrInsert(module_.functions[function_indexes[i]].sig); | 240 table.map.FindOrInsert(module_.functions[function_indexes[i]].sig); |
239 } | 241 } |
240 | 242 |
241 instance->function_tables.push_back( | 243 instance->function_tables.push_back( |
242 isolate_->factory()->NewFixedArray(table_size * 2)); | 244 isolate_->factory()->NewFixedArray(table_size * 2)); |
243 } | 245 } |
244 | 246 |
245 void PopulateIndirectFunctionTable() { | 247 void PopulateIndirectFunctionTable() { |
248 if (execution_mode_ == kExecuteInterpreted) return; | |
246 // Initialize the fixed arrays in instance->function_tables. | 249 // Initialize the fixed arrays in instance->function_tables. |
247 for (uint32_t i = 0; i < instance->function_tables.size(); i++) { | 250 for (uint32_t i = 0; i < instance->function_tables.size(); i++) { |
248 WasmIndirectFunctionTable& table = module_.function_tables[i]; | 251 WasmIndirectFunctionTable& table = module_.function_tables[i]; |
249 Handle<FixedArray> array = instance->function_tables[i]; | 252 Handle<FixedArray> array = instance->function_tables[i]; |
250 int table_size = static_cast<int>(table.values.size()); | 253 int table_size = static_cast<int>(table.values.size()); |
251 for (int j = 0; j < table_size; j++) { | 254 for (int j = 0; j < table_size; j++) { |
252 WasmFunction& function = module_.functions[table.values[j]]; | 255 WasmFunction& function = module_.functions[table.values[j]]; |
253 array->set(j, Smi::FromInt(table.map.Find(function.sig))); | 256 array->set(j, Smi::FromInt(table.map.Find(function.sig))); |
254 array->set(j + table_size, | 257 array->set(j + table_size, |
255 *instance->function_code[function.func_index]); | 258 *instance->function_code[function.func_index]); |
256 } | 259 } |
257 } | 260 } |
258 } | 261 } |
259 | 262 |
260 WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; } | 263 WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; } |
261 | 264 |
262 WasmInterpreter* interpreter() { return interpreter_; } | 265 WasmInterpreter* interpreter() { return interpreter_; } |
263 WasmExecutionMode execution_mode() { return execution_mode_; } | 266 WasmExecutionMode execution_mode() { return execution_mode_; } |
267 Isolate* isolate() { return isolate_; } | |
264 | 268 |
265 private: | 269 private: |
266 WasmExecutionMode execution_mode_; | 270 WasmExecutionMode execution_mode_; |
267 WasmModule module_; | 271 WasmModule module_; |
268 WasmInstance instance_; | 272 WasmInstance instance_; |
269 Isolate* isolate_; | 273 Isolate* isolate_; |
270 v8::internal::AccountingAllocator allocator_; | |
271 uint32_t global_offset; | 274 uint32_t global_offset; |
272 V8_ALIGNED(8) byte global_data[kMaxGlobalsSize]; // preallocated global data. | 275 V8_ALIGNED(8) byte global_data[kMaxGlobalsSize]; // preallocated global data. |
273 WasmInterpreter* interpreter_; | 276 WasmInterpreter* interpreter_; |
274 | 277 |
275 const WasmGlobal* AddGlobal(LocalType type) { | 278 const WasmGlobal* AddGlobal(LocalType type) { |
276 byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type)); | 279 byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type)); |
277 global_offset = (global_offset + size - 1) & ~(size - 1); // align | 280 global_offset = (global_offset + size - 1) & ~(size - 1); // align |
278 module_.globals.push_back( | 281 module_.globals.push_back( |
279 {type, true, WasmInitExpr(), global_offset, false, false}); | 282 {type, true, WasmInitExpr(), global_offset, false, false}); |
280 global_offset += size; | 283 global_offset += size; |
(...skipping 26 matching lines...) Expand all Loading... | |
307 str << ", msg = " << result.error_msg.get(); | 310 str << ", msg = " << result.error_msg.get(); |
308 FATAL(str.str().c_str()); | 311 FATAL(str.str().c_str()); |
309 } | 312 } |
310 builder.Int64LoweringForTesting(); | 313 builder.Int64LoweringForTesting(); |
311 if (!CpuFeatures::SupportsSimd128()) { | 314 if (!CpuFeatures::SupportsSimd128()) { |
312 builder.SimdScalarLoweringForTesting(); | 315 builder.SimdScalarLoweringForTesting(); |
313 } | 316 } |
314 } | 317 } |
315 | 318 |
316 template <typename ReturnType> | 319 template <typename ReturnType> |
317 class WasmFunctionWrapper : public HandleAndZoneScope, | 320 class WasmFunctionWrapper : private GraphAndBuilders { |
318 private GraphAndBuilders { | |
319 public: | 321 public: |
320 WasmFunctionWrapper() | 322 explicit WasmFunctionWrapper(Zone* zone, int num_params) |
321 : GraphAndBuilders(main_zone()), | 323 : GraphAndBuilders(zone), |
322 inner_code_node_(nullptr), | 324 inner_code_node_(nullptr), |
323 signature_(nullptr) { | 325 signature_(nullptr), |
326 num_params_(num_params) { | |
324 // One additional parameter for the pointer to the return value memory. | 327 // One additional parameter for the pointer to the return value memory. |
325 Signature<MachineType>::Builder sig_builder( | 328 Signature<MachineType>::Builder sig_builder(zone, 1, num_params + 1); |
326 zone(), 1, WASM_RUNNER_MAX_NUM_PARAMETERS + 1); | |
327 | 329 |
328 sig_builder.AddReturn(MachineType::Int32()); | 330 sig_builder.AddReturn(MachineType::Int32()); |
329 for (int i = 0; i < WASM_RUNNER_MAX_NUM_PARAMETERS + 1; i++) { | 331 for (int i = 0; i < num_params + 1; i++) { |
330 sig_builder.AddParam(MachineType::Pointer()); | 332 sig_builder.AddParam(MachineType::Pointer()); |
331 } | 333 } |
332 signature_ = sig_builder.Build(); | 334 signature_ = sig_builder.Build(); |
333 } | 335 } |
334 | 336 |
335 void Init(CallDescriptor* descriptor, MachineType p0 = MachineType::None(), | 337 template <typename... ParamTypes> |
336 MachineType p1 = MachineType::None(), | 338 void Init(CallDescriptor* descriptor) { |
337 MachineType p2 = MachineType::None(), | 339 DCHECK_NOT_NULL(descriptor); |
338 MachineType p3 = MachineType::None()) { | 340 // Create the TF graph for the wrapper. |
339 // Create the TF graph for the wrapper. The wrapper always takes four | |
340 // pointers as parameters, but may not pass the values of all pointers to | |
341 // the actual test function. | |
342 | 341 |
343 // Function, effect, and control. | 342 // Function, effect, and control. |
344 Node** parameters = | 343 Node** parameters = zone()->template NewArray<Node*>(num_params_ + 3); |
345 zone()->template NewArray<Node*>(WASM_RUNNER_MAX_NUM_PARAMETERS + 3); | |
346 graph()->SetStart(graph()->NewNode(common()->Start(6))); | 344 graph()->SetStart(graph()->NewNode(common()->Start(6))); |
347 Node* effect = graph()->start(); | 345 Node* effect = graph()->start(); |
348 int parameter_count = 0; | 346 int parameter_count = 0; |
349 | 347 |
350 // Dummy node which gets replaced in SetInnerCode. | 348 // Dummy node which gets replaced in SetInnerCode. |
351 inner_code_node_ = graph()->NewNode(common()->Int32Constant(0)); | 349 inner_code_node_ = graph()->NewNode(common()->Int32Constant(0)); |
352 parameters[parameter_count++] = inner_code_node_; | 350 parameters[parameter_count++] = inner_code_node_; |
353 | 351 |
354 if (p0 != MachineType::None()) { | 352 std::array<MachineType, sizeof...(ParamTypes)> mach_types{ |
titzer
2016/12/14 17:31:14
Can you factor this std::array creation into a met
Clemens Hammacher
2016/12/14 20:41:38
Done. It turned out that also the ReturnType was o
| |
353 {MachineTypeForC<ParamTypes>()...}}; | |
354 int param_idx = 0; | |
355 for (MachineType t : mach_types) { | |
356 DCHECK_NE(MachineType::None(), t); | |
355 parameters[parameter_count] = graph()->NewNode( | 357 parameters[parameter_count] = graph()->NewNode( |
356 machine()->Load(p0), | 358 machine()->Load(t), |
357 graph()->NewNode(common()->Parameter(0), graph()->start()), | 359 graph()->NewNode(common()->Parameter(param_idx++), graph()->start()), |
358 graph()->NewNode(common()->Int32Constant(0)), effect, | 360 graph()->NewNode(common()->Int32Constant(0)), effect, |
359 graph()->start()); | 361 graph()->start()); |
360 effect = parameters[parameter_count++]; | 362 effect = parameters[parameter_count++]; |
361 } | |
362 if (p1 != MachineType::None()) { | |
363 parameters[parameter_count] = graph()->NewNode( | |
364 machine()->Load(p1), | |
365 graph()->NewNode(common()->Parameter(1), graph()->start()), | |
366 graph()->NewNode(common()->Int32Constant(0)), effect, | |
367 graph()->start()); | |
368 effect = parameters[parameter_count++]; | |
369 } | |
370 if (p2 != MachineType::None()) { | |
371 parameters[parameter_count] = graph()->NewNode( | |
372 machine()->Load(p2), | |
373 graph()->NewNode(common()->Parameter(2), graph()->start()), | |
374 graph()->NewNode(common()->Int32Constant(0)), effect, | |
375 graph()->start()); | |
376 effect = parameters[parameter_count++]; | |
377 } | |
378 if (p3 != MachineType::None()) { | |
379 parameters[parameter_count] = graph()->NewNode( | |
380 machine()->Load(p3), | |
381 graph()->NewNode(common()->Parameter(3), graph()->start()), | |
382 graph()->NewNode(common()->Int32Constant(0)), effect, | |
383 graph()->start()); | |
384 effect = parameters[parameter_count++]; | |
385 } | 363 } |
386 | 364 |
387 parameters[parameter_count++] = effect; | 365 parameters[parameter_count++] = effect; |
388 parameters[parameter_count++] = graph()->start(); | 366 parameters[parameter_count++] = graph()->start(); |
389 Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count, | 367 Node* call = graph()->NewNode(common()->Call(descriptor), parameter_count, |
390 parameters); | 368 parameters); |
391 | 369 |
392 effect = graph()->NewNode( | 370 effect = graph()->NewNode( |
393 machine()->Store( | 371 machine()->Store( |
394 StoreRepresentation(MachineTypeForC<ReturnType>().representation(), | 372 StoreRepresentation(MachineTypeForC<ReturnType>().representation(), |
395 WriteBarrierKind::kNoWriteBarrier)), | 373 WriteBarrierKind::kNoWriteBarrier)), |
396 graph()->NewNode(common()->Parameter(WASM_RUNNER_MAX_NUM_PARAMETERS), | 374 graph()->NewNode(common()->Parameter(num_params_), graph()->start()), |
397 graph()->start()), | |
398 graph()->NewNode(common()->Int32Constant(0)), call, effect, | 375 graph()->NewNode(common()->Int32Constant(0)), call, effect, |
399 graph()->start()); | 376 graph()->start()); |
400 Node* zero = graph()->NewNode(common()->Int32Constant(0)); | 377 Node* zero = graph()->NewNode(common()->Int32Constant(0)); |
401 Node* r = graph()->NewNode( | 378 Node* r = graph()->NewNode( |
402 common()->Return(), zero, | 379 common()->Return(), zero, |
403 graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)), | 380 graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)), |
404 effect, graph()->start()); | 381 effect, graph()->start()); |
405 graph()->SetEnd(graph()->NewNode(common()->End(2), r, graph()->start())); | 382 graph()->SetEnd(graph()->NewNode(common()->End(2), r, graph()->start())); |
406 } | 383 } |
407 | 384 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
446 | 423 |
447 return code_; | 424 return code_; |
448 } | 425 } |
449 | 426 |
450 Signature<MachineType>* signature() const { return signature_; } | 427 Signature<MachineType>* signature() const { return signature_; } |
451 | 428 |
452 private: | 429 private: |
453 Node* inner_code_node_; | 430 Node* inner_code_node_; |
454 Handle<Code> code_; | 431 Handle<Code> code_; |
455 Signature<MachineType>* signature_; | 432 Signature<MachineType>* signature_; |
433 int num_params_; | |
456 }; | 434 }; |
457 | 435 |
458 // A helper for compiling WASM functions for testing. This class can create a | 436 // A helper for compiling WASM functions for testing. |
459 // standalone function if {module} is NULL or a function within a | 437 // It contains the internal state for compilation (i.e. TurboFan graph) and |
460 // {TestingModule}. It contains the internal state for compilation (i.e. | 438 // interpretation (by adding to the interpreter manually). |
461 // TurboFan graph) and interpretation (by adding to the interpreter manually). | 439 class WasmFunctionCompiler : private GraphAndBuilders { |
462 class WasmFunctionCompiler : public HandleAndZoneScope, | |
463 private GraphAndBuilders { | |
464 public: | 440 public: |
465 explicit WasmFunctionCompiler( | 441 Isolate* isolate() { return testing_module_->isolate(); } |
466 FunctionSig* sig, WasmExecutionMode mode, | |
467 Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>")) | |
468 : GraphAndBuilders(main_zone()), | |
469 execution_mode_(mode), | |
470 jsgraph(this->isolate(), this->graph(), this->common(), nullptr, | |
471 nullptr, this->machine()), | |
472 sig(sig), | |
473 descriptor_(nullptr), | |
474 testing_module_(nullptr), | |
475 debug_name_(debug_name), | |
476 local_decls(main_zone(), sig), | |
477 source_position_table_(this->graph()), | |
478 interpreter_(nullptr) { | |
479 // Create our own function. | |
480 function_ = new WasmFunction(); | |
481 function_->sig = sig; | |
482 function_->func_index = 0; | |
483 function_->sig_index = 0; | |
484 if (mode == kExecuteInterpreted) { | |
485 ModuleBytesEnv empty_env(nullptr, nullptr, Vector<const byte>::empty()); | |
486 interpreter_ = new WasmInterpreter(empty_env, zone()->allocator()); | |
487 int index = interpreter_->AddFunctionForTesting(function_); | |
488 CHECK_EQ(0, index); | |
489 } | |
490 } | |
491 | |
492 explicit WasmFunctionCompiler( | |
493 FunctionSig* sig, TestingModule* module, | |
494 Vector<const char> debug_name = ArrayVector("<WASM UNNAMED>")) | |
495 : GraphAndBuilders(main_zone()), | |
496 execution_mode_(module->execution_mode()), | |
497 jsgraph(this->isolate(), this->graph(), this->common(), nullptr, | |
498 nullptr, this->machine()), | |
499 sig(sig), | |
500 descriptor_(nullptr), | |
501 testing_module_(module), | |
502 debug_name_(debug_name), | |
503 local_decls(main_zone(), sig), | |
504 source_position_table_(this->graph()), | |
505 interpreter_(module->interpreter()) { | |
506 // Get a new function from the testing module. | |
507 int index = module->AddFunction(sig, Handle<Code>::null()); | |
508 function_ = testing_module_->GetFunctionAt(index); | |
509 } | |
510 | |
511 ~WasmFunctionCompiler() { | |
512 if (testing_module_) return; // testing module owns the below things. | |
513 delete function_; | |
514 if (interpreter_) delete interpreter_; | |
515 } | |
516 | |
517 WasmExecutionMode execution_mode_; | |
518 JSGraph jsgraph; | |
519 FunctionSig* sig; | |
520 // The call descriptor is initialized when the function is compiled. | |
521 CallDescriptor* descriptor_; | |
522 TestingModule* testing_module_; | |
523 Vector<const char> debug_name_; | |
524 WasmFunction* function_; | |
525 LocalDeclEncoder local_decls; | |
526 SourcePositionTable source_position_table_; | |
527 WasmInterpreter* interpreter_; | |
528 | |
529 Isolate* isolate() { return main_isolate(); } | |
530 Graph* graph() const { return main_graph_; } | 442 Graph* graph() const { return main_graph_; } |
531 Zone* zone() const { return graph()->zone(); } | 443 Zone* zone() const { return graph()->zone(); } |
532 CommonOperatorBuilder* common() { return &main_common_; } | 444 CommonOperatorBuilder* common() { return &main_common_; } |
533 MachineOperatorBuilder* machine() { return &main_machine_; } | 445 MachineOperatorBuilder* machine() { return &main_machine_; } |
534 void InitializeDescriptor() { | 446 void InitializeDescriptor() { |
535 if (descriptor_ == nullptr) { | 447 if (descriptor_ == nullptr) { |
536 descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig); | 448 descriptor_ = testing_module_->GetWasmCallDescriptor(zone(), sig); |
537 } | 449 } |
538 } | 450 } |
539 CallDescriptor* descriptor() { return descriptor_; } | 451 CallDescriptor* descriptor() { return descriptor_; } |
540 uint32_t function_index() { return function_->func_index; } | 452 uint32_t function_index() { return function_->func_index; } |
541 | 453 |
542 void Build(const byte* start, const byte* end) { | 454 void Build(const byte* start, const byte* end) { |
543 // Build the TurboFan graph. | 455 local_decls.Prepend(zone(), &start, &end); |
544 local_decls.Prepend(main_zone(), &start, &end); | |
545 TestBuildingGraph(main_zone(), &jsgraph, testing_module_, sig, | |
546 &source_position_table_, start, end); | |
547 if (interpreter_) { | 456 if (interpreter_) { |
548 // Add the code to the interpreter. | 457 // Add the code to the interpreter. |
549 CHECK(interpreter_->SetFunctionCodeForTesting(function_, start, end)); | 458 CHECK(interpreter_->SetFunctionCodeForTesting(function_, start, end)); |
459 return; | |
550 } | 460 } |
461 | |
462 // Build the TurboFan graph. | |
463 TestBuildingGraph(zone(), &jsgraph, testing_module_, sig, | |
464 &source_position_table_, start, end); | |
465 Handle<Code> code = Compile(); | |
466 testing_module_->SetFunctionCode(function_index(), code); | |
551 } | 467 } |
552 | 468 |
553 byte AllocateLocal(LocalType type) { | 469 byte AllocateLocal(LocalType type) { |
554 uint32_t index = local_decls.AddLocals(1, type); | 470 uint32_t index = local_decls.AddLocals(1, type); |
555 byte result = static_cast<byte>(index); | 471 byte result = static_cast<byte>(index); |
556 DCHECK_EQ(index, result); | 472 DCHECK_EQ(index, result); |
557 return result; | 473 return result; |
558 } | 474 } |
559 | 475 |
476 void SetSigIndex(int sig_index) { function_->sig_index = sig_index; } | |
477 | |
478 private: | |
479 template <typename ReturnType, typename... ParamTypes> | |
480 friend class WasmRunner; | |
481 | |
482 explicit WasmFunctionCompiler(Zone* zone, FunctionSig* sig, | |
483 TestingModule* module) | |
484 : GraphAndBuilders(zone), | |
485 jsgraph(module->isolate(), this->graph(), this->common(), nullptr, | |
486 nullptr, this->machine()), | |
487 sig(sig), | |
488 descriptor_(nullptr), | |
489 testing_module_(module), | |
490 local_decls(zone, sig), | |
491 source_position_table_(this->graph()), | |
492 interpreter_(module->interpreter()) { | |
493 // Get a new function from the testing module. | |
494 int index = module->AddFunction(sig, Handle<Code>::null()); | |
495 function_ = testing_module_->GetFunctionAt(index); | |
496 } | |
497 | |
560 Handle<Code> Compile() { | 498 Handle<Code> Compile() { |
561 InitializeDescriptor(); | 499 InitializeDescriptor(); |
562 CallDescriptor* desc = descriptor_; | 500 CallDescriptor* desc = descriptor_; |
563 if (kPointerSize == 4) { | 501 if (kPointerSize == 4) { |
564 desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc); | 502 desc = testing_module_->GetI32WasmCallDescriptor(this->zone(), desc); |
565 } | 503 } |
566 CompilationInfo info(debug_name_, this->isolate(), this->zone(), | 504 CompilationInfo info(CStrVector("wasm"), this->isolate(), this->zone(), |
567 Code::ComputeFlags(Code::WASM_FUNCTION)); | 505 Code::ComputeFlags(Code::WASM_FUNCTION)); |
568 std::unique_ptr<CompilationJob> job(Pipeline::NewWasmCompilationJob( | 506 std::unique_ptr<CompilationJob> job(Pipeline::NewWasmCompilationJob( |
569 &info, &jsgraph, desc, &source_position_table_, nullptr)); | 507 &info, &jsgraph, desc, &source_position_table_, nullptr)); |
570 if (job->ExecuteJob() != CompilationJob::SUCCEEDED || | 508 if (job->ExecuteJob() != CompilationJob::SUCCEEDED || |
571 job->FinalizeJob() != CompilationJob::SUCCEEDED) | 509 job->FinalizeJob() != CompilationJob::SUCCEEDED) |
572 return Handle<Code>::null(); | 510 return Handle<Code>::null(); |
573 | 511 |
574 Handle<Code> code = info.code(); | 512 Handle<Code> code = info.code(); |
575 | 513 |
576 // Length is always 2, since usually <wasm_obj, func_index> is stored in | 514 // Length is always 2, since usually <wasm_obj, func_index> is stored in |
577 // the deopt data. Here, we only store the function index. | 515 // the deopt data. Here, we only store the function index. |
578 DCHECK(code->deoptimization_data() == nullptr || | 516 DCHECK(code->deoptimization_data() == nullptr || |
579 code->deoptimization_data()->length() == 0); | 517 code->deoptimization_data()->length() == 0); |
580 Handle<FixedArray> deopt_data = | 518 Handle<FixedArray> deopt_data = |
581 isolate()->factory()->NewFixedArray(2, TENURED); | 519 isolate()->factory()->NewFixedArray(2, TENURED); |
582 deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index()))); | 520 deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index()))); |
583 deopt_data->set_length(2); | 521 deopt_data->set_length(2); |
584 code->set_deoptimization_data(*deopt_data); | 522 code->set_deoptimization_data(*deopt_data); |
585 | 523 |
586 #ifdef ENABLE_DISASSEMBLER | 524 #ifdef ENABLE_DISASSEMBLER |
587 if (FLAG_print_opt_code) { | 525 if (FLAG_print_opt_code) { |
588 OFStream os(stdout); | 526 OFStream os(stdout); |
589 code->Disassemble("wasm code", os); | 527 code->Disassemble("wasm code", os); |
590 } | 528 } |
591 #endif | 529 #endif |
592 | 530 |
593 return code; | 531 return code; |
594 } | 532 } |
595 | 533 |
596 uint32_t CompileAndAdd(uint16_t sig_index = 0) { | 534 JSGraph jsgraph; |
597 CHECK(testing_module_); | 535 FunctionSig* sig; |
598 function_->sig_index = sig_index; | 536 // The call descriptor is initialized when the function is compiled. |
599 Handle<Code> code = Compile(); | 537 CallDescriptor* descriptor_; |
600 testing_module_->SetFunctionCode(function_index(), code); | 538 TestingModule* testing_module_; |
601 return function_index(); | 539 Vector<const char> debug_name_; |
602 } | 540 WasmFunction* function_; |
603 | 541 LocalDeclEncoder local_decls; |
604 // Set the context, such that e.g. runtime functions can be called. | 542 SourcePositionTable source_position_table_; |
605 void SetModuleContext() { | 543 WasmInterpreter* interpreter_; |
606 if (!testing_module_->instance->context.is_null()) { | |
607 CHECK(testing_module_->instance->context.is_identical_to( | |
608 main_isolate()->native_context())); | |
609 return; | |
610 } | |
611 testing_module_->instance->context = main_isolate()->native_context(); | |
612 } | |
613 }; | 544 }; |
614 | 545 |
615 // A helper class to build graphs from Wasm bytecode, generate machine | 546 // A helper class to build a module around Wasm bytecode, generate machine |
616 // code, and run that code. | 547 // code, and run that code. |
617 template <typename ReturnType> | 548 template <typename MainReturnType, typename... MainParamTypes> |
618 class WasmRunner { | 549 class WasmRunner : public HandleAndZoneScope { |
titzer
2016/12/14 17:31:14
Can you factor out a base class to avoid duplicati
Clemens Hammacher
2016/12/14 20:41:38
Done.
| |
619 public: | 550 public: |
620 WasmRunner(WasmExecutionMode execution_mode, | 551 explicit WasmRunner(WasmExecutionMode execution_mode) |
621 MachineType p0 = MachineType::None(), | 552 : zone_(&allocator_, ZONE_NAME), |
622 MachineType p1 = MachineType::None(), | 553 module_(&zone_, execution_mode), |
623 MachineType p2 = MachineType::None(), | 554 wrapper_(&zone_, sizeof...(MainParamTypes)) { |
624 MachineType p3 = MachineType::None()) | 555 NewFunction<MainReturnType, MainParamTypes...>(); |
625 : zone(&allocator_, ZONE_NAME), | |
626 compiled_(false), | |
627 signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, | |
628 GetParameterCount(p0, p1, p2, p3), storage_), | |
629 compiler_(&signature_, execution_mode) { | |
630 InitSigStorage(p0, p1, p2, p3); | |
631 } | |
632 | |
633 WasmRunner(TestingModule* module, MachineType p0 = MachineType::None(), | |
634 MachineType p1 = MachineType::None(), | |
635 MachineType p2 = MachineType::None(), | |
636 MachineType p3 = MachineType::None()) | |
637 : zone(&allocator_, ZONE_NAME), | |
638 compiled_(false), | |
639 signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, | |
640 GetParameterCount(p0, p1, p2, p3), storage_), | |
641 compiler_(&signature_, module), | |
642 possible_nondeterminism_(false) { | |
643 DCHECK(module); | |
644 InitSigStorage(p0, p1, p2, p3); | |
645 } | |
646 | |
647 void InitSigStorage(MachineType p0, MachineType p1, MachineType p2, | |
648 MachineType p3) { | |
649 int index = 0; | |
650 MachineType ret = MachineTypeForC<ReturnType>(); | |
651 if (ret != MachineType::None()) { | |
652 storage_[index++] = WasmOpcodes::LocalTypeFor(ret); | |
653 } | |
654 if (p0 != MachineType::None()) | |
655 storage_[index++] = WasmOpcodes::LocalTypeFor(p0); | |
656 if (p1 != MachineType::None()) | |
657 storage_[index++] = WasmOpcodes::LocalTypeFor(p1); | |
658 if (p2 != MachineType::None()) | |
659 storage_[index++] = WasmOpcodes::LocalTypeFor(p2); | |
660 if (p3 != MachineType::None()) | |
661 storage_[index++] = WasmOpcodes::LocalTypeFor(p3); | |
662 | |
663 compiler_.InitializeDescriptor(); | |
664 wrapper_.Init(compiler_.descriptor(), p0, p1, p2, p3); | |
665 } | 556 } |
666 | 557 |
667 // Builds a graph from the given Wasm code and generates the machine | 558 // Builds a graph from the given Wasm code and generates the machine |
668 // code and call wrapper for that graph. This method must not be called | 559 // code and call wrapper for that graph. This method must not be called |
669 // more than once. | 560 // more than once. |
670 void Build(const byte* start, const byte* end) { | 561 void Build(const byte* start, const byte* end) { |
671 CHECK(!compiled_); | 562 CHECK(!compiled_); |
672 compiled_ = true; | 563 compiled_ = true; |
673 compiler_.Build(start, end); | 564 functions_[0]->Build(start, end); |
674 | |
675 if (!interpret()) { | |
676 // Compile machine code and install it into the module. | |
677 Handle<Code> code = compiler_.Compile(); | |
678 | |
679 if (compiler_.testing_module_) { | |
680 // Update the table of function code in the module. | |
681 compiler_.testing_module_->SetFunctionCode( | |
682 compiler_.function_->func_index, code); | |
683 } | |
684 | |
685 wrapper_.SetInnerCode(code); | |
686 } | |
687 } | 565 } |
688 | 566 |
689 ReturnType Call() { | 567 // Resets the state for building the next function. |
690 if (interpret()) { | 568 // The main function called will always be the first function. |
691 return CallInterpreter(Vector<WasmVal>(nullptr, 0)); | 569 template <typename NextReturnType, typename... NextParamTypes> |
692 } else { | 570 WasmFunctionCompiler& NewFunction() { |
693 return Call(0, 0, 0, 0); | 571 return NewFunction(CreateSig<NextReturnType, NextParamTypes...>()); |
694 } | |
695 } | 572 } |
696 | 573 |
697 template <typename P0> | 574 // Resets the state for building the next function. |
698 ReturnType Call(P0 p0) { | 575 // The main function called will be the last generated function. |
699 if (interpret()) { | 576 // Returns the index of the previously built function. |
700 WasmVal args[] = {WasmVal(p0)}; | 577 WasmFunctionCompiler& NewFunction(FunctionSig* sig) { |
701 return CallInterpreter(ArrayVector(args)); | 578 functions_.emplace_back(new WasmFunctionCompiler(&zone_, sig, &module_)); |
702 } else { | 579 return *functions_.back(); |
703 return Call(p0, 0, 0, 0); | |
704 } | |
705 } | 580 } |
706 | 581 |
707 template <typename P0, typename P1> | 582 MainReturnType Call(MainParamTypes... p) { |
708 ReturnType Call(P0 p0, P1 p1) { | 583 DCHECK(compiled_); |
709 if (interpret()) { | 584 if (interpret()) return CallInterpreter(p...); |
710 WasmVal args[] = {WasmVal(p0), WasmVal(p1)}; | 585 |
711 return CallInterpreter(ArrayVector(args)); | 586 wrapper_.template Init<MainParamTypes...>(functions_[0]->descriptor()); |
712 } else { | 587 wrapper_.SetInnerCode( |
713 return Call(p0, p1, 0, 0); | 588 module_.GetFunctionCode(functions_[0]->function_index())); |
714 } | 589 CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(), |
590 wrapper_.GetWrapperCode(), wrapper_.signature()); | |
591 MainReturnType return_value; | |
592 int32_t result = runner.Call(static_cast<void*>(&p)..., | |
593 static_cast<void*>(&return_value)); | |
594 CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result); | |
595 return return_value; | |
715 } | 596 } |
716 | 597 |
717 template <typename P0, typename P1, typename P2> | 598 MainReturnType CallInterpreter(MainParamTypes... p) { |
718 ReturnType Call(P0 p0, P1 p1, P2 p2) { | |
719 if (interpret()) { | |
720 WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2)}; | |
721 return CallInterpreter(ArrayVector(args)); | |
722 } else { | |
723 return Call(p0, p1, p2, 0); | |
724 } | |
725 } | |
726 | |
727 template <typename P0, typename P1, typename P2, typename P3> | |
728 ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) { | |
729 if (interpret()) { | |
730 WasmVal args[] = {WasmVal(p0), WasmVal(p1), WasmVal(p2), WasmVal(p3)}; | |
731 return CallInterpreter(ArrayVector(args)); | |
732 } else { | |
733 CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(), | |
734 wrapper_.GetWrapperCode(), | |
735 wrapper_.signature()); | |
736 ReturnType return_value; | |
737 int32_t result = runner.Call<void*, void*, void*, void*, void*>( | |
738 &p0, &p1, &p2, &p3, &return_value); | |
739 CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result); | |
740 return return_value; | |
741 } | |
742 } | |
743 | |
744 ReturnType CallInterpreter(Vector<WasmVal> args) { | |
745 CHECK_EQ(args.length(), | |
746 static_cast<int>(compiler_.function_->sig->parameter_count())); | |
747 WasmInterpreter::Thread* thread = interpreter()->GetThread(0); | 599 WasmInterpreter::Thread* thread = interpreter()->GetThread(0); |
748 thread->Reset(); | 600 thread->Reset(); |
749 thread->PushFrame(compiler_.function_, args.start()); | 601 std::array<WasmVal, sizeof...(p)> args{{WasmVal(p)...}}; |
602 thread->PushFrame(functions_[0]->function_, args.data()); | |
750 if (thread->Run() == WasmInterpreter::FINISHED) { | 603 if (thread->Run() == WasmInterpreter::FINISHED) { |
751 WasmVal val = thread->GetReturnValue(); | 604 WasmVal val = thread->GetReturnValue(); |
752 possible_nondeterminism_ |= thread->PossibleNondeterminism(); | 605 possible_nondeterminism_ |= thread->PossibleNondeterminism(); |
753 return val.to<ReturnType>(); | 606 return val.to<MainReturnType>(); |
754 } else if (thread->state() == WasmInterpreter::TRAPPED) { | 607 } else if (thread->state() == WasmInterpreter::TRAPPED) { |
755 // TODO(titzer): return the correct trap code | 608 // TODO(titzer): return the correct trap code |
756 int64_t result = 0xdeadbeefdeadbeef; | 609 int64_t result = 0xdeadbeefdeadbeef; |
757 return static_cast<ReturnType>(result); | 610 return static_cast<MainReturnType>(result); |
758 } else { | 611 } else { |
759 // TODO(titzer): falling off end | 612 // TODO(titzer): falling off end |
760 ReturnType val = 0; | 613 MainReturnType val = 0; |
761 return val; | 614 return val; |
762 } | 615 } |
763 } | 616 } |
764 | 617 |
765 byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); } | 618 byte AllocateLocal(LocalType type) { |
619 return functions_[0]->AllocateLocal(type); | |
620 } | |
766 | 621 |
767 WasmFunction* function() { return compiler_.function_; } | 622 WasmFunction* function() { return functions_[0]->function_; } |
768 WasmInterpreter* interpreter() { return compiler_.interpreter_; } | 623 WasmInterpreter* interpreter() { return functions_[0]->interpreter_; } |
769 bool possible_nondeterminism() { return possible_nondeterminism_; } | 624 bool possible_nondeterminism() { return possible_nondeterminism_; } |
625 TestingModule& module() { return module_; } | |
626 Zone* zone() { return &zone_; } | |
770 | 627 |
771 protected: | 628 // Set the context, such that e.g. runtime functions can be called. |
629 void SetModuleContext() { | |
630 if (!module_.instance->context.is_null()) { | |
631 CHECK(module_.instance->context.is_identical_to( | |
632 main_isolate()->native_context())); | |
633 return; | |
634 } | |
635 module_.instance->context = main_isolate()->native_context(); | |
636 } | |
637 | |
638 private: | |
639 template <typename ReturnType, typename... ParamTypes> | |
640 FunctionSig* CreateSig() { | |
641 MachineType ret_and_params[] = {MachineTypeForC<ReturnType>(), | |
642 MachineTypeForC<ParamTypes>()...}; | |
643 size_t return_count = ret_and_params[0] == MachineType::None() ? 0 : 1; | |
titzer
2016/12/14 17:31:14
Same here. Can you factor this out to be an entryp
Clemens Hammacher
2016/12/14 20:41:38
Done.
| |
644 size_t param_count = sizeof...(ParamTypes); | |
645 | |
646 // Allocate storage array in zone. | |
647 LocalType* sig_types = | |
648 zone_.NewArray<LocalType>(return_count + param_count); | |
649 | |
650 // Convert machine types to local types, and check that there are no | |
651 // MachineType::None()'s in the parameters. | |
652 if (return_count) | |
653 sig_types[0] = WasmOpcodes::LocalTypeFor(ret_and_params[0]); | |
654 for (size_t i = 0; i < param_count; ++i) { | |
655 CHECK_NE(MachineType::None(), ret_and_params[i + 1]); | |
656 sig_types[i + return_count] = | |
657 WasmOpcodes::LocalTypeFor(ret_and_params[i + 1]); | |
658 } | |
659 return new (&zone_) FunctionSig(return_count, param_count, sig_types); | |
660 } | |
772 v8::internal::AccountingAllocator allocator_; | 661 v8::internal::AccountingAllocator allocator_; |
773 Zone zone; | 662 Zone zone_; |
774 bool compiled_; | 663 TestingModule module_; |
775 LocalType storage_[WASM_RUNNER_MAX_NUM_PARAMETERS]; | 664 std::vector<std::unique_ptr<WasmFunctionCompiler>> functions_; |
776 FunctionSig signature_; | 665 WasmFunctionWrapper<MainReturnType> wrapper_; |
777 WasmFunctionCompiler compiler_; | 666 bool compiled_ = false; |
778 WasmFunctionWrapper<ReturnType> wrapper_; | 667 bool possible_nondeterminism_ = false; |
779 bool possible_nondeterminism_; | |
780 | 668 |
781 bool interpret() { return compiler_.execution_mode_ == kExecuteInterpreted; } | 669 bool interpret() { return module_.execution_mode() == kExecuteInterpreted; } |
782 | |
783 static size_t GetParameterCount(MachineType p0, MachineType p1, | |
784 MachineType p2, MachineType p3) { | |
785 if (p0 == MachineType::None()) return 0; | |
786 if (p1 == MachineType::None()) return 1; | |
787 if (p2 == MachineType::None()) return 2; | |
788 if (p3 == MachineType::None()) return 3; | |
789 return 4; | |
790 } | |
791 }; | 670 }; |
792 | 671 |
793 // A macro to define tests that run in different engine configurations. | 672 // A macro to define tests that run in different engine configurations. |
794 #define WASM_EXEC_TEST(name) \ | 673 #define WASM_EXEC_TEST(name) \ |
795 void RunWasm_##name(WasmExecutionMode execution_mode); \ | 674 void RunWasm_##name(WasmExecutionMode execution_mode); \ |
796 TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \ | 675 TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \ |
797 TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \ | 676 TEST(RunWasmInterpreted_##name) { RunWasm_##name(kExecuteInterpreted); } \ |
798 void RunWasm_##name(WasmExecutionMode execution_mode) | 677 void RunWasm_##name(WasmExecutionMode execution_mode) |
799 | 678 |
800 #define WASM_EXEC_COMPILED_TEST(name) \ | 679 #define WASM_EXEC_COMPILED_TEST(name) \ |
801 void RunWasm_##name(WasmExecutionMode execution_mode); \ | 680 void RunWasm_##name(WasmExecutionMode execution_mode); \ |
802 TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \ | 681 TEST(RunWasmCompiled_##name) { RunWasm_##name(kExecuteCompiled); } \ |
803 void RunWasm_##name(WasmExecutionMode execution_mode) | 682 void RunWasm_##name(WasmExecutionMode execution_mode) |
804 | 683 |
805 } // namespace | 684 } // namespace |
806 | 685 |
807 #endif | 686 #endif |
OLD | NEW |