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/utils/random-number-generator.h"
     13 
     14 #include "src/compiler/graph-visualizer.h"
     15 #include "src/compiler/js-graph.h"
     16 #include "src/compiler/wasm-compiler.h"
     17 
     18 #include "src/wasm/ast-decoder.h"
     19 #include "src/wasm/wasm-js.h"
     20 #include "src/wasm/wasm-module.h"
     21 #include "src/wasm/wasm-opcodes.h"
     22 
     23 #include "test/cctest/cctest.h"
     24 #include "test/cctest/compiler/codegen-tester.h"
     25 #include "test/cctest/compiler/graph-builder-tester.h"
     26 
     27 // TODO(titzer): pull WASM_64 up to a common header.
     28 #if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64
     29 #define WASM_64 1
     30 #else
     31 #define WASM_64 0
     32 #endif
     33 
     34 // TODO(titzer): check traps more robustly in tests.
     35 // Currently, in tests, we just return 0xdeadbeef from the function in which
     36 // the trap occurs if the runtime context is not available to throw a JavaScript
     37 // exception.
     38 #define CHECK_TRAP32(x) \
     39   CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF)
     40 #define CHECK_TRAP64(x) \
     41   CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
     42 #define CHECK_TRAP(x) CHECK_TRAP32(x)
     43 
     44 namespace {
     45 using namespace v8::base;
     46 using namespace v8::internal;
     47 using namespace v8::internal::compiler;
     48 using namespace v8::internal::wasm;
     49 
     50 inline void init_env(FunctionEnv* env, FunctionSig* sig) {
     51   env->module = nullptr;
     52   env->sig = sig;
     53   env->local_int32_count = 0;
     54   env->local_int64_count = 0;
     55   env->local_float32_count = 0;
     56   env->local_float64_count = 0;
     57   env->SumLocals();
     58 }
     59 
     60 const uint32_t kMaxGlobalsSize = 128;
     61 
     62 // A helper for module environments that adds the ability to allocate memory
     63 // and global variables.
     64 class TestingModule : public ModuleEnv {
     65  public:
     66   TestingModule() : mem_size(0), global_offset(0) {
     67     globals_area = 0;
     68     mem_start = 0;
     69     mem_end = 0;
     70     module = nullptr;
     71     linker = nullptr;
     72     function_code = nullptr;
     73     asm_js = false;
     74     memset(global_data, 0, sizeof(global_data));
     75   }
     76 
     77   ~TestingModule() {
     78     if (mem_start) {
     79       free(raw_mem_start<byte>());
     80     }
     81     if (function_code) delete function_code;
     82     if (module) delete module;
     83   }
     84 
     85   byte* AddMemory(size_t size) {
     86     CHECK_EQ(0, mem_start);
     87     CHECK_EQ(0, mem_size);
     88     mem_start = reinterpret_cast<uintptr_t>(malloc(size));
     89     CHECK(mem_start);
     90     byte* raw = raw_mem_start<byte>();
     91     memset(raw, 0, size);
     92     mem_end = mem_start + size;
     93     mem_size = size;
     94     return raw_mem_start<byte>();
     95   }
     96 
     97   template <typename T>
     98   T* AddMemoryElems(size_t count) {
     99     AddMemory(count * sizeof(T));
    100     return raw_mem_start<T>();
    101   }
    102 
    103   template <typename T>
    104   T* AddGlobal(MachineType mem_type) {
    105     WasmGlobal* global = AddGlobal(mem_type);
    106     return reinterpret_cast<T*>(globals_area + global->offset);
    107   }
    108 
    109   byte AddSignature(FunctionSig* sig) {
    110     AllocModule();
    111     if (!module->signatures) {
    112       module->signatures = new std::vector<FunctionSig*>();
    113     }
    114     module->signatures->push_back(sig);
    115     size_t size = module->signatures->size();
    116     CHECK(size < 127);
    117     return static_cast<byte>(size - 1);
    118   }
    119 
    120   template <typename T>
    121   T* raw_mem_start() {
    122     DCHECK(mem_start);
    123     return reinterpret_cast<T*>(mem_start);
    124   }
    125 
    126   template <typename T>
    127   T* raw_mem_end() {
    128     DCHECK(mem_end);
    129     return reinterpret_cast<T*>(mem_end);
    130   }
    131 
    132   template <typename T>
    133   T raw_mem_at(int i) {
    134     DCHECK(mem_start);
    135     return reinterpret_cast<T*>(mem_start)[i];
    136   }
    137 
    138   template <typename T>
    139   T raw_val_at(int i) {
    140     T val;
    141     memcpy(&val, reinterpret_cast<void*>(mem_start + i), sizeof(T));
    142     return val;
    143   }
    144 
    145   // Zero-initialize the memory.
    146   void BlankMemory() {
    147     byte* raw = raw_mem_start<byte>();
    148     memset(raw, 0, mem_size);
    149   }
    150 
    151   // Pseudo-randomly intialize the memory.
    152   void RandomizeMemory(unsigned int seed = 88) {
    153     byte* raw = raw_mem_start<byte>();
    154     byte* end = raw_mem_end<byte>();
    155     v8::base::RandomNumberGenerator rng;
    156     rng.SetSeed(seed);
    157     rng.NextBytes(raw, end - raw);
    158   }
    159 
    160   WasmFunction* AddFunction(FunctionSig* sig, Handle<Code> code) {
    161     AllocModule();
    162     if (module->functions == nullptr) {
    163       module->functions = new std::vector<WasmFunction>();
    164       function_code = new std::vector<Handle<Code>>();
    165     }
    166     module->functions->push_back({sig, 0, 0, 0, 0, 0, 0, 0, false, false});
    167     function_code->push_back(code);
    168     return &module->functions->back();
    169   }
    170 
    171  private:
    172   size_t mem_size;
    173   uint32_t global_offset;
    174   byte global_data[kMaxGlobalsSize];
    175 
    176   WasmGlobal* AddGlobal(MachineType mem_type) {
    177     AllocModule();
    178     if (globals_area == 0) {
    179       globals_area = reinterpret_cast<uintptr_t>(global_data);
    180       module->globals = new std::vector<WasmGlobal>();
    181     }
    182     byte size = WasmOpcodes::MemSize(mem_type);
    183     global_offset = (global_offset + size - 1) & ~(size - 1);  // align
    184     module->globals->push_back({0, mem_type, global_offset, false});
    185     global_offset += size;
    186     // limit number of globals.
    187     CHECK_LT(global_offset, kMaxGlobalsSize);
    188     return &module->globals->back();
    189   }
    190   void AllocModule() {
    191     if (module == nullptr) {
    192       module = new WasmModule();
    193       module->shared_isolate = CcTest::InitIsolateOnce();
    194       module->globals = nullptr;
    195       module->functions = nullptr;
    196       module->data_segments = nullptr;
    197     }
    198   }
    199 };
    200 
    201 
    202 inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, FunctionEnv* env,
    203                               const byte* start, const byte* end) {
    204   compiler::WasmGraphBuilder builder(zone, jsgraph, env->sig);
    205   TreeResult result = BuildTFGraph(&builder, env, start, end);
    206   if (result.failed()) {
    207     ptrdiff_t pc = result.error_pc - result.start;
    208     ptrdiff_t pt = result.error_pt - result.start;
    209     std::ostringstream str;
    210     str << "Verification failed: " << result.error_code << " pc = +" << pc;
    211     if (result.error_pt) str << ", pt = +" << pt;
    212     str << ", msg = " << result.error_msg.get();
    213     FATAL(str.str().c_str());
    214   }
    215   if (FLAG_trace_turbo_graph) {
    216     OFStream os(stdout);
    217     os << AsRPO(*jsgraph->graph());
    218   }
    219 }
    220 
    221 
    222 // A helper for compiling functions that are only internally callable WASM code.
    223 class WasmFunctionCompiler : public HandleAndZoneScope,
    224                              private GraphAndBuilders {
    225  public:
    226   explicit WasmFunctionCompiler(FunctionSig* sig, ModuleEnv* module = nullptr)
    227       : GraphAndBuilders(main_zone()),
    228         jsgraph(this->isolate(), this->graph(), this->common(), nullptr,
    229                 nullptr, this->machine()),
    230         descriptor_(nullptr) {
    231     init_env(&env, sig);
    232     env.module = module;
    233   }
    234 
    235   JSGraph jsgraph;
    236   FunctionEnv env;
    237   // The call descriptor is initialized when the function is compiled.
    238   CallDescriptor* descriptor_;
    239 
    240   Isolate* isolate() { return main_isolate(); }
    241   Graph* graph() const { return main_graph_; }
    242   Zone* zone() const { return graph()->zone(); }
    243   CommonOperatorBuilder* common() { return &main_common_; }
    244   MachineOperatorBuilder* machine() { return &main_machine_; }
    245   CallDescriptor* descriptor() { return descriptor_; }
    246 
    247   void Build(const byte* start, const byte* end) {
    248     TestBuildingGraph(main_zone(), &jsgraph, &env, start, end);
    249   }
    250 
    251   byte AllocateLocal(LocalType type) {
    252     int result = static_cast<int>(env.total_locals);
    253     env.AddLocals(type, 1);
    254     byte b = static_cast<byte>(result);
    255     CHECK_EQ(result, b);
    256     return b;
    257   }
    258 
    259   Handle<Code> Compile(ModuleEnv* module) {
    260     descriptor_ = module->GetWasmCallDescriptor(this->zone(), env.sig);
    261     CompilationInfo info("wasm compile", this->isolate(), this->zone());
    262     Handle<Code> result =
    263         Pipeline::GenerateCodeForTesting(&info, descriptor_, this->graph());
    264 #ifdef ENABLE_DISASSEMBLER
    265     if (!result.is_null() && FLAG_print_opt_code) {
    266       OFStream os(stdout);
    267       result->Disassemble("wasm code", os);
    268     }
    269 #endif
    270 
    271     return result;
    272   }
    273 
    274   uint32_t CompileAndAdd(TestingModule* module) {
    275     uint32_t index = 0;
    276     if (module->module && module->module->functions) {
    277       index = static_cast<uint32_t>(module->module->functions->size());
    278     }
    279     module->AddFunction(env.sig, Compile(module));
    280     return index;
    281   }
    282 };
    283 
    284 
    285 // A helper class to build graphs from Wasm bytecode, generate machine
    286 // code, and run that code.
    287 template <typename ReturnType>
    288 class WasmRunner {
    289  public:
    290   WasmRunner(MachineType p0 = MachineType::None(),
    291              MachineType p1 = MachineType::None(),
    292              MachineType p2 = MachineType::None(),
    293              MachineType p3 = MachineType::None())
    294       : signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1,
    295                    GetParameterCount(p0, p1, p2, p3), storage_),
    296         compiler_(&signature_),
    297         call_wrapper_(p0, p1, p2, p3),
    298         compilation_done_(false) {
    299     int index = 0;
    300     MachineType ret = MachineTypeForC<ReturnType>();
    301     if (ret != MachineType::None()) {
    302       storage_[index++] = WasmOpcodes::LocalTypeFor(ret);
    303     }
    304     if (p0 != MachineType::None())
    305       storage_[index++] = WasmOpcodes::LocalTypeFor(p0);
    306     if (p1 != MachineType::None())
    307       storage_[index++] = WasmOpcodes::LocalTypeFor(p1);
    308     if (p2 != MachineType::None())
    309       storage_[index++] = WasmOpcodes::LocalTypeFor(p2);
    310     if (p3 != MachineType::None())
    311       storage_[index++] = WasmOpcodes::LocalTypeFor(p3);
    312   }
    313 
    314 
    315   FunctionEnv* env() { return &compiler_.env; }
    316 
    317 
    318   // Builds a graph from the given Wasm code, and generates the machine
    319   // code and call wrapper for that graph. This method must not be called
    320   // more than once.
    321   void Build(const byte* start, const byte* end) {
    322     DCHECK(!compilation_done_);
    323     compilation_done_ = true;
    324     // Build the TF graph.
    325     compiler_.Build(start, end);
    326     // Generate code.
    327     Handle<Code> code = compiler_.Compile(env()->module);
    328 
    329     // Construct the call wrapper.
    330     Node* inputs[5];
    331     int input_count = 0;
    332     inputs[input_count++] = call_wrapper_.HeapConstant(code);
    333     for (size_t i = 0; i < signature_.parameter_count(); i++) {
    334       inputs[input_count++] = call_wrapper_.Parameter(i);
    335     }
    336 
    337     call_wrapper_.Return(call_wrapper_.AddNode(
    338         call_wrapper_.common()->Call(compiler_.descriptor()), input_count,
    339         inputs));
    340   }
    341 
    342   ReturnType Call() { return call_wrapper_.Call(); }
    343 
    344   template <typename P0>
    345   ReturnType Call(P0 p0) {
    346     return call_wrapper_.Call(p0);
    347   }
    348 
    349   template <typename P0, typename P1>
    350   ReturnType Call(P0 p0, P1 p1) {
    351     return call_wrapper_.Call(p0, p1);
    352   }
    353 
    354   template <typename P0, typename P1, typename P2>
    355   ReturnType Call(P0 p0, P1 p1, P2 p2) {
    356     return call_wrapper_.Call(p0, p1, p2);
    357   }
    358 
    359   template <typename P0, typename P1, typename P2, typename P3>
    360   ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) {
    361     return call_wrapper_.Call(p0, p1, p2, p3);
    362   }
    363 
    364   byte AllocateLocal(LocalType type) {
    365     int result = static_cast<int>(env()->total_locals);
    366     env()->AddLocals(type, 1);
    367     byte b = static_cast<byte>(result);
    368     CHECK_EQ(result, b);
    369     return b;
    370   }
    371 
    372  private:
    373   LocalType storage_[5];
    374   FunctionSig signature_;
    375   WasmFunctionCompiler compiler_;
    376   BufferedRawMachineAssemblerTester<ReturnType> call_wrapper_;
    377   bool compilation_done_;
    378 
    379   static size_t GetParameterCount(MachineType p0, MachineType p1,
    380                                   MachineType p2, MachineType p3) {
    381     if (p0 == MachineType::None()) return 0;
    382     if (p1 == MachineType::None()) return 1;
    383     if (p2 == MachineType::None()) return 2;
    384     if (p3 == MachineType::None()) return 3;
    385     return 4;
    386   }
    387 };
    388 
    389 }  // namespace
    390 
    391 #endif
    392