Line data Source code
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 895969 : WasmInitExpr() : kind(kNone) {}
63 307 : explicit WasmInitExpr(int32_t v) : kind(kI32Const) { val.i32_const = v; }
64 : explicit WasmInitExpr(int64_t v) : kind(kI64Const) { val.i64_const = v; }
65 4 : explicit WasmInitExpr(float v) : kind(kF32Const) { val.f32_const = v; }
66 70 : 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 20993 : 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 1813 : 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 :
141 : inline bool IsWasm(ModuleOrigin Origin) {
142 1 : return Origin == ModuleOrigin::kWasmOrigin;
143 : }
144 : inline bool IsAsmJs(ModuleOrigin Origin) {
145 121839 : return Origin == ModuleOrigin::kAsmJsOrigin;
146 : }
147 :
148 : struct ModuleWireBytes;
149 :
150 : // Static representation of a module.
151 : struct V8_EXPORT_PRIVATE WasmModule {
152 : static const uint32_t kPageSize = 0x10000; // Page size, 64kb.
153 : static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb
154 :
155 : Zone* owned_zone;
156 : uint32_t min_mem_pages = 0; // minimum size of the memory in 64k pages
157 : uint32_t max_mem_pages = 0; // maximum size of the memory in 64k pages
158 : bool has_max_mem = false; // try if a maximum memory size exists
159 : bool has_memory = false; // true if the memory was defined or imported
160 : bool mem_export = false; // true if the memory is exported
161 : // TODO(wasm): reconcile start function index being an int with
162 : // the fact that we index on uint32_t, so we may technically not be
163 : // able to represent some start_function_index -es.
164 : int start_function_index = -1; // start function, if any
165 :
166 : std::vector<WasmGlobal> globals; // globals in this module.
167 : uint32_t globals_size = 0; // size of globals table.
168 : uint32_t num_imported_functions = 0; // number of imported functions.
169 : uint32_t num_declared_functions = 0; // number of declared functions.
170 : uint32_t num_exported_functions = 0; // number of exported functions.
171 : std::vector<FunctionSig*> signatures; // signatures in this module.
172 : std::vector<WasmFunction> functions; // functions in this module.
173 : std::vector<WasmDataSegment> data_segments; // data segments in this module.
174 : std::vector<WasmIndirectFunctionTable> function_tables; // function tables.
175 : std::vector<WasmImport> import_table; // import table.
176 : std::vector<WasmExport> export_table; // export table.
177 : std::vector<WasmTableInit> table_inits; // initializations of tables
178 : // We store the semaphore here to extend its lifetime. In <libc-2.21, which we
179 : // use on the try bots, semaphore::Wait() can return while some compilation
180 : // tasks are still executing semaphore::Signal(). If the semaphore is cleaned
181 : // up right after semaphore::Wait() returns, then this can cause an
182 : // invalid-semaphore error in the compilation tasks.
183 : // TODO(wasm): Move this semaphore back to CompileInParallel when the try bots
184 : // switch to libc-2.21 or higher.
185 : std::unique_ptr<base::Semaphore> pending_tasks;
186 :
187 : WasmModule() : WasmModule(nullptr) {}
188 : WasmModule(Zone* owned_zone);
189 53121 : ~WasmModule() {
190 17707 : if (owned_zone) delete owned_zone;
191 17707 : }
192 :
193 : ModuleOrigin get_origin() const { return origin_; }
194 19586 : void set_origin(ModuleOrigin new_value) { origin_ = new_value; }
195 : bool is_wasm() const { return wasm::IsWasm(origin_); }
196 : bool is_asm_js() const { return wasm::IsAsmJs(origin_); }
197 :
198 : private:
199 : // TODO(kschimpf) - Encapsulate more fields.
200 : ModuleOrigin origin_ = kWasmOrigin; // origin of the module
201 : };
202 :
203 : typedef Managed<WasmModule> WasmModuleWrapper;
204 :
205 : // An instantiated WASM module, including memory, function table, etc.
206 34472 : struct WasmInstance {
207 : const WasmModule* module; // static representation of the module.
208 : // -- Heap allocated --------------------------------------------------------
209 : Handle<Context> context; // JavaScript native context.
210 : std::vector<Handle<FixedArray>> function_tables; // indirect function tables.
211 : std::vector<Handle<FixedArray>>
212 : signature_tables; // indirect signature tables.
213 : // TODO(wasm): Remove this vector, since it is only used for testing.
214 : std::vector<Handle<Code>> function_code; // code objects for each function.
215 : // -- raw memory ------------------------------------------------------------
216 : byte* mem_start = nullptr; // start of linear memory.
217 : uint32_t mem_size = 0; // size of the linear memory.
218 : // -- raw globals -----------------------------------------------------------
219 : byte* globals_start = nullptr; // start of the globals area.
220 :
221 17254 : explicit WasmInstance(const WasmModule* m)
222 : : module(m),
223 : function_tables(m->function_tables.size()),
224 : signature_tables(m->function_tables.size()),
225 86270 : function_code(m->functions.size()) {}
226 :
227 750 : void ReopenHandles(Isolate* isolate) {
228 750 : context = handle(*context, isolate);
229 :
230 1605 : for (auto& table : function_tables) {
231 105 : table = handle(*table, isolate);
232 : }
233 :
234 1605 : for (auto& table : signature_tables) {
235 105 : table = handle(*table, isolate);
236 : }
237 :
238 2115 : for (auto& code : function_code) {
239 615 : code = handle(*code, isolate);
240 : }
241 750 : }
242 : };
243 :
244 : // Interface to the storage (wire bytes) of a wasm module.
245 : // It is illegal for anyone receiving a ModuleWireBytes to store pointers based
246 : // on module_bytes, as this storage is only guaranteed to be alive as long as
247 : // this struct is alive.
248 : struct V8_EXPORT_PRIVATE ModuleWireBytes {
249 : ModuleWireBytes(Vector<const byte> module_bytes)
250 1253 : : module_bytes_(module_bytes) {}
251 : ModuleWireBytes(const byte* start, const byte* end)
252 18360 : : module_bytes_(start, static_cast<int>(end - start)) {
253 : DCHECK_GE(kMaxInt, end - start);
254 : }
255 :
256 : // Get a string stored in the module bytes representing a name.
257 163 : WasmName GetName(uint32_t offset, uint32_t length) const {
258 163 : if (length == 0) return {"<?>", 3}; // no name.
259 154 : CHECK(BoundsCheck(offset, length));
260 : DCHECK_GE(length, 0);
261 : return Vector<const char>::cast(
262 154 : module_bytes_.SubVector(offset, offset + length));
263 : }
264 :
265 : // Get a string stored in the module bytes representing a function name.
266 : WasmName GetName(const WasmFunction* function) const {
267 163 : return GetName(function->name_offset, function->name_length);
268 : }
269 :
270 : // Get a string stored in the module bytes representing a name.
271 36279 : WasmName GetNameOrNull(uint32_t offset, uint32_t length) const {
272 36279 : if (offset == 0 && length == 0) return {NULL, 0}; // no name.
273 33225 : CHECK(BoundsCheck(offset, length));
274 : DCHECK_GE(length, 0);
275 : return Vector<const char>::cast(
276 33225 : module_bytes_.SubVector(offset, offset + length));
277 : }
278 :
279 : // Get a string stored in the module bytes representing a function name.
280 : WasmName GetNameOrNull(const WasmFunction* function) const {
281 36279 : return GetNameOrNull(function->name_offset, function->name_length);
282 : }
283 :
284 : // Checks the given offset range is contained within the module bytes.
285 : bool BoundsCheck(uint32_t offset, uint32_t length) const {
286 33379 : uint32_t size = static_cast<uint32_t>(module_bytes_.length());
287 33379 : return offset <= size && length <= size - offset;
288 : }
289 :
290 : Vector<const byte> GetFunctionBytes(const WasmFunction* function) const {
291 : return module_bytes_.SubVector(function->code_start_offset,
292 100 : function->code_end_offset);
293 : }
294 :
295 64867 : const byte* start() const { return module_bytes_.start(); }
296 36990 : const byte* end() const { return module_bytes_.end(); }
297 31391 : int length() const { return module_bytes_.length(); }
298 :
299 : private:
300 : const Vector<const byte> module_bytes_;
301 : };
302 :
303 : // Interface provided to the decoder/graph builder which contains only
304 : // minimal information about the globals, functions, and function tables.
305 : struct V8_EXPORT_PRIVATE ModuleEnv {
306 : ModuleEnv(const WasmModule* module, WasmInstance* instance)
307 : : module(module),
308 : instance(instance),
309 : function_tables(instance ? &instance->function_tables : nullptr),
310 46275 : signature_tables(instance ? &instance->signature_tables : nullptr) {}
311 : ModuleEnv(const WasmModule* module,
312 : std::vector<Handle<FixedArray>>* function_tables,
313 : std::vector<Handle<FixedArray>>* signature_tables)
314 : : module(module),
315 : instance(nullptr),
316 : function_tables(function_tables),
317 12496 : signature_tables(signature_tables) {}
318 :
319 : const WasmModule* module;
320 : WasmInstance* instance;
321 :
322 : std::vector<Handle<FixedArray>>* function_tables;
323 : std::vector<Handle<FixedArray>>* signature_tables;
324 :
325 : bool IsValidGlobal(uint32_t index) const {
326 : return module && index < module->globals.size();
327 : }
328 : bool IsValidFunction(uint32_t index) const {
329 : return module && index < module->functions.size();
330 : }
331 : bool IsValidSignature(uint32_t index) const {
332 : return module && index < module->signatures.size();
333 : }
334 : bool IsValidTable(uint32_t index) const {
335 : return module && index < module->function_tables.size();
336 : }
337 : ValueType GetGlobalType(uint32_t index) {
338 : DCHECK(IsValidGlobal(index));
339 37558 : return module->globals[index].type;
340 : }
341 : FunctionSig* GetFunctionSignature(uint32_t index) {
342 : DCHECK(IsValidFunction(index));
343 123164 : return module->functions[index].sig;
344 : }
345 : FunctionSig* GetSignature(uint32_t index) {
346 : DCHECK(IsValidSignature(index));
347 4728 : return module->signatures[index];
348 : }
349 : const WasmIndirectFunctionTable* GetTable(uint32_t index) const {
350 : DCHECK(IsValidTable(index));
351 : return &module->function_tables[index];
352 : }
353 :
354 : bool is_asm_js() const { return module->is_asm_js(); }
355 1 : bool is_wasm() const { return module->is_wasm(); }
356 :
357 : // Only used for testing.
358 : Handle<Code> GetFunctionCode(uint32_t index) {
359 : DCHECK_NOT_NULL(instance);
360 25946 : return instance->function_code[index];
361 : }
362 :
363 : // TODO(titzer): move these into src/compiler/wasm-compiler.cc
364 : static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone,
365 : FunctionSig* sig);
366 : static compiler::CallDescriptor* GetI32WasmCallDescriptor(
367 : Zone* zone, compiler::CallDescriptor* descriptor);
368 : static compiler::CallDescriptor* GetI32WasmCallDescriptorForSimd(
369 : Zone* zone, compiler::CallDescriptor* descriptor);
370 : };
371 :
372 : // A ModuleEnv together with ModuleWireBytes.
373 : struct ModuleBytesEnv {
374 : ModuleBytesEnv(const WasmModule* module, WasmInstance* instance,
375 : Vector<const byte> module_bytes)
376 : : module_env(module, instance), wire_bytes(module_bytes) {}
377 : ModuleBytesEnv(const WasmModule* module, WasmInstance* instance,
378 : const ModuleWireBytes& wire_bytes)
379 16733 : : module_env(module, instance), wire_bytes(wire_bytes) {}
380 :
381 : ModuleEnv module_env;
382 : ModuleWireBytes wire_bytes;
383 : };
384 :
385 : // A helper for printing out the names of functions.
386 : struct WasmFunctionName {
387 : WasmFunctionName(const WasmFunction* function, WasmName name)
388 1173 : : function_(function), name_(name) {}
389 :
390 : const WasmFunction* function_;
391 : WasmName name_;
392 : };
393 :
394 : std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
395 :
396 : // Get the debug info associated with the given wasm object.
397 : // If no debug info exists yet, it is created automatically.
398 : Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm);
399 :
400 : // Check whether the given object represents a WebAssembly.Instance instance.
401 : // This checks the number and type of embedder fields, so it's not 100 percent
402 : // secure. If it turns out that we need more complete checks, we could add a
403 : // special marker as embedder field, which will definitely never occur anywhere
404 : // else.
405 : bool IsWasmInstance(Object* instance);
406 :
407 : // Get the script of the wasm module. If the origin of the module is asm.js, the
408 : // returned Script will be a JavaScript Script of Script::TYPE_NORMAL, otherwise
409 : // it's of type TYPE_WASM.
410 : Handle<Script> GetScript(Handle<JSObject> instance);
411 :
412 : V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes(
413 : Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
414 : ModuleOrigin origin, Handle<Script> asm_js_script,
415 : Vector<const byte> asm_offset_table);
416 :
417 : V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
418 : Handle<Context> context);
419 :
420 : V8_EXPORT_PRIVATE Handle<JSArray> GetImports(Isolate* isolate,
421 : Handle<WasmModuleObject> module);
422 : V8_EXPORT_PRIVATE Handle<JSArray> GetExports(Isolate* isolate,
423 : Handle<WasmModuleObject> module);
424 : V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections(
425 : Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name,
426 : ErrorThrower* thrower);
427 :
428 : // Assumed to be called with a code object associated to a wasm module instance.
429 : // Intended to be called from runtime functions.
430 : // Returns nullptr on failing to get owning instance.
431 : WasmInstanceObject* GetOwningWasmInstance(Code* code);
432 :
433 : Handle<JSArrayBuffer> NewArrayBuffer(Isolate*, size_t size,
434 : bool enable_guard_regions);
435 :
436 : Handle<JSArrayBuffer> SetupArrayBuffer(Isolate*, void* backing_store,
437 : size_t size, bool is_external,
438 : bool enable_guard_regions);
439 :
440 : void DetachWebAssemblyMemoryBuffer(Isolate* isolate,
441 : Handle<JSArrayBuffer> buffer);
442 :
443 : void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
444 : int index, Handle<JSFunction> js_function);
445 :
446 : void GrowDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
447 : uint32_t old_size, uint32_t count);
448 :
449 : //============================================================================
450 : //== Compilation and instantiation ===========================================
451 : //============================================================================
452 : V8_EXPORT_PRIVATE bool SyncValidate(Isolate* isolate, ErrorThrower* thrower,
453 : const ModuleWireBytes& bytes);
454 :
455 : V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompileTranslatedAsmJs(
456 : Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
457 : Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes);
458 :
459 : V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompile(
460 : Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes);
461 :
462 : V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncInstantiate(
463 : Isolate* isolate, ErrorThrower* thrower,
464 : Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
465 : MaybeHandle<JSArrayBuffer> memory);
466 :
467 : V8_EXPORT_PRIVATE void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
468 : const ModuleWireBytes& bytes);
469 :
470 : V8_EXPORT_PRIVATE void AsyncInstantiate(Isolate* isolate,
471 : Handle<JSPromise> promise,
472 : Handle<WasmModuleObject> module_object,
473 : MaybeHandle<JSReceiver> imports);
474 :
475 : #if V8_TARGET_ARCH_64_BIT
476 : const bool kGuardRegionsSupported = true;
477 : #else
478 : const bool kGuardRegionsSupported = false;
479 : #endif
480 :
481 : inline bool EnableGuardRegions() {
482 6604 : return FLAG_wasm_guard_pages && kGuardRegionsSupported;
483 : }
484 :
485 : void UnpackAndRegisterProtectedInstructions(Isolate* isolate,
486 : Handle<FixedArray> code_table);
487 :
488 : // Triggered by the WasmCompileLazy builtin.
489 : // Walks the stack (top three frames) to determine the wasm instance involved
490 : // and which function to compile.
491 : // Then triggers WasmCompiledModule::CompileLazy, taking care of correctly
492 : // patching the call site or indirect function tables.
493 : // Returns either the Code object that has been lazily compiled, or Illegal if
494 : // an error occured. In the latter case, a pending exception has been set, which
495 : // will be triggered when returning from the runtime function, i.e. the Illegal
496 : // builtin will never be called.
497 : Handle<Code> CompileLazy(Isolate* isolate);
498 :
499 : // This class orchestrates the lazy compilation of wasm functions. It is
500 : // triggered by the WasmCompileLazy builtin.
501 : // It contains the logic for compiling and specializing wasm functions, and
502 : // patching the calling wasm code.
503 : // Once we support concurrent lazy compilation, this class will contain the
504 : // logic to actually orchestrate parallel execution of wasm compilation jobs.
505 : // TODO(clemensh): Implement concurrent lazy compilation.
506 : class LazyCompilationOrchestrator {
507 : bool CompileFunction(Isolate*, Handle<WasmInstanceObject>,
508 : int func_index) WARN_UNUSED_RESULT;
509 :
510 : public:
511 : MaybeHandle<Code> CompileLazy(Isolate*, Handle<WasmInstanceObject>,
512 : Handle<Code> caller, int call_offset,
513 : int exported_func_index, bool patch_caller);
514 : };
515 :
516 : namespace testing {
517 : void ValidateInstancesChain(Isolate* isolate,
518 : Handle<WasmModuleObject> module_obj,
519 : int instance_count);
520 : void ValidateModuleState(Isolate* isolate, Handle<WasmModuleObject> module_obj);
521 : void ValidateOrphanedInstance(Isolate* isolate,
522 : Handle<WasmInstanceObject> instance);
523 : } // namespace testing
524 : } // namespace wasm
525 : } // namespace internal
526 : } // namespace v8
527 :
528 : #endif // V8_WASM_MODULE_H_
|