diff options
author | Michaƫl Zasso <targos@protonmail.com> | 2017-09-12 11:34:59 +0200 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2017-09-13 16:15:18 +0200 |
commit | d82e1075dbc2cec2d6598ade10c1f43805f690fd (patch) | |
tree | ccd242b9b491dfc341d1099fe11b0ef528839877 /deps/v8/src/wasm | |
parent | b4b7ac6ae811b2b5a3082468115dfb5a5246fe3f (diff) | |
download | android-node-v8-d82e1075dbc2cec2d6598ade10c1f43805f690fd.tar.gz android-node-v8-d82e1075dbc2cec2d6598ade10c1f43805f690fd.tar.bz2 android-node-v8-d82e1075dbc2cec2d6598ade10c1f43805f690fd.zip |
deps: update V8 to 6.1.534.36
PR-URL: https://github.com/nodejs/node/pull/14730
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'deps/v8/src/wasm')
31 files changed, 4947 insertions, 4488 deletions
diff --git a/deps/v8/src/wasm/OWNERS b/deps/v8/src/wasm/OWNERS index c698fc4776..dfcaa91d2e 100644 --- a/deps/v8/src/wasm/OWNERS +++ b/deps/v8/src/wasm/OWNERS @@ -8,3 +8,5 @@ gdeepti@chromium.org mtrofin@chromium.org rossberg@chromium.org titzer@chromium.org + +# COMPONENT: Blink>JavaScript>WebAssembly diff --git a/deps/v8/src/wasm/compilation-manager.cc b/deps/v8/src/wasm/compilation-manager.cc new file mode 100644 index 0000000000..01e0755e14 --- /dev/null +++ b/deps/v8/src/wasm/compilation-manager.cc @@ -0,0 +1,32 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/wasm/compilation-manager.h" + +#include "src/objects-inl.h" + +namespace v8 { +namespace internal { +namespace wasm { + +void CompilationManager::StartAsyncCompileJob( + Isolate* isolate, std::unique_ptr<byte[]> bytes_copy, size_t length, + Handle<Context> context, Handle<JSPromise> promise) { + std::shared_ptr<AsyncCompileJob> job(new AsyncCompileJob( + isolate, std::move(bytes_copy), length, context, promise)); + jobs_.insert({job.get(), job}); + job->Start(); +} + +void CompilationManager::RemoveJob(AsyncCompileJob* job) { + size_t num_removed = jobs_.erase(job); + USE(num_removed); + DCHECK_EQ(1, num_removed); +} + +void CompilationManager::TearDown() { jobs_.clear(); } + +} // namespace wasm +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/wasm/compilation-manager.h b/deps/v8/src/wasm/compilation-manager.h new file mode 100644 index 0000000000..85b6fd5ce2 --- /dev/null +++ b/deps/v8/src/wasm/compilation-manager.h @@ -0,0 +1,44 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_WASM_COMPILATION_MANAGER_H_ +#define V8_WASM_COMPILATION_MANAGER_H_ + +#include <vector> + +#include "src/handles.h" +#include "src/isolate.h" +#include "src/wasm/module-compiler.h" + +namespace v8 { +namespace internal { +namespace wasm { + +// The CompilationManager manages a list of active WebAssembly compile jobs. The +// manager owns the memory of the compile jobs and can trigger the abortion of +// compile jobs. If the isolate tears down, the CompilationManager makes sure +// that all compile jobs finish executing before the isolate becomes +// unavailable. +class CompilationManager { + public: + void StartAsyncCompileJob(Isolate* isolate, + std::unique_ptr<byte[]> bytes_copy, size_t length, + Handle<Context> context, Handle<JSPromise> promise); + + // Removes {job} from the list of active compile jobs. This will delete {job}. + void RemoveJob(AsyncCompileJob* job); + + void TearDown(); + + private: + // We use an AsyncCompileJob as the key for itself so that we can delete the + // job from the map when it is finished. + std::unordered_map<AsyncCompileJob*, std::shared_ptr<AsyncCompileJob>> jobs_; +}; + +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_WASM_COMPILATION_MANAGER_H_ diff --git a/deps/v8/src/wasm/decoder.h b/deps/v8/src/wasm/decoder.h index 5f242ac1aa..4f0548abb1 100644 --- a/deps/v8/src/wasm/decoder.h +++ b/deps/v8/src/wasm/decoder.h @@ -139,8 +139,7 @@ class Decoder { // Consume {size} bytes and send them to the bit bucket, advancing {pc_}. void consume_bytes(uint32_t size, const char* name = "skip") { // Only trace if the name is not null. - TRACE_IF(name, " +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), - name, size); + TRACE_IF(name, " +%u %-20s: %d bytes\n", pc_offset(), name, size); if (checkAvailable(size)) { pc_ += size; } else { @@ -268,7 +267,7 @@ class Decoder { template <typename IntType> inline IntType consume_little_endian(const char* name) { - TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_), name); + TRACE(" +%u %-20s: ", pc_offset(), name); if (!checkAvailable(sizeof(IntType))) { traceOffEnd(); pc_ = end_; @@ -285,7 +284,7 @@ class Decoder { inline IntType read_leb(const byte* pc, uint32_t* length, const char* name = "varint") { DCHECK_IMPLIES(advance_pc, pc == pc_); - TRACE_IF(trace, " +%d %-20s: ", static_cast<int>(pc - start_), name); + TRACE_IF(trace, " +%u %-20s: ", pc_offset(), name); return read_leb_tail<IntType, checked, advance_pc, trace, 0>(pc, length, name, 0); } @@ -302,7 +301,7 @@ class Decoder { const bool at_end = checked && pc >= end_; byte b = 0; if (!at_end) { - DCHECK_LT(pc_, end_); + DCHECK_LT(pc, end_); b = *pc; TRACE_IF(trace, "%02x ", b); result = result | ((static_cast<IntType>(b) & 0x7f) << shift); @@ -344,7 +343,7 @@ class Decoder { } } constexpr int sign_ext_shift = - is_signed && !is_last_byte ? 8 * sizeof(IntType) - shift - 7 : 0; + is_signed ? Max(0, int{8 * sizeof(IntType)} - shift - 7) : 0; // Perform sign extension. result = (result << sign_ext_shift) >> sign_ext_shift; if (trace && is_signed) { diff --git a/deps/v8/src/wasm/function-body-decoder-impl.h b/deps/v8/src/wasm/function-body-decoder-impl.h index 0df04e7ee0..ec295cb0e0 100644 --- a/deps/v8/src/wasm/function-body-decoder-impl.h +++ b/deps/v8/src/wasm/function-body-decoder-impl.h @@ -100,7 +100,7 @@ struct BlockTypeOperand { types = pc + 1; } else { // Handle multi-value blocks. - if (!CHECKED_COND(FLAG_wasm_mv_prototype)) { + if (!CHECKED_COND(FLAG_experimental_wasm_mv)) { decoder->error(pc + 1, "invalid block arity > 1"); return; } @@ -152,15 +152,6 @@ struct BlockTypeOperand { case kLocalS128: *result = kWasmS128; return true; - case kLocalS1x4: - *result = kWasmS1x4; - return true; - case kLocalS1x8: - *result = kWasmS1x8; - return true; - case kLocalS1x16: - *result = kWasmS1x16; - return true; default: *result = kWasmStmt; return false; @@ -322,15 +313,13 @@ struct SimdShiftOperand { } }; -// Operand for SIMD shuffle operations. +// Operand for SIMD S8x16 shuffle operations. template <bool checked> -struct SimdShuffleOperand { - uint8_t shuffle[16]; - unsigned lanes; +struct Simd8x16ShuffleOperand { + uint8_t shuffle[kSimd128Size]; - inline SimdShuffleOperand(Decoder* decoder, const byte* pc, unsigned lanes_) { - lanes = lanes_; - for (unsigned i = 0; i < lanes; i++) { + inline Simd8x16ShuffleOperand(Decoder* decoder, const byte* pc) { + for (uint32_t i = 0; i < kSimd128Size; ++i) { shuffle[i] = decoder->read_u8<checked>(pc + 2 + i, "shuffle"); } } diff --git a/deps/v8/src/wasm/function-body-decoder.cc b/deps/v8/src/wasm/function-body-decoder.cc index df74485a33..f1224070d6 100644 --- a/deps/v8/src/wasm/function-body-decoder.cc +++ b/deps/v8/src/wasm/function-body-decoder.cc @@ -35,15 +35,19 @@ namespace wasm { #define TRACE(...) #endif -#define CHECK_PROTOTYPE_OPCODE(flag) \ - if (module_ != nullptr && module_->is_asm_js()) { \ - error("Opcode not supported for asmjs modules"); \ - } \ - if (!FLAG_##flag) { \ - error("Invalid opcode (enable with --" #flag ")"); \ - break; \ +#define CHECK_PROTOTYPE_OPCODE(flag) \ + if (module_ != nullptr && module_->is_asm_js()) { \ + error("Opcode not supported for asmjs modules"); \ + } \ + if (!FLAG_experimental_wasm_##flag) { \ + error("Invalid opcode (enable with --experimental-wasm-" #flag ")"); \ + break; \ } +#define PROTOTYPE_NOT_FUNCTIONAL(opcode) \ + errorf(pc_, "Prototype still not functional: %s", \ + WasmOpcodes::OpcodeName(opcode)); + // An SsaEnv environment carries the current local variable renaming // as well as the current effect and control dependency in the TF graph. // It maintains a control state that tracks whether the environment @@ -146,22 +150,6 @@ struct Control { } }; -namespace { -inline unsigned GetShuffleMaskSize(WasmOpcode opcode) { - switch (opcode) { - case kExprS32x4Shuffle: - return 4; - case kExprS16x8Shuffle: - return 8; - case kExprS8x16Shuffle: - return 16; - default: - UNREACHABLE(); - return 0; - } -} -} // namespace - // Macros that build nodes only if there is a graph and the current SSA // environment is reachable from start. This avoids problems with malformed // TF graphs when decoding inputs that have unreachable code. @@ -174,8 +162,8 @@ inline unsigned GetShuffleMaskSize(WasmOpcode opcode) { class WasmDecoder : public Decoder { public: WasmDecoder(const WasmModule* module, FunctionSig* sig, const byte* start, - const byte* end) - : Decoder(start, end), + const byte* end, uint32_t buffer_offset = 0) + : Decoder(start, end, buffer_offset), module_(module), sig_(sig), local_types_(nullptr) {} @@ -229,15 +217,6 @@ class WasmDecoder : public Decoder { case kLocalS128: type = kWasmS128; break; - case kLocalS1x4: - type = kWasmS1x4; - break; - case kLocalS1x8: - type = kWasmS1x8; - break; - case kLocalS1x16: - type = kWasmS1x16; - break; default: decoder->error(decoder->pc() - 1, "invalid local type"); return false; @@ -431,13 +410,12 @@ class WasmDecoder : public Decoder { } } - inline bool Validate(const byte* pc, WasmOpcode opcode, - SimdShuffleOperand<true>& operand) { - unsigned lanes = GetShuffleMaskSize(opcode); + inline bool Validate(const byte* pc, Simd8x16ShuffleOperand<true>& operand) { uint8_t max_lane = 0; - for (unsigned i = 0; i < lanes; i++) + for (uint32_t i = 0; i < kSimd128Size; ++i) max_lane = std::max(max_lane, operand.shuffle[i]); - if (operand.lanes != lanes || max_lane > 2 * lanes) { + // Shuffle indices must be in [0..31] for a 16 lane shuffle. + if (max_lane > 2 * kSimd128Size) { error(pc_ + 2, "invalid shuffle mask"); return false; } else { @@ -521,20 +499,21 @@ class WasmDecoder : public Decoder { #define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: FOREACH_SIMD_0_OPERAND_OPCODE(DECLARE_OPCODE_CASE) #undef DECLARE_OPCODE_CASE - { - return 2; - } + return 2; #define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: FOREACH_SIMD_1_OPERAND_OPCODE(DECLARE_OPCODE_CASE) #undef DECLARE_OPCODE_CASE + return 3; +#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: + FOREACH_SIMD_MEM_OPCODE(DECLARE_OPCODE_CASE) +#undef DECLARE_OPCODE_CASE { - return 3; + MemoryAccessOperand<true> operand(decoder, pc + 1, UINT32_MAX); + return 2 + operand.length; } - // Shuffles contain a byte array to determine the shuffle. - case kExprS32x4Shuffle: - case kExprS16x8Shuffle: + // Shuffles require a byte per lane, or 16 immediate bytes. case kExprS8x16Shuffle: - return 2 + GetShuffleMaskSize(opcode); + return 2 + kSimd128Size; default: decoder->error(pc, "invalid SIMD opcode"); return 2; @@ -551,14 +530,19 @@ class WasmDecoder : public Decoder { FunctionSig* sig = WasmOpcodes::Signature(opcode); if (!sig) sig = WasmOpcodes::AsmjsSignature(opcode); if (sig) return {sig->parameter_count(), sig->return_count()}; + if (WasmOpcodes::IsPrefixOpcode(opcode)) { + opcode = static_cast<WasmOpcode>(opcode << 8 | *(pc + 1)); + } #define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: // clang-format off switch (opcode) { case kExprSelect: return {3, 1}; + case kExprS128StoreMem: FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) return {2, 0}; + case kExprS128LoadMem: FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) case kExprTeeLocal: case kExprGrowMemory: @@ -600,7 +584,8 @@ class WasmDecoder : public Decoder { case kExprUnreachable: return {0, 0}; default: - V8_Fatal(__FILE__, __LINE__, "unimplemented opcode: %x", opcode); + V8_Fatal(__FILE__, __LINE__, "unimplemented opcode: %x (%s)", opcode, + WasmOpcodes::OpcodeName(opcode)); return {0, 0}; } #undef DECLARE_OPCODE_CASE @@ -610,7 +595,7 @@ class WasmDecoder : public Decoder { static const int32_t kNullCatch = -1; -// The full WASM decoder for bytecode. Verifies bytecode and, optionally, +// The full wasm decoder for bytecode. Verifies bytecode and, optionally, // generates a TurboFan IR graph. class WasmFullDecoder : public WasmDecoder { public: @@ -644,6 +629,7 @@ class WasmFullDecoder : public WasmDecoder { WasmDecoder::DecodeLocals(this, sig_, local_types_); InitSsaEnv(); DecodeFunctionBody(); + FinishFunction(); if (failed()) return TraceFailed(); @@ -674,18 +660,17 @@ class WasmFullDecoder : public WasmDecoder { } bool TraceFailed() { - TRACE("wasm-error module+%-6d func+%d: %s\n\n", - baserel(start_ + error_offset_), error_offset_, error_msg_.c_str()); + TRACE("wasm-error module+%-6d func+%d: %s\n\n", error_offset_, + GetBufferRelativeOffset(error_offset_), error_msg_.c_str()); return false; } private: WasmFullDecoder(Zone* zone, const wasm::WasmModule* module, TFBuilder* builder, const FunctionBody& body) - : WasmDecoder(module, body.sig, body.start, body.end), + : WasmDecoder(module, body.sig, body.start, body.end, body.offset), zone_(zone), builder_(builder), - base_(body.base), local_type_vec_(zone), stack_(zone), control_(zone), @@ -698,7 +683,6 @@ class WasmFullDecoder : public WasmDecoder { Zone* zone_; TFBuilder* builder_; - const byte* base_; SsaEnv* ssa_env_; @@ -742,11 +726,6 @@ class WasmFullDecoder : public WasmDecoder { ssa_env->control = start; ssa_env->effect = start; SetEnv("initial", ssa_env); - if (builder_) { - // The function-prologue stack check is associated with position 0, which - // is never a position of any instruction in the function. - builder_->StackCheck(0); - } } TFNode* DefaultValue(ValueType type) { @@ -761,27 +740,9 @@ class WasmFullDecoder : public WasmDecoder { return builder_->Float64Constant(0); case kWasmS128: return builder_->S128Zero(); - case kWasmS1x4: - return builder_->S1x4Zero(); - case kWasmS1x8: - return builder_->S1x8Zero(); - case kWasmS1x16: - return builder_->S1x16Zero(); default: UNREACHABLE(); - return nullptr; - } - } - - char* indentation() { - static const int kMaxIndent = 64; - static char bytes[kMaxIndent + 1]; - for (int i = 0; i < kMaxIndent; ++i) bytes[i] = ' '; - bytes[kMaxIndent] = 0; - if (stack_.size() < kMaxIndent / 2) { - bytes[stack_.size() * 2] = 0; } - return bytes; } bool CheckHasMemory() { @@ -793,9 +754,9 @@ class WasmFullDecoder : public WasmDecoder { // Decodes the body of a function. void DecodeFunctionBody() { - TRACE("wasm-decode %p...%p (module+%d, %d bytes) %s\n", - reinterpret_cast<const void*>(start_), - reinterpret_cast<const void*>(end_), baserel(pc_), + TRACE("wasm-decode %p...%p (module+%u, %d bytes) %s\n", + reinterpret_cast<const void*>(start()), + reinterpret_cast<const void*>(end()), pc_offset(), static_cast<int>(end_ - start_), builder_ ? "graph building" : ""); { @@ -844,8 +805,16 @@ class WasmFullDecoder : public WasmDecoder { len = 1 + operand.length; break; } + case kExprRethrow: { + // TODO(kschimpf): Implement. + CHECK_PROTOTYPE_OPCODE(eh); + PROTOTYPE_NOT_FUNCTIONAL(opcode); + break; + } case kExprThrow: { - CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); + // TODO(kschimpf): Fix to use type signature of exception. + CHECK_PROTOTYPE_OPCODE(eh); + PROTOTYPE_NOT_FUNCTIONAL(opcode); Value value = Pop(0, kWasmI32); BUILD(Throw, value.node); // TODO(titzer): Throw should end control, but currently we build a @@ -855,7 +824,7 @@ class WasmFullDecoder : public WasmDecoder { break; } case kExprTry: { - CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); + CHECK_PROTOTYPE_OPCODE(eh); BlockTypeOperand<true> operand(this, pc_); SsaEnv* outer_env = ssa_env_; SsaEnv* try_env = Steal(outer_env); @@ -867,7 +836,9 @@ class WasmFullDecoder : public WasmDecoder { break; } case kExprCatch: { - CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); + // TODO(kschimpf): Fix to use type signature of exception. + CHECK_PROTOTYPE_OPCODE(eh); + PROTOTYPE_NOT_FUNCTIONAL(opcode); LocalIndexOperand<true> operand(this, pc_); len = 1 + operand.length; @@ -906,6 +877,12 @@ class WasmFullDecoder : public WasmDecoder { break; } + case kExprCatchAll: { + // TODO(kschimpf): Implement. + CHECK_PROTOTYPE_OPCODE(eh); + PROTOTYPE_NOT_FUNCTIONAL(opcode); + break; + } case kExprLoop: { BlockTypeOperand<true> operand(this, pc_); SsaEnv* finish_try_env = Steal(ssa_env_); @@ -1263,10 +1240,6 @@ class WasmFullDecoder : public WasmDecoder { case kExprF64LoadMem: len = DecodeLoadMem(kWasmF64, MachineType::Float64()); break; - case kExprS128LoadMem: - CHECK_PROTOTYPE_OPCODE(wasm_simd_prototype); - len = DecodeLoadMem(kWasmS128, MachineType::Simd128()); - break; case kExprI32StoreMem8: len = DecodeStoreMem(kWasmI32, MachineType::Int8()); break; @@ -1294,10 +1267,6 @@ class WasmFullDecoder : public WasmDecoder { case kExprF64StoreMem: len = DecodeStoreMem(kWasmF64, MachineType::Float64()); break; - case kExprS128StoreMem: - CHECK_PROTOTYPE_OPCODE(wasm_simd_prototype); - len = DecodeStoreMem(kWasmS128, MachineType::Simd128()); - break; case kExprGrowMemory: { if (!CheckHasMemory()) break; MemoryIndexOperand<true> operand(this, pc_); @@ -1343,7 +1312,7 @@ class WasmFullDecoder : public WasmDecoder { break; } case kSimdPrefix: { - CHECK_PROTOTYPE_OPCODE(wasm_simd_prototype); + CHECK_PROTOTYPE_OPCODE(simd); len++; byte simd_index = read_u8<true>(pc_ + 1, "simd index"); opcode = static_cast<WasmOpcode>(opcode << 8 | simd_index); @@ -1357,10 +1326,7 @@ class WasmFullDecoder : public WasmDecoder { error("Atomics are allowed only in AsmJs modules"); break; } - if (!FLAG_wasm_atomics_prototype) { - error("Invalid opcode (enable with --wasm_atomics_prototype)"); - break; - } + CHECK_PROTOTYPE_OPCODE(threads); len = 2; byte atomic_opcode = read_u8<true>(pc_ + 1, "atomic index"); opcode = static_cast<WasmOpcode>(opcode << 8 | atomic_opcode); @@ -1455,6 +1421,10 @@ class WasmFullDecoder : public WasmDecoder { if (pc_ > end_ && ok()) error("Beyond end of code"); } + void FinishFunction() { + if (builder_) builder_->PatchInStackCheckIfNeeded(); + } + void EndControl() { ssa_env_->Kill(SsaEnv::kControlEnd); if (!control_.empty()) { @@ -1539,16 +1509,39 @@ class WasmFullDecoder : public WasmDecoder { Value val = Pop(1, type); Value index = Pop(0, kWasmI32); BUILD(StoreMem, mem_type, index.node, operand.offset, operand.alignment, - val.node, position()); + val.node, position(), type); return 1 + operand.length; } + int DecodePrefixedLoadMem(ValueType type, MachineType mem_type) { + if (!CheckHasMemory()) return 0; + MemoryAccessOperand<true> operand( + this, pc_ + 1, ElementSizeLog2Of(mem_type.representation())); + + Value index = Pop(0, kWasmI32); + TFNode* node = BUILD(LoadMem, type, mem_type, index.node, operand.offset, + operand.alignment, position()); + Push(type, node); + return operand.length; + } + + int DecodePrefixedStoreMem(ValueType type, MachineType mem_type) { + if (!CheckHasMemory()) return 0; + MemoryAccessOperand<true> operand( + this, pc_ + 1, ElementSizeLog2Of(mem_type.representation())); + Value val = Pop(1, type); + Value index = Pop(0, kWasmI32); + BUILD(StoreMem, mem_type, index.node, operand.offset, operand.alignment, + val.node, position(), type); + return operand.length; + } + unsigned SimdExtractLane(WasmOpcode opcode, ValueType type) { SimdLaneOperand<true> operand(this, pc_); if (Validate(pc_, opcode, operand)) { compiler::NodeVector inputs(1, zone_); inputs[0] = Pop(0, ValueType::kSimd128).node; - TFNode* node = BUILD(SimdLaneOp, opcode, operand.lane, inputs); + TFNode* node = BUILD(SimdLaneOp, opcode, operand.lane, inputs.data()); Push(type, node); } return operand.length; @@ -1560,7 +1553,7 @@ class WasmFullDecoder : public WasmDecoder { compiler::NodeVector inputs(2, zone_); inputs[1] = Pop(1, type).node; inputs[0] = Pop(0, ValueType::kSimd128).node; - TFNode* node = BUILD(SimdLaneOp, opcode, operand.lane, inputs); + TFNode* node = BUILD(SimdLaneOp, opcode, operand.lane, inputs.data()); Push(ValueType::kSimd128, node); } return operand.length; @@ -1571,23 +1564,22 @@ class WasmFullDecoder : public WasmDecoder { if (Validate(pc_, opcode, operand)) { compiler::NodeVector inputs(1, zone_); inputs[0] = Pop(0, ValueType::kSimd128).node; - TFNode* node = BUILD(SimdShiftOp, opcode, operand.shift, inputs); + TFNode* node = BUILD(SimdShiftOp, opcode, operand.shift, inputs.data()); Push(ValueType::kSimd128, node); } return operand.length; } - unsigned SimdShuffleOp(WasmOpcode opcode) { - SimdShuffleOperand<true> operand(this, pc_, GetShuffleMaskSize(opcode)); - if (Validate(pc_, opcode, operand)) { + unsigned Simd8x16ShuffleOp() { + Simd8x16ShuffleOperand<true> operand(this, pc_); + if (Validate(pc_, operand)) { compiler::NodeVector inputs(2, zone_); inputs[1] = Pop(1, ValueType::kSimd128).node; inputs[0] = Pop(0, ValueType::kSimd128).node; - TFNode* node = - BUILD(SimdShuffleOp, operand.shuffle, operand.lanes, inputs); + TFNode* node = BUILD(Simd8x16ShuffleOp, operand.shuffle, inputs.data()); Push(ValueType::kSimd128, node); } - return operand.lanes; + return 16; } unsigned DecodeSimdOpcode(WasmOpcode opcode) { @@ -1625,12 +1617,16 @@ class WasmFullDecoder : public WasmDecoder { len = SimdShiftOp(opcode); break; } - case kExprS32x4Shuffle: - case kExprS16x8Shuffle: case kExprS8x16Shuffle: { - len = SimdShuffleOp(opcode); + len = Simd8x16ShuffleOp(); break; } + case kExprS128LoadMem: + len = DecodePrefixedLoadMem(kWasmS128, MachineType::Simd128()); + break; + case kExprS128StoreMem: + len = DecodePrefixedStoreMem(kWasmS128, MachineType::Simd128()); + break; default: { FunctionSig* sig = WasmOpcodes::Signature(opcode); if (sig != nullptr) { @@ -1639,7 +1635,7 @@ class WasmFullDecoder : public WasmDecoder { Value val = Pop(static_cast<int>(i - 1), sig->GetParam(i - 1)); inputs[i - 1] = val.node; } - TFNode* node = BUILD(SimdOp, opcode, inputs); + TFNode* node = BUILD(SimdOp, opcode, inputs.data()); Push(GetReturnType(sig), node); } else { error("invalid simd opcode"); @@ -1722,10 +1718,6 @@ class WasmFullDecoder : public WasmDecoder { return val; } - int baserel(const byte* ptr) { - return base_ ? static_cast<int>(ptr - base_) : 0; - } - int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); } void BreakTo(unsigned depth) { @@ -2132,7 +2124,7 @@ DecodeResult VerifyWasmCode(AccountingAllocator* allocator, Zone zone(allocator, ZONE_NAME); WasmFullDecoder decoder(&zone, module, body); decoder.Decode(); - return decoder.toResult<DecodeStruct*>(nullptr); + return decoder.toResult(nullptr); } DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder, @@ -2140,7 +2132,7 @@ DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder, Zone zone(allocator, ZONE_NAME); WasmFullDecoder decoder(&zone, builder, body); decoder.Decode(); - return decoder.toResult<DecodeStruct*>(nullptr); + return decoder.toResult(nullptr); } unsigned OpcodeLength(const byte* pc, const byte* end) { diff --git a/deps/v8/src/wasm/function-body-decoder.h b/deps/v8/src/wasm/function-body-decoder.h index ef3998f0e1..5efc1e1c18 100644 --- a/deps/v8/src/wasm/function-body-decoder.h +++ b/deps/v8/src/wasm/function-body-decoder.h @@ -5,8 +5,6 @@ #ifndef V8_WASM_FUNCTION_BODY_DECODER_H_ #define V8_WASM_FUNCTION_BODY_DECODER_H_ -#include <iterator> - #include "src/base/compiler-specific.h" #include "src/base/iterator.h" #include "src/globals.h" @@ -32,23 +30,21 @@ struct WasmModule; // forward declaration of module interface. // A wrapper around the signature and bytes of a function. struct FunctionBody { FunctionSig* sig; // function signature - const byte* base; // base of the module bytes, for error reporting + uint32_t offset; // offset in the module bytes, for error reporting const byte* start; // start of the function body const byte* end; // end of the function body }; static inline FunctionBody FunctionBodyForTesting(const byte* start, const byte* end) { - return {nullptr, start, start, end}; + return {nullptr, 0, start, end}; } -struct DecodeStruct { - int unused; -}; -typedef Result<DecodeStruct*> DecodeResult; -inline std::ostream& operator<<(std::ostream& os, const DecodeStruct& tree) { - return os; -} +// A {DecodeResult} only stores the failure / success status, but no data. Thus +// we use {nullptr_t} as data value, such that the only valid data stored in +// this type is a nullptr. +// Storing {void} would require template specialization. +using DecodeResult = Result<std::nullptr_t>; V8_EXPORT_PRIVATE DecodeResult VerifyWasmCode(AccountingAllocator* allocator, const wasm::WasmModule* module, @@ -64,14 +60,14 @@ void PrintRawWasmCode(const byte* start, const byte* end); inline DecodeResult VerifyWasmCode(AccountingAllocator* allocator, const WasmModule* module, FunctionSig* sig, const byte* start, const byte* end) { - FunctionBody body = {sig, nullptr, start, end}; + FunctionBody body = {sig, 0, start, end}; return VerifyWasmCode(allocator, module, body); } inline DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder, FunctionSig* sig, const byte* start, const byte* end) { - FunctionBody body = {sig, nullptr, start, end}; + FunctionBody body = {sig, 0, start, end}; return BuildTFGraph(allocator, builder, body); } @@ -131,7 +127,7 @@ class V8_EXPORT_PRIVATE BytecodeIterator : public NON_EXPORTED_BASE(Decoder) { // If one wants to iterate over the bytecode without looking at {pc_offset()}. class opcode_iterator : public iterator_base, - public std::iterator<std::input_iterator_tag, WasmOpcode> { + public base::iterator<std::input_iterator_tag, WasmOpcode> { public: inline WasmOpcode operator*() { DCHECK_LT(ptr_, end_); @@ -147,7 +143,7 @@ class V8_EXPORT_PRIVATE BytecodeIterator : public NON_EXPORTED_BASE(Decoder) { // opcodes. class offset_iterator : public iterator_base, - public std::iterator<std::input_iterator_tag, uint32_t> { + public base::iterator<std::input_iterator_tag, uint32_t> { public: inline uint32_t operator*() { DCHECK_LT(ptr_, end_); diff --git a/deps/v8/src/wasm/module-compiler.cc b/deps/v8/src/wasm/module-compiler.cc new file mode 100644 index 0000000000..77700b2abe --- /dev/null +++ b/deps/v8/src/wasm/module-compiler.cc @@ -0,0 +1,2356 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <src/wasm/module-compiler.h> + +#include <atomic> + +#include "src/asmjs/asm-js.h" +#include "src/assembler-inl.h" +#include "src/code-stubs.h" +#include "src/counters.h" +#include "src/property-descriptor.h" +#include "src/wasm/compilation-manager.h" +#include "src/wasm/module-decoder.h" +#include "src/wasm/wasm-js.h" +#include "src/wasm/wasm-module.h" +#include "src/wasm/wasm-objects.h" +#include "src/wasm/wasm-result.h" + +#define TRACE(...) \ + do { \ + if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \ + } while (false) + +#define TRACE_CHAIN(instance) \ + do { \ + instance->PrintInstancesChain(); \ + } while (false) + +#define TRACE_COMPILE(...) \ + do { \ + if (FLAG_trace_wasm_compiler) PrintF(__VA_ARGS__); \ + } while (false) + +static const int kInvalidSigIndex = -1; + +namespace v8 { +namespace internal { +namespace wasm { + +ModuleCompiler::CodeGenerationSchedule::CodeGenerationSchedule( + base::RandomNumberGenerator* random_number_generator, size_t max_memory) + : random_number_generator_(random_number_generator), + max_memory_(max_memory) { + DCHECK_NOT_NULL(random_number_generator_); + DCHECK_GT(max_memory_, 0); +} + +void ModuleCompiler::CodeGenerationSchedule::Schedule( + std::unique_ptr<compiler::WasmCompilationUnit>&& item) { + size_t cost = item->memory_cost(); + schedule_.push_back(std::move(item)); + allocated_memory_.Increment(cost); +} + +bool ModuleCompiler::CodeGenerationSchedule::CanAcceptWork() const { + return (!throttle_ || allocated_memory_.Value() <= max_memory_); +} + +bool ModuleCompiler::CodeGenerationSchedule::ShouldIncreaseWorkload() const { + // Half the memory is unused again, we can increase the workload again. + return (!throttle_ || allocated_memory_.Value() <= max_memory_ / 2); +} + +std::unique_ptr<compiler::WasmCompilationUnit> +ModuleCompiler::CodeGenerationSchedule::GetNext() { + DCHECK(!IsEmpty()); + size_t index = GetRandomIndexInSchedule(); + auto ret = std::move(schedule_[index]); + std::swap(schedule_[schedule_.size() - 1], schedule_[index]); + schedule_.pop_back(); + allocated_memory_.Decrement(ret->memory_cost()); + return ret; +} + +size_t ModuleCompiler::CodeGenerationSchedule::GetRandomIndexInSchedule() { + double factor = random_number_generator_->NextDouble(); + size_t index = (size_t)(factor * schedule_.size()); + DCHECK_GE(index, 0); + DCHECK_LT(index, schedule_.size()); + return index; +} + +ModuleCompiler::ModuleCompiler(Isolate* isolate, + std::unique_ptr<WasmModule> module) + : isolate_(isolate), + module_(std::move(module)), + async_counters_(isolate->async_counters()), + executed_units_( + isolate->random_number_generator(), + (isolate->heap()->memory_allocator()->code_range()->valid() + ? isolate->heap()->memory_allocator()->code_range()->size() + : isolate->heap()->code_space()->Capacity()) / + 2), + num_background_tasks_( + Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), + V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads())), + stopped_compilation_tasks_(num_background_tasks_), + centry_stub_(CEntryStub(isolate, 1).GetCode()) {} + +// The actual runnable task that performs compilations in the background. +ModuleCompiler::CompilationTask::CompilationTask(ModuleCompiler* compiler) + : CancelableTask(&compiler->background_task_manager_), + compiler_(compiler) {} + +void ModuleCompiler::CompilationTask::RunInternal() { + while (compiler_->executed_units_.CanAcceptWork() && + compiler_->FetchAndExecuteCompilationUnit()) { + } + + compiler_->OnBackgroundTaskStopped(); +} + +void ModuleCompiler::OnBackgroundTaskStopped() { + base::LockGuard<base::Mutex> guard(&tasks_mutex_); + ++stopped_compilation_tasks_; + DCHECK_LE(stopped_compilation_tasks_, num_background_tasks_); +} + +// Run by each compilation task The no_finisher_callback is called +// within the result_mutex_ lock when no finishing task is running, +// i.e. when the finisher_is_running_ flag is not set. +bool ModuleCompiler::FetchAndExecuteCompilationUnit( + std::function<void()> no_finisher_callback) { + DisallowHeapAllocation no_allocation; + DisallowHandleAllocation no_handles; + DisallowHandleDereference no_deref; + DisallowCodeDependencyChange no_dependency_change; + + std::unique_ptr<compiler::WasmCompilationUnit> unit; + { + base::LockGuard<base::Mutex> guard(&compilation_units_mutex_); + if (compilation_units_.empty()) return false; + unit = std::move(compilation_units_.back()); + compilation_units_.pop_back(); + } + unit->ExecuteCompilation(); + { + base::LockGuard<base::Mutex> guard(&result_mutex_); + executed_units_.Schedule(std::move(unit)); + if (no_finisher_callback != nullptr && !finisher_is_running_) { + no_finisher_callback(); + // We set the flag here so that not more than one finisher is started. + finisher_is_running_ = true; + } + } + return true; +} + +size_t ModuleCompiler::InitializeCompilationUnits( + const std::vector<WasmFunction>& functions, ModuleBytesEnv& module_env) { + uint32_t start = module_env.module_env.module->num_imported_functions + + FLAG_skip_compiling_wasm_funcs; + uint32_t num_funcs = static_cast<uint32_t>(functions.size()); + uint32_t funcs_to_compile = start > num_funcs ? 0 : num_funcs - start; + CompilationUnitBuilder builder(this); + for (uint32_t i = start; i < num_funcs; ++i) { + const WasmFunction* func = &functions[i]; + uint32_t buffer_offset = func->code.offset(); + Vector<const uint8_t> bytes( + module_env.wire_bytes.start() + func->code.offset(), + func->code.end_offset() - func->code.offset()); + WasmName name = module_env.wire_bytes.GetName(func); + builder.AddUnit(&module_env.module_env, func, buffer_offset, bytes, name); + } + builder.Commit(); + return funcs_to_compile; +} + +void ModuleCompiler::ReopenHandlesInDeferredScope() { + centry_stub_ = handle(*centry_stub_, isolate_); +} + +void ModuleCompiler::RestartCompilationTasks() { + base::LockGuard<base::Mutex> guard(&tasks_mutex_); + for (; stopped_compilation_tasks_ > 0; --stopped_compilation_tasks_) { + V8::GetCurrentPlatform()->CallOnBackgroundThread( + new CompilationTask(this), + v8::Platform::ExpectedRuntime::kShortRunningTask); + } +} + +size_t ModuleCompiler::FinishCompilationUnits( + std::vector<Handle<Code>>& results, ErrorThrower* thrower) { + size_t finished = 0; + while (true) { + int func_index = -1; + MaybeHandle<Code> result = FinishCompilationUnit(thrower, &func_index); + if (func_index < 0) break; + ++finished; + DCHECK_IMPLIES(result.is_null(), thrower->error()); + if (result.is_null()) break; + results[func_index] = result.ToHandleChecked(); + } + bool do_restart; + { + base::LockGuard<base::Mutex> guard(&compilation_units_mutex_); + do_restart = !compilation_units_.empty(); + } + if (do_restart) RestartCompilationTasks(); + return finished; +} + +void ModuleCompiler::SetFinisherIsRunning(bool value) { + base::LockGuard<base::Mutex> guard(&result_mutex_); + finisher_is_running_ = value; +} + +MaybeHandle<Code> ModuleCompiler::FinishCompilationUnit(ErrorThrower* thrower, + int* func_index) { + std::unique_ptr<compiler::WasmCompilationUnit> unit; + { + base::LockGuard<base::Mutex> guard(&result_mutex_); + if (executed_units_.IsEmpty()) return {}; + unit = executed_units_.GetNext(); + } + *func_index = unit->func_index(); + return unit->FinishCompilation(thrower); +} + +void ModuleCompiler::CompileInParallel(ModuleBytesEnv* module_env, + std::vector<Handle<Code>>& results, + ErrorThrower* thrower) { + const WasmModule* module = module_env->module_env.module; + // Data structures for the parallel compilation. + + //----------------------------------------------------------------------- + // For parallel compilation: + // 1) The main thread allocates a compilation unit for each wasm function + // and stores them in the vector {compilation_units}. + // 2) The main thread spawns {CompilationTask} instances which run on + // the background threads. + // 3.a) The background threads and the main thread pick one compilation + // unit at a time and execute the parallel phase of the compilation + // unit. After finishing the execution of the parallel phase, the + // result is enqueued in {executed_units}. + // 3.b) If {executed_units} contains a compilation unit, the main thread + // dequeues it and finishes the compilation. + // 4) After the parallel phase of all compilation units has started, the + // main thread waits for all {CompilationTask} instances to finish. + // 5) The main thread finishes the compilation. + + // Turn on the {CanonicalHandleScope} so that the background threads can + // use the node cache. + CanonicalHandleScope canonical(isolate_); + + // 1) The main thread allocates a compilation unit for each wasm function + // and stores them in the vector {compilation_units}. + InitializeCompilationUnits(module->functions, *module_env); + executed_units_.EnableThrottling(); + + // 2) The main thread spawns {CompilationTask} instances which run on + // the background threads. + RestartCompilationTasks(); + + // 3.a) The background threads and the main thread pick one compilation + // unit at a time and execute the parallel phase of the compilation + // unit. After finishing the execution of the parallel phase, the + // result is enqueued in {executed_units}. + // The foreground task bypasses waiting on memory threshold, because + // its results will immediately be converted to code (below). + while (FetchAndExecuteCompilationUnit()) { + // 3.b) If {executed_units} contains a compilation unit, the main thread + // dequeues it and finishes the compilation unit. Compilation units + // are finished concurrently to the background threads to save + // memory. + FinishCompilationUnits(results, thrower); + } + // 4) After the parallel phase of all compilation units has started, the + // main thread waits for all {CompilationTask} instances to finish - which + // happens once they all realize there's no next work item to process. + background_task_manager_.CancelAndWait(); + // Finish all compilation units which have been executed while we waited. + FinishCompilationUnits(results, thrower); +} + +void ModuleCompiler::CompileSequentially(ModuleBytesEnv* module_env, + std::vector<Handle<Code>>& results, + ErrorThrower* thrower) { + DCHECK(!thrower->error()); + + const WasmModule* module = module_env->module_env.module; + for (uint32_t i = FLAG_skip_compiling_wasm_funcs; + i < module->functions.size(); ++i) { + const WasmFunction& func = module->functions[i]; + if (func.imported) continue; // Imports are compiled at instantiation time. + + // Compile the function. + MaybeHandle<Code> code = compiler::WasmCompilationUnit::CompileWasmFunction( + thrower, isolate_, module_env, &func); + if (code.is_null()) { + WasmName str = module_env->wire_bytes.GetName(&func); + // TODO(clemensh): Truncate the function name in the output. + thrower->CompileError("Compilation of #%d:%.*s failed.", i, str.length(), + str.start()); + break; + } + results[i] = code.ToHandleChecked(); + } +} + +void ModuleCompiler::ValidateSequentially(ModuleBytesEnv* module_env, + ErrorThrower* thrower) { + DCHECK(!thrower->error()); + + const WasmModule* module = module_env->module_env.module; + for (uint32_t i = 0; i < module->functions.size(); ++i) { + const WasmFunction& func = module->functions[i]; + if (func.imported) continue; + + const byte* base = module_env->wire_bytes.start(); + FunctionBody body{func.sig, func.code.offset(), base + func.code.offset(), + base + func.code.end_offset()}; + DecodeResult result = VerifyWasmCode(isolate_->allocator(), + module_env->module_env.module, body); + if (result.failed()) { + WasmName str = module_env->wire_bytes.GetName(&func); + thrower->CompileError("Compiling function #%d:%.*s failed: %s @+%u", i, + str.length(), str.start(), + result.error_msg().c_str(), result.error_offset()); + break; + } + } +} + +MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObject( + ErrorThrower* thrower, const ModuleWireBytes& wire_bytes, + Handle<Script> asm_js_script, + Vector<const byte> asm_js_offset_table_bytes) { + Factory* factory = isolate_->factory(); + WasmInstance temp_instance(module_.get()); + temp_instance.context = isolate_->native_context(); + temp_instance.mem_size = WasmModule::kPageSize * module_->min_mem_pages; + temp_instance.mem_start = nullptr; + temp_instance.globals_start = nullptr; + + // Initialize the indirect tables with placeholders. + int function_table_count = static_cast<int>(module_->function_tables.size()); + Handle<FixedArray> function_tables = + factory->NewFixedArray(function_table_count, TENURED); + Handle<FixedArray> signature_tables = + factory->NewFixedArray(function_table_count, TENURED); + for (int i = 0; i < function_table_count; ++i) { + temp_instance.function_tables[i] = factory->NewFixedArray(1, TENURED); + temp_instance.signature_tables[i] = factory->NewFixedArray(1, TENURED); + function_tables->set(i, *temp_instance.function_tables[i]); + signature_tables->set(i, *temp_instance.signature_tables[i]); + } + + TimedHistogramScope wasm_compile_module_time_scope( + module_->is_wasm() ? counters()->wasm_compile_wasm_module_time() + : counters()->wasm_compile_asm_module_time()); + return CompileToModuleObjectInternal( + thrower, wire_bytes, asm_js_script, asm_js_offset_table_bytes, factory, + &temp_instance, &function_tables, &signature_tables); +} + +namespace { +bool compile_lazy(const WasmModule* module) { + return FLAG_wasm_lazy_compilation || + (FLAG_asm_wasm_lazy_compilation && module->is_asm_js()); +} + +void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) { + for (int i = 0; i < code_table->length(); ++i) { + Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i); + Assembler::FlushICache(isolate, code->instruction_start(), + code->instruction_size()); + } +} + +byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) { + return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset; +} + +void RecordStats(Code* code, Counters* counters) { + counters->wasm_generated_code_size()->Increment(code->body_size()); + counters->wasm_reloc_size()->Increment(code->relocation_info()->length()); +} + +void RecordStats(Handle<FixedArray> functions, Counters* counters) { + DisallowHeapAllocation no_gc; + for (int i = 0; i < functions->length(); ++i) { + RecordStats(Code::cast(functions->get(i)), counters); + } +} +Handle<Script> CreateWasmScript(Isolate* isolate, + const ModuleWireBytes& wire_bytes) { + Handle<Script> script = + isolate->factory()->NewScript(isolate->factory()->empty_string()); + script->set_context_data(isolate->native_context()->debug_context_id()); + script->set_type(Script::TYPE_WASM); + + int hash = StringHasher::HashSequentialString( + reinterpret_cast<const char*>(wire_bytes.start()), + static_cast<int>(wire_bytes.length()), kZeroHashSeed); + + const int kBufferSize = 32; + char buffer[kBufferSize]; + int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash); + DCHECK(url_chars >= 0 && url_chars < kBufferSize); + MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte( + Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars), + TENURED); + script->set_source_url(*url_str.ToHandleChecked()); + + int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash); + DCHECK(name_chars >= 0 && name_chars < kBufferSize); + MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte( + Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars), + TENURED); + script->set_name(*name_str.ToHandleChecked()); + + return script; +} + +// Ensure that the code object in <code_table> at offset <func_index> has +// deoptimization data attached. This is needed for lazy compile stubs which are +// called from JS_TO_WASM functions or via exported function tables. The deopt +// data is used to determine which function this lazy compile stub belongs to. +Handle<Code> EnsureExportedLazyDeoptData(Isolate* isolate, + Handle<WasmInstanceObject> instance, + Handle<FixedArray> code_table, + int func_index) { + Handle<Code> code(Code::cast(code_table->get(func_index)), isolate); + if (code->builtin_index() != Builtins::kWasmCompileLazy) { + // No special deopt data needed for compiled functions, and imported + // functions, which map to Illegal at this point (they get compiled at + // instantiation time). + DCHECK(code->kind() == Code::WASM_FUNCTION || + code->kind() == Code::WASM_TO_JS_FUNCTION || + code->builtin_index() == Builtins::kIllegal); + return code; + } + // deopt_data: + // #0: weak instance + // #1: func_index + // might be extended later for table exports (see + // EnsureTableExportLazyDeoptData). + Handle<FixedArray> deopt_data(code->deoptimization_data()); + DCHECK_EQ(0, deopt_data->length() % 2); + if (deopt_data->length() == 0) { + code = isolate->factory()->CopyCode(code); + code_table->set(func_index, *code); + deopt_data = isolate->factory()->NewFixedArray(2, TENURED); + code->set_deoptimization_data(*deopt_data); + if (!instance.is_null()) { + Handle<WeakCell> weak_instance = + isolate->factory()->NewWeakCell(instance); + deopt_data->set(0, *weak_instance); + } + deopt_data->set(1, Smi::FromInt(func_index)); + } + DCHECK_IMPLIES(!instance.is_null(), + WeakCell::cast(code->deoptimization_data()->get(0))->value() == + *instance); + DCHECK_EQ(func_index, Smi::ToInt(code->deoptimization_data()->get(1))); + return code; +} + +// Ensure that the code object in <code_table> at offset <func_index> has +// deoptimization data attached. This is needed for lazy compile stubs which are +// called from JS_TO_WASM functions or via exported function tables. The deopt +// data is used to determine which function this lazy compile stub belongs to. +Handle<Code> EnsureTableExportLazyDeoptData( + Isolate* isolate, Handle<WasmInstanceObject> instance, + Handle<FixedArray> code_table, int func_index, + Handle<FixedArray> export_table, int export_index, + std::unordered_map<uint32_t, uint32_t>& table_export_count) { + Handle<Code> code = + EnsureExportedLazyDeoptData(isolate, instance, code_table, func_index); + if (code->builtin_index() != Builtins::kWasmCompileLazy) return code; + + // deopt_data: + // #0: weak instance + // #1: func_index + // [#2: export table + // #3: export table index] + // [#4: export table + // #5: export table index] + // ... + // table_export_count counts down and determines the index for the new export + // table entry. + auto table_export_entry = table_export_count.find(func_index); + DCHECK(table_export_entry != table_export_count.end()); + DCHECK_LT(0, table_export_entry->second); + uint32_t this_idx = 2 * table_export_entry->second; + --table_export_entry->second; + Handle<FixedArray> deopt_data(code->deoptimization_data()); + DCHECK_EQ(0, deopt_data->length() % 2); + if (deopt_data->length() == 2) { + // Then only the "header" (#0 and #1) exists. Extend for the export table + // entries (make space for this_idx + 2 elements). + deopt_data = isolate->factory()->CopyFixedArrayAndGrow(deopt_data, this_idx, + TENURED); + code->set_deoptimization_data(*deopt_data); + } + DCHECK_LE(this_idx + 2, deopt_data->length()); + DCHECK(deopt_data->get(this_idx)->IsUndefined(isolate)); + DCHECK(deopt_data->get(this_idx + 1)->IsUndefined(isolate)); + deopt_data->set(this_idx, *export_table); + deopt_data->set(this_idx + 1, Smi::FromInt(export_index)); + return code; +} + +bool in_bounds(uint32_t offset, uint32_t size, uint32_t upper) { + return offset + size <= upper && offset + size >= offset; +} + +using WasmInstanceMap = + IdentityMap<Handle<WasmInstanceObject>, FreeStoreAllocationPolicy>; + +Handle<Code> UnwrapOrCompileImportWrapper( + Isolate* isolate, int index, FunctionSig* sig, Handle<JSReceiver> target, + Handle<String> module_name, MaybeHandle<String> import_name, + ModuleOrigin origin, WasmInstanceMap* imported_instances) { + WasmFunction* other_func = GetWasmFunctionForImportWrapper(isolate, target); + if (other_func) { + if (!sig->Equals(other_func->sig)) return Handle<Code>::null(); + // Signature matched. Unwrap the import wrapper and return the raw wasm + // function code. + // Remember the wasm instance of the import. We have to keep it alive. + Handle<WasmInstanceObject> imported_instance( + Handle<WasmExportedFunction>::cast(target)->instance(), isolate); + imported_instances->Set(imported_instance, imported_instance); + return UnwrapImportWrapper(target); + } + // No wasm function or being debugged. Compile a new wrapper for the new + // signature. + return compiler::CompileWasmToJSWrapper(isolate, target, sig, index, + module_name, import_name, origin); +} + +double MonotonicallyIncreasingTimeInMs() { + return V8::GetCurrentPlatform()->MonotonicallyIncreasingTime() * + base::Time::kMillisecondsPerSecond; +} + +void RejectPromise(Isolate* isolate, Handle<Context> context, + ErrorThrower& thrower, Handle<JSPromise> promise) { + v8::Local<v8::Promise::Resolver> resolver = + v8::Utils::PromiseToLocal(promise).As<v8::Promise::Resolver>(); + auto maybe = resolver->Reject(v8::Utils::ToLocal(context), + v8::Utils::ToLocal(thrower.Reify())); + CHECK_IMPLIES(!maybe.FromMaybe(false), isolate->has_scheduled_exception()); +} + +void ResolvePromise(Isolate* isolate, Handle<Context> context, + Handle<JSPromise> promise, Handle<Object> result) { + v8::Local<v8::Promise::Resolver> resolver = + v8::Utils::PromiseToLocal(promise).As<v8::Promise::Resolver>(); + auto maybe = resolver->Resolve(v8::Utils::ToLocal(context), + v8::Utils::ToLocal(result)); + CHECK_IMPLIES(!maybe.FromMaybe(false), isolate->has_scheduled_exception()); +} + +} // namespace + +MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObjectInternal( + ErrorThrower* thrower, const ModuleWireBytes& wire_bytes, + Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes, + Factory* factory, WasmInstance* temp_instance, + Handle<FixedArray>* function_tables, Handle<FixedArray>* signature_tables) { + ModuleBytesEnv module_env(module_.get(), temp_instance, wire_bytes); + + // The {code_table} array contains import wrappers and functions (which + // are both included in {functions.size()}, and export wrappers. + int code_table_size = static_cast<int>(module_->functions.size() + + module_->num_exported_functions); + Handle<FixedArray> code_table = + factory->NewFixedArray(static_cast<int>(code_table_size), TENURED); + + // Check whether lazy compilation is enabled for this module. + bool lazy_compile = compile_lazy(module_.get()); + + // If lazy compile: Initialize the code table with the lazy compile builtin. + // Otherwise: Initialize with the illegal builtin. All call sites will be + // patched at instantiation. + Handle<Code> init_builtin = lazy_compile + ? isolate_->builtins()->WasmCompileLazy() + : isolate_->builtins()->Illegal(); + for (int i = 0, e = static_cast<int>(module_->functions.size()); i < e; ++i) { + code_table->set(i, *init_builtin); + temp_instance->function_code[i] = init_builtin; + } + + (module_->is_wasm() ? counters()->wasm_functions_per_wasm_module() + : counters()->wasm_functions_per_asm_module()) + ->AddSample(static_cast<int>(module_->functions.size())); + + if (!lazy_compile) { + size_t funcs_to_compile = + module_->functions.size() - module_->num_imported_functions; + bool compile_parallel = + !FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks > 0 && + funcs_to_compile > 1 && + V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads() > 0; + if (compile_parallel) { + // Avoid a race condition by collecting results into a second vector. + std::vector<Handle<Code>> results(temp_instance->function_code); + CompileInParallel(&module_env, results, thrower); + temp_instance->function_code.swap(results); + } else { + CompileSequentially(&module_env, temp_instance->function_code, thrower); + } + } else if (module_->is_wasm()) { + // Validate wasm modules for lazy compilation. Don't validate asm.js + // modules, they are valid by construction (otherwise a CHECK will fail + // during lazy compilation). + // TODO(clemensh): According to the spec, we can actually skip validation + // at module creation time, and return a function that always traps at + // (lazy) compilation time. + ValidateSequentially(&module_env, thrower); + } + if (thrower->error()) return {}; + + // At this point, compilation has completed. Update the code table. + for (size_t i = FLAG_skip_compiling_wasm_funcs; + i < temp_instance->function_code.size(); ++i) { + Code* code = *temp_instance->function_code[i]; + code_table->set(static_cast<int>(i), code); + RecordStats(code, counters()); + } + + // Create heap objects for script, module bytes and asm.js offset table to + // be stored in the shared module data. + Handle<Script> script; + Handle<ByteArray> asm_js_offset_table; + if (asm_js_script.is_null()) { + script = CreateWasmScript(isolate_, wire_bytes); + } else { + script = asm_js_script; + asm_js_offset_table = + isolate_->factory()->NewByteArray(asm_js_offset_table_bytes.length()); + asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(), + asm_js_offset_table_bytes.length()); + } + // TODO(wasm): only save the sections necessary to deserialize a + // {WasmModule}. E.g. function bodies could be omitted. + Handle<String> module_bytes = + factory + ->NewStringFromOneByte({wire_bytes.start(), wire_bytes.length()}, + TENURED) + .ToHandleChecked(); + DCHECK(module_bytes->IsSeqOneByteString()); + + // The {module_wrapper} will take ownership of the {WasmModule} object, + // and it will be destroyed when the GC reclaims the wrapper object. + Handle<WasmModuleWrapper> module_wrapper = + WasmModuleWrapper::New(isolate_, module_.release()); + WasmModule* module = module_wrapper->get(); + + // Create the shared module data. + // TODO(clemensh): For the same module (same bytes / same hash), we should + // only have one WasmSharedModuleData. Otherwise, we might only set + // breakpoints on a (potentially empty) subset of the instances. + + Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New( + isolate_, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes), + script, asm_js_offset_table); + if (lazy_compile) WasmSharedModuleData::PrepareForLazyCompilation(shared); + + // Create the compiled module object, and populate with compiled functions + // and information needed at instantiation time. This object needs to be + // serializable. Instantiation may occur off a deserialized version of this + // object. + Handle<WasmCompiledModule> compiled_module = WasmCompiledModule::New( + isolate_, shared, code_table, *function_tables, *signature_tables); + + // If we created a wasm script, finish it now and make it public to the + // debugger. + if (asm_js_script.is_null()) { + script->set_wasm_compiled_module(*compiled_module); + isolate_->debug()->OnAfterCompile(script); + } + + // Compile JS->wasm wrappers for exported functions. + JSToWasmWrapperCache js_to_wasm_cache; + int func_index = 0; + for (auto exp : module->export_table) { + if (exp.kind != kExternalFunction) continue; + Handle<Code> wasm_code = EnsureExportedLazyDeoptData( + isolate_, Handle<WasmInstanceObject>::null(), code_table, exp.index); + Handle<Code> wrapper_code = js_to_wasm_cache.CloneOrCompileJSToWasmWrapper( + isolate_, module, wasm_code, exp.index); + int export_index = static_cast<int>(module->functions.size() + func_index); + code_table->set(export_index, *wrapper_code); + RecordStats(*wrapper_code, counters()); + func_index++; + } + + return WasmModuleObject::New(isolate_, compiled_module); +} + +Handle<Code> JSToWasmWrapperCache::CloneOrCompileJSToWasmWrapper( + Isolate* isolate, const wasm::WasmModule* module, Handle<Code> wasm_code, + uint32_t index) { + const wasm::WasmFunction* func = &module->functions[index]; + int cached_idx = sig_map_.Find(func->sig); + if (cached_idx >= 0) { + Handle<Code> code = isolate->factory()->CopyCode(code_cache_[cached_idx]); + // Now patch the call to wasm code. + for (RelocIterator it(*code, RelocInfo::kCodeTargetMask);; it.next()) { + DCHECK(!it.done()); + Code* target = + Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); + if (target->kind() == Code::WASM_FUNCTION || + target->kind() == Code::WASM_TO_JS_FUNCTION || + target->builtin_index() == Builtins::kIllegal || + target->builtin_index() == Builtins::kWasmCompileLazy) { + it.rinfo()->set_target_address(isolate, wasm_code->instruction_start()); + break; + } + } + return code; + } + + Handle<Code> code = + compiler::CompileJSToWasmWrapper(isolate, module, wasm_code, index); + uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig); + DCHECK_EQ(code_cache_.size(), new_cache_idx); + USE(new_cache_idx); + code_cache_.push_back(code); + return code; +} + +InstanceBuilder::InstanceBuilder( + Isolate* isolate, ErrorThrower* thrower, + Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> ffi, + MaybeHandle<JSArrayBuffer> memory, + WeakCallbackInfo<void>::Callback instance_finalizer_callback) + : isolate_(isolate), + module_(module_object->compiled_module()->module()), + async_counters_(isolate->async_counters()), + thrower_(thrower), + module_object_(module_object), + ffi_(ffi), + memory_(memory), + instance_finalizer_callback_(instance_finalizer_callback) {} + +// Build an instance, in all of its glory. +MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { + // Check that an imports argument was provided, if the module requires it. + // No point in continuing otherwise. + if (!module_->import_table.empty() && ffi_.is_null()) { + thrower_->TypeError( + "Imports argument must be present and must be an object"); + return {}; + } + + // Record build time into correct bucket, then build instance. + TimedHistogramScope wasm_instantiate_module_time_scope( + module_->is_wasm() ? counters()->wasm_instantiate_wasm_module_time() + : counters()->wasm_instantiate_asm_module_time()); + Factory* factory = isolate_->factory(); + + //-------------------------------------------------------------------------- + // Reuse the compiled module (if no owner), otherwise clone. + //-------------------------------------------------------------------------- + Handle<FixedArray> code_table; + // We keep around a copy of the old code table, because we'll be replacing + // imports for the new instance, and then we need the old imports to be + // able to relocate. + Handle<FixedArray> old_code_table; + MaybeHandle<WasmInstanceObject> owner; + + TRACE("Starting new module instantiation\n"); + { + // Root the owner, if any, before doing any allocations, which + // may trigger GC. + // Both owner and original template need to be in sync. Even + // after we lose the original template handle, the code + // objects we copied from it have data relative to the + // instance - such as globals addresses. + Handle<WasmCompiledModule> original; + { + DisallowHeapAllocation no_gc; + original = handle(module_object_->compiled_module()); + if (original->has_weak_owning_instance()) { + owner = handle(WasmInstanceObject::cast( + original->weak_owning_instance()->value())); + } + } + DCHECK(!original.is_null()); + if (original->has_weak_owning_instance()) { + // Clone, but don't insert yet the clone in the instances chain. + // We do that last. Since we are holding on to the owner instance, + // the owner + original state used for cloning and patching + // won't be mutated by possible finalizer runs. + DCHECK(!owner.is_null()); + TRACE("Cloning from %d\n", original->instance_id()); + old_code_table = original->code_table(); + compiled_module_ = WasmCompiledModule::Clone(isolate_, original); + code_table = compiled_module_->code_table(); + // Avoid creating too many handles in the outer scope. + HandleScope scope(isolate_); + + // Clone the code for wasm functions and exports. + for (int i = 0; i < code_table->length(); ++i) { + Handle<Code> orig_code(Code::cast(code_table->get(i)), isolate_); + switch (orig_code->kind()) { + case Code::WASM_TO_JS_FUNCTION: + // Imports will be overwritten with newly compiled wrappers. + break; + case Code::BUILTIN: + DCHECK_EQ(Builtins::kWasmCompileLazy, orig_code->builtin_index()); + // If this code object has deoptimization data, then we need a + // unique copy to attach updated deoptimization data. + if (orig_code->deoptimization_data()->length() > 0) { + Handle<Code> code = factory->CopyCode(orig_code); + Handle<FixedArray> deopt_data = + factory->NewFixedArray(2, TENURED); + deopt_data->set(1, Smi::FromInt(i)); + code->set_deoptimization_data(*deopt_data); + code_table->set(i, *code); + } + break; + case Code::JS_TO_WASM_FUNCTION: + case Code::WASM_FUNCTION: { + Handle<Code> code = factory->CopyCode(orig_code); + code_table->set(i, *code); + break; + } + default: + UNREACHABLE(); + } + } + RecordStats(code_table, counters()); + } else { + // There was no owner, so we can reuse the original. + compiled_module_ = original; + old_code_table = factory->CopyFixedArray(compiled_module_->code_table()); + code_table = compiled_module_->code_table(); + TRACE("Reusing existing instance %d\n", compiled_module_->instance_id()); + } + compiled_module_->set_native_context(isolate_->native_context()); + } + + //-------------------------------------------------------------------------- + // Allocate the instance object. + //-------------------------------------------------------------------------- + Zone instantiation_zone(isolate_->allocator(), ZONE_NAME); + CodeSpecialization code_specialization(isolate_, &instantiation_zone); + Handle<WasmInstanceObject> instance = + WasmInstanceObject::New(isolate_, compiled_module_); + + //-------------------------------------------------------------------------- + // Set up the globals for the new instance. + //-------------------------------------------------------------------------- + MaybeHandle<JSArrayBuffer> old_globals; + uint32_t globals_size = module_->globals_size; + if (globals_size > 0) { + const bool enable_guard_regions = false; + Handle<JSArrayBuffer> global_buffer = + NewArrayBuffer(isolate_, globals_size, enable_guard_regions); + globals_ = global_buffer; + if (globals_.is_null()) { + thrower_->RangeError("Out of memory: wasm globals"); + return {}; + } + Address old_globals_start = compiled_module_->GetGlobalsStartOrNull(); + Address new_globals_start = + static_cast<Address>(global_buffer->backing_store()); + code_specialization.RelocateGlobals(old_globals_start, new_globals_start); + // The address of the backing buffer for the golbals is in native memory + // and, thus, not moving. We need it saved for + // serialization/deserialization purposes - so that the other end + // understands how to relocate the references. We still need to save the + // JSArrayBuffer on the instance, to keep it all alive. + WasmCompiledModule::SetGlobalsStartAddressFrom(factory, compiled_module_, + global_buffer); + instance->set_globals_buffer(*global_buffer); + } + + //-------------------------------------------------------------------------- + // Prepare for initialization of function tables. + //-------------------------------------------------------------------------- + int function_table_count = static_cast<int>(module_->function_tables.size()); + table_instances_.reserve(module_->function_tables.size()); + for (int index = 0; index < function_table_count; ++index) { + table_instances_.push_back( + {Handle<WasmTableObject>::null(), Handle<FixedArray>::null(), + Handle<FixedArray>::null(), Handle<FixedArray>::null()}); + } + + //-------------------------------------------------------------------------- + // Process the imports for the module. + //-------------------------------------------------------------------------- + int num_imported_functions = ProcessImports(code_table, instance); + if (num_imported_functions < 0) return {}; + + //-------------------------------------------------------------------------- + // Process the initialization for the module's globals. + //-------------------------------------------------------------------------- + InitGlobals(); + + //-------------------------------------------------------------------------- + // Set up the indirect function tables for the new instance. + //-------------------------------------------------------------------------- + if (function_table_count > 0) + InitializeTables(instance, &code_specialization); + + //-------------------------------------------------------------------------- + // Set up the memory for the new instance. + //-------------------------------------------------------------------------- + uint32_t min_mem_pages = module_->min_mem_pages; + (module_->is_wasm() ? counters()->wasm_wasm_min_mem_pages_count() + : counters()->wasm_asm_min_mem_pages_count()) + ->AddSample(min_mem_pages); + + if (!memory_.is_null()) { + Handle<JSArrayBuffer> memory = memory_.ToHandleChecked(); + // Set externally passed ArrayBuffer non neuterable. + memory->set_is_neuterable(false); + memory->set_is_wasm_buffer(true); + + DCHECK_IMPLIES(EnableGuardRegions(), + module_->is_asm_js() || memory->has_guard_region()); + } else if (min_mem_pages > 0) { + memory_ = AllocateMemory(min_mem_pages); + if (memory_.is_null()) return {}; // failed to allocate memory + } + + //-------------------------------------------------------------------------- + // Check that indirect function table segments are within bounds. + //-------------------------------------------------------------------------- + for (WasmTableInit& table_init : module_->table_inits) { + DCHECK(table_init.table_index < table_instances_.size()); + uint32_t base = EvalUint32InitExpr(table_init.offset); + uint32_t table_size = + table_instances_[table_init.table_index].function_table->length(); + if (!in_bounds(base, static_cast<uint32_t>(table_init.entries.size()), + table_size)) { + thrower_->LinkError("table initializer is out of bounds"); + return {}; + } + } + + //-------------------------------------------------------------------------- + // Check that memory segments are within bounds. + //-------------------------------------------------------------------------- + for (WasmDataSegment& seg : module_->data_segments) { + uint32_t base = EvalUint32InitExpr(seg.dest_addr); + uint32_t mem_size = 0; + if (!memory_.is_null()) { + CHECK(memory_.ToHandleChecked()->byte_length()->ToUint32(&mem_size)); + } + if (!in_bounds(base, seg.source.length(), mem_size)) { + thrower_->LinkError("data segment is out of bounds"); + return {}; + } + } + + //-------------------------------------------------------------------------- + // Initialize memory. + //-------------------------------------------------------------------------- + if (!memory_.is_null()) { + Handle<JSArrayBuffer> memory = memory_.ToHandleChecked(); + Address mem_start = static_cast<Address>(memory->backing_store()); + uint32_t mem_size; + CHECK(memory->byte_length()->ToUint32(&mem_size)); + LoadDataSegments(mem_start, mem_size); + + uint32_t old_mem_size = compiled_module_->mem_size(); + Address old_mem_start = compiled_module_->GetEmbeddedMemStartOrNull(); + // We might get instantiated again with the same memory. No patching + // needed in this case. + if (old_mem_start != mem_start || old_mem_size != mem_size) { + code_specialization.RelocateMemoryReferences(old_mem_start, old_mem_size, + mem_start, mem_size); + } + // Just like with globals, we need to keep both the JSArrayBuffer + // and save the start pointer. + instance->set_memory_buffer(*memory); + WasmCompiledModule::SetSpecializationMemInfoFrom(factory, compiled_module_, + memory); + } + + //-------------------------------------------------------------------------- + // Set up the runtime support for the new instance. + //-------------------------------------------------------------------------- + Handle<WeakCell> weak_link = factory->NewWeakCell(instance); + + for (int i = num_imported_functions + FLAG_skip_compiling_wasm_funcs, + num_functions = static_cast<int>(module_->functions.size()); + i < num_functions; ++i) { + Handle<Code> code = handle(Code::cast(code_table->get(i)), isolate_); + if (code->kind() == Code::WASM_FUNCTION) { + Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED); + deopt_data->set(0, *weak_link); + deopt_data->set(1, Smi::FromInt(i)); + code->set_deoptimization_data(*deopt_data); + continue; + } + DCHECK_EQ(Builtins::kWasmCompileLazy, code->builtin_index()); + int deopt_len = code->deoptimization_data()->length(); + if (deopt_len == 0) continue; + DCHECK_LE(2, deopt_len); + DCHECK_EQ(i, Smi::ToInt(code->deoptimization_data()->get(1))); + code->deoptimization_data()->set(0, *weak_link); + // Entries [2, deopt_len) encode information about table exports of this + // function. This is rebuilt in {LoadTableSegments}, so reset it here. + for (int i = 2; i < deopt_len; ++i) { + code->deoptimization_data()->set_undefined(isolate_, i); + } + } + + //-------------------------------------------------------------------------- + // Set up the exports object for the new instance. + //-------------------------------------------------------------------------- + ProcessExports(code_table, instance, compiled_module_); + if (thrower_->error()) return {}; + + //-------------------------------------------------------------------------- + // Add instance to Memory object + //-------------------------------------------------------------------------- + if (instance->has_memory_object()) { + Handle<WasmMemoryObject> memory(instance->memory_object(), isolate_); + WasmMemoryObject::AddInstance(isolate_, memory, instance); + } + + //-------------------------------------------------------------------------- + // Initialize the indirect function tables. + //-------------------------------------------------------------------------- + if (function_table_count > 0) LoadTableSegments(code_table, instance); + + // Patch all code with the relocations registered in code_specialization. + code_specialization.RelocateDirectCalls(instance); + code_specialization.ApplyToWholeInstance(*instance, SKIP_ICACHE_FLUSH); + + FlushICache(isolate_, code_table); + + //-------------------------------------------------------------------------- + // Unpack and notify signal handler of protected instructions. + //-------------------------------------------------------------------------- + if (trap_handler::UseTrapHandler()) { + UnpackAndRegisterProtectedInstructions(isolate_, code_table); + } + + //-------------------------------------------------------------------------- + // Set up and link the new instance. + //-------------------------------------------------------------------------- + { + Handle<Object> global_handle = + isolate_->global_handles()->Create(*instance); + Handle<WeakCell> link_to_clone = factory->NewWeakCell(compiled_module_); + Handle<WeakCell> link_to_owning_instance = factory->NewWeakCell(instance); + MaybeHandle<WeakCell> link_to_original; + MaybeHandle<WasmCompiledModule> original; + if (!owner.is_null()) { + // prepare the data needed for publishing in a chain, but don't link + // just yet, because + // we want all the publishing to happen free from GC interruptions, and + // so we do it in + // one GC-free scope afterwards. + original = handle(owner.ToHandleChecked()->compiled_module()); + link_to_original = factory->NewWeakCell(original.ToHandleChecked()); + } + // Publish the new instance to the instances chain. + { + DisallowHeapAllocation no_gc; + if (!link_to_original.is_null()) { + compiled_module_->set_weak_next_instance( + link_to_original.ToHandleChecked()); + original.ToHandleChecked()->set_weak_prev_instance(link_to_clone); + compiled_module_->set_weak_wasm_module( + original.ToHandleChecked()->weak_wasm_module()); + } + module_object_->set_compiled_module(*compiled_module_); + compiled_module_->set_weak_owning_instance(link_to_owning_instance); + GlobalHandles::MakeWeak( + global_handle.location(), global_handle.location(), + instance_finalizer_callback_, v8::WeakCallbackType::kFinalizer); + } + } + + //-------------------------------------------------------------------------- + // Debugging support. + //-------------------------------------------------------------------------- + // Set all breakpoints that were set on the shared module. + WasmSharedModuleData::SetBreakpointsOnNewInstance(compiled_module_->shared(), + instance); + + if (FLAG_wasm_interpret_all && module_->is_wasm()) { + Handle<WasmDebugInfo> debug_info = + WasmInstanceObject::GetOrCreateDebugInfo(instance); + std::vector<int> func_indexes; + for (int func_index = num_imported_functions, + num_wasm_functions = static_cast<int>(module_->functions.size()); + func_index < num_wasm_functions; ++func_index) { + func_indexes.push_back(func_index); + } + WasmDebugInfo::RedirectToInterpreter( + debug_info, Vector<int>(func_indexes.data(), + static_cast<int>(func_indexes.size()))); + } + + //-------------------------------------------------------------------------- + // Run the start function if one was specified. + //-------------------------------------------------------------------------- + if (module_->start_function_index >= 0) { + HandleScope scope(isolate_); + int start_index = module_->start_function_index; + Handle<Code> startup_code = EnsureExportedLazyDeoptData( + isolate_, instance, code_table, start_index); + FunctionSig* sig = module_->functions[start_index].sig; + Handle<Code> wrapper_code = js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper( + isolate_, module_, startup_code, start_index); + Handle<WasmExportedFunction> startup_fct = WasmExportedFunction::New( + isolate_, instance, MaybeHandle<String>(), start_index, + static_cast<int>(sig->parameter_count()), wrapper_code); + RecordStats(*startup_code, counters()); + // Call the JS function. + Handle<Object> undefined = factory->undefined_value(); + MaybeHandle<Object> retval = + Execution::Call(isolate_, startup_fct, undefined, 0, nullptr); + + if (retval.is_null()) { + DCHECK(isolate_->has_pending_exception()); + // It's unfortunate that the new instance is already linked in the + // chain. However, we need to set up everything before executing the + // start function, such that stack trace information can be generated + // correctly already in the start function. + return {}; + } + } + + DCHECK(!isolate_->has_pending_exception()); + TRACE("Finishing instance %d\n", compiled_module_->instance_id()); + TRACE_CHAIN(module_object_->compiled_module()); + return instance; +} + +// Look up an import value in the {ffi_} object. +MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index, + Handle<String> module_name, + + Handle<String> import_name) { + // We pre-validated in the js-api layer that the ffi object is present, and + // a JSObject, if the module has imports. + DCHECK(!ffi_.is_null()); + + // Look up the module first. + MaybeHandle<Object> result = + Object::GetPropertyOrElement(ffi_.ToHandleChecked(), module_name); + if (result.is_null()) { + return ReportTypeError("module not found", index, module_name); + } + + Handle<Object> module = result.ToHandleChecked(); + + // Look up the value in the module. + if (!module->IsJSReceiver()) { + return ReportTypeError("module is not an object or function", index, + module_name); + } + + result = Object::GetPropertyOrElement(module, import_name); + if (result.is_null()) { + ReportLinkError("import not found", index, module_name, import_name); + return MaybeHandle<JSFunction>(); + } + + return result; +} + +// Look up an import value in the {ffi_} object specifically for linking an +// asm.js module. This only performs non-observable lookups, which allows +// falling back to JavaScript proper (and hence re-executing all lookups) if +// module instantiation fails. +MaybeHandle<Object> InstanceBuilder::LookupImportAsm( + uint32_t index, Handle<String> import_name) { + // Check that a foreign function interface object was provided. + if (ffi_.is_null()) { + return ReportLinkError("missing imports object", index, import_name); + } + + // Perform lookup of the given {import_name} without causing any observable + // side-effect. We only accept accesses that resolve to data properties, + // which is indicated by the asm.js spec in section 7 ("Linking") as well. + Handle<Object> result; + LookupIterator it = LookupIterator::PropertyOrElement( + isolate_, ffi_.ToHandleChecked(), import_name); + switch (it.state()) { + case LookupIterator::ACCESS_CHECK: + case LookupIterator::INTEGER_INDEXED_EXOTIC: + case LookupIterator::INTERCEPTOR: + case LookupIterator::JSPROXY: + case LookupIterator::ACCESSOR: + case LookupIterator::TRANSITION: + return ReportLinkError("not a data property", index, import_name); + case LookupIterator::NOT_FOUND: + // Accepting missing properties as undefined does not cause any + // observable difference from JavaScript semantics, we are lenient. + result = isolate_->factory()->undefined_value(); + break; + case LookupIterator::DATA: + result = it.GetDataValue(); + break; + } + + return result; +} + +uint32_t InstanceBuilder::EvalUint32InitExpr(const WasmInitExpr& expr) { + switch (expr.kind) { + case WasmInitExpr::kI32Const: + return expr.val.i32_const; + case WasmInitExpr::kGlobalIndex: { + uint32_t offset = module_->globals[expr.val.global_index].offset; + return *reinterpret_cast<uint32_t*>(raw_buffer_ptr(globals_, offset)); + } + default: + UNREACHABLE(); + } +} + +// Load data segments into the memory. +void InstanceBuilder::LoadDataSegments(Address mem_addr, size_t mem_size) { + Handle<SeqOneByteString> module_bytes(compiled_module_->module_bytes(), + isolate_); + for (const WasmDataSegment& segment : module_->data_segments) { + uint32_t source_size = segment.source.length(); + // Segments of size == 0 are just nops. + if (source_size == 0) continue; + uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr); + DCHECK( + in_bounds(dest_offset, source_size, static_cast<uint32_t>(mem_size))); + byte* dest = mem_addr + dest_offset; + const byte* src = reinterpret_cast<const byte*>( + module_bytes->GetCharsAddress() + segment.source.offset()); + memcpy(dest, src, source_size); + } +} + +void InstanceBuilder::WriteGlobalValue(WasmGlobal& global, + Handle<Object> value) { + double num = value->Number(); + TRACE("init [globals+%u] = %lf, type = %s\n", global.offset, num, + WasmOpcodes::TypeName(global.type)); + switch (global.type) { + case kWasmI32: + *GetRawGlobalPtr<int32_t>(global) = static_cast<int32_t>(num); + break; + case kWasmI64: + // TODO(titzer): initialization of imported i64 globals. + UNREACHABLE(); + break; + case kWasmF32: + *GetRawGlobalPtr<float>(global) = static_cast<float>(num); + break; + case kWasmF64: + *GetRawGlobalPtr<double>(global) = static_cast<double>(num); + break; + default: + UNREACHABLE(); + } +} + +// Process the imports, including functions, tables, globals, and memory, in +// order, loading them from the {ffi_} object. Returns the number of imported +// functions. +int InstanceBuilder::ProcessImports(Handle<FixedArray> code_table, + Handle<WasmInstanceObject> instance) { + int num_imported_functions = 0; + int num_imported_tables = 0; + WasmInstanceMap imported_wasm_instances(isolate_->heap()); + for (int index = 0; index < static_cast<int>(module_->import_table.size()); + ++index) { + WasmImport& import = module_->import_table[index]; + + Handle<String> module_name; + MaybeHandle<String> maybe_module_name = + WasmCompiledModule::ExtractUtf8StringFromModuleBytes( + isolate_, compiled_module_, import.module_name); + if (!maybe_module_name.ToHandle(&module_name)) return -1; + + Handle<String> import_name; + MaybeHandle<String> maybe_import_name = + WasmCompiledModule::ExtractUtf8StringFromModuleBytes( + isolate_, compiled_module_, import.field_name); + if (!maybe_import_name.ToHandle(&import_name)) return -1; + + MaybeHandle<Object> result = + module_->is_asm_js() ? LookupImportAsm(index, import_name) + : LookupImport(index, module_name, import_name); + if (thrower_->error()) return -1; + Handle<Object> value = result.ToHandleChecked(); + + switch (import.kind) { + case kExternalFunction: { + // Function imports must be callable. + if (!value->IsCallable()) { + ReportLinkError("function import requires a callable", index, + module_name, import_name); + return -1; + } + + Handle<Code> import_wrapper = UnwrapOrCompileImportWrapper( + isolate_, index, module_->functions[import.index].sig, + Handle<JSReceiver>::cast(value), module_name, import_name, + module_->origin(), &imported_wasm_instances); + if (import_wrapper.is_null()) { + ReportLinkError("imported function does not match the expected type", + index, module_name, import_name); + return -1; + } + code_table->set(num_imported_functions, *import_wrapper); + RecordStats(*import_wrapper, counters()); + num_imported_functions++; + break; + } + case kExternalTable: { + if (!value->IsWasmTableObject()) { + ReportLinkError("table import requires a WebAssembly.Table", index, + module_name, import_name); + return -1; + } + WasmIndirectFunctionTable& table = + module_->function_tables[num_imported_tables]; + TableInstance& table_instance = table_instances_[num_imported_tables]; + table_instance.table_object = Handle<WasmTableObject>::cast(value); + table_instance.js_wrappers = Handle<FixedArray>( + table_instance.table_object->functions(), isolate_); + + int imported_cur_size = table_instance.js_wrappers->length(); + if (imported_cur_size < static_cast<int>(table.min_size)) { + thrower_->LinkError( + "table import %d is smaller than minimum %d, got %u", index, + table.min_size, imported_cur_size); + return -1; + } + + if (table.has_max) { + int64_t imported_max_size = + table_instance.table_object->maximum_length()->Number(); + if (imported_max_size < 0) { + thrower_->LinkError( + "table import %d has no maximum length, expected %d", index, + table.max_size); + return -1; + } + if (imported_max_size > table.max_size) { + thrower_->LinkError( + "table import %d has maximum larger than maximum %d, " + "got %" PRIx64, + index, table.max_size, imported_max_size); + return -1; + } + } + + // Allocate a new dispatch table and signature table. + int table_size = imported_cur_size; + table_instance.function_table = + isolate_->factory()->NewFixedArray(table_size); + table_instance.signature_table = + isolate_->factory()->NewFixedArray(table_size); + for (int i = 0; i < table_size; ++i) { + table_instance.signature_table->set(i, + Smi::FromInt(kInvalidSigIndex)); + } + // Initialize the dispatch table with the (foreign) JS functions + // that are already in the table. + for (int i = 0; i < table_size; ++i) { + Handle<Object> val(table_instance.js_wrappers->get(i), isolate_); + if (!val->IsJSFunction()) continue; + WasmFunction* function = + GetWasmFunctionForImportWrapper(isolate_, val); + if (function == nullptr) { + thrower_->LinkError("table import %d[%d] is not a wasm function", + index, i); + return -1; + } + int sig_index = table.map.FindOrInsert(function->sig); + table_instance.signature_table->set(i, Smi::FromInt(sig_index)); + table_instance.function_table->set(i, *UnwrapImportWrapper(val)); + } + + num_imported_tables++; + break; + } + case kExternalMemory: { + // Validation should have failed if more than one memory object was + // provided. + DCHECK(!instance->has_memory_object()); + if (!value->IsWasmMemoryObject()) { + ReportLinkError("memory import must be a WebAssembly.Memory object", + index, module_name, import_name); + return -1; + } + auto memory = Handle<WasmMemoryObject>::cast(value); + instance->set_memory_object(*memory); + Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_); + memory_ = buffer; + uint32_t imported_cur_pages = static_cast<uint32_t>( + buffer->byte_length()->Number() / WasmModule::kPageSize); + if (imported_cur_pages < module_->min_mem_pages) { + thrower_->LinkError( + "memory import %d is smaller than maximum %u, got %u", index, + module_->min_mem_pages, imported_cur_pages); + } + int32_t imported_max_pages = memory->maximum_pages(); + if (module_->has_max_mem) { + if (imported_max_pages < 0) { + thrower_->LinkError( + "memory import %d has no maximum limit, expected at most %u", + index, imported_max_pages); + return -1; + } + if (static_cast<uint32_t>(imported_max_pages) > + module_->max_mem_pages) { + thrower_->LinkError( + "memory import %d has larger maximum than maximum %u, got %d", + index, module_->max_mem_pages, imported_max_pages); + return -1; + } + } + break; + } + case kExternalGlobal: { + // Global imports are converted to numbers and written into the + // {globals_} array buffer. + if (module_->globals[import.index].type == kWasmI64) { + ReportLinkError("global import cannot have type i64", index, + module_name, import_name); + return -1; + } + if (module_->is_asm_js()) { + // Accepting {JSFunction} on top of just primitive values here is a + // workaround to support legacy asm.js code with broken binding. Note + // that using {NaN} (or Smi::kZero) here is what using the observable + // conversion via {ToPrimitive} would produce as well. + // TODO(mstarzinger): Still observable if Function.prototype.valueOf + // or friends are patched, we might need to check for that as well. + if (value->IsJSFunction()) value = isolate_->factory()->nan_value(); + if (value->IsPrimitive() && !value->IsSymbol()) { + if (module_->globals[import.index].type == kWasmI32) { + value = Object::ToInt32(isolate_, value).ToHandleChecked(); + } else { + value = Object::ToNumber(value).ToHandleChecked(); + } + } + } + if (!value->IsNumber()) { + ReportLinkError("global import must be a number", index, module_name, + import_name); + return -1; + } + WriteGlobalValue(module_->globals[import.index], value); + break; + } + default: + UNREACHABLE(); + break; + } + } + + if (!imported_wasm_instances.empty()) { + WasmInstanceMap::IteratableScope iteratable_scope(&imported_wasm_instances); + Handle<FixedArray> instances_array = isolate_->factory()->NewFixedArray( + imported_wasm_instances.size(), TENURED); + instance->set_directly_called_instances(*instances_array); + int index = 0; + for (auto it = iteratable_scope.begin(), end = iteratable_scope.end(); + it != end; ++it, ++index) { + instances_array->set(index, ***it); + } + } + + return num_imported_functions; +} + +template <typename T> +T* InstanceBuilder::GetRawGlobalPtr(WasmGlobal& global) { + return reinterpret_cast<T*>(raw_buffer_ptr(globals_, global.offset)); +} + +// Process initialization of globals. +void InstanceBuilder::InitGlobals() { + for (auto global : module_->globals) { + switch (global.init.kind) { + case WasmInitExpr::kI32Const: + *GetRawGlobalPtr<int32_t>(global) = global.init.val.i32_const; + break; + case WasmInitExpr::kI64Const: + *GetRawGlobalPtr<int64_t>(global) = global.init.val.i64_const; + break; + case WasmInitExpr::kF32Const: + *GetRawGlobalPtr<float>(global) = global.init.val.f32_const; + break; + case WasmInitExpr::kF64Const: + *GetRawGlobalPtr<double>(global) = global.init.val.f64_const; + break; + case WasmInitExpr::kGlobalIndex: { + // Initialize with another global. + uint32_t new_offset = global.offset; + uint32_t old_offset = + module_->globals[global.init.val.global_index].offset; + TRACE("init [globals+%u] = [globals+%d]\n", global.offset, old_offset); + size_t size = (global.type == kWasmI64 || global.type == kWasmF64) + ? sizeof(double) + : sizeof(int32_t); + memcpy(raw_buffer_ptr(globals_, new_offset), + raw_buffer_ptr(globals_, old_offset), size); + break; + } + case WasmInitExpr::kNone: + // Happens with imported globals. + break; + default: + UNREACHABLE(); + break; + } + } +} + +// Allocate memory for a module instance as a new JSArrayBuffer. +Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t min_mem_pages) { + if (min_mem_pages > FLAG_wasm_max_mem_pages) { + thrower_->RangeError("Out of memory: wasm memory too large"); + return Handle<JSArrayBuffer>::null(); + } + const bool enable_guard_regions = EnableGuardRegions(); + Handle<JSArrayBuffer> mem_buffer = NewArrayBuffer( + isolate_, min_mem_pages * WasmModule::kPageSize, enable_guard_regions); + + if (mem_buffer.is_null()) { + thrower_->RangeError("Out of memory: wasm memory"); + } + return mem_buffer; +} + +bool InstanceBuilder::NeedsWrappers() const { + if (module_->num_exported_functions > 0) return true; + for (auto& table_instance : table_instances_) { + if (!table_instance.js_wrappers.is_null()) return true; + } + for (auto& table : module_->function_tables) { + if (table.exported) return true; + } + return false; +} + +// Process the exports, creating wrappers for functions, tables, memories, +// and globals. +void InstanceBuilder::ProcessExports( + Handle<FixedArray> code_table, Handle<WasmInstanceObject> instance, + Handle<WasmCompiledModule> compiled_module) { + if (NeedsWrappers()) { + // Fill the table to cache the exported JSFunction wrappers. + js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(), + Handle<JSFunction>::null()); + } + + Handle<JSObject> exports_object; + if (module_->is_wasm()) { + // Create the "exports" object. + exports_object = isolate_->factory()->NewJSObjectWithNullProto(); + } else if (module_->is_asm_js()) { + Handle<JSFunction> object_function = Handle<JSFunction>( + isolate_->native_context()->object_function(), isolate_); + exports_object = isolate_->factory()->NewJSObject(object_function); + } else { + UNREACHABLE(); + } + Handle<String> exports_name = + isolate_->factory()->InternalizeUtf8String("exports"); + JSObject::AddProperty(instance, exports_name, exports_object, NONE); + + Handle<String> single_function_name = + isolate_->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName); + + PropertyDescriptor desc; + desc.set_writable(module_->is_asm_js()); + desc.set_enumerable(true); + desc.set_configurable(module_->is_asm_js()); + + // Store weak references to all exported functions. + Handle<FixedArray> weak_exported_functions; + if (compiled_module->has_weak_exported_functions()) { + weak_exported_functions = compiled_module->weak_exported_functions(); + } else { + int export_count = 0; + for (WasmExport& exp : module_->export_table) { + if (exp.kind == kExternalFunction) ++export_count; + } + weak_exported_functions = isolate_->factory()->NewFixedArray(export_count); + compiled_module->set_weak_exported_functions(weak_exported_functions); + } + + // Process each export in the export table. + int export_index = 0; // Index into {weak_exported_functions}. + for (WasmExport& exp : module_->export_table) { + Handle<String> name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes( + isolate_, compiled_module_, exp.name) + .ToHandleChecked(); + Handle<JSObject> export_to; + if (module_->is_asm_js() && exp.kind == kExternalFunction && + String::Equals(name, single_function_name)) { + export_to = instance; + } else { + export_to = exports_object; + } + + switch (exp.kind) { + case kExternalFunction: { + // Wrap and export the code as a JSFunction. + WasmFunction& function = module_->functions[exp.index]; + int func_index = + static_cast<int>(module_->functions.size() + export_index); + Handle<JSFunction> js_function = js_wrappers_[exp.index]; + if (js_function.is_null()) { + // Wrap the exported code as a JSFunction. + Handle<Code> export_code = + code_table->GetValueChecked<Code>(isolate_, func_index); + MaybeHandle<String> func_name; + if (module_->is_asm_js()) { + // For modules arising from asm.js, honor the names section. + func_name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes( + isolate_, compiled_module_, function.name) + .ToHandleChecked(); + } + js_function = WasmExportedFunction::New( + isolate_, instance, func_name, function.func_index, + static_cast<int>(function.sig->parameter_count()), export_code); + js_wrappers_[exp.index] = js_function; + } + desc.set_value(js_function); + Handle<WeakCell> weak_export = + isolate_->factory()->NewWeakCell(js_function); + DCHECK_GT(weak_exported_functions->length(), export_index); + weak_exported_functions->set(export_index, *weak_export); + export_index++; + break; + } + case kExternalTable: { + // Export a table as a WebAssembly.Table object. + TableInstance& table_instance = table_instances_[exp.index]; + WasmIndirectFunctionTable& table = module_->function_tables[exp.index]; + if (table_instance.table_object.is_null()) { + uint32_t maximum = + table.has_max ? table.max_size : FLAG_wasm_max_table_size; + table_instance.table_object = WasmTableObject::New( + isolate_, table.min_size, maximum, &table_instance.js_wrappers); + } + desc.set_value(table_instance.table_object); + break; + } + case kExternalMemory: { + // Export the memory as a WebAssembly.Memory object. + Handle<WasmMemoryObject> memory_object; + if (!instance->has_memory_object()) { + // If there was no imported WebAssembly.Memory object, create one. + memory_object = WasmMemoryObject::New( + isolate_, + (instance->has_memory_buffer()) + ? handle(instance->memory_buffer()) + : Handle<JSArrayBuffer>::null(), + (module_->max_mem_pages != 0) ? module_->max_mem_pages : -1); + instance->set_memory_object(*memory_object); + } else { + memory_object = + Handle<WasmMemoryObject>(instance->memory_object(), isolate_); + } + + desc.set_value(memory_object); + break; + } + case kExternalGlobal: { + // Export the value of the global variable as a number. + WasmGlobal& global = module_->globals[exp.index]; + double num = 0; + switch (global.type) { + case kWasmI32: + num = *GetRawGlobalPtr<int32_t>(global); + break; + case kWasmF32: + num = *GetRawGlobalPtr<float>(global); + break; + case kWasmF64: + num = *GetRawGlobalPtr<double>(global); + break; + case kWasmI64: + thrower_->LinkError( + "export of globals of type I64 is not allowed."); + return; + default: + UNREACHABLE(); + } + desc.set_value(isolate_->factory()->NewNumber(num)); + break; + } + default: + UNREACHABLE(); + break; + } + + v8::Maybe<bool> status = JSReceiver::DefineOwnProperty( + isolate_, export_to, name, &desc, Object::THROW_ON_ERROR); + if (!status.IsJust()) { + thrower_->LinkError("export of %.*s failed.", name->length(), + name->ToCString().get()); + return; + } + } + DCHECK_EQ(export_index, weak_exported_functions->length()); + + if (module_->is_wasm()) { + v8::Maybe<bool> success = JSReceiver::SetIntegrityLevel( + exports_object, FROZEN, Object::DONT_THROW); + DCHECK(success.FromMaybe(false)); + USE(success); + } +} + +void InstanceBuilder::InitializeTables( + Handle<WasmInstanceObject> instance, + CodeSpecialization* code_specialization) { + int function_table_count = static_cast<int>(module_->function_tables.size()); + Handle<FixedArray> new_function_tables = + isolate_->factory()->NewFixedArray(function_table_count); + Handle<FixedArray> new_signature_tables = + isolate_->factory()->NewFixedArray(function_table_count); + for (int index = 0; index < function_table_count; ++index) { + WasmIndirectFunctionTable& table = module_->function_tables[index]; + TableInstance& table_instance = table_instances_[index]; + int table_size = static_cast<int>(table.min_size); + + if (table_instance.function_table.is_null()) { + // Create a new dispatch table if necessary. + table_instance.function_table = + isolate_->factory()->NewFixedArray(table_size); + table_instance.signature_table = + isolate_->factory()->NewFixedArray(table_size); + for (int i = 0; i < table_size; ++i) { + // Fill the table with invalid signature indexes so that + // uninitialized entries will always fail the signature check. + table_instance.signature_table->set(i, Smi::FromInt(kInvalidSigIndex)); + } + } else { + // Table is imported, patch table bounds check + DCHECK(table_size <= table_instance.function_table->length()); + if (table_size < table_instance.function_table->length()) { + code_specialization->PatchTableSize( + table_size, table_instance.function_table->length()); + } + } + + new_function_tables->set(static_cast<int>(index), + *table_instance.function_table); + new_signature_tables->set(static_cast<int>(index), + *table_instance.signature_table); + } + + FixedArray* old_function_tables = compiled_module_->ptr_to_function_tables(); + DCHECK_EQ(old_function_tables->length(), new_function_tables->length()); + for (int i = 0, e = new_function_tables->length(); i < e; ++i) { + code_specialization->RelocateObject( + handle(old_function_tables->get(i), isolate_), + handle(new_function_tables->get(i), isolate_)); + } + FixedArray* old_signature_tables = + compiled_module_->ptr_to_signature_tables(); + DCHECK_EQ(old_signature_tables->length(), new_signature_tables->length()); + for (int i = 0, e = new_signature_tables->length(); i < e; ++i) { + code_specialization->RelocateObject( + handle(old_signature_tables->get(i), isolate_), + handle(new_signature_tables->get(i), isolate_)); + } + + compiled_module_->set_function_tables(new_function_tables); + compiled_module_->set_signature_tables(new_signature_tables); +} + +void InstanceBuilder::LoadTableSegments(Handle<FixedArray> code_table, + Handle<WasmInstanceObject> instance) { + int function_table_count = static_cast<int>(module_->function_tables.size()); + for (int index = 0; index < function_table_count; ++index) { + WasmIndirectFunctionTable& table = module_->function_tables[index]; + TableInstance& table_instance = table_instances_[index]; + + Handle<FixedArray> all_dispatch_tables; + if (!table_instance.table_object.is_null()) { + // Get the existing dispatch table(s) with the WebAssembly.Table object. + all_dispatch_tables = + handle(table_instance.table_object->dispatch_tables()); + } + + // Count the number of table exports for each function (needed for lazy + // compilation). + std::unordered_map<uint32_t, uint32_t> num_table_exports; + if (compile_lazy(module_)) { + for (auto table_init : module_->table_inits) { + for (uint32_t func_index : table_init.entries) { + Code* code = + Code::cast(code_table->get(static_cast<int>(func_index))); + // Only increase the counter for lazy compile builtins (it's not + // needed otherwise). + if (code->is_wasm_code()) continue; + DCHECK_EQ(Builtins::kWasmCompileLazy, code->builtin_index()); + ++num_table_exports[func_index]; + } + } + } + + // TODO(titzer): this does redundant work if there are multiple tables, + // since initializations are not sorted by table index. + for (auto table_init : module_->table_inits) { + uint32_t base = EvalUint32InitExpr(table_init.offset); + uint32_t num_entries = static_cast<uint32_t>(table_init.entries.size()); + DCHECK(in_bounds(base, num_entries, + table_instance.function_table->length())); + for (uint32_t i = 0; i < num_entries; ++i) { + uint32_t func_index = table_init.entries[i]; + WasmFunction* function = &module_->functions[func_index]; + int table_index = static_cast<int>(i + base); + int32_t sig_index = table.map.Find(function->sig); + DCHECK_GE(sig_index, 0); + table_instance.signature_table->set(table_index, + Smi::FromInt(sig_index)); + Handle<Code> wasm_code = EnsureTableExportLazyDeoptData( + isolate_, instance, code_table, func_index, + table_instance.function_table, table_index, num_table_exports); + table_instance.function_table->set(table_index, *wasm_code); + + if (!all_dispatch_tables.is_null()) { + if (js_wrappers_[func_index].is_null()) { + // No JSFunction entry yet exists for this function. Create one. + // TODO(titzer): We compile JS->wasm wrappers for functions are + // not exported but are in an exported table. This should be done + // at module compile time and cached instead. + + Handle<Code> wrapper_code = + js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper( + isolate_, module_, wasm_code, func_index); + MaybeHandle<String> func_name; + if (module_->is_asm_js()) { + // For modules arising from asm.js, honor the names section. + func_name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes( + isolate_, compiled_module_, function->name) + .ToHandleChecked(); + } + Handle<WasmExportedFunction> js_function = + WasmExportedFunction::New( + isolate_, instance, func_name, func_index, + static_cast<int>(function->sig->parameter_count()), + wrapper_code); + js_wrappers_[func_index] = js_function; + } + table_instance.js_wrappers->set(table_index, + *js_wrappers_[func_index]); + + UpdateDispatchTables(isolate_, all_dispatch_tables, table_index, + function, wasm_code); + } + } + } + +#ifdef DEBUG + // Check that the count of table exports was accurate. The entries are + // decremented on each export, so all should be zero now. + for (auto e : num_table_exports) { + DCHECK_EQ(0, e.second); + } +#endif + + // TODO(titzer): we add the new dispatch table at the end to avoid + // redundant work and also because the new instance is not yet fully + // initialized. + if (!table_instance.table_object.is_null()) { + // Add the new dispatch table to the WebAssembly.Table object. + all_dispatch_tables = WasmTableObject::AddDispatchTable( + isolate_, table_instance.table_object, instance, index, + table_instance.function_table, table_instance.signature_table); + } + } +} + +AsyncCompileJob::AsyncCompileJob(Isolate* isolate, + std::unique_ptr<byte[]> bytes_copy, + size_t length, Handle<Context> context, + Handle<JSPromise> promise) + : isolate_(isolate), + async_counters_(isolate->async_counters()), + bytes_copy_(std::move(bytes_copy)), + wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length) { + // The handles for the context and promise must be deferred. + DeferredHandleScope deferred(isolate); + context_ = Handle<Context>(*context); + module_promise_ = Handle<JSPromise>(*promise); + deferred_handles_.push_back(deferred.Detach()); +} + +void AsyncCompileJob::Start() { + DoAsync<DecodeModule>(); // -- +} + +AsyncCompileJob::~AsyncCompileJob() { + background_task_manager_.CancelAndWait(); + for (auto d : deferred_handles_) delete d; +} + +void AsyncCompileJob::ReopenHandlesInDeferredScope() { + DeferredHandleScope deferred(isolate_); + function_tables_ = handle(*function_tables_, isolate_); + signature_tables_ = handle(*signature_tables_, isolate_); + code_table_ = handle(*code_table_, isolate_); + temp_instance_->ReopenHandles(isolate_); + compiler_->ReopenHandlesInDeferredScope(); + deferred_handles_.push_back(deferred.Detach()); +} + +void AsyncCompileJob::AsyncCompileFailed(ErrorThrower& thrower) { + RejectPromise(isolate_, context_, thrower, module_promise_); + isolate_->wasm_compilation_manager()->RemoveJob(this); +} + +void AsyncCompileJob::AsyncCompileSucceeded(Handle<Object> result) { + ResolvePromise(isolate_, context_, module_promise_, result); + isolate_->wasm_compilation_manager()->RemoveJob(this); +} + +// A closure to run a compilation step (either as foreground or background +// task) and schedule the next step(s), if any. +class AsyncCompileJob::CompileStep { + public: + explicit CompileStep(size_t num_background_tasks = 0) + : num_background_tasks_(num_background_tasks) {} + + virtual ~CompileStep() {} + + void Run(bool on_foreground) { + if (on_foreground) { + DCHECK_EQ(1, job_->num_pending_foreground_tasks_--); + SaveContext saved_context(job_->isolate_); + job_->isolate_->set_context(*job_->context_); + RunInForeground(); + } else { + RunInBackground(); + } + } + + virtual void RunInForeground() { UNREACHABLE(); } + virtual void RunInBackground() { UNREACHABLE(); } + + size_t NumberOfBackgroundTasks() { return num_background_tasks_; } + + AsyncCompileJob* job_ = nullptr; + const size_t num_background_tasks_; +}; + +class AsyncCompileJob::CompileTask : public CancelableTask { + public: + CompileTask(AsyncCompileJob* job, bool on_foreground) + // We only manage the background tasks with the {CancelableTaskManager} of + // the {AsyncCompileJob}. Foreground tasks are managed by the system's + // {CancelableTaskManager}. Background tasks cannot spawn tasks managed by + // their own task manager. + : CancelableTask(on_foreground ? job->isolate_->cancelable_task_manager() + : &job->background_task_manager_), + job_(job), + on_foreground_(on_foreground) {} + + void RunInternal() override { job_->step_->Run(on_foreground_); } + + private: + AsyncCompileJob* job_; + bool on_foreground_; +}; + +void AsyncCompileJob::StartForegroundTask() { + DCHECK_EQ(0, num_pending_foreground_tasks_++); + + V8::GetCurrentPlatform()->CallOnForegroundThread( + reinterpret_cast<v8::Isolate*>(isolate_), new CompileTask(this, true)); +} + +template <typename State, typename... Args> +void AsyncCompileJob::DoSync(Args&&... args) { + step_.reset(new State(std::forward<Args>(args)...)); + step_->job_ = this; + StartForegroundTask(); +} + +void AsyncCompileJob::StartBackgroundTask() { + V8::GetCurrentPlatform()->CallOnBackgroundThread( + new CompileTask(this, false), v8::Platform::kShortRunningTask); +} + +template <typename State, typename... Args> +void AsyncCompileJob::DoAsync(Args&&... args) { + step_.reset(new State(std::forward<Args>(args)...)); + step_->job_ = this; + size_t end = step_->NumberOfBackgroundTasks(); + for (size_t i = 0; i < end; ++i) { + StartBackgroundTask(); + } +} + +//========================================================================== +// Step 1: (async) Decode the module. +//========================================================================== +class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep { + public: + DecodeModule() : CompileStep(1) {} + + void RunInBackground() override { + ModuleResult result; + { + DisallowHandleAllocation no_handle; + DisallowHeapAllocation no_allocation; + // Decode the module bytes. + TRACE_COMPILE("(1) Decoding module...\n"); + result = AsyncDecodeWasmModule(job_->isolate_, job_->wire_bytes_.start(), + job_->wire_bytes_.end(), false, + kWasmOrigin, job_->async_counters()); + } + if (result.failed()) { + // Decoding failure; reject the promise and clean up. + job_->DoSync<DecodeFail>(std::move(result)); + } else { + // Decode passed. + job_->DoSync<PrepareAndStartCompile>(std::move(result.val)); + } + } +}; + +//========================================================================== +// Step 1b: (sync) Fail decoding the module. +//========================================================================== +class AsyncCompileJob::DecodeFail : public CompileStep { + public: + explicit DecodeFail(ModuleResult result) : result_(std::move(result)) {} + + private: + ModuleResult result_; + void RunInForeground() override { + TRACE_COMPILE("(1b) Decoding failed.\n"); + HandleScope scope(job_->isolate_); + ErrorThrower thrower(job_->isolate_, "AsyncCompile"); + thrower.CompileFailed("Wasm decoding failed", result_); + // {job_} is deleted in AsyncCompileFailed, therefore the {return}. + return job_->AsyncCompileFailed(thrower); + } +}; + +//========================================================================== +// Step 2 (sync): Create heap-allocated data and start compile. +//========================================================================== +class AsyncCompileJob::PrepareAndStartCompile : public CompileStep { + public: + explicit PrepareAndStartCompile(std::unique_ptr<WasmModule> module) + : module_(std::move(module)) {} + + private: + std::unique_ptr<WasmModule> module_; + void RunInForeground() override { + TRACE_COMPILE("(2) Prepare and start compile...\n"); + HandleScope scope(job_->isolate_); + + Factory* factory = job_->isolate_->factory(); + job_->temp_instance_.reset(new WasmInstance(module_.get())); + job_->temp_instance_->context = job_->context_; + job_->temp_instance_->mem_size = + WasmModule::kPageSize * module_->min_mem_pages; + job_->temp_instance_->mem_start = nullptr; + job_->temp_instance_->globals_start = nullptr; + + // Initialize the indirect tables with placeholders. + int function_table_count = + static_cast<int>(module_->function_tables.size()); + job_->function_tables_ = + factory->NewFixedArray(function_table_count, TENURED); + job_->signature_tables_ = + factory->NewFixedArray(function_table_count, TENURED); + for (int i = 0; i < function_table_count; ++i) { + job_->temp_instance_->function_tables[i] = + factory->NewFixedArray(1, TENURED); + job_->temp_instance_->signature_tables[i] = + factory->NewFixedArray(1, TENURED); + job_->function_tables_->set(i, *job_->temp_instance_->function_tables[i]); + job_->signature_tables_->set(i, + *job_->temp_instance_->signature_tables[i]); + } + + // The {code_table} array contains import wrappers and functions (which + // are both included in {functions.size()}, and export wrappers. + // The results of compilation will be written into it. + int code_table_size = static_cast<int>(module_->functions.size() + + module_->num_exported_functions); + job_->code_table_ = factory->NewFixedArray(code_table_size, TENURED); + + // Initialize {code_table_} with the illegal builtin. All call sites + // will be patched at instantiation. + Handle<Code> illegal_builtin = job_->isolate_->builtins()->Illegal(); + // TODO(wasm): Fix this for lazy compilation. + for (uint32_t i = 0; i < module_->functions.size(); ++i) { + job_->code_table_->set(static_cast<int>(i), *illegal_builtin); + job_->temp_instance_->function_code[i] = illegal_builtin; + } + + job_->counters()->wasm_functions_per_wasm_module()->AddSample( + static_cast<int>(module_->functions.size())); + + // Transfer ownership of the {WasmModule} to the {ModuleCompiler}, but + // keep a pointer. + WasmModule* module = module_.get(); + job_->compiler_.reset( + new ModuleCompiler(job_->isolate_, std::move(module_))); + job_->compiler_->EnableThrottling(); + + // Reopen all handles which should survive in the DeferredHandleScope. + job_->ReopenHandlesInDeferredScope(); + + DCHECK_LE(module->num_imported_functions, module->functions.size()); + size_t num_functions = + module->functions.size() - module->num_imported_functions; + if (num_functions == 0) { + // Degenerate case of an empty module. + job_->DoSync<FinishCompile>(); + return; + } + + // Start asynchronous compilation tasks. + size_t num_background_tasks = + Max(static_cast<size_t>(1), + Min(num_functions, + Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), + V8::GetCurrentPlatform() + ->NumberOfAvailableBackgroundThreads()))); + job_->module_bytes_env_.reset(new ModuleBytesEnv( + module, job_->temp_instance_.get(), job_->wire_bytes_)); + + job_->outstanding_units_ = job_->compiler_->InitializeCompilationUnits( + module->functions, *job_->module_bytes_env_); + + job_->DoAsync<ExecuteAndFinishCompilationUnits>(num_background_tasks); + } +}; + +//========================================================================== +// Step 3 (async x K tasks): Execute compilation units. +//========================================================================== +class AsyncCompileJob::ExecuteAndFinishCompilationUnits : public CompileStep { + public: + explicit ExecuteAndFinishCompilationUnits(size_t num_compile_tasks) + : CompileStep(num_compile_tasks) {} + + void RunInBackground() override { + std::function<void()> StartFinishCompilationUnit = [this]() { + if (!failed_) job_->StartForegroundTask(); + }; + + TRACE_COMPILE("(3) Compiling...\n"); + while (job_->compiler_->CanAcceptWork()) { + if (failed_) break; + DisallowHandleAllocation no_handle; + DisallowHeapAllocation no_allocation; + if (!job_->compiler_->FetchAndExecuteCompilationUnit( + StartFinishCompilationUnit)) { + finished_ = true; + break; + } + } + stopped_tasks_.Increment(1); + } + + void RestartCompilationTasks() { + size_t num_restarts = stopped_tasks_.Value(); + stopped_tasks_.Decrement(num_restarts); + + for (size_t i = 0; i < num_restarts; ++i) { + job_->StartBackgroundTask(); + } + } + + void RunInForeground() override { + TRACE_COMPILE("(4a) Finishing compilation units...\n"); + if (failed_) { + // The job failed already, no need to do more work. + job_->compiler_->SetFinisherIsRunning(false); + return; + } + HandleScope scope(job_->isolate_); + ErrorThrower thrower(job_->isolate_, "AsyncCompile"); + + // We execute for 1 ms and then reschedule the task, same as the GC. + double deadline = MonotonicallyIncreasingTimeInMs() + 1.0; + + while (true) { + if (!finished_ && job_->compiler_->ShouldIncreaseWorkload()) { + RestartCompilationTasks(); + } + + int func_index = -1; + + MaybeHandle<Code> result = + job_->compiler_->FinishCompilationUnit(&thrower, &func_index); + + if (thrower.error()) { + // An error was detected, we stop compiling and wait for the + // background tasks to finish. + failed_ = true; + break; + } else if (result.is_null()) { + // The working queue was empty, we break the loop. If new work units + // are enqueued, the background task will start this + // FinishCompilationUnits task again. + break; + } else { + DCHECK(func_index >= 0); + job_->code_table_->set(func_index, *result.ToHandleChecked()); + --job_->outstanding_units_; + } + + if (deadline < MonotonicallyIncreasingTimeInMs()) { + // We reached the deadline. We reschedule this task and return + // immediately. Since we rescheduled this task already, we do not set + // the FinisherIsRunning flat to false. + job_->StartForegroundTask(); + return; + } + } + // This task finishes without being rescheduled. Therefore we set the + // FinisherIsRunning flag to false. + job_->compiler_->SetFinisherIsRunning(false); + if (thrower.error()) { + // Make sure all compilation tasks stopped running. + job_->background_task_manager_.CancelAndWait(); + return job_->AsyncCompileFailed(thrower); + } + if (job_->outstanding_units_ == 0) { + // Make sure all compilation tasks stopped running. + job_->background_task_manager_.CancelAndWait(); + job_->DoSync<FinishCompile>(); + } + } + + private: + std::atomic<bool> failed_{false}; + std::atomic<bool> finished_{false}; + base::AtomicNumber<size_t> stopped_tasks_{0}; +}; + +//========================================================================== +// Step 5 (sync): Finish heap-allocated data structures. +//========================================================================== +class AsyncCompileJob::FinishCompile : public CompileStep { + void RunInForeground() override { + TRACE_COMPILE("(5b) Finish compile...\n"); + HandleScope scope(job_->isolate_); + // At this point, compilation has completed. Update the code table. + for (size_t i = FLAG_skip_compiling_wasm_funcs; + i < job_->temp_instance_->function_code.size(); ++i) { + Code* code = Code::cast(job_->code_table_->get(static_cast<int>(i))); + RecordStats(code, job_->counters()); + } + + // Create heap objects for script and module bytes to be stored in the + // shared module data. Asm.js is not compiled asynchronously. + Handle<Script> script = CreateWasmScript(job_->isolate_, job_->wire_bytes_); + Handle<ByteArray> asm_js_offset_table; + // TODO(wasm): Improve efficiency of storing module wire bytes. + // 1. Only store relevant sections, not function bodies + // 2. Don't make a second copy of the bytes here; reuse the copy made + // for asynchronous compilation and store it as an external one + // byte string for serialization/deserialization. + Handle<String> module_bytes = + job_->isolate_->factory() + ->NewStringFromOneByte( + {job_->wire_bytes_.start(), job_->wire_bytes_.length()}, + TENURED) + .ToHandleChecked(); + DCHECK(module_bytes->IsSeqOneByteString()); + + // The {module_wrapper} will take ownership of the {WasmModule} object, + // and it will be destroyed when the GC reclaims the wrapper object. + Handle<WasmModuleWrapper> module_wrapper = WasmModuleWrapper::New( + job_->isolate_, job_->compiler_->ReleaseModule().release()); + + // Create the shared module data. + // TODO(clemensh): For the same module (same bytes / same hash), we should + // only have one WasmSharedModuleData. Otherwise, we might only set + // breakpoints on a (potentially empty) subset of the instances. + + Handle<WasmSharedModuleData> shared = + WasmSharedModuleData::New(job_->isolate_, module_wrapper, + Handle<SeqOneByteString>::cast(module_bytes), + script, asm_js_offset_table); + + // Create the compiled module object and populate with compiled functions + // and information needed at instantiation time. This object needs to be + // serializable. Instantiation may occur off a deserialized version of + // this object. + job_->compiled_module_ = WasmCompiledModule::New( + job_->isolate_, shared, job_->code_table_, job_->function_tables_, + job_->signature_tables_); + + // Finish the wasm script now and make it public to the debugger. + script->set_wasm_compiled_module(*job_->compiled_module_); + job_->isolate_->debug()->OnAfterCompile(script); + + DeferredHandleScope deferred(job_->isolate_); + job_->compiled_module_ = handle(*job_->compiled_module_, job_->isolate_); + job_->deferred_handles_.push_back(deferred.Detach()); + // TODO(wasm): compiling wrappers should be made async as well. + job_->DoSync<CompileWrappers>(); + } +}; + +//========================================================================== +// Step 6 (sync): Compile JS->wasm wrappers. +//========================================================================== +class AsyncCompileJob::CompileWrappers : public CompileStep { + void RunInForeground() override { + TRACE_COMPILE("(6) Compile wrappers...\n"); + // Compile JS->wasm wrappers for exported functions. + HandleScope scope(job_->isolate_); + JSToWasmWrapperCache js_to_wasm_cache; + int func_index = 0; + WasmModule* module = job_->compiled_module_->module(); + for (auto exp : module->export_table) { + if (exp.kind != kExternalFunction) continue; + Handle<Code> wasm_code(Code::cast(job_->code_table_->get(exp.index)), + job_->isolate_); + Handle<Code> wrapper_code = + js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(job_->isolate_, module, + wasm_code, exp.index); + int export_index = + static_cast<int>(module->functions.size() + func_index); + job_->code_table_->set(export_index, *wrapper_code); + RecordStats(*wrapper_code, job_->counters()); + func_index++; + } + + job_->DoSync<FinishModule>(); + } +}; + +//========================================================================== +// Step 7 (sync): Finish the module and resolve the promise. +//========================================================================== +class AsyncCompileJob::FinishModule : public CompileStep { + void RunInForeground() override { + TRACE_COMPILE("(7) Finish module...\n"); + HandleScope scope(job_->isolate_); + Handle<WasmModuleObject> result = + WasmModuleObject::New(job_->isolate_, job_->compiled_module_); + // {job_} is deleted in AsyncCompileSucceeded, therefore the {return}. + return job_->AsyncCompileSucceeded(result); + } +}; +} // namespace wasm +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/wasm/module-compiler.h b/deps/v8/src/wasm/module-compiler.h new file mode 100644 index 0000000000..2e59b357d2 --- /dev/null +++ b/deps/v8/src/wasm/module-compiler.h @@ -0,0 +1,390 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_WASM_MODULE_COMPILER_H_ +#define V8_WASM_MODULE_COMPILER_H_ + +#include <functional> + +#include "src/base/atomic-utils.h" +#include "src/base/utils/random-number-generator.h" +#include "src/cancelable-task.h" +#include "src/compiler/wasm-compiler.h" +#include "src/isolate.h" + +#include "src/wasm/wasm-code-specialization.h" +#include "src/wasm/wasm-objects.h" + +namespace v8 { +namespace internal { +namespace wasm { + +// A class compiling an entire module. +class ModuleCompiler { + public: + // The ModuleCompiler takes ownership of the {WasmModule}. + // In {CompileToModuleObject}, it will transfer ownership to the generated + // {WasmModuleWrapper}. If this method is not called, ownership may be + // reclaimed by explicitely releasing the {module_} field. + ModuleCompiler(Isolate* isolate, std::unique_ptr<WasmModule> module); + + // The actual runnable task that performs compilations in the background. + class CompilationTask : public CancelableTask { + public: + ModuleCompiler* compiler_; + explicit CompilationTask(ModuleCompiler* helper); + + void RunInternal() override; + }; + + // The CompilationUnitBuilder builds compilation units and stores them in an + // internal buffer. The buffer is moved into the working queue of the + // ModuleCompiler when {Commit} is called. + class CompilationUnitBuilder { + public: + explicit CompilationUnitBuilder(ModuleCompiler* compiler) + : compiler_(compiler) {} + + ~CompilationUnitBuilder() { DCHECK(units_.empty()); } + + void AddUnit(ModuleEnv* module_env, const WasmFunction* function, + uint32_t buffer_offset, Vector<const uint8_t> bytes, + WasmName name) { + units_.emplace_back(new compiler::WasmCompilationUnit( + compiler_->isolate_, module_env, + wasm::FunctionBody{function->sig, buffer_offset, bytes.begin(), + bytes.end()}, + name, function->func_index, compiler_->centry_stub_, + compiler_->async_counters())); + } + + void Commit() { + { + base::LockGuard<base::Mutex> guard( + &compiler_->compilation_units_mutex_); + compiler_->compilation_units_.insert( + compiler_->compilation_units_.end(), + std::make_move_iterator(units_.begin()), + std::make_move_iterator(units_.end())); + } + units_.clear(); + } + + private: + ModuleCompiler* compiler_; + std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> units_; + }; + + class CodeGenerationSchedule { + public: + explicit CodeGenerationSchedule( + base::RandomNumberGenerator* random_number_generator, + size_t max_memory = 0); + + void Schedule(std::unique_ptr<compiler::WasmCompilationUnit>&& item); + + bool IsEmpty() const { return schedule_.empty(); } + + std::unique_ptr<compiler::WasmCompilationUnit> GetNext(); + + bool CanAcceptWork() const; + + bool ShouldIncreaseWorkload() const; + + void EnableThrottling() { throttle_ = true; } + + private: + size_t GetRandomIndexInSchedule(); + + base::RandomNumberGenerator* random_number_generator_ = nullptr; + std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> schedule_; + const size_t max_memory_; + bool throttle_ = false; + base::AtomicNumber<size_t> allocated_memory_{0}; + }; + + const std::shared_ptr<Counters>& async_counters() const { + return async_counters_; + } + Counters* counters() const { return async_counters().get(); } + + // Run by each compilation task and by the main thread (i.e. in both + // foreground and background threads). The no_finisher_callback is called + // within the result_mutex_ lock when no finishing task is running, i.e. when + // the finisher_is_running_ flag is not set. + bool FetchAndExecuteCompilationUnit( + std::function<void()> no_finisher_callback = nullptr); + + void OnBackgroundTaskStopped(); + + void EnableThrottling() { executed_units_.EnableThrottling(); } + + bool CanAcceptWork() const { return executed_units_.CanAcceptWork(); } + + bool ShouldIncreaseWorkload() const { + return executed_units_.ShouldIncreaseWorkload(); + } + + size_t InitializeCompilationUnits(const std::vector<WasmFunction>& functions, + ModuleBytesEnv& module_env); + + void ReopenHandlesInDeferredScope(); + + void RestartCompilationTasks(); + + size_t FinishCompilationUnits(std::vector<Handle<Code>>& results, + ErrorThrower* thrower); + + void SetFinisherIsRunning(bool value); + + MaybeHandle<Code> FinishCompilationUnit(ErrorThrower* thrower, + int* func_index); + + void CompileInParallel(ModuleBytesEnv* module_env, + std::vector<Handle<Code>>& results, + ErrorThrower* thrower); + + void CompileSequentially(ModuleBytesEnv* module_env, + std::vector<Handle<Code>>& results, + ErrorThrower* thrower); + + void ValidateSequentially(ModuleBytesEnv* module_env, ErrorThrower* thrower); + + MaybeHandle<WasmModuleObject> CompileToModuleObject( + ErrorThrower* thrower, const ModuleWireBytes& wire_bytes, + Handle<Script> asm_js_script, + Vector<const byte> asm_js_offset_table_bytes); + + std::unique_ptr<WasmModule> ReleaseModule() { return std::move(module_); } + + private: + MaybeHandle<WasmModuleObject> CompileToModuleObjectInternal( + ErrorThrower* thrower, const ModuleWireBytes& wire_bytes, + Handle<Script> asm_js_script, + Vector<const byte> asm_js_offset_table_bytes, Factory* factory, + WasmInstance* temp_instance, Handle<FixedArray>* function_tables, + Handle<FixedArray>* signature_tables); + + Isolate* isolate_; + std::unique_ptr<WasmModule> module_; + const std::shared_ptr<Counters> async_counters_; + std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> + compilation_units_; + base::Mutex compilation_units_mutex_; + CodeGenerationSchedule executed_units_; + base::Mutex result_mutex_; + const size_t num_background_tasks_; + // This flag should only be set while holding result_mutex_. + bool finisher_is_running_ = false; + CancelableTaskManager background_task_manager_; + size_t stopped_compilation_tasks_ = 0; + base::Mutex tasks_mutex_; + Handle<Code> centry_stub_; +}; + +class JSToWasmWrapperCache { + public: + Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate, + const wasm::WasmModule* module, + Handle<Code> wasm_code, + uint32_t index); + + private: + // sig_map_ maps signatures to an index in code_cache_. + wasm::SignatureMap sig_map_; + std::vector<Handle<Code>> code_cache_; +}; + +// A helper class to simplify instantiating a module from a compiled module. +// It closes over the {Isolate}, the {ErrorThrower}, the {WasmCompiledModule}, +// etc. +class InstanceBuilder { + public: + InstanceBuilder(Isolate* isolate, ErrorThrower* thrower, + Handle<WasmModuleObject> module_object, + MaybeHandle<JSReceiver> ffi, + MaybeHandle<JSArrayBuffer> memory, + WeakCallbackInfo<void>::Callback instance_finalizer_callback); + + // Build an instance, in all of its glory. + MaybeHandle<WasmInstanceObject> Build(); + + private: + // Represents the initialized state of a table. + struct TableInstance { + Handle<WasmTableObject> table_object; // WebAssembly.Table instance + Handle<FixedArray> js_wrappers; // JSFunctions exported + Handle<FixedArray> function_table; // internal code array + Handle<FixedArray> signature_table; // internal sig array + }; + + Isolate* isolate_; + WasmModule* const module_; + const std::shared_ptr<Counters> async_counters_; + ErrorThrower* thrower_; + Handle<WasmModuleObject> module_object_; + MaybeHandle<JSReceiver> ffi_; + MaybeHandle<JSArrayBuffer> memory_; + Handle<JSArrayBuffer> globals_; + Handle<WasmCompiledModule> compiled_module_; + std::vector<TableInstance> table_instances_; + std::vector<Handle<JSFunction>> js_wrappers_; + JSToWasmWrapperCache js_to_wasm_cache_; + WeakCallbackInfo<void>::Callback instance_finalizer_callback_; + + const std::shared_ptr<Counters>& async_counters() const { + return async_counters_; + } + Counters* counters() const { return async_counters().get(); } + +// Helper routines to print out errors with imports. +#define ERROR_THROWER_WITH_MESSAGE(TYPE) \ + void Report##TYPE(const char* error, uint32_t index, \ + Handle<String> module_name, Handle<String> import_name) { \ + thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \ + index, module_name->ToCString().get(), \ + import_name->ToCString().get(), error); \ + } \ + \ + MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index, \ + Handle<String> module_name) { \ + thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \ + module_name->ToCString().get(), error); \ + return MaybeHandle<Object>(); \ + } + + ERROR_THROWER_WITH_MESSAGE(LinkError) + ERROR_THROWER_WITH_MESSAGE(TypeError) + + // Look up an import value in the {ffi_} object. + MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name, + Handle<String> import_name); + + // Look up an import value in the {ffi_} object specifically for linking an + // asm.js module. This only performs non-observable lookups, which allows + // falling back to JavaScript proper (and hence re-executing all lookups) if + // module instantiation fails. + MaybeHandle<Object> LookupImportAsm(uint32_t index, + Handle<String> import_name); + + uint32_t EvalUint32InitExpr(const WasmInitExpr& expr); + + // Load data segments into the memory. + void LoadDataSegments(Address mem_addr, size_t mem_size); + + void WriteGlobalValue(WasmGlobal& global, Handle<Object> value); + + // Process the imports, including functions, tables, globals, and memory, in + // order, loading them from the {ffi_} object. Returns the number of imported + // functions. + int ProcessImports(Handle<FixedArray> code_table, + Handle<WasmInstanceObject> instance); + + template <typename T> + T* GetRawGlobalPtr(WasmGlobal& global); + + // Process initialization of globals. + void InitGlobals(); + + // Allocate memory for a module instance as a new JSArrayBuffer. + Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages); + + bool NeedsWrappers() const; + + // Process the exports, creating wrappers for functions, tables, memories, + // and globals. + void ProcessExports(Handle<FixedArray> code_table, + Handle<WasmInstanceObject> instance, + Handle<WasmCompiledModule> compiled_module); + + void InitializeTables(Handle<WasmInstanceObject> instance, + CodeSpecialization* code_specialization); + + void LoadTableSegments(Handle<FixedArray> code_table, + Handle<WasmInstanceObject> instance); +}; + +// Encapsulates all the state and steps of an asynchronous compilation. +// An asynchronous compile job consists of a number of tasks that are executed +// as foreground and background tasks. Any phase that touches the V8 heap or +// allocates on the V8 heap (e.g. creating the module object) must be a +// foreground task. All other tasks (e.g. decoding and validating, the majority +// of the work of compilation) can be background tasks. +// TODO(wasm): factor out common parts of this with the synchronous pipeline. +class AsyncCompileJob { + public: + explicit AsyncCompileJob(Isolate* isolate, std::unique_ptr<byte[]> bytes_copy, + size_t length, Handle<Context> context, + Handle<JSPromise> promise); + + void Start(); + + ~AsyncCompileJob(); + + private: + class CompileTask; + class CompileStep; + + // States of the AsyncCompileJob. + class DecodeModule; + class DecodeFail; + class PrepareAndStartCompile; + class ExecuteAndFinishCompilationUnits; + class WaitForBackgroundTasks; + class FinishCompilationUnits; + class FinishCompile; + class CompileWrappers; + class FinishModule; + + Isolate* isolate_; + const std::shared_ptr<Counters> async_counters_; + std::unique_ptr<byte[]> bytes_copy_; + ModuleWireBytes wire_bytes_; + Handle<Context> context_; + Handle<JSPromise> module_promise_; + std::unique_ptr<ModuleCompiler> compiler_; + std::unique_ptr<ModuleBytesEnv> module_bytes_env_; + + std::vector<DeferredHandles*> deferred_handles_; + Handle<WasmModuleObject> module_object_; + Handle<FixedArray> function_tables_; + Handle<FixedArray> signature_tables_; + Handle<WasmCompiledModule> compiled_module_; + Handle<FixedArray> code_table_; + std::unique_ptr<WasmInstance> temp_instance_ = nullptr; + size_t outstanding_units_ = 0; + std::unique_ptr<CompileStep> step_; + CancelableTaskManager background_task_manager_; +#if DEBUG + // Counts the number of pending foreground tasks. + int32_t num_pending_foreground_tasks_ = 0; +#endif + + const std::shared_ptr<Counters>& async_counters() const { + return async_counters_; + } + Counters* counters() const { return async_counters().get(); } + + void ReopenHandlesInDeferredScope(); + + void AsyncCompileFailed(ErrorThrower& thrower); + + void AsyncCompileSucceeded(Handle<Object> result); + + template <typename Task, typename... Args> + void DoSync(Args&&... args); + + void StartForegroundTask(); + + void StartBackgroundTask(); + + template <typename Task, typename... Args> + void DoAsync(Args&&... args); +}; + +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_WASM_MODULE_COMPILER_H_ diff --git a/deps/v8/src/wasm/module-decoder.cc b/deps/v8/src/wasm/module-decoder.cc index 83cafbd0d8..94dc710b1b 100644 --- a/deps/v8/src/wasm/module-decoder.cc +++ b/deps/v8/src/wasm/module-decoder.cc @@ -3,18 +3,18 @@ // found in the LICENSE file. #include "src/wasm/module-decoder.h" -#include "src/wasm/function-body-decoder-impl.h" #include "src/base/functional.h" #include "src/base/platform/platform.h" +#include "src/base/template-utils.h" #include "src/counters.h" #include "src/flags.h" #include "src/macro-assembler.h" #include "src/objects-inl.h" #include "src/ostreams.h" #include "src/v8.h" - #include "src/wasm/decoder.h" +#include "src/wasm/function-body-decoder-impl.h" #include "src/wasm/wasm-limits.h" namespace v8 { @@ -29,6 +29,18 @@ namespace wasm { #else #define TRACE(...) #endif +namespace { + +const char kNameString[] = "name"; + +const char kExceptionString[] = "exception"; + +template <size_t N> +constexpr size_t num_chars(const char (&)[N]) { + return N - 1; // remove null character at end. +} + +} // namespace const char* SectionName(SectionCode code) { switch (code) { @@ -57,7 +69,9 @@ const char* SectionName(SectionCode code) { case kDataSectionCode: return "Data"; case kNameSectionCode: - return "Name"; + return kNameString; + case kExceptionSectionCode: + return kExceptionString; default: return "<unknown>"; } @@ -65,9 +79,6 @@ const char* SectionName(SectionCode code) { namespace { -const char* kNameString = "name"; -const size_t kNameStringLength = 4; - ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { switch (expr.kind) { case WasmInitExpr::kNone: @@ -86,29 +97,28 @@ ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { return kWasmF64; default: UNREACHABLE(); - return kWasmStmt; } } // Reads a length-prefixed string, checking that it is within bounds. Returns // the offset of the string, and the length as an out parameter. -uint32_t consume_string(Decoder& decoder, uint32_t* length, bool validate_utf8, - const char* name) { - *length = decoder.consume_u32v("string length"); +WireBytesRef consume_string(Decoder& decoder, bool validate_utf8, + const char* name) { + uint32_t length = decoder.consume_u32v("string length"); uint32_t offset = decoder.pc_offset(); const byte* string_start = decoder.pc(); // Consume bytes before validation to guarantee that the string is not oob. - if (*length > 0) { - decoder.consume_bytes(*length, name); + if (length > 0) { + decoder.consume_bytes(length, name); if (decoder.ok() && validate_utf8 && - !unibrow::Utf8::Validate(string_start, *length)) { + !unibrow::Utf8::ValidateEncoding(string_start, length)) { decoder.errorf(string_start, "%s: no valid UTF-8 string", name); } } - return offset; + return {offset, decoder.failed() ? 0 : length}; } -// An iterator over the sections in a WASM binary module. +// An iterator over the sections in a wasm binary module. // Automatically skips all unknown sections. class WasmSectionIterator { public: @@ -190,25 +200,29 @@ class WasmSectionIterator { if (section_code == kUnknownSectionCode) { // Check for the known "name" section. - uint32_t string_length; - uint32_t string_offset = - wasm::consume_string(decoder_, &string_length, true, "section name"); + WireBytesRef string = + wasm::consume_string(decoder_, true, "section name"); if (decoder_.failed() || decoder_.pc() > section_end_) { section_code_ = kUnknownSectionCode; return; } const byte* section_name_start = - decoder_.start() + decoder_.GetBufferRelativeOffset(string_offset); + decoder_.start() + decoder_.GetBufferRelativeOffset(string.offset()); payload_start_ = decoder_.pc(); TRACE(" +%d section name : \"%.*s\"\n", static_cast<int>(section_name_start - decoder_.start()), - string_length < 20 ? string_length : 20, section_name_start); + string.length() < 20 ? string.length() : 20, section_name_start); - if (string_length == kNameStringLength && + if (string.length() == num_chars(kNameString) && strncmp(reinterpret_cast<const char*>(section_name_start), - kNameString, kNameStringLength) == 0) { + kNameString, num_chars(kNameString)) == 0) { section_code = kNameSectionCode; + } else if (FLAG_experimental_wasm_eh && + string.length() == num_chars(kExceptionString) && + strncmp(reinterpret_cast<const char*>(section_name_start), + kExceptionString, num_chars(kExceptionString)) == 0) { + section_code = kExceptionSectionCode; } } else if (!IsValidSectionCode(section_code)) { decoder_.errorf(decoder_.pc(), "unknown section code #0x%02x", @@ -274,7 +288,7 @@ class ModuleDecoder : public Decoder { void StartDecoding(Isolate* isolate) { CHECK_NULL(module_); module_.reset(new WasmModule( - std::unique_ptr<Zone>(new Zone(isolate->allocator(), "signatures")))); + base::make_unique<Zone>(isolate->allocator(), "signatures"))); module_->min_mem_pages = 0; module_->max_mem_pages = 0; module_->mem_export = false; @@ -361,6 +375,9 @@ class ModuleDecoder : public Decoder { case kNameSectionCode: DecodeNameSection(); break; + case kExceptionSectionCode: + DecodeExceptionSection(); + break; default: errorf(pc(), "unexpected section: %s", SectionName(section_code)); return; @@ -395,19 +412,15 @@ class ModuleDecoder : public Decoder { static_cast<int>(pc_ - start_)); module_->import_table.push_back({ - 0, // module_name_length - 0, // module_name_offset - 0, // field_name_offset - 0, // field_name_length + {0, 0}, // module_name + {0, 0}, // field_name kExternalFunction, // kind 0 // index }); WasmImport* import = &module_->import_table.back(); const byte* pos = pc_; - import->module_name_offset = - consume_string(&import->module_name_length, true, "module name"); - import->field_name_offset = - consume_string(&import->field_name_length, true, "field name"); + import->module_name = consume_string(true, "module name"); + import->field_name = consume_string(true, "field name"); import->kind = static_cast<WasmExternalKind>(consume_u8("import kind")); switch (import->kind) { case kExternalFunction: { @@ -417,10 +430,8 @@ class ModuleDecoder : public Decoder { module_->functions.push_back({nullptr, // sig import->index, // func_index 0, // sig_index - 0, // name_offset - 0, // name_length - 0, // code_start_offset - 0, // code_end_offset + {0, 0}, // name_offset + {0, 0}, // code true, // imported false}); // exported WasmFunction* function = &module_->functions.back(); @@ -433,11 +444,10 @@ class ModuleDecoder : public Decoder { if (!AddTable(module_.get())) break; import->index = static_cast<uint32_t>(module_->function_tables.size()); - module_->function_tables.push_back({0, 0, false, - std::vector<int32_t>(), true, - false, SignatureMap()}); - expect_u8("element type", kWasmAnyFunctionTypeForm); + module_->function_tables.emplace_back(); WasmIndirectFunctionTable* table = &module_->function_tables.back(); + table->imported = true; + expect_u8("element type", kWasmAnyFunctionTypeForm); consume_resizable_limits("element count", "elements", FLAG_wasm_max_table_size, &table->min_size, &table->has_max, FLAG_wasm_max_table_size, @@ -483,10 +493,8 @@ class ModuleDecoder : public Decoder { module_->functions.push_back({nullptr, // sig func_index, // func_index 0, // sig_index - 0, // name_offset - 0, // name_length - 0, // code_start_offset - 0, // code_end_offset + {0, 0}, // name + {0, 0}, // code false, // imported false}); // exported WasmFunction* function = &module_->functions.back(); @@ -499,8 +507,7 @@ class ModuleDecoder : public Decoder { for (uint32_t i = 0; ok() && i < table_count; i++) { if (!AddTable(module_.get())) break; - module_->function_tables.push_back( - {0, 0, false, std::vector<int32_t>(), false, false, SignatureMap()}); + module_->function_tables.emplace_back(); WasmIndirectFunctionTable* table = &module_->function_tables.back(); expect_u8("table type", kWasmAnyFunctionTypeForm); consume_resizable_limits("table elements", "elements", @@ -545,14 +552,13 @@ class ModuleDecoder : public Decoder { static_cast<int>(pc_ - start_)); module_->export_table.push_back({ - 0, // name_length - 0, // name_offset + {0, 0}, // name kExternalFunction, // kind 0 // index }); WasmExport* exp = &module_->export_table.back(); - exp->name_offset = consume_string(&exp->name_length, true, "field name"); + exp->name = consume_string(true, "field name"); const byte* pos = pc(); exp->kind = static_cast<WasmExternalKind>(consume_u8("export kind")); @@ -602,12 +608,12 @@ class ModuleDecoder : public Decoder { auto cmp_less = [this](const WasmExport& a, const WasmExport& b) { // Return true if a < b. - if (a.name_length != b.name_length) { - return a.name_length < b.name_length; + if (a.name.length() != b.name.length()) { + return a.name.length() < b.name.length(); } - const byte* left = start() + GetBufferRelativeOffset(a.name_offset); - const byte* right = start() + GetBufferRelativeOffset(b.name_offset); - return memcmp(left, right, a.name_length) < 0; + const byte* left = start() + GetBufferRelativeOffset(a.name.offset()); + const byte* right = start() + GetBufferRelativeOffset(b.name.offset()); + return memcmp(left, right, a.name.length()) < 0; }; std::stable_sort(sorted_exports.begin(), sorted_exports.end(), cmp_less); @@ -616,9 +622,10 @@ class ModuleDecoder : public Decoder { for (auto end = sorted_exports.end(); it != end; last = &*it++) { DCHECK(!cmp_less(*it, *last)); // Vector must be sorted. if (!cmp_less(*last, *it)) { - const byte* pc = start() + GetBufferRelativeOffset(it->name_offset); - errorf(pc, "Duplicate export name '%.*s' for functions %d and %d", - it->name_length, pc, last->index, it->index); + const byte* pc = start() + GetBufferRelativeOffset(it->name.offset()); + errorf(pc, "Duplicate export name '%.*s' for %s %d and %s %d", + it->name.length(), pc, ExternalKindName(last->kind), + last->index, ExternalKindName(it->kind), it->index); break; } } @@ -676,15 +683,14 @@ class ModuleDecoder : public Decoder { errorf(pos, "function body count %u mismatch (%u expected)", functions_count, module_->num_declared_functions); } - for (uint32_t i = 0; ok() && i < functions_count; ++i) { - WasmFunction* function = - &module_->functions[i + module_->num_imported_functions]; + for (uint32_t i = 0; i < functions_count; ++i) { uint32_t size = consume_u32v("body size"); uint32_t offset = pc_offset(); consume_bytes(size, "function body"); if (failed()) break; - function->code_start_offset = offset; - function->code_end_offset = offset + size; + WasmFunction* function = + &module_->functions[i + module_->num_imported_functions]; + function->code = {offset, size}; if (verify_functions) { ModuleBytesEnv module_env(module_.get(), nullptr, ModuleWireBytes(start_, end_)); @@ -708,8 +714,7 @@ class ModuleDecoder : public Decoder { static_cast<int>(pc_ - start_)); module_->data_segments.push_back({ WasmInitExpr(), // dest_addr - 0, // source_offset - 0 // source_size + {0, 0} // source }); WasmDataSegment* segment = &module_->data_segments.back(); DecodeDataSegmentInModule(module_.get(), segment); @@ -731,34 +736,50 @@ class ModuleDecoder : public Decoder { // Decode function names, ignore the rest. // Local names will be decoded when needed. - if (name_type == NameSectionType::kFunction) { - uint32_t functions_count = inner.consume_u32v("functions count"); - - for (; inner.ok() && functions_count > 0; --functions_count) { - uint32_t function_index = inner.consume_u32v("function index"); - uint32_t name_length = 0; - uint32_t name_offset = - wasm::consume_string(inner, &name_length, false, "function name"); - - // Be lenient with errors in the name section: Ignore illegal - // or out-of-order indexes and non-UTF8 names. You can even assign - // to the same function multiple times (last valid one wins). - if (inner.ok() && function_index < module_->functions.size() && - unibrow::Utf8::Validate( - inner.start() + inner.GetBufferRelativeOffset(name_offset), - name_length)) { - module_->functions[function_index].name_offset = name_offset; - module_->functions[function_index].name_length = name_length; + switch (name_type) { + case NameSectionType::kModule: { + WireBytesRef name = wasm::consume_string(inner, false, "module name"); + if (inner.ok() && validate_utf8(&inner, name)) module_->name = name; + break; + } + case NameSectionType::kFunction: { + uint32_t functions_count = inner.consume_u32v("functions count"); + + for (; inner.ok() && functions_count > 0; --functions_count) { + uint32_t function_index = inner.consume_u32v("function index"); + WireBytesRef name = + wasm::consume_string(inner, false, "function name"); + + // Be lenient with errors in the name section: Ignore illegal + // or out-of-order indexes and non-UTF8 names. You can even assign + // to the same function multiple times (last valid one wins). + if (inner.ok() && function_index < module_->functions.size() && + validate_utf8(&inner, name)) { + module_->functions[function_index].name = name; + } } + break; } - } else { - inner.consume_bytes(name_payload_len, "name subsection payload"); + default: + inner.consume_bytes(name_payload_len, "name subsection payload"); + break; } } // Skip the whole names section in the outer decoder. consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); } + void DecodeExceptionSection() { + uint32_t exception_count = + consume_count("exception count", kV8MaxWasmExceptions); + for (uint32_t i = 0; ok() && i < exception_count; ++i) { + TRACE("DecodeExceptionSignature[%d] module+%d\n", i, + static_cast<int>(pc_ - start_)); + module_->exceptions.emplace_back( + consume_exception_sig(module_->signature_zone.get())); + } + } + ModuleResult FinishDecoding(bool verify_functions = true) { if (ok()) { CalculateGlobalOffsets(module_.get()); @@ -809,11 +830,9 @@ class ModuleDecoder : public Decoder { FunctionResult DecodeSingleFunction(Zone* zone, ModuleBytesEnv* module_env, std::unique_ptr<WasmFunction> function) { pc_ = start_; - function->sig = consume_sig(zone); // read signature - function->name_offset = 0; // ---- name - function->name_length = 0; // ---- name length - function->code_start_offset = off(pc_); // ---- code start - function->code_end_offset = off(end_); // ---- code end + function->sig = consume_sig(zone); + function->name = {0, 0}; + function->code = {off(pc_), static_cast<uint32_t>(end_ - pc_)}; if (ok()) VerifyFunctionBody(zone->allocator(), 0, module_env, function.get()); @@ -916,17 +935,18 @@ class ModuleDecoder : public Decoder { const byte* start = pc_; expect_u8("linear memory index", 0); segment->dest_addr = consume_init_expr(module, kWasmI32); - segment->source_size = consume_u32v("source size"); - segment->source_offset = pc_offset(); + uint32_t source_length = consume_u32v("source size"); + uint32_t source_offset = pc_offset(); + segment->source = {source_offset, source_length}; // Validate the data is in the decoder buffer. uint32_t limit = static_cast<uint32_t>(end_ - start_); - if (!IsWithinLimit(limit, GetBufferRelativeOffset(segment->source_offset), - segment->source_size)) { + if (!IsWithinLimit(limit, GetBufferRelativeOffset(segment->source.offset()), + segment->source.length())) { error(start, "segment out of bounds of the section"); } - consume_bytes(segment->source_size, "segment data"); + consume_bytes(segment->source.length(), "segment data"); } // Calculate individual global offsets and total size of globals table. @@ -953,18 +973,19 @@ class ModuleDecoder : public Decoder { menv->wire_bytes.GetNameOrNull(function)); if (FLAG_trace_wasm_decoder || FLAG_trace_wasm_decode_time) { OFStream os(stdout); - os << "Verifying WASM function " << func_name << std::endl; + os << "Verifying wasm function " << func_name << std::endl; } FunctionBody body = { - function->sig, start_, - start_ + GetBufferRelativeOffset(function->code_start_offset), - start_ + GetBufferRelativeOffset(function->code_end_offset)}; + function->sig, function->code.offset(), + start_ + GetBufferRelativeOffset(function->code.offset()), + start_ + GetBufferRelativeOffset(function->code.end_offset())}; DecodeResult result = VerifyWasmCode( allocator, menv == nullptr ? nullptr : menv->module_env.module, body); if (result.failed()) { // Wrap the error message from the function decoder. - std::ostringstream str; - str << "in function " << func_name << ": " << result.error_msg(); + std::ostringstream wrapped; + wrapped << "in function " << func_name << ": " << result.error_msg(); + result.error(result.error_offset(), wrapped.str()); // Set error code and location, if this is the first error. if (intermediate_result_.ok()) { @@ -973,9 +994,14 @@ class ModuleDecoder : public Decoder { } } - uint32_t consume_string(uint32_t* length, bool validate_utf8, - const char* name) { - return wasm::consume_string(*this, length, validate_utf8, name); + WireBytesRef consume_string(bool validate_utf8, const char* name) { + return wasm::consume_string(*this, validate_utf8, name); + } + + bool validate_utf8(Decoder* decoder, WireBytesRef string) { + return unibrow::Utf8::ValidateEncoding( + decoder->start() + decoder->GetBufferRelativeOffset(string.offset()), + string.length()); } uint32_t consume_sig_index(WasmModule* module, FunctionSig** sig) { @@ -1166,16 +1192,10 @@ class ModuleDecoder : public Decoder { case kLocalF64: return kWasmF64; default: - if (origin_ != kAsmJsOrigin && FLAG_wasm_simd_prototype) { + if (origin_ != kAsmJsOrigin && FLAG_experimental_wasm_simd) { switch (t) { case kLocalS128: return kWasmS128; - case kLocalS1x4: - return kWasmS1x4; - case kLocalS1x8: - return kWasmS1x8; - case kLocalS1x16: - return kWasmS1x16; default: break; } @@ -1185,9 +1205,20 @@ class ModuleDecoder : public Decoder { } } - // Parses a type entry, which is currently limited to functions only. FunctionSig* consume_sig(Zone* zone) { - if (!expect_u8("type form", kWasmFunctionTypeForm)) return nullptr; + constexpr bool has_return_values = true; + return consume_sig_internal(zone, has_return_values); + } + + WasmExceptionSig* consume_exception_sig(Zone* zone) { + constexpr bool has_return_values = true; + return consume_sig_internal(zone, !has_return_values); + } + + private: + FunctionSig* consume_sig_internal(Zone* zone, bool has_return_values) { + if (has_return_values && !expect_u8("type form", kWasmFunctionTypeForm)) + return nullptr; // parse parameter types uint32_t param_count = consume_count("param count", kV8MaxWasmFunctionParams); @@ -1197,17 +1228,19 @@ class ModuleDecoder : public Decoder { ValueType param = consume_value_type(); params.push_back(param); } - - // parse return types - const size_t max_return_count = FLAG_wasm_mv_prototype - ? kV8MaxWasmFunctionMultiReturns - : kV8MaxWasmFunctionReturns; - uint32_t return_count = consume_count("return count", max_return_count); - if (failed()) return nullptr; std::vector<ValueType> returns; - for (uint32_t i = 0; ok() && i < return_count; ++i) { - ValueType ret = consume_value_type(); - returns.push_back(ret); + uint32_t return_count = 0; + if (has_return_values) { + // parse return types + const size_t max_return_count = FLAG_experimental_wasm_mv + ? kV8MaxWasmFunctionMultiReturns + : kV8MaxWasmFunctionReturns; + return_count = consume_count("return count", max_return_count); + if (failed()) return nullptr; + for (uint32_t i = 0; ok() && i < return_count; ++i) { + ValueType ret = consume_value_type(); + returns.push_back(ret); + } } if (failed()) return nullptr; @@ -1222,23 +1255,22 @@ class ModuleDecoder : public Decoder { } }; -ModuleResult DecodeWasmModuleInternal(Isolate* isolate, - const byte* module_start, - const byte* module_end, - bool verify_functions, - ModuleOrigin origin, bool is_sync) { +ModuleResult DecodeWasmModule(Isolate* isolate, const byte* module_start, + const byte* module_end, bool verify_functions, + ModuleOrigin origin, Counters* counters) { + auto counter = origin == kWasmOrigin + ? counters->wasm_decode_wasm_module_time() + : counters->wasm_decode_asm_module_time(); + TimedHistogramScope wasm_decode_module_time_scope(counter); size_t size = module_end - module_start; if (module_start > module_end) return ModuleResult::Error("start > end"); if (size >= kV8MaxWasmModuleSize) return ModuleResult::Error("size > maximum module size: %zu", size); // TODO(bradnelson): Improve histogram handling of size_t. - if (is_sync) { - // TODO(karlschimpf): Make this work when asynchronous. - // https://bugs.chromium.org/p/v8/issues/detail?id=6361 - (IsWasm(origin) ? isolate->counters()->wasm_wasm_module_size_bytes() - : isolate->counters()->wasm_asm_module_size_bytes()) - ->AddSample(static_cast<int>(size)); - } + auto size_counter = origin == kWasmOrigin + ? counters->wasm_wasm_module_size_bytes() + : counters->wasm_asm_module_size_bytes(); + size_counter->AddSample(static_cast<int>(size)); // Signatures are stored in zone memory, which have the same lifetime // as the {module}. ModuleDecoder decoder(module_start, module_end, origin); @@ -1247,34 +1279,32 @@ ModuleResult DecodeWasmModuleInternal(Isolate* isolate, // TODO(titzer): this isn't accurate, since it doesn't count the data // allocated on the C++ heap. // https://bugs.chromium.org/p/chromium/issues/detail?id=657320 - if (is_sync && result.ok()) { - // TODO(karlschimpf): Make this work when asynchronous. - // https://bugs.chromium.org/p/v8/issues/detail?id=6361 - (IsWasm(origin) - ? isolate->counters()->wasm_decode_wasm_module_peak_memory_bytes() - : isolate->counters()->wasm_decode_asm_module_peak_memory_bytes()) - ->AddSample( - static_cast<int>(result.val->signature_zone->allocation_size())); + if (result.ok()) { + auto peak_counter = + origin == kWasmOrigin + ? counters->wasm_decode_wasm_module_peak_memory_bytes() + : counters->wasm_decode_asm_module_peak_memory_bytes(); + peak_counter->AddSample( + static_cast<int>(result.val->signature_zone->allocation_size())); } return result; } } // namespace -ModuleResult DecodeWasmModule(Isolate* isolate, const byte* module_start, - const byte* module_end, bool verify_functions, - ModuleOrigin origin, bool is_sync) { - if (is_sync) { - // TODO(karlschimpf): Make this work when asynchronous. - // https://bugs.chromium.org/p/v8/issues/detail?id=6361 - HistogramTimerScope wasm_decode_module_time_scope( - IsWasm(origin) ? isolate->counters()->wasm_decode_wasm_module_time() - : isolate->counters()->wasm_decode_asm_module_time()); - return DecodeWasmModuleInternal(isolate, module_start, module_end, - verify_functions, origin, true); - } - return DecodeWasmModuleInternal(isolate, module_start, module_end, - verify_functions, origin, false); +ModuleResult SyncDecodeWasmModule(Isolate* isolate, const byte* module_start, + const byte* module_end, bool verify_functions, + ModuleOrigin origin) { + return DecodeWasmModule(isolate, module_start, module_end, verify_functions, + origin, isolate->counters()); +} + +ModuleResult AsyncDecodeWasmModule( + Isolate* isolate, const byte* module_start, const byte* module_end, + bool verify_functions, ModuleOrigin origin, + const std::shared_ptr<Counters> async_counters) { + return DecodeWasmModule(isolate, module_start, module_end, verify_functions, + origin, async_counters.get()); } FunctionSig* DecodeWasmSignatureForTesting(Zone* zone, const byte* start, @@ -1291,51 +1321,44 @@ WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end) { namespace { -FunctionResult DecodeWasmFunctionInternal(Isolate* isolate, Zone* zone, - ModuleBytesEnv* module_env, - const byte* function_start, - const byte* function_end, - bool is_sync) { +FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, + ModuleBytesEnv* module_env, + const byte* function_start, + const byte* function_end, + Counters* counters) { size_t size = function_end - function_start; + bool is_wasm = module_env->module_env.is_wasm(); + auto size_histogram = is_wasm ? counters->wasm_wasm_function_size_bytes() + : counters->wasm_asm_function_size_bytes(); + size_histogram->AddSample(static_cast<int>(size)); + auto time_counter = is_wasm ? counters->wasm_decode_wasm_function_time() + : counters->wasm_decode_asm_function_time(); + TimedHistogramScope wasm_decode_function_time_scope(time_counter); if (function_start > function_end) return FunctionResult::Error("start > end"); if (size > kV8MaxWasmFunctionSize) return FunctionResult::Error("size > maximum function size: %zu", size); - if (is_sync) { - // TODO(karlschimpf): Make this work when asynchronous. - // https://bugs.chromium.org/p/v8/issues/detail?id=6361 - bool is_wasm = module_env->module_env.is_wasm(); - (is_wasm ? isolate->counters()->wasm_wasm_function_size_bytes() - : isolate->counters()->wasm_asm_function_size_bytes()) - ->AddSample(static_cast<int>(size)); - } ModuleDecoder decoder(function_start, function_end, kWasmOrigin); - return decoder.DecodeSingleFunction( - zone, module_env, std::unique_ptr<WasmFunction>(new WasmFunction())); + return decoder.DecodeSingleFunction(zone, module_env, + base::make_unique<WasmFunction>()); } } // namespace -FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, - ModuleBytesEnv* module_env, - const byte* function_start, - const byte* function_end, bool is_sync) { - if (is_sync) { - // TODO(karlschimpf): Make this work when asynchronous. - // https://bugs.chromium.org/p/v8/issues/detail?id=6361 - size_t size = function_end - function_start; - bool is_wasm = module_env->module_env.is_wasm(); - (is_wasm ? isolate->counters()->wasm_wasm_function_size_bytes() - : isolate->counters()->wasm_asm_function_size_bytes()) - ->AddSample(static_cast<int>(size)); - HistogramTimerScope wasm_decode_function_time_scope( - is_wasm ? isolate->counters()->wasm_decode_wasm_function_time() - : isolate->counters()->wasm_decode_asm_function_time()); - return DecodeWasmFunctionInternal(isolate, zone, module_env, function_start, - function_end, true); - } - return DecodeWasmFunctionInternal(isolate, zone, module_env, function_start, - function_end, false); +FunctionResult SyncDecodeWasmFunction(Isolate* isolate, Zone* zone, + ModuleBytesEnv* module_env, + const byte* function_start, + const byte* function_end) { + return DecodeWasmFunction(isolate, zone, module_env, function_start, + function_end, isolate->counters()); +} + +FunctionResult AsyncDecodeWasmFunction( + Isolate* isolate, Zone* zone, ModuleBytesEnv* module_env, + const byte* function_start, const byte* function_end, + std::shared_ptr<Counters> async_counters) { + return DecodeWasmFunction(isolate, zone, module_env, function_start, + function_end, async_counters.get()); } AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start, @@ -1411,13 +1434,68 @@ std::vector<CustomSectionOffset> DecodeCustomSections(const byte* start, uint32_t payload_offset = decoder.pc_offset(); uint32_t payload_length = section_length - (payload_offset - section_start); decoder.consume_bytes(payload_length); - result.push_back({section_start, name_offset, name_length, payload_offset, - payload_length, section_length}); + result.push_back({{section_start, section_length}, + {name_offset, name_length}, + {payload_offset, payload_length}}); } return result; } +void DecodeLocalNames(const byte* module_start, const byte* module_end, + LocalNames* result) { + DCHECK_NOT_NULL(result); + DCHECK(result->names.empty()); + + static constexpr int kModuleHeaderSize = 8; + Decoder decoder(module_start, module_end); + decoder.consume_bytes(kModuleHeaderSize, "module header"); + + WasmSectionIterator section_iter(decoder); + + while (decoder.ok() && section_iter.more() && + section_iter.section_code() != kNameSectionCode) { + section_iter.advance(true); + } + if (!section_iter.more()) return; + + // Reset the decoder to not read beyond the name section end. + decoder.Reset(section_iter.payload(), decoder.pc_offset()); + + while (decoder.ok() && decoder.more()) { + uint8_t name_type = decoder.consume_u8("name type"); + if (name_type & 0x80) break; // no varuint7 + + uint32_t name_payload_len = decoder.consume_u32v("name payload length"); + if (!decoder.checkAvailable(name_payload_len)) break; + + if (name_type != NameSectionType::kLocal) { + decoder.consume_bytes(name_payload_len, "name subsection payload"); + continue; + } + + uint32_t local_names_count = decoder.consume_u32v("local names count"); + for (uint32_t i = 0; i < local_names_count; ++i) { + uint32_t func_index = decoder.consume_u32v("function index"); + if (func_index > kMaxInt) continue; + result->names.emplace_back(static_cast<int>(func_index)); + LocalNamesPerFunction& func_names = result->names.back(); + result->max_function_index = + std::max(result->max_function_index, func_names.function_index); + uint32_t num_names = decoder.consume_u32v("namings count"); + for (uint32_t k = 0; k < num_names; ++k) { + uint32_t local_index = decoder.consume_u32v("local index"); + WireBytesRef name = wasm::consume_string(decoder, true, "local name"); + if (!decoder.ok()) break; + if (local_index > kMaxInt) continue; + func_names.max_local_index = + std::max(func_names.max_local_index, static_cast<int>(local_index)); + func_names.names.emplace_back(static_cast<int>(local_index), name); + } + } + } +} + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/deps/v8/src/wasm/module-decoder.h b/deps/v8/src/wasm/module-decoder.h index 91169d8eca..094a39dbe7 100644 --- a/deps/v8/src/wasm/module-decoder.h +++ b/deps/v8/src/wasm/module-decoder.h @@ -21,25 +21,26 @@ const uint8_t kWasmAnyFunctionTypeForm = 0x70; const uint8_t kResizableMaximumFlag = 1; enum SectionCode : int8_t { - kUnknownSectionCode = 0, // code for unknown sections - kTypeSectionCode = 1, // Function signature declarations - kImportSectionCode = 2, // Import declarations - kFunctionSectionCode = 3, // Function declarations - kTableSectionCode = 4, // Indirect function table and other tables - kMemorySectionCode = 5, // Memory attributes - kGlobalSectionCode = 6, // Global declarations - kExportSectionCode = 7, // Exports - kStartSectionCode = 8, // Start function declaration - kElementSectionCode = 9, // Elements section - kCodeSectionCode = 10, // Function code - kDataSectionCode = 11, // Data segments - kNameSectionCode = 12, // Name section (encoded as a string) + kUnknownSectionCode = 0, // code for unknown sections + kTypeSectionCode = 1, // Function signature declarations + kImportSectionCode = 2, // Import declarations + kFunctionSectionCode = 3, // Function declarations + kTableSectionCode = 4, // Indirect function table and other tables + kMemorySectionCode = 5, // Memory attributes + kGlobalSectionCode = 6, // Global declarations + kExportSectionCode = 7, // Exports + kStartSectionCode = 8, // Start function declaration + kElementSectionCode = 9, // Elements section + kCodeSectionCode = 10, // Function code + kDataSectionCode = 11, // Data segments + kNameSectionCode = 12, // Name section (encoded as a string) + kExceptionSectionCode = 13, // Exception section (encoded as a string) // Helper values kFirstSectionInModule = kTypeSectionCode, }; -enum NameSectionType : uint8_t { kFunction = 1, kLocal = 2 }; +enum NameSectionType : uint8_t { kModule = 0, kFunction = 1, kLocal = 2 }; inline bool IsValidSectionCode(uint8_t byte) { return kTypeSectionCode <= byte && byte <= kDataSectionCode; @@ -51,6 +52,7 @@ typedef Result<std::unique_ptr<WasmModule>> ModuleResult; typedef Result<std::unique_ptr<WasmFunction>> FunctionResult; typedef std::vector<std::pair<int, int>> FunctionOffsets; typedef Result<FunctionOffsets> FunctionOffsetsResult; + struct AsmJsOffsetEntry { int byte_offset; int source_position_call; @@ -59,10 +61,35 @@ struct AsmJsOffsetEntry { typedef std::vector<std::vector<AsmJsOffsetEntry>> AsmJsOffsets; typedef Result<AsmJsOffsets> AsmJsOffsetsResult; -// Decodes the bytes of a WASM module between {module_start} and {module_end}. -V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule( +struct LocalName { + int local_index; + WireBytesRef name; + LocalName(int local_index, WireBytesRef name) + : local_index(local_index), name(name) {} +}; +struct LocalNamesPerFunction { + int function_index; + int max_local_index = -1; + std::vector<LocalName> names; + explicit LocalNamesPerFunction(int function_index) + : function_index(function_index) {} +}; +struct LocalNames { + int max_function_index = -1; + std::vector<LocalNamesPerFunction> names; +}; + +// Decodes the bytes of a wasm module between {module_start} and {module_end}. +V8_EXPORT_PRIVATE ModuleResult SyncDecodeWasmModule(Isolate* isolate, + const byte* module_start, + const byte* module_end, + bool verify_functions, + ModuleOrigin origin); + +V8_EXPORT_PRIVATE ModuleResult AsyncDecodeWasmModule( Isolate* isolate, const byte* module_start, const byte* module_end, - bool verify_functions, ModuleOrigin origin, bool is_sync = true); + bool verify_functions, ModuleOrigin origin, + const std::shared_ptr<Counters> async_counters); // Exposed for testing. Decodes a single function signature, allocating it // in the given zone. Returns {nullptr} upon failure. @@ -70,22 +97,24 @@ V8_EXPORT_PRIVATE FunctionSig* DecodeWasmSignatureForTesting(Zone* zone, const byte* start, const byte* end); -// Decodes the bytes of a WASM function between +// Decodes the bytes of a wasm function between // {function_start} and {function_end}. -V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunction( - Isolate* isolate, Zone* zone, ModuleBytesEnv* env, - const byte* function_start, const byte* function_end, bool is_sync = true); +V8_EXPORT_PRIVATE FunctionResult +SyncDecodeWasmFunction(Isolate* isolate, Zone* zone, ModuleBytesEnv* env, + const byte* function_start, const byte* function_end); + +V8_EXPORT_PRIVATE FunctionResult +AsyncDecodeWasmFunction(Isolate* isolate, Zone* zone, ModuleBytesEnv* env, + const byte* function_start, const byte* function_end, + const std::shared_ptr<Counters> async_counters); V8_EXPORT_PRIVATE WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end); struct CustomSectionOffset { - uint32_t section_start; - uint32_t name_offset; - uint32_t name_length; - uint32_t payload_offset; - uint32_t payload_length; - uint32_t section_length; + WireBytesRef section; + WireBytesRef name; + WireBytesRef payload; }; V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections( @@ -99,6 +128,13 @@ V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections( AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* module_start, const byte* module_end); +// Decode the local names assignment from the name section. +// Stores the result in the given {LocalNames} structure. The result will be +// empty if no name section is present. On encountering an error in the name +// section, returns all information decoded up to the first error. +void DecodeLocalNames(const byte* module_start, const byte* module_end, + LocalNames* result); + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/deps/v8/src/wasm/signature-map.cc b/deps/v8/src/wasm/signature-map.cc index e7f8b2fc94..e7ee4eba4e 100644 --- a/deps/v8/src/wasm/signature-map.cc +++ b/deps/v8/src/wasm/signature-map.cc @@ -8,7 +8,10 @@ namespace v8 { namespace internal { namespace wasm { +SignatureMap::SignatureMap() : mutex_(new base::Mutex()) {} + uint32_t SignatureMap::FindOrInsert(FunctionSig* sig) { + base::LockGuard<base::Mutex> guard(mutex_.get()); auto pos = map_.find(sig); if (pos != map_.end()) { return pos->second; @@ -20,6 +23,7 @@ uint32_t SignatureMap::FindOrInsert(FunctionSig* sig) { } int32_t SignatureMap::Find(FunctionSig* sig) const { + base::LockGuard<base::Mutex> guard(mutex_.get()); auto pos = map_.find(sig); if (pos != map_.end()) { return static_cast<int32_t>(pos->second); diff --git a/deps/v8/src/wasm/signature-map.h b/deps/v8/src/wasm/signature-map.h index 3a7ed0a047..0b7ddfc58c 100644 --- a/deps/v8/src/wasm/signature-map.h +++ b/deps/v8/src/wasm/signature-map.h @@ -19,6 +19,12 @@ namespace wasm { // same index. class V8_EXPORT_PRIVATE SignatureMap { public: + // Allow default construction and move construction (because we have vectors + // of objects containing SignatureMaps), but disallow copy or assign. It's + // too easy to get security bugs by accidentally updating a copy of the map. + SignatureMap(); + SignatureMap(SignatureMap&&) = default; + // Gets the index for a signature, assigning a new index if necessary. uint32_t FindOrInsert(FunctionSig* sig); @@ -31,7 +37,10 @@ class V8_EXPORT_PRIVATE SignatureMap { bool operator()(FunctionSig* a, FunctionSig* b) const; }; uint32_t next_ = 0; + std::unique_ptr<base::Mutex> mutex_; std::map<FunctionSig*, uint32_t, CompareFunctionSigs> map_; + + DISALLOW_COPY_AND_ASSIGN(SignatureMap); }; } // namespace wasm diff --git a/deps/v8/src/wasm/streaming-decoder.cc b/deps/v8/src/wasm/streaming-decoder.cc index 2772cd5945..4e9d1a843d 100644 --- a/deps/v8/src/wasm/streaming-decoder.cc +++ b/deps/v8/src/wasm/streaming-decoder.cc @@ -4,18 +4,17 @@ #include "src/wasm/streaming-decoder.h" -#include "src/objects-inl.h" - +#include "src/base/template-utils.h" #include "src/handles.h" +#include "src/objects-inl.h" +#include "src/objects/descriptor-array.h" +#include "src/objects/dictionary.h" #include "src/wasm/decoder.h" #include "src/wasm/leb-helper.h" #include "src/wasm/module-decoder.h" #include "src/wasm/wasm-objects.h" #include "src/wasm/wasm-result.h" -#include "src/objects/descriptor-array.h" -#include "src/objects/dictionary.h" - using namespace v8::internal; using namespace v8::internal::wasm; @@ -235,12 +234,12 @@ size_t StreamingDecoder::DecodeVarInt32::ReadBytes( std::unique_ptr<StreamingDecoder::DecodingState> StreamingDecoder::DecodeVarInt32::Next(StreamingDecoder* streaming) { if (streaming->decoder()->failed()) { - return std::unique_ptr<DecodingState>(nullptr); + return nullptr; } if (value() > max_value_) { streaming->decoder()->errorf(buffer(), "size > maximum function size: %zu", value()); - return std::unique_ptr<DecodingState>(nullptr); + return nullptr; } return NextWithValue(streaming); @@ -271,12 +270,12 @@ void StreamingDecoder::DecodeModuleHeader::CheckHeader(Decoder* decoder) { std::unique_ptr<StreamingDecoder::DecodingState> StreamingDecoder::DecodeModuleHeader::Next(StreamingDecoder* streaming) { CheckHeader(streaming->decoder()); - return std::unique_ptr<DecodingState>(new DecodeSectionID()); + return base::make_unique<DecodeSectionID>(); } std::unique_ptr<StreamingDecoder::DecodingState> StreamingDecoder::DecodeSectionID::Next(StreamingDecoder* streaming) { - return std::unique_ptr<DecodingState>(new DecodeSectionLength(id())); + return base::make_unique<DecodeSectionLength>(id()); } std::unique_ptr<StreamingDecoder::DecodingState> @@ -287,19 +286,19 @@ StreamingDecoder::DecodeSectionLength::NextWithValue( Vector<const uint8_t>(buffer(), static_cast<int>(bytes_needed()))); if (value() == 0) { // There is no payload, we go to the next section immediately. - return std::unique_ptr<DecodingState>(new DecodeSectionID()); + return base::make_unique<DecodeSectionID>(); } else if (section_id() == SectionCode::kCodeSectionCode) { // We reached the code section. All functions of the code section are put // into the same SectionBuffer. - return std::unique_ptr<DecodingState>(new DecodeNumberOfFunctions(buf)); + return base::make_unique<DecodeNumberOfFunctions>(buf); } else { - return std::unique_ptr<DecodingState>(new DecodeSectionPayload(buf)); + return base::make_unique<DecodeSectionPayload>(buf); } } std::unique_ptr<StreamingDecoder::DecodingState> StreamingDecoder::DecodeSectionPayload::Next(StreamingDecoder* streaming) { - return std::unique_ptr<DecodingState>(new DecodeSectionID()); + return base::make_unique<DecodeSectionID>(); } std::unique_ptr<StreamingDecoder::DecodingState> @@ -311,16 +310,16 @@ StreamingDecoder::DecodeNumberOfFunctions::NextWithValue( buffer(), bytes_needed()); } else { streaming->decoder()->error("Invalid code section length"); - return std::unique_ptr<DecodingState>(new DecodeSectionID()); + return base::make_unique<DecodeSectionID>(); } // {value} is the number of functions. if (value() > 0) { - return std::unique_ptr<DecodingState>(new DecodeFunctionLength( + return base::make_unique<DecodeFunctionLength>( section_buffer(), section_buffer()->payload_offset() + bytes_needed(), - value())); + value()); } else { - return std::unique_ptr<DecodingState>(new DecodeSectionID()); + return base::make_unique<DecodeSectionID>(); } } @@ -332,30 +331,30 @@ StreamingDecoder::DecodeFunctionLength::NextWithValue( memcpy(section_buffer_->bytes() + buffer_offset_, buffer(), bytes_needed()); } else { streaming->decoder()->error("Invalid code section length"); - return std::unique_ptr<DecodingState>(new DecodeSectionID()); + return base::make_unique<DecodeSectionID>(); } // {value} is the length of the function. if (value() == 0) { streaming->decoder()->errorf(buffer(), "Invalid function length (0)"); - return std::unique_ptr<DecodingState>(nullptr); + return nullptr; } else if (buffer_offset() + bytes_needed() + value() > section_buffer()->length()) { streaming->decoder()->errorf(buffer(), "not enough code section bytes"); - return std::unique_ptr<DecodingState>(nullptr); + return nullptr; } - return std::unique_ptr<DecodingState>( - new DecodeFunctionBody(section_buffer(), buffer_offset() + bytes_needed(), - value(), num_remaining_functions())); + return base::make_unique<DecodeFunctionBody>( + section_buffer(), buffer_offset() + bytes_needed(), value(), + num_remaining_functions()); } std::unique_ptr<StreamingDecoder::DecodingState> StreamingDecoder::DecodeFunctionBody::Next(StreamingDecoder* streaming) { // TODO(ahaas): Start compilation of the function here. if (num_remaining_functions() != 0) { - return std::unique_ptr<DecodingState>(new DecodeFunctionLength( - section_buffer(), buffer_offset() + size(), num_remaining_functions())); + return base::make_unique<DecodeFunctionLength>( + section_buffer(), buffer_offset() + size(), num_remaining_functions()); } else { if (buffer_offset() + size() != section_buffer()->length()) { streaming->decoder()->Reset( @@ -364,9 +363,9 @@ StreamingDecoder::DecodeFunctionBody::Next(StreamingDecoder* streaming) { streaming->decoder()->errorf( section_buffer()->bytes() + buffer_offset() + size(), "not all code section bytes were used"); - return std::unique_ptr<DecodingState>(nullptr); + return nullptr; } - return std::unique_ptr<DecodingState>(new DecodeSectionID()); + return base::make_unique<DecodeSectionID>(); } } diff --git a/deps/v8/src/wasm/wasm-code-specialization.cc b/deps/v8/src/wasm/wasm-code-specialization.cc index 53e3fe699c..c274497fec 100644 --- a/deps/v8/src/wasm/wasm-code-specialization.cc +++ b/deps/v8/src/wasm/wasm-code-specialization.cc @@ -45,9 +45,9 @@ class PatchDirectCallsHelper { FixedArray* deopt_data = code->deoptimization_data(); DCHECK_EQ(2, deopt_data->length()); WasmCompiledModule* comp_mod = instance->compiled_module(); - int func_index = Smi::cast(deopt_data->get(1))->value(); + int func_index = Smi::ToInt(deopt_data->get(1)); func_bytes = comp_mod->module_bytes()->GetChars() + - comp_mod->module()->functions[func_index].code_start_offset; + comp_mod->module()->functions[func_index].code.offset(); } SourcePositionTableIterator source_pos_it; diff --git a/deps/v8/src/wasm/wasm-debug.cc b/deps/v8/src/wasm/wasm-debug.cc index f942c92127..e302f04adc 100644 --- a/deps/v8/src/wasm/wasm-debug.cc +++ b/deps/v8/src/wasm/wasm-debug.cc @@ -40,8 +40,8 @@ Handle<String> PrintFToOneByteString(Isolate* isolate, const char* format, : isolate->factory()->NewStringFromOneByte(name).ToHandleChecked(); } -Handle<Object> WasmValToValueObject(Isolate* isolate, WasmVal value) { - switch (value.type) { +Handle<Object> WasmValueToValueObject(Isolate* isolate, WasmValue value) { + switch (value.type()) { case kWasmI32: if (Smi::IsValid(value.to<int32_t>())) return handle(Smi::FromInt(value.to<int32_t>()), isolate); @@ -61,6 +61,34 @@ Handle<Object> WasmValToValueObject(Isolate* isolate, WasmVal value) { } } +MaybeHandle<String> GetLocalName(Isolate* isolate, + Handle<WasmDebugInfo> debug_info, + int func_index, int local_index) { + DCHECK_LE(0, func_index); + DCHECK_LE(0, local_index); + if (!debug_info->has_locals_names()) { + Handle<WasmCompiledModule> compiled_module( + debug_info->wasm_instance()->compiled_module(), isolate); + Handle<FixedArray> locals_names = + wasm::DecodeLocalNames(isolate, compiled_module); + debug_info->set_locals_names(*locals_names); + } + + Handle<FixedArray> locals_names(debug_info->locals_names(), isolate); + if (func_index >= locals_names->length() || + locals_names->get(func_index)->IsUndefined(isolate)) { + return {}; + } + + Handle<FixedArray> func_locals_names( + FixedArray::cast(locals_names->get(func_index)), isolate); + if (local_index >= func_locals_names->length() || + func_locals_names->get(local_index)->IsUndefined(isolate)) { + return {}; + } + return handle(String::cast(func_locals_names->get(local_index))); +} + // Forward declaration. class InterpreterHandle; InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info); @@ -116,9 +144,6 @@ class InterpreterHandle { WasmInstanceObject* instance = debug_info->wasm_instance(); - // Store a global handle to the wasm instance in the interpreter. - interpreter_.SetInstanceObject(instance); - // Set memory start pointer and size. instance_.mem_start = nullptr; instance_.mem_size = 0; @@ -165,20 +190,21 @@ class InterpreterHandle { // Returns true if exited regularly, false if a trap/exception occured and was // not handled inside this activation. In the latter case, a pending exception // will have been set on the isolate. - bool Execute(Address frame_pointer, uint32_t func_index, + bool Execute(Handle<WasmInstanceObject> instance_object, + Address frame_pointer, uint32_t func_index, uint8_t* arg_buffer) { DCHECK_GE(module()->functions.size(), func_index); FunctionSig* sig = module()->functions[func_index].sig; DCHECK_GE(kMaxInt, sig->parameter_count()); int num_params = static_cast<int>(sig->parameter_count()); - ScopedVector<WasmVal> wasm_args(num_params); + ScopedVector<WasmValue> wasm_args(num_params); uint8_t* arg_buf_ptr = arg_buffer; for (int i = 0; i < num_params; ++i) { uint32_t param_size = 1 << ElementSizeLog2Of(sig->GetParam(i)); -#define CASE_ARG_TYPE(type, ctype) \ - case type: \ - DCHECK_EQ(param_size, sizeof(ctype)); \ - wasm_args[i] = WasmVal(ReadUnalignedValue<ctype>(arg_buf_ptr)); \ +#define CASE_ARG_TYPE(type, ctype) \ + case type: \ + DCHECK_EQ(param_size, sizeof(ctype)); \ + wasm_args[i] = WasmValue(ReadUnalignedValue<ctype>(arg_buf_ptr)); \ break; switch (sig->GetParam(i)) { CASE_ARG_TYPE(kWasmI32, uint32_t) @@ -194,6 +220,8 @@ class InterpreterHandle { uint32_t activation_id = StartActivation(frame_pointer); + WasmInterpreter::HeapObjectsScope heap_objects_scope(&interpreter_, + instance_object); WasmInterpreter::Thread* thread = interpreter_.GetThread(0); thread->InitFrame(&module()->functions[func_index], wasm_args.start()); bool finished = false; @@ -236,7 +264,7 @@ class InterpreterHandle { // TODO(wasm): Handle multi-value returns. DCHECK_EQ(1, kV8MaxWasmFunctionReturns); if (sig->return_count()) { - WasmVal ret_val = thread->GetReturnValue(0); + WasmValue ret_val = thread->GetReturnValue(0); #define CASE_RET_TYPE(type, ctype) \ case type: \ DCHECK_EQ(1 << ElementSizeLog2Of(sig->GetReturn(0)), sizeof(ctype)); \ @@ -277,7 +305,6 @@ class InterpreterHandle { } default: UNREACHABLE(); - return WasmInterpreter::STOPPED; } } @@ -411,8 +438,10 @@ class InterpreterHandle { } Handle<JSArray> GetScopeDetails(Address frame_pointer, int frame_index, - Handle<WasmInstanceObject> instance) { + Handle<WasmDebugInfo> debug_info) { auto frame = GetInterpretedFrame(frame_pointer, frame_index); + Isolate* isolate = debug_info->GetIsolate(); + Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate); Handle<FixedArray> global_scope = isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize); @@ -435,7 +464,7 @@ class InterpreterHandle { kExternalUint8Array, memory_buffer, 0, byte_length); JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object, name, uint8_array, NONE) - .Check(); + .Assert(); } Handle<FixedArray> local_scope = @@ -451,15 +480,30 @@ class InterpreterHandle { int num_params = frame->GetParameterCount(); int num_locals = frame->GetLocalCount(); DCHECK_LE(num_params, num_locals); - for (int i = 0; i < num_locals; ++i) { - // TODO(clemensh): Use names from name section if present. - const char* label = i < num_params ? "param#%d" : "local#%d"; - Handle<String> name = PrintFToOneByteString<true>(isolate_, label, i); - WasmVal value = frame->GetLocalValue(i); - Handle<Object> value_obj = WasmValToValueObject(isolate_, value); - JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, name, - value_obj, NONE) - .Check(); + if (num_locals > 0) { + Handle<JSObject> locals_obj = + isolate_->factory()->NewJSObjectWithNullProto(); + Handle<String> locals_name = + isolate_->factory()->InternalizeOneByteString( + STATIC_CHAR_VECTOR("locals")); + JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, locals_name, + locals_obj, NONE) + .Assert(); + for (int i = 0; i < num_locals; ++i) { + MaybeHandle<String> name = + GetLocalName(isolate, debug_info, frame->function()->func_index, i); + if (name.is_null()) { + // Parameters should come before locals in alphabetical ordering, so + // we name them "args" here. + const char* label = i < num_params ? "arg#%d" : "local#%d"; + name = PrintFToOneByteString<true>(isolate_, label, i); + } + WasmValue value = frame->GetLocalValue(i); + Handle<Object> value_obj = WasmValueToValueObject(isolate_, value); + JSObject::SetOwnPropertyIgnoreAttributes( + locals_obj, name.ToHandleChecked(), value_obj, NONE) + .Assert(); + } } // Fill stack values. @@ -469,18 +513,18 @@ class InterpreterHandle { // which does not make too much sense here. Handle<JSObject> stack_obj = isolate_->factory()->NewJSObjectWithNullProto(); - for (int i = 0; i < stack_count; ++i) { - WasmVal value = frame->GetStackValue(i); - Handle<Object> value_obj = WasmValToValueObject(isolate_, value); - JSObject::SetOwnElementIgnoreAttributes( - stack_obj, static_cast<uint32_t>(i), value_obj, NONE) - .Check(); - } Handle<String> stack_name = isolate_->factory()->InternalizeOneByteString( STATIC_CHAR_VECTOR("stack")); JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, stack_name, stack_obj, NONE) - .Check(); + .Assert(); + for (int i = 0; i < stack_count; ++i) { + WasmValue value = frame->GetStackValue(i); + Handle<Object> value_obj = WasmValueToValueObject(isolate_, value); + JSObject::SetOwnElementIgnoreAttributes( + stack_obj, static_cast<uint32_t>(i), value_obj, NONE) + .Assert(); + } Handle<JSArray> global_jsarr = isolate_->factory()->NewJSArrayWithElements(global_scope); @@ -495,25 +539,25 @@ class InterpreterHandle { InterpreterHandle* GetOrCreateInterpreterHandle( Isolate* isolate, Handle<WasmDebugInfo> debug_info) { - Handle<Object> handle(debug_info->get(WasmDebugInfo::kInterpreterHandle), + Handle<Object> handle(debug_info->get(WasmDebugInfo::kInterpreterHandleIndex), isolate); if (handle->IsUndefined(isolate)) { InterpreterHandle* cpp_handle = new InterpreterHandle(isolate, *debug_info); handle = Managed<InterpreterHandle>::New(isolate, cpp_handle); - debug_info->set(WasmDebugInfo::kInterpreterHandle, *handle); + debug_info->set(WasmDebugInfo::kInterpreterHandleIndex, *handle); } return Handle<Managed<InterpreterHandle>>::cast(handle)->get(); } InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info) { - Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandle); + Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandleIndex); DCHECK(!handle_obj->IsUndefined(debug_info->GetIsolate())); return Managed<InterpreterHandle>::cast(handle_obj)->get(); } InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo* debug_info) { - Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandle); + Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandleIndex); if (handle_obj->IsUndefined(debug_info->GetIsolate())) return nullptr; return Managed<InterpreterHandle>::cast(handle_obj)->get(); } @@ -527,13 +571,13 @@ int GetNumFunctions(WasmInstanceObject* instance) { Handle<FixedArray> GetOrCreateInterpretedFunctions( Isolate* isolate, Handle<WasmDebugInfo> debug_info) { - Handle<Object> obj(debug_info->get(WasmDebugInfo::kInterpretedFunctions), + Handle<Object> obj(debug_info->get(WasmDebugInfo::kInterpretedFunctionsIndex), isolate); if (!obj->IsUndefined(isolate)) return Handle<FixedArray>::cast(obj); Handle<FixedArray> new_arr = isolate->factory()->NewFixedArray( GetNumFunctions(debug_info->wasm_instance())); - debug_info->set(WasmDebugInfo::kInterpretedFunctions, *new_arr); + debug_info->set(WasmDebugInfo::kInterpretedFunctionsIndex, *new_arr); return new_arr; } @@ -578,8 +622,7 @@ Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) { DCHECK(!instance->has_debug_info()); Factory* factory = instance->GetIsolate()->factory(); Handle<FixedArray> arr = factory->NewFixedArray(kFieldCount, TENURED); - arr->set(kWrapperTracerHeader, Smi::kZero); - arr->set(kInstance, *instance); + arr->set(kInstanceIndex, *instance); Handle<WasmDebugInfo> debug_info = Handle<WasmDebugInfo>::cast(arr); instance->set_debug_info(*debug_info); return debug_info; @@ -592,29 +635,29 @@ WasmInterpreter* WasmDebugInfo::SetupForTesting( InterpreterHandle* cpp_handle = new InterpreterHandle(isolate, *debug_info, instance); Handle<Object> handle = Managed<InterpreterHandle>::New(isolate, cpp_handle); - debug_info->set(kInterpreterHandle, *handle); + debug_info->set(kInterpreterHandleIndex, *handle); return cpp_handle->interpreter(); } -bool WasmDebugInfo::IsDebugInfo(Object* object) { +bool WasmDebugInfo::IsWasmDebugInfo(Object* object) { if (!object->IsFixedArray()) return false; FixedArray* arr = FixedArray::cast(object); if (arr->length() != kFieldCount) return false; - if (!IsWasmInstance(arr->get(kInstance))) return false; + if (!arr->get(kInstanceIndex)->IsWasmInstanceObject()) return false; Isolate* isolate = arr->GetIsolate(); - if (!arr->get(kInterpreterHandle)->IsUndefined(isolate) && - !arr->get(kInterpreterHandle)->IsForeign()) + if (!arr->get(kInterpreterHandleIndex)->IsUndefined(isolate) && + !arr->get(kInterpreterHandleIndex)->IsForeign()) return false; return true; } WasmDebugInfo* WasmDebugInfo::cast(Object* object) { - DCHECK(IsDebugInfo(object)); + DCHECK(IsWasmDebugInfo(object)); return reinterpret_cast<WasmDebugInfo*>(object); } WasmInstanceObject* WasmDebugInfo::wasm_instance() { - return WasmInstanceObject::cast(get(kInstance)); + return WasmInstanceObject::cast(get(kInstanceIndex)); } void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info, @@ -662,8 +705,9 @@ void WasmDebugInfo::PrepareStep(StepAction step_action) { bool WasmDebugInfo::RunInterpreter(Address frame_pointer, int func_index, uint8_t* arg_buffer) { DCHECK_LE(0, func_index); + Handle<WasmInstanceObject> instance(wasm_instance()); return GetInterpreterHandle(this)->Execute( - frame_pointer, static_cast<uint32_t>(func_index), arg_buffer); + instance, frame_pointer, static_cast<uint32_t>(func_index), arg_buffer); } std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack( @@ -696,6 +740,5 @@ Handle<JSArray> WasmDebugInfo::GetScopeDetails(Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) { InterpreterHandle* interp_handle = GetInterpreterHandle(*debug_info); - Handle<WasmInstanceObject> instance(debug_info->wasm_instance()); - return interp_handle->GetScopeDetails(frame_pointer, frame_index, instance); + return interp_handle->GetScopeDetails(frame_pointer, frame_index, debug_info); } diff --git a/deps/v8/src/wasm/wasm-interpreter.cc b/deps/v8/src/wasm/wasm-interpreter.cc index d344d1fae4..6ebc342b62 100644 --- a/deps/v8/src/wasm/wasm-interpreter.cc +++ b/deps/v8/src/wasm/wasm-interpreter.cc @@ -587,11 +587,11 @@ inline double ExecuteF64ReinterpretI64(int64_t a, TrapReason* trap) { return bit_cast<double>(a); } -inline int32_t ExecuteI32ReinterpretF32(WasmVal a) { +inline int32_t ExecuteI32ReinterpretF32(WasmValue a) { return a.to_unchecked<int32_t>(); } -inline int64_t ExecuteI64ReinterpretF64(WasmVal a) { +inline int64_t ExecuteI64ReinterpretF64(WasmValue a) { return a.to_unchecked<int64_t>(); } @@ -936,66 +936,49 @@ class CodeMap { Zone* zone_; const WasmModule* module_; ZoneVector<InterpreterCode> interpreter_code_; - // Global handle to the wasm instance. + // This handle is set and reset by the SetInstanceObject() / + // ClearInstanceObject() method, which is used by the HeapObjectsScope. Handle<WasmInstanceObject> instance_; - // Global handle to array of unwrapped imports. - Handle<FixedArray> imported_functions_; - // Map from WASM_TO_JS wrappers to unwrapped imports (indexes into - // imported_functions_). - IdentityMap<int, ZoneAllocationPolicy> unwrapped_imports_; public: CodeMap(Isolate* isolate, const WasmModule* module, const uint8_t* module_start, Zone* zone) - : zone_(zone), - module_(module), - interpreter_code_(zone), - unwrapped_imports_(isolate->heap(), ZoneAllocationPolicy(zone)) { + : zone_(zone), module_(module), interpreter_code_(zone) { if (module == nullptr) return; interpreter_code_.reserve(module->functions.size()); for (const WasmFunction& function : module->functions) { if (function.imported) { - DCHECK_EQ(function.code_start_offset, function.code_end_offset); + DCHECK(!function.code.is_set()); AddFunction(&function, nullptr, nullptr); } else { - const byte* code_start = module_start + function.code_start_offset; - const byte* code_end = module_start + function.code_end_offset; - AddFunction(&function, code_start, code_end); + AddFunction(&function, module_start + function.code.offset(), + module_start + function.code.end_offset()); } } } - ~CodeMap() { - // Destroy the global handles. - // Cast the location, not the handle, because the handle cast might access - // the object behind the handle. - GlobalHandles::Destroy(reinterpret_cast<Object**>(instance_.location())); - GlobalHandles::Destroy( - reinterpret_cast<Object**>(imported_functions_.location())); + void SetInstanceObject(Handle<WasmInstanceObject> instance) { + DCHECK(instance_.is_null()); + instance_ = instance; } + void ClearInstanceObject() { instance_ = Handle<WasmInstanceObject>::null(); } + const WasmModule* module() const { return module_; } bool has_instance() const { return !instance_.is_null(); } - Handle<WasmInstanceObject> instance() const { + WasmInstanceObject* instance() const { DCHECK(has_instance()); - return instance_; + return *instance_; } MaybeHandle<WasmInstanceObject> maybe_instance() const { - return has_instance() ? instance_ : MaybeHandle<WasmInstanceObject>(); - } - - void SetInstanceObject(WasmInstanceObject* instance) { - // Only set the instance once (otherwise we have to destroy the global - // handle first). - DCHECK(instance_.is_null()); - DCHECK_EQ(instance->module(), module_); - instance_ = instance->GetIsolate()->global_handles()->Create(instance); + return has_instance() ? handle(instance()) + : MaybeHandle<WasmInstanceObject>(); } Code* GetImportedFunction(uint32_t function_index) { - DCHECK(!instance_.is_null()); + DCHECK(has_instance()); DCHECK_GT(module_->num_imported_functions, function_index); - FixedArray* code_table = instance_->compiled_module()->ptr_to_code_table(); + FixedArray* code_table = instance()->compiled_module()->ptr_to_code_table(); return Code::cast(code_table->get(static_cast<int>(function_index))); } @@ -1052,59 +1035,16 @@ class CodeMap { code->side_table = nullptr; Preprocess(code); } - - // Returns a callable object if the imported function has a JS-compatible - // signature, or a null handle otherwise. - Handle<HeapObject> GetCallableObjectForJSImport(Isolate* isolate, - Handle<Code> code) { - DCHECK_EQ(Code::WASM_TO_JS_FUNCTION, code->kind()); - int* unwrapped_index = unwrapped_imports_.Find(code); - if (unwrapped_index) { - return handle( - HeapObject::cast(imported_functions_->get(*unwrapped_index)), - isolate); - } - Handle<HeapObject> called_obj = UnwrapWasmToJSWrapper(isolate, code); - if (!called_obj.is_null()) { - // Cache the unwrapped callable object. - if (imported_functions_.is_null()) { - // This is the first call to an imported function. Allocate the - // FixedArray to cache unwrapped objects. - constexpr int kInitialCacheSize = 8; - Handle<FixedArray> new_imported_functions = - isolate->factory()->NewFixedArray(kInitialCacheSize, TENURED); - // First entry: Number of occupied slots. - new_imported_functions->set(0, Smi::kZero); - imported_functions_ = - isolate->global_handles()->Create(*new_imported_functions); - } - int this_idx = Smi::cast(imported_functions_->get(0))->value() + 1; - if (this_idx == imported_functions_->length()) { - Handle<FixedArray> new_imported_functions = - isolate->factory()->CopyFixedArrayAndGrow(imported_functions_, - this_idx / 2, TENURED); - // Update the existing global handle: - *imported_functions_.location() = *new_imported_functions; - } - DCHECK_GT(imported_functions_->length(), this_idx); - DCHECK(imported_functions_->get(this_idx)->IsUndefined(isolate)); - imported_functions_->set(0, Smi::FromInt(this_idx)); - imported_functions_->set(this_idx, *called_obj); - unwrapped_imports_.Set(code, this_idx); - } - return called_obj; - } }; -Handle<Object> WasmValToNumber(Factory* factory, WasmVal val, - wasm::ValueType type) { +Handle<Object> WasmValueToNumber(Factory* factory, WasmValue val, + wasm::ValueType type) { switch (type) { case kWasmI32: return factory->NewNumberFromInt(val.to<int32_t>()); case kWasmI64: // wasm->js and js->wasm is illegal for i64 type. UNREACHABLE(); - return Handle<Object>::null(); case kWasmF32: return factory->NewNumber(val.to<float>()); case kWasmF64: @@ -1118,15 +1058,15 @@ Handle<Object> WasmValToNumber(Factory* factory, WasmVal val, // Convert JS value to WebAssembly, spec here: // https://github.com/WebAssembly/design/blob/master/JS.md#towebassemblyvalue -WasmVal ToWebAssemblyValue(Isolate* isolate, Handle<Object> value, - wasm::ValueType type) { +WasmValue ToWebAssemblyValue(Isolate* isolate, Handle<Object> value, + wasm::ValueType type) { switch (type) { case kWasmI32: { MaybeHandle<Object> maybe_i32 = Object::ToInt32(isolate, value); // TODO(clemensh): Handle failure here (unwind). int32_t value; CHECK(maybe_i32.ToHandleChecked()->ToInt32(&value)); - return WasmVal(value); + return WasmValue(value); } case kWasmI64: // If the signature contains i64, a type error was thrown before. @@ -1134,18 +1074,18 @@ WasmVal ToWebAssemblyValue(Isolate* isolate, Handle<Object> value, case kWasmF32: { MaybeHandle<Object> maybe_number = Object::ToNumber(value); // TODO(clemensh): Handle failure here (unwind). - return WasmVal( + return WasmValue( static_cast<float>(maybe_number.ToHandleChecked()->Number())); } case kWasmF64: { MaybeHandle<Object> maybe_number = Object::ToNumber(value); // TODO(clemensh): Handle failure here (unwind). - return WasmVal(maybe_number.ToHandleChecked()->Number()); + return WasmValue(maybe_number.ToHandleChecked()->Number()); } default: // TODO(wasm): Handle simd. UNIMPLEMENTED(); - return WasmVal(); + return WasmValue(); } } @@ -1171,7 +1111,7 @@ class ThreadImpl { WasmInterpreter::State state() { return state_; } - void InitFrame(const WasmFunction* function, WasmVal* args) { + void InitFrame(const WasmFunction* function, WasmValue* args) { DCHECK_EQ(current_activation().fp, frames_.size()); InterpreterCode* code = codemap()->GetCode(function); size_t num_params = function->sig->parameter_count(); @@ -1215,8 +1155,8 @@ class ThreadImpl { return static_cast<int>(frames_.size()); } - WasmVal GetReturnValue(uint32_t index) { - if (state_ == WasmInterpreter::TRAPPED) return WasmVal(0xdeadbeef); + WasmValue GetReturnValue(uint32_t index) { + if (state_ == WasmInterpreter::TRAPPED) return WasmValue(0xdeadbeef); DCHECK_EQ(WasmInterpreter::FINISHED, state_); Activation act = current_activation(); // Current activation must be finished. @@ -1224,12 +1164,12 @@ class ThreadImpl { return GetStackValue(act.sp + index); } - WasmVal GetStackValue(sp_t index) { + WasmValue GetStackValue(sp_t index) { DCHECK_GT(StackHeight(), index); return stack_start_[index]; } - void SetStackValue(sp_t index, WasmVal value) { + void SetStackValue(sp_t index, WasmValue value) { DCHECK_GT(StackHeight(), index); stack_start_[index] = value; } @@ -1322,9 +1262,9 @@ class ThreadImpl { CodeMap* codemap_; WasmInstance* instance_; Zone* zone_; - WasmVal* stack_start_ = nullptr; // Start of allocated stack space. - WasmVal* stack_limit_ = nullptr; // End of allocated stack space. - WasmVal* sp_ = nullptr; // Current stack pointer. + WasmValue* stack_start_ = nullptr; // Start of allocated stack space. + WasmValue* stack_limit_ = nullptr; // End of allocated stack space. + WasmValue* sp_ = nullptr; // Current stack pointer. ZoneVector<Frame> frames_; WasmInterpreter::State state_ = WasmInterpreter::STOPPED; pc_t break_pc_ = kInvalidPc; @@ -1365,11 +1305,11 @@ class ThreadImpl { pc_t InitLocals(InterpreterCode* code) { for (auto p : code->locals.type_list) { - WasmVal val; + WasmValue val; switch (p) { -#define CASE_TYPE(wasm, ctype) \ - case kWasm##wasm: \ - val = WasmVal(static_cast<ctype>(0)); \ +#define CASE_TYPE(wasm, ctype) \ + case kWasm##wasm: \ + val = WasmValue(static_cast<ctype>(0)); \ break; WASM_CTYPES(CASE_TYPE) #undef CASE_TYPE @@ -1419,14 +1359,13 @@ class ThreadImpl { } default: UNREACHABLE(); - return 0; } } bool DoReturn(Decoder* decoder, InterpreterCode** code, pc_t* pc, pc_t* limit, size_t arity) { DCHECK_GT(frames_.size(), 0); - WasmVal* sp_dest = stack_start_ + frames_.back().sp; + WasmValue* sp_dest = stack_start_ + frames_.back().sp; frames_.pop_back(); if (frames_.size() == current_activation().fp) { // A return from the last frame terminates the execution. @@ -1463,7 +1402,7 @@ class ThreadImpl { // Copies {arity} values on the top of the stack down the stack to {dest}, // dropping the values in-between. - void DoStackTransfer(WasmVal* dest, size_t arity) { + void DoStackTransfer(WasmValue* dest, size_t arity) { // before: |---------------| pop_count | arity | // ^ 0 ^ dest ^ sp_ // @@ -1490,7 +1429,7 @@ class ThreadImpl { return false; } byte* addr = instance()->mem_start + operand.offset + index; - WasmVal result(static_cast<ctype>(ReadLittleEndianValue<mtype>(addr))); + WasmValue result(static_cast<ctype>(ReadLittleEndianValue<mtype>(addr))); Push(result); len = 1 + operand.length; @@ -1501,7 +1440,7 @@ class ThreadImpl { bool ExecuteStore(Decoder* decoder, InterpreterCode* code, pc_t pc, int& len) { MemoryAccessOperand<false> operand(decoder, code->at(pc), sizeof(ctype)); - WasmVal val = Pop(); + WasmValue val = Pop(); uint32_t index = Pop().to<uint32_t>(); if (!BoundsCheck<mtype>(instance()->mem_size, operand.offset, index)) { @@ -1623,7 +1562,7 @@ class ThreadImpl { } case kExprIf: { BlockTypeOperand<false> operand(&decoder, code->at(pc)); - WasmVal cond = Pop(); + WasmValue cond = Pop(); bool is_true = cond.to<uint32_t>() != 0; if (is_true) { // fall through to the true block. @@ -1641,9 +1580,9 @@ class ThreadImpl { break; } case kExprSelect: { - WasmVal cond = Pop(); - WasmVal fval = Pop(); - WasmVal tval = Pop(); + WasmValue cond = Pop(); + WasmValue fval = Pop(); + WasmValue tval = Pop(); Push(cond.to<int32_t>() != 0 ? tval : fval); break; } @@ -1655,7 +1594,7 @@ class ThreadImpl { } case kExprBrIf: { BreakDepthOperand<false> operand(&decoder, code->at(pc)); - WasmVal cond = Pop(); + WasmValue cond = Pop(); bool is_true = cond.to<uint32_t>() != 0; if (is_true) { len = DoBreak(code, pc, operand.depth); @@ -1694,25 +1633,25 @@ class ThreadImpl { } case kExprI32Const: { ImmI32Operand<false> operand(&decoder, code->at(pc)); - Push(WasmVal(operand.value)); + Push(WasmValue(operand.value)); len = 1 + operand.length; break; } case kExprI64Const: { ImmI64Operand<false> operand(&decoder, code->at(pc)); - Push(WasmVal(operand.value)); + Push(WasmValue(operand.value)); len = 1 + operand.length; break; } case kExprF32Const: { ImmF32Operand<false> operand(&decoder, code->at(pc)); - Push(WasmVal(operand.value)); + Push(WasmValue(operand.value)); len = 1 + operand.length; break; } case kExprF64Const: { ImmF64Operand<false> operand(&decoder, code->at(pc)); - Push(WasmVal(operand.value)); + Push(WasmValue(operand.value)); len = 1 + operand.length; break; } @@ -1724,14 +1663,14 @@ class ThreadImpl { } case kExprSetLocal: { LocalIndexOperand<false> operand(&decoder, code->at(pc)); - WasmVal val = Pop(); + WasmValue val = Pop(); SetStackValue(frames_.back().sp + operand.index, val); len = 1 + operand.length; break; } case kExprTeeLocal: { LocalIndexOperand<false> operand(&decoder, code->at(pc)); - WasmVal val = Pop(); + WasmValue val = Pop(); SetStackValue(frames_.back().sp + operand.index, val); Push(val); len = 1 + operand.length; @@ -1804,11 +1743,11 @@ class ThreadImpl { GlobalIndexOperand<false> operand(&decoder, code->at(pc)); const WasmGlobal* global = &module()->globals[operand.index]; byte* ptr = instance()->globals_start + global->offset; - WasmVal val; + WasmValue val; switch (global->type) { -#define CASE_TYPE(wasm, ctype) \ - case kWasm##wasm: \ - val = WasmVal(*reinterpret_cast<ctype*>(ptr)); \ +#define CASE_TYPE(wasm, ctype) \ + case kWasm##wasm: \ + val = WasmValue(*reinterpret_cast<ctype*>(ptr)); \ break; WASM_CTYPES(CASE_TYPE) #undef CASE_TYPE @@ -1823,7 +1762,7 @@ class ThreadImpl { GlobalIndexOperand<false> operand(&decoder, code->at(pc)); const WasmGlobal* global = &module()->globals[operand.index]; byte* ptr = instance()->globals_start + global->offset; - WasmVal val = Pop(); + WasmValue val = Pop(); switch (global->type) { #define CASE_TYPE(wasm, ctype) \ case kWasm##wasm: \ @@ -1888,7 +1827,7 @@ class ThreadImpl { /* TODO(titzer): alignment for asmjs load mem? */ \ result = static_cast<ctype>(*reinterpret_cast<mtype*>(addr)); \ } \ - Push(WasmVal(result)); \ + Push(WasmValue(result)); \ break; \ } ASMJS_LOAD_CASE(I32AsmjsLoadMem8S, int32_t, int8_t, 0); @@ -1904,7 +1843,7 @@ class ThreadImpl { #define ASMJS_STORE_CASE(name, ctype, mtype) \ case kExpr##name: { \ - WasmVal val = Pop(); \ + WasmValue val = Pop(); \ uint32_t index = Pop().to<uint32_t>(); \ if (BoundsCheck<mtype>(instance()->mem_size, 0, index)) { \ byte* addr = instance()->mem_start + index; \ @@ -1924,15 +1863,15 @@ class ThreadImpl { case kExprGrowMemory: { MemoryIndexOperand<false> operand(&decoder, code->at(pc)); uint32_t delta_pages = Pop().to<uint32_t>(); - Push(WasmVal(ExecuteGrowMemory( + Push(WasmValue(ExecuteGrowMemory( delta_pages, codemap_->maybe_instance(), instance()))); len = 1 + operand.length; break; } case kExprMemorySize: { MemoryIndexOperand<false> operand(&decoder, code->at(pc)); - Push(WasmVal(static_cast<uint32_t>(instance()->mem_size / - WasmModule::kPageSize))); + Push(WasmValue(static_cast<uint32_t>(instance()->mem_size / + WasmModule::kPageSize))); len = 1 + operand.length; break; } @@ -1940,37 +1879,37 @@ class ThreadImpl { // specially to guarantee that the quiet bit of a NaN is preserved on // ia32 by the reinterpret casts. case kExprI32ReinterpretF32: { - WasmVal val = Pop(); - Push(WasmVal(ExecuteI32ReinterpretF32(val))); + WasmValue val = Pop(); + Push(WasmValue(ExecuteI32ReinterpretF32(val))); possible_nondeterminism_ |= std::isnan(val.to<float>()); break; } case kExprI64ReinterpretF64: { - WasmVal val = Pop(); - Push(WasmVal(ExecuteI64ReinterpretF64(val))); + WasmValue val = Pop(); + Push(WasmValue(ExecuteI64ReinterpretF64(val))); possible_nondeterminism_ |= std::isnan(val.to<double>()); break; } -#define EXECUTE_SIMPLE_BINOP(name, ctype, op) \ - case kExpr##name: { \ - WasmVal rval = Pop(); \ - WasmVal lval = Pop(); \ - WasmVal result(lval.to<ctype>() op rval.to<ctype>()); \ - Push(result); \ - break; \ +#define EXECUTE_SIMPLE_BINOP(name, ctype, op) \ + case kExpr##name: { \ + WasmValue rval = Pop(); \ + WasmValue lval = Pop(); \ + WasmValue result(lval.to<ctype>() op rval.to<ctype>()); \ + Push(result); \ + break; \ } FOREACH_SIMPLE_BINOP(EXECUTE_SIMPLE_BINOP) #undef EXECUTE_SIMPLE_BINOP -#define EXECUTE_OTHER_BINOP(name, ctype) \ - case kExpr##name: { \ - TrapReason trap = kTrapCount; \ - volatile ctype rval = Pop().to<ctype>(); \ - volatile ctype lval = Pop().to<ctype>(); \ - WasmVal result(Execute##name(lval, rval, &trap)); \ - if (trap != kTrapCount) return DoTrap(trap, pc); \ - Push(result); \ - break; \ +#define EXECUTE_OTHER_BINOP(name, ctype) \ + case kExpr##name: { \ + TrapReason trap = kTrapCount; \ + volatile ctype rval = Pop().to<ctype>(); \ + volatile ctype lval = Pop().to<ctype>(); \ + WasmValue result(Execute##name(lval, rval, &trap)); \ + if (trap != kTrapCount) return DoTrap(trap, pc); \ + Push(result); \ + break; \ } FOREACH_OTHER_BINOP(EXECUTE_OTHER_BINOP) #undef EXECUTE_OTHER_BINOP @@ -1981,7 +1920,7 @@ class ThreadImpl { TrapReason trap = kTrapCount; volatile float rval = Pop().to<float>(); volatile float lval = Pop().to<float>(); - WasmVal result(ExecuteF32CopySign(lval, rval, &trap)); + WasmValue result(ExecuteF32CopySign(lval, rval, &trap)); Push(result); possible_nondeterminism_ |= std::isnan(rval); break; @@ -1992,7 +1931,7 @@ class ThreadImpl { TrapReason trap = kTrapCount; volatile double rval = Pop().to<double>(); volatile double lval = Pop().to<double>(); - WasmVal result(ExecuteF64CopySign(lval, rval, &trap)); + WasmValue result(ExecuteF64CopySign(lval, rval, &trap)); Push(result); possible_nondeterminism_ |= std::isnan(rval); break; @@ -2001,7 +1940,7 @@ class ThreadImpl { case kExpr##name: { \ TrapReason trap = kTrapCount; \ volatile ctype val = Pop().to<ctype>(); \ - WasmVal result(Execute##name(val, &trap)); \ + WasmValue result(Execute##name(val, &trap)); \ if (trap != kTrapCount) return DoTrap(trap, pc); \ Push(result); \ break; \ @@ -2037,7 +1976,7 @@ class ThreadImpl { CommitPc(pc); } - WasmVal Pop() { + WasmValue Pop() { DCHECK_GT(frames_.size(), 0); DCHECK_GT(StackHeight(), frames_.back().llimit()); // can't pop into locals return *--sp_; @@ -2051,22 +1990,22 @@ class ThreadImpl { sp_ -= n; } - WasmVal PopArity(size_t arity) { - if (arity == 0) return WasmVal(); + WasmValue PopArity(size_t arity) { + if (arity == 0) return WasmValue(); CHECK_EQ(1, arity); return Pop(); } - void Push(WasmVal val) { - DCHECK_NE(kWasmStmt, val.type); + void Push(WasmValue val) { + DCHECK_NE(kWasmStmt, val.type()); DCHECK_LE(1, stack_limit_ - sp_); *sp_++ = val; } - void Push(WasmVal* vals, size_t arity) { + void Push(WasmValue* vals, size_t arity) { DCHECK_LE(arity, stack_limit_ - sp_); - for (WasmVal *val = vals, *end = vals + arity; val != end; ++val) { - DCHECK_NE(kWasmStmt, val->type); + for (WasmValue *val = vals, *end = vals + arity; val != end; ++val) { + DCHECK_NE(kWasmStmt, val->type()); } memcpy(sp_, vals, arity * sizeof(*sp_)); sp_ += arity; @@ -2078,7 +2017,7 @@ class ThreadImpl { size_t requested_size = base::bits::RoundUpToPowerOfTwo64((sp_ - stack_start_) + size); size_t new_size = Max(size_t{8}, Max(2 * old_size, requested_size)); - WasmVal* new_stack = zone_->NewArray<WasmVal>(new_size); + WasmValue* new_stack = zone_->NewArray<WasmValue>(new_size); memcpy(new_stack, stack_start_, old_size * sizeof(*sp_)); sp_ = new_stack + (sp_ - stack_start_); stack_start_ = new_stack; @@ -2087,14 +2026,6 @@ class ThreadImpl { sp_t StackHeight() { return sp_ - stack_start_; } - void TraceStack(const char* phase, pc_t pc) { - if (FLAG_trace_wasm_interpreter) { - PrintF("%s @%zu", phase, pc); - UNIMPLEMENTED(); - PrintF("\n"); - } - } - void TraceValueStack() { #ifdef DEBUG if (!FLAG_trace_wasm_interpreter) return; @@ -2109,8 +2040,8 @@ class ThreadImpl { PrintF(" l%zu:", i); else PrintF(" s%zu:", i); - WasmVal val = GetStackValue(i); - switch (val.type) { + WasmValue val = GetStackValue(i); + switch (val.type()) { case kWasmI32: PrintF("i32:%d", val.to<int32_t>()); break; @@ -2151,18 +2082,17 @@ class ThreadImpl { DCHECK_EQ(2, deopt_data->length()); WasmInstanceObject* target_instance = WasmInstanceObject::cast(WeakCell::cast(deopt_data->get(0))->value()); - if (target_instance != *codemap()->instance()) { + if (target_instance != codemap()->instance()) { // TODO(wasm): Implement calling functions of other instances/modules. UNIMPLEMENTED(); } - int target_func_idx = Smi::cast(deopt_data->get(1))->value(); + int target_func_idx = Smi::ToInt(deopt_data->get(1)); DCHECK_LE(0, target_func_idx); return {ExternalCallResult::INTERNAL, codemap()->GetCode(target_func_idx)}; } - Handle<HeapObject> target = - codemap()->GetCallableObjectForJSImport(isolate, code); + Handle<HeapObject> target = UnwrapWasmToJSWrapper(isolate, code); if (target.is_null()) { isolate->Throw(*isolate->factory()->NewTypeError( @@ -2181,10 +2111,10 @@ class ThreadImpl { // Get all arguments as JS values. std::vector<Handle<Object>> args; args.reserve(num_args); - WasmVal* wasm_args = sp_ - num_args; + WasmValue* wasm_args = sp_ - num_args; for (int i = 0; i < num_args; ++i) { - args.push_back(WasmValToNumber(isolate->factory(), wasm_args[i], - signature->GetParam(i))); + args.push_back(WasmValueToNumber(isolate->factory(), wasm_args[i], + signature->GetParam(i))); } // The receiver is the global proxy if in sloppy mode (default), undefined @@ -2275,8 +2205,7 @@ class ThreadImpl { if (entry_index >= static_cast<uint32_t>(sig_table->length())) { return {ExternalCallResult::INVALID_FUNC}; } - int found_sig = - Smi::cast(sig_table->get(static_cast<int>(entry_index)))->value(); + int found_sig = Smi::ToInt(sig_table->get(static_cast<int>(entry_index))); if (static_cast<uint32_t>(found_sig) != canonical_sig_index) { return {ExternalCallResult::SIGNATURE_MISMATCH}; } @@ -2341,13 +2270,13 @@ class InterpretedFrameImpl { return static_cast<int>(frame_size) - GetLocalCount(); } - WasmVal GetLocalValue(int index) const { + WasmValue GetLocalValue(int index) const { DCHECK_LE(0, index); DCHECK_GT(GetLocalCount(), index); return thread_->GetStackValue(static_cast<int>(frame()->sp) + index); } - WasmVal GetStackValue(int index) const { + WasmValue GetStackValue(int index) const { DCHECK_LE(0, index); // Index must be within the number of stack values of this frame. DCHECK_GT(GetStackHeight(), index); @@ -2384,6 +2313,37 @@ const InterpretedFrameImpl* ToImpl(const InterpretedFrame* frame) { return reinterpret_cast<const InterpretedFrameImpl*>(frame); } +//============================================================================ +// Implementation details of the heap objects scope. +//============================================================================ +class HeapObjectsScopeImpl { + public: + HeapObjectsScopeImpl(CodeMap* codemap, Handle<WasmInstanceObject> instance) + : codemap_(codemap), needs_reset(!codemap_->has_instance()) { + if (needs_reset) { + instance_ = handle(*instance); + codemap_->SetInstanceObject(instance_); + } else { + DCHECK_EQ(*instance, codemap_->instance()); + return; + } + } + + ~HeapObjectsScopeImpl() { + if (!needs_reset) return; + DCHECK_EQ(*instance_, codemap_->instance()); + codemap_->ClearInstanceObject(); + // Clear the handle, such that anyone who accidentally copied them will + // notice. + *instance_.location() = nullptr; + } + + private: + CodeMap* codemap_; + Handle<WasmInstanceObject> instance_; + bool needs_reset; +}; + } // namespace //============================================================================ @@ -2396,7 +2356,7 @@ WasmInterpreter::State WasmInterpreter::Thread::state() { return ToImpl(this)->state(); } void WasmInterpreter::Thread::InitFrame(const WasmFunction* function, - WasmVal* args) { + WasmValue* args) { ToImpl(this)->InitFrame(function, args); } WasmInterpreter::State WasmInterpreter::Thread::Run(int num_steps) { @@ -2420,7 +2380,7 @@ std::unique_ptr<InterpretedFrame> WasmInterpreter::Thread::GetFrame(int index) { return std::unique_ptr<InterpretedFrame>( ToFrame(new InterpretedFrameImpl(ToImpl(this), index))); } -WasmVal WasmInterpreter::Thread::GetReturnValue(int index) { +WasmValue WasmInterpreter::Thread::GetReturnValue(int index) { return ToImpl(this)->GetReturnValue(index); } TrapReason WasmInterpreter::Thread::GetTrapReason() { @@ -2524,10 +2484,6 @@ bool WasmInterpreter::SetTracing(const WasmFunction* function, bool enabled) { return false; } -void WasmInterpreter::SetInstanceObject(WasmInstanceObject* instance) { - internals_->codemap_.SetInstanceObject(instance); -} - int WasmInterpreter::GetThreadCount() { return 1; // only one thread for now. } @@ -2541,12 +2497,12 @@ size_t WasmInterpreter::GetMemorySize() { return internals_->instance_->mem_size; } -WasmVal WasmInterpreter::ReadMemory(size_t offset) { +WasmValue WasmInterpreter::ReadMemory(size_t offset) { UNIMPLEMENTED(); - return WasmVal(); + return WasmValue(); } -void WasmInterpreter::WriteMemory(size_t offset, WasmVal val) { +void WasmInterpreter::WriteMemory(size_t offset, WasmValue val) { UNIMPLEMENTED(); } @@ -2570,7 +2526,7 @@ ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting( // Create some dummy structures, to avoid special-casing the implementation // just for testing. FunctionSig sig(0, 0, nullptr); - WasmFunction function{&sig, 0, 0, 0, 0, 0, 0, false, false}; + WasmFunction function{&sig, 0, 0, {0, 0}, {0, 0}, false, false}; InterpreterCode code{ &function, BodyLocalDecls(zone), start, end, nullptr, nullptr, nullptr}; @@ -2595,13 +2551,26 @@ int InterpretedFrame::GetLocalCount() const { int InterpretedFrame::GetStackHeight() const { return ToImpl(this)->GetStackHeight(); } -WasmVal InterpretedFrame::GetLocalValue(int index) const { +WasmValue InterpretedFrame::GetLocalValue(int index) const { return ToImpl(this)->GetLocalValue(index); } -WasmVal InterpretedFrame::GetStackValue(int index) const { +WasmValue InterpretedFrame::GetStackValue(int index) const { return ToImpl(this)->GetStackValue(index); } +//============================================================================ +// Public API of the heap objects scope. +//============================================================================ +WasmInterpreter::HeapObjectsScope::HeapObjectsScope( + WasmInterpreter* interpreter, Handle<WasmInstanceObject> instance) { + static_assert(sizeof(data) == sizeof(HeapObjectsScopeImpl), "Size mismatch"); + new (data) HeapObjectsScopeImpl(&interpreter->internals_->codemap_, instance); +} + +WasmInterpreter::HeapObjectsScope::~HeapObjectsScope() { + reinterpret_cast<HeapObjectsScopeImpl*>(data)->~HeapObjectsScopeImpl(); +} + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/deps/v8/src/wasm/wasm-interpreter.h b/deps/v8/src/wasm/wasm-interpreter.h index 1259f09ff2..b01a088e98 100644 --- a/deps/v8/src/wasm/wasm-interpreter.h +++ b/deps/v8/src/wasm/wasm-interpreter.h @@ -6,6 +6,7 @@ #define V8_WASM_INTERPRETER_H_ #include "src/wasm/wasm-opcodes.h" +#include "src/wasm/wasm-value.h" #include "src/zone/zone-containers.h" namespace v8 { @@ -43,67 +44,6 @@ struct ControlTransferEntry { using ControlTransferMap = ZoneMap<pc_t, ControlTransferEntry>; -// Macro for defining union members. -#define FOREACH_UNION_MEMBER(V) \ - V(i32, kWasmI32, int32_t) \ - V(u32, kWasmI32, uint32_t) \ - V(i64, kWasmI64, int64_t) \ - V(u64, kWasmI64, uint64_t) \ - V(f32, kWasmF32, float) \ - V(f64, kWasmF64, double) - -// Representation of values within the interpreter. -struct WasmVal { - ValueType type; - union { -#define DECLARE_FIELD(field, localtype, ctype) ctype field; - FOREACH_UNION_MEMBER(DECLARE_FIELD) -#undef DECLARE_FIELD - } val; - - WasmVal() : type(kWasmStmt) {} - -#define DECLARE_CONSTRUCTOR(field, localtype, ctype) \ - explicit WasmVal(ctype v) : type(localtype) { val.field = v; } - FOREACH_UNION_MEMBER(DECLARE_CONSTRUCTOR) -#undef DECLARE_CONSTRUCTOR - - bool operator==(const WasmVal& other) const { - if (type != other.type) return false; -#define CHECK_VAL_EQ(field, localtype, ctype) \ - if (type == localtype) { \ - return val.field == other.val.field; \ - } - FOREACH_UNION_MEMBER(CHECK_VAL_EQ) -#undef CHECK_VAL_EQ - UNREACHABLE(); - return false; - } - - template <typename T> - inline T to() const { - UNREACHABLE(); - } - - template <typename T> - inline T to_unchecked() const { - UNREACHABLE(); - } -}; - -#define DECLARE_CAST(field, localtype, ctype) \ - template <> \ - inline ctype WasmVal::to_unchecked() const { \ - return val.field; \ - } \ - template <> \ - inline ctype WasmVal::to() const { \ - CHECK_EQ(localtype, type); \ - return val.field; \ - } -FOREACH_UNION_MEMBER(DECLARE_CAST) -#undef DECLARE_CAST - // Representation of frames within the interpreter. // // Layout of a frame: @@ -127,8 +67,8 @@ class InterpretedFrame { int GetParameterCount() const; int GetLocalCount() const; int GetStackHeight() const; - WasmVal GetLocalValue(int index) const; - WasmVal GetStackValue(int index) const; + WasmValue GetLocalValue(int index) const; + WasmValue GetStackValue(int index) const; private: friend class WasmInterpreter; @@ -138,9 +78,22 @@ class InterpretedFrame { DISALLOW_COPY_AND_ASSIGN(InterpretedFrame); }; -// An interpreter capable of executing WASM. +// An interpreter capable of executing WebAssembly. class V8_EXPORT_PRIVATE WasmInterpreter { public: + // Open a HeapObjectsScope before running any code in the interpreter which + // needs access to the instance object or needs to call to JS functions. + class V8_EXPORT_PRIVATE HeapObjectsScope { + public: + HeapObjectsScope(WasmInterpreter* interpreter, + Handle<WasmInstanceObject> instance); + ~HeapObjectsScope(); + + private: + char data[3 * sizeof(void*)]; // must match sizeof(HeapObjectsScopeImpl). + DISALLOW_COPY_AND_ASSIGN(HeapObjectsScope); + }; + // State machine for a Thread: // +---------Run()/Step()--------+ // V | @@ -170,7 +123,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter { // Execution control. State state(); - void InitFrame(const WasmFunction* function, WasmVal* args); + void InitFrame(const WasmFunction* function, WasmValue* args); // Pass -1 as num_steps to run till completion, pause or breakpoint. State Run(int num_steps = -1); State Step() { return Run(1); } @@ -186,7 +139,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter { int GetFrameCount(); // The InterpretedFrame is only valid as long as the Thread is paused. std::unique_ptr<InterpretedFrame> GetFrame(int index); - WasmVal GetReturnValue(int index = 0); + WasmValue GetReturnValue(int index = 0); TrapReason GetTrapReason(); // Returns true if the thread executed an instruction which may produce @@ -237,13 +190,6 @@ class V8_EXPORT_PRIVATE WasmInterpreter { // Enable or disable tracing for {function}. Return the previous state. bool SetTracing(const WasmFunction* function, bool enabled); - // Set the associated wasm instance object. - // If the instance object has been set, some tables stored inside it are used - // instead of the tables stored in the WasmModule struct. This allows to call - // back and forth between the interpreter and outside code (JS or wasm - // compiled) without repeatedly copying information. - void SetInstanceObject(WasmInstanceObject*); - //========================================================================== // Thread iteration and inspection. //========================================================================== @@ -254,8 +200,8 @@ class V8_EXPORT_PRIVATE WasmInterpreter { // Memory access. //========================================================================== size_t GetMemorySize(); - WasmVal ReadMemory(size_t offset); - void WriteMemory(size_t offset, WasmVal val); + WasmValue ReadMemory(size_t offset); + void WriteMemory(size_t offset, WasmValue val); // Update the memory region, e.g. after external GrowMemory. void UpdateMemory(byte* mem_start, uint32_t mem_size); diff --git a/deps/v8/src/wasm/wasm-js.cc b/deps/v8/src/wasm/wasm-js.cc index 3dde623594..5f775f0d35 100644 --- a/deps/v8/src/wasm/wasm-js.cc +++ b/deps/v8/src/wasm/wasm-js.cc @@ -40,19 +40,34 @@ namespace { } \ } while (false) -// TODO(wasm): move brand check to the respective types, and don't throw -// in it, rather, use a provided ErrorThrower, or let caller handle it. -static bool HasBrand(i::Handle<i::Object> value, i::Handle<i::Symbol> sym) { - if (!value->IsJSObject()) return false; - i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value); - Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym); - return has_brand.FromMaybe(false); -} - -static bool BrandCheck(i::Handle<i::Object> value, i::Handle<i::Symbol> sym, - ErrorThrower* thrower, const char* msg) { - return HasBrand(value, sym) ? true : (thrower->TypeError("%s", msg), false); -} +// Like an ErrorThrower, but turns all pending exceptions into scheduled +// exceptions when going out of scope. Use this in API methods. +// Note that pending exceptions are not necessarily created by the ErrorThrower, +// but e.g. by the wasm start function. There might also be a scheduled +// exception, created by another API call (e.g. v8::Object::Get). But there +// should never be both pending and scheduled exceptions. +class ScheduledErrorThrower : public ErrorThrower { + public: + ScheduledErrorThrower(v8::Isolate* isolate, const char* context) + : ScheduledErrorThrower(reinterpret_cast<i::Isolate*>(isolate), context) { + } + ScheduledErrorThrower(i::Isolate* isolate, const char* context) + : ErrorThrower(isolate, context) {} + ~ScheduledErrorThrower() { + // There should never be both a pending and a scheduled exception. + DCHECK(!isolate()->has_scheduled_exception() || + !isolate()->has_pending_exception()); + // Don't throw another error if there is already a scheduled error. + if (isolate()->has_scheduled_exception()) { + Reset(); + } else if (isolate()->has_pending_exception()) { + Reset(); + isolate()->OptionalRescheduleException(false); + } else if (error()) { + isolate()->ScheduleThrow(*Reify()); + } + } +}; i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) { return isolate->factory()->NewStringFromAsciiChecked(str); @@ -63,17 +78,14 @@ Local<String> v8_str(Isolate* isolate, const char* str) { i::MaybeHandle<i::WasmModuleObject> GetFirstArgumentAsModule( const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) { - v8::Isolate* isolate = args.GetIsolate(); if (args.Length() < 1) { thrower->TypeError("Argument 0 must be a WebAssembly.Module"); return {}; } - Local<Context> context = isolate->GetCurrentContext(); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); - if (!BrandCheck(Utils::OpenHandle(*args[0]), - i::handle(i_context->wasm_module_sym()), thrower, - "Argument 0 must be a WebAssembly.Module")) { + i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]); + if (!arg0->IsWasmModuleObject()) { + thrower->TypeError("Argument 0 must be a WebAssembly.Module"); return {}; } @@ -121,7 +133,6 @@ i::wasm::ModuleWireBytes GetFirstArgumentAsBytes( i::wasm::kV8MaxWasmModuleSize, length); } if (thrower->error()) return i::wasm::ModuleWireBytes(nullptr, nullptr); - // TODO(titzer): use the handle as well? return i::wasm::ModuleWireBytes(start, start + length); } @@ -137,32 +148,13 @@ i::MaybeHandle<i::JSReceiver> GetValueAsImports(Local<Value> arg, return i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj)); } -void RejectResponseAPI(const v8::FunctionCallbackInfo<v8::Value>& args, - ErrorThrower* thrower) { - v8::Isolate* isolate = args.GetIsolate(); - i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); - - HandleScope scope(isolate); - Local<Context> context = isolate->GetCurrentContext(); - - ASSIGN(Promise::Resolver, resolver, Promise::Resolver::New(context)); - Local<Promise> module_promise = resolver->GetPromise(); - args.GetReturnValue().Set(module_promise); - thrower->TypeError( - "Argument 0 must be provided and must be a Response or Response promise"); - auto maybe = resolver->Reject(context, Utils::ToLocal(thrower->Reify())); - CHECK_IMPLIES(!maybe.FromMaybe(false), i_isolate->has_scheduled_exception()); -} - void WebAssemblyCompileStreaming( const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); MicrotasksScope runs_microtasks(isolate, MicrotasksScope::kRunMicrotasks); - if (!i_isolate->wasm_compile_callback()(args)) { - ErrorThrower thrower(i_isolate, "WebAssembly.compileStreaming()"); - RejectResponseAPI(args, &thrower); - } + DCHECK_NOT_NULL(i_isolate->wasm_compile_streaming_callback()); + i_isolate->wasm_compile_streaming_callback()(args); } // WebAssembly.compile(bytes) -> Promise @@ -170,10 +162,9 @@ void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); MicrotasksScope runs_microtasks(isolate, MicrotasksScope::kRunMicrotasks); - if (i_isolate->wasm_compile_callback()(args)) return; HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.compile()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.compile()"); Local<Context> context = isolate->GetCurrentContext(); ASSIGN(Promise::Resolver, resolver, Promise::Resolver::New(context)); @@ -196,7 +187,7 @@ void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.validate()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.validate()"); auto bytes = GetFirstArgumentAsBytes(args, &thrower); @@ -217,7 +208,7 @@ void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { if (i_isolate->wasm_module_callback()(args)) return; HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Module()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module()"); auto bytes = GetFirstArgumentAsBytes(args, &thrower); @@ -237,7 +228,7 @@ void WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value>& args) { HandleScope scope(args.GetIsolate()); v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Module.imports()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module.imports()"); auto maybe_module = GetFirstArgumentAsModule(args, &thrower); if (thrower.error()) return; @@ -250,7 +241,7 @@ void WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value>& args) { HandleScope scope(args.GetIsolate()); v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Module.exports()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module.exports()"); auto maybe_module = GetFirstArgumentAsModule(args, &thrower); if (thrower.error()) return; @@ -264,7 +255,8 @@ void WebAssemblyModuleCustomSections( HandleScope scope(args.GetIsolate()); v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Module.customSections()"); + ScheduledErrorThrower thrower(i_isolate, + "WebAssembly.Module.customSections()"); auto maybe_module = GetFirstArgumentAsModule(args, &thrower); if (thrower.error()) return; @@ -292,24 +284,23 @@ MaybeLocal<Value> WebAssemblyInstantiateImpl(Isolate* isolate, Local<Value> ffi) { i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly Instantiation"); - i::MaybeHandle<i::JSReceiver> maybe_imports = - GetValueAsImports(ffi, &thrower); - if (thrower.error()) return {}; - - i::Handle<i::WasmModuleObject> module_obj = - i::Handle<i::WasmModuleObject>::cast( - Utils::OpenHandle(Object::Cast(*module))); - i::MaybeHandle<i::Object> instance_object = - i::wasm::SyncInstantiate(i_isolate, &thrower, module_obj, maybe_imports, - i::MaybeHandle<i::JSArrayBuffer>()); - - if (instance_object.is_null()) { - // TODO(wasm): this *should* mean there's an error to throw, but - // we exit sometimes the instantiation pipeline without throwing. - // v8:6232. - return {}; + i::MaybeHandle<i::Object> instance_object; + { + ScheduledErrorThrower thrower(i_isolate, "WebAssembly Instantiation"); + i::MaybeHandle<i::JSReceiver> maybe_imports = + GetValueAsImports(ffi, &thrower); + if (thrower.error()) return {}; + + i::Handle<i::WasmModuleObject> module_obj = + i::Handle<i::WasmModuleObject>::cast( + Utils::OpenHandle(Object::Cast(*module))); + instance_object = + i::wasm::SyncInstantiate(i_isolate, &thrower, module_obj, maybe_imports, + i::MaybeHandle<i::JSArrayBuffer>()); } + + DCHECK_EQ(instance_object.is_null(), i_isolate->has_scheduled_exception()); + if (instance_object.is_null()) return {}; return Utils::ToLocal(instance_object.ToHandleChecked()); } @@ -375,9 +366,9 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) { i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); if (i_isolate->wasm_instance_callback()(args)) return; - ErrorThrower thrower(i_isolate, "WebAssembly.Instance()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance()"); - auto maybe_module = GetFirstArgumentAsModule(args, &thrower); + GetFirstArgumentAsModule(args, &thrower); if (thrower.error()) return; // If args.Length < 2, this will be undefined - see FunctionCallbackInfo. @@ -424,14 +415,12 @@ void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); MicrotasksScope runs_microtasks(isolate, MicrotasksScope::kRunMicrotasks); - if (i_isolate->wasm_instantiate_callback()(args)) return; - ErrorThrower thrower(i_isolate, "WebAssembly.instantiate()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.instantiate()"); HandleScope scope(isolate); Local<Context> context = isolate->GetCurrentContext(); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); ASSIGN(Promise::Resolver, resolver, Promise::Resolver::New(context)); Local<Promise> module_promise = resolver->GetPromise(); @@ -459,7 +448,7 @@ void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) { } FunctionCallback instantiator = nullptr; - if (HasBrand(first_arg, i::Handle<i::Symbol>(i_context->wasm_module_sym()))) { + if (first_arg->IsWasmModuleObject()) { module_promise = resolver->GetPromise(); if (!resolver->Resolve(context, first_arg_value).IsJust()) return; instantiator = WebAssemblyInstantiateImplCallback; @@ -483,7 +472,7 @@ void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) { bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower, Local<Context> context, Local<v8::Object> object, - Local<String> property, int* result, + Local<String> property, int64_t* result, int64_t lower_bound, uint64_t upper_bound) { v8::MaybeLocal<v8::Value> maybe = object->Get(context, property); v8::Local<v8::Value> value; @@ -513,7 +502,7 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Module()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module()"); if (args.Length() < 1 || !args[0]->IsObject()) { thrower.TypeError("Argument 0 must be a table descriptor"); return; @@ -536,14 +525,14 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) { } } // The descriptor's 'initial'. - int initial = 0; + int64_t initial = 0; if (!GetIntegerProperty(isolate, &thrower, context, descriptor, v8_str(isolate, "initial"), &initial, 0, i::FLAG_wasm_max_table_size)) { return; } // The descriptor's 'maximum'. - int maximum = -1; + int64_t maximum = -1; Local<String> maximum_key = v8_str(isolate, "maximum"); Maybe<bool> has_maximum = descriptor->Has(context, maximum_key); @@ -556,8 +545,8 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) { } i::Handle<i::FixedArray> fixed_array; - i::Handle<i::JSObject> table_obj = - i::WasmTableObject::New(i_isolate, initial, maximum, &fixed_array); + i::Handle<i::JSObject> table_obj = i::WasmTableObject::New( + i_isolate, static_cast<uint32_t>(initial), maximum, &fixed_array); v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); return_value.Set(Utils::ToLocal(table_obj)); } @@ -566,7 +555,7 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Memory()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory()"); if (args.Length() < 1 || !args[0]->IsObject()) { thrower.TypeError("Argument 0 must be a memory descriptor"); return; @@ -574,14 +563,14 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) { Local<Context> context = isolate->GetCurrentContext(); Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked(); // The descriptor's 'initial'. - int initial = 0; + int64_t initial = 0; if (!GetIntegerProperty(isolate, &thrower, context, descriptor, v8_str(isolate, "initial"), &initial, 0, i::FLAG_wasm_max_mem_pages)) { return; } // The descriptor's 'maximum'. - int maximum = -1; + int64_t maximum = -1; Local<String> maximum_key = v8_str(isolate, "maximum"); Maybe<bool> has_maximum = descriptor->Has(context, maximum_key); @@ -592,34 +581,71 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) { return; } } + + bool is_shared_memory = false; + if (i::FLAG_experimental_wasm_threads) { + // Shared property of descriptor + Local<String> shared_key = v8_str(isolate, "shared"); + Maybe<bool> has_shared = descriptor->Has(context, shared_key); + if (!has_shared.IsNothing() && has_shared.FromJust()) { + v8::MaybeLocal<v8::Value> maybe = descriptor->Get(context, shared_key); + v8::Local<v8::Value> value; + if (maybe.ToLocal(&value)) { + if (!value->BooleanValue(context).To(&is_shared_memory)) return; + } + } + // Throw TypeError if shared is true, and the descriptor has no "maximum" + if (is_shared_memory && maximum == -1) { + thrower.TypeError( + "If shared is true, maximum property should be defined."); + } + } + size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) * static_cast<size_t>(initial); - i::Handle<i::JSArrayBuffer> buffer = - i::wasm::NewArrayBuffer(i_isolate, size, i::FLAG_wasm_guard_pages); + i::Handle<i::JSArrayBuffer> buffer = i::wasm::NewArrayBuffer( + i_isolate, size, i::FLAG_wasm_guard_pages, + is_shared_memory ? i::SharedFlag::kShared : i::SharedFlag::kNotShared); if (buffer.is_null()) { thrower.RangeError("could not allocate memory"); return; } - i::Handle<i::JSObject> memory_obj = - i::WasmMemoryObject::New(i_isolate, buffer, maximum); + if (buffer->is_shared()) { + Maybe<bool> result = + buffer->SetIntegrityLevel(buffer, i::FROZEN, i::Object::DONT_THROW); + if (!result.FromJust()) { + thrower.TypeError( + "Status of setting SetIntegrityLevel of buffer is false."); + } + } + i::Handle<i::JSObject> memory_obj = i::WasmMemoryObject::New( + i_isolate, buffer, static_cast<int32_t>(maximum)); args.GetReturnValue().Set(Utils::ToLocal(memory_obj)); } +#define NAME_OF_WasmMemoryObject "WebAssembly.Memory" +#define NAME_OF_WasmModuleObject "WebAssembly.Module" +#define NAME_OF_WasmInstanceObject "WebAssembly.Instance" +#define NAME_OF_WasmTableObject "WebAssembly.Table" + +#define EXTRACT_THIS(var, WasmType) \ + i::Handle<i::WasmType> var; \ + { \ + i::Handle<i::Object> this_arg = Utils::OpenHandle(*args.This()); \ + if (!this_arg->Is##WasmType()) { \ + thrower.TypeError("Receiver is not a " NAME_OF_##WasmType); \ + return; \ + } \ + var = i::Handle<i::WasmType>::cast(this_arg); \ + } + void WebAssemblyTableGetLength( const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Table.length()"); - Local<Context> context = isolate->GetCurrentContext(); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); - if (!BrandCheck(Utils::OpenHandle(*args.This()), - i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower, - "Receiver is not a WebAssembly.Table")) { - return; - } - auto receiver = - i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.length()"); + EXTRACT_THIS(receiver, WasmTableObject); args.GetReturnValue().Set( v8::Number::New(isolate, receiver->current_length())); } @@ -629,17 +655,10 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Table.grow()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.grow()"); Local<Context> context = isolate->GetCurrentContext(); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); - if (!BrandCheck(Utils::OpenHandle(*args.This()), - i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower, - "Receiver is not a WebAssembly.Table")) { - return; - } + EXTRACT_THIS(receiver, WasmTableObject); - auto receiver = - i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); i::Handle<i::FixedArray> old_array(receiver->functions(), i_isolate); int old_size = old_array->length(); int64_t new_size64 = 0; @@ -648,9 +667,8 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { } new_size64 += old_size; - int64_t max_size64 = receiver->maximum_length(); - if (max_size64 < 0 || - max_size64 > static_cast<int64_t>(i::FLAG_wasm_max_table_size)) { + int64_t max_size64 = receiver->maximum_length()->Number(); + if (max_size64 < 0 || max_size64 > i::FLAG_wasm_max_table_size) { max_size64 = i::FLAG_wasm_max_table_size; } @@ -682,17 +700,9 @@ void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Table.get()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.get()"); Local<Context> context = isolate->GetCurrentContext(); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); - if (!BrandCheck(Utils::OpenHandle(*args.This()), - i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower, - "Receiver is not a WebAssembly.Table")) { - return; - } - - auto receiver = - i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); + EXTRACT_THIS(receiver, WasmTableObject); i::Handle<i::FixedArray> array(receiver->functions(), i_isolate); int i = 0; if (args.Length() > 0 && !args[0]->Int32Value(context).To(&i)) return; @@ -711,18 +721,20 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Table.set()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.set()"); Local<Context> context = isolate->GetCurrentContext(); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); - if (!BrandCheck(Utils::OpenHandle(*args.This()), - i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower, - "Receiver is not a WebAssembly.Table")) { - return; - } + EXTRACT_THIS(receiver, WasmTableObject); + if (args.Length() < 2) { thrower.TypeError("Argument 1 must be null or a function"); return; } + + // Parameter 0. + int32_t index; + if (!args[0]->Int32Value(context).To(&index)) return; + + // Parameter 1. i::Handle<i::Object> value = Utils::OpenHandle(*args[1]); if (!value->IsNull(i_isolate) && (!value->IsJSFunction() || @@ -732,27 +744,10 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) { return; } - auto receiver = - i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This())); - i::Handle<i::FixedArray> array(receiver->functions(), i_isolate); - int i; - if (!args[0]->Int32Value(context).To(&i)) return; - if (i < 0 || i >= array->length()) { - thrower.RangeError("index out of bounds"); - return; - } - - i::Handle<i::FixedArray> dispatch_tables(receiver->dispatch_tables(), - i_isolate); - if (value->IsNull(i_isolate)) { - i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i, - i::Handle<i::JSFunction>::null()); - } else { - i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i, - i::Handle<i::JSFunction>::cast(value)); - } - - i::Handle<i::FixedArray>::cast(array)->set(i, *value); + i::wasm::TableSet(&thrower, i_isolate, receiver, index, + value->IsNull(i_isolate) + ? i::Handle<i::JSFunction>::null() + : i::Handle<i::JSFunction>::cast(value)); } // WebAssembly.Memory.grow(num) -> num @@ -760,27 +755,21 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Memory.grow()"); + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.grow()"); Local<Context> context = isolate->GetCurrentContext(); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); - if (!BrandCheck(Utils::OpenHandle(*args.This()), - i::Handle<i::Symbol>(i_context->wasm_memory_sym()), &thrower, - "Receiver is not a WebAssembly.Memory")) { - return; - } + EXTRACT_THIS(receiver, WasmMemoryObject); + int64_t delta_size = 0; if (args.Length() < 1 || !args[0]->IntegerValue(context).To(&delta_size)) { thrower.TypeError("Argument 0 required, must be numeric value of pages"); return; } - i::Handle<i::WasmMemoryObject> receiver = - i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This())); int64_t max_size64 = receiver->maximum_pages(); if (max_size64 < 0 || max_size64 > static_cast<int64_t>(i::FLAG_wasm_max_mem_pages)) { max_size64 = i::FLAG_wasm_max_mem_pages; } - i::Handle<i::JSArrayBuffer> old_buffer(receiver->buffer()); + i::Handle<i::JSArrayBuffer> old_buffer(receiver->array_buffer()); uint32_t old_size = old_buffer->byte_length()->Number() / i::wasm::kSpecMaxWasmMemoryPages; int64_t new_size64 = old_size + delta_size; @@ -796,7 +785,9 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { return; } bool free_memory = (delta_size != 0); - i::wasm::DetachWebAssemblyMemoryBuffer(i_isolate, old_buffer, free_memory); + if (!old_buffer->is_shared()) { + i::wasm::DetachWebAssemblyMemoryBuffer(i_isolate, old_buffer, free_memory); + } v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); return_value.Set(ret); } @@ -807,18 +798,23 @@ void WebAssemblyMemoryGetBuffer( v8::Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); HandleScope scope(isolate); - ErrorThrower thrower(i_isolate, "WebAssembly.Memory.buffer"); - Local<Context> context = isolate->GetCurrentContext(); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); - if (!BrandCheck(Utils::OpenHandle(*args.This()), - i::Handle<i::Symbol>(i_context->wasm_memory_sym()), &thrower, - "Receiver is not a WebAssembly.Memory")) { - return; + ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.buffer"); + EXTRACT_THIS(receiver, WasmMemoryObject); + + i::Handle<i::Object> buffer_obj(receiver->array_buffer(), i_isolate); + DCHECK(buffer_obj->IsJSArrayBuffer()); + i::Handle<i::JSArrayBuffer> buffer(i::JSArrayBuffer::cast(*buffer_obj)); + if (buffer->is_shared()) { + // TODO(gdeepti): More needed here for when cached buffer, and current + // buffer are out of sync, handle that here when bounds checks, and Grow + // are handled correctly. + Maybe<bool> result = + buffer->SetIntegrityLevel(buffer, i::FROZEN, i::Object::DONT_THROW); + if (!result.FromJust()) { + thrower.TypeError( + "Status of setting SetIntegrityLevel of buffer is false."); + } } - i::Handle<i::WasmMemoryObject> receiver = - i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This())); - i::Handle<i::Object> buffer(receiver->buffer(), i_isolate); - DCHECK(buffer->IsJSArrayBuffer()); v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); return_value.Set(Utils::ToLocal(buffer)); } @@ -842,8 +838,8 @@ Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object, Handle<String> name = v8_str(isolate, str); Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); Handle<JSFunction> function = - ApiNatives::InstantiateFunction(temp).ToHandleChecked(); - JSFunction::SetName(function, name, isolate->factory()->empty_string()); + ApiNatives::InstantiateFunction(temp, name).ToHandleChecked(); + DCHECK(function->shared()->has_shared_name()); function->shared()->set_length(length); PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM); JSObject::AddProperty(object, name, function, attributes); @@ -854,8 +850,10 @@ Handle<JSFunction> InstallGetter(Isolate* isolate, Handle<JSObject> object, const char* str, FunctionCallback func) { Handle<String> name = v8_str(isolate, str); Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); + // TODO(ishell): shouldn't we set "get "+name as getter's name? Handle<JSFunction> function = ApiNatives::InstantiateFunction(temp).ToHandleChecked(); + DCHECK(function->shared()->has_shared_name()); v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(v8::DontEnum); Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name), @@ -867,55 +865,19 @@ Handle<JSFunction> InstallGetter(Isolate* isolate, Handle<JSObject> object, void WasmJs::Install(Isolate* isolate) { Handle<JSGlobalObject> global = isolate->global_object(); Handle<Context> context(global->native_context(), isolate); - // TODO(titzer): once FLAG_expose_wasm is gone, this should become a DCHECK. - if (context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) return; - - // Install Maps. - - // TODO(titzer): Also make one for strict mode functions? - Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate); - - InstanceType instance_type = prev_map->instance_type(); - int embedder_fields = JSObject::GetEmbedderFieldCount(*prev_map); - CHECK_EQ(0, embedder_fields); - int pre_allocated = - prev_map->GetInObjectProperties() - prev_map->unused_property_fields(); - int instance_size = 0; - int in_object_properties = 0; - int wasm_embedder_fields = embedder_fields + 1 // module instance object - + 1 // function arity - + 1; // function signature - JSFunction::CalculateInstanceSizeHelper(instance_type, wasm_embedder_fields, - 0, &instance_size, - &in_object_properties); - - int unused_property_fields = in_object_properties - pre_allocated; - Handle<Map> map = Map::CopyInitialMap( - prev_map, instance_size, in_object_properties, unused_property_fields); - - context->set_wasm_function_map(*map); - - // Install symbols. + // Install the JS API once only. + Object* prev = context->get(Context::WASM_MODULE_CONSTRUCTOR_INDEX); + if (!prev->IsUndefined(isolate)) { + DCHECK(prev->IsJSFunction()); + return; + } Factory* factory = isolate->factory(); - // Create private symbols. - Handle<Symbol> module_sym = factory->NewPrivateSymbol(); - context->set_wasm_module_sym(*module_sym); - - Handle<Symbol> instance_sym = factory->NewPrivateSymbol(); - context->set_wasm_instance_sym(*instance_sym); - - Handle<Symbol> table_sym = factory->NewPrivateSymbol(); - context->set_wasm_table_sym(*table_sym); - - Handle<Symbol> memory_sym = factory->NewPrivateSymbol(); - context->set_wasm_memory_sym(*memory_sym); - - // Install the JS API. // Setup WebAssembly Handle<String> name = v8_str(isolate, "WebAssembly"); - Handle<JSFunction> cons = factory->NewFunction(name); + Handle<JSFunction> cons = factory->NewFunction(isolate->strict_function_map(), + name, MaybeHandle<Code>()); JSFunction::SetPrototype(cons, isolate->initial_object_prototype()); cons->shared()->set_instance_class_name(*name); Handle<JSObject> webassembly = factory->NewJSObject(cons, TENURED); @@ -926,12 +888,15 @@ void WasmJs::Install(Isolate* isolate) { JSObject::AddProperty(webassembly, factory->to_string_tag_symbol(), v8_str(isolate, "WebAssembly"), ro_attributes); InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile, 1); - InstallFunc(isolate, webassembly, "compileStreaming", - WebAssemblyCompileStreaming, 1); InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1); InstallFunc(isolate, webassembly, "instantiate", WebAssemblyInstantiate, 1); - InstallFunc(isolate, webassembly, "instantiateStreaming", - WebAssemblyInstantiateStreaming, 1); + + if (isolate->wasm_compile_streaming_callback() != nullptr) { + InstallFunc(isolate, webassembly, "compileStreaming", + WebAssemblyCompileStreaming, 1); + InstallFunc(isolate, webassembly, "instantiateStreaming", + WebAssemblyInstantiateStreaming, 1); + } // Setup Module Handle<JSFunction> module_constructor = @@ -940,8 +905,8 @@ void WasmJs::Install(Isolate* isolate) { Handle<JSObject> module_proto = factory->NewJSObject(module_constructor, TENURED); i::Handle<i::Map> module_map = isolate->factory()->NewMap( - i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize + - WasmModuleObject::kFieldCount * i::kPointerSize); + i::WASM_MODULE_TYPE, i::JSObject::kHeaderSize + + WasmModuleObject::kFieldCount * i::kPointerSize); JSFunction::SetInitialMap(module_constructor, module_map, module_proto); InstallFunc(isolate, module_constructor, "imports", WebAssemblyModuleImports, 1); @@ -961,8 +926,7 @@ void WasmJs::Install(Isolate* isolate) { Handle<JSObject> instance_proto = factory->NewJSObject(instance_constructor, TENURED); i::Handle<i::Map> instance_map = isolate->factory()->NewMap( - i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize + - WasmInstanceObject::kFieldCount * i::kPointerSize); + i::WASM_INSTANCE_TYPE, WasmInstanceObject::kSize); JSFunction::SetInitialMap(instance_constructor, instance_map, instance_proto); JSObject::AddProperty(instance_proto, isolate->factory()->constructor_string(), @@ -976,9 +940,8 @@ void WasmJs::Install(Isolate* isolate) { context->set_wasm_table_constructor(*table_constructor); Handle<JSObject> table_proto = factory->NewJSObject(table_constructor, TENURED); - i::Handle<i::Map> table_map = isolate->factory()->NewMap( - i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize + - WasmTableObject::kFieldCount * i::kPointerSize); + i::Handle<i::Map> table_map = + isolate->factory()->NewMap(i::WASM_TABLE_TYPE, WasmTableObject::kSize); JSFunction::SetInitialMap(table_constructor, table_map, table_proto); JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(), table_constructor, DONT_ENUM); @@ -995,9 +958,8 @@ void WasmJs::Install(Isolate* isolate) { context->set_wasm_memory_constructor(*memory_constructor); Handle<JSObject> memory_proto = factory->NewJSObject(memory_constructor, TENURED); - i::Handle<i::Map> memory_map = isolate->factory()->NewMap( - i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize + - WasmMemoryObject::kFieldCount * i::kPointerSize); + i::Handle<i::Map> memory_map = + isolate->factory()->NewMap(i::WASM_MEMORY_TYPE, WasmMemoryObject::kSize); JSFunction::SetInitialMap(memory_constructor, memory_map, memory_proto); JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(), memory_constructor, DONT_ENUM); @@ -1021,15 +983,5 @@ void WasmJs::Install(Isolate* isolate) { JSObject::AddProperty(webassembly, isolate->factory()->RuntimeError_string(), runtime_error, attributes); } - -bool WasmJs::IsWasmMemoryObject(Isolate* isolate, Handle<Object> value) { - i::Handle<i::Symbol> symbol(isolate->context()->wasm_memory_sym(), isolate); - return HasBrand(value, symbol); -} - -bool WasmJs::IsWasmTableObject(Isolate* isolate, Handle<Object> value) { - i::Handle<i::Symbol> symbol(isolate->context()->wasm_table_sym(), isolate); - return HasBrand(value, symbol); -} } // namespace internal } // namespace v8 diff --git a/deps/v8/src/wasm/wasm-js.h b/deps/v8/src/wasm/wasm-js.h index 05d5ea3061..0ef2219b1f 100644 --- a/deps/v8/src/wasm/wasm-js.h +++ b/deps/v8/src/wasm/wasm-js.h @@ -10,7 +10,8 @@ namespace v8 { namespace internal { -// Exposes a WASM API to JavaScript through the V8 API. + +// Exposes a WebAssembly API to JavaScript through the V8 API. class WasmJs { public: V8_EXPORT_PRIVATE static void Install(Isolate* isolate); diff --git a/deps/v8/src/wasm/wasm-limits.h b/deps/v8/src/wasm/wasm-limits.h index c2ecf61657..0e10688cd6 100644 --- a/deps/v8/src/wasm/wasm-limits.h +++ b/deps/v8/src/wasm/wasm-limits.h @@ -20,9 +20,13 @@ constexpr size_t kV8MaxWasmFunctions = 1000000; constexpr size_t kV8MaxWasmImports = 100000; constexpr size_t kV8MaxWasmExports = 100000; constexpr size_t kV8MaxWasmGlobals = 1000000; +constexpr size_t kV8MaxWasmExceptions = 1000000; +constexpr size_t kV8MaxWasmExceptionTypes = 1000000; constexpr size_t kV8MaxWasmDataSegments = 100000; // Don't use this limit directly, but use the value of FLAG_wasm_max_mem_pages. -constexpr size_t kV8MaxWasmMemoryPages = 16384; // = 1 GiB +// Current limit mimics the maximum allowed allocation on an ArrayBuffer +// (2GiB - 1 page). +constexpr size_t kV8MaxWasmMemoryPages = 32767; // ~ 2 GiB constexpr size_t kV8MaxWasmStringSize = 100000; constexpr size_t kV8MaxWasmModuleSize = 1024 * 1024 * 1024; // = 1 GiB constexpr size_t kV8MaxWasmFunctionSize = 128 * 1024; diff --git a/deps/v8/src/wasm/wasm-module.cc b/deps/v8/src/wasm/wasm-module.cc index 5b8020b4f5..7901a6b619 100644 --- a/deps/v8/src/wasm/wasm-module.cc +++ b/deps/v8/src/wasm/wasm-module.cc @@ -2,24 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <functional> #include <memory> -#include "src/asmjs/asm-js.h" -#include "src/assembler-inl.h" -#include "src/base/atomic-utils.h" -#include "src/base/utils/random-number-generator.h" #include "src/code-stubs.h" -#include "src/compiler/wasm-compiler.h" #include "src/debug/interface-types.h" #include "src/frames-inl.h" #include "src/objects.h" #include "src/property-descriptor.h" #include "src/simulator.h" #include "src/snapshot/snapshot.h" -#include "src/trap-handler/trap-handler.h" #include "src/v8.h" -#include "src/wasm/function-body-decoder.h" +#include "src/wasm/compilation-manager.h" +#include "src/wasm/module-compiler.h" #include "src/wasm/module-decoder.h" #include "src/wasm/wasm-code-specialization.h" #include "src/wasm/wasm-js.h" @@ -49,30 +45,6 @@ namespace base = v8::base; namespace { -static const int kInvalidSigIndex = -1; - -byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) { - return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset; -} - -static void RecordStats(Isolate* isolate, Code* code, bool is_sync) { - if (is_sync) { - // TODO(karlschimpf): Make this work when asynchronous. - // https://bugs.chromium.org/p/v8/issues/detail?id=6361 - isolate->counters()->wasm_generated_code_size()->Increment( - code->body_size()); - isolate->counters()->wasm_reloc_size()->Increment( - code->relocation_info()->length()); - } -} - -static void RecordStats(Isolate* isolate, Handle<FixedArray> functions, - bool is_sync) { - DisallowHeapAllocation no_gc; - for (int i = 0; i < functions->length(); ++i) { - RecordStats(isolate, Code::cast(functions->get(i)), is_sync); - } -} void* TryAllocateBackingStore(Isolate* isolate, size_t size, bool enable_guard_regions, void*& allocation_base, @@ -108,721 +80,14 @@ void* TryAllocateBackingStore(Isolate* isolate, size_t size, return memory; } else { - void* memory = isolate->array_buffer_allocator()->Allocate(size); + void* memory = + size == 0 ? nullptr : isolate->array_buffer_allocator()->Allocate(size); allocation_base = memory; allocation_length = size; return memory; } } -void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) { - for (int i = 0; i < code_table->length(); ++i) { - Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i); - Assembler::FlushICache(isolate, code->instruction_start(), - code->instruction_size()); - } -} - -Handle<Script> CreateWasmScript(Isolate* isolate, - const ModuleWireBytes& wire_bytes) { - Handle<Script> script = - isolate->factory()->NewScript(isolate->factory()->empty_string()); - script->set_context_data(isolate->native_context()->debug_context_id()); - script->set_type(Script::TYPE_WASM); - - int hash = StringHasher::HashSequentialString( - reinterpret_cast<const char*>(wire_bytes.start()), - static_cast<int>(wire_bytes.length()), kZeroHashSeed); - - const int kBufferSize = 32; - char buffer[kBufferSize]; - int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash); - DCHECK(url_chars >= 0 && url_chars < kBufferSize); - MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte( - Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars), - TENURED); - script->set_source_url(*url_str.ToHandleChecked()); - - int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash); - DCHECK(name_chars >= 0 && name_chars < kBufferSize); - MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte( - Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars), - TENURED); - script->set_name(*name_str.ToHandleChecked()); - - return script; -} - -class JSToWasmWrapperCache { - public: - Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate, - const wasm::WasmModule* module, - Handle<Code> wasm_code, - uint32_t index) { - const wasm::WasmFunction* func = &module->functions[index]; - int cached_idx = sig_map_.Find(func->sig); - if (cached_idx >= 0) { - Handle<Code> code = isolate->factory()->CopyCode(code_cache_[cached_idx]); - // Now patch the call to wasm code. - for (RelocIterator it(*code, RelocInfo::kCodeTargetMask);; it.next()) { - DCHECK(!it.done()); - Code* target = - Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); - if (target->kind() == Code::WASM_FUNCTION || - target->kind() == Code::WASM_TO_JS_FUNCTION || - target->builtin_index() == Builtins::kIllegal || - target->builtin_index() == Builtins::kWasmCompileLazy) { - it.rinfo()->set_target_address(isolate, - wasm_code->instruction_start()); - break; - } - } - return code; - } - - Handle<Code> code = - compiler::CompileJSToWasmWrapper(isolate, module, wasm_code, index); - uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig); - DCHECK_EQ(code_cache_.size(), new_cache_idx); - USE(new_cache_idx); - code_cache_.push_back(code); - return code; - } - - private: - // sig_map_ maps signatures to an index in code_cache_. - wasm::SignatureMap sig_map_; - std::vector<Handle<Code>> code_cache_; -}; - -// Ensure that the code object in <code_table> at offset <func_index> has -// deoptimization data attached. This is needed for lazy compile stubs which are -// called from JS_TO_WASM functions or via exported function tables. The deopt -// data is used to determine which function this lazy compile stub belongs to. -Handle<Code> EnsureExportedLazyDeoptData(Isolate* isolate, - Handle<WasmInstanceObject> instance, - Handle<FixedArray> code_table, - int func_index) { - Handle<Code> code(Code::cast(code_table->get(func_index)), isolate); - if (code->builtin_index() != Builtins::kWasmCompileLazy) { - // No special deopt data needed for compiled functions, and imported - // functions, which map to Illegal at this point (they get compiled at - // instantiation time). - DCHECK(code->kind() == Code::WASM_FUNCTION || - code->kind() == Code::WASM_TO_JS_FUNCTION || - code->builtin_index() == Builtins::kIllegal); - return code; - } - // deopt_data: - // #0: weak instance - // #1: func_index - // might be extended later for table exports (see - // EnsureTableExportLazyDeoptData). - Handle<FixedArray> deopt_data(code->deoptimization_data()); - DCHECK_EQ(0, deopt_data->length() % 2); - if (deopt_data->length() == 0) { - code = isolate->factory()->CopyCode(code); - code_table->set(func_index, *code); - deopt_data = isolate->factory()->NewFixedArray(2, TENURED); - code->set_deoptimization_data(*deopt_data); - if (!instance.is_null()) { - Handle<WeakCell> weak_instance = - isolate->factory()->NewWeakCell(instance); - deopt_data->set(0, *weak_instance); - } - deopt_data->set(1, Smi::FromInt(func_index)); - } - DCHECK_IMPLIES(!instance.is_null(), - WeakCell::cast(code->deoptimization_data()->get(0))->value() == - *instance); - DCHECK_EQ(func_index, - Smi::cast(code->deoptimization_data()->get(1))->value()); - return code; -} - -// Ensure that the code object in <code_table> at offset <func_index> has -// deoptimization data attached. This is needed for lazy compile stubs which are -// called from JS_TO_WASM functions or via exported function tables. The deopt -// data is used to determine which function this lazy compile stub belongs to. -Handle<Code> EnsureTableExportLazyDeoptData( - Isolate* isolate, Handle<WasmInstanceObject> instance, - Handle<FixedArray> code_table, int func_index, - Handle<FixedArray> export_table, int export_index, - std::unordered_map<uint32_t, uint32_t>& table_export_count) { - Handle<Code> code = - EnsureExportedLazyDeoptData(isolate, instance, code_table, func_index); - if (code->builtin_index() != Builtins::kWasmCompileLazy) return code; - - // deopt_data: - // #0: weak instance - // #1: func_index - // [#2: export table - // #3: export table index] - // [#4: export table - // #5: export table index] - // ... - // table_export_count counts down and determines the index for the new export - // table entry. - auto table_export_entry = table_export_count.find(func_index); - DCHECK(table_export_entry != table_export_count.end()); - DCHECK_LT(0, table_export_entry->second); - uint32_t this_idx = 2 * table_export_entry->second; - --table_export_entry->second; - Handle<FixedArray> deopt_data(code->deoptimization_data()); - DCHECK_EQ(0, deopt_data->length() % 2); - if (deopt_data->length() == 2) { - // Then only the "header" (#0 and #1) exists. Extend for the export table - // entries (make space for this_idx + 2 elements). - deopt_data = isolate->factory()->CopyFixedArrayAndGrow(deopt_data, this_idx, - TENURED); - code->set_deoptimization_data(*deopt_data); - } - DCHECK_LE(this_idx + 2, deopt_data->length()); - DCHECK(deopt_data->get(this_idx)->IsUndefined(isolate)); - DCHECK(deopt_data->get(this_idx + 1)->IsUndefined(isolate)); - deopt_data->set(this_idx, *export_table); - deopt_data->set(this_idx + 1, Smi::FromInt(export_index)); - return code; -} - -bool compile_lazy(const WasmModule* module) { - return FLAG_wasm_lazy_compilation || - (FLAG_asm_wasm_lazy_compilation && module->is_asm_js()); -} - -// A helper for compiling an entire module. -class CompilationHelper { - public: - // The compilation helper takes ownership of the {WasmModule}. - // In {CompileToModuleObject}, it will transfer ownership to the generated - // {WasmModuleWrapper}. If this method is not called, ownership may be - // reclaimed by explicitely releasing the {module_} field. - CompilationHelper(Isolate* isolate, std::unique_ptr<WasmModule> module, - bool is_sync) - : isolate_(isolate), - module_(std::move(module)), - is_sync_(is_sync), - executed_units_( - isolate->random_number_generator(), - (isolate->heap()->memory_allocator()->code_range()->valid() - ? isolate->heap()->memory_allocator()->code_range()->size() - : isolate->heap()->code_space()->Capacity()) / - 2), - num_background_tasks_(Min( - static_cast<size_t>(FLAG_wasm_num_compilation_tasks), - V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads())), - stopped_compilation_tasks_(num_background_tasks_) {} - - bool GetNextUncompiledFunctionId(size_t* index) { - DCHECK_NOT_NULL(index); - // - 1 because AtomicIncrement returns the value after the atomic increment. - *index = next_unit_.Increment(1) - 1; - return *index < compilation_units_.size(); - } - - // The actual runnable task that performs compilations in the background. - class CompilationTask : public CancelableTask { - public: - CompilationHelper* helper_; - explicit CompilationTask(CompilationHelper* helper) - : CancelableTask(helper->isolate_, &helper->background_task_manager_), - helper_(helper) {} - - void RunInternal() override { - size_t index = 0; - while (helper_->executed_units_.CanAcceptWork() && - helper_->GetNextUncompiledFunctionId(&index)) { - helper_->CompileAndSchedule(index); - } - helper_->OnBackgroundTaskStopped(); - } - }; - - void OnBackgroundTaskStopped() { - base::LockGuard<base::Mutex> guard(&tasks_mutex_); - ++stopped_compilation_tasks_; - DCHECK_LE(stopped_compilation_tasks_, num_background_tasks_); - } - - void CompileAndSchedule(size_t index) { - DisallowHeapAllocation no_allocation; - DisallowHandleAllocation no_handles; - DisallowHandleDereference no_deref; - DisallowCodeDependencyChange no_dependency_change; - DCHECK_LT(index, compilation_units_.size()); - - std::unique_ptr<compiler::WasmCompilationUnit> unit = - std::move(compilation_units_.at(index)); - unit->ExecuteCompilation(); - { - base::LockGuard<base::Mutex> guard(&result_mutex_); - executed_units_.Schedule(std::move(unit)); - } - } - - class CodeGenerationSchedule { - public: - explicit CodeGenerationSchedule( - base::RandomNumberGenerator* random_number_generator, - size_t max_memory = 0); - - void Schedule(std::unique_ptr<compiler::WasmCompilationUnit>&& item); - - bool IsEmpty() const { return schedule_.empty(); } - - std::unique_ptr<compiler::WasmCompilationUnit> GetNext(); - - bool CanAcceptWork() const; - - void EnableThrottling() { throttle_ = true; } - - private: - size_t GetRandomIndexInSchedule(); - - base::RandomNumberGenerator* random_number_generator_ = nullptr; - std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> schedule_; - const size_t max_memory_; - bool throttle_ = false; - base::AtomicNumber<size_t> allocated_memory_{0}; - }; - - Isolate* isolate_; - std::unique_ptr<WasmModule> module_; - bool is_sync_; - std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> - compilation_units_; - CodeGenerationSchedule executed_units_; - base::Mutex result_mutex_; - base::AtomicNumber<size_t> next_unit_; - const size_t num_background_tasks_ = 0; - CancelableTaskManager background_task_manager_; - - // Run by each compilation task and by the main thread. - bool FetchAndExecuteCompilationUnit() { - DisallowHeapAllocation no_allocation; - DisallowHandleAllocation no_handles; - DisallowHandleDereference no_deref; - DisallowCodeDependencyChange no_dependency_change; - - // - 1 because AtomicIncrement returns the value after the atomic increment. - size_t index = next_unit_.Increment(1) - 1; - if (index >= compilation_units_.size()) { - return false; - } - - std::unique_ptr<compiler::WasmCompilationUnit> unit = - std::move(compilation_units_.at(index)); - unit->ExecuteCompilation(); - base::LockGuard<base::Mutex> guard(&result_mutex_); - executed_units_.Schedule(std::move(unit)); - return true; - } - - size_t InitializeParallelCompilation( - const std::vector<WasmFunction>& functions, ModuleBytesEnv& module_env) { - uint32_t start = module_env.module_env.module->num_imported_functions + - FLAG_skip_compiling_wasm_funcs; - uint32_t num_funcs = static_cast<uint32_t>(functions.size()); - uint32_t funcs_to_compile = start > num_funcs ? 0 : num_funcs - start; - compilation_units_.reserve(funcs_to_compile); - for (uint32_t i = start; i < num_funcs; ++i) { - const WasmFunction* func = &functions[i]; - constexpr bool is_sync = true; - compilation_units_.push_back( - std::unique_ptr<compiler::WasmCompilationUnit>( - new compiler::WasmCompilationUnit(isolate_, &module_env, func, - !is_sync))); - } - return funcs_to_compile; - } - - void RestartCompilationTasks() { - base::LockGuard<base::Mutex> guard(&tasks_mutex_); - for (; stopped_compilation_tasks_ > 0; --stopped_compilation_tasks_) { - V8::GetCurrentPlatform()->CallOnBackgroundThread( - new CompilationTask(this), v8::Platform::kShortRunningTask); - } - } - - void WaitForCompilationTasks(uint32_t* task_ids) { - for (size_t i = 0; i < num_background_tasks_; ++i) { - // If the task has not started yet, then we abort it. Otherwise we wait - // for it to finish. - if (isolate_->cancelable_task_manager()->TryAbort(task_ids[i]) != - CancelableTaskManager::kTaskAborted) { - module_->pending_tasks.get()->Wait(); - } - } - } - - size_t FinishCompilationUnits(std::vector<Handle<Code>>& results, - ErrorThrower* thrower) { - size_t finished = 0; - while (true) { - int func_index = -1; - Handle<Code> result = FinishCompilationUnit(thrower, &func_index); - if (func_index < 0) break; - results[func_index] = result; - ++finished; - } - RestartCompilationTasks(); - return finished; - } - - Handle<Code> FinishCompilationUnit(ErrorThrower* thrower, int* func_index) { - std::unique_ptr<compiler::WasmCompilationUnit> unit; - { - base::LockGuard<base::Mutex> guard(&result_mutex_); - if (executed_units_.IsEmpty()) return Handle<Code>::null(); - unit = executed_units_.GetNext(); - } - *func_index = unit->func_index(); - Handle<Code> result = unit->FinishCompilation(thrower); - return result; - } - - void CompileInParallel(ModuleBytesEnv* module_env, - std::vector<Handle<Code>>& results, - ErrorThrower* thrower) { - const WasmModule* module = module_env->module_env.module; - // Data structures for the parallel compilation. - - //----------------------------------------------------------------------- - // For parallel compilation: - // 1) The main thread allocates a compilation unit for each wasm function - // and stores them in the vector {compilation_units}. - // 2) The main thread spawns {CompilationTask} instances which run on - // the background threads. - // 3.a) The background threads and the main thread pick one compilation - // unit at a time and execute the parallel phase of the compilation - // unit. After finishing the execution of the parallel phase, the - // result is enqueued in {executed_units}. - // 3.b) If {executed_units} contains a compilation unit, the main thread - // dequeues it and finishes the compilation. - // 4) After the parallel phase of all compilation units has started, the - // main thread waits for all {CompilationTask} instances to finish. - // 5) The main thread finishes the compilation. - - // Turn on the {CanonicalHandleScope} so that the background threads can - // use the node cache. - CanonicalHandleScope canonical(isolate_); - - // 1) The main thread allocates a compilation unit for each wasm function - // and stores them in the vector {compilation_units}. - InitializeParallelCompilation(module->functions, *module_env); - - executed_units_.EnableThrottling(); - - // 2) The main thread spawns {CompilationTask} instances which run on - // the background threads. - RestartCompilationTasks(); - - size_t finished_functions = 0; - while (finished_functions < compilation_units_.size()) { - // 3.a) The background threads and the main thread pick one compilation - // unit at a time and execute the parallel phase of the compilation - // unit. After finishing the execution of the parallel phase, the - // result is enqueued in {executed_units}. - size_t index = 0; - if (GetNextUncompiledFunctionId(&index)) { - CompileAndSchedule(index); - } - // 3.b) If {executed_units} contains a compilation unit, the main thread - // dequeues it and finishes the compilation unit. Compilation units - // are finished concurrently to the background threads to save - // memory. - finished_functions += FinishCompilationUnits(results, thrower); - } - // 4) After the parallel phase of all compilation units has started, the - // main thread waits for all {CompilationTask} instances to finish - - // which happens once they all realize there's no next work item to - // process. - background_task_manager_.CancelAndWait(); - } - - void CompileSequentially(ModuleBytesEnv* module_env, - std::vector<Handle<Code>>& results, - ErrorThrower* thrower) { - DCHECK(!thrower->error()); - - const WasmModule* module = module_env->module_env.module; - for (uint32_t i = FLAG_skip_compiling_wasm_funcs; - i < module->functions.size(); ++i) { - const WasmFunction& func = module->functions[i]; - if (func.imported) - continue; // Imports are compiled at instantiation time. - - // Compile the function. - Handle<Code> code = compiler::WasmCompilationUnit::CompileWasmFunction( - thrower, isolate_, module_env, &func); - if (code.is_null()) { - WasmName str = module_env->wire_bytes.GetName(&func); - thrower->CompileError("Compilation of #%d:%.*s failed.", i, - str.length(), str.start()); - break; - } - results[i] = code; - } - } - - MaybeHandle<WasmModuleObject> CompileToModuleObject( - ErrorThrower* thrower, const ModuleWireBytes& wire_bytes, - Handle<Script> asm_js_script, - Vector<const byte> asm_js_offset_table_bytes) { - Factory* factory = isolate_->factory(); - WasmInstance temp_instance(module_.get()); - temp_instance.context = isolate_->native_context(); - temp_instance.mem_size = WasmModule::kPageSize * module_->min_mem_pages; - temp_instance.mem_start = nullptr; - temp_instance.globals_start = nullptr; - - // Initialize the indirect tables with placeholders. - int function_table_count = - static_cast<int>(module_->function_tables.size()); - Handle<FixedArray> function_tables = - factory->NewFixedArray(function_table_count, TENURED); - Handle<FixedArray> signature_tables = - factory->NewFixedArray(function_table_count, TENURED); - for (int i = 0; i < function_table_count; ++i) { - temp_instance.function_tables[i] = factory->NewFixedArray(1, TENURED); - temp_instance.signature_tables[i] = factory->NewFixedArray(1, TENURED); - function_tables->set(i, *temp_instance.function_tables[i]); - signature_tables->set(i, *temp_instance.signature_tables[i]); - } - - if (is_sync_) { - // TODO(karlschimpf): Make this work when asynchronous. - // https://bugs.chromium.org/p/v8/issues/detail?id=6361 - HistogramTimerScope wasm_compile_module_time_scope( - module_->is_wasm() - ? isolate_->counters()->wasm_compile_wasm_module_time() - : isolate_->counters()->wasm_compile_asm_module_time()); - return CompileToModuleObjectInternal( - thrower, wire_bytes, asm_js_script, asm_js_offset_table_bytes, - factory, &temp_instance, &function_tables, &signature_tables); - } - return CompileToModuleObjectInternal( - thrower, wire_bytes, asm_js_script, asm_js_offset_table_bytes, factory, - &temp_instance, &function_tables, &signature_tables); - } - - private: - MaybeHandle<WasmModuleObject> CompileToModuleObjectInternal( - ErrorThrower* thrower, const ModuleWireBytes& wire_bytes, - Handle<Script> asm_js_script, - Vector<const byte> asm_js_offset_table_bytes, Factory* factory, - WasmInstance* temp_instance, Handle<FixedArray>* function_tables, - Handle<FixedArray>* signature_tables) { - ModuleBytesEnv module_env(module_.get(), temp_instance, wire_bytes); - - // The {code_table} array contains import wrappers and functions (which - // are both included in {functions.size()}, and export wrappers. - int code_table_size = static_cast<int>(module_->functions.size() + - module_->num_exported_functions); - Handle<FixedArray> code_table = - factory->NewFixedArray(static_cast<int>(code_table_size), TENURED); - - // Check whether lazy compilation is enabled for this module. - bool lazy_compile = compile_lazy(module_.get()); - - // If lazy compile: Initialize the code table with the lazy compile builtin. - // Otherwise: Initialize with the illegal builtin. All call sites will be - // patched at instantiation. - Handle<Code> init_builtin = lazy_compile - ? isolate_->builtins()->WasmCompileLazy() - : isolate_->builtins()->Illegal(); - for (int i = 0, e = static_cast<int>(module_->functions.size()); i < e; - ++i) { - code_table->set(i, *init_builtin); - temp_instance->function_code[i] = init_builtin; - } - - if (is_sync_) - // TODO(karlschimpf): Make this work when asynchronous. - // https://bugs.chromium.org/p/v8/issues/detail?id=6361 - (module_->is_wasm() - ? isolate_->counters()->wasm_functions_per_wasm_module() - : isolate_->counters()->wasm_functions_per_asm_module()) - ->AddSample(static_cast<int>(module_->functions.size())); - - if (!lazy_compile) { - size_t funcs_to_compile = - module_->functions.size() - module_->num_imported_functions; - if (!FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks != 0 && - funcs_to_compile > 1) { - // Avoid a race condition by collecting results into a second vector. - std::vector<Handle<Code>> results(temp_instance->function_code); - CompileInParallel(&module_env, results, thrower); - temp_instance->function_code.swap(results); - } else { - CompileSequentially(&module_env, temp_instance->function_code, thrower); - } - if (thrower->error()) return {}; - } - - // At this point, compilation has completed. Update the code table. - for (size_t i = FLAG_skip_compiling_wasm_funcs; - i < temp_instance->function_code.size(); ++i) { - Code* code = *temp_instance->function_code[i]; - code_table->set(static_cast<int>(i), code); - RecordStats(isolate_, code, is_sync_); - } - - // Create heap objects for script, module bytes and asm.js offset table to - // be stored in the shared module data. - Handle<Script> script; - Handle<ByteArray> asm_js_offset_table; - if (asm_js_script.is_null()) { - script = CreateWasmScript(isolate_, wire_bytes); - } else { - script = asm_js_script; - asm_js_offset_table = - isolate_->factory()->NewByteArray(asm_js_offset_table_bytes.length()); - asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(), - asm_js_offset_table_bytes.length()); - } - // TODO(wasm): only save the sections necessary to deserialize a - // {WasmModule}. E.g. function bodies could be omitted. - Handle<String> module_bytes = - factory - ->NewStringFromOneByte({wire_bytes.start(), wire_bytes.length()}, - TENURED) - .ToHandleChecked(); - DCHECK(module_bytes->IsSeqOneByteString()); - - // The {module_wrapper} will take ownership of the {WasmModule} object, - // and it will be destroyed when the GC reclaims the wrapper object. - Handle<WasmModuleWrapper> module_wrapper = - WasmModuleWrapper::New(isolate_, module_.release()); - WasmModule* module = module_wrapper->get(); - - // Create the shared module data. - // TODO(clemensh): For the same module (same bytes / same hash), we should - // only have one WasmSharedModuleData. Otherwise, we might only set - // breakpoints on a (potentially empty) subset of the instances. - - Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New( - isolate_, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes), - script, asm_js_offset_table); - if (lazy_compile) WasmSharedModuleData::PrepareForLazyCompilation(shared); - - // Create the compiled module object, and populate with compiled functions - // and information needed at instantiation time. This object needs to be - // serializable. Instantiation may occur off a deserialized version of this - // object. - Handle<WasmCompiledModule> compiled_module = WasmCompiledModule::New( - isolate_, shared, code_table, *function_tables, *signature_tables); - - // If we created a wasm script, finish it now and make it public to the - // debugger. - if (asm_js_script.is_null()) { - script->set_wasm_compiled_module(*compiled_module); - isolate_->debug()->OnAfterCompile(script); - } - - // Compile JS->WASM wrappers for exported functions. - JSToWasmWrapperCache js_to_wasm_cache; - int func_index = 0; - for (auto exp : module->export_table) { - if (exp.kind != kExternalFunction) continue; - Handle<Code> wasm_code = EnsureExportedLazyDeoptData( - isolate_, Handle<WasmInstanceObject>::null(), code_table, exp.index); - Handle<Code> wrapper_code = - js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(isolate_, module, - wasm_code, exp.index); - int export_index = - static_cast<int>(module->functions.size() + func_index); - code_table->set(export_index, *wrapper_code); - RecordStats(isolate_, *wrapper_code, is_sync_); - func_index++; - } - - return WasmModuleObject::New(isolate_, compiled_module); - } - size_t stopped_compilation_tasks_ = 0; - base::Mutex tasks_mutex_; -}; - -CompilationHelper::CodeGenerationSchedule::CodeGenerationSchedule( - base::RandomNumberGenerator* random_number_generator, size_t max_memory) - : random_number_generator_(random_number_generator), - max_memory_(max_memory) { - DCHECK_NOT_NULL(random_number_generator_); - DCHECK_GT(max_memory_, 0); -} - -void CompilationHelper::CodeGenerationSchedule::Schedule( - std::unique_ptr<compiler::WasmCompilationUnit>&& item) { - size_t cost = item->memory_cost(); - schedule_.push_back(std::move(item)); - allocated_memory_.Increment(cost); -} - -bool CompilationHelper::CodeGenerationSchedule::CanAcceptWork() const { - return (!throttle_ || allocated_memory_.Value() <= max_memory_); -} - -std::unique_ptr<compiler::WasmCompilationUnit> -CompilationHelper::CodeGenerationSchedule::GetNext() { - DCHECK(!IsEmpty()); - size_t index = GetRandomIndexInSchedule(); - auto ret = std::move(schedule_[index]); - std::swap(schedule_[schedule_.size() - 1], schedule_[index]); - schedule_.pop_back(); - allocated_memory_.Decrement(ret->memory_cost()); - return ret; -} - -size_t CompilationHelper::CodeGenerationSchedule::GetRandomIndexInSchedule() { - double factor = random_number_generator_->NextDouble(); - size_t index = (size_t)(factor * schedule_.size()); - DCHECK_GE(index, 0); - DCHECK_LT(index, schedule_.size()); - return index; -} - -static void MemoryInstanceFinalizer(Isolate* isolate, - WasmInstanceObject* instance) { - DisallowHeapAllocation no_gc; - // If the memory object is destroyed, nothing needs to be done here. - if (!instance->has_memory_object()) return; - Handle<WasmInstanceWrapper> instance_wrapper = - handle(instance->instance_wrapper()); - DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper)); - DCHECK(instance_wrapper->has_instance()); - bool has_prev = instance_wrapper->has_previous(); - bool has_next = instance_wrapper->has_next(); - Handle<WasmMemoryObject> memory_object(instance->memory_object()); - - if (!has_prev && !has_next) { - memory_object->ResetInstancesLink(isolate); - return; - } else { - Handle<WasmInstanceWrapper> next_wrapper, prev_wrapper; - if (!has_prev) { - Handle<WasmInstanceWrapper> next_wrapper = - instance_wrapper->next_wrapper(); - next_wrapper->reset_previous_wrapper(); - // As this is the first link in the memory object, destroying - // without updating memory object would corrupt the instance chain in - // the memory object. - memory_object->set_instances_link(*next_wrapper); - } else if (!has_next) { - instance_wrapper->previous_wrapper()->reset_next_wrapper(); - } else { - DCHECK(has_next && has_prev); - Handle<WasmInstanceWrapper> prev_wrapper = - instance_wrapper->previous_wrapper(); - Handle<WasmInstanceWrapper> next_wrapper = - instance_wrapper->next_wrapper(); - prev_wrapper->set_next_wrapper(*next_wrapper); - next_wrapper->set_previous_wrapper(*prev_wrapper); - } - // Reset to avoid dangling pointers - instance_wrapper->reset(); - } -} - static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { DisallowHeapAllocation no_gc; JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter()); @@ -830,7 +95,6 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate()); // If a link to shared memory instances exists, update the list of memory // instances before the instance is destroyed. - if (owner->has_instance_wrapper()) MemoryInstanceFinalizer(isolate, owner); WasmCompiledModule* compiled_module = owner->compiled_module(); TRACE("Finalizing %d {\n", compiled_module->instance_id()); DCHECK(compiled_module->has_weak_wasm_module()); @@ -848,13 +112,25 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { } } + // Since the order of finalizers is not guaranteed, it can be the case + // that {instance->compiled_module()->module()}, which is a + // {Managed<WasmModule>} has been collected earlier in this GC cycle. + // Weak references to this instance won't be cleared until + // the next GC cycle, so we need to manually break some links (such as + // the weak references from {WasmMemoryObject::instances}. + if (owner->has_memory_object()) { + Handle<WasmMemoryObject> memory(owner->memory_object(), isolate); + Handle<WasmInstanceObject> instance(owner, isolate); + WasmMemoryObject::RemoveInstance(isolate, memory, instance); + } + // weak_wasm_module may have been cleared, meaning the module object // was GC-ed. In that case, there won't be any new instances created, // and we don't need to maintain the links between instances. if (!weak_wasm_module->cleared()) { - JSObject* wasm_module = JSObject::cast(weak_wasm_module->value()); - WasmCompiledModule* current_template = - WasmCompiledModule::cast(wasm_module->GetEmbedderField(0)); + WasmModuleObject* wasm_module = + WasmModuleObject::cast(weak_wasm_module->value()); + WasmCompiledModule* current_template = wasm_module->compiled_module(); TRACE("chain before {\n"); TRACE_CHAIN(current_template); @@ -868,10 +144,12 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { if (next == nullptr) { WasmCompiledModule::Reset(isolate, compiled_module); } else { - DCHECK(next->value()->IsFixedArray()); - wasm_module->SetEmbedderField(0, next->value()); + WasmCompiledModule* next_compiled_module = + WasmCompiledModule::cast(next->value()); + WasmModuleObject::cast(wasm_module) + ->set_compiled_module(next_compiled_module); DCHECK_NULL(prev); - WasmCompiledModule::cast(next->value())->reset_weak_prev_instance(); + next_compiled_module->reset_weak_prev_instance(); } } else { DCHECK(!(prev == nullptr && next == nullptr)); @@ -898,7 +176,7 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { } } TRACE("chain after {\n"); - TRACE_CHAIN(WasmCompiledModule::cast(wasm_module->GetEmbedderField(0))); + TRACE_CHAIN(wasm_module->compiled_module()); TRACE("}\n"); } compiled_module->reset_weak_owning_instance(); @@ -927,25 +205,27 @@ int ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc) { return static_cast<int>(call_idx); } -void RecordLazyCodeStats(Isolate* isolate, Code* code) { - isolate->counters()->wasm_lazily_compiled_functions()->Increment(); - isolate->counters()->wasm_generated_code_size()->Increment(code->body_size()); - isolate->counters()->wasm_reloc_size()->Increment( - code->relocation_info()->length()); +void RecordLazyCodeStats(Code* code, Counters* counters) { + counters->wasm_lazily_compiled_functions()->Increment(); + counters->wasm_generated_code_size()->Increment(code->body_size()); + counters->wasm_reloc_size()->Increment(code->relocation_info()->length()); } } // namespace -Handle<JSArrayBuffer> wasm::SetupArrayBuffer(Isolate* isolate, - void* allocation_base, - size_t allocation_length, - void* backing_store, size_t size, - bool is_external, - bool enable_guard_regions) { - Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); +// static +const WasmExceptionSig wasm::WasmException::empty_sig_(0, 0, nullptr); + +Handle<JSArrayBuffer> wasm::SetupArrayBuffer( + Isolate* isolate, void* allocation_base, size_t allocation_length, + void* backing_store, size_t size, bool is_external, + bool enable_guard_regions, SharedFlag shared) { + Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(shared); + DCHECK_GE(kMaxInt, size); + if (shared == SharedFlag::kShared) DCHECK(FLAG_experimental_wasm_threads); JSArrayBuffer::Setup(buffer, isolate, is_external, allocation_base, - allocation_length, backing_store, - static_cast<int>(size)); + allocation_length, backing_store, static_cast<int>(size), + shared); buffer->set_is_neuterable(false); buffer->set_is_wasm_buffer(true); buffer->set_has_guard_region(enable_guard_regions); @@ -953,8 +233,13 @@ Handle<JSArrayBuffer> wasm::SetupArrayBuffer(Isolate* isolate, } Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, - bool enable_guard_regions) { - if (size > (FLAG_wasm_max_mem_pages * WasmModule::kPageSize)) { + bool enable_guard_regions, + SharedFlag shared) { + // Check against kMaxInt, since the byte length is stored as int in the + // JSArrayBuffer. Note that wasm_max_mem_pages can be raised from the command + // line, and we don't want to fail a CHECK then. + if (size > FLAG_wasm_max_mem_pages * WasmModule::kPageSize || + size > kMaxInt) { // TODO(titzer): lift restriction on maximum memory allocated here. return Handle<JSArrayBuffer>::null(); } @@ -966,7 +251,7 @@ Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, void* memory = TryAllocateBackingStore(isolate, size, enable_guard_regions, allocation_base, allocation_length); - if (memory == nullptr) { + if (size > 0 && memory == nullptr) { return Handle<JSArrayBuffer>::null(); } @@ -978,9 +263,9 @@ Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size, } #endif - const bool is_external = false; + constexpr bool is_external = false; return SetupArrayBuffer(isolate, allocation_base, allocation_length, memory, - size, is_external, enable_guard_regions); + size, is_external, enable_guard_regions, shared); } void wasm::UnpackAndRegisterProtectedInstructions( @@ -1021,7 +306,7 @@ void wasm::UnpackAndRegisterProtectedInstructions( std::ostream& wasm::operator<<(std::ostream& os, const WasmFunctionName& name) { os << "#" << name.function_->func_index; - if (name.function_->name_offset > 0) { + if (name.function_->name.is_set()) { if (name.name_.start()) { os << ":"; os.write(name.name_.start(), name.name_.length()); @@ -1047,12 +332,10 @@ WasmInstanceObject* wasm::GetOwningWasmInstance(Code* code) { } WasmModule::WasmModule(std::unique_ptr<Zone> owned) - : signature_zone(std::move(owned)), pending_tasks(new base::Semaphore(0)) {} - -namespace { + : signature_zone(std::move(owned)) {} -WasmFunction* GetWasmFunctionForImportWrapper(Isolate* isolate, - Handle<Object> target) { +WasmFunction* wasm::GetWasmFunctionForImportWrapper(Isolate* isolate, + Handle<Object> target) { if (target->IsJSFunction()) { Handle<JSFunction> func = Handle<JSFunction>::cast(target); if (func->code()->kind() == Code::JS_TO_WASM_FUNCTION) { @@ -1065,7 +348,7 @@ WasmFunction* GetWasmFunctionForImportWrapper(Isolate* isolate, return nullptr; } -static Handle<Code> UnwrapImportWrapper(Handle<Object> import_wrapper) { +Handle<Code> wasm::UnwrapImportWrapper(Handle<Object> import_wrapper) { Handle<JSFunction> func = Handle<JSFunction>::cast(import_wrapper); Handle<Code> export_wrapper_code = handle(func->code()); int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); @@ -1088,33 +371,14 @@ static Handle<Code> UnwrapImportWrapper(Handle<Object> import_wrapper) { return handle(target); } UNREACHABLE(); - return Handle<Code>::null(); -} - -Handle<Code> CompileImportWrapper(Isolate* isolate, int index, FunctionSig* sig, - Handle<JSReceiver> target, - Handle<String> module_name, - MaybeHandle<String> import_name, - ModuleOrigin origin) { - WasmFunction* other_func = GetWasmFunctionForImportWrapper(isolate, target); - if (other_func) { - if (!sig->Equals(other_func->sig)) return Handle<Code>::null(); - // Signature matched. Unwrap the JS->WASM wrapper and return the raw - // WASM function code. - return UnwrapImportWrapper(target); - } - // No wasm function or being debugged. Compile a new wrapper for the new - // signature. - return compiler::CompileWasmToJSWrapper(isolate, target, sig, index, - module_name, import_name, origin); } -void UpdateDispatchTablesInternal(Isolate* isolate, - Handle<FixedArray> dispatch_tables, int index, - WasmFunction* function, Handle<Code> code) { +void wasm::UpdateDispatchTables(Isolate* isolate, + Handle<FixedArray> dispatch_tables, int index, + WasmFunction* function, Handle<Code> code) { DCHECK_EQ(0, dispatch_tables->length() % 4); for (int i = 0; i < dispatch_tables->length(); i += 4) { - int table_index = Smi::cast(dispatch_tables->get(i + 1))->value(); + int table_index = Smi::ToInt(dispatch_tables->get(i + 1)); Handle<FixedArray> function_table( FixedArray::cast(dispatch_tables->get(i + 2)), isolate); Handle<FixedArray> signature_table( @@ -1135,1174 +399,31 @@ void UpdateDispatchTablesInternal(Isolate* isolate, } } -} // namespace -void wasm::UpdateDispatchTables(Isolate* isolate, - Handle<FixedArray> dispatch_tables, int index, - Handle<JSFunction> function) { - if (function.is_null()) { - UpdateDispatchTablesInternal(isolate, dispatch_tables, index, nullptr, - Handle<Code>::null()); - } else { - UpdateDispatchTablesInternal( - isolate, dispatch_tables, index, - GetWasmFunctionForImportWrapper(isolate, function), - UnwrapImportWrapper(function)); - } -} - -// A helper class to simplify instantiating a module from a compiled module. -// It closes over the {Isolate}, the {ErrorThrower}, the {WasmCompiledModule}, -// etc. -class InstantiationHelper { - public: - InstantiationHelper(Isolate* isolate, ErrorThrower* thrower, - Handle<WasmModuleObject> module_object, - MaybeHandle<JSReceiver> ffi, - MaybeHandle<JSArrayBuffer> memory) - : isolate_(isolate), - module_(module_object->compiled_module()->module()), - thrower_(thrower), - module_object_(module_object), - ffi_(ffi.is_null() ? Handle<JSReceiver>::null() - : ffi.ToHandleChecked()), - memory_(memory.is_null() ? Handle<JSArrayBuffer>::null() - : memory.ToHandleChecked()) {} - - // Build an instance, in all of its glory. - MaybeHandle<WasmInstanceObject> Build() { - // Check that an imports argument was provided, if the module requires it. - // No point in continuing otherwise. - if (!module_->import_table.empty() && ffi_.is_null()) { - thrower_->TypeError( - "Imports argument must be present and must be an object"); - return {}; - } - - // Record build time into correct bucket, then build instance. - HistogramTimerScope wasm_instantiate_module_time_scope( - module_->is_wasm() - ? isolate_->counters()->wasm_instantiate_wasm_module_time() - : isolate_->counters()->wasm_instantiate_asm_module_time()); - Factory* factory = isolate_->factory(); - - //-------------------------------------------------------------------------- - // Reuse the compiled module (if no owner), otherwise clone. - //-------------------------------------------------------------------------- - Handle<FixedArray> code_table; - // We keep around a copy of the old code table, because we'll be replacing - // imports for the new instance, and then we need the old imports to be - // able to relocate. - Handle<FixedArray> old_code_table; - MaybeHandle<WasmInstanceObject> owner; - - TRACE("Starting new module instantiation\n"); - { - // Root the owner, if any, before doing any allocations, which - // may trigger GC. - // Both owner and original template need to be in sync. Even - // after we lose the original template handle, the code - // objects we copied from it have data relative to the - // instance - such as globals addresses. - Handle<WasmCompiledModule> original; - { - DisallowHeapAllocation no_gc; - original = handle(module_object_->compiled_module()); - if (original->has_weak_owning_instance()) { - owner = handle(WasmInstanceObject::cast( - original->weak_owning_instance()->value())); - } - } - DCHECK(!original.is_null()); - if (original->has_weak_owning_instance()) { - // Clone, but don't insert yet the clone in the instances chain. - // We do that last. Since we are holding on to the owner instance, - // the owner + original state used for cloning and patching - // won't be mutated by possible finalizer runs. - DCHECK(!owner.is_null()); - TRACE("Cloning from %d\n", original->instance_id()); - old_code_table = original->code_table(); - compiled_module_ = WasmCompiledModule::Clone(isolate_, original); - code_table = compiled_module_->code_table(); - // Avoid creating too many handles in the outer scope. - HandleScope scope(isolate_); - - // Clone the code for WASM functions and exports. - for (int i = 0; i < code_table->length(); ++i) { - Handle<Code> orig_code(Code::cast(code_table->get(i)), isolate_); - switch (orig_code->kind()) { - case Code::WASM_TO_JS_FUNCTION: - // Imports will be overwritten with newly compiled wrappers. - break; - case Code::BUILTIN: - DCHECK_EQ(Builtins::kWasmCompileLazy, orig_code->builtin_index()); - // If this code object has deoptimization data, then we need a - // unique copy to attach updated deoptimization data. - if (orig_code->deoptimization_data()->length() > 0) { - Handle<Code> code = factory->CopyCode(orig_code); - Handle<FixedArray> deopt_data = - factory->NewFixedArray(2, TENURED); - deopt_data->set(1, Smi::FromInt(i)); - code->set_deoptimization_data(*deopt_data); - code_table->set(i, *code); - } - break; - case Code::JS_TO_WASM_FUNCTION: - case Code::WASM_FUNCTION: { - Handle<Code> code = factory->CopyCode(orig_code); - code_table->set(i, *code); - break; - } - default: - UNREACHABLE(); - } - } - RecordStats(isolate_, code_table, is_sync_); - } else { - // There was no owner, so we can reuse the original. - compiled_module_ = original; - old_code_table = - factory->CopyFixedArray(compiled_module_->code_table()); - code_table = compiled_module_->code_table(); - TRACE("Reusing existing instance %d\n", - compiled_module_->instance_id()); - } - compiled_module_->set_native_context(isolate_->native_context()); - } - - //-------------------------------------------------------------------------- - // Allocate the instance object. - //-------------------------------------------------------------------------- - Zone instantiation_zone(isolate_->allocator(), ZONE_NAME); - CodeSpecialization code_specialization(isolate_, &instantiation_zone); - Handle<WasmInstanceObject> instance = - WasmInstanceObject::New(isolate_, compiled_module_); - - //-------------------------------------------------------------------------- - // Set up the globals for the new instance. - //-------------------------------------------------------------------------- - MaybeHandle<JSArrayBuffer> old_globals; - uint32_t globals_size = module_->globals_size; - if (globals_size > 0) { - const bool enable_guard_regions = false; - Handle<JSArrayBuffer> global_buffer = - NewArrayBuffer(isolate_, globals_size, enable_guard_regions); - globals_ = global_buffer; - if (globals_.is_null()) { - thrower_->RangeError("Out of memory: wasm globals"); - return {}; - } - Address old_globals_start = compiled_module_->GetGlobalsStartOrNull(); - Address new_globals_start = - static_cast<Address>(global_buffer->backing_store()); - code_specialization.RelocateGlobals(old_globals_start, new_globals_start); - // The address of the backing buffer for the golbals is in native memory - // and, thus, not moving. We need it saved for - // serialization/deserialization purposes - so that the other end - // understands how to relocate the references. We still need to save the - // JSArrayBuffer on the instance, to keep it all alive. - WasmCompiledModule::SetGlobalsStartAddressFrom(factory, compiled_module_, - global_buffer); - instance->set_globals_buffer(*global_buffer); - } - - //-------------------------------------------------------------------------- - // Prepare for initialization of function tables. - //-------------------------------------------------------------------------- - int function_table_count = - static_cast<int>(module_->function_tables.size()); - table_instances_.reserve(module_->function_tables.size()); - for (int index = 0; index < function_table_count; ++index) { - table_instances_.push_back( - {Handle<WasmTableObject>::null(), Handle<FixedArray>::null(), - Handle<FixedArray>::null(), Handle<FixedArray>::null()}); - } - - //-------------------------------------------------------------------------- - // Process the imports for the module. - //-------------------------------------------------------------------------- - int num_imported_functions = ProcessImports(code_table, instance); - if (num_imported_functions < 0) return {}; - - //-------------------------------------------------------------------------- - // Process the initialization for the module's globals. - //-------------------------------------------------------------------------- - InitGlobals(); - - //-------------------------------------------------------------------------- - // Set up the indirect function tables for the new instance. - //-------------------------------------------------------------------------- - if (function_table_count > 0) - InitializeTables(instance, &code_specialization); - - //-------------------------------------------------------------------------- - // Set up the memory for the new instance. - //-------------------------------------------------------------------------- - uint32_t min_mem_pages = module_->min_mem_pages; - (module_->is_wasm() ? isolate_->counters()->wasm_wasm_min_mem_pages_count() - : isolate_->counters()->wasm_asm_min_mem_pages_count()) - ->AddSample(min_mem_pages); - - if (!memory_.is_null()) { - // Set externally passed ArrayBuffer non neuterable. - memory_->set_is_neuterable(false); - memory_->set_is_wasm_buffer(true); - - DCHECK_IMPLIES(EnableGuardRegions(), - module_->is_asm_js() || memory_->has_guard_region()); - } else if (min_mem_pages > 0) { - memory_ = AllocateMemory(min_mem_pages); - if (memory_.is_null()) return {}; // failed to allocate memory - } +void wasm::TableSet(ErrorThrower* thrower, Isolate* isolate, + Handle<WasmTableObject> table, int32_t index, + Handle<JSFunction> function) { + Handle<FixedArray> array(table->functions(), isolate); - //-------------------------------------------------------------------------- - // Check that indirect function table segments are within bounds. - //-------------------------------------------------------------------------- - for (WasmTableInit& table_init : module_->table_inits) { - DCHECK(table_init.table_index < table_instances_.size()); - uint32_t base = EvalUint32InitExpr(table_init.offset); - uint32_t table_size = - table_instances_[table_init.table_index].function_table->length(); - if (!in_bounds(base, static_cast<uint32_t>(table_init.entries.size()), - table_size)) { - thrower_->LinkError("table initializer is out of bounds"); - return {}; - } - } - - //-------------------------------------------------------------------------- - // Check that memory segments are within bounds. - //-------------------------------------------------------------------------- - for (WasmDataSegment& seg : module_->data_segments) { - uint32_t base = EvalUint32InitExpr(seg.dest_addr); - uint32_t mem_size = memory_.is_null() - ? 0 : static_cast<uint32_t>(memory_->byte_length()->Number()); - if (!in_bounds(base, seg.source_size, mem_size)) { - thrower_->LinkError("data segment is out of bounds"); - return {}; - } - } - - //-------------------------------------------------------------------------- - // Initialize memory. - //-------------------------------------------------------------------------- - if (!memory_.is_null()) { - Address mem_start = static_cast<Address>(memory_->backing_store()); - uint32_t mem_size = - static_cast<uint32_t>(memory_->byte_length()->Number()); - LoadDataSegments(mem_start, mem_size); - - uint32_t old_mem_size = compiled_module_->mem_size(); - Address old_mem_start = compiled_module_->GetEmbeddedMemStartOrNull(); - // We might get instantiated again with the same memory. No patching - // needed in this case. - if (old_mem_start != mem_start || old_mem_size != mem_size) { - code_specialization.RelocateMemoryReferences( - old_mem_start, old_mem_size, mem_start, mem_size); - } - // Just like with globals, we need to keep both the JSArrayBuffer - // and save the start pointer. - instance->set_memory_buffer(*memory_); - WasmCompiledModule::SetSpecializationMemInfoFrom( - factory, compiled_module_, memory_); - } - - //-------------------------------------------------------------------------- - // Set up the runtime support for the new instance. - //-------------------------------------------------------------------------- - Handle<WeakCell> weak_link = factory->NewWeakCell(instance); - - for (int i = num_imported_functions + FLAG_skip_compiling_wasm_funcs, - num_functions = static_cast<int>(module_->functions.size()); - i < num_functions; ++i) { - Handle<Code> code = handle(Code::cast(code_table->get(i)), isolate_); - if (code->kind() == Code::WASM_FUNCTION) { - Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED); - deopt_data->set(0, *weak_link); - deopt_data->set(1, Smi::FromInt(i)); - code->set_deoptimization_data(*deopt_data); - continue; - } - DCHECK_EQ(Builtins::kWasmCompileLazy, code->builtin_index()); - if (code->deoptimization_data()->length() == 0) continue; - DCHECK_LE(2, code->deoptimization_data()->length()); - DCHECK_EQ(i, Smi::cast(code->deoptimization_data()->get(1))->value()); - code->deoptimization_data()->set(0, *weak_link); - } - - //-------------------------------------------------------------------------- - // Set up the exports object for the new instance. - //-------------------------------------------------------------------------- - ProcessExports(code_table, instance, compiled_module_); - - //-------------------------------------------------------------------------- - // Add instance to Memory object - //-------------------------------------------------------------------------- - DCHECK(wasm::IsWasmInstance(*instance)); - if (instance->has_memory_object()) { - instance->memory_object()->AddInstance(isolate_, instance); - } - - //-------------------------------------------------------------------------- - // Initialize the indirect function tables. - //-------------------------------------------------------------------------- - if (function_table_count > 0) LoadTableSegments(code_table, instance); - - // Patch all code with the relocations registered in code_specialization. - code_specialization.RelocateDirectCalls(instance); - code_specialization.ApplyToWholeInstance(*instance, SKIP_ICACHE_FLUSH); - - FlushICache(isolate_, code_table); - - //-------------------------------------------------------------------------- - // Unpack and notify signal handler of protected instructions. - //-------------------------------------------------------------------------- - if (trap_handler::UseTrapHandler()) { - UnpackAndRegisterProtectedInstructions(isolate_, code_table); - } - - //-------------------------------------------------------------------------- - // Set up and link the new instance. - //-------------------------------------------------------------------------- - { - Handle<Object> global_handle = - isolate_->global_handles()->Create(*instance); - Handle<WeakCell> link_to_clone = factory->NewWeakCell(compiled_module_); - Handle<WeakCell> link_to_owning_instance = factory->NewWeakCell(instance); - MaybeHandle<WeakCell> link_to_original; - MaybeHandle<WasmCompiledModule> original; - if (!owner.is_null()) { - // prepare the data needed for publishing in a chain, but don't link - // just yet, because - // we want all the publishing to happen free from GC interruptions, and - // so we do it in - // one GC-free scope afterwards. - original = handle(owner.ToHandleChecked()->compiled_module()); - link_to_original = factory->NewWeakCell(original.ToHandleChecked()); - } - // Publish the new instance to the instances chain. - { - DisallowHeapAllocation no_gc; - if (!link_to_original.is_null()) { - compiled_module_->set_weak_next_instance( - link_to_original.ToHandleChecked()); - original.ToHandleChecked()->set_weak_prev_instance(link_to_clone); - compiled_module_->set_weak_wasm_module( - original.ToHandleChecked()->weak_wasm_module()); - } - module_object_->SetEmbedderField(0, *compiled_module_); - compiled_module_->set_weak_owning_instance(link_to_owning_instance); - GlobalHandles::MakeWeak(global_handle.location(), - global_handle.location(), &InstanceFinalizer, - v8::WeakCallbackType::kFinalizer); - } - } - - //-------------------------------------------------------------------------- - // Debugging support. - //-------------------------------------------------------------------------- - // Set all breakpoints that were set on the shared module. - WasmSharedModuleData::SetBreakpointsOnNewInstance( - compiled_module_->shared(), instance); - - if (FLAG_wasm_interpret_all && module_->is_wasm()) { - Handle<WasmDebugInfo> debug_info = - WasmInstanceObject::GetOrCreateDebugInfo(instance); - std::vector<int> func_indexes; - for (int func_index = num_imported_functions, - num_wasm_functions = static_cast<int>(module_->functions.size()); - func_index < num_wasm_functions; ++func_index) { - func_indexes.push_back(func_index); - } - WasmDebugInfo::RedirectToInterpreter( - debug_info, Vector<int>(func_indexes.data(), - static_cast<int>(func_indexes.size()))); - } - - //-------------------------------------------------------------------------- - // Run the start function if one was specified. - //-------------------------------------------------------------------------- - if (module_->start_function_index >= 0) { - HandleScope scope(isolate_); - int start_index = module_->start_function_index; - Handle<Code> startup_code = EnsureExportedLazyDeoptData( - isolate_, instance, code_table, start_index); - FunctionSig* sig = module_->functions[start_index].sig; - Handle<Code> wrapper_code = - js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper( - isolate_, module_, startup_code, start_index); - Handle<WasmExportedFunction> startup_fct = WasmExportedFunction::New( - isolate_, instance, MaybeHandle<String>(), start_index, - static_cast<int>(sig->parameter_count()), wrapper_code); - RecordStats(isolate_, *startup_code, is_sync_); - // Call the JS function. - Handle<Object> undefined = factory->undefined_value(); - MaybeHandle<Object> retval = - Execution::Call(isolate_, startup_fct, undefined, 0, nullptr); - - if (retval.is_null()) { - DCHECK(isolate_->has_pending_exception()); - isolate_->OptionalRescheduleException(false); - // It's unfortunate that the new instance is already linked in the - // chain. However, we need to set up everything before executing the - // start function, such that stack trace information can be generated - // correctly already in the start function. - return {}; - } - } - - DCHECK(!isolate_->has_pending_exception()); - TRACE("Finishing instance %d\n", compiled_module_->instance_id()); - TRACE_CHAIN(module_object_->compiled_module()); - return instance; - } - - private: - // Represents the initialized state of a table. - struct TableInstance { - Handle<WasmTableObject> table_object; // WebAssembly.Table instance - Handle<FixedArray> js_wrappers; // JSFunctions exported - Handle<FixedArray> function_table; // internal code array - Handle<FixedArray> signature_table; // internal sig array - }; - - Isolate* isolate_; - WasmModule* const module_; - constexpr static bool is_sync_ = true; - ErrorThrower* thrower_; - Handle<WasmModuleObject> module_object_; - Handle<JSReceiver> ffi_; // TODO(titzer): Use MaybeHandle - Handle<JSArrayBuffer> memory_; // TODO(titzer): Use MaybeHandle - Handle<JSArrayBuffer> globals_; - Handle<WasmCompiledModule> compiled_module_; - std::vector<TableInstance> table_instances_; - std::vector<Handle<JSFunction>> js_wrappers_; - JSToWasmWrapperCache js_to_wasm_cache_; - -// Helper routines to print out errors with imports. -#define ERROR_THROWER_WITH_MESSAGE(TYPE) \ - void Report##TYPE(const char* error, uint32_t index, \ - Handle<String> module_name, Handle<String> import_name) { \ - thrower_->TYPE("Import #%d module=\"%.*s\" function=\"%.*s\" error: %s", \ - index, module_name->length(), \ - module_name->ToCString().get(), import_name->length(), \ - import_name->ToCString().get(), error); \ - } \ - \ - MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index, \ - Handle<String> module_name) { \ - thrower_->TYPE("Import #%d module=\"%.*s\" error: %s", index, \ - module_name->length(), module_name->ToCString().get(), \ - error); \ - return MaybeHandle<Object>(); \ - } - - ERROR_THROWER_WITH_MESSAGE(LinkError) - ERROR_THROWER_WITH_MESSAGE(TypeError) - - // Look up an import value in the {ffi_} object. - MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name, - Handle<String> import_name) { - // We pre-validated in the js-api layer that the ffi object is present, and - // a JSObject, if the module has imports. - DCHECK(!ffi_.is_null()); - - // Look up the module first. - MaybeHandle<Object> result = - Object::GetPropertyOrElement(ffi_, module_name); - if (result.is_null()) { - return ReportTypeError("module not found", index, module_name); - } - - Handle<Object> module = result.ToHandleChecked(); - - // Look up the value in the module. - if (!module->IsJSReceiver()) { - return ReportTypeError("module is not an object or function", index, - module_name); - } - - result = Object::GetPropertyOrElement(module, import_name); - if (result.is_null()) { - ReportLinkError("import not found", index, module_name, import_name); - return MaybeHandle<JSFunction>(); - } - - return result; - } - - uint32_t EvalUint32InitExpr(const WasmInitExpr& expr) { - switch (expr.kind) { - case WasmInitExpr::kI32Const: - return expr.val.i32_const; - case WasmInitExpr::kGlobalIndex: { - uint32_t offset = module_->globals[expr.val.global_index].offset; - return *reinterpret_cast<uint32_t*>(raw_buffer_ptr(globals_, offset)); - } - default: - UNREACHABLE(); - return 0; - } - } - - bool in_bounds(uint32_t offset, uint32_t size, uint32_t upper) { - return offset + size <= upper && offset + size >= offset; - } - - // Load data segments into the memory. - void LoadDataSegments(Address mem_addr, size_t mem_size) { - Handle<SeqOneByteString> module_bytes(compiled_module_->module_bytes(), - isolate_); - for (const WasmDataSegment& segment : module_->data_segments) { - uint32_t source_size = segment.source_size; - // Segments of size == 0 are just nops. - if (source_size == 0) continue; - uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr); - DCHECK(in_bounds(dest_offset, source_size, - static_cast<uint32_t>(mem_size))); - byte* dest = mem_addr + dest_offset; - const byte* src = reinterpret_cast<const byte*>( - module_bytes->GetCharsAddress() + segment.source_offset); - memcpy(dest, src, source_size); - } - } - - void WriteGlobalValue(WasmGlobal& global, Handle<Object> value) { - double num = value->Number(); - TRACE("init [globals+%u] = %lf, type = %s\n", global.offset, num, - WasmOpcodes::TypeName(global.type)); - switch (global.type) { - case kWasmI32: - *GetRawGlobalPtr<int32_t>(global) = static_cast<int32_t>(num); - break; - case kWasmI64: - // TODO(titzer): initialization of imported i64 globals. - UNREACHABLE(); - break; - case kWasmF32: - *GetRawGlobalPtr<float>(global) = static_cast<float>(num); - break; - case kWasmF64: - *GetRawGlobalPtr<double>(global) = static_cast<double>(num); - break; - default: - UNREACHABLE(); - } - } - - // Process the imports, including functions, tables, globals, and memory, in - // order, loading them from the {ffi_} object. Returns the number of imported - // functions. - int ProcessImports(Handle<FixedArray> code_table, - Handle<WasmInstanceObject> instance) { - int num_imported_functions = 0; - int num_imported_tables = 0; - for (int index = 0; index < static_cast<int>(module_->import_table.size()); - ++index) { - WasmImport& import = module_->import_table[index]; - - Handle<String> module_name; - MaybeHandle<String> maybe_module_name = - WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate_, compiled_module_, import.module_name_offset, - import.module_name_length); - if (!maybe_module_name.ToHandle(&module_name)) return -1; - - Handle<String> import_name; - MaybeHandle<String> maybe_import_name = - WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate_, compiled_module_, import.field_name_offset, - import.field_name_length); - if (!maybe_import_name.ToHandle(&import_name)) return -1; - - MaybeHandle<Object> result = - LookupImport(index, module_name, import_name); - if (thrower_->error()) return -1; - Handle<Object> value = result.ToHandleChecked(); - - switch (import.kind) { - case kExternalFunction: { - // Function imports must be callable. - if (!value->IsCallable()) { - ReportLinkError("function import requires a callable", index, - module_name, import_name); - return -1; - } - - Handle<Code> import_wrapper = CompileImportWrapper( - isolate_, index, module_->functions[import.index].sig, - Handle<JSReceiver>::cast(value), module_name, import_name, - module_->get_origin()); - if (import_wrapper.is_null()) { - ReportLinkError( - "imported function does not match the expected type", index, - module_name, import_name); - return -1; - } - code_table->set(num_imported_functions, *import_wrapper); - RecordStats(isolate_, *import_wrapper, is_sync_); - num_imported_functions++; - break; - } - case kExternalTable: { - if (!WasmJs::IsWasmTableObject(isolate_, value)) { - ReportLinkError("table import requires a WebAssembly.Table", index, - module_name, import_name); - return -1; - } - WasmIndirectFunctionTable& table = - module_->function_tables[num_imported_tables]; - TableInstance& table_instance = table_instances_[num_imported_tables]; - table_instance.table_object = Handle<WasmTableObject>::cast(value); - table_instance.js_wrappers = Handle<FixedArray>( - table_instance.table_object->functions(), isolate_); - - int imported_cur_size = table_instance.js_wrappers->length(); - if (imported_cur_size < static_cast<int>(table.min_size)) { - thrower_->LinkError( - "table import %d is smaller than minimum %d, got %u", index, - table.min_size, imported_cur_size); - return -1; - } - - if (table.has_max) { - int64_t imported_max_size = - table_instance.table_object->maximum_length(); - if (imported_max_size < 0) { - thrower_->LinkError( - "table import %d has no maximum length, expected %d", index, - table.max_size); - return -1; - } - if (imported_max_size > table.max_size) { - thrower_->LinkError( - "table import %d has maximum larger than maximum %d, " - "got %" PRIx64, - index, table.max_size, imported_max_size); - return -1; - } - } - - // Allocate a new dispatch table and signature table. - int table_size = imported_cur_size; - table_instance.function_table = - isolate_->factory()->NewFixedArray(table_size); - table_instance.signature_table = - isolate_->factory()->NewFixedArray(table_size); - for (int i = 0; i < table_size; ++i) { - table_instance.signature_table->set(i, - Smi::FromInt(kInvalidSigIndex)); - } - // Initialize the dispatch table with the (foreign) JS functions - // that are already in the table. - for (int i = 0; i < table_size; ++i) { - Handle<Object> val(table_instance.js_wrappers->get(i), isolate_); - if (!val->IsJSFunction()) continue; - WasmFunction* function = - GetWasmFunctionForImportWrapper(isolate_, val); - if (function == nullptr) { - thrower_->LinkError("table import %d[%d] is not a WASM function", - index, i); - return -1; - } - int sig_index = table.map.FindOrInsert(function->sig); - table_instance.signature_table->set(i, Smi::FromInt(sig_index)); - table_instance.function_table->set(i, *UnwrapImportWrapper(val)); - } - - num_imported_tables++; - break; - } - case kExternalMemory: { - // Validation should have failed if more than one memory object was - // provided. - DCHECK(!instance->has_memory_object()); - if (!WasmJs::IsWasmMemoryObject(isolate_, value)) { - ReportLinkError("memory import must be a WebAssembly.Memory object", - index, module_name, import_name); - return -1; - } - auto memory = Handle<WasmMemoryObject>::cast(value); - DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory)); - instance->set_memory_object(*memory); - memory_ = Handle<JSArrayBuffer>(memory->buffer(), isolate_); - uint32_t imported_cur_pages = static_cast<uint32_t>( - memory_->byte_length()->Number() / WasmModule::kPageSize); - if (imported_cur_pages < module_->min_mem_pages) { - thrower_->LinkError( - "memory import %d is smaller than maximum %u, got %u", index, - module_->min_mem_pages, imported_cur_pages); - } - int32_t imported_max_pages = memory->maximum_pages(); - if (module_->has_max_mem) { - if (imported_max_pages < 0) { - thrower_->LinkError( - "memory import %d has no maximum limit, expected at most %u", - index, imported_max_pages); - return -1; - } - if (static_cast<uint32_t>(imported_max_pages) > - module_->max_mem_pages) { - thrower_->LinkError( - "memory import %d has larger maximum than maximum %u, got %d", - index, module_->max_mem_pages, imported_max_pages); - return -1; - } - } - break; - } - case kExternalGlobal: { - // Global imports are converted to numbers and written into the - // {globals_} array buffer. - if (module_->globals[import.index].type == kWasmI64) { - ReportLinkError("global import cannot have type i64", index, - module_name, import_name); - return -1; - } - if (module_->is_asm_js()) { - if (module_->globals[import.index].type == kWasmI32) { - value = Object::ToInt32(isolate_, value).ToHandleChecked(); - } else { - value = Object::ToNumber(value).ToHandleChecked(); - } - } - if (!value->IsNumber()) { - ReportLinkError("global import must be a number", index, - module_name, import_name); - return -1; - } - WriteGlobalValue(module_->globals[import.index], value); - break; - } - default: - UNREACHABLE(); - break; - } - } - return num_imported_functions; - } - - template <typename T> - T* GetRawGlobalPtr(WasmGlobal& global) { - return reinterpret_cast<T*>(raw_buffer_ptr(globals_, global.offset)); - } - - // Process initialization of globals. - void InitGlobals() { - for (auto global : module_->globals) { - switch (global.init.kind) { - case WasmInitExpr::kI32Const: - *GetRawGlobalPtr<int32_t>(global) = global.init.val.i32_const; - break; - case WasmInitExpr::kI64Const: - *GetRawGlobalPtr<int64_t>(global) = global.init.val.i64_const; - break; - case WasmInitExpr::kF32Const: - *GetRawGlobalPtr<float>(global) = global.init.val.f32_const; - break; - case WasmInitExpr::kF64Const: - *GetRawGlobalPtr<double>(global) = global.init.val.f64_const; - break; - case WasmInitExpr::kGlobalIndex: { - // Initialize with another global. - uint32_t new_offset = global.offset; - uint32_t old_offset = - module_->globals[global.init.val.global_index].offset; - TRACE("init [globals+%u] = [globals+%d]\n", global.offset, - old_offset); - size_t size = (global.type == kWasmI64 || global.type == kWasmF64) - ? sizeof(double) - : sizeof(int32_t); - memcpy(raw_buffer_ptr(globals_, new_offset), - raw_buffer_ptr(globals_, old_offset), size); - break; - } - case WasmInitExpr::kNone: - // Happens with imported globals. - break; - default: - UNREACHABLE(); - break; - } - } - } - - // Allocate memory for a module instance as a new JSArrayBuffer. - Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { - if (min_mem_pages > FLAG_wasm_max_mem_pages) { - thrower_->RangeError("Out of memory: wasm memory too large"); - return Handle<JSArrayBuffer>::null(); - } - const bool enable_guard_regions = EnableGuardRegions(); - Handle<JSArrayBuffer> mem_buffer = NewArrayBuffer( - isolate_, min_mem_pages * WasmModule::kPageSize, enable_guard_regions); - - if (mem_buffer.is_null()) { - thrower_->RangeError("Out of memory: wasm memory"); - } - return mem_buffer; - } - - bool NeedsWrappers() { - if (module_->num_exported_functions > 0) return true; - for (auto table_instance : table_instances_) { - if (!table_instance.js_wrappers.is_null()) return true; - } - for (auto table : module_->function_tables) { - if (table.exported) return true; - } - return false; - } - - // Process the exports, creating wrappers for functions, tables, memories, - // and globals. - void ProcessExports(Handle<FixedArray> code_table, - Handle<WasmInstanceObject> instance, - Handle<WasmCompiledModule> compiled_module) { - if (NeedsWrappers()) { - // Fill the table to cache the exported JSFunction wrappers. - js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(), - Handle<JSFunction>::null()); - } - - Handle<JSObject> exports_object; - if (module_->is_wasm()) { - // Create the "exports" object. - exports_object = isolate_->factory()->NewJSObjectWithNullProto(); - } else if (module_->is_asm_js()) { - Handle<JSFunction> object_function = Handle<JSFunction>( - isolate_->native_context()->object_function(), isolate_); - exports_object = isolate_->factory()->NewJSObject(object_function); - } else { - UNREACHABLE(); - } - Handle<String> exports_name = - isolate_->factory()->InternalizeUtf8String("exports"); - JSObject::AddProperty(instance, exports_name, exports_object, NONE); - - Handle<String> single_function_name = - isolate_->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName); - - PropertyDescriptor desc; - desc.set_writable(module_->is_asm_js()); - desc.set_enumerable(true); - desc.set_configurable(module_->is_asm_js()); - - // Store weak references to all exported functions. - Handle<FixedArray> weak_exported_functions; - if (compiled_module->has_weak_exported_functions()) { - weak_exported_functions = compiled_module->weak_exported_functions(); - } else { - int export_count = 0; - for (WasmExport& exp : module_->export_table) { - if (exp.kind == kExternalFunction) ++export_count; - } - weak_exported_functions = - isolate_->factory()->NewFixedArray(export_count); - compiled_module->set_weak_exported_functions(weak_exported_functions); - } - - // Process each export in the export table. - int export_index = 0; // Index into {weak_exported_functions}. - for (WasmExport& exp : module_->export_table) { - Handle<String> name = - WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate_, compiled_module_, exp.name_offset, exp.name_length) - .ToHandleChecked(); - Handle<JSObject> export_to; - if (module_->is_asm_js() && exp.kind == kExternalFunction && - String::Equals(name, single_function_name)) { - export_to = instance; - } else { - export_to = exports_object; - } - - switch (exp.kind) { - case kExternalFunction: { - // Wrap and export the code as a JSFunction. - WasmFunction& function = module_->functions[exp.index]; - int func_index = - static_cast<int>(module_->functions.size() + export_index); - Handle<JSFunction> js_function = js_wrappers_[exp.index]; - if (js_function.is_null()) { - // Wrap the exported code as a JSFunction. - Handle<Code> export_code = - code_table->GetValueChecked<Code>(isolate_, func_index); - MaybeHandle<String> func_name; - if (module_->is_asm_js()) { - // For modules arising from asm.js, honor the names section. - func_name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate_, compiled_module_, function.name_offset, - function.name_length) - .ToHandleChecked(); - } - js_function = WasmExportedFunction::New( - isolate_, instance, func_name, function.func_index, - static_cast<int>(function.sig->parameter_count()), export_code); - js_wrappers_[exp.index] = js_function; - } - desc.set_value(js_function); - Handle<WeakCell> weak_export = - isolate_->factory()->NewWeakCell(js_function); - DCHECK_GT(weak_exported_functions->length(), export_index); - weak_exported_functions->set(export_index, *weak_export); - export_index++; - break; - } - case kExternalTable: { - // Export a table as a WebAssembly.Table object. - TableInstance& table_instance = table_instances_[exp.index]; - WasmIndirectFunctionTable& table = - module_->function_tables[exp.index]; - if (table_instance.table_object.is_null()) { - uint32_t maximum = - table.has_max ? table.max_size : FLAG_wasm_max_table_size; - table_instance.table_object = WasmTableObject::New( - isolate_, table.min_size, maximum, &table_instance.js_wrappers); - } - desc.set_value(table_instance.table_object); - break; - } - case kExternalMemory: { - // Export the memory as a WebAssembly.Memory object. - Handle<WasmMemoryObject> memory_object; - if (!instance->has_memory_object()) { - // If there was no imported WebAssembly.Memory object, create one. - memory_object = WasmMemoryObject::New( - isolate_, - (instance->has_memory_buffer()) - ? handle(instance->memory_buffer()) - : Handle<JSArrayBuffer>::null(), - (module_->max_mem_pages != 0) ? module_->max_mem_pages : -1); - instance->set_memory_object(*memory_object); - } else { - memory_object = - Handle<WasmMemoryObject>(instance->memory_object(), isolate_); - DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory_object)); - memory_object->ResetInstancesLink(isolate_); - } - - desc.set_value(memory_object); - break; - } - case kExternalGlobal: { - // Export the value of the global variable as a number. - WasmGlobal& global = module_->globals[exp.index]; - double num = 0; - switch (global.type) { - case kWasmI32: - num = *GetRawGlobalPtr<int32_t>(global); - break; - case kWasmF32: - num = *GetRawGlobalPtr<float>(global); - break; - case kWasmF64: - num = *GetRawGlobalPtr<double>(global); - break; - case kWasmI64: - thrower_->LinkError( - "export of globals of type I64 is not allowed."); - break; - default: - UNREACHABLE(); - } - desc.set_value(isolate_->factory()->NewNumber(num)); - break; - } - default: - UNREACHABLE(); - break; - } - - v8::Maybe<bool> status = JSReceiver::DefineOwnProperty( - isolate_, export_to, name, &desc, Object::THROW_ON_ERROR); - if (!status.IsJust()) { - thrower_->LinkError("export of %.*s failed.", name->length(), - name->ToCString().get()); - return; - } - } - DCHECK_EQ(export_index, weak_exported_functions->length()); - - if (module_->is_wasm()) { - v8::Maybe<bool> success = JSReceiver::SetIntegrityLevel( - exports_object, FROZEN, Object::DONT_THROW); - DCHECK(success.FromMaybe(false)); - USE(success); - } - } - - void InitializeTables(Handle<WasmInstanceObject> instance, - CodeSpecialization* code_specialization) { - int function_table_count = - static_cast<int>(module_->function_tables.size()); - Handle<FixedArray> new_function_tables = - isolate_->factory()->NewFixedArray(function_table_count); - Handle<FixedArray> new_signature_tables = - isolate_->factory()->NewFixedArray(function_table_count); - for (int index = 0; index < function_table_count; ++index) { - WasmIndirectFunctionTable& table = module_->function_tables[index]; - TableInstance& table_instance = table_instances_[index]; - int table_size = static_cast<int>(table.min_size); - - if (table_instance.function_table.is_null()) { - // Create a new dispatch table if necessary. - table_instance.function_table = - isolate_->factory()->NewFixedArray(table_size); - table_instance.signature_table = - isolate_->factory()->NewFixedArray(table_size); - for (int i = 0; i < table_size; ++i) { - // Fill the table with invalid signature indexes so that - // uninitialized entries will always fail the signature check. - table_instance.signature_table->set(i, - Smi::FromInt(kInvalidSigIndex)); - } - } else { - // Table is imported, patch table bounds check - DCHECK(table_size <= table_instance.function_table->length()); - if (table_size < table_instance.function_table->length()) { - code_specialization->PatchTableSize( - table_size, table_instance.function_table->length()); - } - } - - new_function_tables->set(static_cast<int>(index), - *table_instance.function_table); - new_signature_tables->set(static_cast<int>(index), - *table_instance.signature_table); - } - - FixedArray* old_function_tables = - compiled_module_->ptr_to_function_tables(); - DCHECK_EQ(old_function_tables->length(), new_function_tables->length()); - for (int i = 0, e = new_function_tables->length(); i < e; ++i) { - code_specialization->RelocateObject( - handle(old_function_tables->get(i), isolate_), - handle(new_function_tables->get(i), isolate_)); - } - FixedArray* old_signature_tables = - compiled_module_->ptr_to_signature_tables(); - DCHECK_EQ(old_signature_tables->length(), new_signature_tables->length()); - for (int i = 0, e = new_signature_tables->length(); i < e; ++i) { - code_specialization->RelocateObject( - handle(old_signature_tables->get(i), isolate_), - handle(new_signature_tables->get(i), isolate_)); - } - - compiled_module_->set_function_tables(new_function_tables); - compiled_module_->set_signature_tables(new_signature_tables); + if (index < 0 || index >= array->length()) { + thrower->RangeError("index out of bounds"); + return; } - void LoadTableSegments(Handle<FixedArray> code_table, - Handle<WasmInstanceObject> instance) { - int function_table_count = - static_cast<int>(module_->function_tables.size()); - for (int index = 0; index < function_table_count; ++index) { - WasmIndirectFunctionTable& table = module_->function_tables[index]; - TableInstance& table_instance = table_instances_[index]; - - Handle<FixedArray> all_dispatch_tables; - if (!table_instance.table_object.is_null()) { - // Get the existing dispatch table(s) with the WebAssembly.Table object. - all_dispatch_tables = - handle(table_instance.table_object->dispatch_tables()); - } - - // Count the number of table exports for each function (needed for lazy - // compilation). - std::unordered_map<uint32_t, uint32_t> num_table_exports; - if (compile_lazy(module_)) { - for (auto table_init : module_->table_inits) { - for (uint32_t func_index : table_init.entries) { - Code* code = - Code::cast(code_table->get(static_cast<int>(func_index))); - // Only increase the counter for lazy compile builtins (it's not - // needed otherwise). - if (code->is_wasm_code()) continue; - DCHECK_EQ(Builtins::kWasmCompileLazy, code->builtin_index()); - ++num_table_exports[func_index]; - } - } - } - - // TODO(titzer): this does redundant work if there are multiple tables, - // since initializations are not sorted by table index. - for (auto table_init : module_->table_inits) { - uint32_t base = EvalUint32InitExpr(table_init.offset); - DCHECK(in_bounds(base, static_cast<uint32_t>(table_init.entries.size()), - table_instance.function_table->length())); - for (int i = 0, e = static_cast<int>(table_init.entries.size()); i < e; - ++i) { - uint32_t func_index = table_init.entries[i]; - WasmFunction* function = &module_->functions[func_index]; - int table_index = static_cast<int>(i + base); - int32_t sig_index = table.map.Find(function->sig); - DCHECK_GE(sig_index, 0); - table_instance.signature_table->set(table_index, - Smi::FromInt(sig_index)); - Handle<Code> wasm_code = EnsureTableExportLazyDeoptData( - isolate_, instance, code_table, func_index, - table_instance.function_table, table_index, num_table_exports); - table_instance.function_table->set(table_index, *wasm_code); - - if (!all_dispatch_tables.is_null()) { - if (js_wrappers_[func_index].is_null()) { - // No JSFunction entry yet exists for this function. Create one. - // TODO(titzer): We compile JS->WASM wrappers for functions are - // not exported but are in an exported table. This should be done - // at module compile time and cached instead. - - Handle<Code> wrapper_code = - js_to_wasm_cache_.CloneOrCompileJSToWasmWrapper( - isolate_, module_, wasm_code, func_index); - MaybeHandle<String> func_name; - if (module_->is_asm_js()) { - // For modules arising from asm.js, honor the names section. - func_name = - WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate_, compiled_module_, function->name_offset, - function->name_length) - .ToHandleChecked(); - } - Handle<WasmExportedFunction> js_function = - WasmExportedFunction::New( - isolate_, instance, func_name, func_index, - static_cast<int>(function->sig->parameter_count()), - wrapper_code); - js_wrappers_[func_index] = js_function; - } - table_instance.js_wrappers->set(table_index, - *js_wrappers_[func_index]); - - UpdateDispatchTablesInternal(isolate_, all_dispatch_tables, - table_index, function, wasm_code); - } - } - } + Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate); -#ifdef DEBUG - // Check that the count of table exports was accurate. The entries are - // decremented on each export, so all should be zero now. - for (auto e : num_table_exports) { - DCHECK_EQ(0, e.second); - } -#endif + WasmFunction* wasm_function = nullptr; + Handle<Code> code = Handle<Code>::null(); + Handle<Object> value = handle(isolate->heap()->null_value()); - // TODO(titzer): we add the new dispatch table at the end to avoid - // redundant work and also because the new instance is not yet fully - // initialized. - if (!table_instance.table_object.is_null()) { - // Add the new dispatch table to the WebAssembly.Table object. - all_dispatch_tables = WasmTableObject::AddDispatchTable( - isolate_, table_instance.table_object, instance, index, - table_instance.function_table, table_instance.signature_table); - } - } + if (!function.is_null()) { + wasm_function = GetWasmFunctionForImportWrapper(isolate, function); + code = UnwrapImportWrapper(function); + value = Handle<Object>::cast(function); } -}; -bool wasm::IsWasmInstance(Object* object) { - return WasmInstanceObject::IsWasmInstanceObject(object); + UpdateDispatchTables(isolate, dispatch_tables, index, wasm_function, code); + array->set(index, *value); } Handle<Script> wasm::GetScript(Handle<JSObject> instance) { @@ -2312,8 +433,14 @@ Handle<Script> wasm::GetScript(Handle<JSObject> instance) { } bool wasm::IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context) { + // TODO(wasm): Once wasm has its own CSP policy, we should introduce a + // separate callback that includes information about the module about to be + // compiled. For the time being, pass an empty string as placeholder for the + // sources. return isolate->allow_code_gen_callback() == nullptr || - isolate->allow_code_gen_callback()(v8::Utils::ToLocal(context)); + isolate->allow_code_gen_callback()( + v8::Utils::ToLocal(context), + v8::Utils::ToLocal(isolate->factory()->empty_string())); } void wasm::DetachWebAssemblyMemoryBuffer(Isolate* isolate, @@ -2357,8 +484,9 @@ void testing::ValidateInstancesChain(Isolate* isolate, CHECK((prev == nullptr && !current_instance->has_weak_prev_instance()) || current_instance->ptr_to_weak_prev_instance()->value() == prev); CHECK_EQ(current_instance->ptr_to_weak_wasm_module()->value(), *module_obj); - CHECK(IsWasmInstance( - current_instance->ptr_to_weak_owning_instance()->value())); + CHECK(current_instance->ptr_to_weak_owning_instance() + ->value() + ->IsWasmInstanceObject()); prev = current_instance; current_instance = WasmCompiledModule::cast( current_instance->ptr_to_weak_next_instance()->value()); @@ -2405,7 +533,7 @@ Handle<JSArray> wasm::GetImports(Isolate* isolate, // Create the result array. WasmModule* module = compiled_module->module(); int num_imports = static_cast<int>(module->import_table.size()); - Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0); + Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0); Handle<FixedArray> storage = factory->NewFixedArray(num_imports); JSArray::SetContent(array_object, storage); array_object->set_length(Smi::FromInt(num_imports)); @@ -2439,13 +567,11 @@ Handle<JSArray> wasm::GetImports(Isolate* isolate, MaybeHandle<String> import_module = WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate, compiled_module, import.module_name_offset, - import.module_name_length); + isolate, compiled_module, import.module_name); MaybeHandle<String> import_name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate, compiled_module, import.field_name_offset, - import.field_name_length); + isolate, compiled_module, import.field_name); JSObject::AddProperty(entry, module_string, import_module.ToHandleChecked(), NONE); @@ -2476,7 +602,7 @@ Handle<JSArray> wasm::GetExports(Isolate* isolate, // Create the result array. WasmModule* module = compiled_module->module(); int num_exports = static_cast<int>(module->export_table.size()); - Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0); + Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0); Handle<FixedArray> storage = factory->NewFixedArray(num_exports); JSArray::SetContent(array_object, storage); array_object->set_length(Smi::FromInt(num_exports)); @@ -2510,7 +636,7 @@ Handle<JSArray> wasm::GetExports(Isolate* isolate, MaybeHandle<String> export_name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate, compiled_module, exp.name_offset, exp.name_length); + isolate, compiled_module, exp.name); JSObject::AddProperty(entry, name_string, export_name.ToHandleChecked(), NONE); @@ -2544,45 +670,38 @@ Handle<JSArray> wasm::GetCustomSections(Isolate* isolate, std::vector<Handle<Object>> matching_sections; // Gather matching sections. - for (auto section : custom_sections) { + for (auto& section : custom_sections) { MaybeHandle<String> section_name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate, compiled_module, section.name_offset, section.name_length); + isolate, compiled_module, section.name); if (!name->Equals(*section_name.ToHandleChecked())) continue; // Make a copy of the payload data in the section. - void* allocation_base = nullptr; // Set by TryAllocateBackingStore - size_t allocation_length = 0; // Set by TryAllocateBackingStore - const bool enable_guard_regions = false; - void* memory = TryAllocateBackingStore(isolate, section.payload_length, - enable_guard_regions, - allocation_base, allocation_length); - - Handle<Object> section_data = factory->undefined_value(); - if (memory) { - Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); - const bool is_external = false; - JSArrayBuffer::Setup(buffer, isolate, is_external, allocation_base, - allocation_length, memory, - static_cast<int>(section.payload_length)); - DisallowHeapAllocation no_gc; // for raw access to string bytes. - Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(), - isolate); - const byte* start = - reinterpret_cast<const byte*>(module_bytes->GetCharsAddress()); - memcpy(memory, start + section.payload_offset, section.payload_length); - section_data = buffer; - } else { + size_t size = section.payload.length(); + void* memory = + size == 0 ? nullptr : isolate->array_buffer_allocator()->Allocate(size); + + if (size && !memory) { thrower->RangeError("out of memory allocating custom section data"); return Handle<JSArray>(); } + Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); + constexpr bool is_external = false; + JSArrayBuffer::Setup(buffer, isolate, is_external, memory, size, memory, + size); + DisallowHeapAllocation no_gc; // for raw access to string bytes. + Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(), + isolate); + const byte* start = + reinterpret_cast<const byte*>(module_bytes->GetCharsAddress()); + memcpy(memory, start + section.payload.offset(), section.payload.length()); - matching_sections.push_back(section_data); + matching_sections.push_back(buffer); } int num_custom_sections = static_cast<int>(matching_sections.size()); - Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0); + Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0); Handle<FixedArray> storage = factory->NewFixedArray(num_custom_sections); JSArray::SetContent(array_object, storage); array_object->set_length(Smi::FromInt(num_custom_sections)); @@ -2594,10 +713,37 @@ Handle<JSArray> wasm::GetCustomSections(Isolate* isolate, return array_object; } +Handle<FixedArray> wasm::DecodeLocalNames( + Isolate* isolate, Handle<WasmCompiledModule> compiled_module) { + Handle<SeqOneByteString> wire_bytes(compiled_module->module_bytes(), isolate); + LocalNames decoded_locals; + { + DisallowHeapAllocation no_gc; + wasm::DecodeLocalNames(wire_bytes->GetChars(), + wire_bytes->GetChars() + wire_bytes->length(), + &decoded_locals); + } + Handle<FixedArray> locals_names = + isolate->factory()->NewFixedArray(decoded_locals.max_function_index + 1); + for (LocalNamesPerFunction& func : decoded_locals.names) { + Handle<FixedArray> func_locals_names = + isolate->factory()->NewFixedArray(func.max_local_index + 1); + locals_names->set(func.function_index, *func_locals_names); + for (LocalName& name : func.names) { + Handle<String> name_str = + WasmCompiledModule::ExtractUtf8StringFromModuleBytes( + isolate, compiled_module, name.name) + .ToHandleChecked(); + func_locals_names->set(name.local_index, *name_str); + } + } + return locals_names; +} + bool wasm::SyncValidate(Isolate* isolate, const ModuleWireBytes& bytes) { if (bytes.start() == nullptr || bytes.length() == 0) return false; - ModuleResult result = - DecodeWasmModule(isolate, bytes.start(), bytes.end(), true, kWasmOrigin); + ModuleResult result = SyncDecodeWasmModule(isolate, bytes.start(), + bytes.end(), true, kWasmOrigin); return result.ok(); } @@ -2605,9 +751,8 @@ MaybeHandle<WasmModuleObject> wasm::SyncCompileTranslatedAsmJs( Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes, Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes) { - - ModuleResult result = DecodeWasmModule(isolate, bytes.start(), bytes.end(), - false, kAsmJsOrigin); + ModuleResult result = SyncDecodeWasmModule(isolate, bytes.start(), + bytes.end(), false, kAsmJsOrigin); if (result.failed()) { thrower->CompileFailed("Wasm decoding failed", result); return {}; @@ -2615,8 +760,7 @@ MaybeHandle<WasmModuleObject> wasm::SyncCompileTranslatedAsmJs( // Transfer ownership to the {WasmModuleWrapper} generated in // {CompileToModuleObject}. - constexpr bool is_sync = true; - CompilationHelper helper(isolate, std::move(result.val), is_sync); + ModuleCompiler helper(isolate, std::move(result.val)); return helper.CompileToModuleObject(thrower, bytes, asm_js_script, asm_js_offset_table_bytes); } @@ -2629,8 +773,13 @@ MaybeHandle<WasmModuleObject> wasm::SyncCompile(Isolate* isolate, return {}; } - ModuleResult result = - DecodeWasmModule(isolate, bytes.start(), bytes.end(), false, kWasmOrigin); + // TODO(titzer): only make a copy of the bytes if SharedArrayBuffer + std::unique_ptr<byte[]> copy(new byte[bytes.length()]); + memcpy(copy.get(), bytes.start(), bytes.length()); + ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length()); + + ModuleResult result = SyncDecodeWasmModule( + isolate, bytes_copy.start(), bytes_copy.end(), false, kWasmOrigin); if (result.failed()) { thrower->CompileFailed("Wasm decoding failed", result); return {}; @@ -2638,9 +787,8 @@ MaybeHandle<WasmModuleObject> wasm::SyncCompile(Isolate* isolate, // Transfer ownership to the {WasmModuleWrapper} generated in // {CompileToModuleObject}. - constexpr bool is_sync = true; - CompilationHelper helper(isolate, std::move(result.val), is_sync); - return helper.CompileToModuleObject(thrower, bytes, Handle<Script>(), + ModuleCompiler helper(isolate, std::move(result.val)); + return helper.CompileToModuleObject(thrower, bytes_copy, Handle<Script>(), Vector<const byte>()); } @@ -2648,8 +796,22 @@ MaybeHandle<WasmInstanceObject> wasm::SyncInstantiate( Isolate* isolate, ErrorThrower* thrower, Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory) { - InstantiationHelper helper(isolate, thrower, module_object, imports, memory); - return helper.Build(); + InstanceBuilder builder(isolate, thrower, module_object, imports, memory, + &InstanceFinalizer); + return builder.Build(); +} + +MaybeHandle<WasmInstanceObject> wasm::SyncCompileAndInstantiate( + Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes, + MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory) { + MaybeHandle<WasmModuleObject> module = + wasm::SyncCompile(isolate, thrower, bytes); + DCHECK_EQ(thrower->error(), module.is_null()); + if (module.is_null()) return {}; + + return wasm::SyncInstantiate(isolate, thrower, module.ToHandleChecked(), + Handle<JSReceiver>::null(), + Handle<JSArrayBuffer>::null()); } namespace { @@ -2688,480 +850,6 @@ void wasm::AsyncInstantiate(Isolate* isolate, Handle<JSPromise> promise, instance_object.ToHandleChecked()); } -// Encapsulates all the state and steps of an asynchronous compilation. -// An asynchronous compile job consists of a number of tasks that are executed -// as foreground and background tasks. Any phase that touches the V8 heap or -// allocates on the V8 heap (e.g. creating the module object) must be a -// foreground task. All other tasks (e.g. decoding and validating, the majority -// of the work of compilation) can be background tasks. -// TODO(wasm): factor out common parts of this with the synchronous pipeline. -class AsyncCompileJob { - // TODO(ahaas): Fix https://bugs.chromium.org/p/v8/issues/detail?id=6263 to - // make sure that d8 does not shut down before the AsyncCompileJob is - // finished. - public: - explicit AsyncCompileJob(Isolate* isolate, std::unique_ptr<byte[]> bytes_copy, - size_t length, Handle<Context> context, - Handle<JSPromise> promise) - : isolate_(isolate), - bytes_copy_(std::move(bytes_copy)), - wire_bytes_(bytes_copy_.get(), bytes_copy_.get() + length) { - // The handles for the context and promise must be deferred. - DeferredHandleScope deferred(isolate); - context_ = Handle<Context>(*context); - module_promise_ = Handle<JSPromise>(*promise); - deferred_handles_.push_back(deferred.Detach()); - } - - void Start() { - DoAsync<DecodeModule>(); // -- - } - - ~AsyncCompileJob() { - for (auto d : deferred_handles_) delete d; - } - - private: - Isolate* isolate_; - std::unique_ptr<byte[]> bytes_copy_; - ModuleWireBytes wire_bytes_; - Handle<Context> context_; - Handle<JSPromise> module_promise_; - std::unique_ptr<CompilationHelper> helper_; - std::unique_ptr<ModuleBytesEnv> module_bytes_env_; - - bool failed_ = false; - std::vector<DeferredHandles*> deferred_handles_; - Handle<WasmModuleObject> module_object_; - Handle<FixedArray> function_tables_; - Handle<FixedArray> signature_tables_; - Handle<WasmCompiledModule> compiled_module_; - Handle<FixedArray> code_table_; - std::unique_ptr<WasmInstance> temp_instance_ = nullptr; - size_t outstanding_units_ = 0; - size_t num_background_tasks_ = 0; - - void ReopenHandlesInDeferredScope() { - DeferredHandleScope deferred(isolate_); - function_tables_ = handle(*function_tables_, isolate_); - signature_tables_ = handle(*signature_tables_, isolate_); - code_table_ = handle(*code_table_, isolate_); - temp_instance_->ReopenHandles(isolate_); - for (auto& unit : helper_->compilation_units_) { - unit->ReopenCentryStub(); - } - deferred_handles_.push_back(deferred.Detach()); - } - - void AsyncCompileFailed(ErrorThrower& thrower) { - RejectPromise(isolate_, context_, thrower, module_promise_); - // The AsyncCompileJob is finished, we resolved the promise, we do not need - // the data anymore. We can delete the AsyncCompileJob object. - delete this; - } - - void AsyncCompileSucceeded(Handle<Object> result) { - ResolvePromise(isolate_, context_, module_promise_, result); - // The AsyncCompileJob is finished, we resolved the promise, we do not need - // the data anymore. We can delete the AsyncCompileJob object. - delete this; - } - - enum TaskType { SYNC, ASYNC }; - - // A closure to run a compilation step (either as foreground or background - // task) and schedule the next step(s), if any. - class CompileTask : NON_EXPORTED_BASE(public v8::Task) { - public: - AsyncCompileJob* job_ = nullptr; - CompileTask() {} - void Run() override = 0; // Force sub-classes to override Run(). - }; - - class AsyncCompileTask : public CompileTask {}; - - class SyncCompileTask : public CompileTask { - public: - void Run() final { - SaveContext saved_context(job_->isolate_); - job_->isolate_->set_context(*job_->context_); - RunImpl(); - } - - protected: - virtual void RunImpl() = 0; - }; - - template <typename Task, typename... Args> - void DoSync(Args&&... args) { - static_assert(std::is_base_of<SyncCompileTask, Task>::value, - "Scheduled type must be sync"); - Task* task = new Task(std::forward<Args>(args)...); - task->job_ = this; - V8::GetCurrentPlatform()->CallOnForegroundThread( - reinterpret_cast<v8::Isolate*>(isolate_), task); - } - - template <typename Task, typename... Args> - void DoAsync(Args&&... args) { - static_assert(std::is_base_of<AsyncCompileTask, Task>::value, - "Scheduled type must be async"); - Task* task = new Task(std::forward<Args>(args)...); - task->job_ = this; - V8::GetCurrentPlatform()->CallOnBackgroundThread( - task, v8::Platform::kShortRunningTask); - } - - //========================================================================== - // Step 1: (async) Decode the module. - //========================================================================== - class DecodeModule : public AsyncCompileTask { - void Run() override { - ModuleResult result; - { - DisallowHandleAllocation no_handle; - DisallowHeapAllocation no_allocation; - // Decode the module bytes. - TRACE_COMPILE("(1) Decoding module...\n"); - constexpr bool is_sync = true; - result = DecodeWasmModule(job_->isolate_, job_->wire_bytes_.start(), - job_->wire_bytes_.end(), false, kWasmOrigin, - !is_sync); - } - if (result.failed()) { - // Decoding failure; reject the promise and clean up. - job_->DoSync<DecodeFail>(std::move(result)); - } else { - // Decode passed. - job_->DoSync<PrepareAndStartCompile>(std::move(result.val)); - } - } - }; - - //========================================================================== - // Step 1b: (sync) Fail decoding the module. - //========================================================================== - class DecodeFail : public SyncCompileTask { - public: - explicit DecodeFail(ModuleResult result) : result_(std::move(result)) {} - - private: - ModuleResult result_; - void RunImpl() override { - TRACE_COMPILE("(1b) Decoding failed.\n"); - HandleScope scope(job_->isolate_); - ErrorThrower thrower(job_->isolate_, "AsyncCompile"); - thrower.CompileFailed("Wasm decoding failed", result_); - // {job_} is deleted in AsyncCompileFailed, therefore the {return}. - return job_->AsyncCompileFailed(thrower); - } - }; - - //========================================================================== - // Step 2 (sync): Create heap-allocated data and start compile. - //========================================================================== - class PrepareAndStartCompile : public SyncCompileTask { - public: - explicit PrepareAndStartCompile(std::unique_ptr<WasmModule> module) - : module_(std::move(module)) {} - - private: - std::unique_ptr<WasmModule> module_; - void RunImpl() override { - TRACE_COMPILE("(2) Prepare and start compile...\n"); - HandleScope scope(job_->isolate_); - - Factory* factory = job_->isolate_->factory(); - job_->temp_instance_.reset(new WasmInstance(module_.get())); - job_->temp_instance_->context = job_->context_; - job_->temp_instance_->mem_size = - WasmModule::kPageSize * module_->min_mem_pages; - job_->temp_instance_->mem_start = nullptr; - job_->temp_instance_->globals_start = nullptr; - - // Initialize the indirect tables with placeholders. - int function_table_count = - static_cast<int>(module_->function_tables.size()); - job_->function_tables_ = - factory->NewFixedArray(function_table_count, TENURED); - job_->signature_tables_ = - factory->NewFixedArray(function_table_count, TENURED); - for (int i = 0; i < function_table_count; ++i) { - job_->temp_instance_->function_tables[i] = - factory->NewFixedArray(1, TENURED); - job_->temp_instance_->signature_tables[i] = - factory->NewFixedArray(1, TENURED); - job_->function_tables_->set(i, - *job_->temp_instance_->function_tables[i]); - job_->signature_tables_->set( - i, *job_->temp_instance_->signature_tables[i]); - } - - // The {code_table} array contains import wrappers and functions (which - // are both included in {functions.size()}, and export wrappers. - // The results of compilation will be written into it. - int code_table_size = static_cast<int>(module_->functions.size() + - module_->num_exported_functions); - job_->code_table_ = factory->NewFixedArray(code_table_size, TENURED); - - // Initialize {code_table_} with the illegal builtin. All call sites - // will be patched at instantiation. - Handle<Code> illegal_builtin = job_->isolate_->builtins()->Illegal(); - // TODO(wasm): Fix this for lazy compilation. - for (uint32_t i = 0; i < module_->functions.size(); ++i) { - job_->code_table_->set(static_cast<int>(i), *illegal_builtin); - job_->temp_instance_->function_code[i] = illegal_builtin; - } - - job_->isolate_->counters()->wasm_functions_per_wasm_module()->AddSample( - static_cast<int>(module_->functions.size())); - - // Transfer ownership of the {WasmModule} to the {CompilationHelper}, but - // keep a pointer. - WasmModule* module = module_.get(); - constexpr bool is_sync = true; - job_->helper_.reset( - new CompilationHelper(job_->isolate_, std::move(module_), !is_sync)); - - DCHECK_LE(module->num_imported_functions, module->functions.size()); - size_t num_functions = - module->functions.size() - module->num_imported_functions; - if (num_functions == 0) { - job_->ReopenHandlesInDeferredScope(); - // Degenerate case of an empty module. - job_->DoSync<FinishCompile>(); - return; - } - - // Start asynchronous compilation tasks. - job_->num_background_tasks_ = - Max(static_cast<size_t>(1), - Min(num_functions, - Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), - V8::GetCurrentPlatform() - ->NumberOfAvailableBackgroundThreads()))); - job_->module_bytes_env_.reset(new ModuleBytesEnv( - module, job_->temp_instance_.get(), job_->wire_bytes_)); - job_->outstanding_units_ = job_->helper_->InitializeParallelCompilation( - module->functions, *job_->module_bytes_env_); - - // Reopen all handles which should survive in the DeferredHandleScope. - job_->ReopenHandlesInDeferredScope(); - for (size_t i = 0; i < job_->num_background_tasks_; ++i) { - job_->DoAsync<ExecuteCompilationUnits>(); - } - } - }; - - //========================================================================== - // Step 3 (async x K tasks): Execute compilation units. - //========================================================================== - class ExecuteCompilationUnits : public AsyncCompileTask { - void Run() override { - TRACE_COMPILE("(3) Compiling...\n"); - for (;;) { - { - DisallowHandleAllocation no_handle; - DisallowHeapAllocation no_allocation; - if (!job_->helper_->FetchAndExecuteCompilationUnit()) break; - } - // TODO(ahaas): Create one FinishCompilationUnit job for all compilation - // units. - job_->DoSync<FinishCompilationUnit>(); - // TODO(ahaas): Limit the number of outstanding compilation units to be - // finished to reduce memory overhead. - } - job_->helper_->module_->pending_tasks.get()->Signal(); - } - }; - - //========================================================================== - // Step 4 (sync x each function): Finish a single compilation unit. - //========================================================================== - class FinishCompilationUnit : public SyncCompileTask { - void RunImpl() override { - TRACE_COMPILE("(4a) Finishing compilation unit...\n"); - HandleScope scope(job_->isolate_); - if (job_->failed_) return; // already failed - - int func_index = -1; - ErrorThrower thrower(job_->isolate_, "AsyncCompile"); - Handle<Code> result = - job_->helper_->FinishCompilationUnit(&thrower, &func_index); - if (thrower.error()) { - job_->failed_ = true; - } else { - DCHECK(func_index >= 0); - job_->code_table_->set(func_index, *(result)); - } - if (thrower.error() || --job_->outstanding_units_ == 0) { - // All compilation units are done. We still need to wait for the - // background tasks to shut down and only then is it safe to finish the - // compile and delete this job. We can wait for that to happen also - // in a background task. - job_->DoAsync<WaitForBackgroundTasks>(std::move(thrower)); - } - } - }; - - //========================================================================== - // Step 4b (async): Wait for all background tasks to finish. - //========================================================================== - class WaitForBackgroundTasks : public AsyncCompileTask { - public: - explicit WaitForBackgroundTasks(ErrorThrower thrower) - : thrower_(std::move(thrower)) {} - - private: - ErrorThrower thrower_; - - void Run() override { - TRACE_COMPILE("(4b) Waiting for background tasks...\n"); - // Bump next_unit_, such that background tasks stop processing the queue. - job_->helper_->next_unit_.SetValue( - job_->helper_->compilation_units_.size()); - for (size_t i = 0; i < job_->num_background_tasks_; ++i) { - // We wait for it to finish. - job_->helper_->module_->pending_tasks.get()->Wait(); - } - if (thrower_.error()) { - job_->DoSync<FailCompile>(std::move(thrower_)); - } else { - job_->DoSync<FinishCompile>(); - } - } - }; - - //========================================================================== - // Step 5a (sync): Fail compilation (reject promise). - //========================================================================== - class FailCompile : public SyncCompileTask { - public: - explicit FailCompile(ErrorThrower thrower) : thrower_(std::move(thrower)) {} - - private: - ErrorThrower thrower_; - - void RunImpl() override { - TRACE_COMPILE("(5a) Fail compilation...\n"); - HandleScope scope(job_->isolate_); - return job_->AsyncCompileFailed(thrower_); - } - }; - - //========================================================================== - // Step 5b (sync): Finish heap-allocated data structures. - //========================================================================== - class FinishCompile : public SyncCompileTask { - void RunImpl() override { - TRACE_COMPILE("(5b) Finish compile...\n"); - HandleScope scope(job_->isolate_); - // At this point, compilation has completed. Update the code table. - constexpr bool is_sync = true; - for (size_t i = FLAG_skip_compiling_wasm_funcs; - i < job_->temp_instance_->function_code.size(); ++i) { - Code* code = Code::cast(job_->code_table_->get(static_cast<int>(i))); - RecordStats(job_->isolate_, code, !is_sync); - } - - // Create heap objects for script and module bytes to be stored in the - // shared module data. Asm.js is not compiled asynchronously. - Handle<Script> script = - CreateWasmScript(job_->isolate_, job_->wire_bytes_); - Handle<ByteArray> asm_js_offset_table; - // TODO(wasm): Improve efficiency of storing module wire bytes. - // 1. Only store relevant sections, not function bodies - // 2. Don't make a second copy of the bytes here; reuse the copy made - // for asynchronous compilation and store it as an external one - // byte string for serialization/deserialization. - Handle<String> module_bytes = - job_->isolate_->factory() - ->NewStringFromOneByte( - {job_->wire_bytes_.start(), job_->wire_bytes_.length()}, - TENURED) - .ToHandleChecked(); - DCHECK(module_bytes->IsSeqOneByteString()); - - // The {module_wrapper} will take ownership of the {WasmModule} object, - // and it will be destroyed when the GC reclaims the wrapper object. - Handle<WasmModuleWrapper> module_wrapper = WasmModuleWrapper::New( - job_->isolate_, job_->helper_->module_.release()); - - // Create the shared module data. - // TODO(clemensh): For the same module (same bytes / same hash), we should - // only have one WasmSharedModuleData. Otherwise, we might only set - // breakpoints on a (potentially empty) subset of the instances. - - Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New( - job_->isolate_, module_wrapper, - Handle<SeqOneByteString>::cast(module_bytes), script, - asm_js_offset_table); - - // Create the compiled module object and populate with compiled functions - // and information needed at instantiation time. This object needs to be - // serializable. Instantiation may occur off a deserialized version of - // this object. - job_->compiled_module_ = WasmCompiledModule::New( - job_->isolate_, shared, job_->code_table_, job_->function_tables_, - job_->signature_tables_); - - // Finish the WASM script now and make it public to the debugger. - script->set_wasm_compiled_module(*job_->compiled_module_); - job_->isolate_->debug()->OnAfterCompile(script); - - DeferredHandleScope deferred(job_->isolate_); - job_->compiled_module_ = handle(*job_->compiled_module_, job_->isolate_); - job_->deferred_handles_.push_back(deferred.Detach()); - // TODO(wasm): compiling wrappers should be made async as well. - job_->DoSync<CompileWrappers>(); - } - }; - - //========================================================================== - // Step 6 (sync): Compile JS->WASM wrappers. - //========================================================================== - class CompileWrappers : public SyncCompileTask { - void RunImpl() override { - TRACE_COMPILE("(6) Compile wrappers...\n"); - // Compile JS->WASM wrappers for exported functions. - HandleScope scope(job_->isolate_); - JSToWasmWrapperCache js_to_wasm_cache; - int func_index = 0; - constexpr bool is_sync = true; - WasmModule* module = job_->compiled_module_->module(); - for (auto exp : module->export_table) { - if (exp.kind != kExternalFunction) continue; - Handle<Code> wasm_code(Code::cast(job_->code_table_->get(exp.index)), - job_->isolate_); - Handle<Code> wrapper_code = - js_to_wasm_cache.CloneOrCompileJSToWasmWrapper( - job_->isolate_, module, wasm_code, exp.index); - int export_index = - static_cast<int>(module->functions.size() + func_index); - job_->code_table_->set(export_index, *wrapper_code); - RecordStats(job_->isolate_, *wrapper_code, !is_sync); - func_index++; - } - - job_->DoSync<FinishModule>(); - } - }; - - //========================================================================== - // Step 7 (sync): Finish the module and resolve the promise. - //========================================================================== - class FinishModule : public SyncCompileTask { - void RunImpl() override { - TRACE_COMPILE("(7) Finish module...\n"); - HandleScope scope(job_->isolate_); - Handle<WasmModuleObject> result = - WasmModuleObject::New(job_->isolate_, job_->compiled_module_); - // {job_} is deleted in AsyncCompileSucceeded, therefore the {return}. - return job_->AsyncCompileSucceeded(result); - } - }; -}; - void wasm::AsyncCompile(Isolate* isolate, Handle<JSPromise> promise, const ModuleWireBytes& bytes) { if (!FLAG_wasm_async_compilation) { @@ -3182,9 +870,9 @@ void wasm::AsyncCompile(Isolate* isolate, Handle<JSPromise> promise, // during asynchronous compilation. std::unique_ptr<byte[]> copy(new byte[bytes.length()]); memcpy(copy.get(), bytes.start(), bytes.length()); - auto job = new AsyncCompileJob(isolate, std::move(copy), bytes.length(), - handle(isolate->context()), promise); - job->Start(); + isolate->wasm_compilation_manager()->StartAsyncCompileJob( + isolate, std::move(copy), bytes.length(), handle(isolate->context()), + promise); } Handle<Code> wasm::CompileLazy(Isolate* isolate) { @@ -3206,12 +894,12 @@ Handle<Code> wasm::CompileLazy(Isolate* isolate) { Handle<FixedArray> exp_deopt_data; int func_index = -1; if (lazy_compile_code->deoptimization_data()->length() > 0) { - // Then it's an indirect call or via JS->WASM wrapper. + // Then it's an indirect call or via JS->wasm wrapper. DCHECK_LE(2, lazy_compile_code->deoptimization_data()->length()); exp_deopt_data = handle(lazy_compile_code->deoptimization_data(), isolate); auto* weak_cell = WeakCell::cast(exp_deopt_data->get(0)); instance = handle(WasmInstanceObject::cast(weak_cell->value()), isolate); - func_index = Smi::cast(exp_deopt_data->get(1))->value(); + func_index = Smi::ToInt(exp_deopt_data->get(1)); } it.Advance(); // Third frame: The calling wasm code or js-to-wasm wrapper. @@ -3221,9 +909,10 @@ Handle<Code> wasm::CompileLazy(Isolate* isolate) { if (it.frame()->is_js_to_wasm()) { DCHECK(!instance.is_null()); } else if (instance.is_null()) { + // Then this is a direct call (otherwise we would have attached the instance + // via deopt data to the lazy compile stub). Just use the instance of the + // caller. instance = handle(wasm::GetOwningWasmInstance(*caller_code), isolate); - } else { - DCHECK(*instance == wasm::GetOwningWasmInstance(*caller_code)); } int offset = static_cast<int>(it.frame()->pc() - caller_code->instruction_start()); @@ -3246,7 +935,7 @@ Handle<Code> wasm::CompileLazy(Isolate* isolate) { for (int idx = 2, end = exp_deopt_data->length(); idx < end; idx += 2) { if (exp_deopt_data->get(idx)->IsUndefined(isolate)) break; FixedArray* exp_table = FixedArray::cast(exp_deopt_data->get(idx)); - int exp_index = Smi::cast(exp_deopt_data->get(idx + 1))->value(); + int exp_index = Smi::ToInt(exp_deopt_data->get(idx + 1)); DCHECK(exp_table->get(exp_index) == *lazy_compile_code); exp_table->set(exp_index, *compiled_code); } @@ -3288,9 +977,9 @@ void LazyCompilationOrchestrator::CompileFunction( &sig_tables); uint8_t* module_start = compiled_module->module_bytes()->GetChars(); const WasmFunction* func = &module_env.module->functions[func_index]; - wasm::FunctionBody body{func->sig, module_start, - module_start + func->code_start_offset, - module_start + func->code_end_offset}; + wasm::FunctionBody body{func->sig, func->code.offset(), + module_start + func->code.offset(), + module_start + func->code.end_offset()}; // TODO(wasm): Refactor this to only get the name if it is really needed for // tracing / debugging. std::string func_name; @@ -3303,14 +992,18 @@ void LazyCompilationOrchestrator::CompileFunction( } ErrorThrower thrower(isolate, "WasmLazyCompile"); compiler::WasmCompilationUnit unit(isolate, &module_env, body, - CStrVector(func_name.c_str()), func_index); + CStrVector(func_name.c_str()), func_index, + CEntryStub(isolate, 1).GetCode()); unit.ExecuteCompilation(); - Handle<Code> code = unit.FinishCompilation(&thrower); + MaybeHandle<Code> maybe_code = unit.FinishCompilation(&thrower); // If there is a pending error, something really went wrong. The module was // verified before starting execution with lazy compilation. // This might be OOM, but then we cannot continue execution anyway. + // TODO(clemensh): According to the spec, we can actually skip validation at + // module creation time, and return a function that always traps here. CHECK(!thrower.error()); + Handle<Code> code = maybe_code.ToHandleChecked(); Handle<FixedArray> deopt_data = isolate->factory()->NewFixedArray(2, TENURED); Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance); @@ -3336,8 +1029,8 @@ void LazyCompilationOrchestrator::CompileFunction( Address mem_start = reinterpret_cast<Address>(instance->memory_buffer()->backing_store()); int mem_size = instance->memory_buffer()->byte_length()->Number(); - DCHECK_IMPLIES(mem_size == 0, mem_start == nullptr); - if (mem_size > 0) { + DCHECK_IMPLIES(mem_start == nullptr, mem_size == 0); + if (mem_start != nullptr) { code_specialization.RelocateMemoryReferences(nullptr, 0, mem_start, mem_size); } @@ -3346,7 +1039,7 @@ void LazyCompilationOrchestrator::CompileFunction( code_specialization.ApplyToWasmCode(*code, SKIP_ICACHE_FLUSH); Assembler::FlushICache(isolate, code->instruction_start(), code->instruction_size()); - RecordLazyCodeStats(isolate, *code); + RecordLazyCodeStats(*code, isolate->counters()); } Handle<Code> LazyCompilationOrchestrator::CompileLazy( @@ -3371,12 +1064,10 @@ Handle<Code> LazyCompilationOrchestrator::CompileLazy( SourcePositionTableIterator source_pos_iterator( caller->SourcePositionTable()); DCHECK_EQ(2, caller->deoptimization_data()->length()); - int caller_func_index = - Smi::cast(caller->deoptimization_data()->get(1))->value(); + int caller_func_index = Smi::ToInt(caller->deoptimization_data()->get(1)); const byte* func_bytes = - module_bytes->GetChars() + compiled_module->module() - ->functions[caller_func_index] - .code_start_offset; + module_bytes->GetChars() + + compiled_module->module()->functions[caller_func_index].code.offset(); for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done(); it.next()) { Code* callee = @@ -3414,10 +1105,9 @@ Handle<Code> LazyCompilationOrchestrator::CompileLazy( DCHECK_GT(non_compiled_functions.size(), idx); int called_func_index = non_compiled_functions[idx].func_index; // Check that the callee agrees with our assumed called_func_index. - DCHECK_IMPLIES( - callee->deoptimization_data()->length() > 0, - Smi::cast(callee->deoptimization_data()->get(1))->value() == - called_func_index); + DCHECK_IMPLIES(callee->deoptimization_data()->length() > 0, + Smi::ToInt(callee->deoptimization_data()->get(1)) == + called_func_index); if (is_js_to_wasm) { DCHECK_EQ(func_to_return_idx, called_func_index); } else { @@ -3443,3 +1133,17 @@ Handle<Code> LazyCompilationOrchestrator::CompileLazy( DCHECK_EQ(Code::WASM_FUNCTION, ret->kind()); return handle(ret, isolate); } + +const char* wasm::ExternalKindName(WasmExternalKind kind) { + switch (kind) { + case kExternalFunction: + return "function"; + case kExternalTable: + return "table"; + case kExternalMemory: + return "memory"; + case kExternalGlobal: + return "global"; + } + return "unknown"; +} diff --git a/deps/v8/src/wasm/wasm-module.h b/deps/v8/src/wasm/wasm-module.h index 4776298e9f..b36fdff4ea 100644 --- a/deps/v8/src/wasm/wasm-module.h +++ b/deps/v8/src/wasm/wasm-module.h @@ -24,6 +24,7 @@ class WasmCompiledModule; class WasmDebugInfo; class WasmModuleObject; class WasmInstanceObject; +class WasmTableObject; class WasmMemoryObject; namespace compiler { @@ -69,15 +70,34 @@ struct WasmInitExpr { } }; -// Static representation of a WASM function. +// Reference to a string in the wire bytes. +class WireBytesRef { + public: + WireBytesRef() : WireBytesRef(0, 0) {} + WireBytesRef(uint32_t offset, uint32_t length) + : offset_(offset), length_(length) { + DCHECK_IMPLIES(offset_ == 0, length_ == 0); + DCHECK_LE(offset_, offset_ + length_); // no uint32_t overflow. + } + + uint32_t offset() const { return offset_; } + uint32_t length() const { return length_; } + uint32_t end_offset() const { return offset_ + length_; } + bool is_empty() const { return length_ == 0; } + bool is_set() const { return offset_ != 0; } + + private: + uint32_t offset_; + uint32_t length_; +}; + +// Static representation of a wasm function. struct WasmFunction { FunctionSig* sig; // signature of the function. uint32_t func_index; // index into the function table. uint32_t sig_index; // index into the signature table. - uint32_t name_offset; // offset in the module bytes of the name, if any. - uint32_t name_length; // length in bytes of the name. - uint32_t code_start_offset; // offset in the module bytes of code start. - uint32_t code_end_offset; // offset in the module bytes of code end. + WireBytesRef name; // function name, if any. + WireBytesRef code; // code of this function. bool imported; bool exported; }; @@ -92,22 +112,35 @@ struct WasmGlobal { bool exported; // true if exported. }; +// Note: An exception signature only uses the params portion of a +// function signature. +typedef FunctionSig WasmExceptionSig; + +struct WasmException { + explicit WasmException(const WasmExceptionSig* sig = &empty_sig_) + : sig(sig) {} + + const WasmExceptionSig* sig; // type signature of the exception. + + private: + static const WasmExceptionSig empty_sig_; +}; + // Static representation of a wasm data segment. struct WasmDataSegment { WasmInitExpr dest_addr; // destination memory address of the data. - uint32_t source_offset; // start offset in the module bytes. - uint32_t source_size; // end offset in the module bytes. + WireBytesRef source; // start offset in the module bytes. }; // Static representation of a wasm indirect call table. struct WasmIndirectFunctionTable { - uint32_t min_size; // minimum table size. - uint32_t max_size; // maximum table size. - bool has_max; // true if there is a maximum size. + uint32_t min_size = 0; // minimum table size. + uint32_t max_size = 0; // maximum table size. + bool has_max = false; // true if there is a maximum size. // TODO(titzer): Move this to WasmInstance. Needed by interpreter only. std::vector<int32_t> values; // function table, -1 indicating invalid. - bool imported; // true if imported. - bool exported; // true if exported. + bool imported = false; // true if imported. + bool exported = false; // true if exported. SignatureMap map; // canonicalizing map for sig indexes. }; @@ -118,33 +151,23 @@ struct WasmTableInit { std::vector<uint32_t> entries; }; -// Static representation of a WASM import. +// Static representation of a wasm import. struct WasmImport { - uint32_t module_name_length; // length in bytes of the module name. - uint32_t module_name_offset; // offset in module bytes of the module name. - uint32_t field_name_length; // length in bytes of the import name. - uint32_t field_name_offset; // offset in module bytes of the import name. - WasmExternalKind kind; // kind of the import. - uint32_t index; // index into the respective space. + WireBytesRef module_name; // module name. + WireBytesRef field_name; // import name. + WasmExternalKind kind; // kind of the import. + uint32_t index; // index into the respective space. }; -// Static representation of a WASM export. +// Static representation of a wasm export. struct WasmExport { - uint32_t name_length; // length in bytes of the exported name. - uint32_t name_offset; // offset in module bytes of the name to export. + WireBytesRef name; // exported name. WasmExternalKind kind; // kind of the export. uint32_t index; // index into the respective space. }; enum ModuleOrigin : uint8_t { kWasmOrigin, kAsmJsOrigin }; -inline bool IsWasm(ModuleOrigin Origin) { - return Origin == ModuleOrigin::kWasmOrigin; -} -inline bool IsAsmJs(ModuleOrigin Origin) { - return Origin == ModuleOrigin::kAsmJsOrigin; -} - struct ModuleWireBytes; // Static representation of a module. @@ -153,44 +176,36 @@ struct V8_EXPORT_PRIVATE WasmModule { static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb std::unique_ptr<Zone> signature_zone; - uint32_t min_mem_pages = 0; // minimum size of the memory in 64k pages - uint32_t max_mem_pages = 0; // maximum size of the memory in 64k pages - bool has_max_mem = false; // try if a maximum memory size exists - bool has_memory = false; // true if the memory was defined or imported - bool mem_export = false; // true if the memory is exported - // TODO(wasm): reconcile start function index being an int with - // the fact that we index on uint32_t, so we may technically not be - // able to represent some start_function_index -es. - int start_function_index = -1; // start function, if any - - std::vector<WasmGlobal> globals; // globals in this module. - uint32_t globals_size = 0; // size of globals table. - uint32_t num_imported_functions = 0; // number of imported functions. - uint32_t num_declared_functions = 0; // number of declared functions. - uint32_t num_exported_functions = 0; // number of exported functions. - std::vector<FunctionSig*> signatures; // signatures in this module. - std::vector<WasmFunction> functions; // functions in this module. - std::vector<WasmDataSegment> data_segments; // data segments in this module. - std::vector<WasmIndirectFunctionTable> function_tables; // function tables. - std::vector<WasmImport> import_table; // import table. - std::vector<WasmExport> export_table; // export table. - std::vector<WasmTableInit> table_inits; // initializations of tables - // We store the semaphore here to extend its lifetime. In <libc-2.21, which we - // use on the try bots, semaphore::Wait() can return while some compilation - // tasks are still executing semaphore::Signal(). If the semaphore is cleaned - // up right after semaphore::Wait() returns, then this can cause an - // invalid-semaphore error in the compilation tasks. - // TODO(wasm): Move this semaphore back to CompileInParallel when the try bots - // switch to libc-2.21 or higher. - std::unique_ptr<base::Semaphore> pending_tasks; + uint32_t min_mem_pages = 0; // minimum size of the memory in 64k pages + uint32_t max_mem_pages = 0; // maximum size of the memory in 64k pages + bool has_max_mem = false; // try if a maximum memory size exists + bool has_memory = false; // true if the memory was defined or imported + bool mem_export = false; // true if the memory is exported + int start_function_index = -1; // start function, >= 0 if any + + std::vector<WasmGlobal> globals; + uint32_t globals_size = 0; + uint32_t num_imported_functions = 0; + uint32_t num_declared_functions = 0; + uint32_t num_exported_functions = 0; + WireBytesRef name = {0, 0}; + // TODO(wasm): Add url here, for spec'ed location information. + std::vector<FunctionSig*> signatures; + std::vector<WasmFunction> functions; + std::vector<WasmDataSegment> data_segments; + std::vector<WasmIndirectFunctionTable> function_tables; + std::vector<WasmImport> import_table; + std::vector<WasmExport> export_table; + std::vector<WasmException> exceptions; + std::vector<WasmTableInit> table_inits; WasmModule() : WasmModule(nullptr) {} WasmModule(std::unique_ptr<Zone> owned); - ModuleOrigin get_origin() const { return origin_; } + ModuleOrigin origin() const { return origin_; } void set_origin(ModuleOrigin new_value) { origin_ = new_value; } - bool is_wasm() const { return wasm::IsWasm(origin_); } - bool is_asm_js() const { return wasm::IsAsmJs(origin_); } + bool is_wasm() const { return origin_ == kWasmOrigin; } + bool is_asm_js() const { return origin_ == kAsmJsOrigin; } private: // TODO(kschimpf) - Encapsulate more fields. @@ -199,7 +214,7 @@ struct V8_EXPORT_PRIVATE WasmModule { typedef Managed<WasmModule> WasmModuleWrapper; -// An instantiated WASM module, including memory, function table, etc. +// An instantiated wasm module, including memory, function table, etc. struct WasmInstance { const WasmModule* module; // static representation of the module. // -- Heap allocated -------------------------------------------------------- @@ -251,31 +266,29 @@ struct V8_EXPORT_PRIVATE ModuleWireBytes { } // Get a string stored in the module bytes representing a name. - WasmName GetName(uint32_t offset, uint32_t length) const { - if (length == 0) return {"<?>", 3}; // no name. - CHECK(BoundsCheck(offset, length)); - DCHECK_GE(length, 0); + WasmName GetName(WireBytesRef ref) const { + if (ref.is_empty()) return {"<?>", 3}; // no name. + CHECK(BoundsCheck(ref.offset(), ref.length())); return Vector<const char>::cast( - module_bytes_.SubVector(offset, offset + length)); + module_bytes_.SubVector(ref.offset(), ref.end_offset())); } // Get a string stored in the module bytes representing a function name. WasmName GetName(const WasmFunction* function) const { - return GetName(function->name_offset, function->name_length); + return GetName(function->name); } // Get a string stored in the module bytes representing a name. - WasmName GetNameOrNull(uint32_t offset, uint32_t length) const { - if (offset == 0 && length == 0) return {NULL, 0}; // no name. - CHECK(BoundsCheck(offset, length)); - DCHECK_GE(length, 0); + WasmName GetNameOrNull(WireBytesRef ref) const { + if (!ref.is_set()) return {NULL, 0}; // no name. + CHECK(BoundsCheck(ref.offset(), ref.length())); return Vector<const char>::cast( - module_bytes_.SubVector(offset, offset + length)); + module_bytes_.SubVector(ref.offset(), ref.end_offset())); } // Get a string stored in the module bytes representing a function name. WasmName GetNameOrNull(const WasmFunction* function) const { - return GetNameOrNull(function->name_offset, function->name_length); + return GetNameOrNull(function->name); } // Checks the given offset range is contained within the module bytes. @@ -285,8 +298,8 @@ struct V8_EXPORT_PRIVATE ModuleWireBytes { } Vector<const byte> GetFunctionBytes(const WasmFunction* function) const { - return module_bytes_.SubVector(function->code_start_offset, - function->code_end_offset); + return module_bytes_.SubVector(function->code.offset(), + function->code.end_offset()); } const byte* start() const { return module_bytes_.start(); } @@ -356,14 +369,6 @@ struct V8_EXPORT_PRIVATE ModuleEnv { DCHECK_NOT_NULL(instance); return instance->function_code[index]; } - - // TODO(titzer): move these into src/compiler/wasm-compiler.cc - static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone, - FunctionSig* sig); - static compiler::CallDescriptor* GetI32WasmCallDescriptor( - Zone* zone, compiler::CallDescriptor* descriptor); - static compiler::CallDescriptor* GetI32WasmCallDescriptorForSimd( - Zone* zone, compiler::CallDescriptor* descriptor); }; // A ModuleEnv together with ModuleWireBytes. @@ -394,13 +399,6 @@ std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name); // If no debug info exists yet, it is created automatically. Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm); -// Check whether the given object represents a WebAssembly.Instance instance. -// This checks the number and type of embedder fields, so it's not 100 percent -// secure. If it turns out that we need more complete checks, we could add a -// special marker as embedder field, which will definitely never occur anywhere -// else. -bool IsWasmInstance(Object* instance); - // Get the script of the wasm module. If the origin of the module is asm.js, the // returned Script will be a JavaScript Script of Script::TYPE_NORMAL, otherwise // it's of type TYPE_WASM. @@ -422,26 +420,42 @@ V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections( Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name, ErrorThrower* thrower); +// Decode local variable names from the names section. Return FixedArray of +// FixedArray of <undefined|String>. The outer fixed array is indexed by the +// function index, the inner one by the local index. +Handle<FixedArray> DecodeLocalNames(Isolate*, Handle<WasmCompiledModule>); + // Assumed to be called with a code object associated to a wasm module instance. // Intended to be called from runtime functions. // Returns nullptr on failing to get owning instance. WasmInstanceObject* GetOwningWasmInstance(Code* code); -Handle<JSArrayBuffer> NewArrayBuffer(Isolate*, size_t size, - bool enable_guard_regions); +Handle<JSArrayBuffer> NewArrayBuffer( + Isolate*, size_t size, bool enable_guard_regions, + SharedFlag shared = SharedFlag::kNotShared); -Handle<JSArrayBuffer> SetupArrayBuffer(Isolate*, void* allocation_base, - size_t allocation_length, - void* backing_store, size_t size, - bool is_external, - bool enable_guard_regions); +Handle<JSArrayBuffer> SetupArrayBuffer( + Isolate*, void* allocation_base, size_t allocation_length, + void* backing_store, size_t size, bool is_external, + bool enable_guard_regions, SharedFlag shared = SharedFlag::kNotShared); void DetachWebAssemblyMemoryBuffer(Isolate* isolate, Handle<JSArrayBuffer> buffer, bool free_memory); +// The returned pointer is owned by the wasm instance target belongs to. The +// result is alive as long as the instance exists. +WasmFunction* GetWasmFunctionForImportWrapper(Isolate* isolate, + Handle<Object> target); + +Handle<Code> UnwrapImportWrapper(Handle<Object> import_wrapper); + +void TableSet(ErrorThrower* thrower, Isolate* isolate, + Handle<WasmTableObject> table, int32_t index, + Handle<JSFunction> function); + void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables, - int index, Handle<JSFunction> js_function); + int index, WasmFunction* function, Handle<Code> code); //============================================================================ //== Compilation and instantiation =========================================== @@ -461,6 +475,10 @@ V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncInstantiate( Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory); +V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncCompileAndInstantiate( + Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes, + MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory); + V8_EXPORT_PRIVATE void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise, const ModuleWireBytes& bytes); @@ -476,7 +494,16 @@ const bool kGuardRegionsSupported = false; #endif inline bool EnableGuardRegions() { - return FLAG_wasm_guard_pages && kGuardRegionsSupported; + return FLAG_wasm_guard_pages && kGuardRegionsSupported && + !FLAG_experimental_wasm_threads; +} + +inline SharedFlag IsShared(Handle<JSArrayBuffer> buffer) { + if (!buffer.is_null() && buffer->is_shared()) { + DCHECK(FLAG_experimental_wasm_threads); + return SharedFlag::kShared; + } + return SharedFlag::kNotShared; } void UnpackAndRegisterProtectedInstructions(Isolate* isolate, @@ -509,6 +536,8 @@ class LazyCompilationOrchestrator { int exported_func_index, bool patch_caller); }; +const char* ExternalKindName(WasmExternalKind); + namespace testing { void ValidateInstancesChain(Isolate* isolate, Handle<WasmModuleObject> module_obj, diff --git a/deps/v8/src/wasm/wasm-objects.cc b/deps/v8/src/wasm/wasm-objects.cc index d43087b263..71839ba27c 100644 --- a/deps/v8/src/wasm/wasm-objects.cc +++ b/deps/v8/src/wasm/wasm-objects.cc @@ -10,6 +10,7 @@ #include "src/compiler/wasm-compiler.h" #include "src/debug/debug-interface.h" #include "src/objects-inl.h" +#include "src/objects/debug-objects-inl.h" #include "src/wasm/module-decoder.h" #include "src/wasm/wasm-code-specialization.h" #include "src/wasm/wasm-module.h" @@ -28,53 +29,13 @@ using namespace v8::internal; using namespace v8::internal::wasm; -#define DEFINE_GETTER0(getter, Container, name, field, type) \ - type* Container::name() { return type::cast(getter(field)); } - -#define DEFINE_ACCESSORS0(getter, setter, Container, name, field, type) \ - DEFINE_GETTER0(getter, Container, name, field, type) \ - void Container::set_##name(type* value) { return setter(field, value); } - -#define DEFINE_OPTIONAL_ACCESSORS0(getter, setter, Container, name, field, \ - type) \ - DEFINE_ACCESSORS0(getter, setter, Container, name, field, type) \ - bool Container::has_##name() { \ - return !getter(field)->IsUndefined(GetIsolate()); \ - } - -#define DEFINE_OPTIONAL_GETTER0(getter, Container, name, field, type) \ - DEFINE_GETTER0(getter, Container, name, field, type) \ - bool Container::has_##name() { \ - return !getter(field)->IsUndefined(GetIsolate()); \ - } - -#define DEFINE_GETTER0(getter, Container, name, field, type) \ - type* Container::name() { return type::cast(getter(field)); } - -#define DEFINE_OBJ_GETTER(Container, name, field, type) \ - DEFINE_GETTER0(GetEmbedderField, Container, name, field, type) -#define DEFINE_OBJ_ACCESSORS(Container, name, field, type) \ - DEFINE_ACCESSORS0(GetEmbedderField, SetEmbedderField, Container, name, \ - field, type) -#define DEFINE_OPTIONAL_OBJ_ACCESSORS(Container, name, field, type) \ - DEFINE_OPTIONAL_ACCESSORS0(GetEmbedderField, SetEmbedderField, Container, \ - name, field, type) -#define DEFINE_ARR_GETTER(Container, name, field, type) \ - DEFINE_GETTER0(get, Container, name, field, type) -#define DEFINE_ARR_ACCESSORS(Container, name, field, type) \ - DEFINE_ACCESSORS0(get, set, Container, name, field, type) -#define DEFINE_OPTIONAL_ARR_ACCESSORS(Container, name, field, type) \ - DEFINE_OPTIONAL_ACCESSORS0(get, set, Container, name, field, type) -#define DEFINE_OPTIONAL_ARR_GETTER(Container, name, field, type) \ - DEFINE_OPTIONAL_GETTER0(get, Container, name, field, type) - namespace { // An iterator that returns first the module itself, then all modules linked via // next, then all linked via prev. class CompiledModulesIterator - : public std::iterator<std::input_iterator_tag, - Handle<WasmCompiledModule>> { + : public v8::base::iterator<std::input_iterator_tag, + Handle<WasmCompiledModule>> { public: CompiledModulesIterator(Isolate* isolate, Handle<WasmCompiledModule> start_module, bool at_end) @@ -131,8 +92,8 @@ class CompiledModulesIterator // An iterator based on the CompiledModulesIterator, but it returns all live // instances, not the WasmCompiledModules itself. class CompiledModuleInstancesIterator - : public std::iterator<std::input_iterator_tag, - Handle<WasmInstanceObject>> { + : public v8::base::iterator<std::input_iterator_tag, + Handle<WasmInstanceObject>> { public: CompiledModuleInstancesIterator(Isolate* isolate, Handle<WasmCompiledModule> start_module, @@ -182,8 +143,8 @@ bool IsBreakablePosition(Handle<WasmCompiledModule> compiled_module, BodyLocalDecls locals(&tmp); const byte* module_start = compiled_module->module_bytes()->GetChars(); WasmFunction& func = compiled_module->module()->functions[func_index]; - BytecodeIterator iterator(module_start + func.code_start_offset, - module_start + func.code_end_offset, &locals); + BytecodeIterator iterator(module_start + func.code.offset(), + module_start + func.code.end_offset(), &locals); DCHECK_LT(0, locals.encoded_size); for (uint32_t offset : iterator.offsets()) { if (offset > static_cast<uint32_t>(offset_in_func)) break; @@ -197,65 +158,37 @@ bool IsBreakablePosition(Handle<WasmCompiledModule> compiled_module, Handle<WasmModuleObject> WasmModuleObject::New( Isolate* isolate, Handle<WasmCompiledModule> compiled_module) { - WasmModule* module = compiled_module->module(); - Handle<JSObject> module_object; - if (module->is_wasm()) { - Handle<JSFunction> module_cons( - isolate->native_context()->wasm_module_constructor()); - module_object = isolate->factory()->NewJSObject(module_cons); - Handle<Symbol> module_sym(isolate->native_context()->wasm_module_sym()); - Object::SetProperty(module_object, module_sym, module_object, STRICT) - .Check(); - } else { - DCHECK(module->is_asm_js()); - Handle<Map> map = isolate->factory()->NewMap( - JS_OBJECT_TYPE, - JSObject::kHeaderSize + WasmModuleObject::kFieldCount * kPointerSize); - module_object = isolate->factory()->NewJSObjectFromMap(map, TENURED); - } - module_object->SetEmbedderField(WasmModuleObject::kCompiledModule, - *compiled_module); + Handle<JSFunction> module_cons( + isolate->native_context()->wasm_module_constructor()); + auto module_object = Handle<WasmModuleObject>::cast( + isolate->factory()->NewJSObject(module_cons)); + module_object->set_compiled_module(*compiled_module); Handle<WeakCell> link_to_module = isolate->factory()->NewWeakCell(module_object); compiled_module->set_weak_wasm_module(link_to_module); - return Handle<WasmModuleObject>::cast(module_object); -} - -WasmModuleObject* WasmModuleObject::cast(Object* object) { - DCHECK(object->IsJSObject()); - // TODO(titzer): brand check for WasmModuleObject. - return reinterpret_cast<WasmModuleObject*>(object); + return module_object; } -bool WasmModuleObject::IsWasmModuleObject(Object* object) { - return object->IsJSObject() && - JSObject::cast(object)->GetEmbedderFieldCount() == kFieldCount; -} - -DEFINE_OBJ_GETTER(WasmModuleObject, compiled_module, kCompiledModule, - WasmCompiledModule) - Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial, int64_t maximum, Handle<FixedArray>* js_functions) { Handle<JSFunction> table_ctor( isolate->native_context()->wasm_table_constructor()); - Handle<JSObject> table_obj = isolate->factory()->NewJSObject(table_ctor); - table_obj->SetEmbedderField(kWrapperTracerHeader, Smi::kZero); + auto table_obj = Handle<WasmTableObject>::cast( + isolate->factory()->NewJSObject(table_ctor)); *js_functions = isolate->factory()->NewFixedArray(initial); Object* null = isolate->heap()->null_value(); for (int i = 0; i < static_cast<int>(initial); ++i) { (*js_functions)->set(i, null); } - table_obj->SetEmbedderField(kFunctions, *(*js_functions)); + table_obj->set_functions(**js_functions); + DCHECK_EQ(maximum, static_cast<int>(maximum)); Handle<Object> max = isolate->factory()->NewNumber(maximum); - table_obj->SetEmbedderField(kMaximum, *max); + table_obj->set_maximum_length(*max); Handle<FixedArray> dispatch_tables = isolate->factory()->NewFixedArray(0); - table_obj->SetEmbedderField(kDispatchTables, *dispatch_tables); - Handle<Symbol> table_sym(isolate->native_context()->wasm_table_sym()); - Object::SetProperty(table_obj, table_sym, table_obj, STRICT).Check(); + table_obj->set_dispatch_tables(*dispatch_tables); return Handle<WasmTableObject>::cast(table_obj); } @@ -263,14 +196,13 @@ Handle<FixedArray> WasmTableObject::AddDispatchTable( Isolate* isolate, Handle<WasmTableObject> table_obj, Handle<WasmInstanceObject> instance, int table_index, Handle<FixedArray> function_table, Handle<FixedArray> signature_table) { - Handle<FixedArray> dispatch_tables( - FixedArray::cast(table_obj->GetEmbedderField(kDispatchTables)), isolate); + Handle<FixedArray> dispatch_tables(table_obj->dispatch_tables()); DCHECK_EQ(0, dispatch_tables->length() % 4); if (instance.is_null()) return dispatch_tables; // TODO(titzer): use weak cells here to avoid leaking instances. - // Grow the dispatch table and add a new triple at the end. + // Grow the dispatch table and add a new entry at the end. Handle<FixedArray> new_dispatch_tables = isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 4); @@ -280,35 +212,13 @@ Handle<FixedArray> WasmTableObject::AddDispatchTable( new_dispatch_tables->set(dispatch_tables->length() + 2, *function_table); new_dispatch_tables->set(dispatch_tables->length() + 3, *signature_table); - table_obj->SetEmbedderField(WasmTableObject::kDispatchTables, - *new_dispatch_tables); + table_obj->set_dispatch_tables(*new_dispatch_tables); return new_dispatch_tables; } -DEFINE_OBJ_ACCESSORS(WasmTableObject, functions, kFunctions, FixedArray) - -DEFINE_OBJ_GETTER(WasmTableObject, dispatch_tables, kDispatchTables, FixedArray) - -uint32_t WasmTableObject::current_length() { return functions()->length(); } - -bool WasmTableObject::has_maximum_length() { - return GetEmbedderField(kMaximum)->Number() >= 0; -} - -int64_t WasmTableObject::maximum_length() { - return static_cast<int64_t>(GetEmbedderField(kMaximum)->Number()); -} - -WasmTableObject* WasmTableObject::cast(Object* object) { - DCHECK(object && object->IsJSObject()); - // TODO(titzer): brand check for WasmTableObject. - return reinterpret_cast<WasmTableObject*>(object); -} - void WasmTableObject::grow(Isolate* isolate, uint32_t count) { - Handle<FixedArray> dispatch_tables( - FixedArray::cast(GetEmbedderField(kDispatchTables))); + Handle<FixedArray> dispatch_tables(this->dispatch_tables()); DCHECK_EQ(0, dispatch_tables->length() % 4); uint32_t old_size = functions()->length(); @@ -346,25 +256,26 @@ Handle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate, Address old_mem_start = nullptr; uint32_t old_size = 0; if (!old_buffer.is_null()) { - DCHECK(old_buffer->byte_length()->IsNumber()); old_mem_start = static_cast<Address>(old_buffer->backing_store()); - old_size = old_buffer->byte_length()->Number(); + CHECK(old_buffer->byte_length()->ToUint32(&old_size)); } + DCHECK_EQ(0, old_size % WasmModule::kPageSize); + uint32_t old_pages = old_size / WasmModule::kPageSize; DCHECK_GE(std::numeric_limits<uint32_t>::max(), old_size + pages * WasmModule::kPageSize); - uint32_t new_size = old_size + pages * WasmModule::kPageSize; - if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size || - FLAG_wasm_max_mem_pages * WasmModule::kPageSize < new_size) { + if (old_pages > max_pages || pages > max_pages - old_pages) { return Handle<JSArrayBuffer>::null(); } // TODO(gdeepti): Change the protection here instead of allocating a new // buffer before guard regions are turned on, see issue #5886. - const bool enable_guard_regions = - (old_buffer.is_null() && EnableGuardRegions()) || - (!old_buffer.is_null() && old_buffer->has_guard_region()); - Handle<JSArrayBuffer> new_buffer = - NewArrayBuffer(isolate, new_size, enable_guard_regions); + const bool enable_guard_regions = old_buffer.is_null() + ? EnableGuardRegions() + : old_buffer->has_guard_region(); + size_t new_size = + static_cast<size_t>(old_pages + pages) * WasmModule::kPageSize; + Handle<JSArrayBuffer> new_buffer = NewArrayBuffer( + isolate, new_size, enable_guard_regions, IsShared(old_buffer)); if (new_buffer.is_null()) return new_buffer; Address new_mem_start = static_cast<Address>(new_buffer->backing_store()); memcpy(new_mem_start, old_mem_start, old_size); @@ -404,143 +315,96 @@ Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate, int32_t maximum) { Handle<JSFunction> memory_ctor( isolate->native_context()->wasm_memory_constructor()); - Handle<JSObject> memory_obj = - isolate->factory()->NewJSObject(memory_ctor, TENURED); - memory_obj->SetEmbedderField(kWrapperTracerHeader, Smi::kZero); - buffer.is_null() ? memory_obj->SetEmbedderField( - kArrayBuffer, isolate->heap()->undefined_value()) - : memory_obj->SetEmbedderField(kArrayBuffer, *buffer); - Handle<Object> max = isolate->factory()->NewNumber(maximum); - memory_obj->SetEmbedderField(kMaximum, *max); - Handle<Symbol> memory_sym(isolate->native_context()->wasm_memory_sym()); - Object::SetProperty(memory_obj, memory_sym, memory_obj, STRICT).Check(); - return Handle<WasmMemoryObject>::cast(memory_obj); + auto memory_obj = Handle<WasmMemoryObject>::cast( + isolate->factory()->NewJSObject(memory_ctor, TENURED)); + if (buffer.is_null()) { + const bool enable_guard_regions = EnableGuardRegions(); + buffer = SetupArrayBuffer(isolate, nullptr, 0, nullptr, 0, false, + enable_guard_regions); + } + memory_obj->set_array_buffer(*buffer); + memory_obj->set_maximum_pages(maximum); + return memory_obj; } -DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmMemoryObject, buffer, kArrayBuffer, - JSArrayBuffer) -DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmMemoryObject, instances_link, kInstancesLink, - WasmInstanceWrapper) - uint32_t WasmMemoryObject::current_pages() { uint32_t byte_length; - CHECK(buffer()->byte_length()->ToUint32(&byte_length)); + CHECK(array_buffer()->byte_length()->ToUint32(&byte_length)); return byte_length / wasm::WasmModule::kPageSize; } -bool WasmMemoryObject::has_maximum_pages() { - return GetEmbedderField(kMaximum)->Number() >= 0; -} - -int32_t WasmMemoryObject::maximum_pages() { - return static_cast<int32_t>(GetEmbedderField(kMaximum)->Number()); -} - -WasmMemoryObject* WasmMemoryObject::cast(Object* object) { - DCHECK(object && object->IsJSObject()); - // TODO(titzer): brand check for WasmMemoryObject. - return reinterpret_cast<WasmMemoryObject*>(object); -} - void WasmMemoryObject::AddInstance(Isolate* isolate, + Handle<WasmMemoryObject> memory, Handle<WasmInstanceObject> instance) { - Handle<WasmInstanceWrapper> instance_wrapper = - handle(instance->instance_wrapper()); - if (has_instances_link()) { - Handle<WasmInstanceWrapper> current_wrapper(instances_link()); - DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*current_wrapper)); - DCHECK(!current_wrapper->has_previous()); - instance_wrapper->set_next_wrapper(*current_wrapper); - current_wrapper->set_previous_wrapper(*instance_wrapper); + Handle<WeakFixedArray> old_instances = + memory->has_instances() + ? Handle<WeakFixedArray>(memory->instances(), isolate) + : Handle<WeakFixedArray>::null(); + Handle<WeakFixedArray> new_instances = + WeakFixedArray::Add(old_instances, instance); + memory->set_instances(*new_instances); +} + +void WasmMemoryObject::RemoveInstance(Isolate* isolate, + Handle<WasmMemoryObject> memory, + Handle<WasmInstanceObject> instance) { + if (memory->has_instances()) { + memory->instances()->Remove(instance); } - set_instances_link(*instance_wrapper); -} - -void WasmMemoryObject::ResetInstancesLink(Isolate* isolate) { - Handle<Object> undefined = isolate->factory()->undefined_value(); - SetEmbedderField(kInstancesLink, *undefined); } // static int32_t WasmMemoryObject::Grow(Isolate* isolate, Handle<WasmMemoryObject> memory_object, uint32_t pages) { - Handle<JSArrayBuffer> old_buffer; + Handle<JSArrayBuffer> old_buffer(memory_object->array_buffer()); uint32_t old_size = 0; - Address old_mem_start = nullptr; - if (memory_object->has_buffer()) { - old_buffer = handle(memory_object->buffer()); - old_size = old_buffer->byte_length()->Number(); - old_mem_start = static_cast<Address>(old_buffer->backing_store()); - } + CHECK(old_buffer->byte_length()->ToUint32(&old_size)); Handle<JSArrayBuffer> new_buffer; // Return current size if grow by 0. if (pages == 0) { // Even for pages == 0, we need to attach a new JSArrayBuffer with the same // backing store and neuter the old one to be spec compliant. - if (!old_buffer.is_null() && old_size != 0) { + if (old_size != 0) { new_buffer = SetupArrayBuffer( isolate, old_buffer->allocation_base(), old_buffer->allocation_length(), old_buffer->backing_store(), - old_size, old_buffer->is_external(), old_buffer->has_guard_region()); - memory_object->set_buffer(*new_buffer); + old_size, old_buffer->is_external(), old_buffer->has_guard_region(), + IsShared(old_buffer)); + memory_object->set_array_buffer(*new_buffer); } DCHECK_EQ(0, old_size % WasmModule::kPageSize); return old_size / WasmModule::kPageSize; } - if (!memory_object->has_instances_link()) { - // Memory object does not have an instance associated with it, just grow - uint32_t max_pages; - if (memory_object->has_maximum_pages()) { - max_pages = static_cast<uint32_t>(memory_object->maximum_pages()); - if (FLAG_wasm_max_mem_pages < max_pages) return -1; - } else { - max_pages = FLAG_wasm_max_mem_pages; - } - new_buffer = GrowMemoryBuffer(isolate, old_buffer, pages, max_pages); - if (new_buffer.is_null()) return -1; + + uint32_t max_pages; + if (memory_object->has_maximum_pages()) { + max_pages = static_cast<uint32_t>(memory_object->maximum_pages()); + if (FLAG_wasm_max_mem_pages < max_pages) return -1; } else { - Handle<WasmInstanceWrapper> instance_wrapper( - memory_object->instances_link()); - DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper)); - DCHECK(instance_wrapper->has_instance()); - Handle<WasmInstanceObject> instance = instance_wrapper->instance_object(); - DCHECK(IsWasmInstance(*instance)); - uint32_t max_pages = instance->GetMaxMemoryPages(); - - // Grow memory object buffer and update instances associated with it. - new_buffer = GrowMemoryBuffer(isolate, old_buffer, pages, max_pages); - if (new_buffer.is_null()) return -1; - DCHECK(!instance_wrapper->has_previous()); - SetInstanceMemory(isolate, instance, new_buffer); - UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size); - while (instance_wrapper->has_next()) { - instance_wrapper = instance_wrapper->next_wrapper(); - DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper)); - Handle<WasmInstanceObject> instance = instance_wrapper->instance_object(); - DCHECK(IsWasmInstance(*instance)); + max_pages = FLAG_wasm_max_mem_pages; + } + new_buffer = GrowMemoryBuffer(isolate, old_buffer, pages, max_pages); + if (new_buffer.is_null()) return -1; + + if (memory_object->has_instances()) { + Address old_mem_start = static_cast<Address>(old_buffer->backing_store()); + Handle<WeakFixedArray> instances(memory_object->instances(), isolate); + for (int i = 0; i < instances->Length(); i++) { + Object* elem = instances->Get(i); + if (!elem->IsWasmInstanceObject()) continue; + Handle<WasmInstanceObject> instance(WasmInstanceObject::cast(elem), + isolate); SetInstanceMemory(isolate, instance, new_buffer); UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size); } } - memory_object->set_buffer(*new_buffer); + + memory_object->set_array_buffer(*new_buffer); DCHECK_EQ(0, old_size % WasmModule::kPageSize); return old_size / WasmModule::kPageSize; } -DEFINE_OBJ_ACCESSORS(WasmInstanceObject, compiled_module, kCompiledModule, - WasmCompiledModule) -DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, globals_buffer, - kGlobalsArrayBuffer, JSArrayBuffer) -DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, memory_buffer, - kMemoryArrayBuffer, JSArrayBuffer) -DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, memory_object, kMemoryObject, - WasmMemoryObject) -DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, debug_info, kDebugInfo, - WasmDebugInfo) -DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, instance_wrapper, - kWasmMemInstanceWrapper, WasmInstanceWrapper) - WasmModuleObject* WasmInstanceObject::module_object() { return *compiled_module()->wasm_module(); } @@ -555,50 +419,17 @@ Handle<WasmDebugInfo> WasmInstanceObject::GetOrCreateDebugInfo( return new_info; } -WasmInstanceObject* WasmInstanceObject::cast(Object* object) { - DCHECK(IsWasmInstanceObject(object)); - return reinterpret_cast<WasmInstanceObject*>(object); -} - -bool WasmInstanceObject::IsWasmInstanceObject(Object* object) { - if (!object->IsJSObject()) return false; - - JSObject* obj = JSObject::cast(object); - Isolate* isolate = obj->GetIsolate(); - if (obj->GetEmbedderFieldCount() != kFieldCount) { - return false; - } - - Object* mem = obj->GetEmbedderField(kMemoryArrayBuffer); - if (!(mem->IsUndefined(isolate) || mem->IsJSArrayBuffer()) || - !WasmCompiledModule::IsWasmCompiledModule( - obj->GetEmbedderField(kCompiledModule))) { - return false; - } - - // All checks passed. - return true; -} - Handle<WasmInstanceObject> WasmInstanceObject::New( Isolate* isolate, Handle<WasmCompiledModule> compiled_module) { Handle<JSFunction> instance_cons( isolate->native_context()->wasm_instance_constructor()); Handle<JSObject> instance_object = isolate->factory()->NewJSObject(instance_cons, TENURED); - instance_object->SetEmbedderField(kWrapperTracerHeader, Smi::kZero); - Handle<Symbol> instance_sym(isolate->native_context()->wasm_instance_sym()); - Object::SetProperty(instance_object, instance_sym, instance_object, STRICT) - .Check(); Handle<WasmInstanceObject> instance( reinterpret_cast<WasmInstanceObject*>(*instance_object), isolate); - instance->SetEmbedderField(kCompiledModule, *compiled_module); - instance->SetEmbedderField(kMemoryObject, isolate->heap()->undefined_value()); - Handle<WasmInstanceWrapper> instance_wrapper = - WasmInstanceWrapper::New(isolate, instance); - instance->SetEmbedderField(kWasmMemInstanceWrapper, *instance_wrapper); + instance->set_compiled_module(*compiled_module); return instance; } @@ -654,53 +485,72 @@ uint32_t WasmInstanceObject::GetMaxMemoryPages() { return FLAG_wasm_max_mem_pages; } -WasmInstanceObject* WasmExportedFunction::instance() { - return WasmInstanceObject::cast(GetEmbedderField(kInstance)); -} +bool WasmExportedFunction::IsWasmExportedFunction(Object* object) { + if (!object->IsJSFunction()) return false; + Handle<JSFunction> js_function(JSFunction::cast(object)); + if (Code::JS_TO_WASM_FUNCTION != js_function->code()->kind()) return false; -int WasmExportedFunction::function_index() { - int32_t func_index; - CHECK(GetEmbedderField(kIndex)->ToInt32(&func_index)); - return func_index; + Handle<Symbol> symbol( + js_function->GetIsolate()->factory()->wasm_instance_symbol()); + MaybeHandle<Object> maybe_result = + JSObject::GetPropertyOrElement(js_function, symbol); + Handle<Object> result; + if (!maybe_result.ToHandle(&result)) return false; + return result->IsWasmInstanceObject(); } WasmExportedFunction* WasmExportedFunction::cast(Object* object) { - DCHECK(object && object->IsJSFunction()); - DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, - JSFunction::cast(object)->code()->kind()); - // TODO(titzer): brand check for WasmExportedFunction. + DCHECK(IsWasmExportedFunction(object)); return reinterpret_cast<WasmExportedFunction*>(object); } +WasmInstanceObject* WasmExportedFunction::instance() { + DisallowHeapAllocation no_allocation; + Handle<Symbol> symbol(GetIsolate()->factory()->wasm_instance_symbol()); + MaybeHandle<Object> result = + JSObject::GetPropertyOrElement(handle(this), symbol); + return WasmInstanceObject::cast(*(result.ToHandleChecked())); +} + +int WasmExportedFunction::function_index() { + DisallowHeapAllocation no_allocation; + Handle<Symbol> symbol = GetIsolate()->factory()->wasm_function_index_symbol(); + MaybeHandle<Object> result = + JSObject::GetPropertyOrElement(handle(this), symbol); + return result.ToHandleChecked()->Number(); +} + Handle<WasmExportedFunction> WasmExportedFunction::New( Isolate* isolate, Handle<WasmInstanceObject> instance, MaybeHandle<String> maybe_name, int func_index, int arity, Handle<Code> export_wrapper) { + DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind()); Handle<String> name; - if (maybe_name.is_null()) { + if (!maybe_name.ToHandle(&name)) { EmbeddedVector<char, 16> buffer; int length = SNPrintF(buffer, "%d", func_index); name = isolate->factory() ->NewStringFromOneByte( Vector<uint8_t>::cast(buffer.SubVector(0, length))) .ToHandleChecked(); - } else { - name = maybe_name.ToHandleChecked(); } - DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind()); Handle<SharedFunctionInfo> shared = isolate->factory()->NewSharedFunctionInfo(name, export_wrapper, false); shared->set_length(arity); shared->set_internal_formal_parameter_count(arity); - Handle<JSFunction> function = isolate->factory()->NewFunction( - isolate->wasm_function_map(), name, export_wrapper); - function->SetEmbedderField(kWrapperTracerHeader, Smi::kZero); + Handle<JSFunction> js_function = isolate->factory()->NewFunction( + isolate->sloppy_function_map(), name, export_wrapper); + + js_function->set_shared(*shared); + Handle<Symbol> instance_symbol(isolate->factory()->wasm_instance_symbol()); + JSObject::AddProperty(js_function, instance_symbol, instance, DONT_ENUM); - function->set_shared(*shared); + Handle<Symbol> function_index_symbol( + isolate->factory()->wasm_function_index_symbol()); + JSObject::AddProperty(js_function, function_index_symbol, + isolate->factory()->NewNumber(func_index), DONT_ENUM); - function->SetEmbedderField(kInstance, *instance); - function->SetEmbedderField(kIndex, Smi::FromInt(func_index)); - return Handle<WasmExportedFunction>::cast(function); + return Handle<WasmExportedFunction>::cast(js_function); } bool WasmSharedModuleData::IsWasmSharedModuleData(Object* object) { @@ -708,16 +558,16 @@ bool WasmSharedModuleData::IsWasmSharedModuleData(Object* object) { FixedArray* arr = FixedArray::cast(object); if (arr->length() != kFieldCount) return false; Isolate* isolate = arr->GetIsolate(); - if (!arr->get(kModuleWrapper)->IsForeign()) return false; - if (!arr->get(kModuleBytes)->IsUndefined(isolate) && - !arr->get(kModuleBytes)->IsSeqOneByteString()) + if (!arr->get(kModuleWrapperIndex)->IsForeign()) return false; + if (!arr->get(kModuleBytesIndex)->IsUndefined(isolate) && + !arr->get(kModuleBytesIndex)->IsSeqOneByteString()) return false; - if (!arr->get(kScript)->IsScript()) return false; - if (!arr->get(kAsmJsOffsetTable)->IsUndefined(isolate) && - !arr->get(kAsmJsOffsetTable)->IsByteArray()) + if (!arr->get(kScriptIndex)->IsScript()) return false; + if (!arr->get(kAsmJsOffsetTableIndex)->IsUndefined(isolate) && + !arr->get(kAsmJsOffsetTableIndex)->IsByteArray()) return false; - if (!arr->get(kBreakPointInfos)->IsUndefined(isolate) && - !arr->get(kBreakPointInfos)->IsFixedArray()) + if (!arr->get(kBreakPointInfosIndex)->IsUndefined(isolate) && + !arr->get(kBreakPointInfosIndex)->IsFixedArray()) return false; return true; } @@ -734,41 +584,34 @@ wasm::WasmModule* WasmSharedModuleData::module() { // a Managed<WasmModule> object, as well as cases when it's managed // by the embedder. CcTests fall into the latter case. return *(reinterpret_cast<wasm::WasmModule**>( - Foreign::cast(get(kModuleWrapper))->foreign_address())); + Foreign::cast(get(kModuleWrapperIndex))->foreign_address())); } -DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, module_bytes, kModuleBytes, - SeqOneByteString); -DEFINE_ARR_GETTER(WasmSharedModuleData, script, kScript, Script); -DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, asm_js_offset_table, - kAsmJsOffsetTable, ByteArray); -DEFINE_OPTIONAL_ARR_GETTER(WasmSharedModuleData, breakpoint_infos, - kBreakPointInfos, FixedArray); -DEFINE_OPTIONAL_ARR_GETTER(WasmSharedModuleData, lazy_compilation_orchestrator, - kLazyCompilationOrchestrator, Foreign); - Handle<WasmSharedModuleData> WasmSharedModuleData::New( Isolate* isolate, Handle<Foreign> module_wrapper, Handle<SeqOneByteString> module_bytes, Handle<Script> script, Handle<ByteArray> asm_js_offset_table) { Handle<FixedArray> arr = isolate->factory()->NewFixedArray(kFieldCount, TENURED); - arr->set(kWrapperTracerHeader, Smi::kZero); - arr->set(kModuleWrapper, *module_wrapper); + arr->set(kModuleWrapperIndex, *module_wrapper); if (!module_bytes.is_null()) { - arr->set(kModuleBytes, *module_bytes); + arr->set(kModuleBytesIndex, *module_bytes); } if (!script.is_null()) { - arr->set(kScript, *script); + arr->set(kScriptIndex, *script); } if (!asm_js_offset_table.is_null()) { - arr->set(kAsmJsOffsetTable, *asm_js_offset_table); + arr->set(kAsmJsOffsetTableIndex, *asm_js_offset_table); } DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*arr)); return Handle<WasmSharedModuleData>::cast(arr); } +Foreign* WasmSharedModuleData::lazy_compilation_orchestrator() { + return Foreign::cast(get(kLazyCompilationOrchestratorIndex)); +} + bool WasmSharedModuleData::is_asm_js() { bool asm_js = module()->is_asm_js(); DCHECK_EQ(asm_js, script()->IsUserJavaScript()); @@ -778,7 +621,7 @@ bool WasmSharedModuleData::is_asm_js() { void WasmSharedModuleData::ReinitializeAfterDeserialization( Isolate* isolate, Handle<WasmSharedModuleData> shared) { - DCHECK(shared->get(kModuleWrapper)->IsUndefined(isolate)); + DCHECK(shared->get(kModuleWrapperIndex)->IsUndefined(isolate)); #ifdef DEBUG // No BreakpointInfo objects should survive deserialization. if (shared->has_breakpoint_infos()) { @@ -788,7 +631,7 @@ void WasmSharedModuleData::ReinitializeAfterDeserialization( } #endif - shared->set(kBreakPointInfos, isolate->heap()->undefined_value()); + shared->set(kBreakPointInfosIndex, isolate->heap()->undefined_value()); WasmModule* module = nullptr; { @@ -802,7 +645,7 @@ void WasmSharedModuleData::ReinitializeAfterDeserialization( // TODO(titzer): remember the module origin in the compiled_module // For now, we assume serialized modules did not originate from asm.js. ModuleResult result = - DecodeWasmModule(isolate, start, end, false, kWasmOrigin); + SyncDecodeWasmModule(isolate, start, end, false, kWasmOrigin); CHECK(result.ok()); CHECK_NOT_NULL(result.val); // Take ownership of the WasmModule and immediately transfer it to the @@ -813,7 +656,7 @@ void WasmSharedModuleData::ReinitializeAfterDeserialization( Handle<WasmModuleWrapper> module_wrapper = WasmModuleWrapper::New(isolate, module); - shared->set(kModuleWrapper, *module_wrapper); + shared->set(kModuleWrapperIndex, *module_wrapper); DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared)); } @@ -858,7 +701,7 @@ void WasmSharedModuleData::AddBreakpoint(Handle<WasmSharedModuleData> shared, breakpoint_infos = handle(shared->breakpoint_infos(), isolate); } else { breakpoint_infos = isolate->factory()->NewFixedArray(4, TENURED); - shared->set(kBreakPointInfos, *breakpoint_infos); + shared->set(kBreakPointInfosIndex, *breakpoint_infos); } int insert_pos = @@ -882,7 +725,7 @@ void WasmSharedModuleData::AddBreakpoint(Handle<WasmSharedModuleData> shared, if (need_realloc) { new_breakpoint_infos = isolate->factory()->NewFixedArray( 2 * breakpoint_infos->length(), TENURED); - shared->set(kBreakPointInfos, *new_breakpoint_infos); + shared->set(kBreakPointInfosIndex, *new_breakpoint_infos); // Copy over the entries [0, insert_pos). for (int i = 0; i < insert_pos; ++i) new_breakpoint_infos->set(i, breakpoint_infos->get(i)); @@ -932,7 +775,7 @@ void WasmSharedModuleData::SetBreakpointsOnNewInstance( int func_index = compiled_module->GetContainingFunction(position); DCHECK_LE(0, func_index); WasmFunction& func = compiled_module->module()->functions[func_index]; - int offset_in_func = position - func.code_start_offset; + int offset_in_func = position - func.code.offset(); WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func); } } @@ -944,7 +787,7 @@ void WasmSharedModuleData::PrepareForLazyCompilation( LazyCompilationOrchestrator* orch = new LazyCompilationOrchestrator(); Handle<Managed<LazyCompilationOrchestrator>> orch_handle = Managed<LazyCompilationOrchestrator>::New(isolate, orch); - shared->set(WasmSharedModuleData::kLazyCompilationOrchestrator, *orch_handle); + shared->set(kLazyCompilationOrchestratorIndex, *orch_handle); } Handle<WasmCompiledModule> WasmCompiledModule::New( @@ -1132,20 +975,21 @@ void WasmCompiledModule::SetGlobalsStartAddressFrom( MaybeHandle<String> WasmCompiledModule::ExtractUtf8StringFromModuleBytes( Isolate* isolate, Handle<WasmCompiledModule> compiled_module, - uint32_t offset, uint32_t size) { + WireBytesRef ref) { // TODO(wasm): cache strings from modules if it's a performance win. Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(), isolate); - DCHECK_GE(module_bytes->length(), offset); - DCHECK_GE(module_bytes->length() - offset, size); + DCHECK_GE(module_bytes->length(), ref.end_offset()); // UTF8 validation happens at decode time. - DCHECK(unibrow::Utf8::Validate( - reinterpret_cast<const byte*>(module_bytes->GetCharsAddress() + offset), - size)); - DCHECK_GE(kMaxInt, offset); - DCHECK_GE(kMaxInt, size); + DCHECK(unibrow::Utf8::ValidateEncoding( + reinterpret_cast<const byte*>(module_bytes->GetCharsAddress() + + ref.offset()), + ref.length())); + DCHECK_GE(kMaxInt, ref.offset()); + DCHECK_GE(kMaxInt, ref.length()); return isolate->factory()->NewStringFromUtf8SubString( - module_bytes, static_cast<int>(offset), static_cast<int>(size)); + module_bytes, static_cast<int>(ref.offset()), + static_cast<int>(ref.length())); } bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) { @@ -1221,15 +1065,22 @@ uint32_t WasmCompiledModule::default_mem_size() const { return min_mem_pages() * WasmModule::kPageSize; } +MaybeHandle<String> WasmCompiledModule::GetModuleNameOrNull( + Isolate* isolate, Handle<WasmCompiledModule> compiled_module) { + WasmModule* module = compiled_module->module(); + if (!module->name.is_set()) return {}; + return WasmCompiledModule::ExtractUtf8StringFromModuleBytes( + isolate, compiled_module, module->name); +} + MaybeHandle<String> WasmCompiledModule::GetFunctionNameOrNull( Isolate* isolate, Handle<WasmCompiledModule> compiled_module, uint32_t func_index) { DCHECK_LT(func_index, compiled_module->module()->functions.size()); WasmFunction& function = compiled_module->module()->functions[func_index]; - DCHECK_IMPLIES(function.name_offset == 0, function.name_length == 0); - if (!function.name_offset) return {}; + if (!function.name.is_set()) return {}; return WasmCompiledModule::ExtractUtf8StringFromModuleBytes( - isolate, compiled_module, function.name_offset, function.name_length); + isolate, compiled_module, function.name); } Handle<String> WasmCompiledModule::GetFunctionName( @@ -1246,17 +1097,17 @@ Vector<const uint8_t> WasmCompiledModule::GetRawFunctionName( DCHECK_GT(module()->functions.size(), func_index); WasmFunction& function = module()->functions[func_index]; SeqOneByteString* bytes = module_bytes(); - DCHECK_GE(bytes->length(), function.name_offset); - DCHECK_GE(bytes->length() - function.name_offset, function.name_length); - return Vector<const uint8_t>(bytes->GetCharsAddress() + function.name_offset, - function.name_length); + DCHECK_GE(bytes->length(), function.name.end_offset()); + return Vector<const uint8_t>( + bytes->GetCharsAddress() + function.name.offset(), + function.name.length()); } int WasmCompiledModule::GetFunctionOffset(uint32_t func_index) { std::vector<WasmFunction>& functions = module()->functions; if (static_cast<uint32_t>(func_index) >= functions.size()) return -1; - DCHECK_GE(kMaxInt, functions[func_index].code_start_offset); - return static_cast<int>(functions[func_index].code_start_offset); + DCHECK_GE(kMaxInt, functions[func_index].code.offset()); + return static_cast<int>(functions[func_index].code.offset()); } int WasmCompiledModule::GetContainingFunction(uint32_t byte_offset) { @@ -1268,7 +1119,7 @@ int WasmCompiledModule::GetContainingFunction(uint32_t byte_offset) { if (right == 0) return false; while (right - left > 1) { int mid = left + (right - left) / 2; - if (functions[mid].code_start_offset <= byte_offset) { + if (functions[mid].code.offset() <= byte_offset) { left = mid; } else { right = mid; @@ -1276,8 +1127,8 @@ int WasmCompiledModule::GetContainingFunction(uint32_t byte_offset) { } // If the found function does not contains the given position, return -1. WasmFunction& func = functions[left]; - if (byte_offset < func.code_start_offset || - byte_offset >= func.code_end_offset) { + if (byte_offset < func.code.offset() || + byte_offset >= func.code.end_offset()) { return -1; } @@ -1292,9 +1143,9 @@ bool WasmCompiledModule::GetPositionInfo(uint32_t position, WasmFunction& function = module()->functions[func_index]; info->line = func_index; - info->column = position - function.code_start_offset; - info->line_start = function.code_start_offset; - info->line_end = function.code_end_offset; + info->column = position - function.code.offset(); + info->line_start = function.code.offset(); + info->line_end = function.code.end_offset(); return true; } @@ -1355,8 +1206,7 @@ Handle<ByteArray> GetDecodedAsmJsOffsetTable( for (int func = 0; func < num_functions; ++func) { std::vector<AsmJsOffsetEntry>& func_asm_offsets = asm_offsets.val[func]; if (func_asm_offsets.empty()) continue; - int func_offset = - wasm_funs[num_imported_functions + func].code_start_offset; + int func_offset = wasm_funs[num_imported_functions + func].code.offset(); for (AsmJsOffsetEntry& e : func_asm_offsets) { // Byte offsets must be strictly monotonously increasing: DCHECK_IMPLIES(idx > 0, func_offset + e.byte_offset > @@ -1383,7 +1233,7 @@ int WasmCompiledModule::GetAsmJsSourcePosition( DCHECK_LT(func_index, compiled_module->module()->functions.size()); uint32_t func_code_offset = - compiled_module->module()->functions[func_index].code_start_offset; + compiled_module->module()->functions[func_index].code.offset(); uint32_t total_offset = func_code_offset + byte_offset; // Binary search for the total byte offset. @@ -1444,17 +1294,16 @@ bool WasmCompiledModule::GetPossibleBreakpoints( // start_offset and end_offset are module-relative byte offsets. uint32_t start_func_index = start.GetLineNumber(); if (start_func_index >= functions.size()) return false; - int start_func_len = functions[start_func_index].code_end_offset - - functions[start_func_index].code_start_offset; + int start_func_len = functions[start_func_index].code.length(); if (start.GetColumnNumber() > start_func_len) return false; uint32_t start_offset = - functions[start_func_index].code_start_offset + start.GetColumnNumber(); + functions[start_func_index].code.offset() + start.GetColumnNumber(); uint32_t end_func_index; uint32_t end_offset; if (end.IsEmpty()) { // Default: everything till the end of the Script. end_func_index = static_cast<uint32_t>(functions.size() - 1); - end_offset = functions[end_func_index].code_end_offset; + end_offset = functions[end_func_index].code.end_offset(); } else { // If end is specified: Use it and check for valid input. end_func_index = static_cast<uint32_t>(end.GetLineNumber()); @@ -1464,12 +1313,13 @@ bool WasmCompiledModule::GetPossibleBreakpoints( // next function also. if (end.GetColumnNumber() == 0 && end_func_index > 0) { --end_func_index; - end_offset = functions[end_func_index].code_end_offset; + end_offset = functions[end_func_index].code.end_offset(); } else { if (end_func_index >= functions.size()) return false; end_offset = - functions[end_func_index].code_start_offset + end.GetColumnNumber(); - if (end_offset > functions[end_func_index].code_end_offset) return false; + functions[end_func_index].code.offset() + end.GetColumnNumber(); + if (end_offset > functions[end_func_index].code.end_offset()) + return false; } } @@ -1480,14 +1330,14 @@ bool WasmCompiledModule::GetPossibleBreakpoints( for (uint32_t func_idx = start_func_index; func_idx <= end_func_index; ++func_idx) { WasmFunction& func = functions[func_idx]; - if (func.code_start_offset == func.code_end_offset) continue; + if (func.code.length() == 0) continue; BodyLocalDecls locals(&tmp); - BytecodeIterator iterator(module_start + func.code_start_offset, - module_start + func.code_end_offset, &locals); + BytecodeIterator iterator(module_start + func.code.offset(), + module_start + func.code.end_offset(), &locals); DCHECK_LT(0u, locals.encoded_size); for (uint32_t offset : iterator.offsets()) { - uint32_t total_offset = func.code_start_offset + offset; + uint32_t total_offset = func.code.offset() + offset; if (total_offset >= end_offset) { DCHECK_EQ(end_func_index, func_idx); break; @@ -1508,7 +1358,7 @@ bool WasmCompiledModule::SetBreakPoint( int func_index = compiled_module->GetContainingFunction(*position); if (func_index < 0) return false; WasmFunction& func = compiled_module->module()->functions[func_index]; - int offset_in_func = *position - func.code_start_offset; + int offset_in_func = *position - func.code.offset(); // According to the current design, we should only be called with valid // breakable positions. @@ -1562,29 +1412,3 @@ Handle<Code> WasmCompiledModule::CompileLazy( return orch->CompileLazy(isolate, instance, caller, offset, func_index, patch_caller); } - -Handle<WasmInstanceWrapper> WasmInstanceWrapper::New( - Isolate* isolate, Handle<WasmInstanceObject> instance) { - Handle<FixedArray> array = - isolate->factory()->NewFixedArray(kWrapperPropertyCount, TENURED); - Handle<WasmInstanceWrapper> instance_wrapper( - reinterpret_cast<WasmInstanceWrapper*>(*array), isolate); - Handle<WeakCell> cell = isolate->factory()->NewWeakCell(instance); - instance_wrapper->set(kWrapperInstanceObject, *cell); - return instance_wrapper; -} - -bool WasmInstanceWrapper::IsWasmInstanceWrapper(Object* obj) { - if (!obj->IsFixedArray()) return false; - Handle<FixedArray> array = handle(FixedArray::cast(obj)); - if (array->length() != kWrapperPropertyCount) return false; - if (!array->get(kWrapperInstanceObject)->IsWeakCell()) return false; - Isolate* isolate = array->GetIsolate(); - if (!array->get(kNextInstanceWrapper)->IsUndefined(isolate) && - !array->get(kNextInstanceWrapper)->IsFixedArray()) - return false; - if (!array->get(kPreviousInstanceWrapper)->IsUndefined(isolate) && - !array->get(kPreviousInstanceWrapper)->IsFixedArray()) - return false; - return true; -} diff --git a/deps/v8/src/wasm/wasm-objects.h b/deps/v8/src/wasm/wasm-objects.h index 00dfc60f10..ee8915317c 100644 --- a/deps/v8/src/wasm/wasm-objects.h +++ b/deps/v8/src/wasm/wasm-objects.h @@ -8,51 +8,64 @@ #include "src/debug/debug.h" #include "src/debug/interface-types.h" #include "src/objects.h" +#include "src/objects/script.h" #include "src/trap-handler/trap-handler.h" #include "src/wasm/wasm-limits.h" +#include "src/wasm/wasm-module.h" + +#include "src/heap/heap-inl.h" +#include "src/heap/heap.h" + +// Has to be the last include (doesn't have include guards) +#include "src/objects/object-macros.h" namespace v8 { namespace internal { namespace wasm { class InterpretedFrame; -struct WasmModule; -struct WasmInstance; class WasmInterpreter; } class WasmCompiledModule; class WasmDebugInfo; class WasmInstanceObject; -class WasmInstanceWrapper; -#define DECLARE_CASTS(name) \ - static bool Is##name(Object* object); \ - static name* cast(Object* object) +#define DECL_OOL_QUERY(type) static bool Is##type(Object* object); +#define DECL_OOL_CAST(type) static type* cast(Object* object); -#define DECLARE_GETTER(name, type) type* name() +#define DECL_GETTER(name, type) type* name(); -#define DECLARE_ACCESSORS(name, type) \ - void set_##name(type* value); \ - DECLARE_GETTER(name, type) +#define DECL_OPTIONAL_ACCESSORS(name, type) \ + INLINE(bool has_##name()); \ + DECL_ACCESSORS(name, type) -#define DECLARE_OPTIONAL_ACCESSORS(name, type) \ - bool has_##name(); \ - DECLARE_ACCESSORS(name, type) +#define DECL_OPTIONAL_GETTER(name, type) \ + INLINE(bool has_##name()); \ + DECL_GETTER(name, type) -#define DECLARE_OPTIONAL_GETTER(name, type) \ - bool has_##name(); \ - DECLARE_GETTER(name, type) +#define DEF_SIZE(parent) \ + static const int kSize = parent::kHeaderSize + kFieldCount * kPointerSize; \ + static const int kParentSize = parent::kHeaderSize; \ + static const int kHeaderSize = kSize; +#define DEF_OFFSET(name) \ + static const int k##name##Offset = \ + kSize + (k##name##Index - kFieldCount) * kPointerSize; // Representation of a WebAssembly.Module JavaScript-level object. class WasmModuleObject : public JSObject { public: - // If a second field is added, we need a kWrapperTracerHeader field as well. - // TODO(titzer): add the brand as an embedder field instead of a property. - enum Fields { kCompiledModule, kFieldCount }; + DECL_CAST(WasmModuleObject) - DECLARE_CASTS(WasmModuleObject); + // Shared compiled code between multiple WebAssembly.Module objects. + DECL_ACCESSORS(compiled_module, WasmCompiledModule) - WasmCompiledModule* compiled_module(); + enum { // -- + kCompiledModuleIndex, + kFieldCount + }; + + DEF_SIZE(JSObject) + DEF_OFFSET(CompiledModule) static Handle<WasmModuleObject> New( Isolate* isolate, Handle<WasmCompiledModule> compiled_module); @@ -61,23 +74,27 @@ class WasmModuleObject : public JSObject { // Representation of a WebAssembly.Table JavaScript-level object. class WasmTableObject : public JSObject { public: - // The 0-th field is used by the Blink Wrapper Tracer. - // TODO(titzer): add the brand as an embedder field instead of a property. - enum Fields { - kWrapperTracerHeader, - kFunctions, - kMaximum, - kDispatchTables, + DECL_CAST(WasmTableObject) + + DECL_ACCESSORS(functions, FixedArray) + // TODO(titzer): introduce DECL_I64_ACCESSORS macro + DECL_ACCESSORS(maximum_length, Object) + DECL_ACCESSORS(dispatch_tables, FixedArray) + + enum { // -- + kFunctionsIndex, + kMaximumLengthIndex, + kDispatchTablesIndex, kFieldCount }; - DECLARE_CASTS(WasmTableObject); - DECLARE_ACCESSORS(functions, FixedArray); - DECLARE_GETTER(dispatch_tables, FixedArray); + DEF_SIZE(JSObject) + DEF_OFFSET(Functions) + DEF_OFFSET(MaximumLength) + DEF_OFFSET(DispatchTables) - uint32_t current_length(); - bool has_maximum_length(); - int64_t maximum_length(); // Returns < 0 if no maximum. + inline uint32_t current_length() { return functions()->length(); } + inline bool has_maximum_length() { return maximum_length()->Number() >= 0; } void grow(Isolate* isolate, uint32_t count); static Handle<WasmTableObject> New(Isolate* isolate, uint32_t initial, @@ -92,25 +109,32 @@ class WasmTableObject : public JSObject { // Representation of a WebAssembly.Memory JavaScript-level object. class WasmMemoryObject : public JSObject { public: - // The 0-th field is used by the Blink Wrapper Tracer. - // TODO(titzer): add the brand as an embedder field instead of a property. - enum Fields : uint8_t { - kWrapperTracerHeader, - kArrayBuffer, - kMaximum, - kInstancesLink, + DECL_CAST(WasmMemoryObject) + + DECL_ACCESSORS(array_buffer, JSArrayBuffer) + DECL_INT_ACCESSORS(maximum_pages) + DECL_OPTIONAL_ACCESSORS(instances, WeakFixedArray) + + enum { // -- + kArrayBufferIndex, + kMaximumPagesIndex, + kInstancesIndex, kFieldCount }; - DECLARE_CASTS(WasmMemoryObject); - DECLARE_OPTIONAL_ACCESSORS(buffer, JSArrayBuffer); - DECLARE_OPTIONAL_ACCESSORS(instances_link, WasmInstanceWrapper); - - void AddInstance(Isolate* isolate, Handle<WasmInstanceObject> object); - void ResetInstancesLink(Isolate* isolate); + DEF_SIZE(JSObject) + DEF_OFFSET(ArrayBuffer) + DEF_OFFSET(MaximumPages) + DEF_OFFSET(Instances) + + // Add an instance to the internal (weak) list. amortized O(n). + static void AddInstance(Isolate* isolate, Handle<WasmMemoryObject> memory, + Handle<WasmInstanceObject> object); + // Remove an instance from the internal (weak) list. O(n). + static void RemoveInstance(Isolate* isolate, Handle<WasmMemoryObject> memory, + Handle<WasmInstanceObject> object); uint32_t current_pages(); - bool has_maximum_pages(); - int32_t maximum_pages(); // Returns < 0 if there is no maximum. + inline bool has_maximum_pages() { return maximum_pages() >= 0; } static Handle<WasmMemoryObject> New(Isolate* isolate, Handle<JSArrayBuffer> buffer, @@ -119,33 +143,39 @@ class WasmMemoryObject : public JSObject { static int32_t Grow(Isolate*, Handle<WasmMemoryObject>, uint32_t pages); }; -// Representation of a WebAssembly.Instance JavaScript-level object. +// A WebAssembly.Instance JavaScript-level object. class WasmInstanceObject : public JSObject { public: - // The 0-th field is used by the Blink Wrapper Tracer. - // TODO(titzer): add the brand as an embedder field instead of a property. - enum Fields { - kWrapperTracerHeader, - kCompiledModule, - kMemoryObject, - kMemoryArrayBuffer, - kGlobalsArrayBuffer, - kDebugInfo, - kWasmMemInstanceWrapper, + DECL_CAST(WasmInstanceObject) + + DECL_ACCESSORS(compiled_module, WasmCompiledModule) + DECL_OPTIONAL_ACCESSORS(memory_object, WasmMemoryObject) + DECL_OPTIONAL_ACCESSORS(memory_buffer, JSArrayBuffer) + DECL_OPTIONAL_ACCESSORS(globals_buffer, JSArrayBuffer) + DECL_OPTIONAL_ACCESSORS(debug_info, WasmDebugInfo) + // FixedArray of all instances whose code was imported + DECL_OPTIONAL_ACCESSORS(directly_called_instances, FixedArray) + + enum { // -- + kCompiledModuleIndex, + kMemoryObjectIndex, + kMemoryBufferIndex, + kGlobalsBufferIndex, + kDebugInfoIndex, + kDirectlyCalledInstancesIndex, kFieldCount }; - DECLARE_CASTS(WasmInstanceObject); - - DECLARE_ACCESSORS(compiled_module, WasmCompiledModule); - DECLARE_OPTIONAL_ACCESSORS(globals_buffer, JSArrayBuffer); - DECLARE_OPTIONAL_ACCESSORS(memory_buffer, JSArrayBuffer); - DECLARE_OPTIONAL_ACCESSORS(memory_object, WasmMemoryObject); - DECLARE_OPTIONAL_ACCESSORS(debug_info, WasmDebugInfo); - DECLARE_OPTIONAL_ACCESSORS(instance_wrapper, WasmInstanceWrapper); + DEF_SIZE(JSObject) + DEF_OFFSET(CompiledModule) + DEF_OFFSET(MemoryObject) + DEF_OFFSET(MemoryBuffer) + DEF_OFFSET(GlobalsBuffer) + DEF_OFFSET(DebugInfo) + DEF_OFFSET(DirectlyCalledInstances) WasmModuleObject* module_object(); - wasm::WasmModule* module(); + V8_EXPORT_PRIVATE wasm::WasmModule* module(); // Get the debug info associated with the given wasm object. // If no debug info exists yet, it is created automatically. @@ -161,17 +191,15 @@ class WasmInstanceObject : public JSObject { uint32_t GetMaxMemoryPages(); }; -// Representation of an exported WASM function. +// A WASM function that is wrapped and exported to JavaScript. class WasmExportedFunction : public JSFunction { public: - // The 0-th field is used by the Blink Wrapper Tracer. - enum Fields { kWrapperTracerHeader, kInstance, kIndex, kFieldCount }; - - DECLARE_CASTS(WasmExportedFunction); - WasmInstanceObject* instance(); int function_index(); + static WasmExportedFunction* cast(Object* object); + static bool IsWasmExportedFunction(Object* object); + static Handle<WasmExportedFunction> New(Isolate* isolate, Handle<WasmInstanceObject> instance, MaybeHandle<String> maybe_name, @@ -181,31 +209,33 @@ class WasmExportedFunction : public JSFunction { // Information shared by all WasmCompiledModule objects for the same module. class WasmSharedModuleData : public FixedArray { - // The 0-th field is used by the Blink Wrapper Tracer. - enum Fields { - kWrapperTracerHeader, - kModuleWrapper, - kModuleBytes, - kScript, - kAsmJsOffsetTable, - kBreakPointInfos, - kLazyCompilationOrchestrator, + public: + DECL_OOL_QUERY(WasmSharedModuleData) + DECL_OOL_CAST(WasmSharedModuleData) + + DECL_GETTER(module, wasm::WasmModule) + DECL_OPTIONAL_ACCESSORS(module_bytes, SeqOneByteString) + DECL_ACCESSORS(script, Script) + DECL_OPTIONAL_ACCESSORS(asm_js_offset_table, ByteArray) + DECL_OPTIONAL_ACCESSORS(breakpoint_infos, FixedArray) + + enum { // -- + kModuleWrapperIndex, + kModuleBytesIndex, + kScriptIndex, + kAsmJsOffsetTableIndex, + kBreakPointInfosIndex, + kLazyCompilationOrchestratorIndex, kFieldCount }; - public: - DECLARE_CASTS(WasmSharedModuleData); - - DECLARE_GETTER(module, wasm::WasmModule); - DECLARE_OPTIONAL_ACCESSORS(module_bytes, SeqOneByteString); - DECLARE_GETTER(script, Script); - DECLARE_OPTIONAL_ACCESSORS(asm_js_offset_table, ByteArray); - DECLARE_OPTIONAL_GETTER(breakpoint_infos, FixedArray); - - static Handle<WasmSharedModuleData> New( - Isolate* isolate, Handle<Foreign> module_wrapper, - Handle<SeqOneByteString> module_bytes, Handle<Script> script, - Handle<ByteArray> asm_js_offset_table); + DEF_SIZE(FixedArray) + DEF_OFFSET(ModuleWrapper) + DEF_OFFSET(ModuleBytes) + DEF_OFFSET(Script) + DEF_OFFSET(AsmJsOffsetTable) + DEF_OFFSET(BreakPointInfos) + DEF_OFFSET(LazyCompilationOrchestrator) // Check whether this module was generated from asm.js source. bool is_asm_js(); @@ -221,8 +251,13 @@ class WasmSharedModuleData : public FixedArray { static void PrepareForLazyCompilation(Handle<WasmSharedModuleData>); + static Handle<WasmSharedModuleData> New( + Isolate* isolate, Handle<Foreign> module_wrapper, + Handle<SeqOneByteString> module_bytes, Handle<Script> script, + Handle<ByteArray> asm_js_offset_table); + private: - DECLARE_OPTIONAL_GETTER(lazy_compilation_orchestrator, Foreign); + DECL_OPTIONAL_GETTER(lazy_compilation_orchestrator, Foreign) friend class WasmCompiledModule; }; @@ -252,7 +287,9 @@ class WasmSharedModuleData : public FixedArray { // we embed them as objects, and they may move. class WasmCompiledModule : public FixedArray { public: - enum Fields { kFieldCount }; + enum { // -- + kFieldCount + }; static WasmCompiledModule* cast(Object* fixed_array) { SLOW_DCHECK(IsWasmCompiledModule(fixed_array)); @@ -300,13 +337,11 @@ class WasmCompiledModule : public FixedArray { #define WCM_WASM_OBJECT(TYPE, NAME) \ WCM_OBJECT_OR_WEAK(TYPE, NAME, kID_##NAME, TYPE::Is##TYPE(obj), private) -#define WCM_SMALL_CONST_NUMBER(TYPE, NAME) \ - public: \ - TYPE NAME() const { \ - return static_cast<TYPE>(Smi::cast(get(kID_##NAME))->value()); \ - } \ - \ - private: \ +#define WCM_SMALL_CONST_NUMBER(TYPE, NAME) \ + public: \ + TYPE NAME() const { return static_cast<TYPE>(Smi::ToInt(get(kID_##NAME))); } \ + \ + private: \ void set_##NAME(TYPE value) { set(kID_##NAME, Smi::FromInt(value)); } #define WCM_WEAK_LINK(TYPE, NAME) \ @@ -441,6 +476,10 @@ class WasmCompiledModule : public FixedArray { static void ReinitializeAfterDeserialization(Isolate*, Handle<WasmCompiledModule>); + // Get the module name, if set. Returns an empty handle otherwise. + static MaybeHandle<String> GetModuleNameOrNull( + Isolate* isolate, Handle<WasmCompiledModule> compiled_module); + // Get the function name of the function identified by the given index. // Returns a null handle if the function is unnamed or the name is not a valid // UTF-8 string. @@ -494,7 +533,7 @@ class WasmCompiledModule : public FixedArray { // string. static MaybeHandle<String> ExtractUtf8StringFromModuleBytes( Isolate* isolate, Handle<WasmCompiledModule> compiled_module, - uint32_t offset, uint32_t size); + wasm::WireBytesRef ref); // Get a list of all possible breakpoints within a given range of this module. bool GetPossibleBreakpoints(const debug::Location& start, @@ -537,26 +576,34 @@ class WasmCompiledModule : public FixedArray { class WasmDebugInfo : public FixedArray { public: - // The 0-th field is used by the Blink Wrapper Tracer. - enum Fields { - kWrapperTracerHeader, - kInstance, - kInterpreterHandle, - kInterpretedFunctions, + DECL_OOL_QUERY(WasmDebugInfo) + DECL_OOL_CAST(WasmDebugInfo) + + DECL_GETTER(wasm_instance, WasmInstanceObject) + DECL_OPTIONAL_ACCESSORS(locals_names, FixedArray) + + enum { + kInstanceIndex, // instance object. + kInterpreterHandleIndex, // managed object containing the interpreter. + kInterpretedFunctionsIndex, // array of interpreter entry code objects. + kLocalsNamesIndex, // array of array of local names. kFieldCount }; + DEF_SIZE(FixedArray) + DEF_OFFSET(Instance) + DEF_OFFSET(InterpreterHandle) + DEF_OFFSET(InterpretedFunctions) + DEF_OFFSET(LocalsNames) + static Handle<WasmDebugInfo> New(Handle<WasmInstanceObject>); // Setup a WasmDebugInfo with an existing WasmInstance struct. // Returns a pointer to the interpreter instantiated inside this // WasmDebugInfo. // Use for testing only. - static wasm::WasmInterpreter* SetupForTesting(Handle<WasmInstanceObject>, - wasm::WasmInstance*); - - static bool IsDebugInfo(Object*); - static WasmDebugInfo* cast(Object*); + V8_EXPORT_PRIVATE static wasm::WasmInterpreter* SetupForTesting( + Handle<WasmInstanceObject>, wasm::WasmInstance*); // Set a breakpoint in the given function at the given byte offset within that // function. This will redirect all future calls to this function to the @@ -594,8 +641,6 @@ class WasmDebugInfo : public FixedArray { // Returns the number of calls / function frames executed in the interpreter. uint64_t NumInterpretedCalls(); - DECLARE_GETTER(wasm_instance, WasmInstanceObject); - // Update the memory view of the interpreter after executing GrowMemory in // compiled code. void UpdateMemory(JSArrayBuffer* new_memory); @@ -612,65 +657,76 @@ class WasmDebugInfo : public FixedArray { int frame_index); }; -class WasmInstanceWrapper : public FixedArray { - public: - static Handle<WasmInstanceWrapper> New(Isolate* isolate, - Handle<WasmInstanceObject> instance); - static WasmInstanceWrapper* cast(Object* fixed_array) { - SLOW_DCHECK(IsWasmInstanceWrapper(fixed_array)); - return reinterpret_cast<WasmInstanceWrapper*>(fixed_array); - } - static bool IsWasmInstanceWrapper(Object* obj); - bool has_instance() { return get(kWrapperInstanceObject)->IsWeakCell(); } - Handle<WasmInstanceObject> instance_object() { - Object* obj = get(kWrapperInstanceObject); - DCHECK(obj->IsWeakCell()); - WeakCell* cell = WeakCell::cast(obj); - DCHECK(cell->value()->IsJSObject()); - return handle(WasmInstanceObject::cast(cell->value())); - } - bool has_next() { return IsWasmInstanceWrapper(get(kNextInstanceWrapper)); } - bool has_previous() { - return IsWasmInstanceWrapper(get(kPreviousInstanceWrapper)); - } - void set_next_wrapper(Object* obj) { - DCHECK(IsWasmInstanceWrapper(obj)); - set(kNextInstanceWrapper, obj); - } - void set_previous_wrapper(Object* obj) { - DCHECK(IsWasmInstanceWrapper(obj)); - set(kPreviousInstanceWrapper, obj); - } - Handle<WasmInstanceWrapper> next_wrapper() { - Object* obj = get(kNextInstanceWrapper); - DCHECK(IsWasmInstanceWrapper(obj)); - return handle(WasmInstanceWrapper::cast(obj)); - } - Handle<WasmInstanceWrapper> previous_wrapper() { - Object* obj = get(kPreviousInstanceWrapper); - DCHECK(IsWasmInstanceWrapper(obj)); - return handle(WasmInstanceWrapper::cast(obj)); - } - void reset_next_wrapper() { set_undefined(kNextInstanceWrapper); } - void reset_previous_wrapper() { set_undefined(kPreviousInstanceWrapper); } - void reset() { - for (int kID = 0; kID < kWrapperPropertyCount; kID++) set_undefined(kID); +// TODO(titzer): these should be moved to wasm-objects-inl.h +CAST_ACCESSOR(WasmInstanceObject) +CAST_ACCESSOR(WasmMemoryObject) +CAST_ACCESSOR(WasmModuleObject) +CAST_ACCESSOR(WasmTableObject) + +// WasmModuleObject +ACCESSORS(WasmModuleObject, compiled_module, WasmCompiledModule, + kCompiledModuleOffset) + +// WasmTableObject +ACCESSORS(WasmTableObject, functions, FixedArray, kFunctionsOffset) +ACCESSORS(WasmTableObject, maximum_length, Object, kMaximumLengthOffset) +ACCESSORS(WasmTableObject, dispatch_tables, FixedArray, kDispatchTablesOffset) + +// WasmMemoryObject +ACCESSORS(WasmMemoryObject, array_buffer, JSArrayBuffer, kArrayBufferOffset) +SMI_ACCESSORS(WasmMemoryObject, maximum_pages, kMaximumPagesOffset) +ACCESSORS(WasmMemoryObject, instances, WeakFixedArray, kInstancesOffset) + +// WasmInstanceObject +ACCESSORS(WasmInstanceObject, compiled_module, WasmCompiledModule, + kCompiledModuleOffset) +ACCESSORS(WasmInstanceObject, memory_object, WasmMemoryObject, + kMemoryObjectOffset) +ACCESSORS(WasmInstanceObject, memory_buffer, JSArrayBuffer, kMemoryBufferOffset) +ACCESSORS(WasmInstanceObject, globals_buffer, JSArrayBuffer, + kGlobalsBufferOffset) +ACCESSORS(WasmInstanceObject, debug_info, WasmDebugInfo, kDebugInfoOffset) +ACCESSORS(WasmInstanceObject, directly_called_instances, FixedArray, + kDirectlyCalledInstancesOffset) + +// WasmSharedModuleData +ACCESSORS(WasmSharedModuleData, module_bytes, SeqOneByteString, + kModuleBytesOffset) +ACCESSORS(WasmSharedModuleData, script, Script, kScriptOffset) +ACCESSORS(WasmSharedModuleData, asm_js_offset_table, ByteArray, + kAsmJsOffsetTableOffset) +ACCESSORS(WasmSharedModuleData, breakpoint_infos, FixedArray, + kBreakPointInfosOffset) + +#define OPTIONAL_ACCESSOR(holder, name, offset) \ + bool holder::has_##name() { \ + return !READ_FIELD(this, offset)->IsUndefined(GetIsolate()); \ } - private: - enum { - kWrapperInstanceObject, - kNextInstanceWrapper, - kPreviousInstanceWrapper, - kWrapperPropertyCount - }; -}; +OPTIONAL_ACCESSOR(WasmInstanceObject, debug_info, kDebugInfoOffset) +OPTIONAL_ACCESSOR(WasmInstanceObject, memory_buffer, kMemoryBufferOffset) +OPTIONAL_ACCESSOR(WasmInstanceObject, memory_object, kMemoryObjectOffset) + +OPTIONAL_ACCESSOR(WasmMemoryObject, instances, kInstancesOffset) + +OPTIONAL_ACCESSOR(WasmSharedModuleData, breakpoint_infos, + kBreakPointInfosOffset) +OPTIONAL_ACCESSOR(WasmSharedModuleData, asm_js_offset_table, + kAsmJsOffsetTableOffset) +OPTIONAL_ACCESSOR(WasmSharedModuleData, lazy_compilation_orchestrator, + kLazyCompilationOrchestratorOffset) + +ACCESSORS(WasmDebugInfo, locals_names, FixedArray, kLocalsNamesOffset) + +OPTIONAL_ACCESSOR(WasmDebugInfo, locals_names, kLocalsNamesOffset) + +#undef DECL_OOL_QUERY +#undef DECL_OOL_CAST +#undef DECL_GETTER +#undef DECL_OPTIONAL_ACCESSORS +#undef DECL_OPTIONAL_GETTER -#undef DECLARE_CASTS -#undef DECLARE_GETTER -#undef DECLARE_ACCESSORS -#undef DECLARE_OPTIONAL_ACCESSORS -#undef DECLARE_OPTIONAL_GETTER +#include "src/objects/object-macros-undef.h" } // namespace internal } // namespace v8 diff --git a/deps/v8/src/wasm/wasm-opcodes.cc b/deps/v8/src/wasm/wasm-opcodes.cc index 355cdf40b5..10bc69dfb2 100644 --- a/deps/v8/src/wasm/wasm-opcodes.cc +++ b/deps/v8/src/wasm/wasm-opcodes.cc @@ -3,6 +3,10 @@ // found in the LICENSE file. #include "src/wasm/wasm-opcodes.h" + +#include <array> + +#include "src/base/template-utils.h" #include "src/messages.h" #include "src/runtime/runtime.h" #include "src/signature.h" @@ -39,6 +43,7 @@ namespace wasm { CASE_I32x4_OP(name, str) CASE_I16x8_OP(name, str) CASE_I8x16_OP(name, str) #define CASE_SIGN_OP(TYPE, name, str) \ CASE_##TYPE##_OP(name##S, str "_s") CASE_##TYPE##_OP(name##U, str "_u") +#define CASE_UNSIGNED_OP(TYPE, name, str) CASE_##TYPE##_OP(name##U, str "_u") #define CASE_ALL_SIGN_OP(name, str) \ CASE_FLOAT_OP(name, str) CASE_SIGN_OP(INT, name, str) #define CASE_CONVERT_OP(name, RES, SRC, src_suffix, str) \ @@ -48,6 +53,10 @@ namespace wasm { CASE_SIGN_OP(I32, name##8, str "8") \ CASE_SIGN_OP(I32, name##16, str "16") \ CASE_I32_OP(name, str "32") +#define CASE_U32_OP(name, str) \ + CASE_I32_OP(name, str "32") \ + CASE_UNSIGNED_OP(I32, name##8, str "8") \ + CASE_UNSIGNED_OP(I32, name##16, str "16") const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { switch (opcode) { @@ -137,7 +146,9 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { // Non-standard opcodes. CASE_OP(Try, "try") CASE_OP(Throw, "throw") + CASE_OP(Rethrow, "rethrow") CASE_OP(Catch, "catch") + CASE_OP(CatchAll, "catch_all") // asm.js-only opcodes. CASE_F64_OP(Acos, "acos") @@ -214,39 +225,23 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { CASE_S128_OP(Or, "or") CASE_S128_OP(Xor, "xor") CASE_S128_OP(Not, "not") - CASE_S32x4_OP(Shuffle, "shuffle") - CASE_S32x4_OP(Select, "select") - CASE_S16x8_OP(Shuffle, "shuffle") - CASE_S16x8_OP(Select, "select") + CASE_S128_OP(Select, "select") CASE_S8x16_OP(Shuffle, "shuffle") - CASE_S8x16_OP(Select, "select") - CASE_S1x4_OP(And, "and") - CASE_S1x4_OP(Or, "or") - CASE_S1x4_OP(Xor, "xor") - CASE_S1x4_OP(Not, "not") CASE_S1x4_OP(AnyTrue, "any_true") CASE_S1x4_OP(AllTrue, "all_true") - CASE_S1x8_OP(And, "and") - CASE_S1x8_OP(Or, "or") - CASE_S1x8_OP(Xor, "xor") - CASE_S1x8_OP(Not, "not") CASE_S1x8_OP(AnyTrue, "any_true") CASE_S1x8_OP(AllTrue, "all_true") - CASE_S1x16_OP(And, "and") - CASE_S1x16_OP(Or, "or") - CASE_S1x16_OP(Xor, "xor") - CASE_S1x16_OP(Not, "not") CASE_S1x16_OP(AnyTrue, "any_true") CASE_S1x16_OP(AllTrue, "all_true") // Atomic operations. - CASE_L32_OP(AtomicAdd, "atomic_add") - CASE_L32_OP(AtomicAnd, "atomic_and") - CASE_L32_OP(AtomicCompareExchange, "atomic_cmpxchng") - CASE_L32_OP(AtomicExchange, "atomic_xchng") - CASE_L32_OP(AtomicOr, "atomic_or") - CASE_L32_OP(AtomicSub, "atomic_sub") - CASE_L32_OP(AtomicXor, "atomic_xor") + CASE_U32_OP(AtomicAdd, "atomic_add") + CASE_U32_OP(AtomicSub, "atomic_sub") + CASE_U32_OP(AtomicAnd, "atomic_and") + CASE_U32_OP(AtomicOr, "atomic_or") + CASE_U32_OP(AtomicXor, "atomic_xor") + CASE_U32_OP(AtomicExchange, "atomic_xchng") + CASE_U32_OP(AtomicCompareExchange, "atomic_cmpxchng") default : return "unknown"; // clang-format on @@ -255,11 +250,10 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { bool WasmOpcodes::IsPrefixOpcode(WasmOpcode opcode) { switch (opcode) { -#define CHECK_PREFIX(name, opcode) \ - case k##name##Prefix: \ - return true; +#define CHECK_PREFIX(name, opcode) case k##name##Prefix: FOREACH_PREFIX(CHECK_PREFIX) #undef CHECK_PREFIX + return true; default: return false; } @@ -267,11 +261,10 @@ bool WasmOpcodes::IsPrefixOpcode(WasmOpcode opcode) { bool WasmOpcodes::IsControlOpcode(WasmOpcode opcode) { switch (opcode) { -#define CHECK_OPCODE(name, opcode, _) \ - case kExpr##name: \ - return true; +#define CHECK_OPCODE(name, opcode, _) case kExpr##name: FOREACH_CONTROL_OPCODE(CHECK_OPCODE) #undef CHECK_OPCODE + return true; default: return false; } @@ -309,106 +302,103 @@ bool IsJSCompatibleSignature(const FunctionSig* sig) { return true; } +namespace { + #define DECLARE_SIG_ENUM(name, ...) kSigEnum_##name, -enum WasmOpcodeSig { FOREACH_SIGNATURE(DECLARE_SIG_ENUM) }; +enum WasmOpcodeSig : byte { + kSigEnum_None, + FOREACH_SIGNATURE(DECLARE_SIG_ENUM) +}; -// TODO(titzer): not static-initializer safe. Wrap in LazyInstance. -#define DECLARE_SIG(name, ...) \ - static ValueType kTypes_##name[] = {__VA_ARGS__}; \ - static const FunctionSig kSig_##name( \ +#define DECLARE_SIG(name, ...) \ + constexpr ValueType kTypes_##name[] = {__VA_ARGS__}; \ + constexpr FunctionSig kSig_##name( \ 1, static_cast<int>(arraysize(kTypes_##name)) - 1, kTypes_##name); FOREACH_SIGNATURE(DECLARE_SIG) #define DECLARE_SIG_ENTRY(name, ...) &kSig_##name, -static const FunctionSig* kSimpleExprSigs[] = { +constexpr const FunctionSig* kSimpleExprSigs[] = { nullptr, FOREACH_SIGNATURE(DECLARE_SIG_ENTRY)}; -#define DECLARE_SIMD_SIG_ENTRY(name, ...) &kSig_##name, - -static const FunctionSig* kSimdExprSigs[] = { - nullptr, FOREACH_SIMD_SIGNATURE(DECLARE_SIMD_SIG_ENTRY)}; - -static byte kSimpleExprSigTable[256]; -static byte kSimpleAsmjsExprSigTable[256]; -static byte kSimdExprSigTable[256]; -static byte kAtomicExprSigTable[256]; - -// Initialize the signature table. -static void InitSigTables() { -#define SET_SIG_TABLE(name, opcode, sig) \ - kSimpleExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1; - FOREACH_SIMPLE_OPCODE(SET_SIG_TABLE); -#undef SET_SIG_TABLE -#define SET_ASMJS_SIG_TABLE(name, opcode, sig) \ - kSimpleAsmjsExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1; - FOREACH_ASMJS_COMPAT_OPCODE(SET_ASMJS_SIG_TABLE); -#undef SET_ASMJS_SIG_TABLE - byte simd_index; -#define SET_SIG_TABLE(name, opcode, sig) \ - simd_index = opcode & 0xff; \ - kSimdExprSigTable[simd_index] = static_cast<int>(kSigEnum_##sig) + 1; - FOREACH_SIMD_0_OPERAND_OPCODE(SET_SIG_TABLE) -#undef SET_SIG_TABLE - byte atomic_index; -#define SET_ATOMIC_SIG_TABLE(name, opcode, sig) \ - atomic_index = opcode & 0xff; \ - kAtomicExprSigTable[atomic_index] = static_cast<int>(kSigEnum_##sig) + 1; - FOREACH_ATOMIC_OPCODE(SET_ATOMIC_SIG_TABLE) -#undef SET_ATOMIC_SIG_TABLE +// The following constexpr functions are used to initialize the constant arrays +// defined below. They must have exactly one return statement, and no switch. +constexpr WasmOpcodeSig GetOpcodeSigIndex(byte opcode) { + return +#define CASE(name, opc, sig) opcode == opc ? kSigEnum_##sig: + FOREACH_SIMPLE_OPCODE(CASE) +#undef CASE + kSigEnum_None; } -class SigTable { - public: - SigTable() { - // TODO(ahaas): Move {InitSigTable} into the class. - InitSigTables(); - } - FunctionSig* Signature(WasmOpcode opcode) const { - return const_cast<FunctionSig*>( - kSimpleExprSigs[kSimpleExprSigTable[static_cast<byte>(opcode)]]); - } - FunctionSig* AsmjsSignature(WasmOpcode opcode) const { - return const_cast<FunctionSig*>( - kSimpleExprSigs[kSimpleAsmjsExprSigTable[static_cast<byte>(opcode)]]); - } - FunctionSig* SimdSignature(WasmOpcode opcode) const { - return const_cast<FunctionSig*>( - kSimdExprSigs[kSimdExprSigTable[static_cast<byte>(opcode & 0xff)]]); - } - FunctionSig* AtomicSignature(WasmOpcode opcode) const { - return const_cast<FunctionSig*>( - kSimpleExprSigs[kAtomicExprSigTable[static_cast<byte>(opcode & 0xff)]]); - } -}; +constexpr WasmOpcodeSig GetAsmJsOpcodeSigIndex(byte opcode) { + return +#define CASE(name, opc, sig) opcode == opc ? kSigEnum_##sig: + FOREACH_ASMJS_COMPAT_OPCODE(CASE) +#undef CASE + kSigEnum_None; +} + +constexpr WasmOpcodeSig GetSimdOpcodeSigIndex(byte opcode) { + return +#define CASE(name, opc, sig) opcode == (opc & 0xff) ? kSigEnum_##sig: + FOREACH_SIMD_0_OPERAND_OPCODE(CASE) +#undef CASE + kSigEnum_None; +} + +constexpr WasmOpcodeSig GetAtomicOpcodeSigIndex(byte opcode) { + return +#define CASE(name, opc, sig) opcode == (opc & 0xff) ? kSigEnum_##sig: + FOREACH_ATOMIC_OPCODE(CASE) +#undef CASE + kSigEnum_None; +} + +// gcc 4.7 - 4.9 have a bug which prohibits marking the array constexpr +// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52892). +// TODO(clemensh): Remove this once we require gcc >= 5.0. +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 4 +#define CONSTEXPR_IF_NOT_GCC_4 +#else +#define CONSTEXPR_IF_NOT_GCC_4 constexpr +#endif -static base::LazyInstance<SigTable>::type sig_table = LAZY_INSTANCE_INITIALIZER; +CONSTEXPR_IF_NOT_GCC_4 std::array<WasmOpcodeSig, 256> kSimpleExprSigTable = + base::make_array<256>(GetOpcodeSigIndex); +CONSTEXPR_IF_NOT_GCC_4 std::array<WasmOpcodeSig, 256> kSimpleAsmjsExprSigTable = + base::make_array<256>(GetAsmJsOpcodeSigIndex); +CONSTEXPR_IF_NOT_GCC_4 std::array<WasmOpcodeSig, 256> kSimdExprSigTable = + base::make_array<256>(GetSimdOpcodeSigIndex); +CONSTEXPR_IF_NOT_GCC_4 std::array<WasmOpcodeSig, 256> kAtomicExprSigTable = + base::make_array<256>(GetAtomicOpcodeSigIndex); + +} // namespace FunctionSig* WasmOpcodes::Signature(WasmOpcode opcode) { if (opcode >> 8 == kSimdPrefix) { - return sig_table.Get().SimdSignature(opcode); + return const_cast<FunctionSig*>( + kSimpleExprSigs[kSimdExprSigTable[opcode & 0xff]]); } else { - return sig_table.Get().Signature(opcode); + DCHECK_GT(kSimpleExprSigTable.size(), opcode); + return const_cast<FunctionSig*>( + kSimpleExprSigs[kSimpleExprSigTable[opcode]]); } } FunctionSig* WasmOpcodes::AsmjsSignature(WasmOpcode opcode) { - return sig_table.Get().AsmjsSignature(opcode); + DCHECK_GT(kSimpleAsmjsExprSigTable.size(), opcode); + return const_cast<FunctionSig*>( + kSimpleExprSigs[kSimpleAsmjsExprSigTable[opcode]]); } FunctionSig* WasmOpcodes::AtomicSignature(WasmOpcode opcode) { - return sig_table.Get().AtomicSignature(opcode); + return const_cast<FunctionSig*>( + kSimpleExprSigs[kAtomicExprSigTable[opcode & 0xff]]); } -// TODO(titzer): pull WASM_64 up to a common header. -#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 -#define WASM_64 1 -#else -#define WASM_64 0 -#endif - int WasmOpcodes::TrapReasonToMessageId(TrapReason reason) { switch (reason) { #define TRAPREASON_TO_MESSAGE(name) \ diff --git a/deps/v8/src/wasm/wasm-opcodes.h b/deps/v8/src/wasm/wasm-opcodes.h index a1a84366a2..7405a83ec8 100644 --- a/deps/v8/src/wasm/wasm-opcodes.h +++ b/deps/v8/src/wasm/wasm-opcodes.h @@ -21,10 +21,7 @@ enum ValueTypeCode { kLocalI64 = 0x7e, kLocalF32 = 0x7d, kLocalF64 = 0x7c, - kLocalS128 = 0x7b, - kLocalS1x4 = 0x7a, - kLocalS1x8 = 0x79, - kLocalS1x16 = 0x78 + kLocalS128 = 0x7b }; // Type code for multi-value block types. @@ -39,9 +36,6 @@ constexpr ValueType kWasmI64 = MachineRepresentation::kWord64; constexpr ValueType kWasmF32 = MachineRepresentation::kFloat32; constexpr ValueType kWasmF64 = MachineRepresentation::kFloat64; constexpr ValueType kWasmS128 = MachineRepresentation::kSimd128; -constexpr ValueType kWasmS1x4 = MachineRepresentation::kSimd1x4; -constexpr ValueType kWasmS1x8 = MachineRepresentation::kSimd1x8; -constexpr ValueType kWasmS1x16 = MachineRepresentation::kSimd1x16; constexpr ValueType kWasmVar = MachineRepresentation::kTagged; using FunctionSig = Signature<ValueType>; @@ -54,20 +48,22 @@ using WasmCodePosition = int; constexpr WasmCodePosition kNoCodePosition = -1; // Control expressions and blocks. -#define FOREACH_CONTROL_OPCODE(V) \ - V(Unreachable, 0x00, _) \ - V(Nop, 0x01, _) \ - V(Block, 0x02, _) \ - V(Loop, 0x03, _) \ - V(If, 0x004, _) \ - V(Else, 0x05, _) \ - V(Try, 0x06, _ /* eh_prototype */) \ - V(Catch, 0x07, _ /* eh_prototype */) \ - V(Throw, 0x08, _ /* eh_prototype */) \ - V(End, 0x0b, _) \ - V(Br, 0x0c, _) \ - V(BrIf, 0x0d, _) \ - V(BrTable, 0x0e, _) \ +#define FOREACH_CONTROL_OPCODE(V) \ + V(Unreachable, 0x00, _) \ + V(Nop, 0x01, _) \ + V(Block, 0x02, _) \ + V(Loop, 0x03, _) \ + V(If, 0x004, _) \ + V(Else, 0x05, _) \ + V(Try, 0x06, _ /* eh_prototype */) \ + V(Catch, 0x07, _ /* eh_prototype */) \ + V(Throw, 0x08, _ /* eh_prototype */) \ + V(Rethrow, 0x09, _ /* eh_prototype */) \ + V(CatchAll, 0x0a, _ /* eh prototype */) \ + V(End, 0x0b, _) \ + V(Br, 0x0c, _) \ + V(BrIf, 0x0d, _) \ + V(BrTable, 0x0e, _) \ V(Return, 0x0f, _) // Constants, locals, globals, and calls. @@ -101,8 +97,7 @@ constexpr WasmCodePosition kNoCodePosition = -1; V(I64LoadMem16S, 0x32, l_i) \ V(I64LoadMem16U, 0x33, l_i) \ V(I64LoadMem32S, 0x34, l_i) \ - V(I64LoadMem32U, 0x35, l_i) \ - V(S128LoadMem, 0xc0, s_i) + V(I64LoadMem32U, 0x35, l_i) // Store memory expressions. #define FOREACH_STORE_MEM_OPCODE(V) \ @@ -114,8 +109,7 @@ constexpr WasmCodePosition kNoCodePosition = -1; V(I32StoreMem16, 0x3b, i_ii) \ V(I64StoreMem8, 0x3c, l_il) \ V(I64StoreMem16, 0x3d, l_il) \ - V(I64StoreMem32, 0x3e, l_il) \ - V(S128StoreMem, 0xc1, s_is) + V(I64StoreMem32, 0x3e, l_il) // Miscellaneous memory expressions #define FOREACH_MISC_MEM_OPCODE(V) \ @@ -283,192 +277,165 @@ constexpr WasmCodePosition kNoCodePosition = -1; V(I32AsmjsUConvertF64, 0xe3, i_d) #define FOREACH_SIMD_0_OPERAND_OPCODE(V) \ - V(F32x4Splat, 0xe500, s_f) \ - V(F32x4Abs, 0xe503, s_s) \ - V(F32x4Neg, 0xe504, s_s) \ - V(F32x4RecipApprox, 0xe506, s_s) \ - V(F32x4RecipSqrtApprox, 0xe507, s_s) \ - V(F32x4Add, 0xe508, s_ss) \ - V(F32x4AddHoriz, 0xe5b9, s_ss) \ - V(F32x4Sub, 0xe509, s_ss) \ - V(F32x4Mul, 0xe50a, s_ss) \ - V(F32x4Min, 0xe50c, s_ss) \ - V(F32x4Max, 0xe50d, s_ss) \ - V(F32x4Eq, 0xe510, s1x4_ss) \ - V(F32x4Ne, 0xe511, s1x4_ss) \ - V(F32x4Lt, 0xe512, s1x4_ss) \ - V(F32x4Le, 0xe513, s1x4_ss) \ - V(F32x4Gt, 0xe514, s1x4_ss) \ - V(F32x4Ge, 0xe515, s1x4_ss) \ - V(F32x4SConvertI32x4, 0xe519, s_s) \ - V(F32x4UConvertI32x4, 0xe51a, s_s) \ - V(I32x4Splat, 0xe51b, s_i) \ - V(I32x4Neg, 0xe51e, s_s) \ - V(I32x4Add, 0xe51f, s_ss) \ - V(I32x4AddHoriz, 0xe5ba, s_ss) \ - V(I32x4Sub, 0xe520, s_ss) \ - V(I32x4Mul, 0xe521, s_ss) \ - V(I32x4MinS, 0xe522, s_ss) \ - V(I32x4MaxS, 0xe523, s_ss) \ - V(I32x4Eq, 0xe526, s1x4_ss) \ - V(I32x4Ne, 0xe527, s1x4_ss) \ - V(I32x4LtS, 0xe528, s1x4_ss) \ - V(I32x4LeS, 0xe529, s1x4_ss) \ - V(I32x4GtS, 0xe52a, s1x4_ss) \ - V(I32x4GeS, 0xe52b, s1x4_ss) \ - V(I32x4SConvertF32x4, 0xe52f, s_s) \ - V(I32x4UConvertF32x4, 0xe537, s_s) \ - V(I32x4SConvertI16x8Low, 0xe594, s_s) \ - V(I32x4SConvertI16x8High, 0xe595, s_s) \ - V(I32x4UConvertI16x8Low, 0xe596, s_s) \ - V(I32x4UConvertI16x8High, 0xe597, s_s) \ - V(I32x4MinU, 0xe530, s_ss) \ - V(I32x4MaxU, 0xe531, s_ss) \ - V(I32x4LtU, 0xe533, s1x4_ss) \ - V(I32x4LeU, 0xe534, s1x4_ss) \ - V(I32x4GtU, 0xe535, s1x4_ss) \ - V(I32x4GeU, 0xe536, s1x4_ss) \ - V(I16x8Splat, 0xe538, s_i) \ - V(I16x8Neg, 0xe53b, s_s) \ - V(I16x8Add, 0xe53c, s_ss) \ - V(I16x8AddSaturateS, 0xe53d, s_ss) \ - V(I16x8AddHoriz, 0xe5bb, s_ss) \ - V(I16x8Sub, 0xe53e, s_ss) \ - V(I16x8SubSaturateS, 0xe53f, s_ss) \ - V(I16x8Mul, 0xe540, s_ss) \ - V(I16x8MinS, 0xe541, s_ss) \ - V(I16x8MaxS, 0xe542, s_ss) \ - V(I16x8Eq, 0xe545, s1x8_ss) \ - V(I16x8Ne, 0xe546, s1x8_ss) \ - V(I16x8LtS, 0xe547, s1x8_ss) \ - V(I16x8LeS, 0xe548, s1x8_ss) \ - V(I16x8GtS, 0xe549, s1x8_ss) \ - V(I16x8GeS, 0xe54a, s1x8_ss) \ - V(I16x8AddSaturateU, 0xe54e, s_ss) \ - V(I16x8SubSaturateU, 0xe54f, s_ss) \ - V(I16x8MinU, 0xe550, s_ss) \ - V(I16x8MaxU, 0xe551, s_ss) \ - V(I16x8LtU, 0xe553, s1x8_ss) \ - V(I16x8LeU, 0xe554, s1x8_ss) \ - V(I16x8GtU, 0xe555, s1x8_ss) \ - V(I16x8GeU, 0xe556, s1x8_ss) \ - V(I16x8SConvertI32x4, 0xe598, s_ss) \ - V(I16x8UConvertI32x4, 0xe599, s_ss) \ - V(I16x8SConvertI8x16Low, 0xe59a, s_s) \ - V(I16x8SConvertI8x16High, 0xe59b, s_s) \ - V(I16x8UConvertI8x16Low, 0xe59c, s_s) \ - V(I16x8UConvertI8x16High, 0xe59d, s_s) \ - V(I8x16Splat, 0xe557, s_i) \ - V(I8x16Neg, 0xe55a, s_s) \ - V(I8x16Add, 0xe55b, s_ss) \ - V(I8x16AddSaturateS, 0xe55c, s_ss) \ - V(I8x16Sub, 0xe55d, s_ss) \ - V(I8x16SubSaturateS, 0xe55e, s_ss) \ - V(I8x16Mul, 0xe55f, s_ss) \ - V(I8x16MinS, 0xe560, s_ss) \ - V(I8x16MaxS, 0xe561, s_ss) \ - V(I8x16Eq, 0xe564, s1x16_ss) \ - V(I8x16Ne, 0xe565, s1x16_ss) \ - V(I8x16LtS, 0xe566, s1x16_ss) \ - V(I8x16LeS, 0xe567, s1x16_ss) \ - V(I8x16GtS, 0xe568, s1x16_ss) \ - V(I8x16GeS, 0xe569, s1x16_ss) \ - V(I8x16AddSaturateU, 0xe56d, s_ss) \ - V(I8x16SubSaturateU, 0xe56e, s_ss) \ - V(I8x16MinU, 0xe56f, s_ss) \ - V(I8x16MaxU, 0xe570, s_ss) \ - V(I8x16LtU, 0xe572, s1x16_ss) \ - V(I8x16LeU, 0xe573, s1x16_ss) \ - V(I8x16GtU, 0xe574, s1x16_ss) \ - V(I8x16GeU, 0xe575, s1x16_ss) \ - V(I8x16SConvertI16x8, 0xe59e, s_ss) \ - V(I8x16UConvertI16x8, 0xe59f, s_ss) \ - V(S128And, 0xe576, s_ss) \ - V(S128Or, 0xe577, s_ss) \ - V(S128Xor, 0xe578, s_ss) \ - V(S128Not, 0xe579, s_s) \ - V(S32x4Select, 0xe52c, s_s1x4ss) \ - V(S16x8Select, 0xe54b, s_s1x8ss) \ - V(S8x16Select, 0xe56a, s_s1x16ss) \ - V(S1x4And, 0xe580, s1x4_s1x4s1x4) \ - V(S1x4Or, 0xe581, s1x4_s1x4s1x4) \ - V(S1x4Xor, 0xe582, s1x4_s1x4s1x4) \ - V(S1x4Not, 0xe583, s1x4_s1x4) \ - V(S1x4AnyTrue, 0xe584, i_s1x4) \ - V(S1x4AllTrue, 0xe585, i_s1x4) \ - V(S1x8And, 0xe586, s1x8_s1x8s1x8) \ - V(S1x8Or, 0xe587, s1x8_s1x8s1x8) \ - V(S1x8Xor, 0xe588, s1x8_s1x8s1x8) \ - V(S1x8Not, 0xe589, s1x8_s1x8) \ - V(S1x8AnyTrue, 0xe58a, i_s1x8) \ - V(S1x8AllTrue, 0xe58b, i_s1x8) \ - V(S1x16And, 0xe58c, s1x16_s1x16s1x16) \ - V(S1x16Or, 0xe58d, s1x16_s1x16s1x16) \ - V(S1x16Xor, 0xe58e, s1x16_s1x16s1x16) \ - V(S1x16Not, 0xe58f, s1x16_s1x16) \ - V(S1x16AnyTrue, 0xe590, i_s1x16) \ - V(S1x16AllTrue, 0xe591, i_s1x16) + V(F32x4Splat, 0xfd00, s_f) \ + V(F32x4Abs, 0xfd03, s_s) \ + V(F32x4Neg, 0xfd04, s_s) \ + V(F32x4RecipApprox, 0xfd06, s_s) \ + V(F32x4RecipSqrtApprox, 0xfd07, s_s) \ + V(F32x4Add, 0xfd08, s_ss) \ + V(F32x4AddHoriz, 0xfdb9, s_ss) \ + V(F32x4Sub, 0xfd09, s_ss) \ + V(F32x4Mul, 0xfd0a, s_ss) \ + V(F32x4Min, 0xfd0c, s_ss) \ + V(F32x4Max, 0xfd0d, s_ss) \ + V(F32x4Eq, 0xfd10, s_ss) \ + V(F32x4Ne, 0xfd11, s_ss) \ + V(F32x4Lt, 0xfd12, s_ss) \ + V(F32x4Le, 0xfd13, s_ss) \ + V(F32x4Gt, 0xfd14, s_ss) \ + V(F32x4Ge, 0xfd15, s_ss) \ + V(F32x4SConvertI32x4, 0xfd19, s_s) \ + V(F32x4UConvertI32x4, 0xfd1a, s_s) \ + V(I32x4Splat, 0xfd1b, s_i) \ + V(I32x4Neg, 0xfd1e, s_s) \ + V(I32x4Add, 0xfd1f, s_ss) \ + V(I32x4AddHoriz, 0xfdba, s_ss) \ + V(I32x4Sub, 0xfd20, s_ss) \ + V(I32x4Mul, 0xfd21, s_ss) \ + V(I32x4MinS, 0xfd22, s_ss) \ + V(I32x4MaxS, 0xfd23, s_ss) \ + V(I32x4Eq, 0xfd26, s_ss) \ + V(I32x4Ne, 0xfd27, s_ss) \ + V(I32x4LtS, 0xfd28, s_ss) \ + V(I32x4LeS, 0xfd29, s_ss) \ + V(I32x4GtS, 0xfd2a, s_ss) \ + V(I32x4GeS, 0xfd2b, s_ss) \ + V(I32x4SConvertF32x4, 0xfd2f, s_s) \ + V(I32x4UConvertF32x4, 0xfd37, s_s) \ + V(I32x4SConvertI16x8Low, 0xfd94, s_s) \ + V(I32x4SConvertI16x8High, 0xfd95, s_s) \ + V(I32x4UConvertI16x8Low, 0xfd96, s_s) \ + V(I32x4UConvertI16x8High, 0xfd97, s_s) \ + V(I32x4MinU, 0xfd30, s_ss) \ + V(I32x4MaxU, 0xfd31, s_ss) \ + V(I32x4LtU, 0xfd33, s_ss) \ + V(I32x4LeU, 0xfd34, s_ss) \ + V(I32x4GtU, 0xfd35, s_ss) \ + V(I32x4GeU, 0xfd36, s_ss) \ + V(I16x8Splat, 0xfd38, s_i) \ + V(I16x8Neg, 0xfd3b, s_s) \ + V(I16x8Add, 0xfd3c, s_ss) \ + V(I16x8AddSaturateS, 0xfd3d, s_ss) \ + V(I16x8AddHoriz, 0xfdbb, s_ss) \ + V(I16x8Sub, 0xfd3e, s_ss) \ + V(I16x8SubSaturateS, 0xfd3f, s_ss) \ + V(I16x8Mul, 0xfd40, s_ss) \ + V(I16x8MinS, 0xfd41, s_ss) \ + V(I16x8MaxS, 0xfd42, s_ss) \ + V(I16x8Eq, 0xfd45, s_ss) \ + V(I16x8Ne, 0xfd46, s_ss) \ + V(I16x8LtS, 0xfd47, s_ss) \ + V(I16x8LeS, 0xfd48, s_ss) \ + V(I16x8GtS, 0xfd49, s_ss) \ + V(I16x8GeS, 0xfd4a, s_ss) \ + V(I16x8AddSaturateU, 0xfd4e, s_ss) \ + V(I16x8SubSaturateU, 0xfd4f, s_ss) \ + V(I16x8MinU, 0xfd50, s_ss) \ + V(I16x8MaxU, 0xfd51, s_ss) \ + V(I16x8LtU, 0xfd53, s_ss) \ + V(I16x8LeU, 0xfd54, s_ss) \ + V(I16x8GtU, 0xfd55, s_ss) \ + V(I16x8GeU, 0xfd56, s_ss) \ + V(I16x8SConvertI32x4, 0xfd98, s_ss) \ + V(I16x8UConvertI32x4, 0xfd99, s_ss) \ + V(I16x8SConvertI8x16Low, 0xfd9a, s_s) \ + V(I16x8SConvertI8x16High, 0xfd9b, s_s) \ + V(I16x8UConvertI8x16Low, 0xfd9c, s_s) \ + V(I16x8UConvertI8x16High, 0xfd9d, s_s) \ + V(I8x16Splat, 0xfd57, s_i) \ + V(I8x16Neg, 0xfd5a, s_s) \ + V(I8x16Add, 0xfd5b, s_ss) \ + V(I8x16AddSaturateS, 0xfd5c, s_ss) \ + V(I8x16Sub, 0xfd5d, s_ss) \ + V(I8x16SubSaturateS, 0xfd5e, s_ss) \ + V(I8x16Mul, 0xfd5f, s_ss) \ + V(I8x16MinS, 0xfd60, s_ss) \ + V(I8x16MaxS, 0xfd61, s_ss) \ + V(I8x16Eq, 0xfd64, s_ss) \ + V(I8x16Ne, 0xfd65, s_ss) \ + V(I8x16LtS, 0xfd66, s_ss) \ + V(I8x16LeS, 0xfd67, s_ss) \ + V(I8x16GtS, 0xfd68, s_ss) \ + V(I8x16GeS, 0xfd69, s_ss) \ + V(I8x16AddSaturateU, 0xfd6d, s_ss) \ + V(I8x16SubSaturateU, 0xfd6e, s_ss) \ + V(I8x16MinU, 0xfd6f, s_ss) \ + V(I8x16MaxU, 0xfd70, s_ss) \ + V(I8x16LtU, 0xfd72, s_ss) \ + V(I8x16LeU, 0xfd73, s_ss) \ + V(I8x16GtU, 0xfd74, s_ss) \ + V(I8x16GeU, 0xfd75, s_ss) \ + V(I8x16SConvertI16x8, 0xfd9e, s_ss) \ + V(I8x16UConvertI16x8, 0xfd9f, s_ss) \ + V(S128And, 0xfd76, s_ss) \ + V(S128Or, 0xfd77, s_ss) \ + V(S128Xor, 0xfd78, s_ss) \ + V(S128Not, 0xfd79, s_s) \ + V(S128Select, 0xfd2c, s_sss) \ + V(S1x4AnyTrue, 0xfd84, i_s) \ + V(S1x4AllTrue, 0xfd85, i_s) \ + V(S1x8AnyTrue, 0xfd8a, i_s) \ + V(S1x8AllTrue, 0xfd8b, i_s) \ + V(S1x16AnyTrue, 0xfd90, i_s) \ + V(S1x16AllTrue, 0xfd91, i_s) #define FOREACH_SIMD_1_OPERAND_OPCODE(V) \ - V(F32x4ExtractLane, 0xe501, _) \ - V(F32x4ReplaceLane, 0xe502, _) \ - V(I32x4ExtractLane, 0xe51c, _) \ - V(I32x4ReplaceLane, 0xe51d, _) \ - V(I32x4Shl, 0xe524, _) \ - V(I32x4ShrS, 0xe525, _) \ - V(I32x4ShrU, 0xe532, _) \ - V(I16x8ExtractLane, 0xe539, _) \ - V(I16x8ReplaceLane, 0xe53a, _) \ - V(I16x8Shl, 0xe543, _) \ - V(I16x8ShrS, 0xe544, _) \ - V(I16x8ShrU, 0xe552, _) \ - V(I8x16ExtractLane, 0xe558, _) \ - V(I8x16ReplaceLane, 0xe559, _) \ - V(I8x16Shl, 0xe562, _) \ - V(I8x16ShrS, 0xe563, _) \ - V(I8x16ShrU, 0xe571, _) - -#define FOREACH_SIMD_MASK_OPERAND_OPCODE(V) \ - V(S32x4Shuffle, 0xe52d, s_ss) \ - V(S16x8Shuffle, 0xe54c, s_ss) \ - V(S8x16Shuffle, 0xe56b, s_ss) - -#define FOREACH_ATOMIC_OPCODE(V) \ - V(I32AtomicAdd8S, 0xe601, i_ii) \ - V(I32AtomicAdd8U, 0xe602, i_ii) \ - V(I32AtomicAdd16S, 0xe603, i_ii) \ - V(I32AtomicAdd16U, 0xe604, i_ii) \ - V(I32AtomicAdd, 0xe605, i_ii) \ - V(I32AtomicAnd8S, 0xe606, i_ii) \ - V(I32AtomicAnd8U, 0xe607, i_ii) \ - V(I32AtomicAnd16S, 0xe608, i_ii) \ - V(I32AtomicAnd16U, 0xe609, i_ii) \ - V(I32AtomicAnd, 0xe60a, i_ii) \ - V(I32AtomicCompareExchange8S, 0xe60b, i_ii) \ - V(I32AtomicCompareExchange8U, 0xe60c, i_ii) \ - V(I32AtomicCompareExchange16S, 0xe60d, i_ii) \ - V(I32AtomicCompareExchange16U, 0xe60e, i_ii) \ - V(I32AtomicCompareExchange, 0xe60f, i_ii) \ - V(I32AtomicExchange8S, 0xe610, i_ii) \ - V(I32AtomicExchange8U, 0xe611, i_ii) \ - V(I32AtomicExchange16S, 0xe612, i_ii) \ - V(I32AtomicExchange16U, 0xe613, i_ii) \ - V(I32AtomicExchange, 0xe614, i_ii) \ - V(I32AtomicOr8S, 0xe615, i_ii) \ - V(I32AtomicOr8U, 0xe616, i_ii) \ - V(I32AtomicOr16S, 0xe617, i_ii) \ - V(I32AtomicOr16U, 0xe618, i_ii) \ - V(I32AtomicOr, 0xe619, i_ii) \ - V(I32AtomicSub8S, 0xe61a, i_ii) \ - V(I32AtomicSub8U, 0xe61b, i_ii) \ - V(I32AtomicSub16S, 0xe61c, i_ii) \ - V(I32AtomicSub16U, 0xe61d, i_ii) \ - V(I32AtomicSub, 0xe61e, i_ii) \ - V(I32AtomicXor8S, 0xe61f, i_ii) \ - V(I32AtomicXor8U, 0xe620, i_ii) \ - V(I32AtomicXor16S, 0xe621, i_ii) \ - V(I32AtomicXor16U, 0xe622, i_ii) \ - V(I32AtomicXor, 0xe623, i_ii) + V(F32x4ExtractLane, 0xfd01, _) \ + V(F32x4ReplaceLane, 0xfd02, _) \ + V(I32x4ExtractLane, 0xfd1c, _) \ + V(I32x4ReplaceLane, 0xfd1d, _) \ + V(I32x4Shl, 0xfd24, _) \ + V(I32x4ShrS, 0xfd25, _) \ + V(I32x4ShrU, 0xfd32, _) \ + V(I16x8ExtractLane, 0xfd39, _) \ + V(I16x8ReplaceLane, 0xfd3a, _) \ + V(I16x8Shl, 0xfd43, _) \ + V(I16x8ShrS, 0xfd44, _) \ + V(I16x8ShrU, 0xfd52, _) \ + V(I8x16ExtractLane, 0xfd58, _) \ + V(I8x16ReplaceLane, 0xfd59, _) \ + V(I8x16Shl, 0xfd62, _) \ + V(I8x16ShrS, 0xfd63, _) \ + V(I8x16ShrU, 0xfd71, _) + +#define FOREACH_SIMD_MASK_OPERAND_OPCODE(V) V(S8x16Shuffle, 0xfd6b, s_ss) + +#define FOREACH_SIMD_MEM_OPCODE(V) \ + V(S128LoadMem, 0xfd80, s_i) \ + V(S128StoreMem, 0xfd81, s_is) + +#define FOREACH_ATOMIC_OPCODE(V) \ + V(I32AtomicAdd, 0xfe1e, i_ii) \ + V(I32AtomicAdd8U, 0xfe20, i_ii) \ + V(I32AtomicAdd16U, 0xfe21, i_ii) \ + V(I32AtomicSub, 0xfe25, i_ii) \ + V(I32AtomicSub8U, 0xfe27, i_ii) \ + V(I32AtomicSub16U, 0xfe28, i_ii) \ + V(I32AtomicAnd, 0xfe2c, i_ii) \ + V(I32AtomicAnd8U, 0xfe2e, i_ii) \ + V(I32AtomicAnd16U, 0xfe2f, i_ii) \ + V(I32AtomicOr, 0xfe33, i_ii) \ + V(I32AtomicOr8U, 0xfe35, i_ii) \ + V(I32AtomicOr16U, 0xfe36, i_ii) \ + V(I32AtomicXor, 0xfe3a, i_ii) \ + V(I32AtomicXor8U, 0xfe3c, i_ii) \ + V(I32AtomicXor16U, 0xfe3d, i_ii) \ + V(I32AtomicExchange, 0xfe41, i_ii) \ + V(I32AtomicExchange8U, 0xfe43, i_ii) \ + V(I32AtomicExchange16U, 0xfe44, i_ii) \ + V(I32AtomicCompareExchange, 0xfe48, i_ii) \ + V(I32AtomicCompareExchange8U, 0xfe4a, i_ii) \ + V(I32AtomicCompareExchange16U, 0xfe4b, i_ii) // All opcodes. #define FOREACH_OPCODE(V) \ @@ -482,6 +449,7 @@ constexpr WasmCodePosition kNoCodePosition = -1; FOREACH_SIMD_0_OPERAND_OPCODE(V) \ FOREACH_SIMD_1_OPERAND_OPCODE(V) \ FOREACH_SIMD_MASK_OPERAND_OPCODE(V) \ + FOREACH_SIMD_MEM_OPCODE(V) \ FOREACH_ATOMIC_OPCODE(V) // All signatures. @@ -515,32 +483,18 @@ constexpr WasmCodePosition kNoCodePosition = -1; V(f_if, kWasmF32, kWasmI32, kWasmF32) \ V(l_il, kWasmI64, kWasmI32, kWasmI64) -#define FOREACH_SIMD_SIGNATURE(V) \ - V(s_s, kWasmS128, kWasmS128) \ - V(s_f, kWasmS128, kWasmF32) \ - V(s_ss, kWasmS128, kWasmS128, kWasmS128) \ - V(s1x4_ss, kWasmS1x4, kWasmS128, kWasmS128) \ - V(s1x8_ss, kWasmS1x8, kWasmS128, kWasmS128) \ - V(s1x16_ss, kWasmS1x16, kWasmS128, kWasmS128) \ - V(s_i, kWasmS128, kWasmI32) \ - V(s_si, kWasmS128, kWasmS128, kWasmI32) \ - V(i_s, kWasmI32, kWasmS128) \ - V(i_s1x4, kWasmI32, kWasmS1x4) \ - V(i_s1x8, kWasmI32, kWasmS1x8) \ - V(i_s1x16, kWasmI32, kWasmS1x16) \ - V(s_s1x4ss, kWasmS128, kWasmS1x4, kWasmS128, kWasmS128) \ - V(s_s1x8ss, kWasmS128, kWasmS1x8, kWasmS128, kWasmS128) \ - V(s_s1x16ss, kWasmS128, kWasmS1x16, kWasmS128, kWasmS128) \ - V(s1x4_s1x4, kWasmS1x4, kWasmS1x4) \ - V(s1x4_s1x4s1x4, kWasmS1x4, kWasmS1x4, kWasmS1x4) \ - V(s1x8_s1x8, kWasmS1x8, kWasmS1x8) \ - V(s1x8_s1x8s1x8, kWasmS1x8, kWasmS1x8, kWasmS1x8) \ - V(s1x16_s1x16, kWasmS1x16, kWasmS1x16) \ - V(s1x16_s1x16s1x16, kWasmS1x16, kWasmS1x16, kWasmS1x16) +#define FOREACH_SIMD_SIGNATURE(V) \ + V(s_s, kWasmS128, kWasmS128) \ + V(s_f, kWasmS128, kWasmF32) \ + V(s_ss, kWasmS128, kWasmS128, kWasmS128) \ + V(s_i, kWasmS128, kWasmI32) \ + V(s_si, kWasmS128, kWasmS128, kWasmI32) \ + V(i_s, kWasmI32, kWasmS128) \ + V(s_sss, kWasmS128, kWasmS128, kWasmS128, kWasmS128) #define FOREACH_PREFIX(V) \ - V(Simd, 0xe5) \ - V(Atomic, 0xe6) + V(Simd, 0xfd) \ + V(Atomic, 0xfe) enum WasmOpcode { // Declare expression opcodes. @@ -604,17 +558,10 @@ class V8_EXPORT_PRIVATE WasmOpcodes { return kLocalF64; case kWasmS128: return kLocalS128; - case kWasmS1x4: - return kLocalS1x4; - case kWasmS1x8: - return kLocalS1x8; - case kWasmS1x16: - return kLocalS1x16; case kWasmStmt: return kLocalVoid; default: UNREACHABLE(); - return kLocalVoid; } } @@ -630,17 +577,10 @@ class V8_EXPORT_PRIVATE WasmOpcodes { return MachineType::Float64(); case kWasmS128: return MachineType::Simd128(); - case kWasmS1x4: - return MachineType::Simd1x4(); - case kWasmS1x8: - return MachineType::Simd1x8(); - case kWasmS1x16: - return MachineType::Simd1x16(); case kWasmStmt: return MachineType::None(); default: UNREACHABLE(); - return MachineType::None(); } } @@ -658,15 +598,8 @@ class V8_EXPORT_PRIVATE WasmOpcodes { return kWasmF64; case MachineRepresentation::kSimd128: return kWasmS128; - case MachineRepresentation::kSimd1x4: - return kWasmS1x4; - case MachineRepresentation::kSimd1x8: - return kWasmS1x8; - case MachineRepresentation::kSimd1x16: - return kWasmS1x16; default: UNREACHABLE(); - return kWasmI32; } } @@ -681,9 +614,6 @@ class V8_EXPORT_PRIVATE WasmOpcodes { case kWasmF64: return 'd'; case kWasmS128: - case kWasmS1x4: - case kWasmS1x8: - case kWasmS1x16: return 's'; case kWasmStmt: return 'v'; @@ -706,12 +636,6 @@ class V8_EXPORT_PRIVATE WasmOpcodes { return "f64"; case kWasmS128: return "s128"; - case kWasmS1x4: - return "s1x4"; - case kWasmS1x8: - return "s1x8"; - case kWasmS1x16: - return "s1x16"; case kWasmStmt: return "<stmt>"; case kWasmVar: diff --git a/deps/v8/src/wasm/wasm-result.cc b/deps/v8/src/wasm/wasm-result.cc index b83d9dbbaa..6deccae6dc 100644 --- a/deps/v8/src/wasm/wasm-result.cc +++ b/deps/v8/src/wasm/wasm-result.cc @@ -133,15 +133,11 @@ Handle<Object> ErrorThrower::Reify() { constructor = isolate_->wasm_runtime_error_function(); break; } - Vector<const uint8_t> msg_vec( - reinterpret_cast<const uint8_t*>(error_msg_.data()), - static_cast<int>(error_msg_.size())); + Vector<const char> msg_vec(error_msg_.data(), error_msg_.size()); Handle<String> message = - isolate_->factory()->NewStringFromOneByte(msg_vec).ToHandleChecked(); - error_type_ = kNone; // Reset. - Handle<Object> exception = - isolate_->factory()->NewError(constructor, message); - return exception; + isolate_->factory()->NewStringFromUtf8(msg_vec).ToHandleChecked(); + Reset(); + return isolate_->factory()->NewError(constructor, message); } void ErrorThrower::Reset() { @@ -159,7 +155,10 @@ ErrorThrower::ErrorThrower(ErrorThrower&& other) ErrorThrower::~ErrorThrower() { if (error() && !isolate_->has_pending_exception()) { - isolate_->ScheduleThrow(*Reify()); + // We don't want to mix pending exceptions and scheduled exceptions, hence + // an existing exception should be pending, never scheduled. + DCHECK(!isolate_->has_scheduled_exception()); + isolate_->Throw(*Reify()); } } diff --git a/deps/v8/src/wasm/wasm-result.h b/deps/v8/src/wasm/wasm-result.h index 848170c80e..9ae8c33f2f 100644 --- a/deps/v8/src/wasm/wasm-result.h +++ b/deps/v8/src/wasm/wasm-result.h @@ -123,6 +123,8 @@ class V8_EXPORT_PRIVATE ErrorThrower { bool error() const { return error_type_ != kNone; } bool wasm_error() { return error_type_ >= kFirstWasmError; } + Isolate* isolate() const { return isolate_; } + private: enum ErrorType { kNone, @@ -146,6 +148,9 @@ class V8_EXPORT_PRIVATE ErrorThrower { std::string error_msg_; DISALLOW_COPY_AND_ASSIGN(ErrorThrower); + // ErrorThrower should always be stack-allocated, since it constitutes a scope + // (things happen in the destructor). + DISALLOW_NEW_AND_DELETE(); }; } // namespace wasm diff --git a/deps/v8/src/wasm/wasm-text.cc b/deps/v8/src/wasm/wasm-text.cc index 540bfb5ec0..20438b8ae6 100644 --- a/deps/v8/src/wasm/wasm-text.cc +++ b/deps/v8/src/wasm/wasm-text.cc @@ -195,6 +195,7 @@ void wasm::PrintWasmText(const WasmModule *module, FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE) FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE) FOREACH_SIMD_MASK_OPERAND_OPCODE(CASE_OPCODE) + FOREACH_SIMD_MEM_OPCODE(CASE_OPCODE) FOREACH_ATOMIC_OPCODE(CASE_OPCODE) os << WasmOpcodes::OpcodeName(opcode); break; diff --git a/deps/v8/src/wasm/wasm-value.h b/deps/v8/src/wasm/wasm-value.h new file mode 100644 index 0000000000..8e86c4824a --- /dev/null +++ b/deps/v8/src/wasm/wasm-value.h @@ -0,0 +1,86 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_WASM_VALUE_H_ +#define V8_WASM_VALUE_H_ + +#include "src/wasm/wasm-opcodes.h" +#include "src/zone/zone-containers.h" + +namespace v8 { +namespace internal { +namespace wasm { + +// Macro for defining WasmValue union members. +#define FOREACH_WASMVAL_UNION_MEMBER(V) \ + V(i32, kWasmI32, int32_t) \ + V(u32, kWasmI32, uint32_t) \ + V(i64, kWasmI64, int64_t) \ + V(u64, kWasmI64, uint64_t) \ + V(f32, kWasmF32, float) \ + V(f64, kWasmF64, double) + +// A wasm value with type information. +class WasmValue { + public: + WasmValue() : type_(kWasmStmt) {} + +#define DEFINE_TYPE_SPECIFIC_METHODS(field, localtype, ctype) \ + explicit WasmValue(ctype v) : type_(localtype) { value_.field = v; } \ + ctype to_##field() const { \ + DCHECK_EQ(localtype, type_); \ + return value_.field; \ + } + FOREACH_WASMVAL_UNION_MEMBER(DEFINE_TYPE_SPECIFIC_METHODS) +#undef DEFINE_TYPE_SPECIFIC_METHODS + + ValueType type() const { return type_; } + + bool operator==(const WasmValue& other) const { + if (type_ != other.type_) return false; +#define CHECK_VALUE_EQ(field, localtype, ctype) \ + if (type_ == localtype) { \ + return value_.field == other.value_.field; \ + } + FOREACH_WASMVAL_UNION_MEMBER(CHECK_VALUE_EQ) +#undef CHECK_VALUE_EQ + UNREACHABLE(); + } + + template <typename T> + inline T to() const { + static_assert(sizeof(T) == -1, "Do only use this method with valid types"); + } + + template <typename T> + inline T to_unchecked() const { + static_assert(sizeof(T) == -1, "Do only use this method with valid types"); + } + + private: + ValueType type_; + union { +#define DECLARE_FIELD(field, localtype, ctype) ctype field; + FOREACH_WASMVAL_UNION_MEMBER(DECLARE_FIELD) +#undef DECLARE_FIELD + } value_; +}; + +#define DECLARE_CAST(field, localtype, ctype) \ + template <> \ + inline ctype WasmValue::to_unchecked() const { \ + return value_.field; \ + } \ + template <> \ + inline ctype WasmValue::to() const { \ + return to_##field(); \ + } +FOREACH_WASMVAL_UNION_MEMBER(DECLARE_CAST) +#undef DECLARE_CAST + +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_WASM_VALUE_H_ |