summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm
diff options
context:
space:
mode:
authorMichaƫl Zasso <targos@protonmail.com>2017-09-12 11:34:59 +0200
committerAnna Henningsen <anna@addaleax.net>2017-09-13 16:15:18 +0200
commitd82e1075dbc2cec2d6598ade10c1f43805f690fd (patch)
treeccd242b9b491dfc341d1099fe11b0ef528839877 /deps/v8/src/wasm
parentb4b7ac6ae811b2b5a3082468115dfb5a5246fe3f (diff)
downloadandroid-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')
-rw-r--r--deps/v8/src/wasm/OWNERS2
-rw-r--r--deps/v8/src/wasm/compilation-manager.cc32
-rw-r--r--deps/v8/src/wasm/compilation-manager.h44
-rw-r--r--deps/v8/src/wasm/decoder.h11
-rw-r--r--deps/v8/src/wasm/function-body-decoder-impl.h23
-rw-r--r--deps/v8/src/wasm/function-body-decoder.cc224
-rw-r--r--deps/v8/src/wasm/function-body-decoder.h26
-rw-r--r--deps/v8/src/wasm/module-compiler.cc2356
-rw-r--r--deps/v8/src/wasm/module-compiler.h390
-rw-r--r--deps/v8/src/wasm/module-decoder.cc462
-rw-r--r--deps/v8/src/wasm/module-decoder.h90
-rw-r--r--deps/v8/src/wasm/signature-map.cc4
-rw-r--r--deps/v8/src/wasm/signature-map.h9
-rw-r--r--deps/v8/src/wasm/streaming-decoder.cc53
-rw-r--r--deps/v8/src/wasm/wasm-code-specialization.cc4
-rw-r--r--deps/v8/src/wasm/wasm-debug.cc143
-rw-r--r--deps/v8/src/wasm/wasm-interpreter.cc349
-rw-r--r--deps/v8/src/wasm/wasm-interpreter.h96
-rw-r--r--deps/v8/src/wasm/wasm-js.cc424
-rw-r--r--deps/v8/src/wasm/wasm-js.h3
-rw-r--r--deps/v8/src/wasm/wasm-limits.h6
-rw-r--r--deps/v8/src/wasm/wasm-module.cc2716
-rw-r--r--deps/v8/src/wasm/wasm-module.h231
-rw-r--r--deps/v8/src/wasm/wasm-objects.cc574
-rw-r--r--deps/v8/src/wasm/wasm-objects.h408
-rw-r--r--deps/v8/src/wasm/wasm-opcodes.cc196
-rw-r--r--deps/v8/src/wasm/wasm-opcodes.h450
-rw-r--r--deps/v8/src/wasm/wasm-result.cc17
-rw-r--r--deps/v8/src/wasm/wasm-result.h5
-rw-r--r--deps/v8/src/wasm/wasm-text.cc1
-rw-r--r--deps/v8/src/wasm/wasm-value.h86
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_