Home | History | Annotate | Download | only in wasm
      1 // Copyright 2015 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 V8_WASM_MODULE_H_
      6 #define V8_WASM_MODULE_H_
      7 
      8 #include <memory>
      9 
     10 #include "src/api.h"
     11 #include "src/debug/debug-interface.h"
     12 #include "src/globals.h"
     13 #include "src/handles.h"
     14 #include "src/managed.h"
     15 #include "src/parsing/preparse-data.h"
     16 
     17 #include "src/wasm/signature-map.h"
     18 #include "src/wasm/wasm-opcodes.h"
     19 
     20 namespace v8 {
     21 namespace internal {
     22 
     23 class WasmCompiledModule;
     24 class WasmDebugInfo;
     25 class WasmModuleObject;
     26 class WasmInstanceObject;
     27 class WasmMemoryObject;
     28 
     29 namespace compiler {
     30 class CallDescriptor;
     31 }
     32 
     33 namespace wasm {
     34 class ErrorThrower;
     35 
     36 enum WasmExternalKind {
     37   kExternalFunction = 0,
     38   kExternalTable = 1,
     39   kExternalMemory = 2,
     40   kExternalGlobal = 3
     41 };
     42 
     43 // Representation of an initializer expression.
     44 struct WasmInitExpr {
     45   enum WasmInitKind {
     46     kNone,
     47     kGlobalIndex,
     48     kI32Const,
     49     kI64Const,
     50     kF32Const,
     51     kF64Const
     52   } kind;
     53 
     54   union {
     55     int32_t i32_const;
     56     int64_t i64_const;
     57     float f32_const;
     58     double f64_const;
     59     uint32_t global_index;
     60   } val;
     61 
     62   WasmInitExpr() : kind(kNone) {}
     63   explicit WasmInitExpr(int32_t v) : kind(kI32Const) { val.i32_const = v; }
     64   explicit WasmInitExpr(int64_t v) : kind(kI64Const) { val.i64_const = v; }
     65   explicit WasmInitExpr(float v) : kind(kF32Const) { val.f32_const = v; }
     66   explicit WasmInitExpr(double v) : kind(kF64Const) { val.f64_const = v; }
     67   WasmInitExpr(WasmInitKind kind, uint32_t global_index) : kind(kGlobalIndex) {
     68     val.global_index = global_index;
     69   }
     70 };
     71 
     72 // Static representation of a WASM function.
     73 struct WasmFunction {
     74   FunctionSig* sig;      // signature of the function.
     75   uint32_t func_index;   // index into the function table.
     76   uint32_t sig_index;    // index into the signature table.
     77   uint32_t name_offset;  // offset in the module bytes of the name, if any.
     78   uint32_t name_length;  // length in bytes of the name.
     79   uint32_t code_start_offset;    // offset in the module bytes of code start.
     80   uint32_t code_end_offset;      // offset in the module bytes of code end.
     81   bool imported;
     82   bool exported;
     83 };
     84 
     85 // Static representation of a wasm global variable.
     86 struct WasmGlobal {
     87   ValueType type;        // type of the global.
     88   bool mutability;       // {true} if mutable.
     89   WasmInitExpr init;     // the initialization expression of the global.
     90   uint32_t offset;       // offset into global memory.
     91   bool imported;         // true if imported.
     92   bool exported;         // true if exported.
     93 };
     94 
     95 // Static representation of a wasm data segment.
     96 struct WasmDataSegment {
     97   WasmInitExpr dest_addr;  // destination memory address of the data.
     98   uint32_t source_offset;  // start offset in the module bytes.
     99   uint32_t source_size;    // end offset in the module bytes.
    100 };
    101 
    102 // Static representation of a wasm indirect call table.
    103 struct WasmIndirectFunctionTable {
    104   uint32_t min_size;            // minimum table size.
    105   uint32_t max_size;            // maximum table size.
    106   bool has_max;                 // true if there is a maximum size.
    107   // TODO(titzer): Move this to WasmInstance. Needed by interpreter only.
    108   std::vector<int32_t> values;  // function table, -1 indicating invalid.
    109   bool imported;                // true if imported.
    110   bool exported;                // true if exported.
    111   SignatureMap map;             // canonicalizing map for sig indexes.
    112 };
    113 
    114 // Static representation of how to initialize a table.
    115 struct WasmTableInit {
    116   uint32_t table_index;
    117   WasmInitExpr offset;
    118   std::vector<uint32_t> entries;
    119 };
    120 
    121 // Static representation of a WASM import.
    122 struct WasmImport {
    123   uint32_t module_name_length;  // length in bytes of the module name.
    124   uint32_t module_name_offset;  // offset in module bytes of the module name.
    125   uint32_t field_name_length;   // length in bytes of the import name.
    126   uint32_t field_name_offset;   // offset in module bytes of the import name.
    127   WasmExternalKind kind;        // kind of the import.
    128   uint32_t index;               // index into the respective space.
    129 };
    130 
    131 // Static representation of a WASM export.
    132 struct WasmExport {
    133   uint32_t name_length;   // length in bytes of the exported name.
    134   uint32_t name_offset;   // offset in module bytes of the name to export.
    135   WasmExternalKind kind;  // kind of the export.
    136   uint32_t index;         // index into the respective space.
    137 };
    138 
    139 enum ModuleOrigin : uint8_t { kWasmOrigin, kAsmJsOrigin };
    140 struct ModuleWireBytes;
    141 
    142 // Static representation of a module.
    143 struct V8_EXPORT_PRIVATE WasmModule {
    144   static const uint32_t kPageSize = 0x10000;    // Page size, 64kb.
    145   static const uint32_t kMinMemPages = 1;       // Minimum memory size = 64kb
    146 
    147   Zone* owned_zone;
    148   uint32_t min_mem_pages = 0;  // minimum size of the memory in 64k pages
    149   uint32_t max_mem_pages = 0;  // maximum size of the memory in 64k pages
    150   bool has_max_mem = false;    // try if a maximum memory size exists
    151   bool has_memory = false;     // true if the memory was defined or imported
    152   bool mem_export = false;     // true if the memory is exported
    153   // TODO(wasm): reconcile start function index being an int with
    154   // the fact that we index on uint32_t, so we may technically not be
    155   // able to represent some start_function_index -es.
    156   int start_function_index = -1;      // start function, if any
    157   ModuleOrigin origin = kWasmOrigin;  // origin of the module
    158 
    159   std::vector<WasmGlobal> globals;             // globals in this module.
    160   uint32_t globals_size = 0;                   // size of globals table.
    161   uint32_t num_imported_functions = 0;         // number of imported functions.
    162   uint32_t num_declared_functions = 0;         // number of declared functions.
    163   uint32_t num_exported_functions = 0;         // number of exported functions.
    164   std::vector<FunctionSig*> signatures;        // signatures in this module.
    165   std::vector<WasmFunction> functions;         // functions in this module.
    166   std::vector<WasmDataSegment> data_segments;  // data segments in this module.
    167   std::vector<WasmIndirectFunctionTable> function_tables;  // function tables.
    168   std::vector<WasmImport> import_table;        // import table.
    169   std::vector<WasmExport> export_table;        // export table.
    170   std::vector<WasmTableInit> table_inits;      // initializations of tables
    171   // We store the semaphore here to extend its lifetime. In <libc-2.21, which we
    172   // use on the try bots, semaphore::Wait() can return while some compilation
    173   // tasks are still executing semaphore::Signal(). If the semaphore is cleaned
    174   // up right after semaphore::Wait() returns, then this can cause an
    175   // invalid-semaphore error in the compilation tasks.
    176   // TODO(wasm): Move this semaphore back to CompileInParallel when the try bots
    177   // switch to libc-2.21 or higher.
    178   std::unique_ptr<base::Semaphore> pending_tasks;
    179 
    180   WasmModule() : WasmModule(nullptr) {}
    181   WasmModule(Zone* owned_zone);
    182   ~WasmModule() {
    183     if (owned_zone) delete owned_zone;
    184   }
    185 };
    186 
    187 typedef Managed<WasmModule> WasmModuleWrapper;
    188 
    189 // An instantiated WASM module, including memory, function table, etc.
    190 struct WasmInstance {
    191   const WasmModule* module;  // static representation of the module.
    192   // -- Heap allocated --------------------------------------------------------
    193   Handle<Context> context;               // JavaScript native context.
    194   std::vector<Handle<FixedArray>> function_tables;  // indirect function tables.
    195   std::vector<Handle<FixedArray>>
    196       signature_tables;                    // indirect signature tables.
    197   std::vector<Handle<Code>> function_code;  // code objects for each function.
    198   // -- raw memory ------------------------------------------------------------
    199   byte* mem_start = nullptr;  // start of linear memory.
    200   uint32_t mem_size = 0;      // size of the linear memory.
    201   // -- raw globals -----------------------------------------------------------
    202   byte* globals_start = nullptr;  // start of the globals area.
    203 
    204   explicit WasmInstance(const WasmModule* m)
    205       : module(m),
    206         function_tables(m->function_tables.size()),
    207         signature_tables(m->function_tables.size()),
    208         function_code(m->functions.size()) {}
    209 };
    210 
    211 // Interface to the storage (wire bytes) of a wasm module.
    212 // It is illegal for anyone receiving a ModuleWireBytes to store pointers based
    213 // on module_bytes, as this storage is only guaranteed to be alive as long as
    214 // this struct is alive.
    215 struct V8_EXPORT_PRIVATE ModuleWireBytes {
    216   ModuleWireBytes(Vector<const byte> module_bytes)
    217       : module_bytes_(module_bytes) {}
    218   ModuleWireBytes(const byte* start, const byte* end)
    219       : module_bytes_(start, static_cast<int>(end - start)) {
    220     DCHECK_GE(kMaxInt, end - start);
    221   }
    222 
    223   // Get a string stored in the module bytes representing a name.
    224   WasmName GetName(uint32_t offset, uint32_t length) const {
    225     if (length == 0) return {"<?>", 3};  // no name.
    226     CHECK(BoundsCheck(offset, length));
    227     DCHECK_GE(length, 0);
    228     return Vector<const char>::cast(
    229         module_bytes_.SubVector(offset, offset + length));
    230   }
    231 
    232   // Get a string stored in the module bytes representing a function name.
    233   WasmName GetName(const WasmFunction* function) const {
    234     return GetName(function->name_offset, function->name_length);
    235   }
    236 
    237   // Get a string stored in the module bytes representing a name.
    238   WasmName GetNameOrNull(uint32_t offset, uint32_t length) const {
    239     if (offset == 0 && length == 0) return {NULL, 0};  // no name.
    240     CHECK(BoundsCheck(offset, length));
    241     DCHECK_GE(length, 0);
    242     return Vector<const char>::cast(
    243         module_bytes_.SubVector(offset, offset + length));
    244   }
    245 
    246   // Get a string stored in the module bytes representing a function name.
    247   WasmName GetNameOrNull(const WasmFunction* function) const {
    248     return GetNameOrNull(function->name_offset, function->name_length);
    249   }
    250 
    251   // Checks the given offset range is contained within the module bytes.
    252   bool BoundsCheck(uint32_t offset, uint32_t length) const {
    253     uint32_t size = static_cast<uint32_t>(module_bytes_.length());
    254     return offset <= size && length <= size - offset;
    255   }
    256 
    257   Vector<const byte> GetFunctionBytes(const WasmFunction* function) const {
    258     return module_bytes_.SubVector(function->code_start_offset,
    259                                    function->code_end_offset);
    260   }
    261 
    262   const byte* start() const { return module_bytes_.start(); }
    263   const byte* end() const { return module_bytes_.end(); }
    264   int length() const { return module_bytes_.length(); }
    265 
    266  private:
    267   const Vector<const byte> module_bytes_;
    268 };
    269 
    270 // Interface provided to the decoder/graph builder which contains only
    271 // minimal information about the globals, functions, and function tables.
    272 struct V8_EXPORT_PRIVATE ModuleEnv {
    273   ModuleEnv(const WasmModule* module, WasmInstance* instance)
    274       : module(module), instance(instance) {}
    275 
    276   const WasmModule* module;
    277   WasmInstance* instance;
    278 
    279   bool IsValidGlobal(uint32_t index) const {
    280     return module && index < module->globals.size();
    281   }
    282   bool IsValidFunction(uint32_t index) const {
    283     return module && index < module->functions.size();
    284   }
    285   bool IsValidSignature(uint32_t index) const {
    286     return module && index < module->signatures.size();
    287   }
    288   bool IsValidTable(uint32_t index) const {
    289     return module && index < module->function_tables.size();
    290   }
    291   ValueType GetGlobalType(uint32_t index) {
    292     DCHECK(IsValidGlobal(index));
    293     return module->globals[index].type;
    294   }
    295   FunctionSig* GetFunctionSignature(uint32_t index) {
    296     DCHECK(IsValidFunction(index));
    297     return module->functions[index].sig;
    298   }
    299   FunctionSig* GetSignature(uint32_t index) {
    300     DCHECK(IsValidSignature(index));
    301     return module->signatures[index];
    302   }
    303   const WasmIndirectFunctionTable* GetTable(uint32_t index) const {
    304     DCHECK(IsValidTable(index));
    305     return &module->function_tables[index];
    306   }
    307 
    308   bool asm_js() { return module->origin == kAsmJsOrigin; }
    309 
    310   Handle<Code> GetFunctionCode(uint32_t index) {
    311     DCHECK_NOT_NULL(instance);
    312     return instance->function_code[index];
    313   }
    314 
    315   // TODO(titzer): move these into src/compiler/wasm-compiler.cc
    316   static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone,
    317                                                          FunctionSig* sig);
    318   static compiler::CallDescriptor* GetI32WasmCallDescriptor(
    319       Zone* zone, compiler::CallDescriptor* descriptor);
    320   static compiler::CallDescriptor* GetI32WasmCallDescriptorForSimd(
    321       Zone* zone, compiler::CallDescriptor* descriptor);
    322 };
    323 
    324 // A ModuleEnv together with ModuleWireBytes.
    325 struct ModuleBytesEnv {
    326   ModuleBytesEnv(const WasmModule* module, WasmInstance* instance,
    327                  Vector<const byte> module_bytes)
    328       : module_env(module, instance), wire_bytes(module_bytes) {}
    329   ModuleBytesEnv(const WasmModule* module, WasmInstance* instance,
    330                  const ModuleWireBytes& wire_bytes)
    331       : module_env(module, instance), wire_bytes(wire_bytes) {}
    332 
    333   ModuleEnv module_env;
    334   ModuleWireBytes wire_bytes;
    335 };
    336 
    337 // A helper for printing out the names of functions.
    338 struct WasmFunctionName {
    339   WasmFunctionName(const WasmFunction* function, WasmName name)
    340       : function_(function), name_(name) {}
    341 
    342   const WasmFunction* function_;
    343   WasmName name_;
    344 };
    345 
    346 std::ostream& operator<<(std::ostream& os, const WasmModule& module);
    347 std::ostream& operator<<(std::ostream& os, const WasmFunction& function);
    348 std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
    349 
    350 // Get the debug info associated with the given wasm object.
    351 // If no debug info exists yet, it is created automatically.
    352 Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm);
    353 
    354 // Check whether the given object represents a WebAssembly.Instance instance.
    355 // This checks the number and type of internal fields, so it's not 100 percent
    356 // secure. If it turns out that we need more complete checks, we could add a
    357 // special marker as internal field, which will definitely never occur anywhere
    358 // else.
    359 bool IsWasmInstance(Object* instance);
    360 
    361 // Get the script of the wasm module. If the origin of the module is asm.js, the
    362 // returned Script will be a JavaScript Script of Script::TYPE_NORMAL, otherwise
    363 // it's of type TYPE_WASM.
    364 Handle<Script> GetScript(Handle<JSObject> instance);
    365 
    366 V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes(
    367     Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
    368     ModuleOrigin origin, Handle<Script> asm_js_script,
    369     Vector<const byte> asm_offset_table);
    370 
    371 V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
    372                                             Handle<Context> context);
    373 
    374 V8_EXPORT_PRIVATE Handle<JSArray> GetImports(Isolate* isolate,
    375                                              Handle<WasmModuleObject> module);
    376 V8_EXPORT_PRIVATE Handle<JSArray> GetExports(Isolate* isolate,
    377                                              Handle<WasmModuleObject> module);
    378 V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections(
    379     Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name,
    380     ErrorThrower* thrower);
    381 
    382 // Get the offset of the code of a function within a module.
    383 int GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
    384                           int func_index);
    385 
    386 // Assumed to be called with a code object associated to a wasm module instance.
    387 // Intended to be called from runtime functions.
    388 // Returns nullptr on failing to get owning instance.
    389 WasmInstanceObject* GetOwningWasmInstance(Code* code);
    390 
    391 MaybeHandle<JSArrayBuffer> GetInstanceMemory(
    392     Isolate* isolate, Handle<WasmInstanceObject> instance);
    393 
    394 int32_t GetInstanceMemorySize(Isolate* isolate,
    395                               Handle<WasmInstanceObject> instance);
    396 
    397 int32_t GrowInstanceMemory(Isolate* isolate,
    398                            Handle<WasmInstanceObject> instance, uint32_t pages);
    399 
    400 Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
    401                                      bool enable_guard_regions);
    402 
    403 int32_t GrowWebAssemblyMemory(Isolate* isolate,
    404                               Handle<WasmMemoryObject> receiver,
    405                               uint32_t pages);
    406 
    407 int32_t GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
    408                    uint32_t pages);
    409 
    410 void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
    411                           int index, Handle<JSFunction> js_function);
    412 
    413 void GrowDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
    414                         uint32_t old_size, uint32_t count);
    415 
    416 //============================================================================
    417 //== Compilation and instantiation ===========================================
    418 //============================================================================
    419 V8_EXPORT_PRIVATE bool SyncValidate(Isolate* isolate, ErrorThrower* thrower,
    420                                     const ModuleWireBytes& bytes);
    421 
    422 V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompileTranslatedAsmJs(
    423     Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
    424     Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes);
    425 
    426 V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompile(
    427     Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes);
    428 
    429 V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncInstantiate(
    430     Isolate* isolate, ErrorThrower* thrower,
    431     Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
    432     MaybeHandle<JSArrayBuffer> memory);
    433 
    434 V8_EXPORT_PRIVATE void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
    435                                     const ModuleWireBytes& bytes);
    436 
    437 V8_EXPORT_PRIVATE void AsyncInstantiate(Isolate* isolate,
    438                                         Handle<JSPromise> promise,
    439                                         Handle<WasmModuleObject> module_object,
    440                                         MaybeHandle<JSReceiver> imports);
    441 
    442 V8_EXPORT_PRIVATE void AsyncCompileAndInstantiate(
    443     Isolate* isolate, Handle<JSPromise> promise, const ModuleWireBytes& bytes,
    444     MaybeHandle<JSReceiver> imports);
    445 
    446 namespace testing {
    447 void ValidateInstancesChain(Isolate* isolate,
    448                             Handle<WasmModuleObject> module_obj,
    449                             int instance_count);
    450 void ValidateModuleState(Isolate* isolate, Handle<WasmModuleObject> module_obj);
    451 void ValidateOrphanedInstance(Isolate* isolate,
    452                               Handle<WasmInstanceObject> instance);
    453 }  // namespace testing
    454 }  // namespace wasm
    455 }  // namespace internal
    456 }  // namespace v8
    457 
    458 #endif  // V8_WASM_MODULE_H_
    459