Home | History | Annotate | Download | only in wasm
      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