summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2016-12-23 16:30:57 +0100
committerMichaël Zasso <targos@protonmail.com>2017-01-26 22:46:17 +0100
commit2739185b790e040c3b044c577327f5d44bffad4a (patch)
tree29a466999212f4c85958379d9d400eec8a185ba5 /deps/v8/src/wasm
parenta67a04d7654faaa04c8da00e42981ebc9fd0911c (diff)
downloadandroid-node-v8-2739185b790e040c3b044c577327f5d44bffad4a.tar.gz
android-node-v8-2739185b790e040c3b044c577327f5d44bffad4a.tar.bz2
android-node-v8-2739185b790e040c3b044c577327f5d44bffad4a.zip
deps: update V8 to 5.5.372.40
PR-URL: https://github.com/nodejs/node/pull/9618 Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'deps/v8/src/wasm')
-rw-r--r--deps/v8/src/wasm/ast-decoder.cc1047
-rw-r--r--deps/v8/src/wasm/ast-decoder.h202
-rw-r--r--deps/v8/src/wasm/decoder.h15
-rw-r--r--deps/v8/src/wasm/module-decoder.cc1079
-rw-r--r--deps/v8/src/wasm/module-decoder.h15
-rw-r--r--deps/v8/src/wasm/switch-logic.h4
-rw-r--r--deps/v8/src/wasm/wasm-debug.cc10
-rw-r--r--deps/v8/src/wasm/wasm-external-refs.cc3
-rw-r--r--deps/v8/src/wasm/wasm-interpreter.cc389
-rw-r--r--deps/v8/src/wasm/wasm-interpreter.h24
-rw-r--r--deps/v8/src/wasm/wasm-js.cc472
-rw-r--r--deps/v8/src/wasm/wasm-js.h19
-rw-r--r--deps/v8/src/wasm/wasm-macro-gen.h170
-rw-r--r--deps/v8/src/wasm/wasm-module-builder.cc (renamed from deps/v8/src/wasm/encoder.cc)239
-rw-r--r--deps/v8/src/wasm/wasm-module-builder.h (renamed from deps/v8/src/wasm/encoder.h)128
-rw-r--r--deps/v8/src/wasm/wasm-module.cc2028
-rw-r--r--deps/v8/src/wasm/wasm-module.h385
-rw-r--r--deps/v8/src/wasm/wasm-opcodes.cc29
-rw-r--r--deps/v8/src/wasm/wasm-opcodes.h292
-rw-r--r--deps/v8/src/wasm/wasm-result.cc39
-rw-r--r--deps/v8/src/wasm/wasm-result.h36
21 files changed, 4018 insertions, 2607 deletions
diff --git a/deps/v8/src/wasm/ast-decoder.cc b/deps/v8/src/wasm/ast-decoder.cc
index 0f192508ba..02d1db5bda 100644
--- a/deps/v8/src/wasm/ast-decoder.cc
+++ b/deps/v8/src/wasm/ast-decoder.cc
@@ -7,7 +7,7 @@
#include "src/bit-vector.h"
#include "src/flags.h"
#include "src/handles.h"
-#include "src/zone-containers.h"
+#include "src/zone/zone-containers.h"
#include "src/wasm/ast-decoder.h"
#include "src/wasm/decoder.h"
@@ -36,6 +36,8 @@ namespace wasm {
error("Invalid opcode (enable with --" #flag ")"); \
break; \
}
+// TODO(titzer): this is only for intermediate migration.
+#define IMPLICIT_FUNCTION_END 1
// An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph.
@@ -68,62 +70,82 @@ struct Value {
LocalType type;
};
-// An entry on the control stack (i.e. if, block, loop).
-struct Control {
- const byte* pc;
- int stack_depth; // stack height at the beginning of the construct.
- SsaEnv* end_env; // end environment for the construct.
- SsaEnv* false_env; // false environment (only for if).
- SsaEnv* catch_env; // catch environment (only for try with catch).
- SsaEnv* finish_try_env; // the environment where a try with finally lives.
- TFNode* node; // result node for the construct.
- LocalType type; // result type for the construct.
- bool is_loop; // true if this is the inner label of a loop.
+struct TryInfo : public ZoneObject {
+ SsaEnv* catch_env;
+ TFNode* exception;
- bool is_if() const { return *pc == kExprIf; }
+ explicit TryInfo(SsaEnv* c) : catch_env(c), exception(nullptr) {}
+};
- bool is_try() const {
- return *pc == kExprTryCatch || *pc == kExprTryCatchFinally ||
- *pc == kExprTryFinally;
- }
+struct MergeValues {
+ uint32_t arity;
+ union {
+ Value* array;
+ Value first;
+ } vals; // Either multiple values or a single value.
- bool has_catch() const {
- return *pc == kExprTryCatch || *pc == kExprTryCatchFinally;
+ Value& first() {
+ DCHECK_GT(arity, 0u);
+ return arity == 1 ? vals.first : vals.array[0];
}
+};
- bool has_finally() const {
- return *pc == kExprTryCatchFinally || *pc == kExprTryFinally;
- }
+static Value* NO_VALUE = nullptr;
+
+enum ControlKind { kControlIf, kControlBlock, kControlLoop, kControlTry };
+
+// An entry on the control stack (i.e. if, block, loop).
+struct Control {
+ const byte* pc;
+ ControlKind kind;
+ int stack_depth; // stack height at the beginning of the construct.
+ SsaEnv* end_env; // end environment for the construct.
+ SsaEnv* false_env; // false environment (only for if).
+ TryInfo* try_info; // Information used for compiling try statements.
+ int32_t previous_catch; // The previous Control (on the stack) with a catch.
+
+ // Values merged into the end of this control construct.
+ MergeValues merge;
+
+ inline bool is_if() const { return kind == kControlIf; }
+ inline bool is_block() const { return kind == kControlBlock; }
+ inline bool is_loop() const { return kind == kControlLoop; }
+ inline bool is_try() const { return kind == kControlTry; }
// Named constructors.
- static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env) {
- return {pc, stack_depth, end_env, nullptr, nullptr,
- nullptr, nullptr, kAstEnd, false};
+ static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env,
+ int32_t previous_catch) {
+ return {pc, kControlBlock, stack_depth, end_env,
+ nullptr, nullptr, previous_catch, {0, {NO_VALUE}}};
}
static Control If(const byte* pc, int stack_depth, SsaEnv* end_env,
- SsaEnv* false_env) {
- return {pc, stack_depth, end_env, false_env, nullptr,
- nullptr, nullptr, kAstStmt, false};
+ SsaEnv* false_env, int32_t previous_catch) {
+ return {pc, kControlIf, stack_depth, end_env,
+ false_env, nullptr, previous_catch, {0, {NO_VALUE}}};
}
- static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env) {
- return {pc, stack_depth, end_env, nullptr, nullptr,
- nullptr, nullptr, kAstEnd, true};
+ static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env,
+ int32_t previous_catch) {
+ return {pc, kControlLoop, stack_depth, end_env,
+ nullptr, nullptr, previous_catch, {0, {NO_VALUE}}};
}
static Control Try(const byte* pc, int stack_depth, SsaEnv* end_env,
- SsaEnv* catch_env, SsaEnv* finish_try_env) {
- return {pc, stack_depth, end_env, nullptr, catch_env, finish_try_env,
- nullptr, kAstEnd, false};
+ Zone* zone, SsaEnv* catch_env, int32_t previous_catch) {
+ DCHECK_NOT_NULL(catch_env);
+ TryInfo* try_info = new (zone) TryInfo(catch_env);
+ return {pc, kControlTry, stack_depth, end_env,
+ nullptr, try_info, previous_catch, {0, {NO_VALUE}}};
}
};
// 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.
-#define BUILD(func, ...) (build() ? builder_->func(__VA_ARGS__) : nullptr)
-#define BUILD0(func) (build() ? builder_->func() : nullptr)
+#define BUILD(func, ...) \
+ (build() ? CheckForException(builder_->func(__VA_ARGS__)) : nullptr)
+#define BUILD0(func) (build() ? CheckForException(builder_->func()) : nullptr)
// Generic Wasm bytecode decoder with utilities for decoding operands,
// lengths, etc.
@@ -150,17 +172,18 @@ class WasmDecoder : public Decoder {
}
return true;
}
- error(pc, pc + 1, "invalid local index");
+ error(pc, pc + 1, "invalid local index: %u", operand.index);
return false;
}
inline bool Validate(const byte* pc, GlobalIndexOperand& operand) {
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->globals.size()) {
- operand.type = m->module->globals[operand.index].type;
+ operand.global = &m->module->globals[operand.index];
+ operand.type = operand.global->type;
return true;
}
- error(pc, pc + 1, "invalid global index");
+ error(pc, pc + 1, "invalid global index: %u", operand.index);
return false;
}
@@ -175,16 +198,9 @@ class WasmDecoder : public Decoder {
inline bool Validate(const byte* pc, CallFunctionOperand& operand) {
if (Complete(pc, operand)) {
- uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
- if (operand.arity != expected) {
- error(pc, pc + 1,
- "arity mismatch in direct function call (expected %u, got %u)",
- expected, operand.arity);
- return false;
- }
return true;
}
- error(pc, pc + 1, "invalid function index");
+ error(pc, pc + 1, "invalid function index: %u", operand.index);
return false;
}
@@ -199,161 +215,28 @@ class WasmDecoder : public Decoder {
inline bool Validate(const byte* pc, CallIndirectOperand& operand) {
if (Complete(pc, operand)) {
- uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
- if (operand.arity != expected) {
- error(pc, pc + 1,
- "arity mismatch in indirect function call (expected %u, got %u)",
- expected, operand.arity);
- return false;
- }
- return true;
- }
- error(pc, pc + 1, "invalid signature index");
- return false;
- }
-
- inline bool Complete(const byte* pc, CallImportOperand& operand) {
- ModuleEnv* m = module_;
- if (m && m->module && operand.index < m->module->import_table.size()) {
- operand.sig = m->module->import_table[operand.index].sig;
- return true;
- }
- return false;
- }
-
- inline bool Validate(const byte* pc, CallImportOperand& operand) {
- if (Complete(pc, operand)) {
- uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
- if (operand.arity != expected) {
- error(pc, pc + 1, "arity mismatch in import call (expected %u, got %u)",
- expected, operand.arity);
- return false;
- }
return true;
}
- error(pc, pc + 1, "invalid signature index");
+ error(pc, pc + 1, "invalid signature index: #%u", operand.index);
return false;
}
inline bool Validate(const byte* pc, BreakDepthOperand& operand,
ZoneVector<Control>& control) {
- if (operand.arity > 1) {
- error(pc, pc + 1, "invalid arity for br or br_if");
- return false;
- }
if (operand.depth < control.size()) {
operand.target = &control[control.size() - operand.depth - 1];
return true;
}
- error(pc, pc + 1, "invalid break depth");
+ error(pc, pc + 1, "invalid break depth: %u", operand.depth);
return false;
}
bool Validate(const byte* pc, BranchTableOperand& operand,
size_t block_depth) {
- if (operand.arity > 1) {
- error(pc, pc + 1, "invalid arity for break");
- return false;
- }
- // Verify table.
- for (uint32_t i = 0; i < operand.table_count + 1; ++i) {
- uint32_t target = operand.read_entry(this, i);
- if (target >= block_depth) {
- error(operand.table + i * 2, "improper branch in br_table");
- return false;
- }
- }
+ // TODO(titzer): add extra redundant validation for br_table here?
return true;
}
- unsigned OpcodeArity(const byte* pc) {
-#define DECLARE_ARITY(name, ...) \
- static const LocalType kTypes_##name[] = {__VA_ARGS__}; \
- static const int kArity_##name = \
- static_cast<int>(arraysize(kTypes_##name) - 1);
-
- FOREACH_SIGNATURE(DECLARE_ARITY);
-#undef DECLARE_ARITY
-
- switch (static_cast<WasmOpcode>(*pc)) {
- case kExprI8Const:
- case kExprI32Const:
- case kExprI64Const:
- case kExprF64Const:
- case kExprF32Const:
- case kExprGetLocal:
- case kExprGetGlobal:
- case kExprNop:
- case kExprUnreachable:
- case kExprEnd:
- case kExprBlock:
- case kExprThrow:
- case kExprTryCatch:
- case kExprTryCatchFinally:
- case kExprTryFinally:
- case kExprFinally:
- case kExprLoop:
- return 0;
-
- case kExprSetGlobal:
- case kExprSetLocal:
- case kExprElse:
- case kExprCatch:
- return 1;
-
- case kExprBr: {
- BreakDepthOperand operand(this, pc);
- return operand.arity;
- }
- case kExprBrIf: {
- BreakDepthOperand operand(this, pc);
- return 1 + operand.arity;
- }
- case kExprBrTable: {
- BranchTableOperand operand(this, pc);
- return 1 + operand.arity;
- }
-
- case kExprIf:
- return 1;
- case kExprSelect:
- return 3;
-
- case kExprCallFunction: {
- CallFunctionOperand operand(this, pc);
- return operand.arity;
- }
- case kExprCallIndirect: {
- CallIndirectOperand operand(this, pc);
- return 1 + operand.arity;
- }
- case kExprCallImport: {
- CallImportOperand operand(this, pc);
- return operand.arity;
- }
- case kExprReturn: {
- ReturnArityOperand operand(this, pc);
- return operand.arity;
- }
-
-#define DECLARE_OPCODE_CASE(name, opcode, sig) \
- case kExpr##name: \
- return kArity_##sig;
-
- FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
- FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
- FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE)
- FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE)
- FOREACH_SIMPLE_MEM_OPCODE(DECLARE_OPCODE_CASE)
- FOREACH_ASMJS_COMPAT_OPCODE(DECLARE_OPCODE_CASE)
- FOREACH_SIMD_OPCODE(DECLARE_OPCODE_CASE)
-#undef DECLARE_OPCODE_CASE
- default:
- UNREACHABLE();
- return 0;
- }
- }
-
unsigned OpcodeLength(const byte* pc) {
switch (static_cast<WasmOpcode>(*pc)) {
#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
@@ -361,7 +244,7 @@ class WasmDecoder : public Decoder {
FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
#undef DECLARE_OPCODE_CASE
{
- MemoryAccessOperand operand(this, pc);
+ MemoryAccessOperand operand(this, pc, UINT32_MAX);
return 1 + operand.length;
}
case kExprBr:
@@ -383,12 +266,17 @@ class WasmDecoder : public Decoder {
CallIndirectOperand operand(this, pc);
return 1 + operand.length;
}
- case kExprCallImport: {
- CallImportOperand operand(this, pc);
+
+ case kExprTry:
+ case kExprIf: // fall thru
+ case kExprLoop:
+ case kExprBlock: {
+ BlockTypeOperand operand(this, pc);
return 1 + operand.length;
}
case kExprSetLocal:
+ case kExprTeeLocal:
case kExprGetLocal:
case kExprCatch: {
LocalIndexOperand operand(this, pc);
@@ -396,7 +284,8 @@ class WasmDecoder : public Decoder {
}
case kExprBrTable: {
BranchTableOperand operand(this, pc);
- return 1 + operand.length;
+ BranchTableIterator iterator(this, operand);
+ return 1 + iterator.length();
}
case kExprI32Const: {
ImmI32Operand operand(this, pc);
@@ -412,17 +301,14 @@ class WasmDecoder : public Decoder {
return 5;
case kExprF64Const:
return 9;
- case kExprReturn: {
- ReturnArityOperand operand(this, pc);
- return 1 + operand.length;
- }
-
default:
return 1;
}
}
};
+static const int32_t kNullCatch = -1;
+
// The full WASM decoder for bytecode. Both verifies bytecode and generates
// a TurboFan IR graph.
class WasmFullDecoder : public WasmDecoder {
@@ -434,7 +320,9 @@ class WasmFullDecoder : public WasmDecoder {
base_(body.base),
local_type_vec_(zone),
stack_(zone),
- control_(zone) {
+ control_(zone),
+ last_end_found_(false),
+ current_catch_(kNullCatch) {
local_types_ = &local_type_vec_;
}
@@ -447,7 +335,7 @@ class WasmFullDecoder : public WasmDecoder {
control_.clear();
if (end_ < pc_) {
- error(pc_, "function body end < start");
+ error("function body end < start");
return false;
}
@@ -457,23 +345,55 @@ class WasmFullDecoder : public WasmDecoder {
if (failed()) return TraceFailed();
+#if IMPLICIT_FUNCTION_END
+ // With implicit end support (old style), the function block
+ // remains on the stack. Other control blocks are an error.
+ if (control_.size() > 1) {
+ error(pc_, control_.back().pc, "unterminated control structure");
+ return TraceFailed();
+ }
+
+ // Assume an implicit end to the function body block.
+ if (control_.size() == 1) {
+ Control* c = &control_.back();
+ if (ssa_env_->go()) {
+ FallThruTo(c);
+ }
+
+ if (c->end_env->go()) {
+ // Push the end values onto the stack.
+ stack_.resize(c->stack_depth);
+ if (c->merge.arity == 1) {
+ stack_.push_back(c->merge.vals.first);
+ } else {
+ for (unsigned i = 0; i < c->merge.arity; i++) {
+ stack_.push_back(c->merge.vals.array[i]);
+ }
+ }
+
+ TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn");
+ SetEnv("function:end", c->end_env);
+ DoReturn();
+ TRACE("\n");
+ }
+ }
+#else
if (!control_.empty()) {
error(pc_, control_.back().pc, "unterminated control structure");
return TraceFailed();
}
- if (ssa_env_->go()) {
- TRACE(" @%-6d #xx:%-20s|", startrel(pc_), "ImplicitReturn");
- DoReturn();
- if (failed()) return TraceFailed();
- TRACE("\n");
+ if (!last_end_found_) {
+ error("function body must end with \"end\" opcode.");
+ return false;
}
+#endif
if (FLAG_trace_wasm_decode_time) {
double ms = decode_timer.Elapsed().InMillisecondsF();
- PrintF("wasm-decode ok (%0.3f ms)\n\n", ms);
+ PrintF("wasm-decode %s (%0.3f ms)\n\n", ok() ? "ok" : "failed", ms);
} else {
- TRACE("wasm-decode ok\n\n");
+ TRACE("wasm-decode %s\n\n", ok() ? "ok" : "failed");
}
return true;
@@ -526,6 +446,11 @@ class WasmFullDecoder : public WasmDecoder {
ZoneVector<LocalType> local_type_vec_; // types of local variables.
ZoneVector<Value> stack_; // stack of values.
ZoneVector<Control> control_; // stack of blocks, loops, and ifs.
+ bool last_end_found_;
+
+ int32_t current_catch_;
+
+ TryInfo* current_try_info() { return control_[current_catch_].try_info; }
inline bool build() { return builder_ && ssa_env_->go(); }
@@ -574,6 +499,8 @@ class WasmFullDecoder : public WasmDecoder {
return builder_->Float32Constant(0);
case kAstF64:
return builder_->Float64Constant(0);
+ case kAstS128:
+ return builder_->DefaultS128Value();
default:
UNREACHABLE();
return nullptr;
@@ -603,8 +530,13 @@ class WasmFullDecoder : public WasmDecoder {
}
// Decode local declarations, if any.
uint32_t entries = consume_u32v("local decls count");
+ TRACE("local decls count: %u\n", entries);
while (entries-- > 0 && pc_ < limit_) {
uint32_t count = consume_u32v("local count");
+ if (count > kMaxNumWasmLocals) {
+ error(pc_ - 1, "local count too large");
+ return;
+ }
byte code = consume_u8("local type");
LocalType type;
switch (code) {
@@ -620,6 +552,9 @@ class WasmFullDecoder : public WasmDecoder {
case kLocalF64:
type = kAstF64;
break;
+ case kLocalS128:
+ type = kAstS128;
+ break;
default:
error(pc_ - 1, "invalid local type");
return;
@@ -636,82 +571,68 @@ class WasmFullDecoder : public WasmDecoder {
reinterpret_cast<const void*>(limit_), baserel(pc_),
static_cast<int>(limit_ - start_), builder_ ? "graph building" : "");
+ {
+ // Set up initial function block.
+ SsaEnv* break_env = ssa_env_;
+ SetEnv("initial env", Steal(break_env));
+ PushBlock(break_env);
+ Control* c = &control_.back();
+ c->merge.arity = static_cast<uint32_t>(sig_->return_count());
+
+ if (c->merge.arity == 1) {
+ c->merge.vals.first = {pc_, nullptr, sig_->GetReturn(0)};
+ } else if (c->merge.arity > 1) {
+ c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity);
+ for (unsigned i = 0; i < c->merge.arity; i++) {
+ c->merge.vals.array[i] = {pc_, nullptr, sig_->GetReturn(i)};
+ }
+ }
+ }
+
if (pc_ >= limit_) return; // Nothing to do.
while (true) { // decoding loop.
unsigned len = 1;
WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
- TRACE(" @%-6d #%02x:%-20s|", startrel(pc_), opcode,
- WasmOpcodes::ShortOpcodeName(opcode));
+ if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
+ TRACE(" @%-8d #%02x:%-20s|", startrel(pc_), opcode,
+ WasmOpcodes::ShortOpcodeName(opcode));
+ }
FunctionSig* sig = WasmOpcodes::Signature(opcode);
if (sig) {
- // Fast case of a simple operator.
- TFNode* node;
- switch (sig->parameter_count()) {
- case 1: {
- Value val = Pop(0, sig->GetParam(0));
- node = BUILD(Unop, opcode, val.node, position());
- break;
- }
- case 2: {
- Value rval = Pop(1, sig->GetParam(1));
- Value lval = Pop(0, sig->GetParam(0));
- node = BUILD(Binop, opcode, lval.node, rval.node, position());
- break;
- }
- default:
- UNREACHABLE();
- node = nullptr;
- break;
- }
- Push(GetReturnType(sig), node);
+ BuildSimpleOperator(opcode, sig);
} else {
// Complex bytecode.
switch (opcode) {
case kExprNop:
- Push(kAstStmt, nullptr);
break;
case kExprBlock: {
// The break environment is the outer environment.
+ BlockTypeOperand operand(this, pc_);
SsaEnv* break_env = ssa_env_;
PushBlock(break_env);
SetEnv("block:start", Steal(break_env));
+ SetBlockType(&control_.back(), operand);
+ len = 1 + operand.length;
break;
}
case kExprThrow: {
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
- Pop(0, kAstI32);
-
- // TODO(jpp): start exception propagation.
+ Value value = Pop(0, kAstI32);
+ BUILD(Throw, value.node);
break;
}
- case kExprTryCatch: {
+ case kExprTry: {
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
+ BlockTypeOperand operand(this, pc_);
SsaEnv* outer_env = ssa_env_;
SsaEnv* try_env = Steal(outer_env);
- SsaEnv* catch_env = Split(try_env);
- PushTry(outer_env, catch_env, nullptr);
+ SsaEnv* catch_env = UnreachableEnv();
+ PushTry(outer_env, catch_env);
SetEnv("try_catch:start", try_env);
- break;
- }
- case kExprTryCatchFinally: {
- CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
- SsaEnv* outer_env = ssa_env_;
- SsaEnv* try_env = Steal(outer_env);
- SsaEnv* catch_env = Split(try_env);
- SsaEnv* finally_env = Split(try_env);
- PushTry(finally_env, catch_env, outer_env);
- SetEnv("try_catch_finally:start", try_env);
- break;
- }
- case kExprTryFinally: {
- CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
- SsaEnv* outer_env = ssa_env_;
- SsaEnv* try_env = Steal(outer_env);
- SsaEnv* finally_env = Split(outer_env);
- PushTry(finally_env, nullptr, outer_env);
- SetEnv("try_finally:start", try_env);
+ SetBlockType(&control_.back(), operand);
+ len = 1 + operand.length;
break;
}
case kExprCatch: {
@@ -720,97 +641,57 @@ class WasmFullDecoder : public WasmDecoder {
len = 1 + operand.length;
if (control_.empty()) {
- error(pc_, "catch does not match a any try");
+ error("catch does not match any try");
break;
}
Control* c = &control_.back();
- if (!c->has_catch()) {
- error(pc_, "catch does not match a try with catch");
+ if (!c->is_try()) {
+ error("catch does not match any try");
break;
}
- if (c->catch_env == nullptr) {
+ if (c->try_info->catch_env == nullptr) {
error(pc_, "catch already present for try with catch");
break;
}
- Goto(ssa_env_, c->end_env);
+ if (ssa_env_->go()) {
+ MergeValuesInto(c);
+ }
+ stack_.resize(c->stack_depth);
- SsaEnv* catch_env = c->catch_env;
- c->catch_env = nullptr;
+ DCHECK_NOT_NULL(c->try_info);
+ SsaEnv* catch_env = c->try_info->catch_env;
+ c->try_info->catch_env = nullptr;
SetEnv("catch:begin", catch_env);
+ current_catch_ = c->previous_catch;
if (Validate(pc_, operand)) {
- // TODO(jpp): figure out how thrown value is propagated. It is
- // unlikely to be a value on the stack.
if (ssa_env_->locals) {
- ssa_env_->locals[operand.index] = nullptr;
+ TFNode* exception_as_i32 =
+ BUILD(Catch, c->try_info->exception, position());
+ ssa_env_->locals[operand.index] = exception_as_i32;
}
}
- PopUpTo(c->stack_depth);
-
- break;
- }
- case kExprFinally: {
- CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
- if (control_.empty()) {
- error(pc_, "finally does not match a any try");
- break;
- }
-
- Control* c = &control_.back();
- if (c->has_catch() && c->catch_env != nullptr) {
- error(pc_, "missing catch for try with catch and finally");
- break;
- }
-
- if (!c->has_finally()) {
- error(pc_, "finally does not match a try with finally");
- break;
- }
-
- if (c->finish_try_env == nullptr) {
- error(pc_, "finally already present for try with finally");
- break;
- }
-
- // ssa_env_ is either the env for either the try or the catch, but
- // it does not matter: either way we need to direct the control flow
- // to the end_env, which is the env for the finally.
- // c->finish_try_env is the the environment enclosing the try block.
- Goto(ssa_env_, c->end_env);
-
- PopUpTo(c->stack_depth);
-
- // The current environment becomes end_env, and finish_try_env
- // becomes the new end_env. This ensures that any control flow
- // leaving a try block up to now will do so by branching to the
- // finally block. Setting the end_env to be finish_try_env ensures
- // that kExprEnd below can handle the try block as it would any
- // other block construct.
- SsaEnv* finally_env = c->end_env;
- c->end_env = c->finish_try_env;
- SetEnv("finally:begin", finally_env);
- c->finish_try_env = nullptr;
-
break;
}
case kExprLoop: {
- // The break environment is the outer environment.
- SsaEnv* break_env = ssa_env_;
- PushBlock(break_env);
- SsaEnv* finish_try_env = Steal(break_env);
+ BlockTypeOperand operand(this, pc_);
+ SsaEnv* finish_try_env = Steal(ssa_env_);
// The continue environment is the inner environment.
PrepareForLoop(pc_, finish_try_env);
SetEnv("loop:start", Split(finish_try_env));
ssa_env_->SetNotMerged();
PushLoop(finish_try_env);
+ SetBlockType(&control_.back(), operand);
+ len = 1 + operand.length;
break;
}
case kExprIf: {
// Condition on top of stack. Split environments for branches.
+ BlockTypeOperand operand(this, pc_);
Value cond = Pop(0, kAstI32);
TFNode* if_true = nullptr;
TFNode* if_false = nullptr;
@@ -822,11 +703,13 @@ class WasmFullDecoder : public WasmDecoder {
true_env->control = if_true;
PushIf(end_env, false_env);
SetEnv("if:true", true_env);
+ SetBlockType(&control_.back(), operand);
+ len = 1 + operand.length;
break;
}
case kExprElse: {
if (control_.empty()) {
- error(pc_, "else does not match any if");
+ error("else does not match any if");
break;
}
Control* c = &control_.back();
@@ -838,31 +721,38 @@ class WasmFullDecoder : public WasmDecoder {
error(pc_, c->pc, "else already present for if");
break;
}
- Value val = PopUpTo(c->stack_depth);
- MergeInto(c->end_env, &c->node, &c->type, val);
+ FallThruTo(c);
// Switch to environment for false branch.
+ stack_.resize(c->stack_depth);
SetEnv("if_else:false", c->false_env);
c->false_env = nullptr; // record that an else is already seen
break;
}
case kExprEnd: {
if (control_.empty()) {
- error(pc_, "end does not match any if or block");
- break;
+ error("end does not match any if, try, or block");
+ return;
}
const char* name = "block:end";
Control* c = &control_.back();
- Value val = PopUpTo(c->stack_depth);
- if (c->is_loop) {
- // Loops always push control in pairs.
- control_.pop_back();
- c = &control_.back();
- name = "loop:end";
- } else if (c->is_if()) {
+ if (c->is_loop()) {
+ // A loop just leaves the values on the stack.
+ TypeCheckLoopFallThru(c);
+ PopControl();
+ SetEnv("loop:end", ssa_env_);
+ break;
+ }
+ if (c->is_if()) {
if (c->false_env != nullptr) {
// End the true branch of a one-armed if.
Goto(c->false_env, c->end_env);
- val = {val.pc, nullptr, kAstStmt};
+ if (ssa_env_->go() && stack_.size() != c->stack_depth) {
+ error("end of if expected empty stack");
+ stack_.resize(c->stack_depth);
+ }
+ if (c->merge.arity > 0) {
+ error("non-void one-armed if");
+ }
name = "if:merge";
} else {
// End the false branch of a two-armed if.
@@ -871,28 +761,41 @@ class WasmFullDecoder : public WasmDecoder {
} else if (c->is_try()) {
name = "try:end";
- // try blocks do not yield a value.
- val = {val.pc, nullptr, kAstStmt};
-
- // validate that catch/finally were seen.
- if (c->catch_env != nullptr) {
- error(pc_, "missing catch in try with catch");
+ // validate that catch was seen.
+ if (c->try_info->catch_env != nullptr) {
+ error(pc_, "missing catch in try");
break;
}
+ }
+ FallThruTo(c);
+ SetEnv(name, c->end_env);
- if (c->finish_try_env != nullptr) {
- error(pc_, "missing finally in try with finally");
- break;
+ // Push the end values onto the stack.
+ stack_.resize(c->stack_depth);
+ if (c->merge.arity == 1) {
+ stack_.push_back(c->merge.vals.first);
+ } else {
+ for (unsigned i = 0; i < c->merge.arity; i++) {
+ stack_.push_back(c->merge.vals.array[i]);
}
}
- if (ssa_env_->go()) {
- MergeInto(c->end_env, &c->node, &c->type, val);
+ PopControl();
+
+ if (control_.empty()) {
+ // If the last (implicit) control was popped, check we are at end.
+ if (pc_ + 1 != end_) {
+ error(pc_, pc_ + 1, "trailing code after function end");
+ }
+ last_end_found_ = true;
+ if (ssa_env_->go()) {
+ // The result of the block is the return value.
+ TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn");
+ DoReturn();
+ TRACE("\n");
+ }
+ return;
}
- SetEnv(name, c->end_env);
- stack_.resize(c->stack_depth);
- Push(c->type, c->node);
- control_.pop_back();
break;
}
case kExprSelect: {
@@ -901,7 +804,7 @@ class WasmFullDecoder : public WasmDecoder {
Value tval = Pop();
if (tval.type == kAstStmt || tval.type != fval.type) {
if (tval.type != kAstEnd && fval.type != kAstEnd) {
- error(pc_, "type mismatch in select");
+ error("type mismatch in select");
break;
}
}
@@ -923,39 +826,33 @@ class WasmFullDecoder : public WasmDecoder {
}
case kExprBr: {
BreakDepthOperand operand(this, pc_);
- Value val = {pc_, nullptr, kAstStmt};
- if (operand.arity) val = Pop();
if (Validate(pc_, operand, control_)) {
- BreakTo(operand.target, val);
+ BreakTo(operand.depth);
}
len = 1 + operand.length;
- Push(kAstEnd, nullptr);
+ EndControl();
break;
}
case kExprBrIf: {
BreakDepthOperand operand(this, pc_);
- Value cond = Pop(operand.arity, kAstI32);
- Value val = {pc_, nullptr, kAstStmt};
- if (operand.arity == 1) val = Pop();
- if (Validate(pc_, operand, control_)) {
+ Value cond = Pop(0, kAstI32);
+ if (ok() && Validate(pc_, operand, control_)) {
SsaEnv* fenv = ssa_env_;
SsaEnv* tenv = Split(fenv);
fenv->SetNotMerged();
BUILD(Branch, cond.node, &tenv->control, &fenv->control);
ssa_env_ = tenv;
- BreakTo(operand.target, val);
+ BreakTo(operand.depth);
ssa_env_ = fenv;
}
len = 1 + operand.length;
- Push(kAstStmt, nullptr);
break;
}
case kExprBrTable: {
BranchTableOperand operand(this, pc_);
+ BranchTableIterator iterator(this, operand);
if (Validate(pc_, operand, control_.size())) {
- Value key = Pop(operand.arity, kAstI32);
- Value val = {pc_, nullptr, kAstStmt};
- if (operand.arity == 1) val = Pop();
+ Value key = Pop(0, kAstI32);
if (failed()) break;
SsaEnv* break_env = ssa_env_;
@@ -965,42 +862,43 @@ class WasmFullDecoder : public WasmDecoder {
SsaEnv* copy = Steal(break_env);
ssa_env_ = copy;
- for (uint32_t i = 0; i < operand.table_count + 1; ++i) {
- uint16_t target = operand.read_entry(this, i);
+ while (iterator.has_next()) {
+ uint32_t i = iterator.cur_index();
+ const byte* pos = iterator.pc();
+ uint32_t target = iterator.next();
+ if (target >= control_.size()) {
+ error(pos, "improper branch in br_table");
+ break;
+ }
ssa_env_ = Split(copy);
ssa_env_->control = (i == operand.table_count)
? BUILD(IfDefault, sw)
: BUILD(IfValue, i, sw);
- int depth = target;
- Control* c = &control_[control_.size() - depth - 1];
- MergeInto(c->end_env, &c->node, &c->type, val);
+ BreakTo(target);
}
} else {
// Only a default target. Do the equivalent of br.
- uint16_t target = operand.read_entry(this, 0);
- int depth = target;
- Control* c = &control_[control_.size() - depth - 1];
- MergeInto(c->end_env, &c->node, &c->type, val);
+ const byte* pos = iterator.pc();
+ uint32_t target = iterator.next();
+ if (target >= control_.size()) {
+ error(pos, "improper branch in br_table");
+ break;
+ }
+ BreakTo(target);
}
// br_table ends the control flow like br.
ssa_env_ = break_env;
- Push(kAstStmt, nullptr);
}
- len = 1 + operand.length;
+ len = 1 + iterator.length();
break;
}
case kExprReturn: {
- ReturnArityOperand operand(this, pc_);
- if (operand.arity != sig_->return_count()) {
- error(pc_, pc_ + 1, "arity mismatch in return");
- }
DoReturn();
- len = 1 + operand.length;
break;
}
case kExprUnreachable: {
- Push(kAstEnd, BUILD(Unreachable, position()));
- ssa_env_->Kill(SsaEnv::kControlEnd);
+ BUILD(Unreachable, position());
+ EndControl();
break;
}
case kExprI8Const: {
@@ -1050,11 +948,24 @@ class WasmFullDecoder : public WasmDecoder {
if (Validate(pc_, operand)) {
Value val = Pop(0, local_type_vec_[operand.index]);
if (ssa_env_->locals) ssa_env_->locals[operand.index] = val.node;
+ }
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprTeeLocal: {
+ LocalIndexOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ Value val = Pop(0, local_type_vec_[operand.index]);
+ if (ssa_env_->locals) ssa_env_->locals[operand.index] = val.node;
Push(val.type, val.node);
}
len = 1 + operand.length;
break;
}
+ case kExprDrop: {
+ Pop();
+ break;
+ }
case kExprGetGlobal: {
GlobalIndexOperand operand(this, pc_);
if (Validate(pc_, operand)) {
@@ -1066,9 +977,13 @@ class WasmFullDecoder : public WasmDecoder {
case kExprSetGlobal: {
GlobalIndexOperand operand(this, pc_);
if (Validate(pc_, operand)) {
- Value val = Pop(0, operand.type);
- BUILD(SetGlobal, operand.index, val.node);
- Push(val.type, val.node);
+ if (operand.global->mutability) {
+ Value val = Pop(0, operand.type);
+ BUILD(SetGlobal, operand.index, val.node);
+ } else {
+ error(pc_, pc_ + 1, "immutable global #%u cannot be assigned",
+ operand.index);
+ }
}
len = 1 + operand.length;
break;
@@ -1088,7 +1003,6 @@ class WasmFullDecoder : public WasmDecoder {
case kExprI32LoadMem:
len = DecodeLoadMem(kAstI32, MachineType::Int32());
break;
-
case kExprI64LoadMem8S:
len = DecodeLoadMem(kAstI64, MachineType::Int8());
break;
@@ -1143,17 +1057,24 @@ class WasmFullDecoder : public WasmDecoder {
case kExprF64StoreMem:
len = DecodeStoreMem(kAstF64, MachineType::Float64());
break;
-
+ case kExprGrowMemory:
+ if (module_->origin != kAsmJsOrigin) {
+ Value val = Pop(0, kAstI32);
+ Push(kAstI32, BUILD(GrowMemory, val.node));
+ } else {
+ error("grow_memory is not supported for asmjs modules");
+ }
+ break;
case kExprMemorySize:
- Push(kAstI32, BUILD(MemSize, 0));
+ Push(kAstI32, BUILD(CurrentMemoryPages));
break;
case kExprCallFunction: {
CallFunctionOperand operand(this, pc_);
if (Validate(pc_, operand)) {
TFNode** buffer = PopArgs(operand.sig);
- TFNode* call =
- BUILD(CallDirect, operand.index, buffer, position());
- Push(GetReturnType(operand.sig), call);
+ TFNode** rets = nullptr;
+ BUILD(CallDirect, operand.index, buffer, &rets, position());
+ PushReturns(operand.sig, rets);
}
len = 1 + operand.length;
break;
@@ -1161,23 +1082,12 @@ class WasmFullDecoder : public WasmDecoder {
case kExprCallIndirect: {
CallIndirectOperand operand(this, pc_);
if (Validate(pc_, operand)) {
- TFNode** buffer = PopArgs(operand.sig);
Value index = Pop(0, kAstI32);
- if (buffer) buffer[0] = index.node;
- TFNode* call =
- BUILD(CallIndirect, operand.index, buffer, position());
- Push(GetReturnType(operand.sig), call);
- }
- len = 1 + operand.length;
- break;
- }
- case kExprCallImport: {
- CallImportOperand operand(this, pc_);
- if (Validate(pc_, operand)) {
TFNode** buffer = PopArgs(operand.sig);
- TFNode* call =
- BUILD(CallImport, operand.index, buffer, position());
- Push(GetReturnType(operand.sig), call);
+ if (buffer) buffer[0] = index.node;
+ TFNode** rets = nullptr;
+ BUILD(CallIndirect, operand.index, buffer, &rets, position());
+ PushReturns(operand.sig, rets);
}
len = 1 + operand.length;
break;
@@ -1187,20 +1097,34 @@ class WasmFullDecoder : public WasmDecoder {
len++;
byte simd_index = *(pc_ + 1);
opcode = static_cast<WasmOpcode>(opcode << 8 | simd_index);
- DecodeSimdOpcode(opcode);
+ TRACE(" @%-4d #%02x #%02x:%-20s|", startrel(pc_), kSimdPrefix,
+ simd_index, WasmOpcodes::ShortOpcodeName(opcode));
+ len += DecodeSimdOpcode(opcode);
break;
}
- default:
- error("Invalid opcode");
- return;
+ default: {
+ // Deal with special asmjs opcodes.
+ if (module_ && module_->origin == kAsmJsOrigin) {
+ sig = WasmOpcodes::AsmjsSignature(opcode);
+ if (sig) {
+ BuildSimpleOperator(opcode, sig);
+ }
+ } else {
+ error("Invalid opcode");
+ return;
+ }
+ }
}
- } // end complex bytecode
+ }
#if DEBUG
if (FLAG_trace_wasm_decoder) {
for (size_t i = 0; i < stack_.size(); ++i) {
Value& val = stack_[i];
WasmOpcode opcode = static_cast<WasmOpcode>(*val.pc);
+ if (WasmOpcodes::IsPrefixOpcode(opcode)) {
+ opcode = static_cast<WasmOpcode>(opcode << 8 | *(val.pc + 1));
+ }
PrintF(" %c@%d:%s", WasmOpcodes::ShortNameOf(val.type),
static_cast<int>(val.pc - start_),
WasmOpcodes::ShortOpcodeName(opcode));
@@ -1215,7 +1139,8 @@ class WasmFullDecoder : public WasmDecoder {
PrintF("[%u]", operand.index);
break;
}
- case kExprSetLocal: {
+ case kExprSetLocal: // fallthru
+ case kExprTeeLocal: {
LocalIndexOperand operand(this, val.pc);
PrintF("[%u]", operand.index);
break;
@@ -1234,7 +1159,21 @@ class WasmFullDecoder : public WasmDecoder {
return;
}
} // end decode loop
- } // end DecodeFunctionBody()
+ }
+
+ void EndControl() { ssa_env_->Kill(SsaEnv::kControlEnd); }
+
+ void SetBlockType(Control* c, BlockTypeOperand& operand) {
+ c->merge.arity = operand.arity;
+ if (c->merge.arity == 1) {
+ c->merge.vals.first = {pc_, nullptr, operand.read_entry(0)};
+ } else if (c->merge.arity > 1) {
+ c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity);
+ for (unsigned i = 0; i < c->merge.arity; i++) {
+ c->merge.vals.array[i] = {pc_, nullptr, operand.read_entry(i)};
+ }
+ }
+ }
TFNode** PopArgs(FunctionSig* sig) {
if (build()) {
@@ -1260,27 +1199,35 @@ class WasmFullDecoder : public WasmDecoder {
void PushBlock(SsaEnv* end_env) {
const int stack_depth = static_cast<int>(stack_.size());
- control_.emplace_back(Control::Block(pc_, stack_depth, end_env));
+ control_.emplace_back(
+ Control::Block(pc_, stack_depth, end_env, current_catch_));
}
void PushLoop(SsaEnv* end_env) {
const int stack_depth = static_cast<int>(stack_.size());
- control_.emplace_back(Control::Loop(pc_, stack_depth, end_env));
+ control_.emplace_back(
+ Control::Loop(pc_, stack_depth, end_env, current_catch_));
}
void PushIf(SsaEnv* end_env, SsaEnv* false_env) {
const int stack_depth = static_cast<int>(stack_.size());
- control_.emplace_back(Control::If(pc_, stack_depth, end_env, false_env));
+ control_.emplace_back(
+ Control::If(pc_, stack_depth, end_env, false_env, current_catch_));
}
- void PushTry(SsaEnv* end_env, SsaEnv* catch_env, SsaEnv* finish_try_env) {
+ void PushTry(SsaEnv* end_env, SsaEnv* catch_env) {
const int stack_depth = static_cast<int>(stack_.size());
- control_.emplace_back(
- Control::Try(pc_, stack_depth, end_env, catch_env, finish_try_env));
+ control_.emplace_back(Control::Try(pc_, stack_depth, end_env, zone_,
+ catch_env, current_catch_));
+ current_catch_ = static_cast<int32_t>(control_.size() - 1);
}
+ void PopControl() { control_.pop_back(); }
+
int DecodeLoadMem(LocalType type, MachineType mem_type) {
- MemoryAccessOperand operand(this, pc_);
+ MemoryAccessOperand operand(this, pc_,
+ ElementSizeLog2Of(mem_type.representation()));
+
Value index = Pop(0, kAstI32);
TFNode* node = BUILD(LoadMem, type, mem_type, index.node, operand.offset,
operand.alignment, position());
@@ -1289,24 +1236,45 @@ class WasmFullDecoder : public WasmDecoder {
}
int DecodeStoreMem(LocalType type, MachineType mem_type) {
- MemoryAccessOperand operand(this, pc_);
+ MemoryAccessOperand operand(this, pc_,
+ ElementSizeLog2Of(mem_type.representation()));
Value val = Pop(1, type);
Value index = Pop(0, kAstI32);
BUILD(StoreMem, mem_type, index.node, operand.offset, operand.alignment,
val.node, position());
- Push(type, val.node);
return 1 + operand.length;
}
- void DecodeSimdOpcode(WasmOpcode opcode) {
- FunctionSig* sig = WasmOpcodes::Signature(opcode);
- compiler::NodeVector inputs(sig->parameter_count(), zone_);
- for (size_t i = sig->parameter_count(); i > 0; i--) {
- Value val = Pop(static_cast<int>(i - 1), sig->GetParam(i - 1));
- inputs[i - 1] = val.node;
+ unsigned DecodeSimdOpcode(WasmOpcode opcode) {
+ unsigned len = 0;
+ switch (opcode) {
+ case kExprI32x4ExtractLane: {
+ uint8_t lane = this->checked_read_u8(pc_, 2, "lane number");
+ if (lane < 0 || lane > 3) {
+ error(pc_, pc_ + 2, "invalid extract lane value");
+ }
+ TFNode* input = Pop(0, LocalType::kSimd128).node;
+ TFNode* node = BUILD(SimdExtractLane, opcode, lane, input);
+ Push(LocalType::kWord32, node);
+ len++;
+ break;
+ }
+ default: {
+ FunctionSig* sig = WasmOpcodes::Signature(opcode);
+ if (sig != nullptr) {
+ compiler::NodeVector inputs(sig->parameter_count(), zone_);
+ for (size_t i = sig->parameter_count(); i > 0; i--) {
+ Value val = Pop(static_cast<int>(i - 1), sig->GetParam(i - 1));
+ inputs[i - 1] = val.node;
+ }
+ TFNode* node = BUILD(SimdOp, opcode, inputs);
+ Push(GetReturnType(sig), node);
+ } else {
+ error("invalid simd opcode");
+ }
+ }
}
- TFNode* node = BUILD(SimdOp, opcode, inputs);
- Push(GetReturnType(sig), node);
+ return len;
}
void DoReturn() {
@@ -1320,12 +1288,21 @@ class WasmFullDecoder : public WasmDecoder {
if (buffer) buffer[i] = val.node;
}
- Push(kAstEnd, BUILD(Return, count, buffer));
- ssa_env_->Kill(SsaEnv::kControlEnd);
+ BUILD(Return, count, buffer);
+ EndControl();
}
void Push(LocalType type, TFNode* node) {
- stack_.push_back({pc_, node, type});
+ if (type != kAstStmt && type != kAstEnd) {
+ stack_.push_back({pc_, node, type});
+ }
+ }
+
+ void PushReturns(FunctionSig* sig, TFNode** rets) {
+ for (size_t i = 0; i < sig->return_count(); i++) {
+ // When verifying only, then {rets} will be null, so push null.
+ Push(sig->GetReturn(i), rets ? rets[i] : nullptr);
+ }
}
const char* SafeOpcodeNameAt(const byte* pc) {
@@ -1334,6 +1311,10 @@ class WasmFullDecoder : public WasmDecoder {
}
Value Pop(int index, LocalType expected) {
+ if (!ssa_env_->go()) {
+ // Unreachable code is essentially not typechecked.
+ return {pc_, nullptr, expected};
+ }
Value val = Pop();
if (val.type != expected) {
if (val.type != kAstEnd) {
@@ -1346,6 +1327,10 @@ class WasmFullDecoder : public WasmDecoder {
}
Value Pop() {
+ if (!ssa_env_->go()) {
+ // Unreachable code is essentially not typechecked.
+ return {pc_, nullptr, kAstEnd};
+ }
size_t limit = control_.empty() ? 0 : control_.back().stack_depth;
if (stack_.size() <= limit) {
Value val = {pc_, nullptr, kAstStmt};
@@ -1358,6 +1343,10 @@ class WasmFullDecoder : public WasmDecoder {
}
Value PopUpTo(int stack_depth) {
+ if (!ssa_env_->go()) {
+ // Unreachable code is essentially not typechecked.
+ return {pc_, nullptr, kAstEnd};
+ }
if (stack_depth == stack_.size()) {
Value val = {pc_, nullptr, kAstStmt};
return val;
@@ -1375,34 +1364,82 @@ class WasmFullDecoder : public WasmDecoder {
int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); }
- void BreakTo(Control* block, Value& val) {
- if (block->is_loop) {
+ void BreakTo(unsigned depth) {
+ if (!ssa_env_->go()) return;
+ Control* c = &control_[control_.size() - depth - 1];
+ if (c->is_loop()) {
// This is the inner loop block, which does not have a value.
- Goto(ssa_env_, block->end_env);
+ Goto(ssa_env_, c->end_env);
} else {
- // Merge the value into the production for the block.
- MergeInto(block->end_env, &block->node, &block->type, val);
+ // Merge the value(s) into the end of the block.
+ if (static_cast<size_t>(c->stack_depth + c->merge.arity) >
+ stack_.size()) {
+ error(
+ pc_, pc_,
+ "expected at least %d values on the stack for br to @%d, found %d",
+ c->merge.arity, startrel(c->pc),
+ static_cast<int>(stack_.size() - c->stack_depth));
+ return;
+ }
+ MergeValuesInto(c);
+ }
+ }
+
+ void FallThruTo(Control* c) {
+ if (!ssa_env_->go()) return;
+ // Merge the value(s) into the end of the block.
+ int arity = static_cast<int>(c->merge.arity);
+ if (c->stack_depth + arity != stack_.size()) {
+ error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d",
+ arity, startrel(c->pc));
+ return;
}
+ MergeValuesInto(c);
}
- void MergeInto(SsaEnv* target, TFNode** node, LocalType* type, Value& val) {
+ inline Value& GetMergeValueFromStack(Control* c, int i) {
+ return stack_[stack_.size() - c->merge.arity + i];
+ }
+
+ void TypeCheckLoopFallThru(Control* c) {
if (!ssa_env_->go()) return;
- DCHECK_NE(kAstEnd, val.type);
+ // Fallthru must match arity exactly.
+ int arity = static_cast<int>(c->merge.arity);
+ if (c->stack_depth + arity != stack_.size()) {
+ error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d",
+ arity, startrel(c->pc));
+ return;
+ }
+ // Typecheck the values left on the stack.
+ for (unsigned i = 0; i < c->merge.arity; i++) {
+ Value& val = GetMergeValueFromStack(c, i);
+ Value& old =
+ c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i];
+ if (val.type != old.type) {
+ error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i,
+ WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
+ return;
+ }
+ }
+ }
+ void MergeValuesInto(Control* c) {
+ SsaEnv* target = c->end_env;
bool first = target->state == SsaEnv::kUnreachable;
Goto(ssa_env_, target);
- if (first) {
- // first merge to this environment; set the type and the node.
- *type = val.type;
- *node = val.node;
- } else if (val.type == *type && val.type != kAstStmt) {
- // merge with the existing value for this block.
- *node = CreateOrMergeIntoPhi(*type, target->control, *node, val.node);
- } else {
- // types don't match, or block is already a stmt.
- *type = kAstStmt;
- *node = nullptr;
+ for (unsigned i = 0; i < c->merge.arity; i++) {
+ Value& val = GetMergeValueFromStack(c, i);
+ Value& old =
+ c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i];
+ if (val.type != old.type) {
+ error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i,
+ WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
+ return;
+ }
+ old.node =
+ first ? val.node : CreateOrMergeIntoPhi(old.type, target->control,
+ old.node, val.node);
}
}
@@ -1442,6 +1479,45 @@ class WasmFullDecoder : public WasmDecoder {
}
}
+ TFNode* CheckForException(TFNode* node) {
+ if (node == nullptr) {
+ return nullptr;
+ }
+
+ const bool inside_try_scope = current_catch_ != kNullCatch;
+
+ if (!inside_try_scope) {
+ return node;
+ }
+
+ TFNode* if_success = nullptr;
+ TFNode* if_exception = nullptr;
+ if (!builder_->ThrowsException(node, &if_success, &if_exception)) {
+ return node;
+ }
+
+ SsaEnv* success_env = Steal(ssa_env_);
+ success_env->control = if_success;
+
+ SsaEnv* exception_env = Split(success_env);
+ exception_env->control = if_exception;
+ TryInfo* try_info = current_try_info();
+ Goto(exception_env, try_info->catch_env);
+ TFNode* exception = try_info->exception;
+ if (exception == nullptr) {
+ DCHECK_EQ(SsaEnv::kReached, try_info->catch_env->state);
+ try_info->exception = if_exception;
+ } else {
+ DCHECK_EQ(SsaEnv::kMerged, try_info->catch_env->state);
+ try_info->exception =
+ CreateOrMergeIntoPhi(kAstI32, try_info->catch_env->control,
+ try_info->exception, if_exception);
+ }
+
+ SetEnv("if_success", success_env);
+ return node;
+ }
+
void Goto(SsaEnv* from, SsaEnv* to) {
DCHECK_NOT_NULL(to);
if (!from->go()) return;
@@ -1630,16 +1706,15 @@ class WasmFullDecoder : public WasmDecoder {
case kExprLoop:
case kExprIf:
case kExprBlock:
- case kExprTryCatch:
- case kExprTryCatchFinally:
- case kExprTryFinally:
+ case kExprTry:
+ length = OpcodeLength(pc);
depth++;
- DCHECK_EQ(1, OpcodeLength(pc));
break;
- case kExprSetLocal: {
+ case kExprSetLocal: // fallthru
+ case kExprTeeLocal: {
LocalIndexOperand operand(this, pc);
if (assigned->length() > 0 &&
- static_cast<int>(operand.index) < assigned->length()) {
+ operand.index < static_cast<uint32_t>(assigned->length())) {
// Unverified code might have an out-of-bounds index.
assigned->Add(operand.index);
}
@@ -1664,11 +1739,33 @@ class WasmFullDecoder : public WasmDecoder {
DCHECK_EQ(pc_ - start_, offset); // overflows cannot happen
return offset;
}
+
+ inline void BuildSimpleOperator(WasmOpcode opcode, FunctionSig* sig) {
+ TFNode* node;
+ switch (sig->parameter_count()) {
+ case 1: {
+ Value val = Pop(0, sig->GetParam(0));
+ node = BUILD(Unop, opcode, val.node, position());
+ break;
+ }
+ case 2: {
+ Value rval = Pop(1, sig->GetParam(1));
+ Value lval = Pop(0, sig->GetParam(0));
+ node = BUILD(Binop, opcode, lval.node, rval.node, position());
+ break;
+ }
+ default:
+ UNREACHABLE();
+ node = nullptr;
+ break;
+ }
+ Push(GetReturnType(sig), node);
+ }
};
bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start,
const byte* end) {
- base::AccountingAllocator allocator;
+ AccountingAllocator allocator;
Zone tmp(&allocator);
FunctionBody body = {nullptr, nullptr, nullptr, start, end};
WasmFullDecoder decoder(&tmp, nullptr, body);
@@ -1686,7 +1783,7 @@ BytecodeIterator::BytecodeIterator(const byte* start, const byte* end,
}
}
-DecodeResult VerifyWasmCode(base::AccountingAllocator* allocator,
+DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
FunctionBody& body) {
Zone zone(allocator);
WasmFullDecoder decoder(&zone, nullptr, body);
@@ -1694,8 +1791,8 @@ DecodeResult VerifyWasmCode(base::AccountingAllocator* allocator,
return decoder.toResult<DecodeStruct*>(nullptr);
}
-DecodeResult BuildTFGraph(base::AccountingAllocator* allocator,
- TFBuilder* builder, FunctionBody& body) {
+DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder,
+ FunctionBody& body) {
Zone zone(allocator);
WasmFullDecoder decoder(&zone, builder, body);
decoder.Decode();
@@ -1707,18 +1804,13 @@ unsigned OpcodeLength(const byte* pc, const byte* end) {
return decoder.OpcodeLength(pc);
}
-unsigned OpcodeArity(const byte* pc, const byte* end) {
- WasmDecoder decoder(nullptr, nullptr, pc, end);
- return decoder.OpcodeArity(pc);
-}
-
void PrintAstForDebugging(const byte* start, const byte* end) {
- base::AccountingAllocator allocator;
+ AccountingAllocator allocator;
OFStream os(stdout);
PrintAst(&allocator, FunctionBodyForTesting(start, end), os, nullptr);
}
-bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
+bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
std::ostream& os,
std::vector<std::tuple<uint32_t, int, int>>* offset_table) {
Zone zone(allocator);
@@ -1777,68 +1869,57 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
}
switch (opcode) {
- case kExprIf:
case kExprElse:
+ os << " // @" << i.pc_offset();
+ control_depth++;
+ break;
case kExprLoop:
+ case kExprIf:
case kExprBlock:
- case kExprTryCatch:
- case kExprTryCatchFinally:
- case kExprTryFinally:
+ case kExprTry: {
+ BlockTypeOperand operand(&i, i.pc());
os << " // @" << i.pc_offset();
+ for (unsigned i = 0; i < operand.arity; i++) {
+ os << " " << WasmOpcodes::TypeName(operand.read_entry(i));
+ }
control_depth++;
break;
+ }
case kExprEnd:
os << " // @" << i.pc_offset();
control_depth--;
break;
case kExprBr: {
BreakDepthOperand operand(&i, i.pc());
- os << " // arity=" << operand.arity << " depth=" << operand.depth;
+ os << " // depth=" << operand.depth;
break;
}
case kExprBrIf: {
BreakDepthOperand operand(&i, i.pc());
- os << " // arity=" << operand.arity << " depth" << operand.depth;
+ os << " // depth=" << operand.depth;
break;
}
case kExprBrTable: {
BranchTableOperand operand(&i, i.pc());
- os << " // arity=" << operand.arity
- << " entries=" << operand.table_count;
+ os << " // entries=" << operand.table_count;
break;
}
case kExprCallIndirect: {
CallIndirectOperand operand(&i, i.pc());
+ os << " // sig #" << operand.index;
if (decoder.Complete(i.pc(), operand)) {
- os << " // sig #" << operand.index << ": " << *operand.sig;
- } else {
- os << " // arity=" << operand.arity << " sig #" << operand.index;
- }
- break;
- }
- case kExprCallImport: {
- CallImportOperand operand(&i, i.pc());
- if (decoder.Complete(i.pc(), operand)) {
- os << " // import #" << operand.index << ": " << *operand.sig;
- } else {
- os << " // arity=" << operand.arity << " import #" << operand.index;
+ os << ": " << *operand.sig;
}
break;
}
case kExprCallFunction: {
CallFunctionOperand operand(&i, i.pc());
+ os << " // function #" << operand.index;
if (decoder.Complete(i.pc(), operand)) {
- os << " // function #" << operand.index << ": " << *operand.sig;
- } else {
- os << " // arity=" << operand.arity << " function #" << operand.index;
+ os << ": " << *operand.sig;
}
break;
}
- case kExprReturn: {
- ReturnArityOperand operand(&i, i.pc());
- os << " // arity=" << operand.arity;
- break;
- }
default:
break;
}
diff --git a/deps/v8/src/wasm/ast-decoder.h b/deps/v8/src/wasm/ast-decoder.h
index c4f6c1679a..8c2c2c4734 100644
--- a/deps/v8/src/wasm/ast-decoder.h
+++ b/deps/v8/src/wasm/ast-decoder.h
@@ -21,6 +21,9 @@ class WasmGraphBuilder;
namespace wasm {
+const uint32_t kMaxNumWasmLocals = 8000000;
+struct WasmGlobal;
+
// Helpers for decoding different kinds of operands which follow bytecodes.
struct LocalIndexOperand {
uint32_t index;
@@ -79,39 +82,111 @@ struct ImmF64Operand {
struct GlobalIndexOperand {
uint32_t index;
LocalType type;
+ const WasmGlobal* global;
unsigned length;
inline GlobalIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "global index");
+ global = nullptr;
type = kAstStmt;
}
};
+struct BlockTypeOperand {
+ uint32_t arity;
+ const byte* types; // pointer to encoded types for the block.
+ unsigned length;
+
+ inline BlockTypeOperand(Decoder* decoder, const byte* pc) {
+ uint8_t val = decoder->checked_read_u8(pc, 1, "block type");
+ LocalType type = kAstStmt;
+ length = 1;
+ arity = 0;
+ types = nullptr;
+ if (decode_local_type(val, &type)) {
+ arity = type == kAstStmt ? 0 : 1;
+ types = pc + 1;
+ } else {
+ // Handle multi-value blocks.
+ if (!FLAG_wasm_mv_prototype) {
+ decoder->error(pc, pc + 1, "invalid block arity > 1");
+ return;
+ }
+ if (val != kMultivalBlock) {
+ decoder->error(pc, pc + 1, "invalid block type");
+ return;
+ }
+ // Decode and check the types vector of the block.
+ unsigned len = 0;
+ uint32_t count = decoder->checked_read_u32v(pc, 2, &len, "block arity");
+ // {count} is encoded as {arity-2}, so that a {0} count here corresponds
+ // to a block with 2 values. This makes invalid/redundant encodings
+ // impossible.
+ arity = count + 2;
+ length = 1 + len + arity;
+ types = pc + 1 + 1 + len;
+
+ for (uint32_t i = 0; i < arity; i++) {
+ uint32_t offset = 1 + 1 + len + i;
+ val = decoder->checked_read_u8(pc, offset, "block type");
+ decode_local_type(val, &type);
+ if (type == kAstStmt) {
+ decoder->error(pc, pc + offset, "invalid block type");
+ return;
+ }
+ }
+ }
+ }
+ // Decode a byte representing a local type. Return {false} if the encoded
+ // byte was invalid or {kMultivalBlock}.
+ bool decode_local_type(uint8_t val, LocalType* result) {
+ switch (static_cast<LocalTypeCode>(val)) {
+ case kLocalVoid:
+ *result = kAstStmt;
+ return true;
+ case kLocalI32:
+ *result = kAstI32;
+ return true;
+ case kLocalI64:
+ *result = kAstI64;
+ return true;
+ case kLocalF32:
+ *result = kAstF32;
+ return true;
+ case kLocalF64:
+ *result = kAstF64;
+ return true;
+ default:
+ *result = kAstStmt;
+ return false;
+ }
+ }
+ LocalType read_entry(unsigned index) {
+ DCHECK_LT(index, arity);
+ LocalType result;
+ CHECK(decode_local_type(types[index], &result));
+ return result;
+ }
+};
+
struct Control;
struct BreakDepthOperand {
- uint32_t arity;
uint32_t depth;
Control* target;
unsigned length;
inline BreakDepthOperand(Decoder* decoder, const byte* pc) {
- unsigned len1 = 0;
- unsigned len2 = 0;
- arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
- depth = decoder->checked_read_u32v(pc, 1 + len1, &len2, "break depth");
- length = len1 + len2;
+ depth = decoder->checked_read_u32v(pc, 1, &length, "break depth");
target = nullptr;
}
};
struct CallIndirectOperand {
- uint32_t arity;
uint32_t index;
FunctionSig* sig;
unsigned length;
inline CallIndirectOperand(Decoder* decoder, const byte* pc) {
unsigned len1 = 0;
unsigned len2 = 0;
- arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "signature index");
length = len1 + len2;
sig = nullptr;
@@ -119,59 +194,32 @@ struct CallIndirectOperand {
};
struct CallFunctionOperand {
- uint32_t arity;
uint32_t index;
FunctionSig* sig;
unsigned length;
inline CallFunctionOperand(Decoder* decoder, const byte* pc) {
unsigned len1 = 0;
unsigned len2 = 0;
- arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "function index");
length = len1 + len2;
sig = nullptr;
}
};
-struct CallImportOperand {
- uint32_t arity;
- uint32_t index;
- FunctionSig* sig;
- unsigned length;
- inline CallImportOperand(Decoder* decoder, const byte* pc) {
- unsigned len1 = 0;
- unsigned len2 = 0;
- arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
- index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "import index");
- length = len1 + len2;
- sig = nullptr;
- }
-};
-
struct BranchTableOperand {
- uint32_t arity;
uint32_t table_count;
+ const byte* start;
const byte* table;
- unsigned length;
inline BranchTableOperand(Decoder* decoder, const byte* pc) {
+ DCHECK_EQ(kExprBrTable, decoder->checked_read_u8(pc, 0, "opcode"));
+ start = pc + 1;
unsigned len1 = 0;
- unsigned len2 = 0;
- arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
- table_count =
- decoder->checked_read_u32v(pc, 1 + len1, &len2, "table count");
+ table_count = decoder->checked_read_u32v(pc, 1, &len1, "table count");
if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 ||
- len1 + len2 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) {
+ len1 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) {
decoder->error(pc, "branch table size overflow");
}
- length = len1 + len2 + (table_count + 1) * sizeof(uint32_t);
-
- uint32_t table_start = 1 + len1 + len2;
- if (decoder->check(pc, table_start, (table_count + 1) * sizeof(uint32_t),
- "expected <table entries>")) {
- table = pc + table_start;
- } else {
- table = nullptr;
- }
+ table = pc + 1 + len1;
}
inline uint32_t read_entry(Decoder* decoder, unsigned i) {
DCHECK(i <= table_count);
@@ -179,14 +227,58 @@ struct BranchTableOperand {
}
};
+// A helper to iterate over a branch table.
+class BranchTableIterator {
+ public:
+ unsigned cur_index() { return index_; }
+ bool has_next() { return index_ <= table_count_; }
+ uint32_t next() {
+ DCHECK(has_next());
+ index_++;
+ unsigned length = 0;
+ uint32_t result =
+ decoder_->checked_read_u32v(pc_, 0, &length, "branch table entry");
+ pc_ += length;
+ return result;
+ }
+ // length, including the length of the {BranchTableOperand}, but not the
+ // opcode.
+ unsigned length() {
+ while (has_next()) next();
+ return static_cast<unsigned>(pc_ - start_);
+ }
+ const byte* pc() { return pc_; }
+
+ BranchTableIterator(Decoder* decoder, BranchTableOperand& operand)
+ : decoder_(decoder),
+ start_(operand.start),
+ pc_(operand.table),
+ index_(0),
+ table_count_(operand.table_count) {}
+
+ private:
+ Decoder* decoder_;
+ const byte* start_;
+ const byte* pc_;
+ uint32_t index_; // the current index.
+ uint32_t table_count_; // the count of entries, not including default.
+};
+
struct MemoryAccessOperand {
uint32_t alignment;
uint32_t offset;
unsigned length;
- inline MemoryAccessOperand(Decoder* decoder, const byte* pc) {
+ inline MemoryAccessOperand(Decoder* decoder, const byte* pc,
+ uint32_t max_alignment) {
unsigned alignment_length;
alignment =
decoder->checked_read_u32v(pc, 1, &alignment_length, "alignment");
+ if (max_alignment < alignment) {
+ decoder->error(pc, pc + 1,
+ "invalid alignment; expected maximum alignment is %u, "
+ "actual alignment is %u",
+ max_alignment, alignment);
+ }
unsigned offset_length;
offset = decoder->checked_read_u32v(pc, 1 + alignment_length,
&offset_length, "offset");
@@ -194,15 +286,6 @@ struct MemoryAccessOperand {
}
};
-struct ReturnArityOperand {
- uint32_t arity;
- unsigned length;
-
- inline ReturnArityOperand(Decoder* decoder, const byte* pc) {
- arity = decoder->checked_read_u32v(pc, 1, &length, "return count");
- }
-};
-
typedef compiler::WasmGraphBuilder TFBuilder;
struct ModuleEnv; // forward declaration of module interface.
@@ -228,25 +311,25 @@ inline std::ostream& operator<<(std::ostream& os, const DecodeStruct& tree) {
return os;
}
-DecodeResult VerifyWasmCode(base::AccountingAllocator* allocator,
- FunctionBody& body);
-DecodeResult BuildTFGraph(base::AccountingAllocator* allocator,
- TFBuilder* builder, FunctionBody& body);
-bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
+V8_EXPORT_PRIVATE DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
+ FunctionBody& body);
+DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder,
+ FunctionBody& body);
+bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
std::ostream& os,
std::vector<std::tuple<uint32_t, int, int>>* offset_table);
// A simplified form of AST printing, e.g. from a debugger.
void PrintAstForDebugging(const byte* start, const byte* end);
-inline DecodeResult VerifyWasmCode(base::AccountingAllocator* allocator,
+inline DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
ModuleEnv* module, FunctionSig* sig,
const byte* start, const byte* end) {
FunctionBody body = {module, sig, nullptr, start, end};
return VerifyWasmCode(allocator, body);
}
-inline DecodeResult BuildTFGraph(base::AccountingAllocator* allocator,
+inline DecodeResult BuildTFGraph(AccountingAllocator* allocator,
TFBuilder* builder, ModuleEnv* module,
FunctionSig* sig, const byte* start,
const byte* end) {
@@ -276,9 +359,6 @@ BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
// Computes the length of the opcode at the given address.
unsigned OpcodeLength(const byte* pc, const byte* end);
-// Computes the arity (number of sub-nodes) of the opcode at the given address.
-unsigned OpcodeArity(const byte* pc, const byte* end);
-
// A simple forward iterator for bytecodes.
class BytecodeIterator : public Decoder {
public:
diff --git a/deps/v8/src/wasm/decoder.h b/deps/v8/src/wasm/decoder.h
index a6ede54bec..d5c9f43c57 100644
--- a/deps/v8/src/wasm/decoder.h
+++ b/deps/v8/src/wasm/decoder.h
@@ -12,7 +12,7 @@
#include "src/signature.h"
#include "src/utils.h"
#include "src/wasm/wasm-result.h"
-#include "src/zone-containers.h"
+#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
@@ -208,6 +208,19 @@ class Decoder {
// Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
void consume_bytes(int size) {
+ TRACE(" +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), "skip",
+ size);
+ if (checkAvailable(size)) {
+ pc_ += size;
+ } else {
+ pc_ = limit_;
+ }
+ }
+
+ // Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
+ void consume_bytes(uint32_t size, const char* name = "skip") {
+ TRACE(" +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), name,
+ size);
if (checkAvailable(size)) {
pc_ += size;
} else {
diff --git a/deps/v8/src/wasm/module-decoder.cc b/deps/v8/src/wasm/module-decoder.cc
index 542c47ca15..90065616d9 100644
--- a/deps/v8/src/wasm/module-decoder.cc
+++ b/deps/v8/src/wasm/module-decoder.cc
@@ -27,6 +27,141 @@ namespace wasm {
namespace {
+const char* kNameString = "name";
+const size_t kNameStringLength = 4;
+
+LocalType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
+ switch (expr.kind) {
+ case WasmInitExpr::kNone:
+ return kAstStmt;
+ case WasmInitExpr::kGlobalIndex:
+ return expr.val.global_index < module->globals.size()
+ ? module->globals[expr.val.global_index].type
+ : kAstStmt;
+ case WasmInitExpr::kI32Const:
+ return kAstI32;
+ case WasmInitExpr::kI64Const:
+ return kAstI64;
+ case WasmInitExpr::kF32Const:
+ return kAstF32;
+ case WasmInitExpr::kF64Const:
+ return kAstF64;
+ default:
+ UNREACHABLE();
+ return kAstStmt;
+ }
+}
+
+// An iterator over the sections in a WASM binary module.
+// Automatically skips all unknown sections.
+class WasmSectionIterator {
+ public:
+ explicit WasmSectionIterator(Decoder& decoder)
+ : decoder_(decoder),
+ section_code_(kUnknownSectionCode),
+ section_start_(decoder.pc()),
+ section_end_(decoder.pc()) {
+ next();
+ }
+
+ inline bool more() const {
+ return section_code_ != kUnknownSectionCode && decoder_.more();
+ }
+
+ inline WasmSectionCode section_code() const { return section_code_; }
+
+ inline const byte* section_start() const { return section_start_; }
+
+ inline uint32_t section_length() const {
+ return static_cast<uint32_t>(section_end_ - section_start_);
+ }
+
+ inline const byte* section_end() const { return section_end_; }
+
+ // Advances to the next section, checking that decoding the current section
+ // stopped at {section_end_}.
+ void advance() {
+ if (decoder_.pc() != section_end_) {
+ const char* msg = decoder_.pc() < section_end_ ? "shorter" : "longer";
+ decoder_.error(decoder_.pc(), decoder_.pc(),
+ "section was %s than expected size "
+ "(%u bytes expected, %zu decoded)",
+ msg, section_length(),
+ static_cast<size_t>(decoder_.pc() - section_start_));
+ }
+ next();
+ }
+
+ private:
+ Decoder& decoder_;
+ WasmSectionCode section_code_;
+ const byte* section_start_;
+ const byte* section_end_;
+
+ // Reads the section code/name at the current position and sets up
+ // the internal fields.
+ void next() {
+ while (true) {
+ if (!decoder_.more()) {
+ section_code_ = kUnknownSectionCode;
+ return;
+ }
+ uint8_t section_code = decoder_.consume_u8("section code");
+ // Read and check the section size.
+ uint32_t section_length = decoder_.consume_u32v("section length");
+ section_start_ = decoder_.pc();
+ if (decoder_.checkAvailable(section_length)) {
+ // Get the limit of the section within the module.
+ section_end_ = section_start_ + section_length;
+ } else {
+ // The section would extend beyond the end of the module.
+ section_end_ = section_start_;
+ }
+
+ if (section_code == kUnknownSectionCode) {
+ // Check for the known "names" section.
+ uint32_t string_length = decoder_.consume_u32v("section name length");
+ const byte* section_name_start = decoder_.pc();
+ decoder_.consume_bytes(string_length, "section name");
+ if (decoder_.failed() || decoder_.pc() > section_end_) {
+ TRACE("Section name of length %u couldn't be read\n", string_length);
+ section_code_ = kUnknownSectionCode;
+ return;
+ }
+
+ TRACE(" +%d section name : \"%.*s\"\n",
+ static_cast<int>(section_name_start - decoder_.start()),
+ string_length < 20 ? string_length : 20, section_name_start);
+
+ if (string_length == kNameStringLength &&
+ strncmp(reinterpret_cast<const char*>(section_name_start),
+ kNameString, kNameStringLength) == 0) {
+ section_code = kNameSectionCode;
+ } else {
+ section_code = kUnknownSectionCode;
+ }
+ } else if (!IsValidSectionCode(section_code)) {
+ decoder_.error(decoder_.pc(), decoder_.pc(),
+ "unknown section code #0x%02x", section_code);
+ section_code = kUnknownSectionCode;
+ }
+ section_code_ = static_cast<WasmSectionCode>(section_code);
+
+ TRACE("Section: %s\n", SectionName(section_code_));
+ if (section_code_ == kUnknownSectionCode &&
+ section_end_ > decoder_.pc()) {
+ // skip to the end of the unknown section.
+ uint32_t remaining =
+ static_cast<uint32_t>(section_end_ - decoder_.pc());
+ decoder_.consume_bytes(remaining, "section payload");
+ // fall through and continue to the next section.
+ } else {
+ return;
+ }
+ }
+ }
+};
+
// The main logic for decoding the bytes of a module.
class ModuleDecoder : public Decoder {
public:
@@ -77,11 +212,9 @@ class ModuleDecoder : public Decoder {
module->min_mem_pages = 0;
module->max_mem_pages = 0;
module->mem_export = false;
- module->mem_external = false;
module->origin = origin_;
const byte* pos = pc_;
- int current_order = 0;
uint32_t magic_word = consume_u32("wasm magic");
#define BYTES(x) (x & 0xff), (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff
if (magic_word != kWasmMagic) {
@@ -89,7 +222,6 @@ class ModuleDecoder : public Decoder {
"expected magic word %02x %02x %02x %02x, "
"found %02x %02x %02x %02x",
BYTES(kWasmMagic), BYTES(magic_word));
- goto done;
}
pos = pc_;
@@ -100,302 +232,367 @@ class ModuleDecoder : public Decoder {
"expected version %02x %02x %02x %02x, "
"found %02x %02x %02x %02x",
BYTES(kWasmVersion), BYTES(magic_version));
- goto done;
}
}
- // Decode the module sections.
- while (pc_ < limit_) {
- TRACE("DecodeSection\n");
- pos = pc_;
-
- // Read the section name.
- uint32_t string_length = consume_u32v("section name length");
- const byte* section_name_start = pc_;
- consume_bytes(string_length);
- if (failed()) {
- TRACE("Section name of length %u couldn't be read\n", string_length);
- break;
+ WasmSectionIterator section_iter(*this);
+
+ // ===== Type section ====================================================
+ if (section_iter.section_code() == kTypeSectionCode) {
+ uint32_t signatures_count = consume_u32v("signatures count");
+ module->signatures.reserve(SafeReserve(signatures_count));
+ for (uint32_t i = 0; ok() && i < signatures_count; ++i) {
+ TRACE("DecodeSignature[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+ FunctionSig* s = consume_sig();
+ module->signatures.push_back(s);
}
+ section_iter.advance();
+ }
- TRACE(" +%d section name : \"%.*s\"\n",
- static_cast<int>(section_name_start - start_),
- string_length < 20 ? string_length : 20, section_name_start);
-
- WasmSection::Code section =
- WasmSection::lookup(section_name_start, string_length);
-
- // Read and check the section size.
- uint32_t section_length = consume_u32v("section length");
- if (!checkAvailable(section_length)) {
- // The section would extend beyond the end of the module.
- break;
- }
- const byte* section_start = pc_;
- const byte* expected_section_end = pc_ + section_length;
-
- current_order = CheckSectionOrder(current_order, section);
-
- switch (section) {
- case WasmSection::Code::End:
- // Terminate section decoding.
- limit_ = pc_;
- break;
- case WasmSection::Code::Memory: {
- module->min_mem_pages = consume_u32v("min memory");
- module->max_mem_pages = consume_u32v("max memory");
- module->mem_export = consume_u8("export memory") != 0;
- break;
- }
- case WasmSection::Code::Signatures: {
- uint32_t signatures_count = consume_u32v("signatures count");
- module->signatures.reserve(SafeReserve(signatures_count));
- // Decode signatures.
- for (uint32_t i = 0; i < signatures_count; ++i) {
- if (failed()) break;
- TRACE("DecodeSignature[%d] module+%d\n", i,
- static_cast<int>(pc_ - start_));
- FunctionSig* s = consume_sig();
- module->signatures.push_back(s);
- }
- break;
+ // ===== Import section ==================================================
+ if (section_iter.section_code() == kImportSectionCode) {
+ uint32_t import_table_count = consume_u32v("import table count");
+ module->import_table.reserve(SafeReserve(import_table_count));
+ for (uint32_t i = 0; ok() && i < import_table_count; ++i) {
+ TRACE("DecodeImportTable[%d] module+%d\n", i,
+ 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
+ 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);
+ if (import->module_name_length == 0) {
+ error(pos, "import module name cannot be NULL");
}
- case WasmSection::Code::FunctionSignatures: {
- uint32_t functions_count = consume_u32v("functions count");
- module->functions.reserve(SafeReserve(functions_count));
- for (uint32_t i = 0; i < functions_count; ++i) {
- module->functions.push_back({nullptr, // sig
- i, // func_index
- 0, // sig_index
- 0, // name_offset
- 0, // name_length
- 0, // code_start_offset
- 0}); // code_end_offset
+ import->field_name_offset =
+ consume_string(&import->field_name_length, true);
+
+ import->kind = static_cast<WasmExternalKind>(consume_u8("import kind"));
+ switch (import->kind) {
+ case kExternalFunction: {
+ // ===== Imported function =======================================
+ import->index = static_cast<uint32_t>(module->functions.size());
+ module->num_imported_functions++;
+ 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
+ true, // imported
+ false}); // exported
WasmFunction* function = &module->functions.back();
function->sig_index = consume_sig_index(module, &function->sig);
- }
- break;
- }
- case WasmSection::Code::FunctionBodies: {
- const byte* pos = pc_;
- uint32_t functions_count = consume_u32v("functions count");
- if (functions_count != module->functions.size()) {
- error(pos, pos, "function body count %u mismatch (%u expected)",
- functions_count,
- static_cast<uint32_t>(module->functions.size()));
break;
}
- for (uint32_t i = 0; i < functions_count; ++i) {
- WasmFunction* function = &module->functions[i];
- uint32_t size = consume_u32v("body size");
- function->code_start_offset = pc_offset();
- function->code_end_offset = pc_offset() + size;
-
- TRACE(" +%d %-20s: (%d bytes)\n", pc_offset(), "function body",
- size);
- pc_ += size;
- if (pc_ > limit_) {
- error(pc_, "function body extends beyond end of file");
- }
- }
- break;
- }
- case WasmSection::Code::Names: {
- const byte* pos = pc_;
- uint32_t functions_count = consume_u32v("functions count");
- if (functions_count != module->functions.size()) {
- error(pos, pos, "function name count %u mismatch (%u expected)",
- functions_count,
- static_cast<uint32_t>(module->functions.size()));
+ case kExternalTable: {
+ // ===== Imported table ==========================================
+ import->index =
+ static_cast<uint32_t>(module->function_tables.size());
+ module->function_tables.push_back(
+ {0, 0, std::vector<int32_t>(), true, false});
+ expect_u8("element type", 0x20);
+ WasmIndirectFunctionTable* table = &module->function_tables.back();
+ consume_resizable_limits("element count", "elements", kMaxUInt32,
+ &table->size, &table->max_size);
break;
}
-
- for (uint32_t i = 0; i < functions_count; ++i) {
- WasmFunction* function = &module->functions[i];
- function->name_offset =
- consume_string(&function->name_length, false);
-
- uint32_t local_names_count = consume_u32v("local names count");
- for (uint32_t j = 0; j < local_names_count; j++) {
- uint32_t unused = 0;
- uint32_t offset = consume_string(&unused, false);
- USE(unused);
- USE(offset);
- }
+ case kExternalMemory: {
+ // ===== Imported memory =========================================
+ // import->index =
+ // static_cast<uint32_t>(module->memories.size());
+ // TODO(titzer): imported memories
+ break;
}
- break;
- }
- case WasmSection::Code::Globals: {
- uint32_t globals_count = consume_u32v("globals count");
- module->globals.reserve(SafeReserve(globals_count));
- // Decode globals.
- for (uint32_t i = 0; i < globals_count; ++i) {
- if (failed()) break;
- TRACE("DecodeGlobal[%d] module+%d\n", i,
- static_cast<int>(pc_ - start_));
- // Add an uninitialized global and pass a pointer to it.
- module->globals.push_back({0, 0, kAstStmt, 0, false});
+ case kExternalGlobal: {
+ // ===== Imported global =========================================
+ import->index = static_cast<uint32_t>(module->globals.size());
+ module->globals.push_back(
+ {kAstStmt, false, NO_INIT, 0, true, false});
WasmGlobal* global = &module->globals.back();
- DecodeGlobalInModule(global);
- }
- break;
- }
- case WasmSection::Code::DataSegments: {
- uint32_t data_segments_count = consume_u32v("data segments count");
- module->data_segments.reserve(SafeReserve(data_segments_count));
- // Decode data segments.
- for (uint32_t i = 0; i < data_segments_count; ++i) {
- if (failed()) break;
- TRACE("DecodeDataSegment[%d] module+%d\n", i,
- static_cast<int>(pc_ - start_));
- module->data_segments.push_back({0, // dest_addr
- 0, // source_offset
- 0, // source_size
- false}); // init
- WasmDataSegment* segment = &module->data_segments.back();
- DecodeDataSegmentInModule(module, segment);
+ global->type = consume_value_type();
+ global->mutability = consume_u8("mutability") != 0;
+ break;
}
- break;
+ default:
+ error(pos, pos, "unknown import kind 0x%02x", import->kind);
+ break;
}
- case WasmSection::Code::FunctionTable: {
- // An indirect function table requires functions first.
- CheckForFunctions(module, section);
- // Assume only one table for now.
- static const uint32_t kSupportedTableCount = 1;
- module->function_tables.reserve(SafeReserve(kSupportedTableCount));
- // Decode function table.
- for (uint32_t i = 0; i < kSupportedTableCount; ++i) {
- if (failed()) break;
- TRACE("DecodeFunctionTable[%d] module+%d\n", i,
- static_cast<int>(pc_ - start_));
- module->function_tables.push_back({0, 0, std::vector<uint16_t>()});
- DecodeFunctionTableInModule(module, &module->function_tables[i]);
+ }
+ section_iter.advance();
+ }
+
+ // ===== Function section ================================================
+ if (section_iter.section_code() == kFunctionSectionCode) {
+ uint32_t functions_count = consume_u32v("functions count");
+ module->functions.reserve(SafeReserve(functions_count));
+ module->num_declared_functions = functions_count;
+ for (uint32_t i = 0; ok() && i < functions_count; ++i) {
+ uint32_t func_index = static_cast<uint32_t>(module->functions.size());
+ 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
+ false, // imported
+ false}); // exported
+ WasmFunction* function = &module->functions.back();
+ function->sig_index = consume_sig_index(module, &function->sig);
+ }
+ section_iter.advance();
+ }
+
+ // ===== Table section ===================================================
+ if (section_iter.section_code() == kTableSectionCode) {
+ const byte* pos = pc_;
+ uint32_t table_count = consume_u32v("table count");
+ // Require at most one table for now.
+ if (table_count > 1) {
+ error(pos, pos, "invalid table count %d, maximum 1", table_count);
+ }
+
+ for (uint32_t i = 0; ok() && i < table_count; i++) {
+ module->function_tables.push_back(
+ {0, 0, std::vector<int32_t>(), false, false});
+ WasmIndirectFunctionTable* table = &module->function_tables.back();
+ expect_u8("table type", kWasmAnyFunctionTypeForm);
+ consume_resizable_limits("table elements", "elements", kMaxUInt32,
+ &table->size, &table->max_size);
+ }
+ section_iter.advance();
+ }
+
+ // ===== Memory section ==================================================
+ if (section_iter.section_code() == kMemorySectionCode) {
+ const byte* pos = pc_;
+ uint32_t memory_count = consume_u32v("memory count");
+ // Require at most one memory for now.
+ if (memory_count > 1) {
+ error(pos, pos, "invalid memory count %d, maximum 1", memory_count);
+ }
+
+ for (uint32_t i = 0; ok() && i < memory_count; i++) {
+ consume_resizable_limits("memory", "pages", WasmModule::kMaxLegalPages,
+ &module->min_mem_pages,
+ &module->max_mem_pages);
+ }
+ section_iter.advance();
+ }
+
+ // ===== Global section ==================================================
+ if (section_iter.section_code() == kGlobalSectionCode) {
+ uint32_t globals_count = consume_u32v("globals count");
+ module->globals.reserve(SafeReserve(globals_count));
+ for (uint32_t i = 0; ok() && i < globals_count; ++i) {
+ TRACE("DecodeGlobal[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+ // Add an uninitialized global and pass a pointer to it.
+ module->globals.push_back({kAstStmt, false, NO_INIT, 0, false, false});
+ WasmGlobal* global = &module->globals.back();
+ DecodeGlobalInModule(module, i, global);
+ }
+ section_iter.advance();
+ }
+
+ // ===== Export section ==================================================
+ if (section_iter.section_code() == kExportSectionCode) {
+ uint32_t export_table_count = consume_u32v("export table count");
+ module->export_table.reserve(SafeReserve(export_table_count));
+ for (uint32_t i = 0; ok() && i < export_table_count; ++i) {
+ TRACE("DecodeExportTable[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+
+ module->export_table.push_back({
+ 0, // name_length
+ 0, // name_offset
+ kExternalFunction, // kind
+ 0 // index
+ });
+ WasmExport* exp = &module->export_table.back();
+
+ exp->name_offset = consume_string(&exp->name_length, true);
+ const byte* pos = pc();
+ exp->kind = static_cast<WasmExternalKind>(consume_u8("export kind"));
+ switch (exp->kind) {
+ case kExternalFunction: {
+ WasmFunction* func = nullptr;
+ exp->index = consume_func_index(module, &func);
+ module->num_exported_functions++;
+ if (func) func->exported = true;
+ break;
}
- break;
- }
- case WasmSection::Code::StartFunction: {
- // Declares a start function for a module.
- CheckForFunctions(module, section);
- if (module->start_function_index >= 0) {
- error("start function already declared");
+ case kExternalTable: {
+ WasmIndirectFunctionTable* table = nullptr;
+ exp->index = consume_table_index(module, &table);
+ if (table) table->exported = true;
break;
}
- WasmFunction* func;
- const byte* pos = pc_;
- module->start_function_index = consume_func_index(module, &func);
- if (func && func->sig->parameter_count() > 0) {
- error(pos, "invalid start function: non-zero parameter count");
+ case kExternalMemory: {
+ uint32_t index = consume_u32v("memory index");
+ if (index != 0) error("invalid memory index != 0");
+ module->mem_export = true;
break;
}
- break;
- }
- case WasmSection::Code::ImportTable: {
- uint32_t import_table_count = consume_u32v("import table count");
- module->import_table.reserve(SafeReserve(import_table_count));
- // Decode import table.
- for (uint32_t i = 0; i < import_table_count; ++i) {
- if (failed()) break;
- TRACE("DecodeImportTable[%d] module+%d\n", i,
- static_cast<int>(pc_ - start_));
-
- module->import_table.push_back({nullptr, // sig
- 0, // sig_index
- 0, // module_name_offset
- 0, // module_name_length
- 0, // function_name_offset
- 0}); // function_name_length
- WasmImport* import = &module->import_table.back();
-
- import->sig_index = consume_sig_index(module, &import->sig);
- const byte* pos = pc_;
- import->module_name_offset =
- consume_string(&import->module_name_length, true);
- if (import->module_name_length == 0) {
- error(pos, "import module name cannot be NULL");
- }
- import->function_name_offset =
- consume_string(&import->function_name_length, true);
+ case kExternalGlobal: {
+ WasmGlobal* global = nullptr;
+ exp->index = consume_global_index(module, &global);
+ if (global) global->exported = true;
+ break;
}
- break;
+ default:
+ error(pos, pos, "invalid export kind 0x%02x", exp->kind);
+ break;
}
- case WasmSection::Code::ExportTable: {
- // Declares an export table.
- CheckForFunctions(module, section);
- uint32_t export_table_count = consume_u32v("export table count");
- module->export_table.reserve(SafeReserve(export_table_count));
- // Decode export table.
- for (uint32_t i = 0; i < export_table_count; ++i) {
- if (failed()) break;
- TRACE("DecodeExportTable[%d] module+%d\n", i,
- static_cast<int>(pc_ - start_));
-
- module->export_table.push_back({0, // func_index
- 0, // name_offset
- 0}); // name_length
- WasmExport* exp = &module->export_table.back();
-
- WasmFunction* func;
- exp->func_index = consume_func_index(module, &func);
- exp->name_offset = consume_string(&exp->name_length, true);
+ }
+ // Check for duplicate exports.
+ if (ok() && module->export_table.size() > 1) {
+ std::vector<WasmExport> sorted_exports(module->export_table);
+ const byte* base = start_;
+ auto cmp_less = [base](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;
}
- // Check for duplicate exports.
- if (ok() && module->export_table.size() > 1) {
- std::vector<WasmExport> sorted_exports(module->export_table);
- const byte* base = start_;
- auto cmp_less = [base](const WasmExport& a, const WasmExport& b) {
- // Return true if a < b.
- uint32_t len = a.name_length;
- if (len != b.name_length) return len < b.name_length;
- return memcmp(base + a.name_offset, base + b.name_offset, len) <
- 0;
- };
- std::stable_sort(sorted_exports.begin(), sorted_exports.end(),
- cmp_less);
- auto it = sorted_exports.begin();
- WasmExport* last = &*it++;
- 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_ + it->name_offset;
- error(pc, pc,
- "Duplicate export name '%.*s' for functions %d and %d",
- it->name_length, pc, last->func_index, it->func_index);
- break;
- }
- }
+ return memcmp(base + a.name_offset, base + b.name_offset,
+ a.name_length) < 0;
+ };
+ std::stable_sort(sorted_exports.begin(), sorted_exports.end(),
+ cmp_less);
+ auto it = sorted_exports.begin();
+ WasmExport* last = &*it++;
+ 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_ + it->name_offset;
+ error(pc, pc,
+ "Duplicate export name '%.*s' for functions %d and %d",
+ it->name_length, pc, last->index, it->index);
+ break;
}
- break;
}
- case WasmSection::Code::Max:
- // Skip unknown sections.
- TRACE("Unknown section: '");
- for (uint32_t i = 0; i != string_length; ++i) {
- TRACE("%c", *(section_name_start + i));
- }
- TRACE("'\n");
- consume_bytes(section_length);
- break;
- }
-
- if (pc_ != expected_section_end) {
- const char* diff = pc_ < expected_section_end ? "shorter" : "longer";
- size_t expected_length = static_cast<size_t>(section_length);
- size_t actual_length = static_cast<size_t>(pc_ - section_start);
- error(pc_, pc_,
- "section \"%s\" %s (%zu bytes) than specified (%zu bytes)",
- WasmSection::getName(section), diff, actual_length,
- expected_length);
- break;
}
+ section_iter.advance();
+ }
+
+ // ===== Start section ===================================================
+ if (section_iter.section_code() == kStartSectionCode) {
+ WasmFunction* func;
+ const byte* pos = pc_;
+ module->start_function_index = consume_func_index(module, &func);
+ if (func && func->sig->parameter_count() > 0) {
+ error(pos, "invalid start function: non-zero parameter count");
+ }
+ section_iter.advance();
+ }
+
+ // ===== Elements section ================================================
+ if (section_iter.section_code() == kElementSectionCode) {
+ uint32_t element_count = consume_u32v("element count");
+ for (uint32_t i = 0; ok() && i < element_count; ++i) {
+ uint32_t table_index = consume_u32v("table index");
+ if (table_index != 0) error("illegal table index != 0");
+ WasmInitExpr offset = consume_init_expr(module, kAstI32);
+ uint32_t num_elem = consume_u32v("number of elements");
+ std::vector<uint32_t> vector;
+ module->table_inits.push_back({table_index, offset, vector});
+ WasmTableInit* init = &module->table_inits.back();
+ init->entries.reserve(SafeReserve(num_elem));
+ for (uint32_t j = 0; ok() && j < num_elem; j++) {
+ WasmFunction* func = nullptr;
+ init->entries.push_back(consume_func_index(module, &func));
+ }
+ }
+
+ section_iter.advance();
+ }
+
+ // ===== Code section ====================================================
+ if (section_iter.section_code() == kCodeSectionCode) {
+ const byte* pos = pc_;
+ uint32_t functions_count = consume_u32v("functions count");
+ if (functions_count != module->num_declared_functions) {
+ error(pos, 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];
+ uint32_t size = consume_u32v("body size");
+ function->code_start_offset = pc_offset();
+ function->code_end_offset = pc_offset() + size;
+ consume_bytes(size, "function body");
+ }
+ section_iter.advance();
+ }
+
+ // ===== Data section ====================================================
+ if (section_iter.section_code() == kDataSectionCode) {
+ uint32_t data_segments_count = consume_u32v("data segments count");
+ module->data_segments.reserve(SafeReserve(data_segments_count));
+ for (uint32_t i = 0; ok() && i < data_segments_count; ++i) {
+ TRACE("DecodeDataSegment[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+ module->data_segments.push_back({
+ NO_INIT, // dest_addr
+ 0, // source_offset
+ 0 // source_size
+ });
+ WasmDataSegment* segment = &module->data_segments.back();
+ DecodeDataSegmentInModule(module, segment);
+ }
+ section_iter.advance();
}
- done:
- if (ok()) CalculateGlobalsOffsets(module);
+ // ===== Name section ====================================================
+ if (section_iter.section_code() == kNameSectionCode) {
+ const byte* pos = pc_;
+ uint32_t functions_count = consume_u32v("functions count");
+ if (functions_count != module->num_declared_functions) {
+ error(pos, pos, "function name 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];
+ function->name_offset = consume_string(&function->name_length, false);
+
+ uint32_t local_names_count = consume_u32v("local names count");
+ for (uint32_t j = 0; ok() && j < local_names_count; j++) {
+ uint32_t unused = 0;
+ uint32_t offset = consume_string(&unused, false);
+ USE(unused);
+ USE(offset);
+ }
+ }
+ section_iter.advance();
+ }
+
+ // ===== Remaining sections ==============================================
+ if (section_iter.more() && ok()) {
+ error(pc(), pc(), "unexpected section: %s",
+ SectionName(section_iter.section_code()));
+ }
+
+ if (ok()) {
+ CalculateGlobalOffsets(module);
+ PreinitializeIndirectFunctionTables(module);
+ }
const WasmModule* finished_module = module;
ModuleResult result = toResult(finished_module);
- if (FLAG_dump_wasm_module) {
- DumpModule(module, result);
- }
+ if (FLAG_dump_wasm_module) DumpModule(module, result);
return result;
}
@@ -405,27 +602,6 @@ class ModuleDecoder : public Decoder {
return count < kMaxReserve ? count : kMaxReserve;
}
- void CheckForFunctions(WasmModule* module, WasmSection::Code section) {
- if (module->functions.size() == 0) {
- error(pc_ - 1, nullptr, "functions must appear before section %s",
- WasmSection::getName(section));
- }
- }
-
- int CheckSectionOrder(int current_order, WasmSection::Code section) {
- int next_order = WasmSection::getOrder(section);
- if (next_order == 0) return current_order;
- if (next_order == current_order) {
- error(pc_, pc_, "section \"%s\" already defined",
- WasmSection::getName(section));
- }
- if (next_order < current_order) {
- error(pc_, pc_, "section \"%s\" out of order",
- WasmSection::getName(section));
- }
- return next_order;
- }
-
// Decodes a single anonymous function starting at {start_}.
FunctionResult DecodeSingleFunction(ModuleEnv* module_env,
WasmFunction* function) {
@@ -451,6 +627,11 @@ class ModuleDecoder : public Decoder {
return ok() ? result : nullptr;
}
+ WasmInitExpr DecodeInitExpr(const byte* start) {
+ pc_ = start;
+ return consume_init_expr(nullptr, kAstStmt);
+ }
+
private:
Zone* module_zone;
ModuleResult result_;
@@ -459,15 +640,28 @@ class ModuleDecoder : public Decoder {
uint32_t off(const byte* ptr) { return static_cast<uint32_t>(ptr - start_); }
// Decodes a single global entry inside a module starting at {pc_}.
- void DecodeGlobalInModule(WasmGlobal* global) {
- global->name_offset = consume_string(&global->name_length, false);
- if (!unibrow::Utf8::Validate(start_ + global->name_offset,
- global->name_length)) {
- error("global name is not valid utf8");
+ void DecodeGlobalInModule(WasmModule* module, uint32_t index,
+ WasmGlobal* global) {
+ global->type = consume_value_type();
+ global->mutability = consume_u8("mutability") != 0;
+ const byte* pos = pc();
+ global->init = consume_init_expr(module, kAstStmt);
+ switch (global->init.kind) {
+ case WasmInitExpr::kGlobalIndex:
+ if (global->init.val.global_index >= index) {
+ error("invalid global index in init expression");
+ } else if (module->globals[index].type != global->type) {
+ error("type mismatch in global initialization");
+ }
+ break;
+ default:
+ if (global->type != TypeOf(module, global->init)) {
+ error(pos, pos,
+ "type error in global initialization, expected %s, got %s",
+ WasmOpcodes::TypeName(global->type),
+ WasmOpcodes::TypeName(TypeOf(module, global->init)));
+ }
}
- global->type = consume_local_type();
- global->offset = 0;
- global->exported = consume_u8("exported") != 0;
}
bool IsWithinLimit(uint32_t limit, uint32_t offset, uint32_t size) {
@@ -479,10 +673,10 @@ class ModuleDecoder : public Decoder {
// Decodes a single data segment entry inside a module starting at {pc_}.
void DecodeDataSegmentInModule(WasmModule* module, WasmDataSegment* segment) {
const byte* start = pc_;
- segment->dest_addr = consume_u32v("destination");
+ expect_u8("linear memory index", 0);
+ segment->dest_addr = consume_init_expr(module, kAstI32);
segment->source_size = consume_u32v("source size");
segment->source_offset = static_cast<uint32_t>(pc_ - start_);
- segment->init = true;
// Validate the data is in the module.
uint32_t module_limit = static_cast<uint32_t>(limit_ - start_);
@@ -491,40 +685,11 @@ class ModuleDecoder : public Decoder {
error(start, "segment out of bounds of module");
}
- // Validate that the segment will fit into the (minimum) memory.
- uint32_t memory_limit =
- WasmModule::kPageSize * (module ? module->min_mem_pages
- : WasmModule::kMaxMemPages);
- if (!IsWithinLimit(memory_limit, segment->dest_addr,
- segment->source_size)) {
- error(start, "segment out of bounds of memory");
- }
-
- consume_bytes(segment->source_size);
- }
-
- // Decodes a single function table inside a module starting at {pc_}.
- void DecodeFunctionTableInModule(WasmModule* module,
- WasmIndirectFunctionTable* table) {
- table->size = consume_u32v("function table entry count");
- table->max_size = table->size;
-
- if (table->max_size != table->size) {
- error("invalid table maximum size");
- }
-
- for (uint32_t i = 0; i < table->size; ++i) {
- uint16_t index = consume_u32v();
- if (index >= module->functions.size()) {
- error(pc_ - sizeof(index), "invalid function index");
- break;
- }
- table->values.push_back(index);
- }
+ consume_bytes(segment->source_size, "segment data");
}
// Calculate individual global offsets and total size of globals table.
- void CalculateGlobalsOffsets(WasmModule* module) {
+ void CalculateGlobalOffsets(WasmModule* module) {
uint32_t offset = 0;
if (module->globals.size() == 0) {
module->globals_size = 0;
@@ -540,6 +705,30 @@ class ModuleDecoder : public Decoder {
module->globals_size = offset;
}
+ // TODO(titzer): this only works without overlapping initializations from
+ // global bases for entries
+ void PreinitializeIndirectFunctionTables(WasmModule* module) {
+ // Fill all tables with invalid entries first.
+ for (WasmIndirectFunctionTable& table : module->function_tables) {
+ table.values.resize(table.size);
+ for (size_t i = 0; i < table.size; i++) {
+ table.values[i] = kInvalidFunctionIndex;
+ }
+ }
+ for (WasmTableInit& init : module->table_inits) {
+ if (init.offset.kind != WasmInitExpr::kI32Const) continue;
+ if (init.table_index >= module->function_tables.size()) continue;
+ WasmIndirectFunctionTable& table =
+ module->function_tables[init.table_index];
+ for (size_t i = 0; i < init.entries.size(); i++) {
+ size_t index = i + init.offset.val.i32_const;
+ if (index < table.values.size()) {
+ table.values[index] = init.entries[i];
+ }
+ }
+ }
+ }
+
// Verifies the body (code) of a given function.
void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv,
WasmFunction* function) {
@@ -570,26 +759,18 @@ class ModuleDecoder : public Decoder {
}
}
- // Reads a single 32-bit unsigned integer interpreted as an offset, checking
- // the offset is within bounds and advances.
- uint32_t consume_offset(const char* name = nullptr) {
- uint32_t offset = consume_u32(name ? name : "offset");
- if (offset > static_cast<uint32_t>(limit_ - start_)) {
- error(pc_ - sizeof(uint32_t), "offset out of bounds of module");
- }
- return offset;
- }
-
// 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(uint32_t* length, bool validate_utf8) {
*length = consume_u32v("string length");
uint32_t offset = pc_offset();
- TRACE(" +%u %-20s: (%u bytes)\n", offset, "string", *length);
- if (validate_utf8 && !unibrow::Utf8::Validate(pc_, *length)) {
- error(pc_, "no valid UTF-8 string");
+ const byte* string_start = pc_;
+ // Consume bytes before validation to guarantee that the string is not oob.
+ consume_bytes(*length, "string");
+ if (ok() && validate_utf8 &&
+ !unibrow::Utf8::Validate(string_start, *length)) {
+ error(string_start, "no valid UTF-8 string");
}
- consume_bytes(*length);
return offset;
}
@@ -607,25 +788,134 @@ class ModuleDecoder : public Decoder {
}
uint32_t consume_func_index(WasmModule* module, WasmFunction** func) {
+ return consume_index("function index", module->functions, func);
+ }
+
+ uint32_t consume_global_index(WasmModule* module, WasmGlobal** global) {
+ return consume_index("global index", module->globals, global);
+ }
+
+ uint32_t consume_table_index(WasmModule* module,
+ WasmIndirectFunctionTable** table) {
+ return consume_index("table index", module->function_tables, table);
+ }
+
+ template <typename T>
+ uint32_t consume_index(const char* name, std::vector<T>& vector, T** ptr) {
const byte* pos = pc_;
- uint32_t func_index = consume_u32v("function index");
- if (func_index >= module->functions.size()) {
- error(pos, pos, "function index %u out of bounds (%d functions)",
- func_index, static_cast<int>(module->functions.size()));
- *func = nullptr;
+ uint32_t index = consume_u32v(name);
+ if (index >= vector.size()) {
+ error(pos, pos, "%s %u out of bounds (%d entries)", name, index,
+ static_cast<int>(vector.size()));
+ *ptr = nullptr;
return 0;
}
- *func = &module->functions[func_index];
- return func_index;
+ *ptr = &vector[index];
+ return index;
+ }
+
+ void consume_resizable_limits(const char* name, const char* units,
+ uint32_t max_value, uint32_t* initial,
+ uint32_t* maximum) {
+ uint32_t flags = consume_u32v("resizable limits flags");
+ const byte* pos = pc();
+ *initial = consume_u32v("initial size");
+ if (*initial > max_value) {
+ error(pos, pos,
+ "initial %s size (%u %s) is larger than maximum allowable (%u)",
+ name, *initial, units, max_value);
+ }
+ if (flags & 1) {
+ pos = pc();
+ *maximum = consume_u32v("maximum size");
+ if (*maximum > max_value) {
+ error(pos, pos,
+ "maximum %s size (%u %s) is larger than maximum allowable (%u)",
+ name, *maximum, units, max_value);
+ }
+ if (*maximum < *initial) {
+ error(pos, pos, "maximum %s size (%u %s) is less than initial (%u %s)",
+ name, *maximum, units, *initial, units);
+ }
+ } else {
+ *maximum = 0;
+ }
+ }
+
+ bool expect_u8(const char* name, uint8_t expected) {
+ const byte* pos = pc();
+ uint8_t value = consume_u8(name);
+ if (value != expected) {
+ error(pos, pos, "expected %s 0x%02x, got 0x%02x", name, expected, value);
+ return false;
+ }
+ return true;
+ }
+
+ WasmInitExpr consume_init_expr(WasmModule* module, LocalType expected) {
+ const byte* pos = pc();
+ uint8_t opcode = consume_u8("opcode");
+ WasmInitExpr expr;
+ unsigned len = 0;
+ switch (opcode) {
+ case kExprGetGlobal: {
+ GlobalIndexOperand operand(this, pc() - 1);
+ expr.kind = WasmInitExpr::kGlobalIndex;
+ expr.val.global_index = operand.index;
+ len = operand.length;
+ break;
+ }
+ case kExprI32Const: {
+ ImmI32Operand operand(this, pc() - 1);
+ expr.kind = WasmInitExpr::kI32Const;
+ expr.val.i32_const = operand.value;
+ len = operand.length;
+ break;
+ }
+ case kExprF32Const: {
+ ImmF32Operand operand(this, pc() - 1);
+ expr.kind = WasmInitExpr::kF32Const;
+ expr.val.f32_const = operand.value;
+ len = operand.length;
+ break;
+ }
+ case kExprI64Const: {
+ ImmI64Operand operand(this, pc() - 1);
+ expr.kind = WasmInitExpr::kI64Const;
+ expr.val.i64_const = operand.value;
+ len = operand.length;
+ break;
+ }
+ case kExprF64Const: {
+ ImmF64Operand operand(this, pc() - 1);
+ expr.kind = WasmInitExpr::kF64Const;
+ expr.val.f64_const = operand.value;
+ len = operand.length;
+ break;
+ }
+ default: {
+ error("invalid opcode in initialization expression");
+ expr.kind = WasmInitExpr::kNone;
+ expr.val.i32_const = 0;
+ }
+ }
+ consume_bytes(len, "init code");
+ if (!expect_u8("end opcode", kExprEnd)) {
+ expr.kind = WasmInitExpr::kNone;
+ }
+ if (expected != kAstStmt && TypeOf(module, expr) != kAstI32) {
+ error(pos, pos, "type error in init expression, expected %s, got %s",
+ WasmOpcodes::TypeName(expected),
+ WasmOpcodes::TypeName(TypeOf(module, expr)));
+ }
+ return expr;
}
// Reads a single 8-bit integer, interpreting it as a local type.
- LocalType consume_local_type() {
- byte val = consume_u8("local type");
+ LocalType consume_value_type() {
+ byte val = consume_u8("value type");
LocalTypeCode t = static_cast<LocalTypeCode>(val);
switch (t) {
- case kLocalVoid:
- return kAstStmt;
case kLocalI32:
return kAstI32;
case kLocalI64:
@@ -634,6 +924,8 @@ class ModuleDecoder : public Decoder {
return kAstF32;
case kLocalF64:
return kAstF64;
+ case kLocalS128:
+ return kAstS128;
default:
error(pc_ - 1, "invalid local type");
return kAstStmt;
@@ -642,19 +934,12 @@ class ModuleDecoder : public Decoder {
// Parses a type entry, which is currently limited to functions only.
FunctionSig* consume_sig() {
- const byte* pos = pc_;
- byte form = consume_u8("type form");
- if (form != kWasmFunctionTypeForm) {
- error(pos, pos, "expected function type form (0x%02x), got: 0x%02x",
- kWasmFunctionTypeForm, form);
- return nullptr;
- }
+ if (!expect_u8("type form", kWasmFunctionTypeForm)) return nullptr;
// parse parameter types
uint32_t param_count = consume_u32v("param count");
std::vector<LocalType> params;
- for (uint32_t i = 0; i < param_count; ++i) {
- LocalType param = consume_local_type();
- if (param == kAstStmt) error(pc_ - 1, "invalid void parameter type");
+ for (uint32_t i = 0; ok() && i < param_count; ++i) {
+ LocalType param = consume_value_type();
params.push_back(param);
}
@@ -667,12 +952,16 @@ class ModuleDecoder : public Decoder {
return nullptr;
}
std::vector<LocalType> returns;
- for (uint32_t i = 0; i < return_count; ++i) {
- LocalType ret = consume_local_type();
- if (ret == kAstStmt) error(pc_ - 1, "invalid void return type");
+ for (uint32_t i = 0; ok() && i < return_count; ++i) {
+ LocalType ret = consume_value_type();
returns.push_back(ret);
}
+ if (failed()) {
+ // Decoding failed, return void -> void
+ return new (module_zone) FunctionSig(0, 0, nullptr);
+ }
+
// FunctionSig stores the return types first.
LocalType* buffer =
module_zone->NewArray<LocalType>(param_count + return_count);
@@ -711,7 +1000,7 @@ class FunctionError : public FunctionResult {
};
Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
- WasmSection::Code code) {
+ WasmSectionCode code) {
Decoder decoder(module_start, module_end);
uint32_t magic_word = decoder.consume_u32("wasm magic");
@@ -720,24 +1009,14 @@ Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
uint32_t magic_version = decoder.consume_u32("wasm version");
if (magic_version != kWasmVersion) decoder.error("wrong wasm version");
- while (decoder.more() && decoder.ok()) {
- // Read the section name.
- uint32_t string_length = decoder.consume_u32v("section name length");
- const byte* section_name_start = decoder.pc();
- decoder.consume_bytes(string_length);
- if (decoder.failed()) break;
-
- WasmSection::Code section =
- WasmSection::lookup(section_name_start, string_length);
-
- // Read and check the section size.
- uint32_t section_length = decoder.consume_u32v("section length");
-
- const byte* section_start = decoder.pc();
- decoder.consume_bytes(section_length);
- if (section == code && decoder.ok()) {
- return Vector<const uint8_t>(section_start, section_length);
+ WasmSectionIterator section_iter(decoder);
+ while (section_iter.more()) {
+ if (section_iter.section_code() == code) {
+ return Vector<const uint8_t>(section_iter.section_start(),
+ section_iter.section_length());
}
+ decoder.consume_bytes(section_iter.section_length(), "section payload");
+ section_iter.advance();
}
return Vector<const uint8_t>();
@@ -772,6 +1051,13 @@ FunctionSig* DecodeWasmSignatureForTesting(Zone* zone, const byte* start,
return decoder.DecodeFunctionSignature(start);
}
+WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end) {
+ AccountingAllocator allocator;
+ Zone zone(&allocator);
+ ModuleDecoder decoder(&zone, start, end, kWasmOrigin);
+ return decoder.DecodeInitExpr(start);
+}
+
FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone,
ModuleEnv* module_env,
const byte* function_start,
@@ -789,15 +1075,26 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone,
return decoder.DecodeSingleFunction(module_env, function);
}
-FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
- const byte* module_end) {
+FunctionOffsetsResult DecodeWasmFunctionOffsets(
+ const byte* module_start, const byte* module_end,
+ uint32_t num_imported_functions) {
+ // Find and decode the code section.
Vector<const byte> code_section =
- FindSection(module_start, module_end, WasmSection::Code::FunctionBodies);
+ FindSection(module_start, module_end, kCodeSectionCode);
Decoder decoder(code_section.start(), code_section.end());
- if (!code_section.start()) decoder.error("no code section");
+ FunctionOffsets table;
+ if (!code_section.start()) {
+ decoder.error("no code section");
+ return decoder.toResult(std::move(table));
+ }
+
+ // Reserve entries for the imported functions.
+ table.reserve(num_imported_functions);
+ for (uint32_t i = 0; i < num_imported_functions; i++) {
+ table.push_back(std::make_pair(0, 0));
+ }
uint32_t functions_count = decoder.consume_u32v("functions count");
- FunctionOffsets table;
// Take care of invalid input here.
if (functions_count < static_cast<unsigned>(code_section.length()) / 2)
table.reserve(functions_count);
diff --git a/deps/v8/src/wasm/module-decoder.h b/deps/v8/src/wasm/module-decoder.h
index dd6bd3bc86..22a313cec3 100644
--- a/deps/v8/src/wasm/module-decoder.h
+++ b/deps/v8/src/wasm/module-decoder.h
@@ -12,9 +12,11 @@ namespace v8 {
namespace internal {
namespace wasm {
// Decodes the bytes of a WASM module between {module_start} and {module_end}.
-ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
- const byte* module_start, const byte* module_end,
- bool verify_functions, ModuleOrigin origin);
+V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
+ const byte* module_start,
+ const byte* module_end,
+ bool verify_functions,
+ ModuleOrigin origin);
// Exposed for testing. Decodes a single function signature, allocating it
// in the given zone. Returns {nullptr} upon failure.
@@ -30,8 +32,11 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, ModuleEnv* env,
// Extracts the function offset table from the wasm module bytes.
// Returns a vector with <offset, length> entries, or failure if the wasm bytes
// are detected as invalid. Note that this validation is not complete.
-FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
- const byte* module_end);
+FunctionOffsetsResult DecodeWasmFunctionOffsets(
+ const byte* module_start, const byte* module_end,
+ uint32_t num_imported_functions);
+
+WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end);
} // namespace wasm
} // namespace internal
diff --git a/deps/v8/src/wasm/switch-logic.h b/deps/v8/src/wasm/switch-logic.h
index 8cef08b98b..160e0d69b2 100644
--- a/deps/v8/src/wasm/switch-logic.h
+++ b/deps/v8/src/wasm/switch-logic.h
@@ -5,8 +5,8 @@
#ifndef V8_WASM_SWITCH_LOGIC_H
#define V8_WASM_SWITCH_LOGIC_H
-#include "src/zone-containers.h"
-#include "src/zone.h"
+#include "src/zone/zone-containers.h"
+#include "src/zone/zone.h"
namespace v8 {
namespace internal {
diff --git a/deps/v8/src/wasm/wasm-debug.cc b/deps/v8/src/wasm/wasm-debug.cc
index 54e7100935..42a8e5f2ab 100644
--- a/deps/v8/src/wasm/wasm-debug.cc
+++ b/deps/v8/src/wasm/wasm-debug.cc
@@ -32,11 +32,15 @@ ByteArray *GetOrCreateFunctionOffsetTable(Handle<WasmDebugInfo> debug_info) {
FunctionOffsetsResult function_offsets;
{
DisallowHeapAllocation no_gc;
+ Handle<JSObject> wasm_object(debug_info->wasm_object(), isolate);
+ uint32_t num_imported_functions =
+ wasm::GetNumImportedFunctions(wasm_object);
SeqOneByteString *wasm_bytes =
wasm::GetWasmBytes(debug_info->wasm_object());
const byte *bytes_start = wasm_bytes->GetChars();
const byte *bytes_end = bytes_start + wasm_bytes->length();
- function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end);
+ function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end,
+ num_imported_functions);
}
DCHECK(function_offsets.ok());
size_t array_size = 2 * kIntSize * function_offsets.val.size();
@@ -179,7 +183,7 @@ Handle<String> WasmDebugInfo::DisassembleFunction(
Vector<const uint8_t> bytes_vec = GetFunctionBytes(debug_info, func_index);
DisallowHeapAllocation no_gc;
- base::AccountingAllocator allocator;
+ AccountingAllocator allocator;
bool ok = PrintAst(
&allocator, FunctionBodyForTesting(bytes_vec.start(), bytes_vec.end()),
disassembly_os, nullptr);
@@ -208,7 +212,7 @@ Handle<FixedArray> WasmDebugInfo::GetFunctionOffsetTable(
Vector<const uint8_t> bytes_vec = GetFunctionBytes(debug_info, func_index);
DisallowHeapAllocation no_gc;
- v8::base::AccountingAllocator allocator;
+ AccountingAllocator allocator;
bool ok = PrintAst(
&allocator, FunctionBodyForTesting(bytes_vec.start(), bytes_vec.end()),
null_stream, &offset_table_vec);
diff --git a/deps/v8/src/wasm/wasm-external-refs.cc b/deps/v8/src/wasm/wasm-external-refs.cc
index 09294c2c28..4c4c91b29c 100644
--- a/deps/v8/src/wasm/wasm-external-refs.cc
+++ b/deps/v8/src/wasm/wasm-external-refs.cc
@@ -206,9 +206,6 @@ uint32_t word64_popcnt_wrapper(uint64_t* input) {
void float64_pow_wrapper(double* param0, double* param1) {
double x = ReadDoubleValue(param0);
double y = ReadDoubleValue(param1);
- if (std::isnan(y) || ((x == 1 || x == -1) && std::isinf(y))) {
- WriteDoubleValue(param0, std::numeric_limits<double>::quiet_NaN());
- }
WriteDoubleValue(param0, Pow(x, y));
}
} // namespace wasm
diff --git a/deps/v8/src/wasm/wasm-interpreter.cc b/deps/v8/src/wasm/wasm-interpreter.cc
index 7e3127dd53..2ac681eff2 100644
--- a/deps/v8/src/wasm/wasm-interpreter.cc
+++ b/deps/v8/src/wasm/wasm-interpreter.cc
@@ -10,8 +10,8 @@
#include "src/wasm/wasm-external-refs.h"
#include "src/wasm/wasm-module.h"
-#include "src/base/accounting-allocator.h"
-#include "src/zone-containers.h"
+#include "src/zone/accounting-allocator.h"
+#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
@@ -654,6 +654,48 @@ static inline int64_t ExecuteI64ReinterpretF64(double a, TrapReason* trap) {
return bit_cast<int64_t>(a);
}
+static inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
+ WasmModuleInstance* instance) {
+ // TODO(ahaas): Move memory allocation to wasm-module.cc for better
+ // encapsulation.
+ if (delta_pages > wasm::WasmModule::kMaxMemPages) {
+ return -1;
+ }
+ uint32_t old_size = instance->mem_size;
+ uint32_t new_size;
+ byte* new_mem_start;
+ if (instance->mem_size == 0) {
+ if (delta_pages > wasm::WasmModule::kMaxMemPages) {
+ return -1;
+ }
+ // TODO(gdeepti): Fix bounds check to take into account size of memtype.
+ new_size = delta_pages * wasm::WasmModule::kPageSize;
+ new_mem_start = static_cast<byte*>(calloc(new_size, sizeof(byte)));
+ if (!new_mem_start) {
+ return -1;
+ }
+ } else {
+ DCHECK_NOT_NULL(instance->mem_start);
+ new_size = old_size + delta_pages * wasm::WasmModule::kPageSize;
+ if (new_size >
+ wasm::WasmModule::kMaxMemPages * wasm::WasmModule::kPageSize) {
+ return -1;
+ }
+ new_mem_start = static_cast<byte*>(realloc(instance->mem_start, new_size));
+ if (!new_mem_start) {
+ return -1;
+ }
+ // Zero initializing uninitialized memory from realloc
+ memset(new_mem_start + old_size, 0, new_size - old_size);
+ }
+ instance->mem_start = new_mem_start;
+ instance->mem_size = new_size;
+ // realloc
+ // update mem_start
+ // update mem_size
+ return static_cast<int32_t>(old_size / WasmModule::kPageSize);
+}
+
enum InternalOpcode {
#define DECL_INTERNAL_ENUM(name, value) kInternal##name = value,
FOREACH_INTERNAL_OPCODE(DECL_INTERNAL_ENUM)
@@ -680,54 +722,38 @@ class ControlTransfers : public ZoneObject {
public:
ControlTransferMap map_;
- ControlTransfers(Zone* zone, size_t locals_encoded_size, const byte* start,
- const byte* end)
+ ControlTransfers(Zone* zone, ModuleEnv* env, AstLocalDecls* locals,
+ const byte* start, const byte* end)
: map_(zone) {
- // A control reference including from PC, from value depth, and whether
- // a value is explicitly passed (e.g. br/br_if/br_table with value).
- struct CRef {
- const byte* pc;
- sp_t value_depth;
- bool explicit_value;
- };
-
// Represents a control flow label.
struct CLabel : public ZoneObject {
const byte* target;
- size_t value_depth;
- ZoneVector<CRef> refs;
+ ZoneVector<const byte*> refs;
- CLabel(Zone* zone, size_t v)
- : target(nullptr), value_depth(v), refs(zone) {}
+ explicit CLabel(Zone* zone) : target(nullptr), refs(zone) {}
// Bind this label to the given PC.
- void Bind(ControlTransferMap* map, const byte* start, const byte* pc,
- bool expect_value) {
+ void Bind(ControlTransferMap* map, const byte* start, const byte* pc) {
DCHECK_NULL(target);
target = pc;
- for (auto from : refs) {
- auto pcdiff = static_cast<pcdiff_t>(target - from.pc);
- auto spdiff = static_cast<spdiff_t>(from.value_depth - value_depth);
- ControlTransfer::StackAction action = ControlTransfer::kNoAction;
- if (expect_value && !from.explicit_value) {
- action = spdiff == 0 ? ControlTransfer::kPushVoid
- : ControlTransfer::kPopAndRepush;
- }
- pc_t offset = static_cast<size_t>(from.pc - start);
- (*map)[offset] = {pcdiff, spdiff, action};
+ for (auto from_pc : refs) {
+ auto pcdiff = static_cast<pcdiff_t>(target - from_pc);
+ size_t offset = static_cast<size_t>(from_pc - start);
+ (*map)[offset] = pcdiff;
}
}
// Reference this label from the given location.
- void Ref(ControlTransferMap* map, const byte* start, CRef from) {
- DCHECK_GE(from.value_depth, value_depth);
+ void Ref(ControlTransferMap* map, const byte* start,
+ const byte* from_pc) {
if (target) {
- auto pcdiff = static_cast<pcdiff_t>(target - from.pc);
- auto spdiff = static_cast<spdiff_t>(from.value_depth - value_depth);
- pc_t offset = static_cast<size_t>(from.pc - start);
- (*map)[offset] = {pcdiff, spdiff, ControlTransfer::kNoAction};
+ // Target being bound before a reference means this is a loop.
+ DCHECK_EQ(kExprLoop, *target);
+ auto pcdiff = static_cast<pcdiff_t>(target - from_pc);
+ size_t offset = static_cast<size_t>(from_pc - start);
+ (*map)[offset] = pcdiff;
} else {
- refs.push_back(from);
+ refs.push_back(from_pc);
}
}
};
@@ -738,122 +764,104 @@ class ControlTransfers : public ZoneObject {
CLabel* end_label;
CLabel* else_label;
- void Ref(ControlTransferMap* map, const byte* start, const byte* from_pc,
- size_t from_value_depth, bool explicit_value) {
- end_label->Ref(map, start, {from_pc, from_value_depth, explicit_value});
+ void Ref(ControlTransferMap* map, const byte* start,
+ const byte* from_pc) {
+ end_label->Ref(map, start, from_pc);
}
};
// Compute the ControlTransfer map.
- // This works by maintaining a stack of control constructs similar to the
+ // This algorithm maintains a stack of control constructs similar to the
// AST decoder. The {control_stack} allows matching {br,br_if,br_table}
// bytecodes with their target, as well as determining whether the current
// bytecodes are within the true or false block of an else.
- // The value stack depth is tracked as {value_depth} and is needed to
- // determine how many values to pop off the stack for explicit and
- // implicit control flow.
-
std::vector<Control> control_stack;
- size_t value_depth = 0;
- for (BytecodeIterator i(start + locals_encoded_size, end); i.has_next();
- i.next()) {
+ CLabel* func_label = new (zone) CLabel(zone);
+ control_stack.push_back({start, func_label, nullptr});
+ for (BytecodeIterator i(start, end, locals); i.has_next(); i.next()) {
WasmOpcode opcode = i.current();
- TRACE("@%u: control %s (depth = %zu)\n", i.pc_offset(),
- WasmOpcodes::OpcodeName(opcode), value_depth);
+ TRACE("@%u: control %s\n", i.pc_offset(),
+ WasmOpcodes::OpcodeName(opcode));
switch (opcode) {
case kExprBlock: {
- TRACE("control @%u $%zu: Block\n", i.pc_offset(), value_depth);
- CLabel* label = new (zone) CLabel(zone, value_depth);
+ TRACE("control @%u: Block\n", i.pc_offset());
+ CLabel* label = new (zone) CLabel(zone);
control_stack.push_back({i.pc(), label, nullptr});
break;
}
case kExprLoop: {
- TRACE("control @%u $%zu: Loop\n", i.pc_offset(), value_depth);
- CLabel* label1 = new (zone) CLabel(zone, value_depth);
- CLabel* label2 = new (zone) CLabel(zone, value_depth);
- control_stack.push_back({i.pc(), label1, nullptr});
- control_stack.push_back({i.pc(), label2, nullptr});
- label2->Bind(&map_, start, i.pc(), false);
+ TRACE("control @%u: Loop\n", i.pc_offset());
+ CLabel* label = new (zone) CLabel(zone);
+ control_stack.push_back({i.pc(), label, nullptr});
+ label->Bind(&map_, start, i.pc());
break;
}
case kExprIf: {
- TRACE("control @%u $%zu: If\n", i.pc_offset(), value_depth);
- value_depth--;
- CLabel* end_label = new (zone) CLabel(zone, value_depth);
- CLabel* else_label = new (zone) CLabel(zone, value_depth);
+ TRACE("control @%u: If\n", i.pc_offset());
+ CLabel* end_label = new (zone) CLabel(zone);
+ CLabel* else_label = new (zone) CLabel(zone);
control_stack.push_back({i.pc(), end_label, else_label});
- else_label->Ref(&map_, start, {i.pc(), value_depth, false});
+ else_label->Ref(&map_, start, i.pc());
break;
}
case kExprElse: {
Control* c = &control_stack.back();
- TRACE("control @%u $%zu: Else\n", i.pc_offset(), value_depth);
- c->end_label->Ref(&map_, start, {i.pc(), value_depth, false});
- value_depth = c->end_label->value_depth;
+ TRACE("control @%u: Else\n", i.pc_offset());
+ c->end_label->Ref(&map_, start, i.pc());
DCHECK_NOT_NULL(c->else_label);
- c->else_label->Bind(&map_, start, i.pc() + 1, false);
+ c->else_label->Bind(&map_, start, i.pc() + 1);
c->else_label = nullptr;
break;
}
case kExprEnd: {
Control* c = &control_stack.back();
- TRACE("control @%u $%zu: End\n", i.pc_offset(), value_depth);
+ TRACE("control @%u: End\n", i.pc_offset());
if (c->end_label->target) {
// only loops have bound labels.
DCHECK_EQ(kExprLoop, *c->pc);
- control_stack.pop_back();
- c = &control_stack.back();
+ } else {
+ if (c->else_label) c->else_label->Bind(&map_, start, i.pc());
+ c->end_label->Bind(&map_, start, i.pc() + 1);
}
- if (c->else_label)
- c->else_label->Bind(&map_, start, i.pc() + 1, true);
- c->end_label->Ref(&map_, start, {i.pc(), value_depth, false});
- c->end_label->Bind(&map_, start, i.pc() + 1, true);
- value_depth = c->end_label->value_depth + 1;
control_stack.pop_back();
break;
}
case kExprBr: {
BreakDepthOperand operand(&i, i.pc());
- TRACE("control @%u $%zu: Br[arity=%u, depth=%u]\n", i.pc_offset(),
- value_depth, operand.arity, operand.depth);
- value_depth -= operand.arity;
- control_stack[control_stack.size() - operand.depth - 1].Ref(
- &map_, start, i.pc(), value_depth, operand.arity > 0);
- value_depth++;
+ TRACE("control @%u: Br[depth=%u]\n", i.pc_offset(), operand.depth);
+ Control* c = &control_stack[control_stack.size() - operand.depth - 1];
+ c->Ref(&map_, start, i.pc());
break;
}
case kExprBrIf: {
BreakDepthOperand operand(&i, i.pc());
- TRACE("control @%u $%zu: BrIf[arity=%u, depth=%u]\n", i.pc_offset(),
- value_depth, operand.arity, operand.depth);
- value_depth -= (operand.arity + 1);
- control_stack[control_stack.size() - operand.depth - 1].Ref(
- &map_, start, i.pc(), value_depth, operand.arity > 0);
- value_depth++;
+ TRACE("control @%u: BrIf[depth=%u]\n", i.pc_offset(), operand.depth);
+ Control* c = &control_stack[control_stack.size() - operand.depth - 1];
+ c->Ref(&map_, start, i.pc());
break;
}
case kExprBrTable: {
BranchTableOperand operand(&i, i.pc());
- TRACE("control @%u $%zu: BrTable[arity=%u count=%u]\n", i.pc_offset(),
- value_depth, operand.arity, operand.table_count);
- value_depth -= (operand.arity + 1);
- for (uint32_t j = 0; j < operand.table_count + 1; ++j) {
- uint32_t target = operand.read_entry(&i, j);
- control_stack[control_stack.size() - target - 1].Ref(
- &map_, start, i.pc() + j, value_depth, operand.arity > 0);
+ BranchTableIterator iterator(&i, operand);
+ TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(),
+ operand.table_count);
+ while (iterator.has_next()) {
+ uint32_t j = iterator.cur_index();
+ uint32_t target = iterator.next();
+ Control* c = &control_stack[control_stack.size() - target - 1];
+ c->Ref(&map_, start, i.pc() + j);
}
- value_depth++;
break;
}
default: {
- value_depth = value_depth - OpcodeArity(i.pc(), end) + 1;
break;
}
}
}
+ if (!func_label->target) func_label->Bind(&map_, start, end);
}
- ControlTransfer Lookup(pc_t from) {
+ pcdiff_t Lookup(pc_t from) {
auto result = map_.find(from);
if (result == map_.end()) {
V8_Fatal(__FILE__, __LINE__, "no control target for pc %zu", from);
@@ -899,7 +907,7 @@ class CodeMap {
if (function->func_index < interpreter_code_.size()) {
InterpreterCode* code = &interpreter_code_[function->func_index];
DCHECK_EQ(function, code->function);
- return code;
+ return Preprocess(code);
}
return nullptr;
}
@@ -923,9 +931,9 @@ class CodeMap {
if (code->targets == nullptr && code->start) {
// Compute the control targets map and the local declarations.
CHECK(DecodeLocalDecls(code->locals, code->start, code->end));
- code->targets =
- new (zone_) ControlTransfers(zone_, code->locals.decls_encoded_size,
- code->orig_start, code->orig_end);
+ ModuleEnv env = {module_, nullptr, kWasmOrigin};
+ code->targets = new (zone_) ControlTransfers(
+ zone_, &env, &code->locals, code->orig_start, code->orig_end);
}
return code;
}
@@ -964,6 +972,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
instance_(instance),
stack_(zone),
frames_(zone),
+ blocks_(zone),
state_(WasmInterpreter::STOPPED),
break_pc_(kInvalidPc),
trap_reason_(kTrapCount) {}
@@ -984,6 +993,9 @@ class ThreadImpl : public WasmInterpreter::Thread {
stack_.push_back(args[i]);
}
frames_.back().ret_pc = InitLocals(code);
+ blocks_.push_back(
+ {0, stack_.size(), frames_.size(),
+ static_cast<uint32_t>(code->function->sig->return_count())});
TRACE(" => PushFrame(#%u @%zu)\n", code->function->func_index,
frames_.back().ret_pc);
}
@@ -1032,11 +1044,11 @@ class ThreadImpl : public WasmInterpreter::Thread {
return nullptr;
}
- virtual WasmVal GetReturnValue() {
+ virtual WasmVal GetReturnValue(int index) {
if (state_ == WasmInterpreter::TRAPPED) return WasmVal(0xdeadbeef);
CHECK_EQ(WasmInterpreter::FINISHED, state_);
- CHECK_EQ(1, stack_.size());
- return stack_[0];
+ CHECK_LT(static_cast<size_t>(index), stack_.size());
+ return stack_[index];
}
virtual pc_t GetBreakpointPc() { return break_pc_; }
@@ -1060,10 +1072,18 @@ class ThreadImpl : public WasmInterpreter::Thread {
sp_t llimit() { return plimit() + code->locals.total_local_count; }
};
+ struct Block {
+ pc_t pc;
+ sp_t sp;
+ size_t fp;
+ unsigned arity;
+ };
+
CodeMap* codemap_;
WasmModuleInstance* instance_;
ZoneVector<WasmVal> stack_;
ZoneVector<Frame> frames_;
+ ZoneVector<Block> blocks_;
WasmInterpreter::State state_;
pc_t break_pc_;
TrapReason trap_reason_;
@@ -1088,6 +1108,9 @@ class ThreadImpl : public WasmInterpreter::Thread {
DCHECK_GE(stack_.size(), arity);
// The parameters will overlap the arguments already on the stack.
frames_.push_back({code, 0, 0, stack_.size() - arity});
+ blocks_.push_back(
+ {0, stack_.size(), frames_.size(),
+ static_cast<uint32_t>(code->function->sig->return_count())});
frames_.back().ret_pc = InitLocals(code);
TRACE(" => push func#%u @%zu\n", code->function->func_index,
frames_.back().ret_pc);
@@ -1126,21 +1149,38 @@ class ThreadImpl : public WasmInterpreter::Thread {
bool SkipBreakpoint(InterpreterCode* code, pc_t pc) {
if (pc == break_pc_) {
+ // Skip the previously hit breakpoint when resuming.
break_pc_ = kInvalidPc;
return true;
}
return false;
}
- bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, WasmVal val) {
+ int LookupTarget(InterpreterCode* code, pc_t pc) {
+ return static_cast<int>(code->targets->Lookup(pc));
+ }
+
+ int DoBreak(InterpreterCode* code, pc_t pc, size_t depth) {
+ size_t bp = blocks_.size() - depth - 1;
+ Block* target = &blocks_[bp];
+ DoStackTransfer(target->sp, target->arity);
+ blocks_.resize(bp);
+ return LookupTarget(code, pc);
+ }
+
+ bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, size_t arity) {
DCHECK_GT(frames_.size(), 0u);
- stack_.resize(frames_.back().sp);
+ // Pop all blocks for this frame.
+ while (!blocks_.empty() && blocks_.back().fp == frames_.size()) {
+ blocks_.pop_back();
+ }
+
+ sp_t dest = frames_.back().sp;
frames_.pop_back();
if (frames_.size() == 0) {
- // A return from the top frame terminates the execution.
+ // A return from the last frame terminates the execution.
state_ = WasmInterpreter::FINISHED;
- stack_.clear();
- stack_.push_back(val);
+ DoStackTransfer(0, arity);
TRACE(" => finish\n");
return false;
} else {
@@ -1149,16 +1189,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
*code = top->code;
*pc = top->ret_pc;
*limit = top->code->end - top->code->start;
- if (top->code->start[top->call_pc] == kExprCallIndirect ||
- (top->code->orig_start &&
- top->code->orig_start[top->call_pc] == kExprCallIndirect)) {
- // UGLY: An indirect call has the additional function index on the
- // stack.
- stack_.pop_back();
- }
TRACE(" => pop func#%u @%zu\n", (*code)->function->func_index, *pc);
-
- stack_.push_back(val);
+ DoStackTransfer(dest, arity);
return true;
}
}
@@ -1169,31 +1201,21 @@ class ThreadImpl : public WasmInterpreter::Thread {
*limit = target->end - target->start;
}
- // Adjust the program counter {pc} and the stack contents according to the
- // code's precomputed control transfer map. Returns the different between
- // the new pc and the old pc.
- int DoControlTransfer(InterpreterCode* code, pc_t pc) {
- auto target = code->targets->Lookup(pc);
- switch (target.action) {
- case ControlTransfer::kNoAction:
- TRACE(" action [sp-%u]\n", target.spdiff);
- PopN(target.spdiff);
- break;
- case ControlTransfer::kPopAndRepush: {
- WasmVal val = Pop();
- TRACE(" action [pop x, sp-%u, push x]\n", target.spdiff - 1);
- DCHECK_GE(target.spdiff, 1u);
- PopN(target.spdiff - 1);
- Push(pc, val);
- break;
- }
- case ControlTransfer::kPushVoid:
- TRACE(" action [sp-%u, push void]\n", target.spdiff);
- PopN(target.spdiff);
- Push(pc, WasmVal());
- break;
+ // Copies {arity} values on the top of the stack down the stack to {dest},
+ // dropping the values in-between.
+ void DoStackTransfer(sp_t dest, size_t arity) {
+ // before: |---------------| pop_count | arity |
+ // ^ 0 ^ dest ^ stack_.size()
+ //
+ // after: |---------------| arity |
+ // ^ 0 ^ stack_.size()
+ DCHECK_LE(dest, stack_.size());
+ DCHECK_LE(dest + arity, stack_.size());
+ size_t pop_count = stack_.size() - dest - arity;
+ for (size_t i = 0; i < arity; i++) {
+ stack_[dest + i] = stack_[dest + pop_count + i];
}
- return target.pcdiff;
+ stack_.resize(stack_.size() - pop_count);
}
void Execute(InterpreterCode* code, pc_t pc, int max) {
@@ -1209,8 +1231,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
if (pc >= limit) {
// Fell off end of code; do an implicit return.
TRACE("@%-3zu: ImplicitReturn\n", pc);
- WasmVal val = PopArity(code->function->sig->return_count());
- if (!DoReturn(&code, &pc, &limit, val)) return;
+ if (!DoReturn(&code, &pc, &limit, code->function->sig->return_count()))
+ return;
decoder.Reset(code->start, code->end);
continue;
}
@@ -1243,27 +1265,37 @@ class ThreadImpl : public WasmInterpreter::Thread {
switch (orig) {
case kExprNop:
- Push(pc, WasmVal());
break;
- case kExprBlock:
+ case kExprBlock: {
+ BlockTypeOperand operand(&decoder, code->at(pc));
+ blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity});
+ len = 1 + operand.length;
+ break;
+ }
case kExprLoop: {
- // Do nothing.
+ BlockTypeOperand operand(&decoder, code->at(pc));
+ blocks_.push_back({pc, stack_.size(), frames_.size(), 0});
+ len = 1 + operand.length;
break;
}
case kExprIf: {
+ BlockTypeOperand operand(&decoder, code->at(pc));
WasmVal cond = Pop();
bool is_true = cond.to<uint32_t>() != 0;
+ blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity});
if (is_true) {
// fall through to the true block.
+ len = 1 + operand.length;
TRACE(" true => fallthrough\n");
} else {
- len = DoControlTransfer(code, pc);
+ len = LookupTarget(code, pc);
TRACE(" false => @%zu\n", pc + len);
}
break;
}
case kExprElse: {
- len = DoControlTransfer(code, pc);
+ blocks_.pop_back();
+ len = LookupTarget(code, pc);
TRACE(" end => @%zu\n", pc + len);
break;
}
@@ -1276,42 +1308,34 @@ class ThreadImpl : public WasmInterpreter::Thread {
}
case kExprBr: {
BreakDepthOperand operand(&decoder, code->at(pc));
- WasmVal val = PopArity(operand.arity);
- len = DoControlTransfer(code, pc);
+ len = DoBreak(code, pc, operand.depth);
TRACE(" br => @%zu\n", pc + len);
- if (operand.arity > 0) Push(pc, val);
break;
}
case kExprBrIf: {
BreakDepthOperand operand(&decoder, code->at(pc));
WasmVal cond = Pop();
- WasmVal val = PopArity(operand.arity);
bool is_true = cond.to<uint32_t>() != 0;
if (is_true) {
- len = DoControlTransfer(code, pc);
+ len = DoBreak(code, pc, operand.depth);
TRACE(" br_if => @%zu\n", pc + len);
- if (operand.arity > 0) Push(pc, val);
} else {
TRACE(" false => fallthrough\n");
len = 1 + operand.length;
- Push(pc, WasmVal());
}
break;
}
case kExprBrTable: {
BranchTableOperand operand(&decoder, code->at(pc));
uint32_t key = Pop().to<uint32_t>();
- WasmVal val = PopArity(operand.arity);
if (key >= operand.table_count) key = operand.table_count;
- len = DoControlTransfer(code, pc + key) + key;
- TRACE(" br[%u] => @%zu\n", key, pc + len);
- if (operand.arity > 0) Push(pc, val);
+ len = key + DoBreak(code, pc + key, operand.table[key]);
+ TRACE(" br[%u] => @%zu\n", key, pc + key + len);
break;
}
case kExprReturn: {
- ReturnArityOperand operand(&decoder, code->at(pc));
- WasmVal val = PopArity(operand.arity);
- if (!DoReturn(&code, &pc, &limit, val)) return;
+ size_t arity = code->function->sig->return_count();
+ if (!DoReturn(&code, &pc, &limit, arity)) return;
decoder.Reset(code->start, code->end);
continue;
}
@@ -1320,8 +1344,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
return CommitPc(pc);
}
case kExprEnd: {
- len = DoControlTransfer(code, pc);
- DCHECK_EQ(1, len);
+ blocks_.pop_back();
break;
}
case kExprI8Const: {
@@ -1364,10 +1387,21 @@ class ThreadImpl : public WasmInterpreter::Thread {
LocalIndexOperand operand(&decoder, code->at(pc));
WasmVal val = Pop();
stack_[frames_.back().sp + operand.index] = val;
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprTeeLocal: {
+ LocalIndexOperand operand(&decoder, code->at(pc));
+ WasmVal val = Pop();
+ stack_[frames_.back().sp + operand.index] = val;
Push(pc, val);
len = 1 + operand.length;
break;
}
+ case kExprDrop: {
+ Pop();
+ break;
+ }
case kExprCallFunction: {
CallFunctionOperand operand(&decoder, code->at(pc));
InterpreterCode* target = codemap()->GetCode(operand.index);
@@ -1378,9 +1412,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
}
case kExprCallIndirect: {
CallIndirectOperand operand(&decoder, code->at(pc));
- size_t index = stack_.size() - operand.arity - 1;
- DCHECK_LT(index, stack_.size());
- uint32_t entry_index = stack_[index].to<uint32_t>();
+ uint32_t entry_index = Pop().to<uint32_t>();
// Assume only one table for now.
DCHECK_LE(module()->function_tables.size(), 1u);
InterpreterCode* target = codemap()->GetIndirectCode(0, entry_index);
@@ -1395,10 +1427,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
decoder.Reset(code->start, code->end);
continue;
}
- case kExprCallImport: {
- UNIMPLEMENTED();
- break;
- }
case kExprGetGlobal: {
GlobalIndexOperand operand(&decoder, code->at(pc));
const WasmGlobal* global = &module()->globals[operand.index];
@@ -1437,14 +1465,13 @@ class ThreadImpl : public WasmInterpreter::Thread {
} else {
UNREACHABLE();
}
- Push(pc, val);
len = 1 + operand.length;
break;
}
#define LOAD_CASE(name, ctype, mtype) \
case kExpr##name: { \
- MemoryAccessOperand operand(&decoder, code->at(pc)); \
+ MemoryAccessOperand operand(&decoder, code->at(pc), sizeof(ctype)); \
uint32_t index = Pop().to<uint32_t>(); \
size_t effective_mem_size = instance()->mem_size - sizeof(mtype); \
if (operand.offset > effective_mem_size || \
@@ -1476,7 +1503,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
#define STORE_CASE(name, ctype, mtype) \
case kExpr##name: { \
- MemoryAccessOperand operand(&decoder, code->at(pc)); \
+ MemoryAccessOperand operand(&decoder, code->at(pc), sizeof(ctype)); \
WasmVal val = Pop(); \
uint32_t index = Pop().to<uint32_t>(); \
size_t effective_mem_size = instance()->mem_size - sizeof(mtype); \
@@ -1486,7 +1513,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
} \
byte* addr = instance()->mem_start + operand.offset + index; \
WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>())); \
- Push(pc, val); \
len = 1 + operand.length; \
break; \
}
@@ -1546,9 +1572,14 @@ class ThreadImpl : public WasmInterpreter::Thread {
ASMJS_STORE_CASE(F32AsmjsStoreMem, float, float);
ASMJS_STORE_CASE(F64AsmjsStoreMem, double, double);
#undef ASMJS_STORE_CASE
-
+ case kExprGrowMemory: {
+ uint32_t delta_pages = Pop().to<uint32_t>();
+ Push(pc, WasmVal(ExecuteGrowMemory(delta_pages, instance())));
+ break;
+ }
case kExprMemorySize: {
- Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size)));
+ Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size /
+ WasmModule::kPageSize)));
break;
}
#define EXECUTE_SIMPLE_BINOP(name, ctype, op) \
@@ -1623,7 +1654,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
void Push(pc_t pc, WasmVal val) {
// TODO(titzer): store PC as well?
- stack_.push_back(val);
+ if (val.type != kAstStmt) stack_.push_back(val);
}
void TraceStack(const char* phase, pc_t pc) {
@@ -1700,7 +1731,7 @@ class WasmInterpreterInternals : public ZoneObject {
// Implementation of the public interface of the interpreter.
//============================================================================
WasmInterpreter::WasmInterpreter(WasmModuleInstance* instance,
- base::AccountingAllocator* allocator)
+ AccountingAllocator* allocator)
: zone_(allocator),
internals_(new (&zone_) WasmInterpreterInternals(&zone_, instance)) {}
@@ -1804,7 +1835,7 @@ bool WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function,
ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
Zone* zone, const byte* start, const byte* end) {
- ControlTransfers targets(zone, 0, start, end);
+ ControlTransfers targets(zone, nullptr, nullptr, start, end);
return targets.map_;
}
diff --git a/deps/v8/src/wasm/wasm-interpreter.h b/deps/v8/src/wasm/wasm-interpreter.h
index b106a202d2..b61e092e23 100644
--- a/deps/v8/src/wasm/wasm-interpreter.h
+++ b/deps/v8/src/wasm/wasm-interpreter.h
@@ -6,7 +6,7 @@
#define V8_WASM_INTERPRETER_H_
#include "src/wasm/wasm-opcodes.h"
-#include "src/zone-containers.h"
+#include "src/zone/zone-containers.h"
namespace v8 {
namespace base {
@@ -28,15 +28,7 @@ typedef uint32_t spdiff_t;
const pc_t kInvalidPc = 0x80000000;
-// Visible for testing. A {ControlTransfer} helps the interpreter figure out
-// the target program counter and stack manipulations for a branch.
-struct ControlTransfer {
- enum StackAction { kNoAction, kPopAndRepush, kPushVoid };
- pcdiff_t pcdiff; // adjustment to the program counter (positive or negative).
- spdiff_t spdiff; // number of elements to pop off the stack.
- StackAction action; // action to perform on the stack.
-};
-typedef ZoneMap<pc_t, ControlTransfer> ControlTransferMap;
+typedef ZoneMap<pc_t, pcdiff_t> ControlTransferMap;
// Macro for defining union members.
#define FOREACH_UNION_MEMBER(V) \
@@ -102,7 +94,7 @@ class WasmFrame {
};
// An interpreter capable of executing WASM.
-class WasmInterpreter {
+class V8_EXPORT_PRIVATE WasmInterpreter {
public:
// State machine for a Thread:
// +---------------Run()-----------+
@@ -132,15 +124,14 @@ class WasmInterpreter {
virtual int GetFrameCount() = 0;
virtual const WasmFrame* GetFrame(int index) = 0;
virtual WasmFrame* GetMutableFrame(int index) = 0;
- virtual WasmVal GetReturnValue() = 0;
+ virtual WasmVal GetReturnValue(int index = 0) = 0;
// Thread-specific breakpoints.
bool SetBreakpoint(const WasmFunction* function, int pc, bool enabled);
bool GetBreakpoint(const WasmFunction* function, int pc);
};
- WasmInterpreter(WasmModuleInstance* instance,
- base::AccountingAllocator* allocator);
+ WasmInterpreter(WasmModuleInstance* instance, AccountingAllocator* allocator);
~WasmInterpreter();
//==========================================================================
@@ -190,9 +181,8 @@ class WasmInterpreter {
bool SetFunctionCodeForTesting(const WasmFunction* function,
const byte* start, const byte* end);
- // Computes the control targets for the given bytecode as {pc offset, sp
- // offset}
- // pairs. Used internally in the interpreter, but exposed for testing.
+ // Computes the control transfers for the given bytecode. Used internally in
+ // the interpreter, but exposed for testing.
static ControlTransferMap ComputeControlTransfersForTesting(Zone* zone,
const byte* start,
const byte* end);
diff --git a/deps/v8/src/wasm/wasm-js.cc b/deps/v8/src/wasm/wasm-js.cc
index 10ae43c78b..254fd7061a 100644
--- a/deps/v8/src/wasm/wasm-js.cc
+++ b/deps/v8/src/wasm/wasm-js.cc
@@ -9,8 +9,6 @@
#include "src/asmjs/asm-wasm-builder.h"
#include "src/assert-scope.h"
#include "src/ast/ast.h"
-#include "src/ast/scopes.h"
-#include "src/compiler.h"
#include "src/execution.h"
#include "src/factory.h"
#include "src/handles.h"
@@ -18,7 +16,6 @@
#include "src/objects.h"
#include "src/parsing/parse-info.h"
-#include "src/wasm/encoder.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-module.h"
@@ -31,6 +28,13 @@ using v8::internal::wasm::ErrorThrower;
namespace v8 {
namespace {
+i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
+ return isolate->factory()->NewStringFromAsciiChecked(str);
+}
+Local<String> v8_str(Isolate* isolate, const char* str) {
+ return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str));
+}
+
struct RawBuffer {
const byte* start;
const byte* end;
@@ -80,7 +84,7 @@ void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
ErrorThrower thrower(isolate, "Wasm.verifyModule()");
if (args.Length() < 1) {
- thrower.Error("Argument 0 must be a buffer source");
+ thrower.TypeError("Argument 0 must be a buffer source");
return;
}
RawBuffer buffer = GetRawBufferSource(args[0], &thrower);
@@ -104,7 +108,7 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
ErrorThrower thrower(isolate, "Wasm.verifyFunction()");
if (args.Length() < 1) {
- thrower.Error("Argument 0 must be a buffer source");
+ thrower.TypeError("Argument 0 must be a buffer source");
return;
}
RawBuffer buffer = GetRawBufferSource(args[0], &thrower);
@@ -135,13 +139,11 @@ i::MaybeHandle<i::JSObject> InstantiateModule(
// Decode but avoid a redundant pass over function bodies for verification.
// Verification will happen during compilation.
i::Zone zone(isolate->allocator());
- internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
- isolate, &zone, start, end, false, origin);
-
+ i::MaybeHandle<i::JSObject> module_object =
+ i::wasm::CreateModuleObjectFromBytes(isolate, start, end, thrower,
+ origin);
i::MaybeHandle<i::JSObject> object;
- if (result.failed()) {
- thrower->Failed("", result);
- } else {
+ if (!module_object.is_null()) {
// Success. Instantiate the module and return the object.
i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null();
if (args.Length() > 1 && args[1]->IsObject()) {
@@ -156,19 +158,12 @@ i::MaybeHandle<i::JSObject> InstantiateModule(
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
}
- i::MaybeHandle<i::FixedArray> compiled_module =
- result.val->CompileFunctions(isolate, thrower);
- if (!thrower->error()) {
- DCHECK(!compiled_module.is_null());
- object = i::wasm::WasmModule::Instantiate(
- isolate, compiled_module.ToHandleChecked(), ffi, memory);
- if (!object.is_null()) {
- args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
- }
+ object = i::wasm::WasmModule::Instantiate(
+ isolate, thrower, module_object.ToHandleChecked(), ffi, memory);
+ if (!object.is_null()) {
+ args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
}
}
-
- if (result.val) delete result.val;
return object;
}
@@ -178,7 +173,7 @@ void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
ErrorThrower thrower(isolate, "Wasm.instantiateModule()");
if (args.Length() < 1) {
- thrower.Error("Argument 0 must be a buffer source");
+ thrower.TypeError("Argument 0 must be a buffer source");
return;
}
RawBuffer buffer = GetRawBufferSource(args[0], &thrower);
@@ -197,20 +192,37 @@ static i::MaybeHandle<i::JSObject> CreateModuleObject(
if (buffer.start == nullptr) return i::MaybeHandle<i::JSObject>();
DCHECK(source->IsArrayBuffer() || source->IsTypedArray());
- i::Zone zone(i_isolate->allocator());
- i::wasm::ModuleResult result = i::wasm::DecodeWasmModule(
- i_isolate, &zone, buffer.start, buffer.end, false, i::wasm::kWasmOrigin);
- std::unique_ptr<const i::wasm::WasmModule> decoded_module(result.val);
- if (result.failed()) {
- thrower->Failed("", result);
- return nothing;
- }
- i::MaybeHandle<i::FixedArray> compiled_module =
- decoded_module->CompileFunctions(i_isolate, thrower);
- if (compiled_module.is_null()) return nothing;
+ return i::wasm::CreateModuleObjectFromBytes(
+ i_isolate, buffer.start, buffer.end, thrower,
+ i::wasm::ModuleOrigin::kWasmOrigin);
+}
+
+static bool ValidateModule(v8::Isolate* isolate,
+ const v8::Local<v8::Value> source,
+ ErrorThrower* thrower) {
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ i::MaybeHandle<i::JSObject> nothing;
+
+ RawBuffer buffer = GetRawBufferSource(source, thrower);
+ if (buffer.start == nullptr) return false;
- return i::wasm::CreateCompiledModuleObject(i_isolate,
- compiled_module.ToHandleChecked());
+ DCHECK(source->IsArrayBuffer() || source->IsTypedArray());
+ return i::wasm::ValidateModuleBytes(i_isolate, buffer.start, buffer.end,
+ thrower,
+ i::wasm::ModuleOrigin::kWasmOrigin);
+}
+
+bool BrandCheck(Isolate* isolate, i::Handle<i::Object> value,
+ i::Handle<i::Symbol> sym, const char* msg) {
+ if (value->IsJSObject()) {
+ i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value);
+ Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym);
+ if (has_brand.IsNothing()) return false;
+ if (has_brand.ToChecked()) return true;
+ }
+ v8::Local<v8::Value> e = v8::Exception::TypeError(v8_str(isolate, msg));
+ isolate->ThrowException(e);
+ return false;
}
void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
@@ -220,7 +232,7 @@ void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
"WebAssembly.compile()");
if (args.Length() < 1) {
- thrower.Error("Argument 0 must be a buffer source");
+ thrower.TypeError("Argument 0 must be a buffer source");
return;
}
i::MaybeHandle<i::JSObject> module_obj =
@@ -238,6 +250,25 @@ void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
return_value.Set(resolver->GetPromise());
}
+void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ v8::Isolate* isolate = args.GetIsolate();
+ HandleScope scope(isolate);
+ ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
+ "WebAssembly.validate()");
+
+ if (args.Length() < 1) {
+ thrower.TypeError("Argument 0 must be a buffer source");
+ return;
+ }
+
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
+ if (ValidateModule(isolate, args[0], &thrower)) {
+ return_value.Set(v8::True(isolate));
+ } else {
+ return_value.Set(v8::False(isolate));
+ }
+}
+
void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
@@ -245,7 +276,7 @@ void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
"WebAssembly.Module()");
if (args.Length() < 1) {
- thrower.Error("Argument 0 must be a buffer source");
+ thrower.TypeError("Argument 0 must be a buffer source");
return;
}
i::MaybeHandle<i::JSObject> module_obj =
@@ -264,18 +295,15 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
ErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
if (args.Length() < 1) {
- thrower.Error(
- "Argument 0 must be provided, and must be a WebAssembly.Module object");
+ thrower.TypeError("Argument 0 must be a WebAssembly.Module");
return;
}
Local<Context> context = isolate->GetCurrentContext();
i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
- i::Handle<i::Symbol> module_sym(i_context->wasm_module_sym());
- i::MaybeHandle<i::Object> source =
- i::Object::GetProperty(Utils::OpenHandle(*args[0]), module_sym);
- if (source.is_null() || source.ToHandleChecked()->IsUndefined(i_isolate)) {
- thrower.Error("Argument 0 must be a WebAssembly.Module");
+ if (!BrandCheck(isolate, Utils::OpenHandle(*args[0]),
+ i::Handle<i::Symbol>(i_context->wasm_module_sym()),
+ "Argument 0 must be a WebAssembly.Module")) {
return;
}
@@ -285,13 +313,10 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj));
if (module_obj->GetInternalFieldCount() < 1 ||
!module_obj->GetInternalField(0)->IsFixedArray()) {
- thrower.Error("Argument 0 is an invalid WebAssembly.Module");
+ thrower.TypeError("Argument 0 is an invalid WebAssembly.Module");
return;
}
- i::Handle<i::FixedArray> compiled_code = i::Handle<i::FixedArray>(
- i::FixedArray::cast(module_obj->GetInternalField(0)));
-
i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null();
if (args.Length() > 1 && args[1]->IsObject()) {
Local<Object> obj = Local<Object>::Cast(args[1]);
@@ -304,17 +329,211 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
}
- i::MaybeHandle<i::JSObject> instance =
- i::wasm::WasmModule::Instantiate(i_isolate, compiled_code, ffi, memory);
+ i::MaybeHandle<i::JSObject> instance = i::wasm::WasmModule::Instantiate(
+ i_isolate, &thrower, module_obj, ffi, memory);
if (instance.is_null()) {
- thrower.Error("Could not instantiate module");
+ if (!thrower.error()) thrower.Error("Could not instantiate module");
return;
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(instance.ToHandleChecked()));
}
+
+bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
+ Local<Context> context, Local<v8::Object> object,
+ Local<String> property, int* result, int lower_bound,
+ int upper_bound) {
+ v8::MaybeLocal<v8::Value> maybe = object->Get(context, property);
+ v8::Local<v8::Value> value;
+ if (maybe.ToLocal(&value)) {
+ int64_t number;
+ if (!value->IntegerValue(context).To(&number)) return false;
+ if (number < static_cast<int64_t>(lower_bound)) {
+ thrower->RangeError("Property value %" PRId64
+ " is below the lower bound %d",
+ number, lower_bound);
+ return false;
+ }
+ if (number > static_cast<int64_t>(upper_bound)) {
+ thrower->RangeError("Property value %" PRId64
+ " is above the upper bound %d",
+ number, upper_bound);
+ return false;
+ }
+ *result = static_cast<int>(number);
+ return true;
+ }
+ return false;
+}
+
+void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ v8::Isolate* isolate = args.GetIsolate();
+ HandleScope scope(isolate);
+ ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
+ "WebAssembly.Module()");
+ if (args.Length() < 1 || !args[0]->IsObject()) {
+ thrower.TypeError("Argument 0 must be a table descriptor");
+ return;
+ }
+ Local<Context> context = isolate->GetCurrentContext();
+ Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
+ // The descriptor's 'element'.
+ {
+ v8::MaybeLocal<v8::Value> maybe =
+ descriptor->Get(context, v8_str(isolate, "element"));
+ v8::Local<v8::Value> value;
+ if (!maybe.ToLocal(&value)) return;
+ v8::Local<v8::String> string;
+ if (!value->ToString(context).ToLocal(&string)) return;
+ bool equal;
+ if (!string->Equals(context, v8_str(isolate, "anyfunc")).To(&equal)) return;
+ if (!equal) {
+ thrower.TypeError("Descriptor property 'element' must be 'anyfunc'");
+ return;
+ }
+ }
+ const int max_table_size = 1 << 26;
+ // The descriptor's 'initial'.
+ int initial;
+ if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
+ v8_str(isolate, "initial"), &initial, 0,
+ max_table_size)) {
+ return;
+ }
+ // The descriptor's 'maximum'.
+ int maximum = 0;
+ Local<String> maximum_key = v8_str(isolate, "maximum");
+ Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);
+
+ if (has_maximum.IsNothing()) {
+ // There has been an exception, just return.
+ return;
+ }
+ if (has_maximum.FromJust()) {
+ if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
+ &maximum, initial, max_table_size)) {
+ return;
+ }
+ }
+
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ i::Handle<i::JSFunction> table_ctor(
+ i_isolate->native_context()->wasm_table_constructor());
+ i::Handle<i::JSObject> table_obj =
+ i_isolate->factory()->NewJSObject(table_ctor);
+ i::Handle<i::FixedArray> fixed_array =
+ i_isolate->factory()->NewFixedArray(initial);
+ i::Object* null = i_isolate->heap()->null_value();
+ for (int i = 0; i < initial; ++i) fixed_array->set(i, null);
+ table_obj->SetInternalField(0, *fixed_array);
+ table_obj->SetInternalField(
+ 1, has_maximum.FromJust()
+ ? static_cast<i::Object*>(i::Smi::FromInt(maximum))
+ : static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
+ i::Handle<i::Symbol> table_sym(i_isolate->native_context()->wasm_table_sym());
+ i::Object::SetProperty(table_obj, table_sym, table_obj, i::STRICT).Check();
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
+ return_value.Set(Utils::ToLocal(table_obj));
+}
+
+void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ v8::Isolate* isolate = args.GetIsolate();
+ HandleScope scope(isolate);
+ ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate),
+ "WebAssembly.Module()");
+ if (args.Length() < 1 || !args[0]->IsObject()) {
+ thrower.TypeError("Argument 0 must be a table descriptor");
+ return;
+ }
+ Local<Context> context = isolate->GetCurrentContext();
+ Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
+ // The descriptor's 'initial'.
+ int initial;
+ if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
+ v8_str(isolate, "initial"), &initial, 0, 65536)) {
+ return;
+ }
+ // The descriptor's 'maximum'.
+ int maximum = 0;
+ Local<String> maximum_key = v8_str(isolate, "maximum");
+ Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);
+
+ if (has_maximum.IsNothing()) {
+ // There has been an exception, just return.
+ return;
+ }
+ if (has_maximum.FromJust()) {
+ if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
+ &maximum, initial, 65536)) {
+ return;
+ }
+ }
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ i::Handle<i::JSArrayBuffer> buffer =
+ i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared);
+ size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) *
+ static_cast<size_t>(initial);
+ i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size);
+
+ i::Handle<i::JSObject> memory_obj = i::WasmJs::CreateWasmMemoryObject(
+ i_isolate, buffer, has_maximum.FromJust(), maximum);
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
+ return_value.Set(Utils::ToLocal(memory_obj));
+}
+void WebAssemblyTableGetLength(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // TODO(rossberg)
+}
+void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // TODO(rossberg)
+}
+void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // TODO(rossberg)
+}
+void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // TODO(rossberg)
+}
+void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // TODO(rossberg)
+}
+void WebAssemblyMemoryGetBuffer(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ v8::Isolate* isolate = args.GetIsolate();
+ Local<Context> context = isolate->GetCurrentContext();
+ i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
+ if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()),
+ i::Handle<i::Symbol>(i_context->wasm_memory_sym()),
+ "Receiver is not a WebAssembly.Memory")) {
+ return;
+ }
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ i::Handle<i::JSObject> receiver =
+ i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This()));
+ i::Handle<i::Object> buffer(receiver->GetInternalField(0), i_isolate);
+ DCHECK(buffer->IsJSArrayBuffer());
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
+ return_value.Set(Utils::ToLocal(buffer));
+}
} // namespace
+i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject(
+ i::Isolate* i_isolate, i::Handle<i::JSArrayBuffer> buffer, bool has_maximum,
+ int maximum) {
+ i::Handle<i::JSFunction> memory_ctor(
+ i_isolate->native_context()->wasm_memory_constructor());
+ i::Handle<i::JSObject> memory_obj =
+ i_isolate->factory()->NewJSObject(memory_ctor);
+ memory_obj->SetInternalField(0, *buffer);
+ memory_obj->SetInternalField(
+ 1, has_maximum
+ ? static_cast<i::Object*>(i::Smi::FromInt(maximum))
+ : static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
+ i::Handle<i::Symbol> memory_sym(
+ i_isolate->native_context()->wasm_memory_sym());
+ i::Object::SetProperty(memory_obj, memory_sym, memory_obj, i::STRICT).Check();
+ return memory_obj;
+}
+
// TODO(titzer): we use the API to create the function template because the
// internal guts are too ugly to replicate here.
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
@@ -325,12 +544,9 @@ static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
}
namespace internal {
-static Handle<String> v8_str(Isolate* isolate, const char* str) {
- return isolate->factory()->NewStringFromAsciiChecked(str);
-}
-static Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object,
- const char* str, FunctionCallback func) {
+Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object,
+ const char* str, FunctionCallback func) {
Handle<String> name = v8_str(isolate, str);
Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
Handle<JSFunction> function =
@@ -341,6 +557,112 @@ static Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object,
return function;
}
+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);
+ Handle<JSFunction> function =
+ ApiNatives::InstantiateFunction(temp).ToHandleChecked();
+ v8::PropertyAttribute attributes =
+ static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly);
+ Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name),
+ Utils::ToLocal(function),
+ Local<Function>(), attributes);
+ return function;
+}
+
+void WasmJs::InstallWasmModuleSymbolIfNeeded(Isolate* isolate,
+ Handle<JSGlobalObject> global,
+ Handle<Context> context) {
+ if (!context->get(Context::WASM_MODULE_SYM_INDEX)->IsSymbol() ||
+ !context->get(Context::WASM_INSTANCE_SYM_INDEX)->IsSymbol()) {
+ InstallWasmMapsIfNeeded(isolate, isolate->native_context());
+ InstallWasmConstructors(isolate, isolate->global_object(),
+ isolate->native_context());
+ }
+}
+
+void WasmJs::InstallWasmConstructors(Isolate* isolate,
+ Handle<JSGlobalObject> global,
+ Handle<Context> context) {
+ 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);
+
+ // Bind the WebAssembly object.
+ Handle<String> name = v8_str(isolate, "WebAssembly");
+ Handle<JSFunction> cons = factory->NewFunction(name);
+ JSFunction::SetInstancePrototype(
+ cons, Handle<Object>(context->initial_object_prototype(), isolate));
+ cons->shared()->set_instance_class_name(*name);
+ Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED);
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
+ JSObject::AddProperty(global, name, wasm_object, attributes);
+
+ // Setup compile
+ InstallFunc(isolate, wasm_object, "compile", WebAssemblyCompile);
+
+ // Setup compile
+ InstallFunc(isolate, wasm_object, "validate", WebAssemblyValidate);
+
+ // Setup Module
+ Handle<JSFunction> module_constructor =
+ InstallFunc(isolate, wasm_object, "Module", WebAssemblyModule);
+ context->set_wasm_module_constructor(*module_constructor);
+ Handle<JSObject> module_proto =
+ factory->NewJSObject(module_constructor, TENURED);
+ i::Handle<i::Map> map = isolate->factory()->NewMap(
+ i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + i::kPointerSize);
+ JSFunction::SetInitialMap(module_constructor, map, module_proto);
+ JSObject::AddProperty(module_proto, isolate->factory()->constructor_string(),
+ module_constructor, DONT_ENUM);
+
+ // Setup Instance
+ Handle<JSFunction> instance_constructor =
+ InstallFunc(isolate, wasm_object, "Instance", WebAssemblyInstance);
+ context->set_wasm_instance_constructor(*instance_constructor);
+
+ // Setup Table
+ Handle<JSFunction> table_constructor =
+ InstallFunc(isolate, wasm_object, "Table", WebAssemblyTable);
+ context->set_wasm_table_constructor(*table_constructor);
+ Handle<JSObject> table_proto =
+ factory->NewJSObject(table_constructor, TENURED);
+ map = isolate->factory()->NewMap(
+ i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 2 * i::kPointerSize);
+ JSFunction::SetInitialMap(table_constructor, map, table_proto);
+ JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(),
+ table_constructor, DONT_ENUM);
+ InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength);
+ InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow);
+ InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet);
+ InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet);
+
+ // Setup Memory
+ Handle<JSFunction> memory_constructor =
+ InstallFunc(isolate, wasm_object, "Memory", WebAssemblyMemory);
+ context->set_wasm_memory_constructor(*memory_constructor);
+ Handle<JSObject> memory_proto =
+ factory->NewJSObject(memory_constructor, TENURED);
+ map = isolate->factory()->NewMap(
+ i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 2 * i::kPointerSize);
+ JSFunction::SetInitialMap(memory_constructor, map, memory_proto);
+ JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(),
+ memory_constructor, DONT_ENUM);
+ InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow);
+ InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer);
+}
+
void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
if (!FLAG_expose_wasm && !FLAG_validate_asm) {
return;
@@ -350,7 +672,7 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
// Setup wasm function map.
Handle<Context> context(global->native_context(), isolate);
- InstallWasmFunctionMap(isolate, context);
+ InstallWasmMapsIfNeeded(isolate, context);
if (!FLAG_expose_wasm) {
return;
@@ -383,39 +705,11 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
JSObject::AddProperty(wasm_object, name, value, attributes);
}
}
-
- // Create private symbols.
- Handle<Symbol> module_sym = isolate->factory()->NewPrivateSymbol();
- Handle<Symbol> instance_sym = isolate->factory()->NewPrivateSymbol();
- context->set_wasm_module_sym(*module_sym);
- context->set_wasm_instance_sym(*instance_sym);
-
- // Bind the WebAssembly object.
- Handle<String> name = v8_str(isolate, "WebAssembly");
- Handle<JSFunction> cons = factory->NewFunction(name);
- JSFunction::SetInstancePrototype(
- cons, Handle<Object>(context->initial_object_prototype(), isolate));
- cons->shared()->set_instance_class_name(*name);
- Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED);
- PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
- JSObject::AddProperty(global, name, wasm_object, attributes);
-
- // Install static methods on WebAssembly object.
- InstallFunc(isolate, wasm_object, "compile", WebAssemblyCompile);
- Handle<JSFunction> module_constructor =
- InstallFunc(isolate, wasm_object, "Module", WebAssemblyModule);
- Handle<JSFunction> instance_constructor =
- InstallFunc(isolate, wasm_object, "Instance", WebAssemblyInstance);
- i::Handle<i::Map> map = isolate->factory()->NewMap(
- i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + i::kPointerSize);
- module_constructor->set_prototype_or_initial_map(*map);
- map->SetConstructor(*module_constructor);
-
- context->set_wasm_module_constructor(*module_constructor);
- context->set_wasm_instance_constructor(*instance_constructor);
+ InstallWasmConstructors(isolate, global, context);
}
-void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) {
+void WasmJs::InstallWasmMapsIfNeeded(Isolate* isolate,
+ Handle<Context> context) {
if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) {
// TODO(titzer): Move this to bootstrapper.cc??
// TODO(titzer): Also make one for strict mode functions?
diff --git a/deps/v8/src/wasm/wasm-js.h b/deps/v8/src/wasm/wasm-js.h
index ded9a1a90b..4f26494624 100644
--- a/deps/v8/src/wasm/wasm-js.h
+++ b/deps/v8/src/wasm/wasm-js.h
@@ -5,13 +5,8 @@
#ifndef V8_WASM_JS_H_
#define V8_WASM_JS_H_
-#ifndef V8_SHARED
#include "src/allocation.h"
#include "src/base/hashmap.h"
-#else
-#include "include/v8.h"
-#include "src/base/compiler-specific.h"
-#endif // !V8_SHARED
namespace v8 {
namespace internal {
@@ -19,7 +14,19 @@ namespace internal {
class WasmJs {
public:
static void Install(Isolate* isolate, Handle<JSGlobalObject> global_object);
- static void InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context);
+
+ V8_EXPORT_PRIVATE static void InstallWasmModuleSymbolIfNeeded(
+ Isolate* isolate, Handle<JSGlobalObject> global, Handle<Context> context);
+
+ V8_EXPORT_PRIVATE static void InstallWasmMapsIfNeeded(
+ Isolate* isolate, Handle<Context> context);
+ static void InstallWasmConstructors(Isolate* isolate,
+ Handle<JSGlobalObject> global,
+ Handle<Context> context);
+
+ static Handle<JSObject> CreateWasmMemoryObject(Isolate* isolate,
+ Handle<JSArrayBuffer> buffer,
+ bool has_maximum, int maximum);
};
} // namespace internal
diff --git a/deps/v8/src/wasm/wasm-macro-gen.h b/deps/v8/src/wasm/wasm-macro-gen.h
index abd57d505a..fd10a3929a 100644
--- a/deps/v8/src/wasm/wasm-macro-gen.h
+++ b/deps/v8/src/wasm/wasm-macro-gen.h
@@ -7,7 +7,7 @@
#include "src/wasm/wasm-opcodes.h"
-#include "src/zone-containers.h"
+#include "src/zone/zone-containers.h"
#define U32_LE(v) \
static_cast<byte>(v), static_cast<byte>((v) >> 8), \
@@ -17,17 +17,17 @@
#define WASM_MODULE_HEADER U32_LE(kWasmMagic), U32_LE(kWasmVersion)
-#define SIG_INDEX(v) U16_LE(v)
-// TODO(binji): make SIG_INDEX match this.
#define IMPORT_SIG_INDEX(v) U32V_1(v)
#define FUNC_INDEX(v) U32V_1(v)
+#define TABLE_INDEX(v) U32V_1(v)
#define NO_NAME U32V_1(0)
#define NAME_LENGTH(v) U32V_1(v)
+#define ENTRY_COUNT(v) U32V_1(v)
#define ZERO_ALIGNMENT 0
#define ZERO_OFFSET 0
-#define BR_TARGET(v) U32_LE(v)
+#define BR_TARGET(v) U32V_1(v)
#define MASK_7 ((1 << 7) - 1)
#define MASK_14 ((1 << 14) - 1)
@@ -62,36 +62,76 @@
#define ARITY_0 0
#define ARITY_1 1
+#define ARITY_2 2
#define DEPTH_0 0
#define DEPTH_1 1
+#define DEPTH_2 2
+#define ARITY_2 2
+
+#define WASM_BLOCK(...) kExprBlock, kLocalVoid, __VA_ARGS__, kExprEnd
+
+#define WASM_BLOCK_T(t, ...) \
+ kExprBlock, static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t)), \
+ __VA_ARGS__, kExprEnd
+
+#define WASM_BLOCK_TT(t1, t2, ...) \
+ kExprBlock, kMultivalBlock, 0, \
+ static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t1)), \
+ static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t2)), __VA_ARGS__, \
+ kExprEnd
+
+#define WASM_BLOCK_I(...) kExprBlock, kLocalI32, __VA_ARGS__, kExprEnd
+#define WASM_BLOCK_L(...) kExprBlock, kLocalI64, __VA_ARGS__, kExprEnd
+#define WASM_BLOCK_F(...) kExprBlock, kLocalF32, __VA_ARGS__, kExprEnd
+#define WASM_BLOCK_D(...) kExprBlock, kLocalF64, __VA_ARGS__, kExprEnd
+
+#define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd
+
+#define WASM_LOOP(...) kExprLoop, kLocalVoid, __VA_ARGS__, kExprEnd
+#define WASM_LOOP_I(...) kExprLoop, kLocalI32, __VA_ARGS__, kExprEnd
+#define WASM_LOOP_L(...) kExprLoop, kLocalI64, __VA_ARGS__, kExprEnd
+#define WASM_LOOP_F(...) kExprLoop, kLocalF32, __VA_ARGS__, kExprEnd
+#define WASM_LOOP_D(...) kExprLoop, kLocalF64, __VA_ARGS__, kExprEnd
+
+#define WASM_IF(cond, tstmt) cond, kExprIf, kLocalVoid, tstmt, kExprEnd
-#define WASM_BLOCK(...) kExprBlock, __VA_ARGS__, kExprEnd
-#define WASM_INFINITE_LOOP kExprLoop, kExprBr, ARITY_0, DEPTH_0, kExprEnd
-#define WASM_LOOP(...) kExprLoop, __VA_ARGS__, kExprEnd
-#define WASM_IF(cond, tstmt) cond, kExprIf, tstmt, kExprEnd
#define WASM_IF_ELSE(cond, tstmt, fstmt) \
- cond, kExprIf, tstmt, kExprElse, fstmt, kExprEnd
+ cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd
+
+#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \
+ cond, kExprIf, static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t)), tstmt, \
+ kExprElse, fstmt, kExprEnd
+
+#define WASM_IF_ELSE_TT(t1, t2, cond, tstmt, fstmt) \
+ cond, kExprIf, kMultivalBlock, 0, \
+ static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t1)), \
+ static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t2)), tstmt, kExprElse, \
+ fstmt, kExprEnd
+
+#define WASM_IF_ELSE_I(cond, tstmt, fstmt) \
+ cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd
+#define WASM_IF_ELSE_L(cond, tstmt, fstmt) \
+ cond, kExprIf, kLocalI64, tstmt, kExprElse, fstmt, kExprEnd
+#define WASM_IF_ELSE_F(cond, tstmt, fstmt) \
+ cond, kExprIf, kLocalF32, tstmt, kExprElse, fstmt, kExprEnd
+#define WASM_IF_ELSE_D(cond, tstmt, fstmt) \
+ cond, kExprIf, kLocalF64, tstmt, kExprElse, fstmt, kExprEnd
+
#define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect
-#define WASM_BR(depth) kExprBr, ARITY_0, static_cast<byte>(depth)
-#define WASM_BR_IF(depth, cond) \
- cond, kExprBrIf, ARITY_0, static_cast<byte>(depth)
-#define WASM_BRV(depth, val) val, kExprBr, ARITY_1, static_cast<byte>(depth)
-#define WASM_BRV_IF(depth, val, cond) \
- val, cond, kExprBrIf, ARITY_1, static_cast<byte>(depth)
-#define WASM_BREAK(depth) kExprBr, ARITY_0, static_cast<byte>(depth + 1)
-#define WASM_CONTINUE(depth) kExprBr, ARITY_0, static_cast<byte>(depth)
-#define WASM_BREAKV(depth, val) \
- val, kExprBr, ARITY_1, static_cast<byte>(depth + 1)
-#define WASM_RETURN0 kExprReturn, ARITY_0
-#define WASM_RETURN1(val) val, kExprReturn, ARITY_1
-#define WASM_RETURNN(count, ...) __VA_ARGS__, kExprReturn, count
+
+#define WASM_RETURN0 kExprReturn
+#define WASM_RETURN1(val) val, kExprReturn
+#define WASM_RETURNN(count, ...) __VA_ARGS__, kExprReturn
+
+#define WASM_BR(depth) kExprBr, static_cast<byte>(depth)
+#define WASM_BR_IF(depth, cond) cond, kExprBrIf, static_cast<byte>(depth)
+#define WASM_BR_IFD(depth, val, cond) \
+ val, cond, kExprBrIf, static_cast<byte>(depth), kExprDrop
+#define WASM_CONTINUE(depth) kExprBr, static_cast<byte>(depth)
#define WASM_UNREACHABLE kExprUnreachable
#define WASM_BR_TABLE(key, count, ...) \
- key, kExprBrTable, ARITY_0, U32V_1(count), __VA_ARGS__
-
-#define WASM_BR_TABLEV(val, key, count, ...) \
- val, key, kExprBrTable, ARITY_1, U32V_1(count), __VA_ARGS__
+ key, kExprBrTable, U32V_1(count), __VA_ARGS__
#define WASM_CASE(x) static_cast<byte>(x), static_cast<byte>(x >> 8)
#define WASM_CASE_BR(x) static_cast<byte>(x), static_cast<byte>(0x80 | (x) >> 8)
@@ -343,6 +383,8 @@ class LocalDeclEncoder {
static_cast<byte>(bit_cast<uint64_t>(val) >> 56)
#define WASM_GET_LOCAL(index) kExprGetLocal, static_cast<byte>(index)
#define WASM_SET_LOCAL(index, val) val, kExprSetLocal, static_cast<byte>(index)
+#define WASM_TEE_LOCAL(index, val) val, kExprTeeLocal, static_cast<byte>(index)
+#define WASM_DROP kExprDrop
#define WASM_GET_GLOBAL(index) kExprGetGlobal, static_cast<byte>(index)
#define WASM_SET_GLOBAL(index, val) \
val, kExprSetGlobal, static_cast<byte>(index)
@@ -374,49 +416,25 @@ class LocalDeclEncoder {
v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \
alignment, ZERO_OFFSET
-#define WASM_CALL_FUNCTION0(index) \
- kExprCallFunction, 0, static_cast<byte>(index)
-#define WASM_CALL_FUNCTION1(index, a) \
- a, kExprCallFunction, 1, static_cast<byte>(index)
-#define WASM_CALL_FUNCTION2(index, a, b) \
- a, b, kExprCallFunction, 2, static_cast<byte>(index)
-#define WASM_CALL_FUNCTION3(index, a, b, c) \
- a, b, c, kExprCallFunction, 3, static_cast<byte>(index)
-#define WASM_CALL_FUNCTION4(index, a, b, c, d) \
- a, b, c, d, kExprCallFunction, 4, static_cast<byte>(index)
-#define WASM_CALL_FUNCTION5(index, a, b, c, d, e) \
- kExprCallFunction, 5, static_cast<byte>(index)
-#define WASM_CALL_FUNCTIONN(arity, index, ...) \
- __VA_ARGS__, kExprCallFunction, arity, static_cast<byte>(index)
-
-#define WASM_CALL_IMPORT0(index) kExprCallImport, 0, static_cast<byte>(index)
-#define WASM_CALL_IMPORT1(index, a) \
- a, kExprCallImport, 1, static_cast<byte>(index)
-#define WASM_CALL_IMPORT2(index, a, b) \
- a, b, kExprCallImport, 2, static_cast<byte>(index)
-#define WASM_CALL_IMPORT3(index, a, b, c) \
- a, b, c, kExprCallImport, 3, static_cast<byte>(index)
-#define WASM_CALL_IMPORT4(index, a, b, c, d) \
- a, b, c, d, kExprCallImport, 4, static_cast<byte>(index)
-#define WASM_CALL_IMPORT5(index, a, b, c, d, e) \
- a, b, c, d, e, kExprCallImport, 5, static_cast<byte>(index)
-#define WASM_CALL_IMPORTN(arity, index, ...) \
- __VA_ARGS__, kExprCallImport, U32V_1(arity), static_cast<byte>(index),
+#define WASM_CALL_FUNCTION0(index) kExprCallFunction, static_cast<byte>(index)
+#define WASM_CALL_FUNCTION(index, ...) \
+ __VA_ARGS__, kExprCallFunction, static_cast<byte>(index)
+// TODO(titzer): change usages of these macros to put func last.
#define WASM_CALL_INDIRECT0(index, func) \
- func, kExprCallIndirect, 0, static_cast<byte>(index)
+ func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT1(index, func, a) \
- func, a, kExprCallIndirect, 1, static_cast<byte>(index)
+ a, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT2(index, func, a, b) \
- func, a, b, kExprCallIndirect, 2, static_cast<byte>(index)
+ a, b, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT3(index, func, a, b, c) \
- func, a, b, c, kExprCallIndirect, 3, static_cast<byte>(index)
+ a, b, c, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT4(index, func, a, b, c, d) \
- func, a, b, c, d, kExprCallIndirect, 4, static_cast<byte>(index)
+ a, b, c, d, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT5(index, func, a, b, c, d, e) \
- func, a, b, c, d, e, kExprCallIndirect, 5, static_cast<byte>(index)
+ a, b, c, d, e, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECTN(arity, index, func, ...) \
- func, __VA_ARGS__, kExprCallIndirect, U32V_1(arity), static_cast<byte>(index)
+ __VA_ARGS__, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_NOT(x) x, kExprI32Eqz
#define WASM_SEQ(...) __VA_ARGS__
@@ -424,11 +442,16 @@ class LocalDeclEncoder {
//------------------------------------------------------------------------------
// Constructs that are composed of multiple bytecodes.
//------------------------------------------------------------------------------
-#define WASM_WHILE(x, y) \
- kExprLoop, x, kExprIf, y, kExprBr, ARITY_1, DEPTH_1, kExprEnd, kExprEnd
+#define WASM_WHILE(x, y) \
+ kExprLoop, kLocalVoid, x, kExprIf, kLocalVoid, y, kExprBr, DEPTH_1, \
+ kExprEnd, kExprEnd
#define WASM_INC_LOCAL(index) \
kExprGetLocal, static_cast<byte>(index), kExprI8Const, 1, kExprI32Add, \
- kExprSetLocal, static_cast<byte>(index)
+ kExprTeeLocal, static_cast<byte>(index)
+#define WASM_INC_LOCAL_BYV(index, count) \
+ kExprGetLocal, static_cast<byte>(index), kExprI8Const, \
+ static_cast<byte>(count), kExprI32Add, kExprTeeLocal, \
+ static_cast<byte>(index)
#define WASM_INC_LOCAL_BY(index, count) \
kExprGetLocal, static_cast<byte>(index), kExprI8Const, \
static_cast<byte>(count), kExprI32Add, kExprSetLocal, \
@@ -580,11 +603,17 @@ class LocalDeclEncoder {
#define WASM_I64_REINTERPRET_F64(x) x, kExprI64ReinterpretF64
//------------------------------------------------------------------------------
+// Memory Operations.
+//------------------------------------------------------------------------------
+#define WASM_GROW_MEMORY(x) x, kExprGrowMemory
+#define WASM_MEMORY_SIZE kExprMemorySize
+
+//------------------------------------------------------------------------------
// Simd Operations.
//------------------------------------------------------------------------------
#define WASM_SIMD_I32x4_SPLAT(x) x, kSimdPrefix, kExprI32x4Splat & 0xff
-#define WASM_SIMD_I32x4_EXTRACT_LANE(x, y) \
- x, y, kSimdPrefix, kExprI32x4ExtractLane & 0xff
+#define WASM_SIMD_I32x4_EXTRACT_LANE(lane, x) \
+ x, kSimdPrefix, kExprI32x4ExtractLane & 0xff, static_cast<byte>(lane)
#define SIG_ENTRY_v_v kWasmFunctionTypeForm, 0, 0
#define SIZEOF_SIG_ENTRY_v_v 3
@@ -605,4 +634,13 @@ class LocalDeclEncoder {
#define SIZEOF_SIG_ENTRY_x_xx 6
#define SIZEOF_SIG_ENTRY_x_xxx 7
+#define WASM_BRV(depth, val) val, kExprBr, static_cast<byte>(depth)
+#define WASM_BRV_IF(depth, val, cond) \
+ val, cond, kExprBrIf, static_cast<byte>(depth)
+#define WASM_BRV_IFD(depth, val, cond) \
+ val, cond, kExprBrIf, static_cast<byte>(depth), kExprDrop
+#define WASM_IFB(cond, ...) cond, kExprIf, kLocalVoid, __VA_ARGS__, kExprEnd
+#define WASM_BR_TABLEV(val, key, count, ...) \
+ val, key, kExprBrTable, U32V_1(count), __VA_ARGS__
+
#endif // V8_WASM_MACRO_GEN_H_
diff --git a/deps/v8/src/wasm/encoder.cc b/deps/v8/src/wasm/wasm-module-builder.cc
index ef0bddc836..084f5a0c1a 100644
--- a/deps/v8/src/wasm/encoder.cc
+++ b/deps/v8/src/wasm/wasm-module-builder.cc
@@ -6,12 +6,12 @@
#include "src/handles.h"
#include "src/v8.h"
-#include "src/zone-containers.h"
+#include "src/zone/zone-containers.h"
#include "src/wasm/ast-decoder.h"
-#include "src/wasm/encoder.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/wasm-macro-gen.h"
+#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
@@ -30,15 +30,11 @@ namespace v8 {
namespace internal {
namespace wasm {
-// Emit a section name and the size as a padded varint that can be patched
+// Emit a section code and the size as a padded varint that can be patched
// later.
-size_t EmitSection(WasmSection::Code code, ZoneBuffer& buffer) {
- // Emit the section name.
- const char* name = WasmSection::getName(code);
- TRACE("emit section: %s\n", name);
- size_t length = WasmSection::getNameLength(code);
- buffer.write_size(length); // Section name string size.
- buffer.write(reinterpret_cast<const byte*>(name), length);
+size_t EmitSection(WasmSectionCode code, ZoneBuffer& buffer) {
+ // Emit the section code.
+ buffer.write_u8(code);
// Emit a placeholder for the length.
return buffer.reserve_u32v();
@@ -55,8 +51,14 @@ WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
locals_(builder->zone()),
signature_index_(0),
exported_(0),
+ func_index_(static_cast<uint32_t>(builder->functions_.size())),
body_(builder->zone()),
- name_(builder->zone()) {}
+ name_(builder->zone()),
+ i32_temps_(builder->zone()),
+ i64_temps_(builder->zone()),
+ f32_temps_(builder->zone()),
+ f64_temps_(builder->zone()),
+ direct_calls_(builder->zone()) {}
void WasmFunctionBuilder::EmitVarInt(uint32_t val) {
byte buffer[8];
@@ -86,6 +88,10 @@ void WasmFunctionBuilder::EmitSetLocal(uint32_t local_index) {
EmitWithVarInt(kExprSetLocal, local_index);
}
+void WasmFunctionBuilder::EmitTeeLocal(uint32_t local_index) {
+ EmitWithVarInt(kExprTeeLocal, local_index);
+}
+
void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size) {
for (size_t i = 0; i < code_size; ++i) {
body_.push_back(code[i]);
@@ -124,6 +130,15 @@ void WasmFunctionBuilder::EmitI32Const(int32_t value) {
}
}
+void WasmFunctionBuilder::EmitDirectCallIndex(uint32_t index) {
+ DirectCallIndex call;
+ call.offset = body_.size();
+ call.direct_index = index;
+ direct_calls_.push_back(call);
+ byte code[] = {U32V_5(0)};
+ EmitCode(code, sizeof(code));
+}
+
void WasmFunctionBuilder::SetExported() { exported_ = true; }
void WasmFunctionBuilder::SetName(const char* name, int name_length) {
@@ -139,14 +154,15 @@ void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
buffer.write_u32v(signature_index_);
}
-void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer,
- uint32_t func_index) const {
+void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer) const {
if (exported_) {
- buffer.write_u32v(func_index);
buffer.write_size(name_.size());
if (name_.size() > 0) {
buffer.write(reinterpret_cast<const byte*>(&name_[0]), name_.size());
}
+ buffer.write_u8(kExternalFunction);
+ buffer.write_u32v(func_index_ +
+ static_cast<uint32_t>(builder_->imports_.size()));
}
}
@@ -158,24 +174,16 @@ void WasmFunctionBuilder::WriteBody(ZoneBuffer& buffer) const {
locals_.Emit(*ptr);
(*ptr) += locals_size; // UGLY: manual bump of position pointer
if (body_.size() > 0) {
+ size_t base = buffer.offset();
buffer.write(&body_[0], body_.size());
+ for (DirectCallIndex call : direct_calls_) {
+ buffer.patch_u32v(
+ base + call.offset,
+ call.direct_index + static_cast<uint32_t>(builder_->imports_.size()));
+ }
}
}
-WasmDataSegmentEncoder::WasmDataSegmentEncoder(Zone* zone, const byte* data,
- uint32_t size, uint32_t dest)
- : data_(zone), dest_(dest) {
- for (size_t i = 0; i < size; ++i) {
- data_.push_back(data[i]);
- }
-}
-
-void WasmDataSegmentEncoder::Write(ZoneBuffer& buffer) const {
- buffer.write_u32v(dest_);
- buffer.write_u32v(static_cast<uint32_t>(data_.size()));
- buffer.write(&data_[0], data_.size());
-}
-
WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
: zone_(zone),
signatures_(zone),
@@ -187,23 +195,22 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
signature_map_(zone),
start_function_index_(-1) {}
-uint32_t WasmModuleBuilder::AddFunction() {
+WasmFunctionBuilder* WasmModuleBuilder::AddFunction(FunctionSig* sig) {
functions_.push_back(new (zone_) WasmFunctionBuilder(this));
- return static_cast<uint32_t>(functions_.size() - 1);
+ // Add the signature if one was provided here.
+ if (sig) functions_.back()->SetSignature(sig);
+ return functions_.back();
}
-WasmFunctionBuilder* WasmModuleBuilder::FunctionAt(size_t index) {
- if (functions_.size() > index) {
- return functions_.at(index);
- } else {
- return nullptr;
+void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
+ uint32_t dest) {
+ data_segments_.push_back({ZoneVector<byte>(zone()), dest});
+ ZoneVector<byte>& vec = data_segments_.back().data;
+ for (uint32_t i = 0; i < size; i++) {
+ vec.push_back(data[i]);
}
}
-void WasmModuleBuilder::AddDataSegment(WasmDataSegmentEncoder* data) {
- data_segments_.push_back(data);
-}
-
bool WasmModuleBuilder::CompareFunctionSigs::operator()(FunctionSig* a,
FunctionSig* b) const {
if (a->return_count() < b->return_count()) return true;
@@ -243,12 +250,13 @@ uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length,
return static_cast<uint32_t>(imports_.size() - 1);
}
-void WasmModuleBuilder::MarkStartFunction(uint32_t index) {
- start_function_index_ = index;
+void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) {
+ start_function_index_ = function->func_index();
}
-uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported) {
- globals_.push_back(std::make_pair(type, exported));
+uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported,
+ bool mutability) {
+ globals_.push_back({type, exported, mutability});
return static_cast<uint32_t>(globals_.size() - 1);
}
@@ -262,7 +270,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
// == Emit signatures ========================================================
if (signatures_.size() > 0) {
- size_t start = EmitSection(WasmSection::Code::Signatures, buffer);
+ size_t start = EmitSection(kTypeSectionCode, buffer);
buffer.write_size(signatures_.size());
for (FunctionSig* sig : signatures_) {
@@ -279,86 +287,128 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
FixupSection(buffer, start);
}
- // == Emit globals ===========================================================
- if (globals_.size() > 0) {
- size_t start = EmitSection(WasmSection::Code::Globals, buffer);
- buffer.write_size(globals_.size());
-
- for (auto global : globals_) {
- buffer.write_u32v(0); // Length of the global name.
- buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(global.first));
- buffer.write_u8(global.second);
- }
- FixupSection(buffer, start);
- }
-
// == Emit imports ===========================================================
if (imports_.size() > 0) {
- size_t start = EmitSection(WasmSection::Code::ImportTable, buffer);
+ size_t start = EmitSection(kImportSectionCode, buffer);
buffer.write_size(imports_.size());
for (auto import : imports_) {
- buffer.write_u32v(import.sig_index);
- buffer.write_u32v(import.name_length);
- buffer.write(reinterpret_cast<const byte*>(import.name),
+ buffer.write_u32v(import.name_length); // module name length
+ buffer.write(reinterpret_cast<const byte*>(import.name), // module name
import.name_length);
- buffer.write_u32v(0);
+ buffer.write_u32v(0); // field name length
+ buffer.write_u8(kExternalFunction);
+ buffer.write_u32v(import.sig_index);
}
FixupSection(buffer, start);
}
// == Emit function signatures ===============================================
+ bool has_names = false;
if (functions_.size() > 0) {
- size_t start = EmitSection(WasmSection::Code::FunctionSignatures, buffer);
+ size_t start = EmitSection(kFunctionSectionCode, buffer);
buffer.write_size(functions_.size());
for (auto function : functions_) {
function->WriteSignature(buffer);
if (function->exported()) exports++;
+ if (function->name_.size() > 0) has_names = true;
}
FixupSection(buffer, start);
}
// == emit function table ====================================================
if (indirect_functions_.size() > 0) {
- size_t start = EmitSection(WasmSection::Code::FunctionTable, buffer);
+ size_t start = EmitSection(kTableSectionCode, buffer);
+ buffer.write_u8(1); // table count
+ buffer.write_u8(kWasmAnyFunctionTypeForm);
+ buffer.write_u8(kResizableMaximumFlag);
+ buffer.write_size(indirect_functions_.size());
buffer.write_size(indirect_functions_.size());
-
- for (auto index : indirect_functions_) {
- buffer.write_u32v(index);
- }
FixupSection(buffer, start);
}
// == emit memory declaration ================================================
{
- size_t start = EmitSection(WasmSection::Code::Memory, buffer);
+ size_t start = EmitSection(kMemorySectionCode, buffer);
+ buffer.write_u8(1); // memory count
+ buffer.write_u32v(kResizableMaximumFlag);
buffer.write_u32v(16); // min memory size
buffer.write_u32v(16); // max memory size
- buffer.write_u8(0); // memory export
- static_assert(kDeclMemorySize == 3, "memory size must match emit above");
+ FixupSection(buffer, start);
+ }
+
+ // == Emit globals ===========================================================
+ if (globals_.size() > 0) {
+ size_t start = EmitSection(kGlobalSectionCode, buffer);
+ buffer.write_size(globals_.size());
+
+ for (auto global : globals_) {
+ buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(global.type));
+ buffer.write_u8(global.mutability ? 1 : 0);
+ switch (global.type) {
+ case kAstI32: {
+ static const byte code[] = {WASM_I32V_1(0)};
+ buffer.write(code, sizeof(code));
+ break;
+ }
+ case kAstF32: {
+ static const byte code[] = {WASM_F32(0)};
+ buffer.write(code, sizeof(code));
+ break;
+ }
+ case kAstI64: {
+ static const byte code[] = {WASM_I64V_1(0)};
+ buffer.write(code, sizeof(code));
+ break;
+ }
+ case kAstF64: {
+ static const byte code[] = {WASM_F64(0.0)};
+ buffer.write(code, sizeof(code));
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ buffer.write_u8(kExprEnd);
+ }
FixupSection(buffer, start);
}
// == emit exports ===========================================================
if (exports > 0) {
- size_t start = EmitSection(WasmSection::Code::ExportTable, buffer);
+ size_t start = EmitSection(kExportSectionCode, buffer);
buffer.write_u32v(exports);
- uint32_t index = 0;
- for (auto function : functions_) {
- function->WriteExport(buffer, index++);
- }
+ for (auto function : functions_) function->WriteExport(buffer);
FixupSection(buffer, start);
}
// == emit start function index ==============================================
if (start_function_index_ >= 0) {
- size_t start = EmitSection(WasmSection::Code::StartFunction, buffer);
- buffer.write_u32v(start_function_index_);
+ size_t start = EmitSection(kStartSectionCode, buffer);
+ buffer.write_u32v(start_function_index_ +
+ static_cast<uint32_t>(imports_.size()));
+ FixupSection(buffer, start);
+ }
+
+ // == emit function table elements ===========================================
+ if (indirect_functions_.size() > 0) {
+ size_t start = EmitSection(kElementSectionCode, buffer);
+ buffer.write_u8(1); // count of entries
+ buffer.write_u8(0); // table index
+ buffer.write_u8(kExprI32Const); // offset
+ buffer.write_u32v(0);
+ buffer.write_u8(kExprEnd);
+ buffer.write_size(indirect_functions_.size()); // element count
+
+ for (auto index : indirect_functions_) {
+ buffer.write_u32v(index + static_cast<uint32_t>(imports_.size()));
+ }
+
FixupSection(buffer, start);
}
// == emit code ==============================================================
if (functions_.size() > 0) {
- size_t start = EmitSection(WasmSection::Code::FunctionBodies, buffer);
+ size_t start = EmitSection(kCodeSectionCode, buffer);
buffer.write_size(functions_.size());
for (auto function : functions_) {
function->WriteBody(buffer);
@@ -368,11 +418,38 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
// == emit data segments =====================================================
if (data_segments_.size() > 0) {
- size_t start = EmitSection(WasmSection::Code::DataSegments, buffer);
+ size_t start = EmitSection(kDataSectionCode, buffer);
buffer.write_size(data_segments_.size());
for (auto segment : data_segments_) {
- segment->Write(buffer);
+ buffer.write_u8(0); // linear memory segment
+ buffer.write_u8(kExprI32Const); // initializer expression for dest
+ buffer.write_u32v(segment.dest);
+ buffer.write_u8(kExprEnd);
+ buffer.write_u32v(static_cast<uint32_t>(segment.data.size()));
+ buffer.write(&segment.data[0], segment.data.size());
+ }
+ FixupSection(buffer, start);
+ }
+
+ // == Emit names =============================================================
+ if (has_names) {
+ // Emit the section code.
+ buffer.write_u8(kUnknownSectionCode);
+ // Emit a placeholder for the length.
+ size_t start = buffer.reserve_u32v();
+ // Emit the section string.
+ buffer.write_size(4);
+ buffer.write(reinterpret_cast<const byte*>("name"), 4);
+ // Emit the names.
+ buffer.write_size(functions_.size());
+ for (auto function : functions_) {
+ buffer.write_size(function->name_.size());
+ if (function->name_.size() > 0) {
+ buffer.write(reinterpret_cast<const byte*>(&function->name_[0]),
+ function->name_.size());
+ }
+ buffer.write_u8(0);
}
FixupSection(buffer, start);
}
diff --git a/deps/v8/src/wasm/encoder.h b/deps/v8/src/wasm/wasm-module-builder.h
index eb8aa64abd..dcaf6c8e86 100644
--- a/deps/v8/src/wasm/encoder.h
+++ b/deps/v8/src/wasm/wasm-module-builder.h
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef V8_WASM_ENCODER_H_
-#define V8_WASM_ENCODER_H_
+#ifndef V8_WASM_WASM_MODULE_BUILDER_H_
+#define V8_WASM_WASM_MODULE_BUILDER_H_
#include "src/signature.h"
-#include "src/zone-containers.h"
+#include "src/zone/zone-containers.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/wasm-macro-gen.h"
@@ -90,13 +90,14 @@ class ZoneBuffer : public ZoneObject {
void EnsureSpace(size_t size) {
if ((pos_ + size) > end_) {
- size_t new_size = 4096 + (end_ - buffer_) * 3;
+ size_t new_size = 4096 + size + (end_ - buffer_) * 3;
byte* new_buffer = reinterpret_cast<byte*>(zone_->New(new_size));
memcpy(new_buffer, buffer_, (pos_ - buffer_));
pos_ = new_buffer + (pos_ - buffer_);
buffer_ = new_buffer;
end_ = new_buffer + new_size;
}
+ DCHECK(pos_ + size <= end_);
}
byte** pos_ptr() { return &pos_; }
@@ -110,7 +111,7 @@ class ZoneBuffer : public ZoneObject {
class WasmModuleBuilder;
-class WasmFunctionBuilder : public ZoneObject {
+class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
public:
// Building methods.
void SetSignature(FunctionSig* sig);
@@ -120,61 +121,102 @@ class WasmFunctionBuilder : public ZoneObject {
void Emit(WasmOpcode opcode);
void EmitGetLocal(uint32_t index);
void EmitSetLocal(uint32_t index);
+ void EmitTeeLocal(uint32_t index);
void EmitI32Const(int32_t val);
void EmitWithU8(WasmOpcode opcode, const byte immediate);
void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate);
+ void EmitDirectCallIndex(uint32_t index);
void SetExported();
void SetName(const char* name, int name_length);
- bool exported() { return exported_; }
- // Writing methods.
void WriteSignature(ZoneBuffer& buffer) const;
- void WriteExport(ZoneBuffer& buffer, uint32_t func_index) const;
+ void WriteExport(ZoneBuffer& buffer) const;
void WriteBody(ZoneBuffer& buffer) const;
+ bool exported() { return exported_; }
+ uint32_t func_index() { return func_index_; }
+ FunctionSig* signature();
+
private:
explicit WasmFunctionBuilder(WasmModuleBuilder* builder);
friend class WasmModuleBuilder;
+ friend class WasmTemporary;
+
+ struct DirectCallIndex {
+ size_t offset;
+ uint32_t direct_index;
+ };
+
WasmModuleBuilder* builder_;
LocalDeclEncoder locals_;
uint32_t signature_index_;
bool exported_;
+ uint32_t func_index_;
ZoneVector<uint8_t> body_;
ZoneVector<char> name_;
+ ZoneVector<uint32_t> i32_temps_;
+ ZoneVector<uint32_t> i64_temps_;
+ ZoneVector<uint32_t> f32_temps_;
+ ZoneVector<uint32_t> f64_temps_;
+ ZoneVector<DirectCallIndex> direct_calls_;
};
-// TODO(titzer): kill!
-class WasmDataSegmentEncoder : public ZoneObject {
+class WasmTemporary {
public:
- WasmDataSegmentEncoder(Zone* zone, const byte* data, uint32_t size,
- uint32_t dest);
- void Write(ZoneBuffer& buffer) const;
+ WasmTemporary(WasmFunctionBuilder* builder, LocalType type) {
+ switch (type) {
+ case kAstI32:
+ temporary_ = &builder->i32_temps_;
+ break;
+ case kAstI64:
+ temporary_ = &builder->i64_temps_;
+ break;
+ case kAstF32:
+ temporary_ = &builder->f32_temps_;
+ break;
+ case kAstF64:
+ temporary_ = &builder->f64_temps_;
+ break;
+ default:
+ UNREACHABLE();
+ temporary_ = nullptr;
+ }
+ if (temporary_->size() == 0) {
+ // Allocate a new temporary.
+ index_ = builder->AddLocal(type);
+ } else {
+ // Reuse a previous temporary.
+ index_ = temporary_->back();
+ temporary_->pop_back();
+ }
+ }
+ ~WasmTemporary() {
+ temporary_->push_back(index_); // return the temporary to the list.
+ }
+ uint32_t index() { return index_; }
private:
- ZoneVector<byte> data_;
- uint32_t dest_;
-};
-
-struct WasmFunctionImport {
- uint32_t sig_index;
- const char* name;
- int name_length;
+ ZoneVector<uint32_t>* temporary_;
+ uint32_t index_;
};
-class WasmModuleBuilder : public ZoneObject {
+class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
public:
explicit WasmModuleBuilder(Zone* zone);
// Building methods.
- uint32_t AddFunction();
- uint32_t AddGlobal(LocalType type, bool exported);
- WasmFunctionBuilder* FunctionAt(size_t index);
- void AddDataSegment(WasmDataSegmentEncoder* data);
+ uint32_t AddImport(const char* name, int name_length, FunctionSig* sig);
+ void SetImportName(uint32_t index, const char* name, int name_length) {
+ imports_[index].name = name;
+ imports_[index].name_length = name_length;
+ }
+ WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr);
+ uint32_t AddGlobal(LocalType type, bool exported, bool mutability = true);
+ void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
uint32_t AddSignature(FunctionSig* sig);
void AddIndirectFunction(uint32_t index);
- void MarkStartFunction(uint32_t index);
- uint32_t AddImport(const char* name, int name_length, FunctionSig* sig);
+ void MarkStartFunction(WasmFunctionBuilder* builder);
// Writing methods.
void WriteTo(ZoneBuffer& buffer) const;
@@ -186,20 +228,44 @@ class WasmModuleBuilder : public ZoneObject {
Zone* zone() { return zone_; }
+ FunctionSig* GetSignature(uint32_t index) { return signatures_[index]; }
+
private:
+ struct WasmFunctionImport {
+ uint32_t sig_index;
+ const char* name;
+ int name_length;
+ };
+
+ struct WasmGlobal {
+ LocalType type;
+ bool exported;
+ bool mutability;
+ };
+
+ struct WasmDataSegment {
+ ZoneVector<byte> data;
+ uint32_t dest;
+ };
+
+ friend class WasmFunctionBuilder;
Zone* zone_;
ZoneVector<FunctionSig*> signatures_;
ZoneVector<WasmFunctionImport> imports_;
ZoneVector<WasmFunctionBuilder*> functions_;
- ZoneVector<WasmDataSegmentEncoder*> data_segments_;
+ ZoneVector<WasmDataSegment> data_segments_;
ZoneVector<uint32_t> indirect_functions_;
- ZoneVector<std::pair<LocalType, bool>> globals_;
+ ZoneVector<WasmGlobal> globals_;
SignatureMap signature_map_;
int start_function_index_;
};
+inline FunctionSig* WasmFunctionBuilder::signature() {
+ return builder_->signatures_[signature_index_];
+}
+
} // namespace wasm
} // namespace internal
} // namespace v8
-#endif // V8_WASM_ENCODER_H_
+#endif // V8_WASM_WASM_MODULE_BUILDER_H_
diff --git a/deps/v8/src/wasm/wasm-module.cc b/deps/v8/src/wasm/wasm-module.cc
index 94bf998e53..f4cf505f5a 100644
--- a/deps/v8/src/wasm/wasm-module.cc
+++ b/deps/v8/src/wasm/wasm-module.cc
@@ -18,6 +18,7 @@
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-debug.h"
#include "src/wasm/wasm-function-name-table.h"
+#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-result.h"
@@ -27,179 +28,56 @@ namespace v8 {
namespace internal {
namespace wasm {
-enum JSFunctionExportInternalField {
- kInternalModuleInstance,
- kInternalArity,
- kInternalSignature
-};
+#define TRACE(...) \
+ do { \
+ if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \
+ } while (false)
-static const int kPlaceholderMarker = 1000000000;
+#define TRACE_CHAIN(instance) \
+ do { \
+ instance->PrintInstancesChain(); \
+ } while (false)
-static const char* wasmSections[] = {
-#define F(enumerator, order, string) string,
- FOR_EACH_WASM_SECTION_TYPE(F)
-#undef F
- "<unknown>" // entry for "Max"
-};
+namespace {
-static uint8_t wasmSectionsLengths[]{
-#define F(enumerator, order, string) sizeof(string) - 1,
- FOR_EACH_WASM_SECTION_TYPE(F)
-#undef F
- 9 // entry for "Max"
-};
+static const int kPlaceholderMarker = 1000000000;
-static uint8_t wasmSectionsOrders[]{
-#define F(enumerator, order, string) order,
- FOR_EACH_WASM_SECTION_TYPE(F)
-#undef F
- 0 // entry for "Max"
+enum JSFunctionExportInternalField {
+ kInternalModuleInstance,
+ kInternalArity,
+ kInternalSignature
};
-static_assert(sizeof(wasmSections) / sizeof(wasmSections[0]) ==
- (size_t)WasmSection::Code::Max + 1,
- "expected enum WasmSection::Code to be monotonic from 0");
-
-WasmSection::Code WasmSection::begin() { return (WasmSection::Code)0; }
-WasmSection::Code WasmSection::end() { return WasmSection::Code::Max; }
-WasmSection::Code WasmSection::next(WasmSection::Code code) {
- return (WasmSection::Code)(1 + (uint32_t)code);
-}
-
-const char* WasmSection::getName(WasmSection::Code code) {
- return wasmSections[(size_t)code];
-}
-
-size_t WasmSection::getNameLength(WasmSection::Code code) {
- return wasmSectionsLengths[(size_t)code];
-}
-
-int WasmSection::getOrder(WasmSection::Code code) {
- return wasmSectionsOrders[(size_t)code];
-}
-
-WasmSection::Code WasmSection::lookup(const byte* string, uint32_t length) {
- // TODO(jfb) Linear search, it may be better to do a common-prefix search.
- for (Code i = begin(); i != end(); i = next(i)) {
- if (getNameLength(i) == length && 0 == memcmp(getName(i), string, length)) {
- return i;
- }
- }
- return Code::Max;
-}
-
-std::ostream& operator<<(std::ostream& os, const WasmModule& module) {
- os << "WASM module with ";
- os << (module.min_mem_pages * module.kPageSize) << " min mem";
- os << (module.max_mem_pages * module.kPageSize) << " max mem";
- os << module.functions.size() << " functions";
- os << module.functions.size() << " globals";
- os << module.functions.size() << " data segments";
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const WasmFunction& function) {
- os << "WASM function with signature " << *function.sig;
-
- os << " code bytes: "
- << (function.code_end_offset - function.code_start_offset);
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) {
- os << "#" << pair.function_->func_index << ":";
- if (pair.function_->name_offset > 0) {
- if (pair.module_) {
- WasmName name = pair.module_->GetName(pair.function_->name_offset,
- pair.function_->name_length);
- os.write(name.start(), name.length());
- } else {
- os << "+" << pair.function_->func_index;
- }
- } else {
- os << "?";
- }
- return os;
-}
-
-Handle<JSFunction> WrapExportCodeAsJSFunction(
- Isolate* isolate, Handle<Code> export_code, Handle<String> name, int arity,
- MaybeHandle<ByteArray> maybe_signature, Handle<JSObject> module_instance) {
- Handle<SharedFunctionInfo> shared =
- isolate->factory()->NewSharedFunctionInfo(name, export_code, false);
- shared->set_length(arity);
- shared->set_internal_formal_parameter_count(arity);
- Handle<JSFunction> function = isolate->factory()->NewFunction(
- isolate->wasm_function_map(), name, export_code);
- function->set_shared(*shared);
-
- function->SetInternalField(kInternalModuleInstance, *module_instance);
- // add another Internal Field as the function arity
- function->SetInternalField(kInternalArity, Smi::FromInt(arity));
- // add another Internal Field as the signature of the foreign function
- Handle<ByteArray> signature;
- if (maybe_signature.ToHandle(&signature)) {
- function->SetInternalField(kInternalSignature, *signature);
- }
- return function;
-}
-
-namespace {
// Internal constants for the layout of the module object.
-const int kWasmModuleFunctionTable = 0;
-const int kWasmModuleCodeTable = 1;
-const int kWasmMemArrayBuffer = 2;
-const int kWasmGlobalsArrayBuffer = 3;
-// TODO(clemensh): Remove function name array, extract names from module bytes.
-const int kWasmFunctionNamesArray = 4;
-const int kWasmModuleBytesString = 5;
-const int kWasmDebugInfo = 6;
-const int kWasmModuleInternalFieldCount = 7;
-
-// TODO(mtrofin): Unnecessary once we stop using JS Heap for wasm code.
-// For now, each field is expected to have the type commented by its side.
-// The elements typed as "maybe" are optional. The others are mandatory. Since
-// the compiled module is either obtained from the current v8 instance, or from
-// a snapshot produced by a compatible (==identical) v8 instance, we simply
-// fail at instantiation time, in the face of invalid data.
-enum CompiledWasmObjectFields {
- kFunctions, // FixedArray of Code
- kImportData, // maybe FixedArray of FixedArray respecting the
- // WasmImportMetadata structure.
- kExports, // maybe FixedArray of FixedArray of WasmExportMetadata
- // structure
- kStartupFunction, // maybe FixedArray of WasmExportMetadata structure
- kTableOfIndirectFunctionTables, // maybe FixedArray of FixedArray of
- // WasmIndirectFunctionTableMetadata
- kModuleBytes, // maybe String
- kFunctionNameTable, // maybe ByteArray
- kMinRequiredMemory, // Smi. an uint32_t
- // The following 2 are either together present or absent:
- kDataSegmentsInfo, // maybe FixedArray of FixedArray respecting the
- // WasmSegmentInfo structure
- kDataSegments, // maybe ByteArray.
-
- kGlobalsSize, // Smi. an uint32_t
- kExportMem, // Smi. bool
- kOrigin, // Smi. ModuleOrigin
- kCompiledWasmObjectTableSize // Sentinel value.
+enum WasmInstanceObjectFields {
+ kWasmCompiledModule = 0,
+ kWasmModuleFunctionTable,
+ kWasmModuleCodeTable,
+ kWasmMemArrayBuffer,
+ kWasmGlobalsArrayBuffer,
+ // TODO(clemensh): Remove function name array, extract names from module
+ // bytes.
+ kWasmFunctionNamesArray,
+ kWasmModuleBytesString,
+ kWasmDebugInfo,
+ kWasmNumImportedFunctions,
+ kWasmModuleInternalFieldCount
};
-enum WasmImportMetadata {
- kModuleName, // String
- kFunctionName, // maybe String
- kOutputCount, // Smi. an uint32_t
- kSignature, // ByteArray. A copy of the data in FunctionSig
- kWasmImportDataTableSize // Sentinel value.
+enum WasmImportData {
+ kModuleName, // String
+ kFunctionName, // maybe String
+ kOutputCount, // Smi. an uint32_t
+ kSignature, // ByteArray. A copy of the data in FunctionSig
+ kWasmImportDataSize // Sentinel value.
};
-enum WasmExportMetadata {
- kExportCode, // Code
- kExportName, // String
- kExportArity, // Smi, an int
- kExportedFunctionIndex, // Smi, an uint32_t
- kExportedSignature, // ByteArray. A copy of the data in FunctionSig
- kWasmExportMetadataTableSize // Sentinel value.
+enum WasmExportData {
+ kExportName, // String
+ kExportArity, // Smi, an int
+ kExportedFunctionIndex, // Smi, an uint32_t
+ kExportedSignature, // ByteArray. A copy of the data in FunctionSig
+ kWasmExportDataSize // Sentinel value.
};
enum WasmSegmentInfo {
@@ -208,31 +86,26 @@ enum WasmSegmentInfo {
kWasmSegmentInfoSize // Sentinel value.
};
-enum WasmIndirectFunctionTableMetadata {
- kSize, // Smi. an uint32_t
- kTable, // FixedArray of indirect function table
- kWasmIndirectFunctionTableMetadataSize // Sentinel value.
+enum WasmIndirectFunctionTableData {
+ kSize, // Smi. an uint32_t
+ kTable, // FixedArray of indirect function table
+ kWasmIndirectFunctionTableDataSize // Sentinel value.
};
uint32_t GetMinModuleMemSize(const WasmModule* module) {
return WasmModule::kPageSize * module->min_mem_pages;
}
-void LoadDataSegments(Handle<FixedArray> compiled_module, Address mem_addr,
- size_t mem_size) {
- Isolate* isolate = compiled_module->GetIsolate();
- MaybeHandle<ByteArray> maybe_data =
- compiled_module->GetValue<ByteArray>(isolate, kDataSegments);
- MaybeHandle<FixedArray> maybe_segments =
- compiled_module->GetValue<FixedArray>(isolate, kDataSegmentsInfo);
+void LoadDataSegments(Handle<WasmCompiledModule> compiled_module,
+ Address mem_addr, size_t mem_size) {
+ CHECK(compiled_module->has_data_segments() ==
+ compiled_module->has_data_segments_info());
- // We either have both or neither.
- CHECK(maybe_data.is_null() == maybe_segments.is_null());
// If we have neither, we're done.
- if (maybe_data.is_null()) return;
+ if (!compiled_module->has_data_segments()) return;
- Handle<ByteArray> data = maybe_data.ToHandleChecked();
- Handle<FixedArray> segments = maybe_segments.ToHandleChecked();
+ Handle<ByteArray> data = compiled_module->data_segments();
+ Handle<FixedArray> segments = compiled_module->data_segments_info();
uint32_t last_extraction_pos = 0;
for (int i = 0; i < segments->length(); ++i) {
@@ -250,12 +123,11 @@ void LoadDataSegments(Handle<FixedArray> compiled_module, Address mem_addr,
}
void SaveDataSegmentInfo(Factory* factory, const WasmModule* module,
- Handle<FixedArray> compiled_module) {
+ Handle<WasmCompiledModule> compiled_module) {
Handle<FixedArray> segments = factory->NewFixedArray(
static_cast<int>(module->data_segments.size()), TENURED);
uint32_t data_size = 0;
for (const WasmDataSegment& segment : module->data_segments) {
- if (!segment.init) continue;
if (segment.source_size == 0) continue;
data_size += segment.source_size;
}
@@ -264,11 +136,12 @@ void SaveDataSegmentInfo(Factory* factory, const WasmModule* module,
uint32_t last_insertion_pos = 0;
for (uint32_t i = 0; i < module->data_segments.size(); ++i) {
const WasmDataSegment& segment = module->data_segments[i];
- if (!segment.init) continue;
if (segment.source_size == 0) continue;
Handle<ByteArray> js_segment =
factory->NewByteArray(kWasmSegmentInfoSize * sizeof(uint32_t), TENURED);
- js_segment->set_int(kDestAddr, segment.dest_addr);
+ // TODO(titzer): add support for global offsets for dest_addr
+ CHECK_EQ(WasmInitExpr::kI32Const, segment.dest_addr.kind);
+ js_segment->set_int(kDestAddr, segment.dest_addr.val.i32_const);
js_segment->set_int(kSourceSize, segment.source_size);
segments->set(i, *js_segment);
data->copy_in(last_insertion_pos,
@@ -276,8 +149,8 @@ void SaveDataSegmentInfo(Factory* factory, const WasmModule* module,
segment.source_size);
last_insertion_pos += segment.source_size;
}
- compiled_module->set(kDataSegmentsInfo, *segments);
- compiled_module->set(kDataSegments, *data);
+ compiled_module->set_data_segments_info(segments);
+ compiled_module->set_data_segments(data);
}
void PatchFunctionTable(Handle<Code> code,
@@ -315,8 +188,9 @@ Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) {
return buffer;
}
-void RelocateInstanceCode(Handle<JSObject> instance, Address start,
- uint32_t prev_size, uint32_t new_size) {
+void RelocateInstanceCode(Handle<JSObject> instance, Address old_start,
+ Address start, uint32_t prev_size,
+ uint32_t new_size) {
Handle<FixedArray> functions = Handle<FixedArray>(
FixedArray::cast(instance->GetInternalField(kWasmModuleCodeTable)));
for (int i = 0; i < functions->length(); ++i) {
@@ -325,7 +199,7 @@ void RelocateInstanceCode(Handle<JSObject> instance, Address start,
int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) |
(1 << RelocInfo::WASM_MEMORY_SIZE_REFERENCE);
for (RelocIterator it(*function, mask); !it.done(); it.next()) {
- it.rinfo()->update_wasm_memory_reference(nullptr, start, prev_size,
+ it.rinfo()->update_wasm_memory_reference(old_start, start, prev_size,
new_size);
}
}
@@ -347,7 +221,8 @@ Handle<JSArrayBuffer> AllocateMemory(ErrorThrower* thrower, Isolate* isolate,
return mem_buffer;
}
-void RelocateGlobals(Handle<JSObject> instance, Address globals_start) {
+void RelocateGlobals(Handle<JSObject> instance, Address old_start,
+ Address globals_start) {
Handle<FixedArray> functions = Handle<FixedArray>(
FixedArray::cast(instance->GetInternalField(kWasmModuleCodeTable)));
uint32_t function_count = static_cast<uint32_t>(functions->length());
@@ -356,7 +231,7 @@ void RelocateGlobals(Handle<JSObject> instance, Address globals_start) {
AllowDeferredHandleDereference embedding_raw_address;
int mask = 1 << RelocInfo::WASM_GLOBAL_REFERENCE;
for (RelocIterator it(*function, mask); !it.done(); it.next()) {
- it.rinfo()->update_wasm_global_reference(nullptr, globals_start);
+ it.rinfo()->update_wasm_global_reference(old_start, globals_start);
}
}
}
@@ -375,64 +250,41 @@ Handle<Code> CreatePlaceholder(Factory* factory, uint32_t index,
return code;
}
-// TODO(mtrofin): remove when we stop relying on placeholders.
-void InitializePlaceholders(Factory* factory,
- std::vector<Handle<Code>>* placeholders,
- size_t size) {
- DCHECK(placeholders->empty());
- placeholders->reserve(size);
-
- for (uint32_t i = 0; i < size; ++i) {
- placeholders->push_back(CreatePlaceholder(factory, i, Code::WASM_FUNCTION));
- }
-}
-
bool LinkFunction(Handle<Code> unlinked,
- const std::vector<Handle<Code>>& code_targets,
- Code::Kind kind) {
+ std::vector<Handle<Code>>& code_table) {
bool modified = false;
- int mode_mask = RelocInfo::kCodeTargetMask;
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
AllowDeferredHandleDereference embedding_raw_address;
for (RelocIterator it(*unlinked, mode_mask); !it.done(); it.next()) {
RelocInfo::Mode mode = it.rinfo()->rmode();
if (RelocInfo::IsCodeTarget(mode)) {
Code* target =
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (target->kind() == kind &&
- target->constant_pool_offset() >= kPlaceholderMarker) {
- // Patch direct calls to placeholder code objects.
- uint32_t index = target->constant_pool_offset() - kPlaceholderMarker;
- CHECK(index < code_targets.size());
- Handle<Code> new_target = code_targets[index];
- if (target != *new_target) {
- it.rinfo()->set_target_address(new_target->instruction_start(),
- UPDATE_WRITE_BARRIER,
- SKIP_ICACHE_FLUSH);
- modified = true;
+ if (target->constant_pool_offset() < kPlaceholderMarker) continue;
+ switch (target->kind()) {
+ case Code::WASM_FUNCTION: // fall through
+ case Code::WASM_TO_JS_FUNCTION: // fall through
+ case Code::JS_TO_WASM_FUNCTION: {
+ // Patch direct calls to placeholder code objects.
+ uint32_t index = target->constant_pool_offset() - kPlaceholderMarker;
+ Handle<Code> new_target = code_table[index];
+ if (target != *new_target) {
+ it.rinfo()->set_target_address(new_target->instruction_start(),
+ UPDATE_WRITE_BARRIER,
+ SKIP_ICACHE_FLUSH);
+ modified = true;
+ }
+ break;
}
+ default:
+ break;
}
}
}
return modified;
}
-void LinkModuleFunctions(Isolate* isolate,
- std::vector<Handle<Code>>& functions) {
- for (size_t i = 0; i < functions.size(); ++i) {
- Handle<Code> code = functions[i];
- LinkFunction(code, functions, Code::WASM_FUNCTION);
- }
-}
-
-void LinkImports(Isolate* isolate, std::vector<Handle<Code>>& functions,
- const std::vector<Handle<Code>>& imports) {
- for (uint32_t i = 0; i < functions.size(); ++i) {
- Handle<Code> code = functions[i];
- LinkFunction(code, imports, Code::WASM_TO_JS_FUNCTION);
- }
-}
-
-void FlushAssemblyCache(Isolate* isolate, Handle<FixedArray> functions) {
+void FlushICache(Isolate* isolate, Handle<FixedArray> functions) {
for (int i = 0; i < functions->length(); ++i) {
Handle<Code> code = functions->GetValueChecked<Code>(isolate, i);
Assembler::FlushICache(isolate, code->instruction_start(),
@@ -440,84 +292,6 @@ void FlushAssemblyCache(Isolate* isolate, Handle<FixedArray> functions) {
}
}
-} // namespace
-
-WasmModule::WasmModule(byte* module_start)
- : module_start(module_start),
- module_end(nullptr),
- min_mem_pages(0),
- max_mem_pages(0),
- mem_export(false),
- mem_external(false),
- start_function_index(-1),
- origin(kWasmOrigin),
- globals_size(0),
- pending_tasks(new base::Semaphore(0)) {}
-
-static MaybeHandle<JSFunction> ReportFFIError(
- ErrorThrower& thrower, const char* error, uint32_t index,
- Handle<String> module_name, MaybeHandle<String> function_name) {
- Handle<String> function_name_handle;
- if (function_name.ToHandle(&function_name_handle)) {
- thrower.Error("Import #%d module=\"%.*s\" function=\"%.*s\" error: %s",
- index, module_name->length(), module_name->ToCString().get(),
- function_name_handle->length(),
- function_name_handle->ToCString().get(), error);
- } else {
- thrower.Error("Import #%d module=\"%.*s\" error: %s", index,
- module_name->length(), module_name->ToCString().get(), error);
- }
- thrower.Error("Import ");
- return MaybeHandle<JSFunction>();
-}
-
-static MaybeHandle<JSReceiver> LookupFunction(
- ErrorThrower& thrower, Factory* factory, Handle<JSReceiver> ffi,
- uint32_t index, Handle<String> module_name,
- MaybeHandle<String> function_name) {
- if (ffi.is_null()) {
- return ReportFFIError(thrower, "FFI is not an object", index, module_name,
- function_name);
- }
-
- // Look up the module first.
- MaybeHandle<Object> result = Object::GetProperty(ffi, module_name);
- if (result.is_null()) {
- return ReportFFIError(thrower, "module not found", index, module_name,
- function_name);
- }
-
- Handle<Object> module = result.ToHandleChecked();
-
- if (!module->IsJSReceiver()) {
- return ReportFFIError(thrower, "module is not an object or function", index,
- module_name, function_name);
- }
-
- Handle<Object> function;
- if (!function_name.is_null()) {
- // Look up the function in the module.
- MaybeHandle<Object> result =
- Object::GetProperty(module, function_name.ToHandleChecked());
- if (result.is_null()) {
- return ReportFFIError(thrower, "function not found", index, module_name,
- function_name);
- }
- function = result.ToHandleChecked();
- } else {
- // No function specified. Use the "default export".
- function = module;
- }
-
- if (!function->IsCallable()) {
- return ReportFFIError(thrower, "not a callable", index, module_name,
- function_name);
- }
-
- return Handle<JSReceiver>::cast(function);
-}
-
-namespace {
// Fetches the compilation unit of a wasm function and executes its parallel
// phase.
bool FetchAndExecuteCompilationUnit(
@@ -530,7 +304,7 @@ bool FetchAndExecuteCompilationUnit(
DisallowHandleDereference no_deref;
DisallowCodeDependencyChange no_dependency_change;
- // - 1 because AtomicIntrement returns the value after the atomic increment.
+ // - 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;
@@ -539,10 +313,8 @@ bool FetchAndExecuteCompilationUnit(
compiler::WasmCompilationUnit* unit = compilation_units->at(index);
if (unit != nullptr) {
unit->ExecuteCompilation();
- {
- base::LockGuard<base::Mutex> guard(result_mutex);
- executed_units->push(unit);
- }
+ base::LockGuard<base::Mutex> guard(result_mutex);
+ executed_units->push(unit);
}
return true;
}
@@ -585,11 +357,6 @@ static void RecordStats(Isolate* isolate, Code* code) {
code->relocation_info()->length());
}
-static void RecordStats(Isolate* isolate,
- const std::vector<Handle<Code>>& functions) {
- for (Handle<Code> c : functions) RecordStats(isolate, *c);
-}
-
static void RecordStats(Isolate* isolate, Handle<FixedArray> functions) {
DisallowHeapAllocation no_gc;
for (int i = 0; i < functions->length(); ++i) {
@@ -597,16 +364,27 @@ static void RecordStats(Isolate* isolate, Handle<FixedArray> functions) {
}
}
-Handle<FixedArray> GetImportsMetadata(Factory* factory,
- const WasmModule* module) {
+Address GetGlobalStartAddressFromCodeTemplate(Object* undefined,
+ JSObject* owner) {
+ Address old_address = nullptr;
+ Object* stored_value = owner->GetInternalField(kWasmGlobalsArrayBuffer);
+ if (stored_value != undefined) {
+ old_address = static_cast<Address>(
+ JSArrayBuffer::cast(stored_value)->backing_store());
+ }
+ return old_address;
+}
+
+Handle<FixedArray> GetImportsData(Factory* factory, const WasmModule* module) {
Handle<FixedArray> ret = factory->NewFixedArray(
static_cast<int>(module->import_table.size()), TENURED);
for (size_t i = 0; i < module->import_table.size(); ++i) {
const WasmImport& import = module->import_table[i];
+ if (import.kind != kExternalFunction) continue;
WasmName module_name = module->GetNameOrNull(import.module_name_offset,
import.module_name_length);
- WasmName function_name = module->GetNameOrNull(import.function_name_offset,
- import.function_name_length);
+ WasmName function_name = module->GetNameOrNull(import.field_name_offset,
+ import.field_name_length);
Handle<String> module_name_string =
factory->InternalizeUtf8String(module_name);
@@ -614,116 +392,172 @@ Handle<FixedArray> GetImportsMetadata(Factory* factory,
function_name.is_empty()
? Handle<String>::null()
: factory->InternalizeUtf8String(function_name);
- Handle<ByteArray> sig =
- factory->NewByteArray(static_cast<int>(import.sig->parameter_count() +
- import.sig->return_count()),
- TENURED);
- sig->copy_in(0, reinterpret_cast<const byte*>(import.sig->raw_data()),
+ FunctionSig* fsig = module->functions[import.index].sig;
+ Handle<ByteArray> sig = factory->NewByteArray(
+ static_cast<int>(fsig->parameter_count() + fsig->return_count()),
+ TENURED);
+ sig->copy_in(0, reinterpret_cast<const byte*>(fsig->raw_data()),
sig->length());
Handle<FixedArray> encoded_import =
- factory->NewFixedArray(kWasmImportDataTableSize, TENURED);
+ factory->NewFixedArray(kWasmImportDataSize, TENURED);
encoded_import->set(kModuleName, *module_name_string);
if (!function_name_string.is_null()) {
encoded_import->set(kFunctionName, *function_name_string);
}
- encoded_import->set(
- kOutputCount,
- Smi::FromInt(static_cast<int>(import.sig->return_count())));
+ encoded_import->set(kOutputCount,
+ Smi::FromInt(static_cast<int>(fsig->return_count())));
encoded_import->set(kSignature, *sig);
ret->set(static_cast<int>(i), *encoded_import);
}
return ret;
}
-bool CompileWrappersToImportedFunctions(Isolate* isolate,
- const Handle<JSReceiver> ffi,
- std::vector<Handle<Code>>& imports,
- Handle<FixedArray> import_data,
- ErrorThrower* thrower) {
- uint32_t import_count = static_cast<uint32_t>(import_data->length());
- if (import_count > 0) {
- imports.reserve(import_count);
- for (uint32_t index = 0; index < import_count; ++index) {
- Handle<FixedArray> data =
- import_data->GetValueChecked<FixedArray>(isolate, index);
- Handle<String> module_name =
- data->GetValueChecked<String>(isolate, kModuleName);
- MaybeHandle<String> function_name =
- data->GetValue<String>(isolate, kFunctionName);
-
- // TODO(mtrofin): this is an uint32_t, actually. We should rationalize
- // it when we rationalize signed/unsigned stuff.
- int ret_count = Smi::cast(data->get(kOutputCount))->value();
- CHECK(ret_count >= 0);
- Handle<ByteArray> sig_data =
- data->GetValueChecked<ByteArray>(isolate, kSignature);
- int sig_data_size = sig_data->length();
- int param_count = sig_data_size - ret_count;
- CHECK(param_count >= 0);
-
- MaybeHandle<JSReceiver> function = LookupFunction(
- *thrower, isolate->factory(), ffi, index, module_name, function_name);
- if (function.is_null()) return false;
- Handle<Code> code;
- Handle<JSReceiver> target = function.ToHandleChecked();
- bool isMatch = false;
- Handle<Code> export_wrapper_code;
- if (target->IsJSFunction()) {
- Handle<JSFunction> func = Handle<JSFunction>::cast(target);
- export_wrapper_code = handle(func->code());
- if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) {
- int exported_param_count =
- Smi::cast(func->GetInternalField(kInternalArity))->value();
- Handle<ByteArray> exportedSig = Handle<ByteArray>(
- ByteArray::cast(func->GetInternalField(kInternalSignature)));
- if (exported_param_count == param_count &&
- exportedSig->length() == sig_data->length() &&
- memcmp(exportedSig->data(), sig_data->data(),
- exportedSig->length()) == 0) {
- isMatch = true;
- }
- }
+static MaybeHandle<JSFunction> ReportFFIError(
+ ErrorThrower* thrower, const char* error, uint32_t index,
+ Handle<String> module_name, MaybeHandle<String> function_name) {
+ Handle<String> function_name_handle;
+ if (function_name.ToHandle(&function_name_handle)) {
+ thrower->Error("Import #%d module=\"%.*s\" function=\"%.*s\" error: %s",
+ index, module_name->length(), module_name->ToCString().get(),
+ function_name_handle->length(),
+ function_name_handle->ToCString().get(), error);
+ } else {
+ thrower->Error("Import #%d module=\"%.*s\" error: %s", index,
+ module_name->length(), module_name->ToCString().get(),
+ error);
+ }
+ thrower->Error("Import ");
+ return MaybeHandle<JSFunction>();
+}
+
+static MaybeHandle<JSReceiver> LookupFunction(
+ ErrorThrower* thrower, Factory* factory, Handle<JSReceiver> ffi,
+ uint32_t index, Handle<String> module_name,
+ MaybeHandle<String> function_name) {
+ if (ffi.is_null()) {
+ return ReportFFIError(thrower, "FFI is not an object", index, module_name,
+ function_name);
+ }
+
+ // Look up the module first.
+ MaybeHandle<Object> result = Object::GetProperty(ffi, module_name);
+ if (result.is_null()) {
+ return ReportFFIError(thrower, "module not found", index, module_name,
+ function_name);
+ }
+
+ Handle<Object> module = result.ToHandleChecked();
+
+ if (!module->IsJSReceiver()) {
+ return ReportFFIError(thrower, "module is not an object or function", index,
+ module_name, function_name);
+ }
+
+ Handle<Object> function;
+ if (!function_name.is_null()) {
+ // Look up the function in the module.
+ MaybeHandle<Object> result =
+ Object::GetProperty(module, function_name.ToHandleChecked());
+ if (result.is_null()) {
+ return ReportFFIError(thrower, "function not found", index, module_name,
+ function_name);
+ }
+ function = result.ToHandleChecked();
+ } else {
+ // No function specified. Use the "default export".
+ function = module;
+ }
+
+ if (!function->IsCallable()) {
+ return ReportFFIError(thrower, "not a callable", index, module_name,
+ function_name);
+ }
+
+ return Handle<JSReceiver>::cast(function);
+}
+
+Handle<Code> CompileImportWrapper(Isolate* isolate,
+ const Handle<JSReceiver> ffi, int index,
+ Handle<FixedArray> import_data,
+ ErrorThrower* thrower) {
+ Handle<FixedArray> data =
+ import_data->GetValueChecked<FixedArray>(isolate, index);
+ Handle<String> module_name =
+ data->GetValueChecked<String>(isolate, kModuleName);
+ MaybeHandle<String> function_name =
+ data->GetValue<String>(isolate, kFunctionName);
+
+ // TODO(mtrofin): this is an uint32_t, actually. We should rationalize
+ // it when we rationalize signed/unsigned stuff.
+ int ret_count = Smi::cast(data->get(kOutputCount))->value();
+ CHECK_GE(ret_count, 0);
+ Handle<ByteArray> sig_data =
+ data->GetValueChecked<ByteArray>(isolate, kSignature);
+ int sig_data_size = sig_data->length();
+ int param_count = sig_data_size - ret_count;
+ CHECK(param_count >= 0);
+
+ MaybeHandle<JSReceiver> function = LookupFunction(
+ thrower, isolate->factory(), ffi, index, module_name, function_name);
+ if (function.is_null()) return Handle<Code>::null();
+ Handle<Code> code;
+ Handle<JSReceiver> target = function.ToHandleChecked();
+ bool isMatch = false;
+ Handle<Code> export_wrapper_code;
+ if (target->IsJSFunction()) {
+ Handle<JSFunction> func = Handle<JSFunction>::cast(target);
+ export_wrapper_code = handle(func->code());
+ if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) {
+ int exported_param_count =
+ Smi::cast(func->GetInternalField(kInternalArity))->value();
+ Handle<ByteArray> exportedSig = Handle<ByteArray>(
+ ByteArray::cast(func->GetInternalField(kInternalSignature)));
+ if (exported_param_count == param_count &&
+ exportedSig->length() == sig_data->length() &&
+ memcmp(exportedSig->data(), sig_data->data(),
+ exportedSig->length()) == 0) {
+ isMatch = true;
}
- if (isMatch) {
- int wasm_count = 0;
- int const mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
- for (RelocIterator it(*export_wrapper_code, mask); !it.done();
- it.next()) {
- RelocInfo* rinfo = it.rinfo();
- Address target_address = rinfo->target_address();
- Code* target = Code::GetCodeFromTargetAddress(target_address);
- if (target->kind() == Code::WASM_FUNCTION) {
- ++wasm_count;
- code = handle(target);
- }
- }
- DCHECK(wasm_count == 1);
- } else {
- // Copy the signature to avoid a raw pointer into a heap object when
- // GC can happen.
- Zone zone(isolate->allocator());
- MachineRepresentation* reps =
- zone.NewArray<MachineRepresentation>(sig_data_size);
- memcpy(reps, sig_data->data(),
- sizeof(MachineRepresentation) * sig_data_size);
- FunctionSig sig(ret_count, param_count, reps);
-
- code = compiler::CompileWasmToJSWrapper(isolate, target, &sig, index,
- module_name, function_name);
+ }
+ }
+ if (isMatch) {
+ int wasm_count = 0;
+ int const mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
+ for (RelocIterator it(*export_wrapper_code, mask); !it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ Address target_address = rinfo->target_address();
+ Code* target = Code::GetCodeFromTargetAddress(target_address);
+ if (target->kind() == Code::WASM_FUNCTION) {
+ ++wasm_count;
+ code = handle(target);
}
- imports.push_back(code);
}
+ DCHECK(wasm_count == 1);
+ return code;
+ } else {
+ // Copy the signature to avoid a raw pointer into a heap object when
+ // GC can happen.
+ Zone zone(isolate->allocator());
+ MachineRepresentation* reps =
+ zone.NewArray<MachineRepresentation>(sig_data_size);
+ memcpy(reps, sig_data->data(),
+ sizeof(MachineRepresentation) * sig_data_size);
+ FunctionSig sig(ret_count, param_count, reps);
+
+ return compiler::CompileWasmToJSWrapper(isolate, target, &sig, index,
+ module_name, function_name);
}
- return true;
}
void InitializeParallelCompilation(
Isolate* isolate, const std::vector<WasmFunction>& functions,
std::vector<compiler::WasmCompilationUnit*>& compilation_units,
- ModuleEnv& module_env, ErrorThrower& thrower) {
+ ModuleEnv& module_env, ErrorThrower* thrower) {
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) {
- compilation_units[i] = new compiler::WasmCompilationUnit(
- &thrower, isolate, &module_env, &functions[i], i);
+ const WasmFunction* func = &functions[i];
+ compilation_units[i] =
+ func->imported ? nullptr : new compiler::WasmCompilationUnit(
+ thrower, isolate, &module_env, func, i);
}
}
@@ -812,7 +646,7 @@ void CompileInParallel(Isolate* isolate, const WasmModule* module,
// 1) The main thread allocates a compilation unit for each wasm function
// and stores them in the vector {compilation_units}.
InitializeParallelCompilation(isolate, module->functions, compilation_units,
- *module_env, *thrower);
+ *module_env, thrower);
// Objects for the synchronization with the background threads.
base::Mutex result_mutex;
@@ -853,8 +687,8 @@ void CompileSequentially(Isolate* isolate, const WasmModule* 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.
- DCHECK_EQ(i, func.func_index);
WasmName str = module->GetName(func.name_offset, func.name_length);
Handle<Code> code = Handle<Code>::null();
// Compile the function.
@@ -870,190 +704,321 @@ void CompileSequentially(Isolate* isolate, const WasmModule* module,
}
}
-void SetDebugSupport(Factory* factory, Handle<FixedArray> compiled_module,
- Handle<JSObject> js_object) {
- Isolate* isolate = compiled_module->GetIsolate();
- MaybeHandle<String> module_bytes_string =
- compiled_module->GetValue<String>(isolate, kModuleBytes);
- if (!module_bytes_string.is_null()) {
- js_object->SetInternalField(kWasmModuleBytesString,
- *module_bytes_string.ToHandleChecked());
- }
- Handle<FixedArray> functions = Handle<FixedArray>(
- FixedArray::cast(js_object->GetInternalField(kWasmModuleCodeTable)));
+void PatchDirectCalls(Handle<FixedArray> old_functions,
+ Handle<FixedArray> new_functions, int start) {
+ DCHECK_EQ(new_functions->length(), old_functions->length());
- for (int i = FLAG_skip_compiling_wasm_funcs; i < functions->length(); ++i) {
- Handle<Code> code = functions->GetValueChecked<Code>(isolate, i);
- DCHECK(code->deoptimization_data() == nullptr ||
- code->deoptimization_data()->length() == 0);
- Handle<FixedArray> deopt_data = factory->NewFixedArray(2, TENURED);
- if (!js_object.is_null()) {
- deopt_data->set(0, *js_object);
+ DisallowHeapAllocation no_gc;
+ std::map<Code*, Code*> old_to_new_code;
+ for (int i = 0; i < new_functions->length(); ++i) {
+ old_to_new_code.insert(std::make_pair(Code::cast(old_functions->get(i)),
+ Code::cast(new_functions->get(i))));
+ }
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
+ AllowDeferredHandleDereference embedding_raw_address;
+ for (int i = start; i < new_functions->length(); ++i) {
+ Code* wasm_function = Code::cast(new_functions->get(i));
+ for (RelocIterator it(wasm_function, mode_mask); !it.done(); it.next()) {
+ Code* old_code =
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
+ if (old_code->kind() == Code::WASM_TO_JS_FUNCTION ||
+ old_code->kind() == Code::WASM_FUNCTION) {
+ auto found = old_to_new_code.find(old_code);
+ DCHECK(found != old_to_new_code.end());
+ Code* new_code = found->second;
+ if (new_code != old_code) {
+ it.rinfo()->set_target_address(new_code->instruction_start(),
+ UPDATE_WRITE_BARRIER,
+ SKIP_ICACHE_FLUSH);
+ }
+ }
}
- deopt_data->set(1, Smi::FromInt(static_cast<int>(i)));
- deopt_data->set_length(2);
- code->set_deoptimization_data(*deopt_data);
}
+}
- MaybeHandle<ByteArray> function_name_table =
- compiled_module->GetValue<ByteArray>(isolate, kFunctionNameTable);
- if (!function_name_table.is_null()) {
- js_object->SetInternalField(kWasmFunctionNamesArray,
- *function_name_table.ToHandleChecked());
+static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
+ WasmCompiledModule* compiled_module) {
+ TRACE("Resetting %d\n", compiled_module->instance_id());
+ Object* undefined = *isolate->factory()->undefined_value();
+ uint32_t old_mem_size = compiled_module->has_heap()
+ ? compiled_module->mem_size()
+ : compiled_module->default_mem_size();
+ uint32_t default_mem_size = compiled_module->default_mem_size();
+ Object* mem_start = compiled_module->ptr_to_heap();
+ Address old_mem_address = nullptr;
+ Address globals_start =
+ GetGlobalStartAddressFromCodeTemplate(undefined, owner);
+
+ if (old_mem_size > 0) {
+ CHECK_NE(mem_start, undefined);
+ old_mem_address =
+ static_cast<Address>(JSArrayBuffer::cast(mem_start)->backing_store());
+ }
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_REFERENCE) |
+ RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_SIZE_REFERENCE) |
+ RelocInfo::ModeMask(RelocInfo::WASM_GLOBAL_REFERENCE);
+
+ Object* fct_obj = compiled_module->ptr_to_code_table();
+ if (fct_obj != nullptr && fct_obj != undefined &&
+ (old_mem_size > 0 || globals_start != nullptr)) {
+ FixedArray* functions = FixedArray::cast(fct_obj);
+ for (int i = 0; i < functions->length(); ++i) {
+ Code* code = Code::cast(functions->get(i));
+ bool changed = false;
+ for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
+ RelocInfo::Mode mode = it.rinfo()->rmode();
+ if (RelocInfo::IsWasmMemoryReference(mode) ||
+ RelocInfo::IsWasmMemorySizeReference(mode)) {
+ it.rinfo()->update_wasm_memory_reference(
+ old_mem_address, nullptr, old_mem_size, default_mem_size);
+ changed = true;
+ } else {
+ CHECK(RelocInfo::IsWasmGlobalReference(mode));
+ it.rinfo()->update_wasm_global_reference(globals_start, nullptr);
+ changed = true;
+ }
+ }
+ if (changed) {
+ Assembler::FlushICache(isolate, code->instruction_start(),
+ code->instruction_size());
+ }
+ }
}
+ compiled_module->reset_heap();
}
-bool SetupGlobals(Isolate* isolate, Handle<FixedArray> compiled_module,
- Handle<JSObject> instance, ErrorThrower* thrower) {
- uint32_t globals_size = static_cast<uint32_t>(
- Smi::cast(compiled_module->get(kGlobalsSize))->value());
- if (globals_size > 0) {
- Handle<JSArrayBuffer> globals_buffer =
- NewArrayBuffer(isolate, globals_size);
- if (globals_buffer.is_null()) {
- thrower->Error("Out of memory: wasm globals");
- return false;
+static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
+ JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
+ JSObject* owner = *p;
+ WasmCompiledModule* compiled_module =
+ WasmCompiledModule::cast(owner->GetInternalField(kWasmCompiledModule));
+ TRACE("Finalizing %d {\n", compiled_module->instance_id());
+ Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
+ DCHECK(compiled_module->has_weak_module_object());
+ WeakCell* weak_module_obj = compiled_module->ptr_to_weak_module_object();
+
+ // weak_module_obj 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_module_obj->cleared()) {
+ JSObject* module_obj = JSObject::cast(weak_module_obj->value());
+ WasmCompiledModule* current_template =
+ WasmCompiledModule::cast(module_obj->GetInternalField(0));
+
+ TRACE("chain before {\n");
+ TRACE_CHAIN(current_template);
+ TRACE("}\n");
+
+ DCHECK(!current_template->has_weak_prev_instance());
+ WeakCell* next = compiled_module->ptr_to_weak_next_instance();
+ WeakCell* prev = compiled_module->ptr_to_weak_prev_instance();
+
+ if (current_template == compiled_module) {
+ if (next == nullptr) {
+ ResetCompiledModule(isolate, owner, compiled_module);
+ } else {
+ DCHECK(next->value()->IsFixedArray());
+ module_obj->SetInternalField(0, next->value());
+ DCHECK_NULL(prev);
+ WasmCompiledModule::cast(next->value())->reset_weak_prev_instance();
+ }
+ } else {
+ DCHECK(!(prev == nullptr && next == nullptr));
+ // the only reason prev or next would be cleared is if the
+ // respective objects got collected, but if that happened,
+ // we would have relinked the list.
+ if (prev != nullptr) {
+ DCHECK(!prev->cleared());
+ if (next == nullptr) {
+ WasmCompiledModule::cast(prev->value())->reset_weak_next_instance();
+ } else {
+ WasmCompiledModule::cast(prev->value())
+ ->set_ptr_to_weak_next_instance(next);
+ }
+ }
+ if (next != nullptr) {
+ DCHECK(!next->cleared());
+ if (prev == nullptr) {
+ WasmCompiledModule::cast(next->value())->reset_weak_prev_instance();
+ } else {
+ WasmCompiledModule::cast(next->value())
+ ->set_ptr_to_weak_prev_instance(prev);
+ }
+ }
}
- RelocateGlobals(instance,
- static_cast<Address>(globals_buffer->backing_store()));
- instance->SetInternalField(kWasmGlobalsArrayBuffer, *globals_buffer);
+ TRACE("chain after {\n");
+ TRACE_CHAIN(WasmCompiledModule::cast(module_obj->GetInternalField(0)));
+ TRACE("}\n");
}
- return true;
+ compiled_module->reset_weak_owning_instance();
+ GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
+ TRACE("}\n");
}
-bool SetupInstanceHeap(Isolate* isolate, Handle<FixedArray> compiled_module,
- Handle<JSObject> instance, Handle<JSArrayBuffer> memory,
- ErrorThrower* thrower) {
- uint32_t min_mem_pages = static_cast<uint32_t>(
- Smi::cast(compiled_module->get(kMinRequiredMemory))->value());
- isolate->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages);
- // TODO(wasm): re-enable counter for max_mem_pages when we use that field.
+Handle<FixedArray> SetupIndirectFunctionTable(
+ Isolate* isolate, Handle<FixedArray> wasm_functions,
+ Handle<FixedArray> indirect_table_template,
+ Handle<FixedArray> tables_to_replace) {
+ Factory* factory = isolate->factory();
+ Handle<FixedArray> cloned_indirect_tables =
+ factory->CopyFixedArray(indirect_table_template);
+ for (int i = 0; i < cloned_indirect_tables->length(); ++i) {
+ Handle<FixedArray> orig_metadata =
+ cloned_indirect_tables->GetValueChecked<FixedArray>(isolate, i);
+ Handle<FixedArray> cloned_metadata = factory->CopyFixedArray(orig_metadata);
+ cloned_indirect_tables->set(i, *cloned_metadata);
- if (memory.is_null() && min_mem_pages > 0) {
- memory = AllocateMemory(thrower, isolate, min_mem_pages);
- if (memory.is_null()) {
- return false;
+ Handle<FixedArray> orig_table =
+ cloned_metadata->GetValueChecked<FixedArray>(isolate, kTable);
+ Handle<FixedArray> cloned_table = factory->CopyFixedArray(orig_table);
+ cloned_metadata->set(kTable, *cloned_table);
+ // Patch the cloned code to refer to the cloned kTable.
+ Handle<FixedArray> table_to_replace =
+ tables_to_replace->GetValueChecked<FixedArray>(isolate, i)
+ ->GetValueChecked<FixedArray>(isolate, kTable);
+ for (int fct_index = 0; fct_index < wasm_functions->length(); ++fct_index) {
+ Handle<Code> wasm_function =
+ wasm_functions->GetValueChecked<Code>(isolate, fct_index);
+ PatchFunctionTable(wasm_function, table_to_replace, cloned_table);
}
}
+ return cloned_indirect_tables;
+}
- if (!memory.is_null()) {
- instance->SetInternalField(kWasmMemArrayBuffer, *memory);
- Address mem_start = static_cast<Address>(memory->backing_store());
- uint32_t mem_size = static_cast<uint32_t>(memory->byte_length()->Number());
- RelocateInstanceCode(instance, mem_start,
- WasmModule::kPageSize * min_mem_pages, mem_size);
- LoadDataSegments(compiled_module, mem_start, mem_size);
+} // namespace
+
+const char* SectionName(WasmSectionCode code) {
+ switch (code) {
+ case kUnknownSectionCode:
+ return "Unknown";
+ case kTypeSectionCode:
+ return "Type";
+ case kImportSectionCode:
+ return "Import";
+ case kFunctionSectionCode:
+ return "Function";
+ case kTableSectionCode:
+ return "Table";
+ case kMemorySectionCode:
+ return "Memory";
+ case kGlobalSectionCode:
+ return "Global";
+ case kExportSectionCode:
+ return "Export";
+ case kStartSectionCode:
+ return "Start";
+ case kCodeSectionCode:
+ return "Code";
+ case kElementSectionCode:
+ return "Element";
+ case kDataSectionCode:
+ return "Data";
+ case kNameSectionCode:
+ return "Name";
+ default:
+ return "<unknown>";
}
- return true;
}
-bool SetupImports(Isolate* isolate, Handle<FixedArray> compiled_module,
- Handle<JSObject> instance, ErrorThrower* thrower,
- Handle<JSReceiver> ffi) {
- //-------------------------------------------------------------------------
- // Compile wrappers to imported functions.
- //-------------------------------------------------------------------------
- std::vector<Handle<Code>> import_code;
- MaybeHandle<FixedArray> maybe_import_data =
- compiled_module->GetValue<FixedArray>(isolate, kImportData);
- Handle<FixedArray> import_data;
- if (maybe_import_data.ToHandle(&import_data)) {
- if (!CompileWrappersToImportedFunctions(isolate, ffi, import_code,
- import_data, thrower)) {
- return false;
+std::ostream& operator<<(std::ostream& os, const WasmModule& module) {
+ os << "WASM module with ";
+ os << (module.min_mem_pages * module.kPageSize) << " min mem";
+ os << (module.max_mem_pages * module.kPageSize) << " max mem";
+ os << module.functions.size() << " functions";
+ os << module.functions.size() << " globals";
+ os << module.functions.size() << " data segments";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const WasmFunction& function) {
+ os << "WASM function with signature " << *function.sig;
+
+ os << " code bytes: "
+ << (function.code_end_offset - function.code_start_offset);
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) {
+ os << "#" << pair.function_->func_index << ":";
+ if (pair.function_->name_offset > 0) {
+ if (pair.module_) {
+ WasmName name = pair.module_->GetName(pair.function_->name_offset,
+ pair.function_->name_length);
+ os.write(name.start(), name.length());
+ } else {
+ os << "+" << pair.function_->func_index;
}
+ } else {
+ os << "?";
}
+ return os;
+}
- RecordStats(isolate, import_code);
+Handle<JSFunction> WrapExportCodeAsJSFunction(
+ Isolate* isolate, Handle<Code> export_code, Handle<String> name, int arity,
+ MaybeHandle<ByteArray> maybe_signature, Handle<JSObject> module_instance) {
+ Handle<SharedFunctionInfo> shared =
+ isolate->factory()->NewSharedFunctionInfo(name, export_code, false);
+ shared->set_length(arity);
+ shared->set_internal_formal_parameter_count(arity);
+ Handle<JSFunction> function = isolate->factory()->NewFunction(
+ isolate->wasm_function_map(), name, export_code);
+ function->set_shared(*shared);
- Handle<FixedArray> code_table = Handle<FixedArray>(
- FixedArray::cast(instance->GetInternalField(kWasmModuleCodeTable)));
- // TODO(mtrofin): get the code off std::vector and on FixedArray, for
- // consistency.
- std::vector<Handle<Code>> function_code(code_table->length());
- for (int i = 0; i < code_table->length(); ++i) {
- Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i)));
- function_code[i] = code;
+ function->SetInternalField(kInternalModuleInstance, *module_instance);
+ // add another Internal Field as the function arity
+ function->SetInternalField(kInternalArity, Smi::FromInt(arity));
+ // add another Internal Field as the signature of the foreign function
+ Handle<ByteArray> signature;
+ if (maybe_signature.ToHandle(&signature)) {
+ function->SetInternalField(kInternalSignature, *signature);
}
-
- LinkImports(isolate, function_code, import_code);
- return true;
+ return function;
}
-bool SetupExportsObject(Handle<FixedArray> compiled_module, Isolate* isolate,
- Handle<JSObject> instance, ErrorThrower* thrower) {
- Factory* factory = isolate->factory();
- bool mem_export =
- static_cast<bool>(Smi::cast(compiled_module->get(kExportMem))->value());
- ModuleOrigin origin = static_cast<ModuleOrigin>(
- Smi::cast(compiled_module->get(kOrigin))->value());
-
- MaybeHandle<FixedArray> maybe_exports =
- compiled_module->GetValue<FixedArray>(isolate, kExports);
- if (!maybe_exports.is_null() || mem_export) {
- PropertyDescriptor desc;
- desc.set_writable(false);
+Object* GetOwningWasmInstance(Code* code) {
+ DCHECK(code->kind() == Code::WASM_FUNCTION);
+ DisallowHeapAllocation no_gc;
+ FixedArray* deopt_data = code->deoptimization_data();
+ DCHECK_NOT_NULL(deopt_data);
+ DCHECK(deopt_data->length() == 2);
+ Object* weak_link = deopt_data->get(0);
+ if (!weak_link->IsWeakCell()) return nullptr;
+ WeakCell* cell = WeakCell::cast(weak_link);
+ return cell->value();
+}
- Handle<JSObject> exports_object = instance;
- if (origin == kWasmOrigin) {
- // Create the "exports" object.
- Handle<JSFunction> object_function = Handle<JSFunction>(
- isolate->native_context()->object_function(), isolate);
- exports_object = factory->NewJSObject(object_function, TENURED);
- Handle<String> exports_name = factory->InternalizeUtf8String("exports");
- JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY);
- }
- Handle<FixedArray> exports;
- if (maybe_exports.ToHandle(&exports)) {
- int exports_size = exports->length();
- for (int i = 0; i < exports_size; ++i) {
- if (thrower->error()) return false;
- Handle<FixedArray> export_metadata =
- exports->GetValueChecked<FixedArray>(isolate, i);
- Handle<Code> export_code =
- export_metadata->GetValueChecked<Code>(isolate, kExportCode);
- RecordStats(isolate, *export_code);
- Handle<String> name =
- export_metadata->GetValueChecked<String>(isolate, kExportName);
- int arity = Smi::cast(export_metadata->get(kExportArity))->value();
- MaybeHandle<ByteArray> signature =
- export_metadata->GetValue<ByteArray>(isolate, kExportedSignature);
- Handle<JSFunction> function = WrapExportCodeAsJSFunction(
- isolate, export_code, name, arity, signature, instance);
- desc.set_value(function);
- Maybe<bool> status = JSReceiver::DefineOwnProperty(
- isolate, exports_object, name, &desc, Object::THROW_ON_ERROR);
- if (!status.IsJust()) {
- thrower->Error("export of %.*s failed.", name->length(),
- name->ToCString().get());
- return false;
- }
- }
- }
- if (mem_export) {
- // Export the memory as a named property.
- Handle<String> name = factory->InternalizeUtf8String("memory");
- Handle<JSArrayBuffer> memory = Handle<JSArrayBuffer>(
- JSArrayBuffer::cast(instance->GetInternalField(kWasmMemArrayBuffer)));
- JSObject::AddProperty(exports_object, name, memory, READ_ONLY);
- }
- }
- return true;
+uint32_t GetNumImportedFunctions(Handle<JSObject> wasm_object) {
+ return static_cast<uint32_t>(
+ Smi::cast(wasm_object->GetInternalField(kWasmNumImportedFunctions))
+ ->value());
}
-} // namespace
+WasmModule::WasmModule(byte* module_start)
+ : module_start(module_start),
+ module_end(nullptr),
+ min_mem_pages(0),
+ max_mem_pages(0),
+ mem_export(false),
+ start_function_index(-1),
+ origin(kWasmOrigin),
+ globals_size(0),
+ num_imported_functions(0),
+ num_declared_functions(0),
+ num_exported_functions(0),
+ pending_tasks(new base::Semaphore(0)) {}
-MaybeHandle<FixedArray> WasmModule::CompileFunctions(
+MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
Isolate* isolate, ErrorThrower* thrower) const {
Factory* factory = isolate->factory();
- MaybeHandle<FixedArray> nothing;
+ MaybeHandle<WasmCompiledModule> nothing;
- WasmModuleInstance temp_instance_for_compilation(this);
- temp_instance_for_compilation.context = isolate->native_context();
- temp_instance_for_compilation.mem_size = GetMinModuleMemSize(this);
- temp_instance_for_compilation.mem_start = nullptr;
- temp_instance_for_compilation.globals_start = nullptr;
+ WasmModuleInstance temp_instance(this);
+ temp_instance.context = isolate->native_context();
+ temp_instance.mem_size = GetMinModuleMemSize(this);
+ temp_instance.mem_start = nullptr;
+ temp_instance.globals_start = nullptr;
MaybeHandle<FixedArray> indirect_table =
function_tables.size()
@@ -1062,10 +1027,10 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(
: MaybeHandle<FixedArray>();
for (uint32_t i = 0; i < function_tables.size(); ++i) {
Handle<FixedArray> values = wasm::BuildFunctionTable(isolate, i, this);
- temp_instance_for_compilation.function_tables[i] = values;
+ temp_instance.function_tables[i] = values;
Handle<FixedArray> metadata = isolate->factory()->NewFixedArray(
- kWasmIndirectFunctionTableMetadataSize, TENURED);
+ kWasmIndirectFunctionTableDataSize, TENURED);
metadata->set(kSize, Smi::FromInt(function_tables[i].size));
metadata->set(kTable, *values);
indirect_table.ToHandleChecked()->set(i, *metadata);
@@ -1076,61 +1041,90 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(
ModuleEnv module_env;
module_env.module = this;
- module_env.instance = &temp_instance_for_compilation;
+ module_env.instance = &temp_instance;
module_env.origin = origin;
- InitializePlaceholders(factory, &module_env.placeholders, functions.size());
- Handle<FixedArray> compiled_functions =
- factory->NewFixedArray(static_cast<int>(functions.size()), TENURED);
+ // 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>(functions.size() + num_exported_functions);
+ Handle<FixedArray> code_table =
+ factory->NewFixedArray(static_cast<int>(code_table_size), TENURED);
- temp_instance_for_compilation.import_code.resize(import_table.size());
- for (uint32_t i = 0; i < import_table.size(); ++i) {
- temp_instance_for_compilation.import_code[i] =
- CreatePlaceholder(factory, i, Code::WASM_TO_JS_FUNCTION);
+ // Initialize the code table with placeholders.
+ for (uint32_t i = 0; i < functions.size(); i++) {
+ Code::Kind kind = Code::WASM_FUNCTION;
+ if (i < num_imported_functions) kind = Code::WASM_TO_JS_FUNCTION;
+ Handle<Code> placeholder = CreatePlaceholder(factory, i, kind);
+ code_table->set(static_cast<int>(i), *placeholder);
+ temp_instance.function_code[i] = placeholder;
}
+
isolate->counters()->wasm_functions_per_module()->AddSample(
static_cast<int>(functions.size()));
- if (FLAG_wasm_num_compilation_tasks != 0) {
- CompileInParallel(isolate, this,
- temp_instance_for_compilation.function_code, thrower,
- &module_env);
+ if (!FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks != 0) {
+ // Avoid a race condition by collecting results into a second vector.
+ std::vector<Handle<Code>> results;
+ results.reserve(temp_instance.function_code.size());
+ for (size_t i = 0; i < temp_instance.function_code.size(); i++) {
+ results.push_back(temp_instance.function_code[i]);
+ }
+ CompileInParallel(isolate, this, results, thrower, &module_env);
+
+ for (size_t i = 0; i < results.size(); i++) {
+ temp_instance.function_code[i] = results[i];
+ }
} else {
- CompileSequentially(isolate, this,
- temp_instance_for_compilation.function_code, thrower,
+ CompileSequentially(isolate, this, temp_instance.function_code, thrower,
&module_env);
}
if (thrower->error()) return nothing;
// At this point, compilation has completed. Update the code table.
for (size_t i = FLAG_skip_compiling_wasm_funcs;
- i < temp_instance_for_compilation.function_code.size(); ++i) {
- Code* code = *temp_instance_for_compilation.function_code[i];
- compiled_functions->set(static_cast<int>(i), code);
+ i < temp_instance.function_code.size(); ++i) {
+ Code* code = *temp_instance.function_code[i];
+ code_table->set(static_cast<int>(i), code);
+ }
+
+ // Link the functions in the module.
+ for (size_t i = FLAG_skip_compiling_wasm_funcs;
+ i < temp_instance.function_code.size(); ++i) {
+ Handle<Code> code = temp_instance.function_code[i];
+ bool modified = LinkFunction(code, temp_instance.function_code);
+ if (modified) {
+ // TODO(mtrofin): do we need to flush the cache here?
+ Assembler::FlushICache(isolate, code->instruction_start(),
+ code->instruction_size());
+ }
}
// 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<FixedArray> ret =
- factory->NewFixedArray(kCompiledWasmObjectTableSize, TENURED);
- ret->set(kFunctions, *compiled_functions);
+ Handle<WasmCompiledModule> ret = WasmCompiledModule::New(
+ isolate, min_mem_pages, globals_size, mem_export, origin);
+ ret->set_code_table(code_table);
if (!indirect_table.is_null()) {
- ret->set(kTableOfIndirectFunctionTables, *indirect_table.ToHandleChecked());
+ ret->set_indirect_function_tables(indirect_table.ToHandleChecked());
}
- Handle<FixedArray> import_data = GetImportsMetadata(factory, this);
- ret->set(kImportData, *import_data);
+ Handle<FixedArray> import_data = GetImportsData(factory, this);
+ ret->set_import_data(import_data);
- // Compile export functions.
- int export_size = static_cast<int>(export_table.size());
- Handle<Code> startup_fct;
+ // Compile exported function wrappers.
+ int export_size = static_cast<int>(num_exported_functions);
if (export_size > 0) {
Handle<FixedArray> exports = factory->NewFixedArray(export_size, TENURED);
- for (int i = 0; i < export_size; ++i) {
- Handle<FixedArray> export_metadata =
- factory->NewFixedArray(kWasmExportMetadataTableSize, TENURED);
- const WasmExport& exp = export_table[i];
- FunctionSig* funcSig = functions[exp.func_index].sig;
+ int index = -1;
+
+ for (const WasmExport& exp : export_table) {
+ if (exp.kind != kExternalFunction)
+ continue; // skip non-function exports.
+ index++;
+ Handle<FixedArray> export_data =
+ factory->NewFixedArray(kWasmExportDataSize, TENURED);
+ FunctionSig* funcSig = functions[exp.index].sig;
Handle<ByteArray> exportedSig =
factory->NewByteArray(static_cast<int>(funcSig->parameter_count() +
funcSig->return_count()),
@@ -1138,45 +1132,34 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(
exportedSig->copy_in(0,
reinterpret_cast<const byte*>(funcSig->raw_data()),
exportedSig->length());
- export_metadata->set(kExportedSignature, *exportedSig);
+ export_data->set(kExportedSignature, *exportedSig);
WasmName str = GetName(exp.name_offset, exp.name_length);
Handle<String> name = factory->InternalizeUtf8String(str);
- Handle<Code> code =
- temp_instance_for_compilation.function_code[exp.func_index];
+ Handle<Code> code = code_table->GetValueChecked<Code>(isolate, exp.index);
Handle<Code> export_code = compiler::CompileJSToWasmWrapper(
- isolate, &module_env, code, exp.func_index);
+ isolate, &module_env, code, exp.index);
if (thrower->error()) return nothing;
- export_metadata->set(kExportCode, *export_code);
- export_metadata->set(kExportName, *name);
- export_metadata->set(
- kExportArity, Smi::FromInt(static_cast<int>(
- functions[exp.func_index].sig->parameter_count())));
- export_metadata->set(kExportedFunctionIndex,
- Smi::FromInt(static_cast<int>(exp.func_index)));
- exports->set(i, *export_metadata);
- if (exp.func_index == start_function_index) {
- startup_fct = export_code;
- }
+ export_data->set(kExportName, *name);
+ export_data->set(kExportArity,
+ Smi::FromInt(static_cast<int>(
+ functions[exp.index].sig->parameter_count())));
+ export_data->set(kExportedFunctionIndex,
+ Smi::FromInt(static_cast<int>(exp.index)));
+ exports->set(index, *export_data);
+ code_table->set(static_cast<int>(functions.size() + index), *export_code);
}
- ret->set(kExports, *exports);
+ ret->set_exports(exports);
}
- // Compile startup function, if we haven't already.
+ // Record data for startup function.
if (start_function_index >= 0) {
- uint32_t index = static_cast<uint32_t>(start_function_index);
HandleScope scope(isolate);
- if (startup_fct.is_null()) {
- Handle<Code> code = temp_instance_for_compilation.function_code[index];
- DCHECK_EQ(0, functions[index].sig->parameter_count());
- startup_fct =
- compiler::CompileJSToWasmWrapper(isolate, &module_env, code, index);
- }
- Handle<FixedArray> metadata =
- factory->NewFixedArray(kWasmExportMetadataTableSize, TENURED);
- metadata->set(kExportCode, *startup_fct);
- metadata->set(kExportArity, Smi::FromInt(0));
- metadata->set(kExportedFunctionIndex, Smi::FromInt(start_function_index));
- ret->set(kStartupFunction, *metadata);
+ Handle<FixedArray> startup_data =
+ factory->NewFixedArray(kWasmExportDataSize, TENURED);
+ startup_data->set(kExportArity, Smi::FromInt(0));
+ startup_data->set(kExportedFunctionIndex,
+ Smi::FromInt(start_function_index));
+ ret->set_startup_function(startup_data);
}
// TODO(wasm): saving the module bytes for debugging is wasteful. We should
@@ -1189,213 +1172,334 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(
Handle<String> module_bytes_string =
factory->NewStringFromOneByte(module_bytes_vec, TENURED)
.ToHandleChecked();
- ret->set(kModuleBytes, *module_bytes_string);
+ ret->set_module_bytes(module_bytes_string);
}
Handle<ByteArray> function_name_table =
BuildFunctionNamesTable(isolate, module_env.module);
- ret->set(kFunctionNameTable, *function_name_table);
- ret->set(kMinRequiredMemory, Smi::FromInt(min_mem_pages));
+ ret->set_function_names(function_name_table);
if (data_segments.size() > 0) SaveDataSegmentInfo(factory, this, ret);
- ret->set(kGlobalsSize, Smi::FromInt(globals_size));
- ret->set(kExportMem, Smi::FromInt(mem_export));
- ret->set(kOrigin, Smi::FromInt(origin));
+ DCHECK_EQ(ret->default_mem_size(), temp_instance.mem_size);
return ret;
}
-void PatchJSWrapper(Isolate* isolate, Handle<Code> wrapper,
- Handle<Code> new_target) {
- AllowDeferredHandleDereference embedding_raw_address;
- bool seen = false;
- for (RelocIterator it(*wrapper, 1 << RelocInfo::CODE_TARGET); !it.done();
- it.next()) {
- Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (target->kind() == Code::WASM_FUNCTION) {
- DCHECK(!seen);
- seen = true;
- it.rinfo()->set_target_address(new_target->instruction_start(),
- UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
+// Instantiates a WASM module, creating a WebAssembly.Instance from a
+// WebAssembly.Module.
+MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
+ ErrorThrower* thrower,
+ Handle<JSObject> module_object,
+ Handle<JSReceiver> ffi,
+ Handle<JSArrayBuffer> memory) {
+ MaybeHandle<JSObject> nothing;
+ HistogramTimerScope wasm_instantiate_module_time_scope(
+ isolate->counters()->wasm_instantiate_module_time());
+ Factory* factory = isolate->factory();
+
+ //--------------------------------------------------------------------------
+ // Reuse the compiled module (if no owner), otherwise clone.
+ //--------------------------------------------------------------------------
+ Handle<WasmCompiledModule> compiled_module;
+ Handle<FixedArray> code_table;
+ Handle<FixedArray> old_code_table;
+ Handle<JSObject> owner;
+ // If we don't clone, this will be null(). Otherwise, this will
+ // be a weak link to the original. If we lose the original to GC,
+ // this will be a cleared. We'll link the instances chain last.
+ MaybeHandle<WeakCell> link_to_original;
+
+ TRACE("Starting new module instantiation\n");
+ {
+ Handle<WasmCompiledModule> original(
+ WasmCompiledModule::cast(module_object->GetInternalField(0)), isolate);
+ // Always make a new copy of the code_table, since the old_code_table
+ // may still have placeholders for imports.
+ old_code_table = original->code_table();
+ code_table = factory->CopyFixedArray(old_code_table);
+
+ if (original->has_weak_owning_instance()) {
+ WeakCell* tmp = original->ptr_to_weak_owning_instance();
+ DCHECK(!tmp->cleared());
+ // There is already an owner, clone everything.
+ owner = Handle<JSObject>(JSObject::cast(tmp->value()), isolate);
+ // Insert the latest clone in front.
+ TRACE("Cloning from %d\n", original->instance_id());
+ compiled_module = WasmCompiledModule::Clone(isolate, original);
+ // Replace the strong reference to point to the new instance here.
+ // This allows any of the other instances, including the original,
+ // to be collected.
+ module_object->SetInternalField(0, *compiled_module);
+ compiled_module->set_weak_module_object(original->weak_module_object());
+ link_to_original = factory->NewWeakCell(original);
+ // Don't link to original here. We remember the original
+ // as a weak link. If that link isn't clear by the time we finish
+ // instantiating this instance, then we link it at that time.
+ compiled_module->reset_weak_next_instance();
+
+ // Clone the code for WASM functions and exports.
+ for (int i = 0; i < code_table->length(); ++i) {
+ Handle<Code> orig_code = code_table->GetValueChecked<Code>(isolate, i);
+ switch (orig_code->kind()) {
+ case Code::WASM_TO_JS_FUNCTION:
+ // Imports will be overwritten with newly compiled wrappers.
+ 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);
+ } else {
+ // There was no owner, so we can reuse the original.
+ compiled_module = original;
+ TRACE("Reusing existing instance %d\n", compiled_module->instance_id());
}
+ compiled_module->set_code_table(code_table);
}
- CHECK(seen);
- Assembler::FlushICache(isolate, wrapper->instruction_start(),
- wrapper->instruction_size());
-}
-Handle<FixedArray> SetupIndirectFunctionTable(
- Isolate* isolate, Handle<FixedArray> wasm_functions,
- Handle<FixedArray> indirect_table_template) {
- Factory* factory = isolate->factory();
- Handle<FixedArray> cloned_indirect_tables =
- factory->CopyFixedArray(indirect_table_template);
- for (int i = 0; i < cloned_indirect_tables->length(); ++i) {
- Handle<FixedArray> orig_metadata =
- cloned_indirect_tables->GetValueChecked<FixedArray>(isolate, i);
- Handle<FixedArray> cloned_metadata = factory->CopyFixedArray(orig_metadata);
- cloned_indirect_tables->set(i, *cloned_metadata);
+ //--------------------------------------------------------------------------
+ // Allocate the instance object.
+ //--------------------------------------------------------------------------
+ Handle<Map> map = factory->NewMap(
+ JS_OBJECT_TYPE,
+ JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
+ Handle<JSObject> instance = factory->NewJSObjectFromMap(map, TENURED);
+ instance->SetInternalField(kWasmModuleCodeTable, *code_table);
- Handle<FixedArray> orig_table =
- cloned_metadata->GetValueChecked<FixedArray>(isolate, kTable);
- Handle<FixedArray> cloned_table = factory->CopyFixedArray(orig_table);
- cloned_metadata->set(kTable, *cloned_table);
- // Patch the cloned code to refer to the cloned kTable.
- for (int i = 0; i < wasm_functions->length(); ++i) {
- Handle<Code> wasm_function =
- wasm_functions->GetValueChecked<Code>(isolate, i);
- PatchFunctionTable(wasm_function, orig_table, cloned_table);
- }
+ //--------------------------------------------------------------------------
+ // Set up the memory for the new instance.
+ //--------------------------------------------------------------------------
+ MaybeHandle<JSArrayBuffer> old_memory;
+ // TODO(titzer): handle imported memory properly.
+
+ uint32_t min_mem_pages = compiled_module->min_memory_pages();
+ isolate->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages);
+ // TODO(wasm): re-enable counter for max_mem_pages when we use that field.
+
+ if (memory.is_null() && min_mem_pages > 0) {
+ memory = AllocateMemory(thrower, isolate, min_mem_pages);
+ if (memory.is_null()) return nothing; // failed to allocate memory
}
- return cloned_indirect_tables;
-}
-Handle<FixedArray> CloneModuleForInstance(Isolate* isolate,
- Handle<FixedArray> original) {
- Factory* factory = isolate->factory();
- Handle<FixedArray> clone = factory->CopyFixedArray(original);
-
- // Clone each wasm code object.
- Handle<FixedArray> orig_wasm_functions =
- original->GetValueChecked<FixedArray>(isolate, kFunctions);
- Handle<FixedArray> clone_wasm_functions =
- factory->CopyFixedArray(orig_wasm_functions);
- clone->set(kFunctions, *clone_wasm_functions);
- for (int i = 0; i < clone_wasm_functions->length(); ++i) {
- Handle<Code> orig_code =
- clone_wasm_functions->GetValueChecked<Code>(isolate, i);
- Handle<Code> cloned_code = factory->CopyCode(orig_code);
- clone_wasm_functions->set(i, *cloned_code);
- }
-
- MaybeHandle<FixedArray> maybe_orig_exports =
- original->GetValue<FixedArray>(isolate, kExports);
- Handle<FixedArray> orig_exports;
- if (maybe_orig_exports.ToHandle(&orig_exports)) {
- Handle<FixedArray> cloned_exports = factory->CopyFixedArray(orig_exports);
- clone->set(kExports, *cloned_exports);
- for (int i = 0; i < orig_exports->length(); ++i) {
- Handle<FixedArray> export_metadata =
- orig_exports->GetValueChecked<FixedArray>(isolate, i);
- Handle<FixedArray> clone_metadata =
- factory->CopyFixedArray(export_metadata);
- cloned_exports->set(i, *clone_metadata);
- Handle<Code> orig_code =
- export_metadata->GetValueChecked<Code>(isolate, kExportCode);
- Handle<Code> cloned_code = factory->CopyCode(orig_code);
- clone_metadata->set(kExportCode, *cloned_code);
- // TODO(wasm): This is actually a uint32_t, but since FixedArray indexes
- // in int, we are taking the risk of invalid values.
- int exported_fct_index =
- Smi::cast(export_metadata->get(kExportedFunctionIndex))->value();
- CHECK_GE(exported_fct_index, 0);
- CHECK_LT(exported_fct_index, clone_wasm_functions->length());
- Handle<Code> new_target = clone_wasm_functions->GetValueChecked<Code>(
- isolate, exported_fct_index);
- PatchJSWrapper(isolate, cloned_code, new_target);
+ if (!memory.is_null()) {
+ instance->SetInternalField(kWasmMemArrayBuffer, *memory);
+ Address mem_start = static_cast<Address>(memory->backing_store());
+ uint32_t mem_size = static_cast<uint32_t>(memory->byte_length()->Number());
+ LoadDataSegments(compiled_module, mem_start, mem_size);
+
+ uint32_t old_mem_size = compiled_module->has_heap()
+ ? compiled_module->mem_size()
+ : compiled_module->default_mem_size();
+ Address old_mem_start =
+ compiled_module->has_heap()
+ ? static_cast<Address>(compiled_module->heap()->backing_store())
+ : nullptr;
+ RelocateInstanceCode(instance, old_mem_start, mem_start, old_mem_size,
+ mem_size);
+ compiled_module->set_heap(memory);
+ }
+
+ //--------------------------------------------------------------------------
+ // Set up the globals for the new instance.
+ //--------------------------------------------------------------------------
+ MaybeHandle<JSArrayBuffer> old_globals;
+ MaybeHandle<JSArrayBuffer> globals;
+ uint32_t globals_size = compiled_module->globals_size();
+ if (globals_size > 0) {
+ Handle<JSArrayBuffer> global_buffer = NewArrayBuffer(isolate, globals_size);
+ globals = global_buffer;
+ if (globals.is_null()) {
+ thrower->Error("Out of memory: wasm globals");
+ return nothing;
+ }
+ Address old_address =
+ owner.is_null() ? nullptr : GetGlobalStartAddressFromCodeTemplate(
+ *isolate->factory()->undefined_value(),
+ JSObject::cast(*owner));
+ RelocateGlobals(instance, old_address,
+ static_cast<Address>(global_buffer->backing_store()));
+ instance->SetInternalField(kWasmGlobalsArrayBuffer, *global_buffer);
+ }
+
+ //--------------------------------------------------------------------------
+ // Compile the import wrappers for the new instance.
+ //--------------------------------------------------------------------------
+ // TODO(titzer): handle imported globals and function tables.
+ int num_imported_functions = 0;
+ if (compiled_module->has_import_data()) {
+ Handle<FixedArray> import_data = compiled_module->import_data();
+ num_imported_functions = import_data->length();
+ for (int index = 0; index < num_imported_functions; index++) {
+ Handle<Code> import_wrapper =
+ CompileImportWrapper(isolate, ffi, index, import_data, thrower);
+ if (thrower->error()) return nothing;
+ code_table->set(index, *import_wrapper);
+ RecordStats(isolate, *import_wrapper);
}
}
- MaybeHandle<FixedArray> maybe_startup =
- original->GetValue<FixedArray>(isolate, kStartupFunction);
- if (!maybe_startup.is_null()) {
- Handle<FixedArray> startup_metadata =
- factory->CopyFixedArray(maybe_startup.ToHandleChecked());
- Handle<Code> startup_fct_clone = factory->CopyCode(
- startup_metadata->GetValueChecked<Code>(isolate, kExportCode));
- startup_metadata->set(kExportCode, *startup_fct_clone);
- clone->set(kStartupFunction, *startup_metadata);
- // TODO(wasm): see todo above about int vs size_t indexing in FixedArray.
- int startup_fct_index =
- Smi::cast(startup_metadata->get(kExportedFunctionIndex))->value();
- CHECK_GE(startup_fct_index, 0);
- CHECK_LT(startup_fct_index, clone_wasm_functions->length());
- Handle<Code> new_target =
- clone_wasm_functions->GetValueChecked<Code>(isolate, startup_fct_index);
- PatchJSWrapper(isolate, startup_fct_clone, new_target);
- }
- return clone;
-}
+ //--------------------------------------------------------------------------
+ // Set up the debug support for the new instance.
+ //--------------------------------------------------------------------------
+ // TODO(wasm): avoid referencing this stuff from the instance, use it off
+ // the compiled module instead. See the following 3 assignments:
+ if (compiled_module->has_module_bytes()) {
+ instance->SetInternalField(kWasmModuleBytesString,
+ compiled_module->ptr_to_module_bytes());
+ }
-// Instantiates a wasm module as a JSObject.
-// * allocates a backing store of {mem_size} bytes.
-// * installs a named property "memory" for that buffer if exported
-// * installs named properties on the object for exported functions
-// * compiles wasm code to machine code
-MaybeHandle<JSObject> WasmModule::Instantiate(
- Isolate* isolate, Handle<FixedArray> compiled_module,
- Handle<JSReceiver> ffi, Handle<JSArrayBuffer> memory) {
- HistogramTimerScope wasm_instantiate_module_time_scope(
- isolate->counters()->wasm_instantiate_module_time());
- ErrorThrower thrower(isolate, "WasmModule::Instantiate()");
- Factory* factory = isolate->factory();
+ if (compiled_module->has_function_names()) {
+ instance->SetInternalField(kWasmFunctionNamesArray,
+ compiled_module->ptr_to_function_names());
+ }
- compiled_module = CloneModuleForInstance(isolate, compiled_module);
+ {
+ Handle<Object> handle = factory->NewNumber(num_imported_functions);
+ instance->SetInternalField(kWasmNumImportedFunctions, *handle);
+ }
+
+ //--------------------------------------------------------------------------
+ // Set up the runtime support for the new instance.
+ //--------------------------------------------------------------------------
+ Handle<WeakCell> weak_link = isolate->factory()->NewWeakCell(instance);
+
+ for (int i = num_imported_functions + FLAG_skip_compiling_wasm_funcs;
+ i < code_table->length(); ++i) {
+ Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
+ if (code->kind() == Code::WASM_FUNCTION) {
+ Handle<FixedArray> deopt_data =
+ isolate->factory()->NewFixedArray(2, TENURED);
+ deopt_data->set(0, *weak_link);
+ deopt_data->set(1, Smi::FromInt(static_cast<int>(i)));
+ deopt_data->set_length(2);
+ code->set_deoptimization_data(*deopt_data);
+ }
+ }
- // These fields are compulsory.
- Handle<FixedArray> code_table =
- compiled_module->GetValueChecked<FixedArray>(isolate, kFunctions);
+ //--------------------------------------------------------------------------
+ // Set up the indirect function tables for the new instance.
+ //--------------------------------------------------------------------------
+ {
+ std::vector<Handle<Code>> functions(
+ static_cast<size_t>(code_table->length()));
+ for (int i = 0; i < code_table->length(); ++i) {
+ functions[i] = code_table->GetValueChecked<Code>(isolate, i);
+ }
- std::vector<Handle<Code>> functions(
- static_cast<size_t>(code_table->length()));
- for (int i = 0; i < code_table->length(); ++i) {
- functions[static_cast<size_t>(i)] =
- code_table->GetValueChecked<Code>(isolate, i);
+ if (compiled_module->has_indirect_function_tables()) {
+ Handle<FixedArray> indirect_tables_template =
+ compiled_module->indirect_function_tables();
+ Handle<FixedArray> to_replace =
+ owner.is_null() ? indirect_tables_template
+ : handle(FixedArray::cast(owner->GetInternalField(
+ kWasmModuleFunctionTable)));
+ Handle<FixedArray> indirect_tables = SetupIndirectFunctionTable(
+ isolate, code_table, indirect_tables_template, to_replace);
+ for (int i = 0; i < indirect_tables->length(); ++i) {
+ Handle<FixedArray> metadata =
+ indirect_tables->GetValueChecked<FixedArray>(isolate, i);
+ uint32_t size = Smi::cast(metadata->get(kSize))->value();
+ Handle<FixedArray> table =
+ metadata->GetValueChecked<FixedArray>(isolate, kTable);
+ PopulateFunctionTable(table, size, &functions);
+ }
+ instance->SetInternalField(kWasmModuleFunctionTable, *indirect_tables);
+ }
}
- LinkModuleFunctions(isolate, functions);
- RecordStats(isolate, code_table);
+ //--------------------------------------------------------------------------
+ // Set up the exports object for the new instance.
+ //--------------------------------------------------------------------------
+ bool mem_export = compiled_module->export_memory();
+ ModuleOrigin origin = compiled_module->origin();
- MaybeHandle<JSObject> nothing;
+ if (compiled_module->has_exports() || mem_export) {
+ PropertyDescriptor desc;
+ desc.set_writable(false);
- Handle<Map> map = factory->NewMap(
- JS_OBJECT_TYPE,
- JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
- Handle<JSObject> js_object = factory->NewJSObjectFromMap(map, TENURED);
- js_object->SetInternalField(kWasmModuleCodeTable, *code_table);
-
- if (!(SetupInstanceHeap(isolate, compiled_module, js_object, memory,
- &thrower) &&
- SetupGlobals(isolate, compiled_module, js_object, &thrower) &&
- SetupImports(isolate, compiled_module, js_object, &thrower, ffi) &&
- SetupExportsObject(compiled_module, isolate, js_object, &thrower))) {
- return nothing;
+ Handle<JSObject> exports_object = instance;
+ if (origin == kWasmOrigin) {
+ // Create the "exports" object.
+ Handle<JSFunction> object_function = Handle<JSFunction>(
+ isolate->native_context()->object_function(), isolate);
+ exports_object = factory->NewJSObject(object_function, TENURED);
+ Handle<String> exports_name = factory->InternalizeUtf8String("exports");
+ JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY);
+ }
+ int first_export = -1;
+ // TODO(wasm): another iteration over the code objects.
+ for (int i = 0; i < code_table->length(); i++) {
+ Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
+ if (code->kind() == Code::JS_TO_WASM_FUNCTION) {
+ first_export = i;
+ break;
+ }
+ }
+ if (compiled_module->has_exports()) {
+ Handle<FixedArray> exports = compiled_module->exports();
+ int export_size = exports->length();
+ for (int i = 0; i < export_size; ++i) {
+ Handle<FixedArray> export_data =
+ exports->GetValueChecked<FixedArray>(isolate, i);
+ Handle<String> name =
+ export_data->GetValueChecked<String>(isolate, kExportName);
+ int arity = Smi::cast(export_data->get(kExportArity))->value();
+ MaybeHandle<ByteArray> signature =
+ export_data->GetValue<ByteArray>(isolate, kExportedSignature);
+ Handle<Code> export_code =
+ code_table->GetValueChecked<Code>(isolate, first_export + i);
+ Handle<JSFunction> function = WrapExportCodeAsJSFunction(
+ isolate, export_code, name, arity, signature, instance);
+ desc.set_value(function);
+ Maybe<bool> status = JSReceiver::DefineOwnProperty(
+ isolate, exports_object, name, &desc, Object::THROW_ON_ERROR);
+ if (!status.IsJust()) {
+ thrower->Error("export of %.*s failed.", name->length(),
+ name->ToCString().get());
+ return nothing;
+ }
+ }
+ }
+ if (mem_export) {
+ // Export the memory as a named property.
+ Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>(
+ JSArrayBuffer::cast(instance->GetInternalField(kWasmMemArrayBuffer)));
+ Handle<Object> memory_object =
+ WasmJs::CreateWasmMemoryObject(isolate, buffer, false, 0);
+ // TODO(titzer): export the memory with the correct name.
+ Handle<String> name = factory->InternalizeUtf8String("memory");
+ JSObject::AddProperty(exports_object, name, memory_object, READ_ONLY);
+ }
}
- SetDebugSupport(factory, compiled_module, js_object);
-
- FlushAssemblyCache(isolate, code_table);
-
- MaybeHandle<FixedArray> maybe_indirect_tables =
- compiled_module->GetValue<FixedArray>(isolate,
- kTableOfIndirectFunctionTables);
- Handle<FixedArray> indirect_tables_template;
- if (maybe_indirect_tables.ToHandle(&indirect_tables_template)) {
- Handle<FixedArray> indirect_tables = SetupIndirectFunctionTable(
- isolate, code_table, indirect_tables_template);
- for (int i = 0; i < indirect_tables->length(); ++i) {
- Handle<FixedArray> metadata =
- indirect_tables->GetValueChecked<FixedArray>(isolate, i);
- uint32_t size = Smi::cast(metadata->get(kSize))->value();
- Handle<FixedArray> table =
- metadata->GetValueChecked<FixedArray>(isolate, kTable);
- wasm::PopulateFunctionTable(table, size, &functions);
- }
- js_object->SetInternalField(kWasmModuleFunctionTable, *indirect_tables);
+ if (num_imported_functions > 0 || !owner.is_null()) {
+ // If the code was cloned, or new imports were compiled, patch.
+ PatchDirectCalls(old_code_table, code_table, num_imported_functions);
}
+ FlushICache(isolate, code_table);
+
+ //--------------------------------------------------------------------------
// Run the start function if one was specified.
- MaybeHandle<FixedArray> maybe_startup_fct =
- compiled_module->GetValue<FixedArray>(isolate, kStartupFunction);
- Handle<FixedArray> metadata;
- if (maybe_startup_fct.ToHandle(&metadata)) {
+ //--------------------------------------------------------------------------
+ if (compiled_module->has_startup_function()) {
+ Handle<FixedArray> startup_data = compiled_module->startup_function();
HandleScope scope(isolate);
+ int32_t start_index =
+ startup_data->GetValueChecked<Smi>(isolate, kExportedFunctionIndex)
+ ->value();
Handle<Code> startup_code =
- metadata->GetValueChecked<Code>(isolate, kExportCode);
- int arity = Smi::cast(metadata->get(kExportArity))->value();
+ code_table->GetValueChecked<Code>(isolate, start_index);
+ int arity = Smi::cast(startup_data->get(kExportArity))->value();
MaybeHandle<ByteArray> startup_signature =
- metadata->GetValue<ByteArray>(isolate, kExportedSignature);
+ startup_data->GetValue<ByteArray>(isolate, kExportedSignature);
Handle<JSFunction> startup_fct = WrapExportCodeAsJSFunction(
isolate, startup_code, factory->InternalizeUtf8String("start"), arity,
- startup_signature, js_object);
+ startup_signature, instance);
RecordStats(isolate, *startup_code);
// Call the JS function.
Handle<Object> undefined = isolate->factory()->undefined_value();
@@ -1403,35 +1507,86 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
Execution::Call(isolate, startup_fct, undefined, 0, nullptr);
if (retval.is_null()) {
- thrower.Error("WASM.instantiateModule(): start function failed");
+ thrower->Error("WASM.instantiateModule(): start function failed");
return nothing;
}
}
- DCHECK(wasm::IsWasmObject(*js_object));
- return js_object;
+ DCHECK(wasm::IsWasmObject(*instance));
+
+ {
+ Handle<WeakCell> link_to_owner = factory->NewWeakCell(instance);
+
+ Handle<Object> global_handle = isolate->global_handles()->Create(*instance);
+ Handle<WeakCell> link_to_clone = factory->NewWeakCell(compiled_module);
+ {
+ DisallowHeapAllocation no_gc;
+ compiled_module->set_weak_owning_instance(link_to_owner);
+ Handle<WeakCell> next;
+ if (link_to_original.ToHandle(&next) && !next->cleared()) {
+ WasmCompiledModule* original = WasmCompiledModule::cast(next->value());
+ DCHECK(original->has_weak_owning_instance());
+ DCHECK(!original->weak_owning_instance()->cleared());
+ compiled_module->set_weak_next_instance(next);
+ original->set_weak_prev_instance(link_to_clone);
+ }
+
+ compiled_module->set_weak_owning_instance(link_to_owner);
+ instance->SetInternalField(kWasmCompiledModule, *compiled_module);
+ GlobalHandles::MakeWeak(global_handle.location(),
+ global_handle.location(), &InstanceFinalizer,
+ v8::WeakCallbackType::kFinalizer);
+ }
+ }
+ TRACE("Finishing instance %d\n", compiled_module->instance_id());
+ TRACE_CHAIN(WasmCompiledModule::cast(module_object->GetInternalField(0)));
+ return instance;
}
-// TODO(mtrofin): remove this once we move to WASM_DIRECT_CALL
-Handle<Code> ModuleEnv::GetCodeOrPlaceholder(uint32_t index) const {
- DCHECK(IsValidFunction(index));
- if (!placeholders.empty()) return placeholders[index];
- DCHECK_NOT_NULL(instance);
- return instance->function_code[index];
+#if DEBUG
+uint32_t WasmCompiledModule::instance_id_counter_ = 0;
+#endif
+
+Handle<WasmCompiledModule> WasmCompiledModule::New(Isolate* isolate,
+ uint32_t min_memory_pages,
+ uint32_t globals_size,
+ bool export_memory,
+ ModuleOrigin origin) {
+ Handle<FixedArray> ret =
+ isolate->factory()->NewFixedArray(PropertyIndices::Count, TENURED);
+ // Globals size is expected to fit into an int without overflow. This is not
+ // supported by the spec at the moment, however, we don't support array
+ // buffer sizes over 1g, so, for now, we avoid alocating a HeapNumber for
+ // the globals size. The CHECK guards this assumption.
+ CHECK_GE(static_cast<int>(globals_size), 0);
+ ret->set(kID_min_memory_pages,
+ Smi::FromInt(static_cast<int>(min_memory_pages)));
+ ret->set(kID_globals_size, Smi::FromInt(static_cast<int>(globals_size)));
+ ret->set(kID_export_memory, Smi::FromInt(static_cast<int>(export_memory)));
+ ret->set(kID_origin, Smi::FromInt(static_cast<int>(origin)));
+ WasmCompiledModule::cast(*ret)->Init();
+ return handle(WasmCompiledModule::cast(*ret));
}
-Handle<Code> ModuleEnv::GetImportCode(uint32_t index) {
- DCHECK(IsValidImport(index));
- return instance ? instance->import_code[index] : Handle<Code>::null();
+void WasmCompiledModule::Init() {
+#if DEBUG
+ set(kID_instance_id, Smi::FromInt(instance_id_counter_++));
+ TRACE("New compiled module id: %d\n", instance_id());
+#endif
}
-compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
- uint32_t index) {
- DCHECK(IsValidFunction(index));
- // Always make a direct call to whatever is in the table at that location.
- // A wrapper will be generated for FFI calls.
- const WasmFunction* function = &module->functions[index];
- return GetWasmCallDescriptor(zone, function->sig);
+void WasmCompiledModule::PrintInstancesChain() {
+#if DEBUG
+ if (!FLAG_trace_wasm_instances) return;
+ for (WasmCompiledModule* current = this; current != nullptr;) {
+ PrintF("->%d", current->instance_id());
+ if (current->ptr_to_weak_next_instance() == nullptr) break;
+ CHECK(!current->ptr_to_weak_next_instance()->cleared());
+ current =
+ WasmCompiledModule::cast(current->ptr_to_weak_next_instance()->value());
+ }
+ PrintF("\n");
+#endif
}
Handle<Object> GetWasmFunctionNameOrNull(Isolate* isolate, Handle<Object> wasm,
@@ -1577,93 +1732,188 @@ int GetNumberOfFunctions(JSObject* wasm) {
return ByteArray::cast(func_names_obj)->get_int(0);
}
-Handle<JSObject> CreateCompiledModuleObject(
- Isolate* isolate, Handle<FixedArray> compiled_module) {
- Handle<JSFunction> module_cons(
- isolate->native_context()->wasm_module_constructor());
- Handle<JSObject> module_obj = isolate->factory()->NewJSObject(module_cons);
+Handle<JSObject> CreateCompiledModuleObject(Isolate* isolate,
+ Handle<FixedArray> compiled_module,
+ ModuleOrigin origin) {
+ Handle<JSObject> module_obj;
+ if (origin == ModuleOrigin::kWasmOrigin) {
+ Handle<JSFunction> module_cons(
+ isolate->native_context()->wasm_module_constructor());
+ module_obj = isolate->factory()->NewJSObject(module_cons);
+ } else {
+ DCHECK(origin == ModuleOrigin::kAsmJsOrigin);
+ Handle<Map> map = isolate->factory()->NewMap(
+ JS_OBJECT_TYPE, JSObject::kHeaderSize + kPointerSize);
+ module_obj = isolate->factory()->NewJSObjectFromMap(map, TENURED);
+ }
module_obj->SetInternalField(0, *compiled_module);
- Handle<Symbol> module_sym(isolate->native_context()->wasm_module_sym());
- Object::SetProperty(module_obj, module_sym, module_obj, STRICT).Check();
+ if (origin == ModuleOrigin::kWasmOrigin) {
+ Handle<Symbol> module_sym(isolate->native_context()->wasm_module_sym());
+ Object::SetProperty(module_obj, module_sym, module_obj, STRICT).Check();
+ }
+ Handle<WeakCell> link_to_module = isolate->factory()->NewWeakCell(module_obj);
+ WasmCompiledModule::cast(*compiled_module)
+ ->set_weak_module_object(link_to_module);
return module_obj;
}
-namespace testing {
-
-int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
- const byte* module_end, bool asm_js) {
- HandleScope scope(isolate);
+MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
+ const byte* start,
+ const byte* end,
+ ErrorThrower* thrower,
+ ModuleOrigin origin) {
+ MaybeHandle<JSObject> nothing;
Zone zone(isolate->allocator());
- ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
-
- // Decode the module, but don't verify function bodies, since we'll
- // be compiling them anyway.
- ModuleResult decoding_result =
- DecodeWasmModule(isolate, &zone, module_start, module_end, false,
- asm_js ? kAsmJsOrigin : kWasmOrigin);
-
- std::unique_ptr<const WasmModule> module(decoding_result.val);
- if (decoding_result.failed()) {
- // Module verification failed. throw.
- thrower.Error("WASM.compileRun() failed: %s",
- decoding_result.error_msg.get());
- return -1;
+ ModuleResult result =
+ DecodeWasmModule(isolate, &zone, start, end, false, origin);
+ std::unique_ptr<const WasmModule> decoded_module(result.val);
+ if (result.failed()) {
+ thrower->Failed("Wasm decoding failed", result);
+ return nothing;
}
+ MaybeHandle<FixedArray> compiled_module =
+ decoded_module->CompileFunctions(isolate, thrower);
+ if (compiled_module.is_null()) return nothing;
- if (module->import_table.size() > 0) {
- thrower.Error("Not supported: module has imports.");
- }
- if (module->export_table.size() == 0) {
- thrower.Error("Not supported: module has no exports.");
+ return CreateCompiledModuleObject(isolate, compiled_module.ToHandleChecked(),
+ origin);
+}
+
+bool ValidateModuleBytes(Isolate* isolate, const byte* start, const byte* end,
+ ErrorThrower* thrower, ModuleOrigin origin) {
+ Zone zone(isolate->allocator());
+ ModuleResult result =
+ DecodeWasmModule(isolate, &zone, start, end, false, origin);
+ if (result.ok()) {
+ DCHECK_NOT_NULL(result.val);
+ delete result.val;
+ return true;
}
+ return false;
+}
- if (thrower.error()) return -1;
- MaybeHandle<FixedArray> compiled_module =
- module->CompileFunctions(isolate, &thrower);
+MaybeHandle<JSArrayBuffer> GetInstanceMemory(Isolate* isolate,
+ Handle<JSObject> instance) {
+ Object* mem = instance->GetInternalField(kWasmMemArrayBuffer);
+ DCHECK(IsWasmObject(*instance));
+ if (mem->IsUndefined(isolate)) return MaybeHandle<JSArrayBuffer>();
+ return Handle<JSArrayBuffer>(JSArrayBuffer::cast(mem));
+}
- if (compiled_module.is_null()) return -1;
- Handle<JSObject> instance =
- WasmModule::Instantiate(isolate, compiled_module.ToHandleChecked(),
- Handle<JSReceiver>::null(),
- Handle<JSArrayBuffer>::null())
- .ToHandleChecked();
+void SetInstanceMemory(Handle<JSObject> instance, JSArrayBuffer* buffer) {
+ DisallowHeapAllocation no_gc;
+ DCHECK(IsWasmObject(*instance));
+ instance->SetInternalField(kWasmMemArrayBuffer, buffer);
+ WasmCompiledModule* module =
+ WasmCompiledModule::cast(instance->GetInternalField(kWasmCompiledModule));
+ module->set_ptr_to_heap(buffer);
+}
- return CallFunction(isolate, instance, &thrower, "main", 0, nullptr);
+int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance) {
+ MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
+ GetInstanceMemory(isolate, instance);
+ Handle<JSArrayBuffer> buffer;
+ if (!maybe_mem_buffer.ToHandle(&buffer)) {
+ return 0;
+ } else {
+ return buffer->byte_length()->Number() / WasmModule::kPageSize;
+ }
}
-int32_t CallFunction(Isolate* isolate, Handle<JSObject> instance,
- ErrorThrower* thrower, const char* name, int argc,
- Handle<Object> argv[]) {
- Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
- Handle<JSObject> exports_object = Handle<JSObject>::cast(
- JSObject::GetProperty(instance, exports).ToHandleChecked());
- Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
- PropertyDescriptor desc;
- Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
- isolate, exports_object, main_name, &desc);
- if (!property_found.FromMaybe(false)) return -1;
-
- Handle<JSFunction> main_export = Handle<JSFunction>::cast(desc.value());
-
- // Call the JS function.
- Handle<Object> undefined = isolate->factory()->undefined_value();
- MaybeHandle<Object> retval =
- Execution::Call(isolate, main_export, undefined, argc, argv);
-
- // The result should be a number.
- if (retval.is_null()) {
- thrower->Error("WASM.compileRun() failed: Invocation was null");
+int32_t GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
+ uint32_t pages) {
+ Address old_mem_start = nullptr;
+ uint32_t old_size = 0, new_size = 0;
+
+ MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
+ GetInstanceMemory(isolate, instance);
+ Handle<JSArrayBuffer> old_buffer;
+ if (!maybe_mem_buffer.ToHandle(&old_buffer)) {
+ // If module object does not have linear memory associated with it,
+ // Allocate new array buffer of given size.
+ // TODO(gdeepti): Fix bounds check to take into account size of memtype.
+ new_size = pages * WasmModule::kPageSize;
+ // The code generated in the wasm compiler guarantees this precondition.
+ DCHECK(pages <= WasmModule::kMaxMemPages);
+ } else {
+ old_mem_start = static_cast<Address>(old_buffer->backing_store());
+ old_size = old_buffer->byte_length()->Number();
+ // If the old memory was zero-sized, we should have been in the
+ // "undefined" case above.
+ DCHECK_NOT_NULL(old_mem_start);
+ DCHECK_NE(0, old_size);
+ DCHECK(old_size + pages * WasmModule::kPageSize <=
+ std::numeric_limits<uint32_t>::max());
+ new_size = old_size + pages * WasmModule::kPageSize;
+ }
+
+ if (new_size <= old_size ||
+ WasmModule::kMaxMemPages * WasmModule::kPageSize <= new_size) {
return -1;
}
- Handle<Object> result = retval.ToHandleChecked();
- if (result->IsSmi()) {
- return Smi::cast(*result)->value();
+ Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size);
+ if (buffer.is_null()) return -1;
+ Address new_mem_start = static_cast<Address>(buffer->backing_store());
+ if (old_size != 0) {
+ memcpy(new_mem_start, old_mem_start, old_size);
}
- if (result->IsHeapNumber()) {
- return static_cast<int32_t>(HeapNumber::cast(*result)->value());
+ SetInstanceMemory(instance, *buffer);
+ if (!UpdateWasmModuleMemory(instance, old_mem_start, new_mem_start, old_size,
+ new_size)) {
+ return -1;
}
- thrower->Error("WASM.compileRun() failed: Return value should be number");
- return -1;
+ DCHECK(old_size % WasmModule::kPageSize == 0);
+ return (old_size / WasmModule::kPageSize);
+}
+
+namespace testing {
+
+void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> module_obj,
+ int instance_count) {
+ CHECK_GE(instance_count, 0);
+ DisallowHeapAllocation no_gc;
+ WasmCompiledModule* compiled_module =
+ WasmCompiledModule::cast(module_obj->GetInternalField(0));
+ CHECK_EQ(
+ JSObject::cast(compiled_module->ptr_to_weak_module_object()->value()),
+ *module_obj);
+ Object* prev = nullptr;
+ int found_instances = compiled_module->has_weak_owning_instance() ? 1 : 0;
+ WasmCompiledModule* current_instance = compiled_module;
+ while (current_instance->has_weak_next_instance()) {
+ 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_module_object()->value(),
+ *module_obj);
+ CHECK(
+ IsWasmObject(current_instance->ptr_to_weak_owning_instance()->value()));
+ prev = current_instance;
+ current_instance = WasmCompiledModule::cast(
+ current_instance->ptr_to_weak_next_instance()->value());
+ ++found_instances;
+ CHECK_LE(found_instances, instance_count);
+ }
+ CHECK_EQ(found_instances, instance_count);
+}
+
+void ValidateModuleState(Isolate* isolate, Handle<JSObject> module_obj) {
+ DisallowHeapAllocation no_gc;
+ WasmCompiledModule* compiled_module =
+ WasmCompiledModule::cast(module_obj->GetInternalField(0));
+ CHECK(compiled_module->has_weak_module_object());
+ CHECK_EQ(compiled_module->ptr_to_weak_module_object()->value(), *module_obj);
+ CHECK(!compiled_module->has_weak_prev_instance());
+ CHECK(!compiled_module->has_weak_next_instance());
+ CHECK(!compiled_module->has_weak_owning_instance());
+}
+
+void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance) {
+ DisallowHeapAllocation no_gc;
+ CHECK(IsWasmObject(*instance));
+ WasmCompiledModule* compiled_module =
+ WasmCompiledModule::cast(instance->GetInternalField(kWasmCompiledModule));
+ CHECK(compiled_module->has_weak_module_object());
+ CHECK(compiled_module->ptr_to_weak_module_object()->cleared());
}
} // namespace testing
diff --git a/deps/v8/src/wasm/wasm-module.h b/deps/v8/src/wasm/wasm-module.h
index 0c3df51d76..ac75042392 100644
--- a/deps/v8/src/wasm/wasm-module.h
+++ b/deps/v8/src/wasm/wasm-module.h
@@ -27,84 +27,71 @@ const size_t kMaxModuleSize = 1024 * 1024 * 1024;
const size_t kMaxFunctionSize = 128 * 1024;
const size_t kMaxStringSize = 256;
const uint32_t kWasmMagic = 0x6d736100;
-const uint32_t kWasmVersion = 0x0b;
+const uint32_t kWasmVersion = 0x0c;
+
const uint8_t kWasmFunctionTypeForm = 0x40;
+const uint8_t kWasmAnyFunctionTypeForm = 0x20;
+
+enum WasmSectionCode {
+ 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)
+};
+
+inline bool IsValidSectionCode(uint8_t byte) {
+ return kTypeSectionCode <= byte && byte <= kDataSectionCode;
+}
-// WebAssembly sections are named as strings in the binary format, but
-// internally V8 uses an enum to handle them.
-//
-// Entries have the form F(enumerator, string).
-#define FOR_EACH_WASM_SECTION_TYPE(F) \
- F(Signatures, 1, "type") \
- F(ImportTable, 2, "import") \
- F(FunctionSignatures, 3, "function") \
- F(FunctionTable, 4, "table") \
- F(Memory, 5, "memory") \
- F(ExportTable, 6, "export") \
- F(StartFunction, 7, "start") \
- F(FunctionBodies, 8, "code") \
- F(DataSegments, 9, "data") \
- F(Names, 10, "name") \
- F(Globals, 0, "global") \
- F(End, 0, "end")
-
-// Contants for the above section types: {LEB128 length, characters...}.
-#define WASM_SECTION_MEMORY 6, 'm', 'e', 'm', 'o', 'r', 'y'
-#define WASM_SECTION_SIGNATURES 4, 't', 'y', 'p', 'e'
-#define WASM_SECTION_GLOBALS 6, 'g', 'l', 'o', 'b', 'a', 'l'
-#define WASM_SECTION_DATA_SEGMENTS 4, 'd', 'a', 't', 'a'
-#define WASM_SECTION_FUNCTION_TABLE 5, 't', 'a', 'b', 'l', 'e'
-#define WASM_SECTION_END 3, 'e', 'n', 'd'
-#define WASM_SECTION_START_FUNCTION 5, 's', 't', 'a', 'r', 't'
-#define WASM_SECTION_IMPORT_TABLE 6, 'i', 'm', 'p', 'o', 'r', 't'
-#define WASM_SECTION_EXPORT_TABLE 6, 'e', 'x', 'p', 'o', 'r', 't'
-#define WASM_SECTION_FUNCTION_SIGNATURES \
- 8, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n'
-#define WASM_SECTION_FUNCTION_BODIES 4, 'c', 'o', 'd', 'e'
-#define WASM_SECTION_NAMES 4, 'n', 'a', 'm', 'e'
-
-// Constants for the above section headers' size (LEB128 + characters).
-#define WASM_SECTION_MEMORY_SIZE ((size_t)7)
-#define WASM_SECTION_SIGNATURES_SIZE ((size_t)5)
-#define WASM_SECTION_GLOBALS_SIZE ((size_t)7)
-#define WASM_SECTION_DATA_SEGMENTS_SIZE ((size_t)5)
-#define WASM_SECTION_FUNCTION_TABLE_SIZE ((size_t)6)
-#define WASM_SECTION_END_SIZE ((size_t)4)
-#define WASM_SECTION_START_FUNCTION_SIZE ((size_t)6)
-#define WASM_SECTION_IMPORT_TABLE_SIZE ((size_t)7)
-#define WASM_SECTION_EXPORT_TABLE_SIZE ((size_t)7)
-#define WASM_SECTION_FUNCTION_SIGNATURES_SIZE ((size_t)9)
-#define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)5)
-#define WASM_SECTION_NAMES_SIZE ((size_t)5)
+const char* SectionName(WasmSectionCode code);
class WasmDebugInfo;
-struct WasmSection {
- enum class Code : uint32_t {
-#define F(enumerator, order, string) enumerator,
- FOR_EACH_WASM_SECTION_TYPE(F)
-#undef F
- Max
- };
- static WasmSection::Code begin();
- static WasmSection::Code end();
- static WasmSection::Code next(WasmSection::Code code);
- static const char* getName(Code code);
- static int getOrder(Code code);
- static size_t getNameLength(Code code);
- static WasmSection::Code lookup(const byte* string, uint32_t length);
+// Constants for fixed-size elements within a module.
+static const uint32_t kMaxReturnCount = 1;
+static const uint8_t kResizableMaximumFlag = 1;
+static const int32_t kInvalidFunctionIndex = -1;
+
+enum WasmExternalKind {
+ kExternalFunction = 0,
+ kExternalTable = 1,
+ kExternalMemory = 2,
+ kExternalGlobal = 3
};
-enum WasmFunctionDeclBit {
- kDeclFunctionName = 0x01,
- kDeclFunctionExport = 0x08
+// Representation of an initializer expression.
+struct WasmInitExpr {
+ enum WasmInitKind {
+ kNone,
+ kGlobalIndex,
+ kI32Const,
+ kI64Const,
+ kF32Const,
+ kF64Const
+ } kind;
+
+ union {
+ int32_t i32_const;
+ int64_t i64_const;
+ float f32_const;
+ double f64_const;
+ uint32_t global_index;
+ } val;
};
-// Constants for fixed-size elements within a module.
-static const size_t kDeclMemorySize = 3;
-static const size_t kDeclDataSegmentSize = 13;
-
-static const uint32_t kMaxReturnCount = 1;
+#define NO_INIT \
+ { \
+ WasmInitExpr::kNone, { 0u } \
+ }
// Static representation of a WASM function.
struct WasmFunction {
@@ -115,54 +102,69 @@ struct WasmFunction {
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.
-};
-
-// Static representation of an imported WASM function.
-struct WasmImport {
- FunctionSig* sig; // signature of the function.
- uint32_t sig_index; // index into the signature table.
- uint32_t module_name_offset; // offset in module bytes of the module name.
- uint32_t module_name_length; // length in bytes of the module name.
- uint32_t function_name_offset; // offset in module bytes of the import name.
- uint32_t function_name_length; // length in bytes of the import name.
-};
-
-// Static representation of an exported WASM function.
-struct WasmExport {
- uint32_t func_index; // index into the function table.
- uint32_t name_offset; // offset in module bytes of the name to export.
- uint32_t name_length; // length in bytes of the exported name.
+ bool imported;
+ bool exported;
};
// Static representation of a wasm global variable.
struct WasmGlobal {
- uint32_t name_offset; // offset in the module bytes of the name, if any.
- uint32_t name_length; // length in bytes of the global name.
LocalType type; // type of the global.
- uint32_t offset; // offset from beginning of globals area.
- bool exported; // true if this global is exported.
+ bool mutability; // {true} if mutable.
+ WasmInitExpr init; // the initialization expression of the global.
+ uint32_t offset; // offset into global memory.
+ bool imported; // true if imported.
+ bool exported; // true if exported.
};
// Static representation of a wasm data segment.
struct WasmDataSegment {
- uint32_t dest_addr; // destination memory address of the data.
+ 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.
- bool init; // true if loaded upon instantiation.
};
// Static representation of a wasm indirect call table.
struct WasmIndirectFunctionTable {
- uint32_t size; // initial table size.
- uint32_t max_size; // maximum table size.
- std::vector<uint16_t> values; // function table.
+ uint32_t size; // initial table size.
+ uint32_t max_size; // maximum table size.
+ std::vector<int32_t> values; // function table, -1 indicating invalid.
+ bool imported; // true if imported.
+ bool exported; // true if exported.
+};
+
+// Static representation of how to initialize a table.
+struct WasmTableInit {
+ uint32_t table_index;
+ WasmInitExpr offset;
+ std::vector<uint32_t> entries;
+};
+
+// 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.
+};
+
+// 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.
+ WasmExternalKind kind; // kind of the export.
+ uint32_t index; // index into the respective space.
};
enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
+class WasmCompiledModule;
+
// Static representation of a module.
struct WasmModule {
static const uint32_t kPageSize = 0x10000; // Page size, 64kb.
+ static const uint32_t kMaxLegalPages = 65536; // Maximum legal pages
static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb
static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb
@@ -171,7 +173,6 @@ struct WasmModule {
uint32_t min_mem_pages; // minimum size of the memory in 64k pages.
uint32_t max_mem_pages; // maximum size of the memory in 64k pages.
bool mem_export; // true if the memory is exported.
- bool mem_external; // true if the memory is external.
// 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.
@@ -180,12 +181,16 @@ struct WasmModule {
std::vector<WasmGlobal> globals; // globals in this module.
uint32_t globals_size; // size of globals table.
+ uint32_t num_imported_functions; // number of imported functions.
+ uint32_t num_declared_functions; // number of declared functions.
+ uint32_t num_exported_functions; // 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
@@ -233,13 +238,12 @@ struct WasmModule {
}
// Creates a new instantiation of the module in the given isolate.
- static MaybeHandle<JSObject> Instantiate(Isolate* isolate,
- Handle<FixedArray> compiled_module,
- Handle<JSReceiver> ffi,
- Handle<JSArrayBuffer> memory);
+ V8_EXPORT_PRIVATE static MaybeHandle<JSObject> Instantiate(
+ Isolate* isolate, ErrorThrower* thrower, Handle<JSObject> module_object,
+ Handle<JSReceiver> ffi, Handle<JSArrayBuffer> memory);
- MaybeHandle<FixedArray> CompileFunctions(Isolate* isolate,
- ErrorThrower* thrower) const;
+ MaybeHandle<WasmCompiledModule> CompileFunctions(Isolate* isolate,
+ ErrorThrower* thrower) const;
private:
DISALLOW_COPY_AND_ASSIGN(WasmModule);
@@ -255,7 +259,6 @@ struct WasmModuleInstance {
Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals.
std::vector<Handle<FixedArray>> function_tables; // indirect function tables.
std::vector<Handle<Code>> function_code; // code objects for each function.
- std::vector<Handle<Code>> import_code; // code objects for each import.
// -- raw memory ------------------------------------------------------------
byte* mem_start; // start of linear memory.
uint32_t mem_size; // size of the linear memory.
@@ -266,7 +269,6 @@ struct WasmModuleInstance {
: module(m),
function_tables(m->function_tables.size()),
function_code(m->functions.size()),
- import_code(m->import_table.size()),
mem_start(nullptr),
mem_size(0),
globals_start(nullptr) {}
@@ -278,9 +280,6 @@ struct ModuleEnv {
const WasmModule* module;
WasmModuleInstance* instance;
ModuleOrigin origin;
- // TODO(mtrofin): remove this once we introduce WASM_DIRECT_CALL
- // reloc infos.
- std::vector<Handle<Code>> placeholders;
bool IsValidGlobal(uint32_t index) const {
return module && index < module->globals.size();
@@ -291,9 +290,6 @@ struct ModuleEnv {
bool IsValidSignature(uint32_t index) const {
return module && index < module->signatures.size();
}
- bool IsValidImport(uint32_t index) const {
- return module && index < module->import_table.size();
- }
bool IsValidTable(uint32_t index) const {
return module && index < module->function_tables.size();
}
@@ -305,10 +301,6 @@ struct ModuleEnv {
DCHECK(IsValidFunction(index));
return module->functions[index].sig;
}
- FunctionSig* GetImportSignature(uint32_t index) {
- DCHECK(IsValidImport(index));
- return module->import_table[index].sig;
- }
FunctionSig* GetSignature(uint32_t index) {
DCHECK(IsValidSignature(index));
return module->signatures[index];
@@ -320,14 +312,15 @@ struct ModuleEnv {
bool asm_js() { return origin == kAsmJsOrigin; }
- Handle<Code> GetCodeOrPlaceholder(uint32_t index) const;
- Handle<Code> GetImportCode(uint32_t index);
+ Handle<Code> GetFunctionCode(uint32_t index) {
+ DCHECK_NOT_NULL(instance);
+ return instance->function_code[index];
+ }
static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone,
FunctionSig* sig);
static compiler::CallDescriptor* GetI32WasmCallDescriptor(
Zone* zone, compiler::CallDescriptor* descriptor);
- compiler::CallDescriptor* GetCallDescriptor(Zone* zone, uint32_t index);
};
// A helper for printing out the names of functions.
@@ -347,6 +340,128 @@ typedef Result<WasmFunction*> FunctionResult;
typedef std::vector<std::pair<int, int>> FunctionOffsets;
typedef Result<FunctionOffsets> FunctionOffsetsResult;
+class WasmCompiledModule : public FixedArray {
+ public:
+ static WasmCompiledModule* cast(Object* fixed_array) {
+ return reinterpret_cast<WasmCompiledModule*>(fixed_array);
+ }
+
+#define WCM_OBJECT_OR_WEAK(TYPE, NAME, ID) \
+ Handle<TYPE> NAME() const { return handle(ptr_to_##NAME()); } \
+ \
+ MaybeHandle<TYPE> maybe_##NAME() const { \
+ if (has_##NAME()) return NAME(); \
+ return MaybeHandle<TYPE>(); \
+ } \
+ \
+ TYPE* ptr_to_##NAME() const { \
+ Object* obj = get(ID); \
+ if (!obj->Is##TYPE()) return nullptr; \
+ return TYPE::cast(obj); \
+ } \
+ \
+ void set_##NAME(Handle<TYPE> value) { set_ptr_to_##NAME(*value); } \
+ \
+ void set_ptr_to_##NAME(TYPE* value) { set(ID, value); } \
+ \
+ bool has_##NAME() const { return get(ID)->Is##TYPE(); } \
+ \
+ void reset_##NAME() { set_undefined(ID); }
+
+#define WCM_OBJECT(TYPE, NAME) WCM_OBJECT_OR_WEAK(TYPE, NAME, kID_##NAME)
+
+#define WCM_SMALL_NUMBER(TYPE, NAME) \
+ TYPE NAME() const { \
+ return static_cast<TYPE>(Smi::cast(get(kID_##NAME))->value()); \
+ }
+
+#define WCM_WEAK_LINK(TYPE, NAME) \
+ WCM_OBJECT_OR_WEAK(WeakCell, weak_##NAME, kID_##NAME); \
+ \
+ Handle<TYPE> NAME() const { \
+ return handle(TYPE::cast(weak_##NAME()->value())); \
+ }
+
+#define CORE_WCM_PROPERTY_TABLE(MACRO) \
+ MACRO(OBJECT, FixedArray, code_table) \
+ MACRO(OBJECT, FixedArray, import_data) \
+ MACRO(OBJECT, FixedArray, exports) \
+ MACRO(OBJECT, FixedArray, startup_function) \
+ MACRO(OBJECT, FixedArray, indirect_function_tables) \
+ MACRO(OBJECT, String, module_bytes) \
+ MACRO(OBJECT, ByteArray, function_names) \
+ MACRO(SMALL_NUMBER, uint32_t, min_memory_pages) \
+ MACRO(OBJECT, FixedArray, data_segments_info) \
+ MACRO(OBJECT, ByteArray, data_segments) \
+ MACRO(SMALL_NUMBER, uint32_t, globals_size) \
+ MACRO(OBJECT, JSArrayBuffer, heap) \
+ MACRO(SMALL_NUMBER, bool, export_memory) \
+ MACRO(SMALL_NUMBER, ModuleOrigin, origin) \
+ MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \
+ MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \
+ MACRO(WEAK_LINK, JSObject, owning_instance) \
+ MACRO(WEAK_LINK, JSObject, module_object)
+
+#if DEBUG
+#define DEBUG_ONLY_TABLE(MACRO) MACRO(SMALL_NUMBER, uint32_t, instance_id)
+#else
+#define DEBUG_ONLY_TABLE(IGNORE)
+ uint32_t instance_id() const { return -1; }
+#endif
+
+#define WCM_PROPERTY_TABLE(MACRO) \
+ CORE_WCM_PROPERTY_TABLE(MACRO) \
+ DEBUG_ONLY_TABLE(MACRO)
+
+ private:
+ enum PropertyIndices {
+#define INDICES(IGNORE1, IGNORE2, NAME) kID_##NAME,
+ WCM_PROPERTY_TABLE(INDICES) Count
+#undef INDICES
+ };
+
+ public:
+ static Handle<WasmCompiledModule> New(Isolate* isolate,
+ uint32_t min_memory_pages,
+ uint32_t globals_size,
+ bool export_memory,
+ ModuleOrigin origin);
+
+ static Handle<WasmCompiledModule> Clone(Isolate* isolate,
+ Handle<WasmCompiledModule> module) {
+ Handle<WasmCompiledModule> ret = Handle<WasmCompiledModule>::cast(
+ isolate->factory()->CopyFixedArray(module));
+ ret->Init();
+ ret->reset_weak_owning_instance();
+ ret->reset_weak_next_instance();
+ ret->reset_weak_prev_instance();
+ return ret;
+ }
+
+ uint32_t mem_size() const {
+ DCHECK(has_heap());
+ return heap()->byte_length()->Number();
+ }
+
+ uint32_t default_mem_size() const {
+ return min_memory_pages() * WasmModule::kPageSize;
+ }
+
+#define DECLARATION(KIND, TYPE, NAME) WCM_##KIND(TYPE, NAME)
+ WCM_PROPERTY_TABLE(DECLARATION)
+#undef DECLARATION
+
+ void PrintInstancesChain();
+
+ private:
+#if DEBUG
+ static uint32_t instance_id_counter_;
+#endif
+ void Init();
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WasmCompiledModule);
+};
+
// Extract a function name from the given wasm object.
// Returns "<WASM UNNAMED>" if the function is unnamed or the name is not a
// valid UTF-8 string.
@@ -399,18 +514,38 @@ void PopulateFunctionTable(Handle<FixedArray> table, uint32_t table_size,
const std::vector<Handle<Code>>* code_table);
Handle<JSObject> CreateCompiledModuleObject(Isolate* isolate,
- Handle<FixedArray> compiled_module);
+ Handle<FixedArray> compiled_module,
+ ModuleOrigin origin);
+
+V8_EXPORT_PRIVATE MaybeHandle<JSObject> CreateModuleObjectFromBytes(
+ Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
+ ModuleOrigin origin);
+
+V8_EXPORT_PRIVATE bool ValidateModuleBytes(Isolate* isolate, const byte* start,
+ const byte* end,
+ ErrorThrower* thrower,
+ ModuleOrigin origin);
+
+// Get the number of imported functions for a WASM instance.
+uint32_t GetNumImportedFunctions(Handle<JSObject> wasm_object);
+
+// 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.
+Object* GetOwningWasmInstance(Code* code);
+
+int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance);
+
+int32_t GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
+ uint32_t pages);
namespace testing {
-// Decode, verify, and run the function labeled "main" in the
-// given encoded module. The module should have no imports.
-int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
- const byte* module_end, bool asm_js = false);
+void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> module_obj,
+ int instance_count);
+void ValidateModuleState(Isolate* isolate, Handle<JSObject> module_obj);
+void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance);
-int32_t CallFunction(Isolate* isolate, Handle<JSObject> instance,
- ErrorThrower* thrower, const char* name, int argc,
- Handle<Object> argv[]);
} // namespace testing
} // namespace wasm
} // namespace internal
diff --git a/deps/v8/src/wasm/wasm-opcodes.cc b/deps/v8/src/wasm/wasm-opcodes.cc
index 8f54207661..cd2dde4748 100644
--- a/deps/v8/src/wasm/wasm-opcodes.cc
+++ b/deps/v8/src/wasm/wasm-opcodes.cc
@@ -38,6 +38,18 @@ const char* WasmOpcodes::ShortOpcodeName(WasmOpcode opcode) {
return "Unknown";
}
+bool WasmOpcodes::IsPrefixOpcode(WasmOpcode opcode) {
+ switch (opcode) {
+#define CHECK_PREFIX(name, opcode) \
+ case k##name##Prefix: \
+ return true;
+ FOREACH_PREFIX(CHECK_PREFIX)
+#undef CHECK_PREFIX
+ default:
+ return false;
+ }
+}
+
std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) {
if (sig.return_count() == 0) os << "v";
for (size_t i = 0; i < sig.return_count(); ++i) {
@@ -74,6 +86,7 @@ static const FunctionSig* kSimdExprSigs[] = {
nullptr, FOREACH_SIMD_SIGNATURE(DECLARE_SIMD_SIG_ENTRY)};
static byte kSimpleExprSigTable[256];
+static byte kSimpleAsmjsExprSigTable[256];
static byte kSimdExprSigTable[256];
// Initialize the signature table.
@@ -81,14 +94,16 @@ static void InitSigTables() {
#define SET_SIG_TABLE(name, opcode, sig) \
kSimpleExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1;
FOREACH_SIMPLE_OPCODE(SET_SIG_TABLE);
- FOREACH_SIMPLE_MEM_OPCODE(SET_SIG_TABLE);
- FOREACH_ASMJS_COMPAT_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_OPCODE(SET_SIG_TABLE)
+ FOREACH_SIMD_0_OPERAND_OPCODE(SET_SIG_TABLE)
#undef SET_SIG_TABLE
}
@@ -102,6 +117,10 @@ class SigTable {
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)]]);
@@ -118,6 +137,10 @@ FunctionSig* WasmOpcodes::Signature(WasmOpcode opcode) {
}
}
+FunctionSig* WasmOpcodes::AsmjsSignature(WasmOpcode opcode) {
+ return sig_table.Get().AsmjsSignature(opcode);
+}
+
// TODO(titzer): pull WASM_64 up to a common header.
#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64
#define WASM_64 1
diff --git a/deps/v8/src/wasm/wasm-opcodes.h b/deps/v8/src/wasm/wasm-opcodes.h
index 4d66e567ef..03827b2035 100644
--- a/deps/v8/src/wasm/wasm-opcodes.h
+++ b/deps/v8/src/wasm/wasm-opcodes.h
@@ -22,6 +22,9 @@ enum LocalTypeCode {
kLocalS128 = 5
};
+// Type code for multi-value block types.
+static const uint8_t kMultivalBlock = 0x41;
+
// We reuse the internal machine type to represent WebAssembly AST types.
// A typedef improves readability without adding a whole new type system.
typedef MachineRepresentation LocalType;
@@ -44,7 +47,7 @@ const WasmCodePosition kNoCodePosition = -1;
// Control expressions and blocks.
#define FOREACH_CONTROL_OPCODE(V) \
- V(Nop, 0x00, _) \
+ V(Unreachable, 0x00, _) \
V(Block, 0x01, _) \
V(Loop, 0x02, _) \
V(If, 0x03, _) \
@@ -54,13 +57,10 @@ const WasmCodePosition kNoCodePosition = -1;
V(BrIf, 0x07, _) \
V(BrTable, 0x08, _) \
V(Return, 0x09, _) \
- V(Unreachable, 0x0a, _) \
+ V(Nop, 0x0a, _) \
V(Throw, 0xfa, _) \
- V(TryCatch, 0xfb, _) \
- V(TryCatchFinally, 0xfc, _) \
- V(TryFinally, 0xfd, _) \
+ V(Try, 0xfb, _) \
V(Catch, 0xfe, _) \
- V(Finally, 0xff, _) \
V(End, 0x0F, _)
// Constants, locals, globals, and calls.
@@ -71,9 +71,10 @@ const WasmCodePosition kNoCodePosition = -1;
V(F32Const, 0x13, _) \
V(GetLocal, 0x14, _) \
V(SetLocal, 0x15, _) \
+ V(TeeLocal, 0x19, _) \
+ V(Drop, 0x0b, _) \
V(CallFunction, 0x16, _) \
V(CallIndirect, 0x17, _) \
- V(CallImport, 0x18, _) \
V(I8Const, 0xcb, _) \
V(GetGlobal, 0xbb, _) \
V(SetGlobal, 0xbc, _)
@@ -273,141 +274,144 @@ const WasmCodePosition kNoCodePosition = -1;
V(I32AsmjsSConvertF64, 0xe2, i_d) \
V(I32AsmjsUConvertF64, 0xe3, i_d)
-#define FOREACH_SIMD_OPCODE(V) \
- V(F32x4Splat, 0xe500, s_f) \
- V(F32x4ExtractLane, 0xe501, f_si) \
- V(F32x4ReplaceLane, 0xe502, s_sif) \
- V(F32x4Abs, 0xe503, s_s) \
- V(F32x4Neg, 0xe504, s_s) \
- V(F32x4Sqrt, 0xe505, s_s) \
- V(F32x4RecipApprox, 0xe506, s_s) \
- V(F32x4SqrtApprox, 0xe507, s_s) \
- V(F32x4Add, 0xe508, s_ss) \
- V(F32x4Sub, 0xe509, s_ss) \
- V(F32x4Mul, 0xe50a, s_ss) \
- V(F32x4Div, 0xe50b, s_ss) \
- V(F32x4Min, 0xe50c, s_ss) \
- V(F32x4Max, 0xe50d, s_ss) \
- V(F32x4MinNum, 0xe50e, s_ss) \
- V(F32x4MaxNum, 0xe50f, s_ss) \
- V(F32x4Eq, 0xe510, s_ss) \
- V(F32x4Ne, 0xe511, s_ss) \
- V(F32x4Lt, 0xe512, s_ss) \
- V(F32x4Le, 0xe513, s_ss) \
- V(F32x4Gt, 0xe514, s_ss) \
- V(F32x4Ge, 0xe515, s_ss) \
- V(F32x4Select, 0xe516, s_sss) \
- V(F32x4Swizzle, 0xe517, s_s) \
- V(F32x4Shuffle, 0xe518, s_ss) \
- V(F32x4FromInt32x4, 0xe519, s_s) \
- V(F32x4FromUint32x4, 0xe51a, s_s) \
- V(I32x4Splat, 0xe51b, s_i) \
- V(I32x4ExtractLane, 0xe51c, i_si) \
- V(I32x4ReplaceLane, 0xe51d, s_sii) \
- V(I32x4Neg, 0xe51e, s_s) \
- V(I32x4Add, 0xe51f, s_ss) \
- V(I32x4Sub, 0xe520, s_ss) \
- V(I32x4Mul, 0xe521, s_ss) \
- V(I32x4Min_s, 0xe522, s_ss) \
- V(I32x4Max_s, 0xe523, s_ss) \
- V(I32x4Shl, 0xe524, s_si) \
- V(I32x4Shr_s, 0xe525, s_si) \
- V(I32x4Eq, 0xe526, s_ss) \
- V(I32x4Ne, 0xe527, s_ss) \
- V(I32x4Lt_s, 0xe528, s_ss) \
- V(I32x4Le_s, 0xe529, s_ss) \
- V(I32x4Gt_s, 0xe52a, s_ss) \
- V(I32x4Ge_s, 0xe52b, s_ss) \
- V(I32x4Select, 0xe52c, s_sss) \
- V(I32x4Swizzle, 0xe52d, s_s) \
- V(I32x4Shuffle, 0xe52e, s_ss) \
- V(I32x4FromFloat32x4, 0xe52f, s_s) \
- V(I32x4Min_u, 0xe530, s_ss) \
- V(I32x4Max_u, 0xe531, s_ss) \
- V(I32x4Shr_u, 0xe532, s_ss) \
- V(I32x4Lt_u, 0xe533, s_ss) \
- V(I32x4Le_u, 0xe534, s_ss) \
- V(I32x4Gt_u, 0xe535, s_ss) \
- V(I32x4Ge_u, 0xe536, s_ss) \
- V(Ui32x4FromFloat32x4, 0xe537, s_s) \
- V(I16x8Splat, 0xe538, s_i) \
- V(I16x8ExtractLane, 0xe539, i_si) \
- V(I16x8ReplaceLane, 0xe53a, s_sii) \
- V(I16x8Neg, 0xe53b, s_s) \
- V(I16x8Add, 0xe53c, s_ss) \
- V(I16x8AddSaturate_s, 0xe53d, s_ss) \
- V(I16x8Sub, 0xe53e, s_ss) \
- V(I16x8SubSaturate_s, 0xe53f, s_ss) \
- V(I16x8Mul, 0xe540, s_ss) \
- V(I16x8Min_s, 0xe541, s_ss) \
- V(I16x8Max_s, 0xe542, s_ss) \
- V(I16x8Shl, 0xe543, s_si) \
- V(I16x8Shr_s, 0xe544, s_si) \
- V(I16x8Eq, 0xe545, s_ss) \
- V(I16x8Ne, 0xe546, s_ss) \
- V(I16x8Lt_s, 0xe547, s_ss) \
- V(I16x8Le_s, 0xe548, s_ss) \
- V(I16x8Gt_s, 0xe549, s_ss) \
- V(I16x8Ge_s, 0xe54a, s_ss) \
- V(I16x8Select, 0xe54b, s_sss) \
- V(I16x8Swizzle, 0xe54c, s_s) \
- V(I16x8Shuffle, 0xe54d, s_ss) \
- V(I16x8AddSaturate_u, 0xe54e, s_ss) \
- V(I16x8SubSaturate_u, 0xe54f, s_ss) \
- V(I16x8Min_u, 0xe550, s_ss) \
- V(I16x8Max_u, 0xe551, s_ss) \
- V(I16x8Shr_u, 0xe552, s_si) \
- V(I16x8Lt_u, 0xe553, s_ss) \
- V(I16x8Le_u, 0xe554, s_ss) \
- V(I16x8Gt_u, 0xe555, s_ss) \
- V(I16x8Ge_u, 0xe556, s_ss) \
- V(I8x16Splat, 0xe557, s_i) \
- V(I8x16ExtractLane, 0xe558, i_si) \
- V(I8x16ReplaceLane, 0xe559, s_sii) \
- V(I8x16Neg, 0xe55a, s_s) \
- V(I8x16Add, 0xe55b, s_ss) \
- V(I8x16AddSaturate_s, 0xe55c, s_ss) \
- V(I8x16Sub, 0xe55d, s_ss) \
- V(I8x16SubSaturate_s, 0xe55e, s_ss) \
- V(I8x16Mul, 0xe55f, s_ss) \
- V(I8x16Min_s, 0xe560, s_ss) \
- V(I8x16Max_s, 0xe561, s_ss) \
- V(I8x16Shl, 0xe562, s_si) \
- V(I8x16Shr_s, 0xe563, s_si) \
- V(I8x16Eq, 0xe564, s_ss) \
- V(I8x16Neq, 0xe565, s_ss) \
- V(I8x16Lt_s, 0xe566, s_ss) \
- V(I8x16Le_s, 0xe567, s_ss) \
- V(I8x16Gt_s, 0xe568, s_ss) \
- V(I8x16Ge_s, 0xe569, s_ss) \
- V(I8x16Select, 0xe56a, s_sss) \
- V(I8x16Swizzle, 0xe56b, s_s) \
- V(I8x16Shuffle, 0xe56c, s_ss) \
- V(I8x16AddSaturate_u, 0xe56d, s_ss) \
- V(I8x16Sub_saturate_u, 0xe56e, s_ss) \
- V(I8x16Min_u, 0xe56f, s_ss) \
- V(I8x16Max_u, 0xe570, s_ss) \
- V(I8x16Shr_u, 0xe571, s_ss) \
- V(I8x16Lt_u, 0xe572, s_ss) \
- V(I8x16Le_u, 0xe573, s_ss) \
- V(I8x16Gt_u, 0xe574, s_ss) \
- V(I8x16Ge_u, 0xe575, s_ss) \
- V(S128And, 0xe576, s_ss) \
- V(S128Ior, 0xe577, s_ss) \
- V(S128Xor, 0xe578, s_ss) \
+#define FOREACH_SIMD_0_OPERAND_OPCODE(V) \
+ V(F32x4Splat, 0xe500, s_f) \
+ V(F32x4ReplaceLane, 0xe502, s_sif) \
+ V(F32x4Abs, 0xe503, s_s) \
+ V(F32x4Neg, 0xe504, s_s) \
+ V(F32x4Sqrt, 0xe505, s_s) \
+ V(F32x4RecipApprox, 0xe506, s_s) \
+ V(F32x4SqrtApprox, 0xe507, s_s) \
+ V(F32x4Add, 0xe508, s_ss) \
+ V(F32x4Sub, 0xe509, s_ss) \
+ V(F32x4Mul, 0xe50a, s_ss) \
+ V(F32x4Div, 0xe50b, s_ss) \
+ V(F32x4Min, 0xe50c, s_ss) \
+ V(F32x4Max, 0xe50d, s_ss) \
+ V(F32x4MinNum, 0xe50e, s_ss) \
+ V(F32x4MaxNum, 0xe50f, s_ss) \
+ V(F32x4Eq, 0xe510, s_ss) \
+ V(F32x4Ne, 0xe511, s_ss) \
+ V(F32x4Lt, 0xe512, s_ss) \
+ V(F32x4Le, 0xe513, s_ss) \
+ V(F32x4Gt, 0xe514, s_ss) \
+ V(F32x4Ge, 0xe515, s_ss) \
+ V(F32x4Select, 0xe516, s_sss) \
+ V(F32x4Swizzle, 0xe517, s_s) \
+ V(F32x4Shuffle, 0xe518, s_ss) \
+ V(F32x4FromInt32x4, 0xe519, s_s) \
+ V(F32x4FromUint32x4, 0xe51a, s_s) \
+ V(I32x4Splat, 0xe51b, s_i) \
+ V(I32x4ReplaceLane, 0xe51d, s_sii) \
+ V(I32x4Neg, 0xe51e, s_s) \
+ V(I32x4Add, 0xe51f, s_ss) \
+ V(I32x4Sub, 0xe520, s_ss) \
+ V(I32x4Mul, 0xe521, s_ss) \
+ V(I32x4Min_s, 0xe522, s_ss) \
+ V(I32x4Max_s, 0xe523, s_ss) \
+ V(I32x4Shl, 0xe524, s_si) \
+ V(I32x4Shr_s, 0xe525, s_si) \
+ V(I32x4Eq, 0xe526, s_ss) \
+ V(I32x4Ne, 0xe527, s_ss) \
+ V(I32x4Lt_s, 0xe528, s_ss) \
+ V(I32x4Le_s, 0xe529, s_ss) \
+ V(I32x4Gt_s, 0xe52a, s_ss) \
+ V(I32x4Ge_s, 0xe52b, s_ss) \
+ V(I32x4Select, 0xe52c, s_sss) \
+ V(I32x4Swizzle, 0xe52d, s_s) \
+ V(I32x4Shuffle, 0xe52e, s_ss) \
+ V(I32x4FromFloat32x4, 0xe52f, s_s) \
+ V(I32x4Min_u, 0xe530, s_ss) \
+ V(I32x4Max_u, 0xe531, s_ss) \
+ V(I32x4Shr_u, 0xe532, s_ss) \
+ V(I32x4Lt_u, 0xe533, s_ss) \
+ V(I32x4Le_u, 0xe534, s_ss) \
+ V(I32x4Gt_u, 0xe535, s_ss) \
+ V(I32x4Ge_u, 0xe536, s_ss) \
+ V(Ui32x4FromFloat32x4, 0xe537, s_s) \
+ V(I16x8Splat, 0xe538, s_i) \
+ V(I16x8ReplaceLane, 0xe53a, s_sii) \
+ V(I16x8Neg, 0xe53b, s_s) \
+ V(I16x8Add, 0xe53c, s_ss) \
+ V(I16x8AddSaturate_s, 0xe53d, s_ss) \
+ V(I16x8Sub, 0xe53e, s_ss) \
+ V(I16x8SubSaturate_s, 0xe53f, s_ss) \
+ V(I16x8Mul, 0xe540, s_ss) \
+ V(I16x8Min_s, 0xe541, s_ss) \
+ V(I16x8Max_s, 0xe542, s_ss) \
+ V(I16x8Shl, 0xe543, s_si) \
+ V(I16x8Shr_s, 0xe544, s_si) \
+ V(I16x8Eq, 0xe545, s_ss) \
+ V(I16x8Ne, 0xe546, s_ss) \
+ V(I16x8Lt_s, 0xe547, s_ss) \
+ V(I16x8Le_s, 0xe548, s_ss) \
+ V(I16x8Gt_s, 0xe549, s_ss) \
+ V(I16x8Ge_s, 0xe54a, s_ss) \
+ V(I16x8Select, 0xe54b, s_sss) \
+ V(I16x8Swizzle, 0xe54c, s_s) \
+ V(I16x8Shuffle, 0xe54d, s_ss) \
+ V(I16x8AddSaturate_u, 0xe54e, s_ss) \
+ V(I16x8SubSaturate_u, 0xe54f, s_ss) \
+ V(I16x8Min_u, 0xe550, s_ss) \
+ V(I16x8Max_u, 0xe551, s_ss) \
+ V(I16x8Shr_u, 0xe552, s_si) \
+ V(I16x8Lt_u, 0xe553, s_ss) \
+ V(I16x8Le_u, 0xe554, s_ss) \
+ V(I16x8Gt_u, 0xe555, s_ss) \
+ V(I16x8Ge_u, 0xe556, s_ss) \
+ V(I8x16Splat, 0xe557, s_i) \
+ V(I8x16ReplaceLane, 0xe559, s_sii) \
+ V(I8x16Neg, 0xe55a, s_s) \
+ V(I8x16Add, 0xe55b, s_ss) \
+ V(I8x16AddSaturate_s, 0xe55c, s_ss) \
+ V(I8x16Sub, 0xe55d, s_ss) \
+ V(I8x16SubSaturate_s, 0xe55e, s_ss) \
+ V(I8x16Mul, 0xe55f, s_ss) \
+ V(I8x16Min_s, 0xe560, s_ss) \
+ V(I8x16Max_s, 0xe561, s_ss) \
+ V(I8x16Shl, 0xe562, s_si) \
+ V(I8x16Shr_s, 0xe563, s_si) \
+ V(I8x16Eq, 0xe564, s_ss) \
+ V(I8x16Neq, 0xe565, s_ss) \
+ V(I8x16Lt_s, 0xe566, s_ss) \
+ V(I8x16Le_s, 0xe567, s_ss) \
+ V(I8x16Gt_s, 0xe568, s_ss) \
+ V(I8x16Ge_s, 0xe569, s_ss) \
+ V(I8x16Select, 0xe56a, s_sss) \
+ V(I8x16Swizzle, 0xe56b, s_s) \
+ V(I8x16Shuffle, 0xe56c, s_ss) \
+ V(I8x16AddSaturate_u, 0xe56d, s_ss) \
+ V(I8x16Sub_saturate_u, 0xe56e, s_ss) \
+ V(I8x16Min_u, 0xe56f, s_ss) \
+ V(I8x16Max_u, 0xe570, s_ss) \
+ V(I8x16Shr_u, 0xe571, s_ss) \
+ V(I8x16Lt_u, 0xe572, s_ss) \
+ V(I8x16Le_u, 0xe573, s_ss) \
+ V(I8x16Gt_u, 0xe574, s_ss) \
+ V(I8x16Ge_u, 0xe575, s_ss) \
+ V(S128And, 0xe576, s_ss) \
+ V(S128Ior, 0xe577, s_ss) \
+ V(S128Xor, 0xe578, s_ss) \
V(S128Not, 0xe579, s_s)
+#define FOREACH_SIMD_1_OPERAND_OPCODE(V) \
+ V(F32x4ExtractLane, 0xe501, _) \
+ V(I32x4ExtractLane, 0xe51c, _) \
+ V(I16x8ExtractLane, 0xe539, _) \
+ V(I8x16ExtractLane, 0xe558, _)
+
// All opcodes.
-#define FOREACH_OPCODE(V) \
- FOREACH_CONTROL_OPCODE(V) \
- FOREACH_MISC_OPCODE(V) \
- FOREACH_SIMPLE_OPCODE(V) \
- FOREACH_SIMPLE_MEM_OPCODE(V) \
- FOREACH_STORE_MEM_OPCODE(V) \
- FOREACH_LOAD_MEM_OPCODE(V) \
- FOREACH_MISC_MEM_OPCODE(V) \
- FOREACH_ASMJS_COMPAT_OPCODE(V) \
- FOREACH_SIMD_OPCODE(V)
+#define FOREACH_OPCODE(V) \
+ FOREACH_CONTROL_OPCODE(V) \
+ FOREACH_MISC_OPCODE(V) \
+ FOREACH_SIMPLE_OPCODE(V) \
+ FOREACH_SIMPLE_MEM_OPCODE(V) \
+ FOREACH_STORE_MEM_OPCODE(V) \
+ FOREACH_LOAD_MEM_OPCODE(V) \
+ FOREACH_MISC_MEM_OPCODE(V) \
+ FOREACH_ASMJS_COMPAT_OPCODE(V) \
+ FOREACH_SIMD_0_OPERAND_OPCODE(V) \
+ FOREACH_SIMD_1_OPERAND_OPCODE(V)
// All signatures.
#define FOREACH_SIGNATURE(V) \
@@ -443,12 +447,10 @@ const WasmCodePosition kNoCodePosition = -1;
#define FOREACH_SIMD_SIGNATURE(V) \
V(s_s, kAstS128, kAstS128) \
V(s_f, kAstS128, kAstF32) \
- V(f_si, kAstF32, kAstS128, kAstI32) \
V(s_sif, kAstS128, kAstS128, kAstI32, kAstF32) \
V(s_ss, kAstS128, kAstS128, kAstS128) \
V(s_sss, kAstS128, kAstS128, kAstS128, kAstS128) \
V(s_i, kAstS128, kAstI32) \
- V(i_si, kAstI32, kAstS128, kAstI32) \
V(s_sii, kAstS128, kAstS128, kAstI32, kAstI32) \
V(s_si, kAstS128, kAstS128, kAstI32)
@@ -489,6 +491,8 @@ class WasmOpcodes {
static const char* OpcodeName(WasmOpcode opcode);
static const char* ShortOpcodeName(WasmOpcode opcode);
static FunctionSig* Signature(WasmOpcode opcode);
+ static FunctionSig* AsmjsSignature(WasmOpcode opcode);
+ static bool IsPrefixOpcode(WasmOpcode opcode);
static int TrapReasonToMessageId(TrapReason reason);
static const char* TrapReasonMessage(TrapReason reason);
@@ -497,6 +501,8 @@ class WasmOpcodes {
return 1 << ElementSizeLog2Of(type.representation());
}
+ static byte MemSize(LocalType type) { return 1 << ElementSizeLog2Of(type); }
+
static LocalTypeCode LocalTypeCodeFor(LocalType type) {
switch (type) {
case kAstI32:
@@ -507,10 +513,10 @@ class WasmOpcodes {
return kLocalF32;
case kAstF64:
return kLocalF64;
- case kAstStmt:
- return kLocalVoid;
case kAstS128:
return kLocalS128;
+ case kAstStmt:
+ return kLocalVoid;
default:
UNREACHABLE();
return kLocalVoid;
diff --git a/deps/v8/src/wasm/wasm-result.cc b/deps/v8/src/wasm/wasm-result.cc
index 30268ac8ad..7d251f03df 100644
--- a/deps/v8/src/wasm/wasm-result.cc
+++ b/deps/v8/src/wasm/wasm-result.cc
@@ -27,15 +27,13 @@ std::ostream& operator<<(std::ostream& os, const ErrorCode& error_code) {
return os;
}
-void ErrorThrower::Error(const char* format, ...) {
+void ErrorThrower::Format(i::Handle<i::JSFunction> constructor,
+ const char* format, va_list args) {
// Only report the first error.
if (error()) return;
char buffer[256];
- va_list arguments;
- va_start(arguments, format);
- base::OS::VSNPrintF(buffer, 255, format, arguments);
- va_end(arguments);
+ base::OS::VSNPrintF(buffer, 255, format, args);
std::ostringstream str;
if (context_ != nullptr) {
@@ -43,12 +41,39 @@ void ErrorThrower::Error(const char* format, ...) {
}
str << buffer;
- message_ = isolate_->factory()->NewStringFromAsciiChecked(str.str().c_str());
+ i::Handle<i::String> message =
+ isolate_->factory()->NewStringFromAsciiChecked(str.str().c_str());
+ exception_ = isolate_->factory()->NewError(constructor, message);
+}
+
+void ErrorThrower::Error(const char* format, ...) {
+ if (error()) return;
+ va_list arguments;
+ va_start(arguments, format);
+ Format(isolate_->error_function(), format, arguments);
+ va_end(arguments);
+}
+
+void ErrorThrower::TypeError(const char* format, ...) {
+ if (error()) return;
+ va_list arguments;
+ va_start(arguments, format);
+ Format(isolate_->type_error_function(), format, arguments);
+ va_end(arguments);
+}
+
+void ErrorThrower::RangeError(const char* format, ...) {
+ if (error()) return;
+ va_list arguments;
+ va_start(arguments, format);
+ CHECK(*isolate_->range_error_function() != *isolate_->type_error_function());
+ Format(isolate_->range_error_function(), format, arguments);
+ va_end(arguments);
}
ErrorThrower::~ErrorThrower() {
if (error() && !isolate_->has_pending_exception()) {
- isolate_->ScheduleThrow(*message_);
+ isolate_->ScheduleThrow(*exception_);
}
}
} // namespace wasm
diff --git a/deps/v8/src/wasm/wasm-result.h b/deps/v8/src/wasm/wasm-result.h
index f16c15906d..ecc54e5b7a 100644
--- a/deps/v8/src/wasm/wasm-result.h
+++ b/deps/v8/src/wasm/wasm-result.h
@@ -22,19 +22,7 @@ namespace wasm {
// Error codes for programmatic checking of the decoder's verification.
enum ErrorCode {
kSuccess,
- kError, // TODO(titzer): remove me
- kOutOfMemory, // decoder ran out of memory
- kEndOfCode, // end of code reached prematurely
- kInvalidOpcode, // found invalid opcode
- kUnreachableCode, // found unreachable code
- kImproperContinue, // improperly nested continue
- kImproperBreak, // improperly nested break
- kReturnCount, // return count mismatch
- kTypeError, // type mismatch
- kInvalidLocalIndex, // invalid local
- kInvalidGlobalIndex, // invalid global
- kInvalidFunctionIndex, // invalid function
- kInvalidMemType // invalid memory type
+ kError, // TODO(titzer): introduce real error codes
};
// The overall result of decoding a function or a module.
@@ -97,33 +85,37 @@ std::ostream& operator<<(std::ostream& os, const Result<T>& result) {
std::ostream& operator<<(std::ostream& os, const ErrorCode& error_code);
// A helper for generating error messages that bubble up to JS exceptions.
-class ErrorThrower {
+class V8_EXPORT_PRIVATE ErrorThrower {
public:
- ErrorThrower(Isolate* isolate, const char* context)
+ ErrorThrower(i::Isolate* isolate, const char* context)
: isolate_(isolate), context_(context) {}
~ErrorThrower();
PRINTF_FORMAT(2, 3) void Error(const char* fmt, ...);
+ PRINTF_FORMAT(2, 3) void TypeError(const char* fmt, ...);
+ PRINTF_FORMAT(2, 3) void RangeError(const char* fmt, ...);
template <typename T>
void Failed(const char* error, Result<T>& result) {
std::ostringstream str;
str << error << result;
- return Error("%s", str.str().c_str());
+ Error("%s", str.str().c_str());
}
- i::Handle<i::String> Reify() {
- auto result = message_;
- message_ = i::Handle<i::String>();
+ i::Handle<i::Object> Reify() {
+ i::Handle<i::Object> result = exception_;
+ exception_ = i::Handle<i::Object>::null();
return result;
}
- bool error() const { return !message_.is_null(); }
+ bool error() const { return !exception_.is_null(); }
private:
- Isolate* isolate_;
+ void Format(i::Handle<i::JSFunction> constructor, const char* fmt, va_list);
+
+ i::Isolate* isolate_;
const char* context_;
- i::Handle<i::String> message_;
+ i::Handle<i::Object> exception_;
};
} // namespace wasm
} // namespace internal