• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
execution_mode_(mode)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 
~TestingModule()91   ~TestingModule() {
92     if (instance->mem_start) {
93       free(instance->mem_start);
94     }
95     if (interpreter_) delete interpreter_;
96   }
97 
AddMemory(uint32_t size)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>
AddMemoryElems(uint32_t count)109   T* AddMemoryElems(uint32_t count) {
110     AddMemory(count * sizeof(T));
111     return raw_mem_start<T>();
112   }
113 
114   template <typename T>
AddGlobal(MachineType mem_type)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 
AddSignature(FunctionSig * sig)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>
raw_mem_start()128   T* raw_mem_start() {
129     DCHECK(instance->mem_start);
130     return reinterpret_cast<T*>(instance->mem_start);
131   }
132 
133   template <typename T>
raw_mem_end()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>
raw_mem_at(int i)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>
raw_val_at(int i)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.
BlankMemory()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 
AddFunction(FunctionSig * sig,Handle<Code> code)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 
AddJsFunction(FunctionSig * sig,const char * source)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 
WrapCode(uint32_t index)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 
SetFunctionCode(uint32_t index,Handle<Code> code)207   void SetFunctionCode(uint32_t index, Handle<Code> code) {
208     instance->function_code[index] = code;
209   }
210 
AddIndirectFunctionTable(int * functions,int table_size)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 
PopulateIndirectFunctionTable()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   }
GetFunctionAt(int index)232   WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; }
233 
interpreter()234   WasmInterpreter* interpreter() { return interpreter_; }
execution_mode()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 
AddGlobal(MachineType mem_type)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 
TestBuildingGraph(Zone * zone,JSGraph * jsgraph,ModuleEnv * module,FunctionSig * sig,SourcePositionTable * source_position_table,const byte * start,const byte * end)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:
WasmFunctionWrapper()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 
SetInnerCode(Handle<Code> code_handle)372   void SetInnerCode(Handle<Code> code_handle) {
373     NodeProperties::ChangeOp(inner_code_node_,
374                              common()->HeapConstant(code_handle));
375   }
376 
GetWrapperCode()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 
signature()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>"))
GraphAndBuilders(main_zone ())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>"))
GraphAndBuilders(main_zone ())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 
~WasmFunctionCompiler()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 
isolate()491   Isolate* isolate() { return main_isolate(); }
graph()492   Graph* graph() const { return main_graph_; }
zone()493   Zone* zone() const { return graph()->zone(); }
common()494   CommonOperatorBuilder* common() { return &main_common_; }
machine()495   MachineOperatorBuilder* machine() { return &main_machine_; }
InitializeDescriptor()496   void InitializeDescriptor() {
497     if (descriptor_ == nullptr) {
498       descriptor_ = testing_module_->GetWasmCallDescriptor(main_zone(), sig);
499     }
500   }
descriptor()501   CallDescriptor* descriptor() { return descriptor_; }
function_index()502   uint32_t function_index() { return function_->func_index; }
503 
Build(const byte * start,const byte * end)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 
AllocateLocal(LocalType type)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 
Compile()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.
SetModuleContext()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 
InitSigStorage(MachineType p0,MachineType p1,MachineType p2,MachineType p3)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.
Build(const byte * start,const byte * end)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 
Call()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>
Call(P0 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>
Call(P0 p0,P1 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>
Call(P0 p0,P1 p1,P2 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>
Call(P0 p0,P1 p1,P2 p2,P3 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 
CallInterpreter(Vector<WasmVal> args)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 
AllocateLocal(LocalType type)725   byte AllocateLocal(LocalType type) { return compiler_.AllocateLocal(type); }
726 
function()727   WasmFunction* function() { return compiler_.function_; }
interpreter()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 
interpret()739   bool interpret() { return compiler_.execution_mode_ == kExecuteInterpreted; }
740 
GetParameterCount(MachineType p0,MachineType p1,MachineType p2,MachineType p3)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