summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2016-05-27 16:37:42 +0200
committerMichaël Zasso <targos@protonmail.com>2016-06-29 09:04:28 +0200
commit2cc29517966de7257a2f1b34c58c77225a21e05d (patch)
tree210bd177df2f06eec16e1e22edafdbcbffe66f8a /deps/v8/src/wasm
parentbbf3838c70aaec1dd296fa75ae334fd1c7866df3 (diff)
downloadnode-new-2cc29517966de7257a2f1b34c58c77225a21e05d.tar.gz
deps: update V8 to 5.1.281.69
Pick up the latest branch-head for V8 5.1. This branch brings in improved language support and performance improvements. For full details: http://v8project.blogspot.com/2016/04/v8-release-51.html * Picks up the latest branch head for 5.1 [1] * Edit v8 gitignore to allow trace_event copy * Update V8 DEP trace_event as per deps/v8/DEPS [2] [1] https://chromium.googlesource.com/v8/v8.git/+/dc81244 [2] https://chromium.googlesource.com/chromium/src/base/trace_event/common/+/c8c8665 PR-URL: https://github.com/nodejs/node/pull/7016 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'deps/v8/src/wasm')
-rw-r--r--deps/v8/src/wasm/asm-wasm-builder.cc463
-rw-r--r--deps/v8/src/wasm/asm-wasm-builder.h4
-rw-r--r--deps/v8/src/wasm/ast-decoder.cc655
-rw-r--r--deps/v8/src/wasm/ast-decoder.h169
-rw-r--r--deps/v8/src/wasm/decoder.h146
-rw-r--r--deps/v8/src/wasm/encoder.cc297
-rw-r--r--deps/v8/src/wasm/encoder.h14
-rw-r--r--deps/v8/src/wasm/module-decoder.cc514
-rw-r--r--deps/v8/src/wasm/module-decoder.h2
-rw-r--r--deps/v8/src/wasm/wasm-external-refs.h181
-rw-r--r--deps/v8/src/wasm/wasm-js.cc107
-rw-r--r--deps/v8/src/wasm/wasm-macro-gen.h300
-rw-r--r--deps/v8/src/wasm/wasm-module.cc293
-rw-r--r--deps/v8/src/wasm/wasm-module.h174
-rw-r--r--deps/v8/src/wasm/wasm-opcodes.cc3
-rw-r--r--deps/v8/src/wasm/wasm-opcodes.h117
16 files changed, 2425 insertions, 1014 deletions
diff --git a/deps/v8/src/wasm/asm-wasm-builder.cc b/deps/v8/src/wasm/asm-wasm-builder.cc
index ee5427b174..d16d3a8bdd 100644
--- a/deps/v8/src/wasm/asm-wasm-builder.cc
+++ b/deps/v8/src/wasm/asm-wasm-builder.cc
@@ -4,6 +4,12 @@
#include "src/v8.h"
+// Required to get M_E etc. in MSVC.
+#if defined(_WIN32)
+#define _USE_MATH_DEFINES
+#endif
+#include <math.h>
+
#include "src/wasm/asm-wasm-builder.h"
#include "src/wasm/wasm-macro-gen.h"
#include "src/wasm/wasm-opcodes.h"
@@ -28,7 +34,7 @@ namespace wasm {
class AsmWasmBuilderImpl : public AstVisitor {
public:
AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal,
- Handle<Object> foreign)
+ Handle<Object> foreign, AsmTyper* typer)
: local_variables_(HashMap::PointersMatch,
ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)),
@@ -46,6 +52,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
isolate_(isolate),
zone_(zone),
foreign_(foreign),
+ typer_(typer),
cache_(TypeCache::Get()),
breakable_blocks_(zone),
block_size_(0),
@@ -59,12 +66,10 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
void InitializeInitFunction() {
- unsigned char init[] = "__init__";
init_function_index_ = builder_->AddFunction();
current_function_builder_ = builder_->FunctionAt(init_function_index_);
- current_function_builder_->SetName(init, 8);
current_function_builder_->ReturnType(kAstStmt);
- current_function_builder_->Exported(1);
+ builder_->MarkStartFunction(init_function_index_);
current_function_builder_ = nullptr;
}
@@ -133,13 +138,14 @@ class AsmWasmBuilderImpl : public AstVisitor {
: builder_(builder) {
builder_->breakable_blocks_.push_back(std::make_pair(stmt, is_loop));
builder_->current_function_builder_->Emit(opcode);
- index_ = builder_->current_function_builder_->EmitEditableImmediate(0);
+ index_ =
+ builder_->current_function_builder_->EmitEditableVarIntImmediate();
prev_block_size_ = builder_->block_size_;
builder_->block_size_ = initial_block_size;
}
~BlockVisitor() {
- builder_->current_function_builder_->EditImmediate(index_,
- builder_->block_size_);
+ builder_->current_function_builder_->EditVarIntImmediate(
+ index_, builder_->block_size_);
builder_->block_size_ = prev_block_size_;
builder_->breakable_blocks_.pop_back();
}
@@ -188,7 +194,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
}
DCHECK(i >= 0);
- current_function_builder_->EmitWithU8(kExprBr, block_distance);
+ current_function_builder_->EmitWithVarInt(kExprBr, block_distance);
current_function_builder_->Emit(kExprNop);
}
@@ -211,7 +217,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
}
DCHECK(i >= 0);
- current_function_builder_->EmitWithU8(kExprBr, block_distance);
+ current_function_builder_->EmitWithVarInt(kExprBr, block_distance);
current_function_builder_->Emit(kExprNop);
}
@@ -232,7 +238,8 @@ class AsmWasmBuilderImpl : public AstVisitor {
void SetLocalTo(uint16_t index, int value) {
current_function_builder_->Emit(kExprSetLocal);
AddLeb128(index, true);
- byte code[] = {WASM_I32(value)};
+ // TODO(bradnelson): variable size
+ byte code[] = {WASM_I32V(value)};
current_function_builder_->EmitCode(code, sizeof(code));
block_size_++;
}
@@ -286,7 +293,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
RECURSE(Visit(stmt->body()));
current_function_builder_->Emit(kExprIf);
RECURSE(Visit(stmt->cond()));
- current_function_builder_->EmitWithU8(kExprBr, 0);
+ current_function_builder_->EmitWithVarInt(kExprBr, 0);
current_function_builder_->Emit(kExprNop);
}
@@ -296,7 +303,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
1);
current_function_builder_->Emit(kExprIf);
RECURSE(Visit(stmt->cond()));
- current_function_builder_->EmitWithU8(kExprBr, 0);
+ current_function_builder_->EmitWithVarInt(kExprBr, 0);
RECURSE(Visit(stmt->body()));
}
@@ -311,9 +318,9 @@ class AsmWasmBuilderImpl : public AstVisitor {
if (stmt->cond() != nullptr) {
block_size_++;
current_function_builder_->Emit(kExprIf);
- current_function_builder_->Emit(kExprBoolNot);
+ current_function_builder_->Emit(kExprI32Eqz);
RECURSE(Visit(stmt->cond()));
- current_function_builder_->EmitWithU8(kExprBr, 1);
+ current_function_builder_->EmitWithVarInt(kExprBr, 1);
current_function_builder_->Emit(kExprNop);
}
if (stmt->body() != nullptr) {
@@ -325,7 +332,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
RECURSE(Visit(stmt->next()));
}
block_size_++;
- current_function_builder_->EmitWithU8(kExprBr, 0);
+ current_function_builder_->EmitWithVarInt(kExprBr, 0);
current_function_builder_->Emit(kExprNop);
}
@@ -371,6 +378,58 @@ class AsmWasmBuilderImpl : public AstVisitor {
RECURSE(Visit(expr->else_expression()));
}
+ bool VisitStdlibConstant(Variable* var) {
+ AsmTyper::StandardMember standard_object =
+ typer_->VariableAsStandardMember(var);
+ double value;
+ switch (standard_object) {
+ case AsmTyper::kInfinity: {
+ value = std::numeric_limits<double>::infinity();
+ break;
+ }
+ case AsmTyper::kNaN: {
+ value = std::numeric_limits<double>::quiet_NaN();
+ break;
+ }
+ case AsmTyper::kMathE: {
+ value = M_E;
+ break;
+ }
+ case AsmTyper::kMathLN10: {
+ value = M_LN10;
+ break;
+ }
+ case AsmTyper::kMathLN2: {
+ value = M_LN2;
+ break;
+ }
+ case AsmTyper::kMathLOG10E: {
+ value = M_LOG10E;
+ break;
+ }
+ case AsmTyper::kMathLOG2E: {
+ value = M_LOG2E;
+ break;
+ }
+ case AsmTyper::kMathPI: {
+ value = M_PI;
+ break;
+ }
+ case AsmTyper::kMathSQRT1_2: {
+ value = M_SQRT1_2;
+ break;
+ }
+ case AsmTyper::kMathSQRT2: {
+ value = M_SQRT2;
+ break;
+ }
+ default: { return false; }
+ }
+ byte code[] = {WASM_F64(value)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ return true;
+ }
+
void VisitVariableProxy(VariableProxy* expr) {
if (in_function_) {
Variable* var = expr->var();
@@ -382,6 +441,9 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
is_set_op_ = false;
} else {
+ if (VisitStdlibConstant(var)) {
+ return;
+ }
if (var->IsContextSlot()) {
current_function_builder_->Emit(kExprLoadGlobal);
} else {
@@ -399,32 +461,32 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
void VisitLiteral(Literal* expr) {
- if (in_function_) {
- if (expr->raw_value()->IsNumber()) {
- LocalType type = TypeOf(expr);
- switch (type) {
- case kAstI32: {
- int val = static_cast<int>(expr->raw_value()->AsNumber());
- byte code[] = {WASM_I32(val)};
- current_function_builder_->EmitCode(code, sizeof(code));
- break;
- }
- case kAstF32: {
- float val = static_cast<float>(expr->raw_value()->AsNumber());
- byte code[] = {WASM_F32(val)};
- current_function_builder_->EmitCode(code, sizeof(code));
- break;
- }
- case kAstF64: {
- double val = static_cast<double>(expr->raw_value()->AsNumber());
- byte code[] = {WASM_F64(val)};
- current_function_builder_->EmitCode(code, sizeof(code));
- break;
- }
- default:
- UNREACHABLE();
- }
+ Handle<Object> value = expr->value();
+ if (!in_function_ || !value->IsNumber()) {
+ return;
+ }
+ Type* type = expr->bounds().upper;
+ if (type->Is(cache_.kAsmSigned)) {
+ int32_t i = 0;
+ if (!value->ToInt32(&i)) {
+ UNREACHABLE();
}
+ byte code[] = {WASM_I32V(i)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ } else if (type->Is(cache_.kAsmUnsigned) || type->Is(cache_.kAsmFixnum)) {
+ uint32_t u = 0;
+ if (!value->ToUint32(&u)) {
+ UNREACHABLE();
+ }
+ int32_t i = static_cast<int32_t>(u);
+ byte code[] = {WASM_I32V(i)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ } else if (type->Is(cache_.kAsmDouble)) {
+ double val = expr->raw_value()->AsNumber();
+ byte code[] = {WASM_F64(val)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ } else {
+ UNREACHABLE();
}
}
@@ -589,29 +651,33 @@ class AsmWasmBuilderImpl : public AstVisitor {
UnLoadInitFunction();
return;
}
- // TODO(bradnelson): Get rid of this.
- if (TypeOf(expr->value()) == kAstStmt) {
- Property* prop = expr->value()->AsProperty();
- if (prop != nullptr) {
- VariableProxy* vp = prop->obj()->AsVariableProxy();
- if (vp != nullptr && vp->var()->IsParameter() &&
- vp->var()->index() == 1) {
- VariableProxy* target = expr->target()->AsVariableProxy();
- if (target->bounds().lower->Is(Type::Function())) {
- const AstRawString* name =
- prop->key()->AsLiteral()->AsRawPropertyName();
- imported_function_table_.AddImport(
- target->var(), name->raw_data(), name->length());
- }
- }
- }
- ArrayLiteral* funcs = expr->value()->AsArrayLiteral();
- if (funcs != nullptr &&
- funcs->bounds().lower->AsArray()->Element()->IsFunction()) {
+ Property* prop = expr->value()->AsProperty();
+ if (prop != nullptr) {
+ VariableProxy* vp = prop->obj()->AsVariableProxy();
+ if (vp != nullptr && vp->var()->IsParameter() &&
+ vp->var()->index() == 1) {
VariableProxy* target = expr->target()->AsVariableProxy();
- DCHECK_NOT_NULL(target);
- AddFunctionTable(target, funcs);
+ if (target->bounds().lower->Is(Type::Function())) {
+ const AstRawString* name =
+ prop->key()->AsLiteral()->AsRawPropertyName();
+ imported_function_table_.AddImport(target->var(), name->raw_data(),
+ name->length());
+ }
}
+ // Property values in module scope don't emit code, so return.
+ return;
+ }
+ ArrayLiteral* funcs = expr->value()->AsArrayLiteral();
+ if (funcs != nullptr &&
+ funcs->bounds().lower->AsArray()->Element()->IsFunction()) {
+ VariableProxy* target = expr->target()->AsVariableProxy();
+ DCHECK_NOT_NULL(target);
+ AddFunctionTable(target, funcs);
+ // Only add to the function table. No init needed.
+ return;
+ }
+ if (expr->value()->IsCallNew()) {
+ // No init code to emit for CallNew nodes.
return;
}
in_init = true;
@@ -630,6 +696,12 @@ class AsmWasmBuilderImpl : public AstVisitor {
is_set_op_ = true;
RECURSE(Visit(expr->target()));
DCHECK(!is_set_op_);
+ // Assignment to heapf32 from float64 converts.
+ if (TypeOf(expr->value()) == kAstF64 && expr->target()->IsProperty() &&
+ expr->target()->AsProperty()->obj()->bounds().lower->Is(
+ cache_.kFloat32Array)) {
+ current_function_builder_->Emit(kExprF32ConvertF64);
+ }
RECURSE(Visit(expr->value()));
if (in_init) {
UnLoadInitFunction();
@@ -672,7 +744,8 @@ class AsmWasmBuilderImpl : public AstVisitor {
Handle<Object> nvalue = maybe_nvalue.ToHandleChecked();
if (nvalue->IsNumber()) {
int32_t val = static_cast<int32_t>(nvalue->Number());
- byte code[] = {WASM_I32(val)};
+ // TODO(bradnelson): variable size
+ byte code[] = {WASM_I32V(val)};
current_function_builder_->EmitCode(code, sizeof(code));
return;
}
@@ -684,7 +757,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
byte code[] = {WASM_F64(std::numeric_limits<double>::quiet_NaN())};
current_function_builder_->EmitCode(code, sizeof(code));
} else {
- byte code[] = {WASM_I32(0)};
+ byte code[] = {WASM_I32V_1(0)};
current_function_builder_->EmitCode(code, sizeof(code));
}
}
@@ -725,9 +798,9 @@ class AsmWasmBuilderImpl : public AstVisitor {
} else {
UNREACHABLE();
}
- current_function_builder_->EmitWithU8(
- WasmOpcodes::LoadStoreOpcodeOf(mtype, is_set_op_),
- WasmOpcodes::LoadStoreAccessOf(false));
+ // TODO(titzer): use special asm-compatibility opcodes?
+ current_function_builder_->EmitWithU8U8(
+ WasmOpcodes::LoadStoreOpcodeOf(mtype, is_set_op_), 0, 0);
is_set_op_ = false;
if (size == 1) {
// Allow more general expression in byte arrays than the spec
@@ -742,7 +815,8 @@ class AsmWasmBuilderImpl : public AstVisitor {
DCHECK(value->raw_value()->IsNumber());
DCHECK_EQ(kAstI32, TypeOf(value));
int val = static_cast<int>(value->raw_value()->AsNumber());
- byte code[] = {WASM_I32(val * size)};
+ // TODO(bradnelson): variable size
+ byte code[] = {WASM_I32V(val * size)};
current_function_builder_->EmitCode(code, sizeof(code));
return;
}
@@ -765,11 +839,209 @@ class AsmWasmBuilderImpl : public AstVisitor {
UNREACHABLE();
}
+ bool VisitStdlibFunction(Call* call, VariableProxy* expr) {
+ Variable* var = expr->var();
+ AsmTyper::StandardMember standard_object =
+ typer_->VariableAsStandardMember(var);
+ ZoneList<Expression*>* args = call->arguments();
+ LocalType call_type = TypeOf(call);
+ switch (standard_object) {
+ case AsmTyper::kNone: {
+ return false;
+ }
+ case AsmTyper::kMathAcos: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Acos);
+ break;
+ }
+ case AsmTyper::kMathAsin: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Asin);
+ break;
+ }
+ case AsmTyper::kMathAtan: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Atan);
+ break;
+ }
+ case AsmTyper::kMathCos: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Cos);
+ break;
+ }
+ case AsmTyper::kMathSin: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Sin);
+ break;
+ }
+ case AsmTyper::kMathTan: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Tan);
+ break;
+ }
+ case AsmTyper::kMathExp: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Exp);
+ break;
+ }
+ case AsmTyper::kMathLog: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Log);
+ break;
+ }
+ case AsmTyper::kMathCeil: {
+ if (call_type == kAstF32) {
+ current_function_builder_->Emit(kExprF32Ceil);
+ } else if (call_type == kAstF64) {
+ current_function_builder_->Emit(kExprF64Ceil);
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ }
+ case AsmTyper::kMathFloor: {
+ if (call_type == kAstF32) {
+ current_function_builder_->Emit(kExprF32Floor);
+ } else if (call_type == kAstF64) {
+ current_function_builder_->Emit(kExprF64Floor);
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ }
+ case AsmTyper::kMathSqrt: {
+ if (call_type == kAstF32) {
+ current_function_builder_->Emit(kExprF32Sqrt);
+ } else if (call_type == kAstF64) {
+ current_function_builder_->Emit(kExprF64Sqrt);
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ }
+ case AsmTyper::kMathAbs: {
+ // TODO(bradnelson): Should this be cast to float?
+ if (call_type == kAstI32) {
+ current_function_builder_->Emit(kExprIfElse);
+ current_function_builder_->Emit(kExprI32LtS);
+ Visit(args->at(0));
+ byte code[] = {WASM_I8(0)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ current_function_builder_->Emit(kExprI32Sub);
+ current_function_builder_->EmitCode(code, sizeof(code));
+ Visit(args->at(0));
+ } else if (call_type == kAstF32) {
+ current_function_builder_->Emit(kExprF32Abs);
+ } else if (call_type == kAstF64) {
+ current_function_builder_->Emit(kExprF64Abs);
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ }
+ case AsmTyper::kMathMin: {
+ // TODO(bradnelson): Change wasm to match Math.min in asm.js mode.
+ if (call_type == kAstI32) {
+ current_function_builder_->Emit(kExprIfElse);
+ current_function_builder_->Emit(kExprI32LeS);
+ Visit(args->at(0));
+ Visit(args->at(1));
+ } else if (call_type == kAstF32) {
+ current_function_builder_->Emit(kExprF32Min);
+ } else if (call_type == kAstF64) {
+ current_function_builder_->Emit(kExprF64Min);
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ }
+ case AsmTyper::kMathMax: {
+ // TODO(bradnelson): Change wasm to match Math.max in asm.js mode.
+ if (call_type == kAstI32) {
+ current_function_builder_->Emit(kExprIfElse);
+ current_function_builder_->Emit(kExprI32GtS);
+ Visit(args->at(0));
+ Visit(args->at(1));
+ } else if (call_type == kAstF32) {
+ current_function_builder_->Emit(kExprF32Max);
+ } else if (call_type == kAstF64) {
+ current_function_builder_->Emit(kExprF64Max);
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ }
+ case AsmTyper::kMathAtan2: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Atan2);
+ break;
+ }
+ case AsmTyper::kMathPow: {
+ DCHECK_EQ(kAstF64, call_type);
+ current_function_builder_->Emit(kExprF64Pow);
+ break;
+ }
+ case AsmTyper::kMathImul: {
+ current_function_builder_->Emit(kExprI32Mul);
+ break;
+ }
+ case AsmTyper::kMathFround: {
+ DCHECK(args->length() == 1);
+ Literal* literal = args->at(0)->AsLiteral();
+ if (literal != nullptr) {
+ if (literal->raw_value()->IsNumber()) {
+ float val = static_cast<float>(literal->raw_value()->AsNumber());
+ byte code[] = {WASM_F32(val)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ return true;
+ }
+ }
+ switch (TypeIndexOf(args->at(0))) {
+ case kInt32:
+ case kFixnum:
+ current_function_builder_->Emit(kExprF32SConvertI32);
+ break;
+ case kUint32:
+ current_function_builder_->Emit(kExprF32UConvertI32);
+ break;
+ case kFloat32:
+ break;
+ case kFloat64:
+ current_function_builder_->Emit(kExprF32ConvertF64);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ }
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ VisitCallArgs(call);
+ return true;
+ }
+
+ void VisitCallArgs(Call* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ for (int i = 0; i < args->length(); ++i) {
+ Expression* arg = args->at(i);
+ RECURSE(Visit(arg));
+ }
+ }
+
void VisitCall(Call* expr) {
Call::CallType call_type = expr->GetCallType(isolate_);
switch (call_type) {
case Call::OTHER_CALL: {
DCHECK(in_function_);
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+ if (proxy != nullptr) {
+ if (VisitStdlibFunction(expr, proxy)) {
+ return;
+ }
+ }
uint16_t index;
VariableProxy* vp = expr->expression()->AsVariableProxy();
if (vp != nullptr &&
@@ -802,10 +1074,11 @@ class AsmWasmBuilderImpl : public AstVisitor {
VariableProxy* var = p->obj()->AsVariableProxy();
DCHECK_NOT_NULL(var);
FunctionTableIndices* indices = LookupFunctionTable(var->var());
- current_function_builder_->EmitWithU8(kExprCallIndirect,
- indices->signature_index);
+ current_function_builder_->EmitWithVarInt(kExprCallIndirect,
+ indices->signature_index);
current_function_builder_->Emit(kExprI32Add);
- byte code[] = {WASM_I32(indices->start_index)};
+ // TODO(bradnelson): variable size
+ byte code[] = {WASM_I32V(indices->start_index)};
current_function_builder_->EmitCode(code, sizeof(code));
RECURSE(Visit(p->key()));
break;
@@ -813,11 +1086,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
default:
UNREACHABLE();
}
- ZoneList<Expression*>* args = expr->arguments();
- for (int i = 0; i < args->length(); ++i) {
- Expression* arg = args->at(i);
- RECURSE(Visit(arg));
- }
+ VisitCallArgs(expr);
}
void VisitCallNew(CallNew* expr) { UNREACHABLE(); }
@@ -828,7 +1097,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
switch (expr->op()) {
case Token::NOT: {
DCHECK_EQ(kAstI32, TypeOf(expr->expression()));
- current_function_builder_->Emit(kExprBoolNot);
+ current_function_builder_->Emit(kExprI32Eqz);
break;
}
default:
@@ -1022,7 +1291,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
} else if (type == kUint32) {
current_function_builder_->Emit(kExprI32RemU);
} else if (type == kFloat64) {
- ModF64(expr);
+ current_function_builder_->Emit(kExprF64Mod);
return;
} else {
UNREACHABLE();
@@ -1030,7 +1299,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
break;
}
case Token::COMMA: {
- current_function_builder_->EmitWithU8(kExprBlock, 2);
+ current_function_builder_->EmitWithVarInt(kExprBlock, 2);
break;
}
default:
@@ -1041,32 +1310,6 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
}
- void ModF64(BinaryOperation* expr) {
- current_function_builder_->EmitWithU8(kExprBlock, 3);
- uint16_t index_0 = current_function_builder_->AddLocal(kAstF64);
- uint16_t index_1 = current_function_builder_->AddLocal(kAstF64);
- current_function_builder_->Emit(kExprSetLocal);
- AddLeb128(index_0, true);
- RECURSE(Visit(expr->left()));
- current_function_builder_->Emit(kExprSetLocal);
- AddLeb128(index_1, true);
- RECURSE(Visit(expr->right()));
- current_function_builder_->Emit(kExprF64Sub);
- current_function_builder_->Emit(kExprGetLocal);
- AddLeb128(index_0, true);
- current_function_builder_->Emit(kExprF64Mul);
- current_function_builder_->Emit(kExprGetLocal);
- AddLeb128(index_1, true);
- // Use trunc instead of two casts
- current_function_builder_->Emit(kExprF64SConvertI32);
- current_function_builder_->Emit(kExprI32SConvertF64);
- current_function_builder_->Emit(kExprF64Div);
- current_function_builder_->Emit(kExprGetLocal);
- AddLeb128(index_0, true);
- current_function_builder_->Emit(kExprGetLocal);
- AddLeb128(index_1, true);
- }
-
void AddLeb128(uint32_t index, bool is_local) {
std::vector<uint8_t> index_vec = UnsignedLEB128From(index);
if (is_local) {
@@ -1262,6 +1505,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
Isolate* isolate_;
Zone* zone_;
Handle<Object> foreign_;
+ AsmTyper* typer_;
TypeCache const& cache_;
ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
int block_size_;
@@ -1277,13 +1521,18 @@ class AsmWasmBuilderImpl : public AstVisitor {
};
AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
- FunctionLiteral* literal, Handle<Object> foreign)
- : isolate_(isolate), zone_(zone), literal_(literal), foreign_(foreign) {}
+ FunctionLiteral* literal, Handle<Object> foreign,
+ AsmTyper* typer)
+ : isolate_(isolate),
+ zone_(zone),
+ literal_(literal),
+ foreign_(foreign),
+ typer_(typer) {}
// TODO(aseemgarg): probably should take zone (to write wasm to) as input so
// that zone in constructor may be thrown away once wasm module is written.
WasmModuleIndex* AsmWasmBuilder::Run() {
- AsmWasmBuilderImpl impl(isolate_, zone_, literal_, foreign_);
+ AsmWasmBuilderImpl impl(isolate_, zone_, literal_, foreign_, typer_);
impl.Compile();
WasmModuleWriter* writer = impl.builder_->Build(zone_);
return writer->WriteTo(zone_);
diff --git a/deps/v8/src/wasm/asm-wasm-builder.h b/deps/v8/src/wasm/asm-wasm-builder.h
index 9b761f9040..09645ee3c4 100644
--- a/deps/v8/src/wasm/asm-wasm-builder.h
+++ b/deps/v8/src/wasm/asm-wasm-builder.h
@@ -7,6 +7,7 @@
#include "src/allocation.h"
#include "src/objects.h"
+#include "src/typing-asm.h"
#include "src/wasm/encoder.h"
#include "src/zone.h"
@@ -20,7 +21,7 @@ namespace wasm {
class AsmWasmBuilder {
public:
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
- Handle<Object> foreign);
+ Handle<Object> foreign, AsmTyper* typer);
WasmModuleIndex* Run();
private:
@@ -28,6 +29,7 @@ class AsmWasmBuilder {
Zone* zone_;
FunctionLiteral* literal_;
Handle<Object> foreign_;
+ AsmTyper* typer_;
};
} // namespace wasm
} // namespace internal
diff --git a/deps/v8/src/wasm/ast-decoder.cc b/deps/v8/src/wasm/ast-decoder.cc
index c97c781c12..e2f6a046b3 100644
--- a/deps/v8/src/wasm/ast-decoder.cc
+++ b/deps/v8/src/wasm/ast-decoder.cc
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "src/base/platform/elapsed-timer.h"
#include "src/signature.h"
#include "src/bit-vector.h"
@@ -15,6 +14,8 @@
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
+#include "src/ostreams.h"
+
#include "src/compiler/wasm-compiler.h"
namespace v8 {
@@ -52,7 +53,6 @@ struct Production {
Tree* last() const { return index > 0 ? tree->children[index - 1] : nullptr; }
};
-
// An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph.
// It maintains a control state that tracks whether the environment
@@ -74,14 +74,12 @@ struct SsaEnv {
}
};
-
// An entry in the stack of blocks during decoding.
struct Block {
SsaEnv* ssa_env; // SSA renaming environment.
int stack_depth; // production stack depth.
};
-
// An entry in the stack of ifs during decoding.
struct IfEnv {
SsaEnv* false_env;
@@ -89,27 +87,27 @@ struct IfEnv {
SsaEnv** case_envs;
};
-
// 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)
-
// Generic Wasm bytecode decoder with utilities for decoding operands,
// lengths, etc.
class WasmDecoder : public Decoder {
public:
- WasmDecoder() : Decoder(nullptr, nullptr), function_env_(nullptr) {}
- WasmDecoder(FunctionEnv* env, const byte* start, const byte* end)
- : Decoder(start, end), function_env_(env) {}
- FunctionEnv* function_env_;
-
- void Reset(FunctionEnv* function_env, const byte* start, const byte* end) {
- Decoder::Reset(start, end);
- function_env_ = function_env;
- }
+ WasmDecoder(ModuleEnv* module, FunctionSig* sig, const byte* start,
+ const byte* end)
+ : Decoder(start, end),
+ module_(module),
+ sig_(sig),
+ total_locals_(0),
+ local_types_(nullptr) {}
+ ModuleEnv* module_;
+ FunctionSig* sig_;
+ size_t total_locals_;
+ ZoneVector<LocalType>* local_types_;
byte ByteOperand(const byte* pc, const char* msg = "missing 1-byte operand") {
if ((pc + sizeof(byte)) >= limit_) {
@@ -136,8 +134,12 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, LocalIndexOperand& operand) {
- if (operand.index < function_env_->total_locals) {
- operand.type = function_env_->GetLocalType(operand.index);
+ if (operand.index < total_locals_) {
+ if (local_types_) {
+ operand.type = local_types_->at(operand.index);
+ } else {
+ operand.type = kAstStmt;
+ }
return true;
}
error(pc, pc + 1, "invalid local index");
@@ -145,9 +147,9 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, GlobalIndexOperand& operand) {
- ModuleEnv* m = function_env_->module;
- if (m && m->module && operand.index < m->module->globals->size()) {
- operand.machine_type = m->module->globals->at(operand.index).type;
+ ModuleEnv* m = module_;
+ if (m && m->module && operand.index < m->module->globals.size()) {
+ operand.machine_type = m->module->globals[operand.index].type;
operand.type = WasmOpcodes::LocalTypeFor(operand.machine_type);
return true;
}
@@ -156,9 +158,9 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, FunctionIndexOperand& operand) {
- ModuleEnv* m = function_env_->module;
- if (m && m->module && operand.index < m->module->functions->size()) {
- operand.sig = m->module->functions->at(operand.index).sig;
+ ModuleEnv* m = module_;
+ if (m && m->module && operand.index < m->module->functions.size()) {
+ operand.sig = m->module->functions[operand.index].sig;
return true;
}
error(pc, pc + 1, "invalid function index");
@@ -166,9 +168,9 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, SignatureIndexOperand& operand) {
- ModuleEnv* m = function_env_->module;
- if (m && m->module && operand.index < m->module->signatures->size()) {
- operand.sig = m->module->signatures->at(operand.index);
+ ModuleEnv* m = module_;
+ if (m && m->module && operand.index < m->module->signatures.size()) {
+ operand.sig = m->module->signatures[operand.index];
return true;
}
error(pc, pc + 1, "invalid signature index");
@@ -176,9 +178,9 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, ImportIndexOperand& operand) {
- ModuleEnv* m = function_env_->module;
- if (m && m->module && operand.index < m->module->import_table->size()) {
- operand.sig = m->module->import_table->at(operand.index).sig;
+ 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;
}
error(pc, pc + 1, "invalid signature index");
@@ -195,26 +197,14 @@ class WasmDecoder : public Decoder {
return false;
}
- bool Validate(const byte* pc, TableSwitchOperand& operand,
+ bool Validate(const byte* pc, BranchTableOperand& operand,
size_t block_depth) {
- if (operand.table_count == 0) {
- error(pc, "tableswitch with 0 entries");
- return false;
- }
// Verify table.
- for (uint32_t i = 0; i < operand.table_count; i++) {
- uint16_t target = operand.read_entry(this, i);
- if (target >= 0x8000) {
- size_t depth = target - 0x8000;
- if (depth > block_depth) {
- error(operand.table + i * 2, "improper branch in tableswitch");
- return false;
- }
- } else {
- if (target >= operand.case_count) {
- error(operand.table + i * 2, "invalid case target in tableswitch");
- return false;
- }
+ 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;
}
}
return true;
@@ -262,27 +252,23 @@ class WasmDecoder : public Decoder {
case kExprCallFunction: {
FunctionIndexOperand operand(this, pc);
return static_cast<int>(
- function_env_->module->GetFunctionSignature(operand.index)
- ->parameter_count());
+ module_->GetFunctionSignature(operand.index)->parameter_count());
}
case kExprCallIndirect: {
SignatureIndexOperand operand(this, pc);
return 1 + static_cast<int>(
- function_env_->module->GetSignature(operand.index)
- ->parameter_count());
+ module_->GetSignature(operand.index)->parameter_count());
}
case kExprCallImport: {
ImportIndexOperand operand(this, pc);
return static_cast<int>(
- function_env_->module->GetImportSignature(operand.index)
- ->parameter_count());
+ module_->GetImportSignature(operand.index)->parameter_count());
}
case kExprReturn: {
- return static_cast<int>(function_env_->sig->return_count());
+ return static_cast<int>(sig_->return_count());
}
- case kExprTableSwitch: {
- TableSwitchOperand operand(this, pc);
- return 1 + operand.case_count;
+ case kExprBrTable: {
+ return 1;
}
#define DECLARE_OPCODE_CASE(name, opcode, sig) \
@@ -293,10 +279,13 @@ class WasmDecoder : public Decoder {
FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE)
FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE)
+ FOREACH_ASMJS_COMPAT_OPCODE(DECLARE_OPCODE_CASE)
#undef DECLARE_OPCODE_CASE
+ case kExprDeclLocals:
+ default:
+ UNREACHABLE();
+ return 0;
}
- UNREACHABLE();
- return 0;
}
int OpcodeLength(const byte* pc) {
@@ -343,16 +332,22 @@ class WasmDecoder : public Decoder {
LocalIndexOperand operand(this, pc);
return 1 + operand.length;
}
- case kExprTableSwitch: {
- TableSwitchOperand operand(this, pc);
+ case kExprBrTable: {
+ BranchTableOperand operand(this, pc);
+ return 1 + operand.length;
+ }
+ case kExprI32Const: {
+ ImmI32Operand operand(this, pc);
+ return 1 + operand.length;
+ }
+ case kExprI64Const: {
+ ImmI64Operand operand(this, pc);
return 1 + operand.length;
}
case kExprI8Const:
return 2;
- case kExprI32Const:
case kExprF32Const:
return 5;
- case kExprI64Const:
case kExprF64Const:
return 9;
@@ -365,35 +360,28 @@ class WasmDecoder : public Decoder {
// A shift-reduce-parser strategy for decoding Wasm code that uses an explicit
// shift-reduce strategy with multiple internal stacks.
-class LR_WasmDecoder : public WasmDecoder {
+class SR_WasmDecoder : public WasmDecoder {
public:
- LR_WasmDecoder(Zone* zone, TFBuilder* builder)
- : zone_(zone),
+ SR_WasmDecoder(Zone* zone, TFBuilder* builder, FunctionBody& body)
+ : WasmDecoder(body.module, body.sig, body.start, body.end),
+ zone_(zone),
builder_(builder),
+ base_(body.base),
+ local_type_vec_(zone),
trees_(zone),
stack_(zone),
blocks_(zone),
- ifs_(zone) {}
-
- TreeResult Decode(FunctionEnv* function_env, const byte* base, const byte* pc,
- const byte* end) {
- base::ElapsedTimer decode_timer;
- if (FLAG_trace_wasm_decode_time) {
- decode_timer.Start();
- }
- trees_.clear();
- stack_.clear();
- blocks_.clear();
- ifs_.clear();
+ ifs_(zone) {
+ local_types_ = &local_type_vec_;
+ }
- if (end < pc) {
- error(pc, "function body end < start");
+ TreeResult Decode() {
+ if (end_ < pc_) {
+ error(pc_, "function body end < start");
return result_;
}
- base_ = base;
- Reset(function_env, pc, end);
-
+ DecodeLocalDecls();
InitSsaEnv();
DecodeFunctionBody();
@@ -401,12 +389,12 @@ class LR_WasmDecoder : public WasmDecoder {
if (ok()) {
if (ssa_env_->go()) {
if (stack_.size() > 0) {
- error(stack_.back().pc(), end, "fell off end of code");
+ error(stack_.back().pc(), end_, "fell off end of code");
}
AddImplicitReturnAtEnd();
}
if (trees_.size() == 0) {
- if (function_env_->sig->return_count() > 0) {
+ if (sig_->return_count() > 0) {
error(start_, "no trees created");
}
} else {
@@ -415,15 +403,7 @@ class LR_WasmDecoder : public WasmDecoder {
}
if (ok()) {
- if (FLAG_trace_wasm_ast) {
- PrintAst(function_env, pc, end);
- }
- if (FLAG_trace_wasm_decode_time) {
- double ms = decode_timer.Elapsed().InMillisecondsF();
- PrintF("wasm-decode ok (%0.3f ms)\n\n", ms);
- } else {
- TRACE("wasm-decode ok\n\n");
- }
+ TRACE("wasm-decode ok\n");
} else {
TRACE("wasm-error module+%-6d func+%d: %s\n\n", baserel(error_pc_),
startrel(error_pc_), error_msg_.get());
@@ -432,6 +412,36 @@ class LR_WasmDecoder : public WasmDecoder {
return toResult(tree);
}
+ bool DecodeLocalDecls(AstLocalDecls& decls) {
+ DecodeLocalDecls();
+ if (failed()) return false;
+ decls.decls_encoded_size = pc_offset();
+ decls.total_local_count = 0;
+ decls.local_types.reserve(local_type_vec_.size());
+ for (size_t pos = 0; pos < local_type_vec_.size();) {
+ uint32_t count = 0;
+ LocalType type = local_type_vec_[pos];
+ while (pos < local_type_vec_.size() && local_type_vec_[pos] == type) {
+ pos++;
+ count++;
+ }
+ decls.total_local_count += count;
+ decls.local_types.push_back(std::pair<LocalType, uint32_t>(type, count));
+ }
+ return true;
+ }
+
+ BitVector* AnalyzeLoopAssignmentForTesting(const byte* pc,
+ size_t num_locals) {
+ total_locals_ = num_locals;
+ local_type_vec_.reserve(num_locals);
+ if (num_locals > local_type_vec_.size()) {
+ local_type_vec_.insert(local_type_vec_.end(),
+ num_locals - local_type_vec_.size(), kAstI32);
+ }
+ return AnalyzeLoopAssignment(pc);
+ }
+
private:
static const size_t kErrorMsgSize = 128;
@@ -442,6 +452,7 @@ class LR_WasmDecoder : public WasmDecoder {
SsaEnv* ssa_env_;
+ ZoneVector<LocalType> local_type_vec_;
ZoneVector<Tree*> trees_;
ZoneVector<Production> stack_;
ZoneVector<Block> blocks_;
@@ -450,8 +461,6 @@ class LR_WasmDecoder : public WasmDecoder {
inline bool build() { return builder_ && ssa_env_->go(); }
void InitSsaEnv() {
- FunctionSig* sig = function_env_->sig;
- int param_count = static_cast<int>(sig->parameter_count());
TFNode* start = nullptr;
SsaEnv* ssa_env = reinterpret_cast<SsaEnv*>(zone_->New(sizeof(SsaEnv)));
size_t size = sizeof(TFNode*) * EnvironmentCount();
@@ -459,50 +468,46 @@ class LR_WasmDecoder : public WasmDecoder {
ssa_env->locals =
size > 0 ? reinterpret_cast<TFNode**>(zone_->New(size)) : nullptr;
- int pos = 0;
if (builder_) {
- start = builder_->Start(param_count + 1);
- // Initialize parameters.
- for (int i = 0; i < param_count; i++) {
- ssa_env->locals[pos++] = builder_->Param(i, sig->GetParam(i));
- }
- // Initialize int32 locals.
- if (function_env_->local_i32_count > 0) {
- TFNode* zero = builder_->Int32Constant(0);
- for (uint32_t i = 0; i < function_env_->local_i32_count; i++) {
- ssa_env->locals[pos++] = zero;
- }
- }
- // Initialize int64 locals.
- if (function_env_->local_i64_count > 0) {
- TFNode* zero = builder_->Int64Constant(0);
- for (uint32_t i = 0; i < function_env_->local_i64_count; i++) {
- ssa_env->locals[pos++] = zero;
- }
+ start = builder_->Start(static_cast<int>(sig_->parameter_count() + 1));
+ // Initialize local variables.
+ uint32_t index = 0;
+ while (index < sig_->parameter_count()) {
+ ssa_env->locals[index] = builder_->Param(index, local_type_vec_[index]);
+ index++;
}
- // Initialize float32 locals.
- if (function_env_->local_f32_count > 0) {
- TFNode* zero = builder_->Float32Constant(0);
- for (uint32_t i = 0; i < function_env_->local_f32_count; i++) {
- ssa_env->locals[pos++] = zero;
+ while (index < local_type_vec_.size()) {
+ LocalType type = local_type_vec_[index];
+ TFNode* node = DefaultValue(type);
+ while (index < local_type_vec_.size() &&
+ local_type_vec_[index] == type) {
+ // Do a whole run of like-typed locals at a time.
+ ssa_env->locals[index++] = node;
}
}
- // Initialize float64 locals.
- if (function_env_->local_f64_count > 0) {
- TFNode* zero = builder_->Float64Constant(0);
- for (uint32_t i = 0; i < function_env_->local_f64_count; i++) {
- ssa_env->locals[pos++] = zero;
- }
- }
- DCHECK_EQ(function_env_->total_locals, pos);
- DCHECK_EQ(EnvironmentCount(), pos);
- builder_->set_module(function_env_->module);
+ builder_->set_module(module_);
}
ssa_env->control = start;
ssa_env->effect = start;
SetEnv("initial", ssa_env);
}
+ TFNode* DefaultValue(LocalType type) {
+ switch (type) {
+ case kAstI32:
+ return builder_->Int32Constant(0);
+ case kAstI64:
+ return builder_->Int64Constant(0);
+ case kAstF32:
+ return builder_->Float32Constant(0);
+ case kAstF64:
+ return builder_->Float64Constant(0);
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ }
+
void Leaf(LocalType type, TFNode* node = nullptr) {
size_t size = sizeof(Tree);
Tree* tree = reinterpret_cast<Tree*>(zone_->New(size));
@@ -561,6 +566,45 @@ class LR_WasmDecoder : public WasmDecoder {
return bytes;
}
+ // Decodes the locals declarations, if any, populating {local_type_vec_}.
+ void DecodeLocalDecls() {
+ DCHECK_EQ(0, local_type_vec_.size());
+ // Initialize {local_type_vec} from signature.
+ if (sig_) {
+ local_type_vec_.reserve(sig_->parameter_count());
+ for (size_t i = 0; i < sig_->parameter_count(); i++) {
+ local_type_vec_.push_back(sig_->GetParam(i));
+ }
+ }
+ // Decode local declarations, if any.
+ int length;
+ uint32_t entries = consume_u32v(&length, "local decls count");
+ while (entries-- > 0 && pc_ < limit_) {
+ uint32_t count = consume_u32v(&length, "local count");
+ byte code = consume_u8("local type");
+ LocalType type;
+ switch (code) {
+ case kLocalI32:
+ type = kAstI32;
+ break;
+ case kLocalI64:
+ type = kAstI64;
+ break;
+ case kLocalF32:
+ type = kAstF32;
+ break;
+ case kLocalF64:
+ type = kAstF64;
+ break;
+ default:
+ error(pc_ - 1, "invalid local type");
+ return;
+ }
+ local_type_vec_.insert(local_type_vec_.end(), count, type);
+ }
+ total_locals_ = local_type_vec_.size();
+ }
+
// Decodes the body of a function, producing reduced trees into {result}.
void DecodeFunctionBody() {
TRACE("wasm-decode %p...%p (%d bytes) %s\n",
@@ -621,7 +665,7 @@ class LR_WasmDecoder : public WasmDecoder {
PushBlock(break_env);
SsaEnv* cont_env = Steal(break_env);
// The continue environment is the inner environment.
- PrepareForLoop(cont_env);
+ PrepareForLoop(pc_, cont_env);
SetEnv("loop:start", Split(cont_env));
if (ssa_env_->go()) ssa_env_->state = SsaEnv::kReached;
PushBlock(cont_env);
@@ -655,16 +699,16 @@ class LR_WasmDecoder : public WasmDecoder {
len = 1 + operand.length;
break;
}
- case kExprTableSwitch: {
- TableSwitchOperand operand(this, pc_);
+ case kExprBrTable: {
+ BranchTableOperand operand(this, pc_);
if (Validate(pc_, operand, blocks_.size())) {
- Shift(kAstEnd, 1 + operand.case_count);
+ Shift(kAstEnd, 1);
}
len = 1 + operand.length;
break;
}
case kExprReturn: {
- int count = static_cast<int>(function_env_->sig->return_count());
+ int count = static_cast<int>(sig_->return_count());
if (count == 0) {
BUILD(Return, 0, builder_->Buffer(0));
ssa_env_->Kill();
@@ -821,6 +865,7 @@ class LR_WasmDecoder : public WasmDecoder {
len = 1 + operand.length;
break;
}
+ case kExprDeclLocals:
default:
error("Invalid opcode");
return;
@@ -853,7 +898,7 @@ class LR_WasmDecoder : public WasmDecoder {
}
void AddImplicitReturnAtEnd() {
- int retcount = static_cast<int>(function_env_->sig->return_count());
+ int retcount = static_cast<int>(sig_->return_count());
if (retcount == 0) {
BUILD0(ReturnVoid);
return;
@@ -872,7 +917,7 @@ class LR_WasmDecoder : public WasmDecoder {
for (int index = 0; index < retcount; index++) {
Tree* tree = trees_[trees_.size() - 1 - index];
if (buffer) buffer[index] = tree->node;
- LocalType expected = function_env_->sig->GetReturn(index);
+ LocalType expected = sig_->GetReturn(index);
if (tree->type != expected) {
error(limit_, tree->pc,
"ImplicitReturn[%d] expected type %s, found %s of type %s", index,
@@ -1043,73 +1088,42 @@ class LR_WasmDecoder : public WasmDecoder {
}
break;
}
- case kExprTableSwitch: {
+ case kExprBrTable: {
if (p->index == 1) {
// Switch key finished.
TypeCheckLast(p, kAstI32);
if (failed()) break;
- TableSwitchOperand operand(this, p->pc());
+ BranchTableOperand operand(this, p->pc());
DCHECK(Validate(p->pc(), operand, blocks_.size()));
- // Build the switch only if it has more than just a default target.
- bool build_switch = operand.table_count > 1;
+ // Build a switch only if it has more than just a default target.
+ bool build_switch = operand.table_count > 0;
TFNode* sw = nullptr;
- if (build_switch)
- sw = BUILD(Switch, operand.table_count, p->last()->node);
-
- // Allocate environments for each case.
- SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(operand.case_count);
- for (uint32_t i = 0; i < operand.case_count; i++) {
- case_envs[i] = UnreachableEnv();
+ if (build_switch) {
+ sw = BUILD(Switch, operand.table_count + 1, p->last()->node);
}
- ifs_.push_back({nullptr, nullptr, case_envs});
- SsaEnv* break_env = ssa_env_;
- PushBlock(break_env);
- SsaEnv* copy = Steal(break_env);
- ssa_env_ = copy;
-
- // Build the environments for each case based on the table.
- for (uint32_t i = 0; i < operand.table_count; i++) {
- uint16_t target = operand.read_entry(this, i);
+ // Process the targets of the break table.
+ SsaEnv* prev = ssa_env_;
+ SsaEnv* copy = Steal(prev);
+ for (uint32_t i = 0; i < operand.table_count + 1; i++) {
+ uint32_t target = operand.read_entry(this, i);
SsaEnv* env = copy;
if (build_switch) {
- env = Split(env);
- env->control = (i == operand.table_count - 1)
- ? BUILD(IfDefault, sw)
- : BUILD(IfValue, i, sw);
- }
- if (target >= 0x8000) {
- // Targets an outer block.
- int depth = target - 0x8000;
- SsaEnv* tenv = blocks_[blocks_.size() - depth - 1].ssa_env;
- Goto(env, tenv);
- } else {
- // Targets a case.
- Goto(env, case_envs[target]);
+ ssa_env_ = env = Split(env);
+ env->control = i == operand.table_count ? BUILD(IfDefault, sw)
+ : BUILD(IfValue, i, sw);
}
+ SsaEnv* tenv = blocks_[blocks_.size() - target - 1].ssa_env;
+ Goto(env, tenv);
}
- }
-
- if (p->done()) {
- // Last case. Fall through to the end.
- Block* block = &blocks_.back();
- if (p->index > 1) ReduceBreakToExprBlock(p, block);
- SsaEnv* next = block->ssa_env;
- blocks_.pop_back();
- ifs_.pop_back();
- SetEnv("switch:end", next);
- } else {
- // Interior case. Maybe fall through to the next case.
- SsaEnv* next = ifs_.back().case_envs[p->index - 1];
- if (p->index > 1 && ssa_env_->go()) Goto(ssa_env_, next);
- SetEnv("switch:case", next);
+ ssa_env_ = prev;
}
break;
}
case kExprReturn: {
- TypeCheckLast(p, function_env_->sig->GetReturn(p->index - 1));
+ TypeCheckLast(p, sig_->GetReturn(p->index - 1));
if (p->done()) {
if (build()) {
int count = p->tree->count;
@@ -1346,6 +1360,7 @@ class LR_WasmDecoder : public WasmDecoder {
}
void SetEnv(const char* reason, SsaEnv* env) {
+#if DEBUG
TRACE(" env = %p, block depth = %d, reason = %s", static_cast<void*>(env),
static_cast<int>(blocks_.size()), reason);
if (FLAG_trace_wasm_decoder && env && env->control) {
@@ -1353,6 +1368,7 @@ class LR_WasmDecoder : public WasmDecoder {
compiler::WasmGraphBuilder::PrintDebugName(env->control);
}
TRACE("\n");
+#endif
ssa_env_ = env;
if (builder_) {
builder_->set_control_ptr(&env->control);
@@ -1389,8 +1405,7 @@ class LR_WasmDecoder : public WasmDecoder {
TFNode* b = from->locals[i];
if (a != b) {
TFNode* vals[] = {a, b};
- to->locals[i] =
- builder_->Phi(function_env_->GetLocalType(i), 2, vals, merge);
+ to->locals[i] = builder_->Phi(local_type_vec_[i], 2, vals, merge);
}
}
break;
@@ -1425,8 +1440,8 @@ class LR_WasmDecoder : public WasmDecoder {
vals[j] = tnode;
}
vals[count - 1] = fnode;
- to->locals[i] = builder_->Phi(function_env_->GetLocalType(i), count,
- vals, merge);
+ to->locals[i] =
+ builder_->Phi(local_type_vec_[i], count, vals, merge);
}
}
break;
@@ -1451,29 +1466,32 @@ class LR_WasmDecoder : public WasmDecoder {
return tnode;
}
- void BuildInfiniteLoop() {
- if (ssa_env_->go()) {
- PrepareForLoop(ssa_env_);
- SsaEnv* cont_env = ssa_env_;
- ssa_env_ = Split(ssa_env_);
- ssa_env_->state = SsaEnv::kReached;
- Goto(ssa_env_, cont_env);
- }
- }
-
- void PrepareForLoop(SsaEnv* env) {
- if (env->go()) {
- env->state = SsaEnv::kMerged;
- if (builder_) {
- env->control = builder_->Loop(env->control);
- env->effect = builder_->EffectPhi(1, &env->effect, env->control);
- builder_->Terminate(env->effect, env->control);
+ void PrepareForLoop(const byte* pc, SsaEnv* env) {
+ if (!env->go()) return;
+ env->state = SsaEnv::kMerged;
+ if (!builder_) return;
+
+ env->control = builder_->Loop(env->control);
+ env->effect = builder_->EffectPhi(1, &env->effect, env->control);
+ builder_->Terminate(env->effect, env->control);
+ if (FLAG_wasm_loop_assignment_analysis) {
+ BitVector* assigned = AnalyzeLoopAssignment(pc);
+ if (assigned != nullptr) {
+ // Only introduce phis for variables assigned in this loop.
for (int i = EnvironmentCount() - 1; i >= 0; i--) {
- env->locals[i] = builder_->Phi(function_env_->GetLocalType(i), 1,
- &env->locals[i], env->control);
+ if (!assigned->Contains(i)) continue;
+ env->locals[i] = builder_->Phi(local_type_vec_[i], 1, &env->locals[i],
+ env->control);
}
+ return;
}
}
+
+ // Conservatively introduce phis for all local variables.
+ for (int i = EnvironmentCount() - 1; i >= 0; i--) {
+ env->locals[i] =
+ builder_->Phi(local_type_vec_[i], 1, &env->locals[i], env->control);
+ }
}
// Create a complete copy of the {from}.
@@ -1524,7 +1542,7 @@ class LR_WasmDecoder : public WasmDecoder {
}
int EnvironmentCount() {
- if (builder_) return static_cast<int>(function_env_->GetLocalCount());
+ if (builder_) return static_cast<int>(local_type_vec_.size());
return 0; // if we aren't building a graph, don't bother with SSA renaming.
}
@@ -1560,23 +1578,84 @@ class LR_WasmDecoder : public WasmDecoder {
PrintProduction(depth + 1);
}
#endif
+
+ BitVector* AnalyzeLoopAssignment(const byte* pc) {
+ if (pc >= limit_) return nullptr;
+ if (*pc != kExprLoop) return nullptr;
+
+ BitVector* assigned =
+ new (zone_) BitVector(static_cast<int>(total_locals_), zone_);
+ // Keep a stack to model the nesting of expressions.
+ std::vector<int> arity_stack;
+ arity_stack.push_back(OpcodeArity(pc));
+ pc += OpcodeLength(pc);
+
+ // Iteratively process all AST nodes nested inside the loop.
+ while (pc < limit_) {
+ WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
+ int arity = 0;
+ int length = 1;
+ int assigned_index = -1;
+ if (opcode == kExprSetLocal) {
+ LocalIndexOperand operand(this, pc);
+ if (assigned->length() > 0 &&
+ static_cast<int>(operand.index) < assigned->length()) {
+ // Unverified code might have an out-of-bounds index.
+ // Ignore out-of-bounds indices, as the main verification will fail.
+ assigned->Add(operand.index);
+ assigned_index = operand.index;
+ }
+ arity = 1;
+ length = 1 + operand.length;
+ } else {
+ arity = OpcodeArity(pc);
+ length = OpcodeLength(pc);
+ }
+
+ TRACE("loop-assign module+%-6d %s func+%d: 0x%02x %s", baserel(pc),
+ indentation(), startrel(pc), opcode,
+ WasmOpcodes::OpcodeName(opcode));
+
+ if (assigned_index >= 0) {
+ TRACE(" (assigned local #%d)\n", assigned_index);
+ } else {
+ TRACE("\n");
+ }
+
+ pc += length;
+ arity_stack.push_back(arity);
+ while (arity_stack.back() == 0) {
+ arity_stack.pop_back();
+ if (arity_stack.empty()) return assigned; // reached end of loop
+ arity_stack.back()--;
+ }
+ }
+ return assigned;
+ }
};
+bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start,
+ const byte* end) {
+ base::AccountingAllocator allocator;
+ Zone tmp(&allocator);
+ FunctionBody body = {nullptr, nullptr, nullptr, start, end};
+ SR_WasmDecoder decoder(&tmp, nullptr, body);
+ return decoder.DecodeLocalDecls(decls);
+}
-TreeResult VerifyWasmCode(FunctionEnv* env, const byte* base, const byte* start,
- const byte* end) {
- Zone zone;
- LR_WasmDecoder decoder(&zone, nullptr);
- TreeResult result = decoder.Decode(env, base, start, end);
+TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
+ FunctionBody& body) {
+ Zone zone(allocator);
+ SR_WasmDecoder decoder(&zone, nullptr, body);
+ TreeResult result = decoder.Decode();
return result;
}
-
-TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env, const byte* base,
- const byte* start, const byte* end) {
- Zone zone;
- LR_WasmDecoder decoder(&zone, builder);
- TreeResult result = decoder.Decode(env, base, start, end);
+TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
+ TFBuilder* builder, FunctionBody& body) {
+ Zone zone(allocator);
+ SR_WasmDecoder decoder(&zone, builder, body);
+ TreeResult result = decoder.Decode();
return result;
}
@@ -1608,20 +1687,49 @@ ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte* pc,
}
int OpcodeLength(const byte* pc, const byte* end) {
- WasmDecoder decoder(nullptr, pc, end);
+ WasmDecoder decoder(nullptr, nullptr, pc, end);
return decoder.OpcodeLength(pc);
}
-int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end) {
- WasmDecoder decoder(env, pc, end);
+int OpcodeArity(ModuleEnv* module, FunctionSig* sig, const byte* pc,
+ const byte* end) {
+ WasmDecoder decoder(module, sig, pc, end);
return decoder.OpcodeArity(pc);
}
-void PrintAst(FunctionEnv* env, const byte* start, const byte* end) {
- WasmDecoder decoder(env, start, end);
- const byte* pc = start;
+void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
+ Zone zone(allocator);
+ SR_WasmDecoder decoder(&zone, nullptr, body);
+
+ OFStream os(stdout);
+
+ // Print the function signature.
+ if (body.sig) {
+ os << "// signature: " << *body.sig << std::endl;
+ }
+
+ // Print the local declarations.
+ AstLocalDecls decls(&zone);
+ decoder.DecodeLocalDecls(decls);
+ const byte* pc = decoder.pc();
+ if (body.start != decoder.pc()) {
+ printf("// locals:");
+ for (auto p : decls.local_types) {
+ LocalType type = p.first;
+ uint32_t count = p.second;
+ os << " " << count << " " << WasmOpcodes::TypeName(type);
+ }
+ os << std::endl;
+
+ for (const byte* locals = body.start; locals < pc; locals++) {
+ printf(" 0x%02x,", *locals);
+ }
+ printf("\n");
+ }
+
+ printf("// body: \n");
std::vector<int> arity_stack;
- while (pc < end) {
+ while (pc < body.end) {
int arity = decoder.OpcodeArity(pc);
size_t length = decoder.OpcodeLength(pc);
@@ -1636,6 +1744,35 @@ void PrintAst(FunctionEnv* env, const byte* start, const byte* end) {
for (size_t i = 1; i < length; i++) {
printf(" 0x%02x,", pc[i]);
}
+
+ if (body.module) {
+ switch (opcode) {
+ case kExprCallIndirect: {
+ SignatureIndexOperand operand(&decoder, pc);
+ if (decoder.Validate(pc, operand)) {
+ os << " // sig #" << operand.index << ": " << *operand.sig;
+ }
+ break;
+ }
+ case kExprCallImport: {
+ ImportIndexOperand operand(&decoder, pc);
+ if (decoder.Validate(pc, operand)) {
+ os << " // import #" << operand.index << ": " << *operand.sig;
+ }
+ break;
+ }
+ case kExprCallFunction: {
+ FunctionIndexOperand operand(&decoder, pc);
+ if (decoder.Validate(pc, operand)) {
+ os << " // function #" << operand.index << ": " << *operand.sig;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
pc += length;
printf("\n");
@@ -1648,65 +1785,11 @@ void PrintAst(FunctionEnv* env, const byte* start, const byte* end) {
}
}
-// Analyzes loop bodies for static assignments to locals, which helps in
-// reducing the number of phis introduced at loop headers.
-class LoopAssignmentAnalyzer : public WasmDecoder {
- public:
- LoopAssignmentAnalyzer(Zone* zone, FunctionEnv* function_env) : zone_(zone) {
- function_env_ = function_env;
- }
-
- BitVector* Analyze(const byte* pc, const byte* limit) {
- Decoder::Reset(pc, limit);
- if (pc_ >= limit_) return nullptr;
- if (*pc_ != kExprLoop) return nullptr;
-
- BitVector* assigned =
- new (zone_) BitVector(function_env_->total_locals, zone_);
- // Keep a stack to model the nesting of expressions.
- std::vector<int> arity_stack;
- arity_stack.push_back(OpcodeArity(pc_));
- pc_ += OpcodeLength(pc_);
-
- // Iteratively process all AST nodes nested inside the loop.
- while (pc_ < limit_) {
- WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
- int arity = 0;
- int length = 1;
- if (opcode == kExprSetLocal) {
- LocalIndexOperand operand(this, pc_);
- if (assigned->length() > 0 &&
- static_cast<int>(operand.index) < assigned->length()) {
- // Unverified code might have an out-of-bounds index.
- assigned->Add(operand.index);
- }
- arity = 1;
- length = 1 + operand.length;
- } else {
- arity = OpcodeArity(pc_);
- length = OpcodeLength(pc_);
- }
-
- pc_ += length;
- arity_stack.push_back(arity);
- while (arity_stack.back() == 0) {
- arity_stack.pop_back();
- if (arity_stack.empty()) return assigned; // reached end of loop
- arity_stack.back()--;
- }
- }
- return assigned;
- }
-
- private:
- Zone* zone_;
-};
-
-
-BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
+BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
const byte* start, const byte* end) {
- LoopAssignmentAnalyzer analyzer(zone, env);
- return analyzer.Analyze(start, end);
+ FunctionBody body = {nullptr, nullptr, nullptr, start, end};
+ SR_WasmDecoder decoder(zone, nullptr, body);
+ return decoder.AnalyzeLoopAssignmentForTesting(start, num_locals);
}
} // namespace wasm
diff --git a/deps/v8/src/wasm/ast-decoder.h b/deps/v8/src/wasm/ast-decoder.h
index 465bacaab8..5376e7bfdd 100644
--- a/deps/v8/src/wasm/ast-decoder.h
+++ b/deps/v8/src/wasm/ast-decoder.h
@@ -46,8 +46,7 @@ struct ImmI32Operand {
int32_t value;
int length;
inline ImmI32Operand(Decoder* decoder, const byte* pc) {
- value = bit_cast<int32_t>(decoder->checked_read_u32(pc, 1, "immi32"));
- length = 4;
+ value = decoder->checked_read_i32v(pc, 1, &length, "immi32");
}
};
@@ -55,8 +54,7 @@ struct ImmI64Operand {
int64_t value;
int length;
inline ImmI64Operand(Decoder* decoder, const byte* pc) {
- value = bit_cast<int64_t>(decoder->checked_read_u64(pc, 1, "immi64"));
- length = 8;
+ value = decoder->checked_read_i64v(pc, 1, &length, "immi64");
}
};
@@ -97,8 +95,7 @@ struct BreakDepthOperand {
Block* target;
int length;
inline BreakDepthOperand(Decoder* decoder, const byte* pc) {
- depth = decoder->checked_read_u8(pc, 1, "break depth");
- length = 1;
+ depth = decoder->checked_read_u32v(pc, 1, &length, "break depth");
target = nullptr;
}
};
@@ -107,8 +104,7 @@ struct BlockCountOperand {
uint32_t count;
int length;
inline BlockCountOperand(Decoder* decoder, const byte* pc) {
- count = decoder->checked_read_u8(pc, 1, "block count");
- length = 1;
+ count = decoder->checked_read_u32v(pc, 1, &length, "block count");
}
};
@@ -142,103 +138,55 @@ struct ImportIndexOperand {
}
};
-struct TableSwitchOperand {
- uint32_t case_count;
+struct BranchTableOperand {
uint32_t table_count;
const byte* table;
int length;
- inline TableSwitchOperand(Decoder* decoder, const byte* pc) {
- case_count = decoder->checked_read_u16(pc, 1, "expected #cases");
- table_count = decoder->checked_read_u16(pc, 3, "expected #entries");
- length = 4 + table_count * 2;
-
- if (decoder->check(pc, 5, table_count * 2, "expected <table entries>")) {
- table = pc + 5;
+ inline BranchTableOperand(Decoder* decoder, const byte* pc) {
+ int varint_length;
+ table_count =
+ decoder->checked_read_u32v(pc, 1, &varint_length, "expected #entries");
+ length = varint_length + (table_count + 1) * sizeof(uint32_t);
+
+ uint32_t table_start = 1 + varint_length;
+ if (decoder->check(pc, table_start, (table_count + 1) * sizeof(uint32_t),
+ "expected <table entries>")) {
+ table = pc + table_start;
} else {
table = nullptr;
}
}
- inline uint16_t read_entry(Decoder* decoder, int i) {
- DCHECK(i >= 0 && static_cast<uint32_t>(i) < table_count);
- return table ? decoder->read_u16(table + i * sizeof(uint16_t)) : 0;
+ inline uint32_t read_entry(Decoder* decoder, int i) {
+ DCHECK(i >= 0 && static_cast<uint32_t>(i) <= table_count);
+ return table ? decoder->read_u32(table + i * sizeof(uint32_t)) : 0;
}
};
struct MemoryAccessOperand {
- bool aligned;
+ uint32_t alignment;
uint32_t offset;
int length;
inline MemoryAccessOperand(Decoder* decoder, const byte* pc) {
- byte bitfield = decoder->checked_read_u8(pc, 1, "memory access byte");
- aligned = MemoryAccess::AlignmentField::decode(bitfield);
- if (MemoryAccess::OffsetField::decode(bitfield)) {
- offset = decoder->checked_read_u32v(pc, 2, &length, "memory offset");
- length++;
- } else {
- offset = 0;
- length = 1;
- }
+ int alignment_length;
+ alignment =
+ decoder->checked_read_u32v(pc, 1, &alignment_length, "alignment");
+ int offset_length;
+ offset = decoder->checked_read_u32v(pc, 1 + alignment_length,
+ &offset_length, "offset");
+ length = alignment_length + offset_length;
}
};
typedef compiler::WasmGraphBuilder TFBuilder;
struct ModuleEnv; // forward declaration of module interface.
-// Interface the function environment during decoding, include the signature
-// and number of locals.
-struct FunctionEnv {
- ModuleEnv* module; // module environment
- FunctionSig* sig; // signature of this function
- uint32_t local_i32_count; // number of int32 locals
- uint32_t local_i64_count; // number of int64 locals
- uint32_t local_f32_count; // number of float32 locals
- uint32_t local_f64_count; // number of float64 locals
- uint32_t total_locals; // sum of parameters and all locals
-
- uint32_t GetLocalCount() { return total_locals; }
- LocalType GetLocalType(uint32_t index) {
- if (index < static_cast<uint32_t>(sig->parameter_count())) {
- return sig->GetParam(index);
- }
- index -= static_cast<uint32_t>(sig->parameter_count());
- if (index < local_i32_count) return kAstI32;
- index -= local_i32_count;
- if (index < local_i64_count) return kAstI64;
- index -= local_i64_count;
- if (index < local_f32_count) return kAstF32;
- index -= local_f32_count;
- if (index < local_f64_count) return kAstF64;
- return kAstStmt;
- }
-
- void AddLocals(LocalType type, uint32_t count) {
- switch (type) {
- case kAstI32:
- local_i32_count += count;
- break;
- case kAstI64:
- local_i64_count += count;
- break;
- case kAstF32:
- local_f32_count += count;
- break;
- case kAstF64:
- local_f64_count += count;
- break;
- default:
- UNREACHABLE();
- }
- total_locals += count;
- DCHECK_EQ(total_locals,
- (sig->parameter_count() + local_i32_count + local_i64_count +
- local_f32_count + local_f64_count));
- }
-
- void SumLocals() {
- total_locals = static_cast<uint32_t>(sig->parameter_count()) +
- local_i32_count + local_i64_count + local_f32_count +
- local_f64_count;
- }
+// All of the various data structures necessary to decode a function body.
+struct FunctionBody {
+ ModuleEnv* module; // module environment
+ FunctionSig* sig; // function signature
+ const byte* base; // base of the module bytes, for error reporting
+ const byte* start; // start of the function body
+ const byte* end; // end of the function body
};
struct Tree;
@@ -246,21 +194,25 @@ typedef Result<Tree*> TreeResult;
std::ostream& operator<<(std::ostream& os, const Tree& tree);
-TreeResult VerifyWasmCode(FunctionEnv* env, const byte* base, const byte* start,
- const byte* end);
-TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env, const byte* base,
- const byte* start, const byte* end);
-
-void PrintAst(FunctionEnv* env, const byte* start, const byte* end);
-
-inline TreeResult VerifyWasmCode(FunctionEnv* env, const byte* start,
- const byte* end) {
- return VerifyWasmCode(env, nullptr, start, end);
+TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
+ FunctionBody& body);
+TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
+ TFBuilder* builder, FunctionBody& body);
+void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body);
+
+inline TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
+ ModuleEnv* module, FunctionSig* sig,
+ const byte* start, const byte* end) {
+ FunctionBody body = {module, sig, nullptr, start, end};
+ return VerifyWasmCode(allocator, body);
}
-inline TreeResult BuildTFGraph(TFBuilder* builder, FunctionEnv* env,
- const byte* start, const byte* end) {
- return BuildTFGraph(builder, env, nullptr, start, end);
+inline TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
+ TFBuilder* builder, ModuleEnv* module,
+ FunctionSig* sig, const byte* start,
+ const byte* end) {
+ FunctionBody body = {module, sig, nullptr, start, end};
+ return BuildTFGraph(allocator, builder, body);
}
enum ReadUnsignedLEB128ErrorCode { kNoError, kInvalidLEB128, kMissingLEB128 };
@@ -268,14 +220,31 @@ enum ReadUnsignedLEB128ErrorCode { kNoError, kInvalidLEB128, kMissingLEB128 };
ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte*, const byte*,
int*, uint32_t*);
-BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
+struct AstLocalDecls {
+ // The size of the encoded declarations.
+ uint32_t decls_encoded_size; // size of encoded declarations
+
+ // Total number of locals.
+ uint32_t total_local_count;
+
+ // List of {local type, count} pairs.
+ ZoneVector<std::pair<LocalType, uint32_t>> local_types;
+
+ // Constructor initializes the vector.
+ explicit AstLocalDecls(Zone* zone)
+ : decls_encoded_size(0), total_local_count(0), local_types(zone) {}
+};
+
+bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start, const byte* end);
+BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
const byte* start, const byte* end);
// Computes the length of the opcode at the given address.
int OpcodeLength(const byte* pc, const byte* end);
// Computes the arity (number of sub-nodes) of the opcode at the given address.
-int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end);
+int OpcodeArity(ModuleEnv* module, FunctionSig* sig, const byte* pc,
+ const byte* end);
} // namespace wasm
} // namespace internal
} // namespace v8
diff --git a/deps/v8/src/wasm/decoder.h b/deps/v8/src/wasm/decoder.h
index 0e88eda022..f9de2e1143 100644
--- a/deps/v8/src/wasm/decoder.h
+++ b/deps/v8/src/wasm/decoder.h
@@ -77,33 +77,44 @@ class Decoder {
return check(base, offset, 8, msg) ? read_u64(base + offset) : 0;
}
+ // Reads a variable-length unsigned integer (little endian).
uint32_t checked_read_u32v(const byte* base, int offset, int* length,
- const char* msg = "expected LEB128") {
- if (!check(base, offset, 1, msg)) {
- *length = 0;
- return 0;
- }
+ const char* msg = "expected LEB32") {
+ return checked_read_leb<uint32_t, false>(base, offset, length, msg);
+ }
- const ptrdiff_t kMaxDiff = 5; // maximum 5 bytes.
- const byte* ptr = base + offset;
- const byte* end = ptr + kMaxDiff;
- if (end > limit_) end = limit_;
- int shift = 0;
- byte b = 0;
- uint32_t result = 0;
- while (ptr < end) {
- b = *ptr++;
- result = result | ((b & 0x7F) << shift);
- if ((b & 0x80) == 0) break;
- shift += 7;
+ // Reads a variable-length signed integer (little endian).
+ int32_t checked_read_i32v(const byte* base, int offset, int* length,
+ const char* msg = "expected SLEB32") {
+ uint32_t result =
+ checked_read_leb<uint32_t, true>(base, offset, length, msg);
+ if (*length == 5) return bit_cast<int32_t>(result);
+ if (*length > 0) {
+ int shift = 32 - 7 * *length;
+ // Perform sign extension.
+ return bit_cast<int32_t>(result << shift) >> shift;
}
- DCHECK_LE(ptr - (base + offset), kMaxDiff);
- *length = static_cast<int>(ptr - (base + offset));
- if (ptr == end && (b & 0x80)) {
- error(base, ptr, msg);
- return 0;
+ return 0;
+ }
+
+ // Reads a variable-length unsigned integer (little endian).
+ uint64_t checked_read_u64v(const byte* base, int offset, int* length,
+ const char* msg = "expected LEB64") {
+ return checked_read_leb<uint64_t, false>(base, offset, length, msg);
+ }
+
+ // Reads a variable-length signed integer (little endian).
+ int64_t checked_read_i64v(const byte* base, int offset, int* length,
+ const char* msg = "expected SLEB64") {
+ uint64_t result =
+ checked_read_leb<uint64_t, true>(base, offset, length, msg);
+ if (*length == 10) return bit_cast<int64_t>(result);
+ if (*length > 0) {
+ int shift = 64 - 7 * *length;
+ // Perform sign extension.
+ return bit_cast<int64_t>(result << shift) >> shift;
}
- return result;
+ return 0;
}
// Reads a single 16-bit unsigned integer (little endian).
@@ -214,6 +225,8 @@ class Decoder {
*length = static_cast<int>(pc_ - pos);
if (pc_ == end && (b & 0x80)) {
error(pc_ - 1, "varint too large");
+ } else if (*length == 0) {
+ error(pc_, "varint of length 0");
} else {
TRACE("= %u\n", result);
}
@@ -222,9 +235,22 @@ class Decoder {
return traceOffEnd<uint32_t>();
}
+ // Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
+ void consume_bytes(int size) {
+ if (checkAvailable(size)) {
+ pc_ += size;
+ } else {
+ pc_ = limit_;
+ }
+ }
+
// Check that at least {size} bytes exist between {pc_} and {limit_}.
bool checkAvailable(int size) {
- if (pc_ < start_ || (pc_ + size) > limit_) {
+ intptr_t pc_overflow_value = std::numeric_limits<intptr_t>::max() - size;
+ if (size < 0 || (intptr_t)pc_ > pc_overflow_value) {
+ error(pc_, nullptr, "reading %d bytes would underflow/overflow", size);
+ return false;
+ } else if (pc_ < start_ || limit_ < (pc_ + size)) {
error(pc_, nullptr, "expected %d bytes, fell off end", size);
return false;
} else {
@@ -232,12 +258,6 @@ class Decoder {
}
}
- bool RangeOk(const byte* pc, int length) {
- if (pc < start_ || pc_ >= limit_) return false;
- if ((pc + length) >= limit_) return false;
- return true;
- }
-
void error(const char* msg) { error(pc_, nullptr, msg); }
void error(const byte* pc, const char* msg) { error(pc, nullptr, msg); }
@@ -283,12 +303,13 @@ class Decoder {
Result<T> toResult(T val) {
Result<T> result;
if (error_pc_) {
+ TRACE("Result error: %s\n", error_msg_.get());
result.error_code = kError;
result.start = start_;
result.error_pc = error_pc_;
result.error_pt = error_pt_;
- result.error_msg = error_msg_;
- error_msg_.Reset(nullptr);
+ // transfer ownership of the error to the result.
+ result.error_msg.Reset(error_msg_.Detach());
} else {
result.error_code = kSuccess;
}
@@ -308,7 +329,12 @@ class Decoder {
}
bool ok() const { return error_pc_ == nullptr; }
- bool failed() const { return error_pc_ != nullptr; }
+ bool failed() const { return !error_msg_.is_empty(); }
+ bool more() const { return pc_ < limit_; }
+
+ const byte* start() { return start_; }
+ const byte* pc() { return pc_; }
+ uint32_t pc_offset() { return static_cast<uint32_t>(pc_ - start_); }
protected:
const byte* start_;
@@ -318,6 +344,60 @@ class Decoder {
const byte* error_pc_;
const byte* error_pt_;
base::SmartArrayPointer<char> error_msg_;
+
+ private:
+ template <typename IntType, bool is_signed>
+ IntType checked_read_leb(const byte* base, int offset, int* length,
+ const char* msg) {
+ if (!check(base, offset, 1, msg)) {
+ *length = 0;
+ return 0;
+ }
+
+ const int kMaxLength = (sizeof(IntType) * 8 + 6) / 7;
+ const byte* ptr = base + offset;
+ const byte* end = ptr + kMaxLength;
+ if (end > limit_) end = limit_;
+ int shift = 0;
+ byte b = 0;
+ IntType result = 0;
+ while (ptr < end) {
+ b = *ptr++;
+ result = result | (static_cast<IntType>(b & 0x7F) << shift);
+ if ((b & 0x80) == 0) break;
+ shift += 7;
+ }
+ DCHECK_LE(ptr - (base + offset), kMaxLength);
+ *length = static_cast<int>(ptr - (base + offset));
+ if (ptr == end) {
+ // Check there are no bits set beyond the bitwidth of {IntType}.
+ const int kExtraBits = (1 + kMaxLength * 7) - (sizeof(IntType) * 8);
+ const byte kExtraBitsMask =
+ static_cast<byte>((0xFF << (8 - kExtraBits)) & 0xFF);
+ int extra_bits_value;
+ if (is_signed) {
+ // A signed-LEB128 must sign-extend the final byte, excluding its
+ // most-signifcant bit. e.g. for a 32-bit LEB128:
+ // kExtraBits = 4
+ // kExtraBitsMask = 0xf0
+ // If b is 0x0f, the value is negative, so extra_bits_value is 0x70.
+ // If b is 0x03, the value is positive, so extra_bits_value is 0x00.
+ extra_bits_value = (static_cast<int8_t>(b << kExtraBits) >> 8) &
+ kExtraBitsMask & ~0x80;
+ } else {
+ extra_bits_value = 0;
+ }
+ if (*length == kMaxLength && (b & kExtraBitsMask) != extra_bits_value) {
+ error(base, ptr, "extra bits in varint");
+ return 0;
+ }
+ if ((b & 0x80) != 0) {
+ error(base, ptr, msg);
+ return 0;
+ }
+ }
+ return result;
+ }
};
#undef TRACE
diff --git a/deps/v8/src/wasm/encoder.cc b/deps/v8/src/wasm/encoder.cc
index d80a275338..92e6b1145c 100644
--- a/deps/v8/src/wasm/encoder.cc
+++ b/deps/v8/src/wasm/encoder.cc
@@ -10,11 +10,21 @@
#include "src/wasm/ast-decoder.h"
#include "src/wasm/encoder.h"
+#include "src/wasm/wasm-macro-gen.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/v8memory.h"
+#if DEBUG
+#define TRACE(...) \
+ do { \
+ if (FLAG_trace_wasm_encoder) PrintF(__VA_ARGS__); \
+ } while (false)
+#else
+#define TRACE(...)
+#endif
+
namespace v8 {
namespace internal {
namespace wasm {
@@ -40,6 +50,11 @@ void EmitUint32(byte** b, uint32_t x) {
*b += 4;
}
+// Sections all start with a size, but it's unknown at the start.
+// We generate a large varint which we then fixup later when the size is known.
+//
+// TODO(jfb) Not strictly necessary since sizes are calculated ahead of time.
+const size_t padded_varint = 5;
void EmitVarInt(byte** b, size_t val) {
while (true) {
@@ -54,8 +69,47 @@ void EmitVarInt(byte** b, size_t val) {
}
}
}
-} // namespace
+size_t SizeOfVarInt(size_t value) {
+ size_t size = 0;
+ do {
+ size++;
+ value = value >> 7;
+ } while (value > 0);
+ return size;
+}
+
+void FixupSection(byte* start, byte* end) {
+ // Same as EmitVarInt, but fixed-width with zeroes in the MSBs.
+ size_t val = end - start - padded_varint;
+ TRACE(" fixup %u\n", (unsigned)val);
+ for (size_t pos = 0; pos != padded_varint; ++pos) {
+ size_t next = val >> 7;
+ byte out = static_cast<byte>(val & 0x7f);
+ if (pos != padded_varint - 1) {
+ *(start++) = 0x80 | out;
+ val = next;
+ } else {
+ *(start++) = out;
+ // TODO(jfb) check that the pre-allocated fixup size isn't overflowed.
+ }
+ }
+}
+
+// Returns the start of the section, where the section VarInt size is.
+byte* EmitSection(WasmSection::Code code, byte** b) {
+ byte* start = *b;
+ const char* name = WasmSection::getName(code);
+ size_t length = WasmSection::getNameLength(code);
+ TRACE("emit section: %s\n", name);
+ for (size_t padding = 0; padding != padded_varint; ++padding) {
+ EmitUint8(b, 0xff); // Will get fixed up later.
+ }
+ EmitVarInt(b, length); // Section name string size.
+ for (size_t i = 0; i != length; ++i) EmitUint8(b, name[i]);
+ return start;
+}
+} // namespace
struct WasmFunctionBuilder::Type {
bool param_;
@@ -120,16 +174,48 @@ void WasmFunctionBuilder::EmitWithU8(WasmOpcode opcode, const byte immediate) {
body_.push_back(immediate);
}
+void WasmFunctionBuilder::EmitWithU8U8(WasmOpcode opcode, const byte imm1,
+ const byte imm2) {
+ body_.push_back(static_cast<byte>(opcode));
+ body_.push_back(imm1);
+ body_.push_back(imm2);
+}
-uint32_t WasmFunctionBuilder::EmitEditableImmediate(const byte immediate) {
- body_.push_back(immediate);
+void WasmFunctionBuilder::EmitWithVarInt(WasmOpcode opcode,
+ uint32_t immediate) {
+ body_.push_back(static_cast<byte>(opcode));
+ size_t immediate_size = SizeOfVarInt(immediate);
+ body_.insert(body_.end(), immediate_size, 0);
+ byte* p = &body_[body_.size() - immediate_size];
+ EmitVarInt(&p, immediate);
+}
+
+uint32_t WasmFunctionBuilder::EmitEditableVarIntImmediate() {
+ // Guess that the immediate will be 1 byte. If it is more, we'll have to
+ // shift everything down.
+ body_.push_back(0);
return static_cast<uint32_t>(body_.size()) - 1;
}
+void WasmFunctionBuilder::EditVarIntImmediate(uint32_t offset,
+ const uint32_t immediate) {
+ uint32_t immediate_size = static_cast<uint32_t>(SizeOfVarInt(immediate));
+ // In EmitEditableVarIntImmediate, we guessed that we'd only need one byte.
+ // If we need more, shift everything down to make room for the larger
+ // immediate.
+ if (immediate_size > 1) {
+ uint32_t diff = immediate_size - 1;
+ body_.insert(body_.begin() + offset, diff, 0);
-void WasmFunctionBuilder::EditImmediate(uint32_t offset, const byte immediate) {
- DCHECK(offset < body_.size());
- body_[offset] = immediate;
+ for (size_t i = 0; i < local_indices_.size(); ++i) {
+ if (local_indices_[i] >= offset) {
+ local_indices_[i] += diff;
+ }
+ }
+ }
+ DCHECK(offset + immediate_size <= body_.size());
+ byte* p = &body_[offset];
+ EmitVarInt(&p, immediate);
}
@@ -144,7 +230,6 @@ void WasmFunctionBuilder::SetName(const unsigned char* name, int name_length) {
for (int i = 0; i < name_length; i++) {
name_.push_back(*(name + i));
}
- name_.push_back('\0');
}
}
@@ -250,15 +335,25 @@ WasmFunctionEncoder::WasmFunctionEncoder(Zone* zone, LocalType return_type,
uint32_t WasmFunctionEncoder::HeaderSize() const {
uint32_t size = 3;
- if (HasLocals()) size += 8;
if (!external_) size += 2;
- if (HasName()) size += 4;
+ if (HasName()) {
+ uint32_t name_size = NameSize();
+ size += static_cast<uint32_t>(SizeOfVarInt(name_size)) + name_size;
+ }
return size;
}
uint32_t WasmFunctionEncoder::BodySize(void) const {
- return external_ ? 0 : static_cast<uint32_t>(body_.size());
+ // TODO(titzer): embed a LocalDeclEncoder in the WasmFunctionEncoder
+ LocalDeclEncoder local_decl;
+ local_decl.AddLocals(local_i32_count_, kAstI32);
+ local_decl.AddLocals(local_i64_count_, kAstI64);
+ local_decl.AddLocals(local_f32_count_, kAstF32);
+ local_decl.AddLocals(local_f64_count_, kAstF64);
+
+ return external_ ? 0
+ : static_cast<uint32_t>(body_.size() + local_decl.Size());
}
@@ -271,28 +366,29 @@ void WasmFunctionEncoder::Serialize(byte* buffer, byte** header,
byte** body) const {
uint8_t decl_bits = (exported_ ? kDeclFunctionExport : 0) |
(external_ ? kDeclFunctionImport : 0) |
- (HasLocals() ? kDeclFunctionLocals : 0) |
(HasName() ? kDeclFunctionName : 0);
EmitUint8(header, decl_bits);
EmitUint16(header, signature_index_);
if (HasName()) {
- uint32_t name_offset = static_cast<uint32_t>(*body - buffer);
- EmitUint32(header, name_offset);
- std::memcpy(*body, &name_[0], name_.size());
- (*body) += name_.size();
+ EmitVarInt(header, NameSize());
+ for (size_t i = 0; i < name_.size(); ++i) {
+ EmitUint8(header, name_[i]);
+ }
}
- if (HasLocals()) {
- EmitUint16(header, local_i32_count_);
- EmitUint16(header, local_i64_count_);
- EmitUint16(header, local_f32_count_);
- EmitUint16(header, local_f64_count_);
- }
if (!external_) {
- EmitUint16(header, static_cast<uint16_t>(body_.size()));
+ // TODO(titzer): embed a LocalDeclEncoder in the WasmFunctionEncoder
+ LocalDeclEncoder local_decl;
+ local_decl.AddLocals(local_i32_count_, kAstI32);
+ local_decl.AddLocals(local_i64_count_, kAstI64);
+ local_decl.AddLocals(local_f32_count_, kAstF32);
+ local_decl.AddLocals(local_f64_count_, kAstF64);
+
+ EmitUint16(header, static_cast<uint16_t>(body_.size() + local_decl.Size()));
+ (*header) += local_decl.Emit(*header);
if (body_.size() > 0) {
std::memcpy(*header, &body_[0], body_.size());
(*header) += body_.size();
@@ -323,17 +419,13 @@ uint32_t WasmDataSegmentEncoder::BodySize() const {
void WasmDataSegmentEncoder::Serialize(byte* buffer, byte** header,
byte** body) const {
- uint32_t body_offset = static_cast<uint32_t>(*body - buffer);
- EmitUint32(header, dest_);
- EmitUint32(header, body_offset);
- EmitUint32(header, static_cast<uint32_t>(data_.size()));
- EmitUint8(header, 1); // init
+ EmitVarInt(header, dest_);
+ EmitVarInt(header, static_cast<uint32_t>(data_.size()));
- std::memcpy(*body, &data_[0], data_.size());
- (*body) += data_.size();
+ std::memcpy(*header, &data_[0], data_.size());
+ (*header) += data_.size();
}
-
WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
: zone_(zone),
signatures_(zone),
@@ -341,8 +433,8 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
data_segments_(zone),
indirect_functions_(zone),
globals_(zone),
- signature_map_(zone) {}
-
+ signature_map_(zone),
+ start_function_index_(-1) {}
uint16_t WasmModuleBuilder::AddFunction() {
functions_.push_back(new (zone_) WasmFunctionBuilder(zone_));
@@ -399,6 +491,9 @@ void WasmModuleBuilder::AddIndirectFunction(uint16_t index) {
indirect_functions_.push_back(index);
}
+void WasmModuleBuilder::MarkStartFunction(uint16_t index) {
+ start_function_index_ = index;
+}
WasmModuleWriter* WasmModuleBuilder::Build(Zone* zone) {
WasmModuleWriter* writer = new (zone) WasmModuleWriter(zone);
@@ -417,6 +512,7 @@ WasmModuleWriter* WasmModuleBuilder::Build(Zone* zone) {
for (auto global : globals_) {
writer->globals_.push_back(global);
}
+ writer->start_function_index_ = start_function_index_;
return writer;
}
@@ -434,7 +530,6 @@ WasmModuleWriter::WasmModuleWriter(Zone* zone)
indirect_functions_(zone),
globals_(zone) {}
-
struct Sizes {
size_t header_size;
size_t body_size;
@@ -446,80 +541,124 @@ struct Sizes {
body_size += body;
}
- void AddSection(size_t size) {
- if (size > 0) {
- Add(1, 0);
- while (size > 0) {
- Add(1, 0);
- size = size >> 7;
- }
- }
+ void AddSection(WasmSection::Code code, size_t other_size) {
+ Add(padded_varint + SizeOfVarInt(WasmSection::getNameLength(code)) +
+ WasmSection::getNameLength(code),
+ 0);
+ if (other_size) Add(SizeOfVarInt(other_size), 0);
}
};
-
WasmModuleIndex* WasmModuleWriter::WriteTo(Zone* zone) const {
Sizes sizes = {0, 0};
- sizes.Add(1, 0);
+ sizes.Add(2 * sizeof(uint32_t), 0); // header
+
+ sizes.AddSection(WasmSection::Code::Memory, 0);
sizes.Add(kDeclMemorySize, 0);
+ TRACE("Size after memory: %u, %u\n", (unsigned)sizes.header_size,
+ (unsigned)sizes.body_size);
- sizes.AddSection(signatures_.size());
- for (auto sig : signatures_) {
- sizes.Add(2 + sig->parameter_count(), 0);
+ if (globals_.size() > 0) {
+ sizes.AddSection(WasmSection::Code::Globals, globals_.size());
+ /* These globals never have names, so are always 3 bytes. */
+ sizes.Add(3 * globals_.size(), 0);
+ TRACE("Size after globals: %u, %u\n", (unsigned)sizes.header_size,
+ (unsigned)sizes.body_size);
}
- sizes.AddSection(globals_.size());
- if (globals_.size() > 0) {
- sizes.Add(kDeclGlobalSize * globals_.size(), 0);
+ if (signatures_.size() > 0) {
+ sizes.AddSection(WasmSection::Code::Signatures, signatures_.size());
+ for (auto sig : signatures_) {
+ sizes.Add(
+ 1 + SizeOfVarInt(sig->parameter_count()) + sig->parameter_count(), 0);
+ }
+ TRACE("Size after signatures: %u, %u\n", (unsigned)sizes.header_size,
+ (unsigned)sizes.body_size);
}
- sizes.AddSection(functions_.size());
- for (auto function : functions_) {
- sizes.Add(function->HeaderSize() + function->BodySize(),
- function->NameSize());
+ if (functions_.size() > 0) {
+ sizes.AddSection(WasmSection::Code::Functions, functions_.size());
+ for (auto function : functions_) {
+ sizes.Add(function->HeaderSize() + function->BodySize(),
+ function->NameSize());
+ }
+ TRACE("Size after functions: %u, %u\n", (unsigned)sizes.header_size,
+ (unsigned)sizes.body_size);
}
- sizes.AddSection(data_segments_.size());
- for (auto segment : data_segments_) {
- sizes.Add(segment->HeaderSize(), segment->BodySize());
+ if (start_function_index_ >= 0) {
+ sizes.AddSection(WasmSection::Code::StartFunction, 0);
+ sizes.Add(SizeOfVarInt(start_function_index_), 0);
+ TRACE("Size after start: %u, %u\n", (unsigned)sizes.header_size,
+ (unsigned)sizes.body_size);
}
- sizes.AddSection(indirect_functions_.size());
- sizes.Add(2 * static_cast<uint32_t>(indirect_functions_.size()), 0);
+ if (data_segments_.size() > 0) {
+ sizes.AddSection(WasmSection::Code::DataSegments, data_segments_.size());
+ for (auto segment : data_segments_) {
+ sizes.Add(segment->HeaderSize(), segment->BodySize());
+ }
+ TRACE("Size after data segments: %u, %u\n", (unsigned)sizes.header_size,
+ (unsigned)sizes.body_size);
+ }
- if (sizes.body_size > 0) sizes.Add(1, 0);
+ if (indirect_functions_.size() > 0) {
+ sizes.AddSection(WasmSection::Code::FunctionTable,
+ indirect_functions_.size());
+ for (auto function_index : indirect_functions_) {
+ sizes.Add(SizeOfVarInt(function_index), 0);
+ }
+ TRACE("Size after indirect functions: %u, %u\n",
+ (unsigned)sizes.header_size, (unsigned)sizes.body_size);
+ }
+
+ if (sizes.body_size > 0) {
+ sizes.AddSection(WasmSection::Code::End, 0);
+ TRACE("Size after end: %u, %u\n", (unsigned)sizes.header_size,
+ (unsigned)sizes.body_size);
+ }
ZoneVector<uint8_t> buffer_vector(sizes.total(), zone);
byte* buffer = &buffer_vector[0];
byte* header = buffer;
byte* body = buffer + sizes.header_size;
+ // -- emit magic -------------------------------------------------------------
+ TRACE("emit magic\n");
+ EmitUint32(&header, kWasmMagic);
+ EmitUint32(&header, kWasmVersion);
+
// -- emit memory declaration ------------------------------------------------
- EmitUint8(&header, kDeclMemory);
- EmitUint8(&header, 16); // min memory size
- EmitUint8(&header, 16); // max memory size
- EmitUint8(&header, 0); // memory export
+ {
+ byte* section = EmitSection(WasmSection::Code::Memory, &header);
+ EmitVarInt(&header, 16); // min memory size
+ EmitVarInt(&header, 16); // max memory size
+ EmitUint8(&header, 0); // memory export
+ static_assert(kDeclMemorySize == 3, "memory size must match emit above");
+ FixupSection(section, header);
+ }
// -- emit globals -----------------------------------------------------------
if (globals_.size() > 0) {
- EmitUint8(&header, kDeclGlobals);
+ byte* section = EmitSection(WasmSection::Code::Globals, &header);
EmitVarInt(&header, globals_.size());
for (auto global : globals_) {
- EmitUint32(&header, 0);
+ EmitVarInt(&header, 0); // Length of the global name.
EmitUint8(&header, WasmOpcodes::MemTypeCodeFor(global.first));
EmitUint8(&header, global.second);
}
+ FixupSection(section, header);
}
// -- emit signatures --------------------------------------------------------
if (signatures_.size() > 0) {
- EmitUint8(&header, kDeclSignatures);
+ byte* section = EmitSection(WasmSection::Code::Signatures, &header);
EmitVarInt(&header, signatures_.size());
for (FunctionSig* sig : signatures_) {
- EmitUint8(&header, static_cast<byte>(sig->parameter_count()));
+ EmitVarInt(&header, sig->parameter_count());
if (sig->return_count() > 0) {
EmitUint8(&header, WasmOpcodes::LocalTypeCodeFor(sig->GetReturn()));
} else {
@@ -529,39 +668,53 @@ WasmModuleIndex* WasmModuleWriter::WriteTo(Zone* zone) const {
EmitUint8(&header, WasmOpcodes::LocalTypeCodeFor(sig->GetParam(j)));
}
}
+ FixupSection(section, header);
}
// -- emit functions ---------------------------------------------------------
if (functions_.size() > 0) {
- EmitUint8(&header, kDeclFunctions);
+ byte* section = EmitSection(WasmSection::Code::Functions, &header);
EmitVarInt(&header, functions_.size());
for (auto func : functions_) {
func->Serialize(buffer, &header, &body);
}
+ FixupSection(section, header);
+ }
+
+ // -- emit start function index ----------------------------------------------
+ if (start_function_index_ >= 0) {
+ byte* section = EmitSection(WasmSection::Code::StartFunction, &header);
+ EmitVarInt(&header, start_function_index_);
+ FixupSection(section, header);
}
// -- emit data segments -----------------------------------------------------
if (data_segments_.size() > 0) {
- EmitUint8(&header, kDeclDataSegments);
+ byte* section = EmitSection(WasmSection::Code::DataSegments, &header);
EmitVarInt(&header, data_segments_.size());
for (auto segment : data_segments_) {
segment->Serialize(buffer, &header, &body);
}
+ FixupSection(section, header);
}
// -- emit function table ----------------------------------------------------
if (indirect_functions_.size() > 0) {
- EmitUint8(&header, kDeclFunctionTable);
+ byte* section = EmitSection(WasmSection::Code::FunctionTable, &header);
EmitVarInt(&header, indirect_functions_.size());
for (auto index : indirect_functions_) {
- EmitUint16(&header, index);
+ EmitVarInt(&header, index);
}
+ FixupSection(section, header);
}
- if (sizes.body_size > 0) EmitUint8(&header, kDeclEnd);
+ if (sizes.body_size > 0) {
+ byte* section = EmitSection(WasmSection::Code::End, &header);
+ FixupSection(section, header);
+ }
return new (zone) WasmModuleIndex(buffer, buffer + sizes.total());
}
diff --git a/deps/v8/src/wasm/encoder.h b/deps/v8/src/wasm/encoder.h
index 7b651bf95e..49a7bf7d05 100644
--- a/deps/v8/src/wasm/encoder.h
+++ b/deps/v8/src/wasm/encoder.h
@@ -42,11 +42,6 @@ class WasmFunctionEncoder : public ZoneObject {
ZoneVector<uint8_t> body_;
ZoneVector<char> name_;
- bool HasLocals() const {
- return (local_i32_count_ + local_i64_count_ + local_f32_count_ +
- local_f64_count_) > 0;
- }
-
bool HasName() const { return (exported_ || external_) && name_.size() > 0; }
};
@@ -60,8 +55,10 @@ class WasmFunctionBuilder : public ZoneObject {
const uint32_t* local_indices, uint32_t indices_size);
void Emit(WasmOpcode opcode);
void EmitWithU8(WasmOpcode opcode, const byte immediate);
- uint32_t EmitEditableImmediate(const byte immediate);
- void EditImmediate(uint32_t offset, const byte immediate);
+ void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
+ void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate);
+ uint32_t EmitEditableVarIntImmediate();
+ void EditVarIntImmediate(uint32_t offset, const uint32_t immediate);
void Exported(uint8_t flag);
void External(uint8_t flag);
void SetName(const unsigned char* name, int name_length);
@@ -120,6 +117,7 @@ class WasmModuleWriter : public ZoneObject {
ZoneVector<FunctionSig*> signatures_;
ZoneVector<uint16_t> indirect_functions_;
ZoneVector<std::pair<MachineType, bool>> globals_;
+ int start_function_index_;
};
class WasmModuleBuilder : public ZoneObject {
@@ -131,6 +129,7 @@ class WasmModuleBuilder : public ZoneObject {
void AddDataSegment(WasmDataSegmentEncoder* data);
uint16_t AddSignature(FunctionSig* sig);
void AddIndirectFunction(uint16_t index);
+ void MarkStartFunction(uint16_t index);
WasmModuleWriter* Build(Zone* zone);
struct CompareFunctionSigs {
@@ -146,6 +145,7 @@ class WasmModuleBuilder : public ZoneObject {
ZoneVector<uint16_t> indirect_functions_;
ZoneVector<std::pair<MachineType, bool>> globals_;
SignatureMap signature_map_;
+ int start_function_index_;
};
std::vector<uint8_t> UnsignedLEB128From(uint32_t result);
diff --git a/deps/v8/src/wasm/module-decoder.cc b/deps/v8/src/wasm/module-decoder.cc
index 62b000da2b..3e85a1b53c 100644
--- a/deps/v8/src/wasm/module-decoder.cc
+++ b/deps/v8/src/wasm/module-decoder.cc
@@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "src/wasm/module-decoder.h"
+
+#include "src/base/functional.h"
+#include "src/base/platform/platform.h"
#include "src/macro-assembler.h"
#include "src/objects.h"
#include "src/v8.h"
#include "src/wasm/decoder.h"
-#include "src/wasm/module-decoder.h"
namespace v8 {
namespace internal {
@@ -27,8 +30,8 @@ namespace wasm {
class ModuleDecoder : public Decoder {
public:
ModuleDecoder(Zone* zone, const byte* module_start, const byte* module_end,
- bool asm_js)
- : Decoder(module_start, module_end), module_zone(zone), asm_js_(asm_js) {
+ ModuleOrigin origin)
+ : Decoder(module_start, module_end), module_zone(zone), origin_(origin) {
result_.start = start_;
if (limit_ < start_) {
error(start_, "end is less than start");
@@ -40,86 +43,196 @@ class ModuleDecoder : public Decoder {
pc_ = limit_; // On error, terminate section decoding loop.
}
+ static void DumpModule(WasmModule* module, ModuleResult result) {
+ std::string path;
+ if (FLAG_dump_wasm_module_path) {
+ path = FLAG_dump_wasm_module_path;
+ if (path.size() &&
+ !base::OS::isDirectorySeparator(path[path.size() - 1])) {
+ path += base::OS::DirectorySeparator();
+ }
+ }
+ // File are named `HASH.{ok,failed}.wasm`.
+ size_t hash = base::hash_range(module->module_start, module->module_end);
+ char buf[32] = {'\0'};
+#if V8_OS_WIN && _MSC_VER < 1900
+#define snprintf sprintf_s
+#endif
+ snprintf(buf, sizeof(buf) - 1, "%016zx.%s.wasm", hash,
+ result.ok() ? "ok" : "failed");
+ std::string name(buf);
+ if (FILE* wasm_file = base::OS::FOpen((path + name).c_str(), "wb")) {
+ fwrite(module->module_start, module->module_end - module->module_start, 1,
+ wasm_file);
+ fclose(wasm_file);
+ }
+ }
+
// Decodes an entire module.
ModuleResult DecodeModule(WasmModule* module, bool verify_functions = true) {
pc_ = start_;
module->module_start = start_;
module->module_end = limit_;
- module->min_mem_size_log2 = 0;
- module->max_mem_size_log2 = 0;
+ module->min_mem_pages = 0;
+ module->max_mem_pages = 0;
module->mem_export = false;
module->mem_external = false;
- module->globals = new std::vector<WasmGlobal>();
- module->signatures = new std::vector<FunctionSig*>();
- module->functions = new std::vector<WasmFunction>();
- module->data_segments = new std::vector<WasmDataSegment>();
- module->function_table = new std::vector<uint16_t>();
- module->import_table = new std::vector<WasmImport>();
+ module->origin = origin_;
+
+ bool sections[(size_t)WasmSection::Code::Max] = {false};
+
+ const byte* pos = pc_;
+ 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) {
+ error(pos, pos,
+ "expected magic word %02x %02x %02x %02x, "
+ "found %02x %02x %02x %02x",
+ BYTES(kWasmMagic), BYTES(magic_word));
+ goto done;
+ }
- bool sections[kMaxModuleSectionCode];
- memset(sections, 0, sizeof(sections));
+ pos = pc_;
+ {
+ uint32_t magic_version = consume_u32("wasm version");
+ if (magic_version != kWasmVersion) {
+ error(pos, pos,
+ "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");
- WasmSectionDeclCode section =
- static_cast<WasmSectionDeclCode>(consume_u8("section"));
- // Each section should appear at most once.
- if (section < kMaxModuleSectionCode) {
- CheckForPreviousSection(sections, section, false);
- sections[section] = true;
+ pos = pc_;
+
+ int length;
+ uint32_t section_length = consume_u32v(&length, "section size");
+
+ int section_string_leb_length = 0;
+ uint32_t section_string_length = 0;
+ WasmSection::Code section = consume_section_name(
+ &section_string_leb_length, &section_string_length);
+ uint32_t string_and_leb_length =
+ section_string_leb_length + section_string_length;
+ if (string_and_leb_length > section_length) {
+ error(pos, pos,
+ "section string of size %u longer than total section bytes %u",
+ string_and_leb_length, section_length);
+ break;
}
+ if (section == WasmSection::Code::Max) {
+ // Skip unknown section.
+ uint32_t skip = section_length - string_and_leb_length;
+ TRACE("skipping %u bytes from unknown section\n", skip);
+ consume_bytes(skip);
+ continue;
+ }
+
+ // Each section should appear at most once.
+ CheckForPreviousSection(sections, section, false);
+ sections[(size_t)section] = true;
+
switch (section) {
- case kDeclEnd:
+ case WasmSection::Code::End:
// Terminate section decoding.
limit_ = pc_;
break;
- case kDeclMemory:
- module->min_mem_size_log2 = consume_u8("min memory");
- module->max_mem_size_log2 = consume_u8("max memory");
+ case WasmSection::Code::Memory:
+ int length;
+ module->min_mem_pages = consume_u32v(&length, "min memory");
+ module->max_mem_pages = consume_u32v(&length, "max memory");
module->mem_export = consume_u8("export memory") != 0;
break;
- case kDeclSignatures: {
+ case WasmSection::Code::Signatures: {
int length;
uint32_t signatures_count = consume_u32v(&length, "signatures count");
- module->signatures->reserve(SafeReserve(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(); // read function sig.
- module->signatures->push_back(s);
+ module->signatures.push_back(s);
}
break;
}
- case kDeclFunctions: {
+ case WasmSection::Code::FunctionSignatures: {
// Functions require a signature table first.
- CheckForPreviousSection(sections, kDeclSignatures, true);
+ CheckForPreviousSection(sections, WasmSection::Code::Signatures,
+ true);
int length;
uint32_t functions_count = consume_u32v(&length, "functions count");
- module->functions->reserve(SafeReserve(functions_count));
+ module->functions.reserve(SafeReserve(functions_count));
+ for (uint32_t i = 0; i < functions_count; i++) {
+ module->functions.push_back(
+ {nullptr, i, 0, 0, 0, 0, 0, 0, false, false});
+ WasmFunction* function = &module->functions.back();
+ function->sig_index = consume_sig_index(module, &function->sig);
+ }
+ break;
+ }
+ case WasmSection::Code::FunctionBodies: {
+ // Function bodies should follow signatures.
+ CheckForPreviousSection(sections,
+ WasmSection::Code::FunctionSignatures, true);
+ int length;
+ const byte* pos = pc_;
+ uint32_t functions_count = consume_u32v(&length, "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];
+ int length;
+ uint32_t size = consume_u32v(&length, "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::Functions: {
+ // Functions require a signature table first.
+ CheckForPreviousSection(sections, WasmSection::Code::Signatures,
+ true);
+ int length;
+ uint32_t functions_count = consume_u32v(&length, "functions count");
+ module->functions.reserve(SafeReserve(functions_count));
// Set up module environment for verification.
ModuleEnv menv;
menv.module = module;
menv.instance = nullptr;
- menv.asm_js = asm_js_;
+ menv.origin = origin_;
// Decode functions.
for (uint32_t i = 0; i < functions_count; i++) {
if (failed()) break;
TRACE("DecodeFunction[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
- module->functions->push_back(
+ module->functions.push_back(
{nullptr, i, 0, 0, 0, 0, 0, 0, false, false});
- WasmFunction* function = &module->functions->back();
+ WasmFunction* function = &module->functions.back();
DecodeFunctionInModule(module, function, false);
}
if (ok() && verify_functions) {
for (uint32_t i = 0; i < functions_count; i++) {
if (failed()) break;
- WasmFunction* function = &module->functions->at(i);
+ WasmFunction* function = &module->functions[i];
if (!function->external) {
VerifyFunctionBody(i, &menv, function);
if (result_.failed())
@@ -129,132 +242,166 @@ class ModuleDecoder : public Decoder {
}
break;
}
- case kDeclGlobals: {
+ case WasmSection::Code::Names: {
+ // Names correspond to functions.
+ CheckForPreviousSection(sections,
+ WasmSection::Code::FunctionSignatures, true);
+ int length;
+ const byte* pos = pc_;
+ uint32_t functions_count = consume_u32v(&length, "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()));
+ break;
+ }
+
+ for (uint32_t i = 0; i < functions_count; i++) {
+ WasmFunction* function = &module->functions[i];
+ function->name_offset =
+ consume_string(&function->name_length, "function name");
+
+ uint32_t local_names_count =
+ consume_u32v(&length, "local names count");
+ for (uint32_t j = 0; j < local_names_count; j++) {
+ uint32_t unused = 0;
+ uint32_t offset = consume_string(&unused, "local name");
+ USE(unused);
+ USE(offset);
+ }
+ }
+ break;
+ }
+ case WasmSection::Code::Globals: {
int length;
uint32_t globals_count = consume_u32v(&length, "globals count");
- module->globals->reserve(SafeReserve(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_));
- module->globals->push_back({0, MachineType::Int32(), 0, false});
- WasmGlobal* global = &module->globals->back();
+ module->globals.push_back({0, 0, MachineType::Int32(), 0, false});
+ WasmGlobal* global = &module->globals.back();
DecodeGlobalInModule(global);
}
break;
}
- case kDeclDataSegments: {
+ case WasmSection::Code::DataSegments: {
int length;
uint32_t data_segments_count =
consume_u32v(&length, "data segments count");
- module->data_segments->reserve(SafeReserve(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, 0, 0});
- WasmDataSegment* segment = &module->data_segments->back();
+ module->data_segments.push_back({0, 0, 0});
+ WasmDataSegment* segment = &module->data_segments.back();
DecodeDataSegmentInModule(module, segment);
}
break;
}
- case kDeclFunctionTable: {
+ case WasmSection::Code::FunctionTable: {
// An indirect function table requires functions first.
- CheckForPreviousSection(sections, kDeclFunctions, true);
+ CheckForFunctions(module, section);
int length;
uint32_t function_table_count =
consume_u32v(&length, "function table count");
- module->function_table->reserve(SafeReserve(function_table_count));
+ module->function_table.reserve(SafeReserve(function_table_count));
// Decode function table.
for (uint32_t i = 0; i < function_table_count; i++) {
if (failed()) break;
TRACE("DecodeFunctionTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
- uint16_t index = consume_u16();
- if (index >= module->functions->size()) {
+ uint16_t index = consume_u32v(&length);
+ if (index >= module->functions.size()) {
error(pc_ - 2, "invalid function index");
break;
}
- module->function_table->push_back(index);
+ module->function_table.push_back(index);
}
break;
}
- case kDeclStartFunction: {
+ case WasmSection::Code::StartFunction: {
// Declares a start function for a module.
- CheckForPreviousSection(sections, kDeclFunctions, true);
+ CheckForFunctions(module, section);
if (module->start_function_index >= 0) {
error("start function already declared");
break;
}
- int length;
- const byte* before = pc_;
- uint32_t index = consume_u32v(&length, "start function index");
- if (index >= module->functions->size()) {
- error(before, "invalid start function index");
- break;
- }
- module->start_function_index = static_cast<int>(index);
- FunctionSig* sig =
- module->signatures->at(module->functions->at(index).sig_index);
- if (sig->parameter_count() > 0) {
- error(before, "invalid start function: non-zero parameter count");
+ 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");
break;
}
break;
}
- case kDeclImportTable: {
+ case WasmSection::Code::ImportTable: {
// Declares an import table.
- CheckForPreviousSection(sections, kDeclSignatures, true);
+ CheckForPreviousSection(sections, WasmSection::Code::Signatures,
+ true);
int length;
uint32_t import_table_count =
consume_u32v(&length, "import table count");
- module->import_table->reserve(SafeReserve(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, 0, 0});
- WasmImport* import = &module->import_table->back();
+ module->import_table.push_back({nullptr, 0, 0});
+ WasmImport* import = &module->import_table.back();
- const byte* sigpos = pc_;
- import->sig_index = consume_u16("signature index");
-
- if (import->sig_index >= module->signatures->size()) {
- error(sigpos, "invalid signature index");
- } else {
- import->sig = module->signatures->at(import->sig_index);
+ import->sig_index = consume_sig_index(module, &import->sig);
+ const byte* pos = pc_;
+ import->module_name_offset = consume_string(
+ &import->module_name_length, "import module name");
+ if (import->module_name_length == 0) {
+ error(pos, "import module name cannot be NULL");
}
- import->module_name_offset = consume_string("import module name");
- import->function_name_offset =
- consume_string("import function name");
+ import->function_name_offset = consume_string(
+ &import->function_name_length, "import function name");
}
break;
}
- case kDeclWLL: {
- // Reserved for experimentation by the Web Low-level Language project
- // which is augmenting the binary encoding with source code meta
- // information. This section does not affect the semantics of the code
- // and can be ignored by the runtime. https://github.com/JSStats/wll
- int length = 0;
- uint32_t section_size = consume_u32v(&length, "section size");
- if (pc_ + section_size > limit_ || pc_ + section_size < pc_) {
- error(pc_ - length, "invalid section size");
- break;
+ case WasmSection::Code::ExportTable: {
+ // Declares an export table.
+ CheckForFunctions(module, section);
+ int length;
+ uint32_t export_table_count =
+ consume_u32v(&length, "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, 0});
+ WasmExport* exp = &module->export_table.back();
+
+ WasmFunction* func;
+ exp->func_index = consume_func_index(module, &func);
+ exp->name_offset = consume_string(&exp->name_length, "export name");
}
- pc_ += section_size;
break;
}
- default:
- error(pc_ - 1, nullptr, "unrecognized section 0x%02x", section);
- break;
+ case WasmSection::Code::Max:
+ UNREACHABLE(); // Already skipped unknown sections.
}
}
- return toResult(module);
+ done:
+ ModuleResult result = toResult(module);
+ if (FLAG_dump_wasm_module) {
+ DumpModule(module, result);
+ }
+ return result;
}
uint32_t SafeReserve(uint32_t count) {
@@ -263,38 +410,23 @@ class ModuleDecoder : public Decoder {
return count < kMaxReserve ? count : kMaxReserve;
}
- void CheckForPreviousSection(bool* sections, WasmSectionDeclCode section,
- bool present) {
- if (section >= kMaxModuleSectionCode) return;
- if (sections[section] == present) return;
- const char* name = "";
- switch (section) {
- case kDeclMemory:
- name = "memory";
- break;
- case kDeclSignatures:
- name = "signatures";
- break;
- case kDeclFunctions:
- name = "function declaration";
- break;
- case kDeclGlobals:
- name = "global variable";
- break;
- case kDeclDataSegments:
- name = "data segment";
- break;
- case kDeclFunctionTable:
- name = "function table";
- break;
- default:
- name = "";
- break;
+ 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));
}
+ }
+
+ void CheckForPreviousSection(bool* sections, WasmSection::Code section,
+ bool present) {
+ if (section >= WasmSection::Code::Max) return;
+ if (sections[(size_t)section] == present) return;
if (present) {
- error(pc_ - 1, nullptr, "required %s section missing", name);
+ error(pc_ - 1, nullptr, "required %s section missing",
+ WasmSection::getName(section));
} else {
- error(pc_ - 1, nullptr, "%s section already present", name);
+ error(pc_ - 1, nullptr, "%s section already present",
+ WasmSection::getName(section));
}
}
@@ -302,16 +434,13 @@ class ModuleDecoder : public Decoder {
FunctionResult DecodeSingleFunction(ModuleEnv* module_env,
WasmFunction* function) {
pc_ = start_;
- function->sig = consume_sig(); // read signature
- function->name_offset = 0; // ---- name
- function->code_start_offset = off(pc_ + 8); // ---- code start
- function->code_end_offset = off(limit_); // ---- code end
- function->local_i32_count = consume_u16(); // read u16
- function->local_i64_count = consume_u16(); // read u16
- function->local_f32_count = consume_u16(); // read u16
- function->local_f64_count = consume_u16(); // read u16
- function->exported = false; // ---- exported
- function->external = false; // ---- external
+ function->sig = consume_sig(); // read signature
+ function->name_offset = 0; // ---- name
+ function->name_length = 0; // ---- name length
+ function->code_start_offset = off(pc_); // ---- code start
+ function->code_end_offset = off(limit_); // ---- code end
+ function->exported = false; // ---- exported
+ function->external = false; // ---- external
if (ok()) VerifyFunctionBody(0, module_env, function);
@@ -331,19 +460,20 @@ class ModuleDecoder : public Decoder {
private:
Zone* module_zone;
ModuleResult result_;
- bool asm_js_;
+ ModuleOrigin origin_;
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");
+ global->name_offset = consume_string(&global->name_length, "global name");
global->type = mem_type();
global->offset = 0;
global->exported = consume_u8("exported") != 0;
}
// Decodes a single function entry inside a module starting at {pc_}.
+ // TODO(titzer): legacy function body; remove
void DecodeFunctionInModule(WasmModule* module, WasmFunction* function,
bool verify_body = true) {
byte decl_bits = consume_u8("function decl");
@@ -351,10 +481,10 @@ class ModuleDecoder : public Decoder {
const byte* sigpos = pc_;
function->sig_index = consume_u16("signature index");
- if (function->sig_index >= module->signatures->size()) {
+ if (function->sig_index >= module->signatures.size()) {
return error(sigpos, "invalid signature index");
} else {
- function->sig = module->signatures->at(function->sig_index);
+ function->sig = module->signatures[function->sig_index];
}
TRACE(" +%d <function attributes:%s%s%s%s%s>\n",
@@ -366,7 +496,8 @@ class ModuleDecoder : public Decoder {
(decl_bits & kDeclFunctionImport) == 0 ? " body" : "");
if (decl_bits & kDeclFunctionName) {
- function->name_offset = consume_string("function name");
+ function->name_offset =
+ consume_string(&function->name_length, "function name");
}
function->exported = decl_bits & kDeclFunctionExport;
@@ -406,25 +537,30 @@ class ModuleDecoder : public Decoder {
// Decodes a single data segment entry inside a module starting at {pc_}.
void DecodeDataSegmentInModule(WasmModule* module, WasmDataSegment* segment) {
- segment->dest_addr = consume_u32("destination");
- segment->source_offset = consume_offset("source offset");
- segment->source_size = consume_u32("source size");
- segment->init = consume_u8("init");
+ const byte* start = pc_;
+ int length;
+ segment->dest_addr = consume_u32v(&length, "destination");
+ segment->source_size = consume_u32v(&length, "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_);
if (!IsWithinLimit(module_limit, segment->source_offset,
segment->source_size)) {
- error(pc_ - sizeof(uint32_t), "segment out of bounds of module");
+ error(start, "segment out of bounds of module");
}
// Validate that the segment will fit into the (minimum) memory.
uint32_t memory_limit =
- 1 << (module ? module->min_mem_size_log2 : WasmModule::kMaxMemSize);
+ WasmModule::kPageSize * (module ? module->min_mem_pages
+ : WasmModule::kMaxMemPages);
if (!IsWithinLimit(memory_limit, segment->dest_addr,
segment->source_size)) {
- error(pc_ - sizeof(uint32_t), "segment out of bounds of memory");
+ error(start, "segment out of bounds of memory");
}
+
+ consume_bytes(segment->source_size);
}
// Verifies the body (code) of a given function.
@@ -436,18 +572,10 @@ class ModuleDecoder : public Decoder {
<< std::endl;
os << std::endl;
}
- FunctionEnv fenv;
- fenv.module = menv;
- fenv.sig = function->sig;
- fenv.local_i32_count = function->local_i32_count;
- fenv.local_i64_count = function->local_i64_count;
- fenv.local_f32_count = function->local_f32_count;
- fenv.local_f64_count = function->local_f64_count;
- fenv.SumLocals();
-
- TreeResult result =
- VerifyWasmCode(&fenv, start_, start_ + function->code_start_offset,
- start_ + function->code_end_offset);
+ FunctionBody body = {menv, function->sig, start_,
+ start_ + function->code_start_offset,
+ start_ + function->code_end_offset};
+ TreeResult result = VerifyWasmCode(module_zone->allocator(), body);
if (result.failed()) {
// Wrap the error message from the function decoder.
std::ostringstream str;
@@ -476,11 +604,67 @@ class ModuleDecoder : public Decoder {
return offset;
}
- // Reads a single 32-bit unsigned integer interpreted as an offset into the
- // data and validating the string there and advances.
- uint32_t consume_string(const char* name = nullptr) {
- // TODO(titzer): validate string
- return consume_offset(name ? name : "string");
+ // 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, const char* name = nullptr) {
+ int varint_length;
+ *length = consume_u32v(&varint_length, "string length");
+ uint32_t offset = pc_offset();
+ TRACE(" +%u %-20s: (%u bytes)\n", offset, "string", *length);
+ consume_bytes(*length);
+ return offset;
+ }
+
+ uint32_t consume_sig_index(WasmModule* module, FunctionSig** sig) {
+ const byte* pos = pc_;
+ int length;
+ uint32_t sig_index = consume_u32v(&length, "signature index");
+ if (sig_index >= module->signatures.size()) {
+ error(pos, pos, "signature index %u out of bounds (%d signatures)",
+ sig_index, static_cast<int>(module->signatures.size()));
+ *sig = nullptr;
+ return 0;
+ }
+ *sig = module->signatures[sig_index];
+ return sig_index;
+ }
+
+ uint32_t consume_func_index(WasmModule* module, WasmFunction** func) {
+ const byte* pos = pc_;
+ int length;
+ uint32_t func_index = consume_u32v(&length, "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;
+ return 0;
+ }
+ *func = &module->functions[func_index];
+ return func_index;
+ }
+
+ // Reads a section name.
+ WasmSection::Code consume_section_name(int* string_leb_length,
+ uint32_t* string_length) {
+ *string_length = consume_u32v(string_leb_length, "name length");
+ const byte* start = pc_;
+ consume_bytes(*string_length);
+ if (failed()) {
+ TRACE("Section name of length %u couldn't be read\n", *string_length);
+ return WasmSection::Code::Max;
+ }
+ // TODO(jfb) Linear search, it may be better to do a common-prefix search.
+ for (WasmSection::Code i = WasmSection::begin(); i != WasmSection::end();
+ i = WasmSection::next(i)) {
+ if (WasmSection::getNameLength(i) == *string_length &&
+ 0 == memcmp(WasmSection::getName(i), start, *string_length)) {
+ return i;
+ }
+ }
+ TRACE("Unknown section: '");
+ for (uint32_t i = 0; i != *string_length; ++i) TRACE("%c", *(start + i));
+ TRACE("'\n");
+ return WasmSection::Code::Max;
}
// Reads a single 8-bit integer, interpreting it as a local type.
@@ -537,7 +721,8 @@ class ModuleDecoder : public Decoder {
// Parses an inline function signature.
FunctionSig* consume_sig() {
- byte count = consume_u8("param count");
+ int length;
+ byte count = consume_u32v(&length, "param count");
LocalType ret = consume_local_type();
FunctionSig::Builder builder(module_zone, ret == kAstStmt ? 0 : 1, count);
if (ret != kAstStmt) builder.AddReturn(ret);
@@ -579,22 +764,21 @@ class FunctionError : public FunctionResult {
}
};
-
ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
const byte* module_start, const byte* module_end,
- bool verify_functions, bool asm_js) {
+ bool verify_functions, ModuleOrigin origin) {
size_t size = module_end - module_start;
if (module_start > module_end) return ModuleError("start > end");
if (size >= kMaxModuleSize) return ModuleError("size > maximum module size");
WasmModule* module = new WasmModule();
- ModuleDecoder decoder(zone, module_start, module_end, asm_js);
+ ModuleDecoder decoder(zone, module_start, module_end, origin);
return decoder.DecodeModule(module, verify_functions);
}
FunctionSig* DecodeWasmSignatureForTesting(Zone* zone, const byte* start,
const byte* end) {
- ModuleDecoder decoder(zone, start, end, false);
+ ModuleDecoder decoder(zone, start, end, kWasmOrigin);
return decoder.DecodeFunctionSignature(start);
}
@@ -608,7 +792,7 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone,
if (size > kMaxFunctionSize)
return FunctionError("size > maximum function size");
WasmFunction* function = new WasmFunction();
- ModuleDecoder decoder(zone, function_start, function_end, false);
+ ModuleDecoder decoder(zone, function_start, function_end, kWasmOrigin);
return decoder.DecodeSingleFunction(module_env, function);
}
} // namespace wasm
diff --git a/deps/v8/src/wasm/module-decoder.h b/deps/v8/src/wasm/module-decoder.h
index 3f469a500e..00a9b878c6 100644
--- a/deps/v8/src/wasm/module-decoder.h
+++ b/deps/v8/src/wasm/module-decoder.h
@@ -14,7 +14,7 @@ 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, bool asm_js);
+ bool verify_functions, ModuleOrigin origin);
// Exposed for testing. Decodes a single function signature, allocating it
// in the given zone. Returns {nullptr} upon failure.
diff --git a/deps/v8/src/wasm/wasm-external-refs.h b/deps/v8/src/wasm/wasm-external-refs.h
new file mode 100644
index 0000000000..4aa452bbf5
--- /dev/null
+++ b/deps/v8/src/wasm/wasm-external-refs.h
@@ -0,0 +1,181 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WASM_EXTERNAL_REFS_H
+#define WASM_EXTERNAL_REFS_H
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+static void f32_trunc_wrapper(float* param) { *param = truncf(*param); }
+
+static void f32_floor_wrapper(float* param) { *param = floorf(*param); }
+
+static void f32_ceil_wrapper(float* param) { *param = ceilf(*param); }
+
+static void f32_nearest_int_wrapper(float* param) {
+ *param = nearbyintf(*param);
+}
+
+static void f64_trunc_wrapper(double* param) { *param = trunc(*param); }
+
+static void f64_floor_wrapper(double* param) { *param = floor(*param); }
+
+static void f64_ceil_wrapper(double* param) { *param = ceil(*param); }
+
+static void f64_nearest_int_wrapper(double* param) {
+ *param = nearbyint(*param);
+}
+
+static void int64_to_float32_wrapper(int64_t* input, float* output) {
+ *output = static_cast<float>(*input);
+}
+
+static void uint64_to_float32_wrapper(uint64_t* input, float* output) {
+#if V8_CC_MSVC
+ // With MSVC we use static_cast<float>(uint32_t) instead of
+ // static_cast<float>(uint64_t) to achieve round-to-nearest-ties-even
+ // semantics. The idea is to calculate
+ // static_cast<float>(high_word) * 2^32 + static_cast<float>(low_word). To
+ // achieve proper rounding in all cases we have to adjust the high_word
+ // with a "rounding bit" sometimes. The rounding bit is stored in the LSB of
+ // the high_word if the low_word may affect the rounding of the high_word.
+ uint32_t low_word = static_cast<uint32_t>(*input & 0xffffffff);
+ uint32_t high_word = static_cast<uint32_t>(*input >> 32);
+
+ float shift = static_cast<float>(1ull << 32);
+ // If the MSB of the high_word is set, then we make space for a rounding bit.
+ if (high_word < 0x80000000) {
+ high_word <<= 1;
+ shift = static_cast<float>(1ull << 31);
+ }
+
+ if ((high_word & 0xfe000000) && low_word) {
+ // Set the rounding bit.
+ high_word |= 1;
+ }
+
+ float result = static_cast<float>(high_word);
+ result *= shift;
+ result += static_cast<float>(low_word);
+ *output = result;
+
+#else
+ *output = static_cast<float>(*input);
+#endif
+}
+
+static void int64_to_float64_wrapper(int64_t* input, double* output) {
+ *output = static_cast<double>(*input);
+}
+
+static void uint64_to_float64_wrapper(uint64_t* input, double* output) {
+#if V8_CC_MSVC
+ // With MSVC we use static_cast<double>(uint32_t) instead of
+ // static_cast<double>(uint64_t) to achieve round-to-nearest-ties-even
+ // semantics. The idea is to calculate
+ // static_cast<double>(high_word) * 2^32 + static_cast<double>(low_word).
+ uint32_t low_word = static_cast<uint32_t>(*input & 0xffffffff);
+ uint32_t high_word = static_cast<uint32_t>(*input >> 32);
+
+ double shift = static_cast<double>(1ull << 32);
+
+ double result = static_cast<double>(high_word);
+ result *= shift;
+ result += static_cast<double>(low_word);
+ *output = result;
+
+#else
+ *output = static_cast<double>(*input);
+#endif
+}
+
+static int32_t float32_to_int64_wrapper(float* input, int64_t* output) {
+ // We use "<" here to check the upper bound because of rounding problems: With
+ // "<=" some inputs would be considered within int64 range which are actually
+ // not within int64 range.
+ if (*input >= static_cast<float>(std::numeric_limits<int64_t>::min()) &&
+ *input < static_cast<float>(std::numeric_limits<int64_t>::max())) {
+ *output = static_cast<int64_t>(*input);
+ return 1;
+ }
+ return 0;
+}
+
+static int32_t float32_to_uint64_wrapper(float* input, uint64_t* output) {
+ // We use "<" here to check the upper bound because of rounding problems: With
+ // "<=" some inputs would be considered within uint64 range which are actually
+ // not within uint64 range.
+ if (*input > -1.0 &&
+ *input < static_cast<float>(std::numeric_limits<uint64_t>::max())) {
+ *output = static_cast<uint64_t>(*input);
+ return 1;
+ }
+ return 0;
+}
+
+static int32_t float64_to_int64_wrapper(double* input, int64_t* output) {
+ // We use "<" here to check the upper bound because of rounding problems: With
+ // "<=" some inputs would be considered within int64 range which are actually
+ // not within int64 range.
+ if (*input >= static_cast<double>(std::numeric_limits<int64_t>::min()) &&
+ *input < static_cast<double>(std::numeric_limits<int64_t>::max())) {
+ *output = static_cast<int64_t>(*input);
+ return 1;
+ }
+ return 0;
+}
+
+static int32_t float64_to_uint64_wrapper(double* input, uint64_t* output) {
+ // We use "<" here to check the upper bound because of rounding problems: With
+ // "<=" some inputs would be considered within uint64 range which are actually
+ // not within uint64 range.
+ if (*input > -1.0 &&
+ *input < static_cast<double>(std::numeric_limits<uint64_t>::max())) {
+ *output = static_cast<uint64_t>(*input);
+ return 1;
+ }
+ return 0;
+}
+
+static int32_t int64_div_wrapper(int64_t* dst, int64_t* src) {
+ if (*src == 0) {
+ return 0;
+ }
+ if (*src == -1 && *dst == std::numeric_limits<int64_t>::min()) {
+ return -1;
+ }
+ *dst /= *src;
+ return 1;
+}
+
+static int32_t int64_mod_wrapper(int64_t* dst, int64_t* src) {
+ if (*src == 0) {
+ return 0;
+ }
+ *dst %= *src;
+ return 1;
+}
+
+static int32_t uint64_div_wrapper(uint64_t* dst, uint64_t* src) {
+ if (*src == 0) {
+ return 0;
+ }
+ *dst /= *src;
+ return 1;
+}
+
+static int32_t uint64_mod_wrapper(uint64_t* dst, uint64_t* src) {
+ if (*src == 0) {
+ return 0;
+ }
+ *dst %= *src;
+ return 1;
+}
+} // namespace wasm
+} // namespace internal
+} // namespace v8
+
+#endif
diff --git a/deps/v8/src/wasm/wasm-js.cc b/deps/v8/src/wasm/wasm-js.cc
index 62a2676032..83009d7c81 100644
--- a/deps/v8/src/wasm/wasm-js.cc
+++ b/deps/v8/src/wasm/wasm-js.cc
@@ -37,20 +37,43 @@ struct RawBuffer {
RawBuffer GetRawBufferArgument(
ErrorThrower& thrower, const v8::FunctionCallbackInfo<v8::Value>& args) {
- // TODO(titzer): allow typed array views.
- if (args.Length() < 1 || !args[0]->IsArrayBuffer()) {
+ if (args.Length() < 1) {
thrower.Error("Argument 0 must be an array buffer");
return {nullptr, nullptr};
}
- Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]);
- ArrayBuffer::Contents contents = buffer->GetContents();
- const byte* start = reinterpret_cast<const byte*>(contents.Data());
- const byte* end = start + contents.ByteLength();
+ const byte* start = nullptr;
+ const byte* end = nullptr;
- if (start == nullptr) {
- thrower.Error("ArrayBuffer argument is empty");
+ if (args[0]->IsArrayBuffer()) {
+ // A raw array buffer was passed.
+ Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]);
+ ArrayBuffer::Contents contents = buffer->GetContents();
+
+ start = reinterpret_cast<const byte*>(contents.Data());
+ end = start + contents.ByteLength();
+
+ if (start == nullptr || end == start) {
+ thrower.Error("ArrayBuffer argument is empty");
+ }
+ } else if (args[0]->IsTypedArray()) {
+ // A TypedArray was passed.
+ Local<TypedArray> array = Local<TypedArray>::Cast(args[0]);
+ Local<ArrayBuffer> buffer = array->Buffer();
+
+ ArrayBuffer::Contents contents = buffer->GetContents();
+
+ start =
+ reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset();
+ end = start + array->ByteLength();
+
+ if (start == nullptr || end == start) {
+ thrower.Error("ArrayBuffer argument is empty");
+ }
+ } else {
+ thrower.Error("Argument 0 must be an ArrayBuffer or Uint8Array");
}
+
return {start, end};
}
@@ -63,9 +86,10 @@ void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
RawBuffer buffer = GetRawBufferArgument(thrower, args);
if (thrower.error()) return;
- i::Zone zone;
- internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
- isolate, &zone, buffer.start, buffer.end, true, false);
+ i::Zone zone(isolate->allocator());
+ internal::wasm::ModuleResult result =
+ internal::wasm::DecodeWasmModule(isolate, &zone, buffer.start, buffer.end,
+ true, internal::wasm::kWasmOrigin);
if (result.failed()) {
thrower.Failed("", result);
@@ -87,7 +111,7 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
{
// Verification of a single function shouldn't allocate.
i::DisallowHeapAllocation no_allocation;
- i::Zone zone;
+ i::Zone zone(isolate->allocator());
result = internal::wasm::DecodeWasmFunction(isolate, &zone, nullptr,
buffer.start, buffer.end);
}
@@ -123,25 +147,18 @@ v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
return nullptr;
}
- auto module = v8::internal::wasm::AsmWasmBuilder(
- info->isolate(), info->zone(), info->literal(), foreign)
- .Run();
-
- if (i::FLAG_dump_asmjs_wasm) {
- FILE* wasm_file = fopen(i::FLAG_asmjs_wasm_dumpfile, "wb");
- if (wasm_file) {
- fwrite(module->Begin(), module->End() - module->Begin(), 1, wasm_file);
- fclose(wasm_file);
- }
- }
+ auto module =
+ v8::internal::wasm::AsmWasmBuilder(info->isolate(), info->zone(),
+ info->literal(), foreign, &typer)
+ .Run();
return module;
}
-
void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args,
const byte* start, const byte* end,
- ErrorThrower* thrower, bool must_decode) {
+ ErrorThrower* thrower,
+ internal::wasm::ModuleOrigin origin) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
@@ -153,11 +170,11 @@ void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args,
// Decode but avoid a redundant pass over function bodies for verification.
// Verification will happen during compilation.
- i::Zone zone;
+ i::Zone zone(isolate->allocator());
internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
- isolate, &zone, start, end, false, false);
+ isolate, &zone, start, end, false, origin);
- if (result.failed() && must_decode) {
+ if (result.failed() && origin == internal::wasm::kAsmJsOrigin) {
thrower->Error("Asm.js converted module failed to decode");
} else if (result.failed()) {
thrower->Failed("", result);
@@ -192,7 +209,7 @@ void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
i::Factory* factory = isolate->factory();
- i::Zone zone;
+ i::Zone zone(isolate->allocator());
Local<String> source = Local<String>::Cast(args[0]);
i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
i::ParseInfo info(&zone, script);
@@ -208,7 +225,8 @@ void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
- InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower, true);
+ InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower,
+ internal::wasm::kAsmJsOrigin);
}
@@ -220,7 +238,8 @@ void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
RawBuffer buffer = GetRawBufferArgument(thrower, args);
if (buffer.start == nullptr) return;
- InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower, false);
+ InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower,
+ internal::wasm::kWasmOrigin);
}
} // namespace
@@ -260,7 +279,7 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
// Bind the WASM object.
Factory* factory = isolate->factory();
- Handle<String> name = v8_str(isolate, "_WASMEXP_");
+ Handle<String> name = v8_str(isolate, "Wasm");
Handle<JSFunction> cons = factory->NewFunction(name);
JSFunction::SetInstancePrototype(
cons, Handle<Object>(context->initial_object_prototype(), isolate));
@@ -280,10 +299,26 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) {
if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) {
- Handle<Map> wasm_function_map = isolate->factory()->NewMap(
- JS_FUNCTION_TYPE, JSFunction::kSize + kPointerSize);
- wasm_function_map->set_is_callable();
- context->set_wasm_function_map(*wasm_function_map);
+ // TODO(titzer): Move this to bootstrapper.cc??
+ // TODO(titzer): Also make one for strict mode functions?
+ Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);
+
+ InstanceType instance_type = prev_map->instance_type();
+ int internal_fields = JSObject::GetInternalFieldCount(*prev_map);
+ CHECK_EQ(0, internal_fields);
+ int pre_allocated =
+ prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
+ int instance_size;
+ int in_object_properties;
+ JSFunction::CalculateInstanceSizeHelper(instance_type, internal_fields + 1,
+ 0, &instance_size,
+ &in_object_properties);
+
+ int unused_property_fields = in_object_properties - pre_allocated;
+ Handle<Map> map = Map::CopyInitialMap(
+ prev_map, instance_size, in_object_properties, unused_property_fields);
+
+ context->set_wasm_function_map(*map);
}
}
diff --git a/deps/v8/src/wasm/wasm-macro-gen.h b/deps/v8/src/wasm/wasm-macro-gen.h
index dd653c1740..d9199e82fb 100644
--- a/deps/v8/src/wasm/wasm-macro-gen.h
+++ b/deps/v8/src/wasm/wasm-macro-gen.h
@@ -7,6 +7,50 @@
#include "src/wasm/wasm-opcodes.h"
+#define U32_LE(v) \
+ static_cast<byte>(v), static_cast<byte>((v) >> 8), \
+ static_cast<byte>((v) >> 16), static_cast<byte>((v) >> 24)
+
+#define U16_LE(v) static_cast<byte>(v), static_cast<byte>((v) >> 8)
+
+#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 NO_NAME U32V_1(0)
+#define NAME_LENGTH(v) U32V_1(v)
+
+#define ZERO_ALIGNMENT 0
+#define ZERO_OFFSET 0
+
+#define BR_TARGET(v) U32_LE(v)
+
+#define MASK_7 ((1 << 7) - 1)
+#define MASK_14 ((1 << 14) - 1)
+#define MASK_21 ((1 << 21) - 1)
+#define MASK_28 ((1 << 28) - 1)
+
+#define U32V_1(x) static_cast<byte>((x)&MASK_7)
+#define U32V_2(x) \
+ static_cast<byte>(((x)&MASK_7) | 0x80), static_cast<byte>(((x) >> 7) & MASK_7)
+#define U32V_3(x) \
+ static_cast<byte>((((x)) & MASK_7) | 0x80), \
+ static_cast<byte>((((x) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((x) >> 14) & MASK_7)
+#define U32V_4(x) \
+ static_cast<byte>(((x)&MASK_7) | 0x80), \
+ static_cast<byte>((((x) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>((((x) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>(((x) >> 21) & MASK_7)
+#define U32V_5(x) \
+ static_cast<byte>(((x)&MASK_7) | 0x80), \
+ static_cast<byte>((((x) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>((((x) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>((((x) >> 21) & MASK_7) | 0x80), \
+ static_cast<byte>((((x) >> 28) & MASK_7))
+
// Convenience macros for building Wasm bytecode directly into a byte array.
//------------------------------------------------------------------------------
@@ -33,14 +77,8 @@
#define WASM_RETURN(...) kExprReturn, __VA_ARGS__
#define WASM_UNREACHABLE kExprUnreachable
-#define WASM_TABLESWITCH_OP(case_count, table_count, ...) \
- kExprTableSwitch, static_cast<byte>(case_count), \
- static_cast<byte>(case_count >> 8), static_cast<byte>(table_count), \
- static_cast<byte>(table_count >> 8), __VA_ARGS__
-
-#define WASM_TABLESWITCH_BODY0(key) key
-
-#define WASM_TABLESWITCH_BODY(key, ...) key, __VA_ARGS__
+#define WASM_BR_TABLE(key, count, ...) \
+ kExprBrTable, U32V_1(count), __VA_ARGS__, key
#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)
@@ -52,18 +90,222 @@
#define WASM_ZERO kExprI8Const, 0
#define WASM_ONE kExprI8Const, 1
#define WASM_I8(val) kExprI8Const, static_cast<byte>(val)
-#define WASM_I32(val) \
- kExprI32Const, static_cast<byte>(val), static_cast<byte>(val >> 8), \
- static_cast<byte>(val >> 16), static_cast<byte>(val >> 24)
-#define WASM_I64(val) \
- kExprI64Const, static_cast<byte>(static_cast<uint64_t>(val)), \
- static_cast<byte>(static_cast<uint64_t>(val) >> 8), \
- static_cast<byte>(static_cast<uint64_t>(val) >> 16), \
- static_cast<byte>(static_cast<uint64_t>(val) >> 24), \
- static_cast<byte>(static_cast<uint64_t>(val) >> 32), \
- static_cast<byte>(static_cast<uint64_t>(val) >> 40), \
- static_cast<byte>(static_cast<uint64_t>(val) >> 48), \
- static_cast<byte>(static_cast<uint64_t>(val) >> 56)
+
+#define I32V_MIN(length) -(1 << (6 + (7 * ((length) - 1))))
+#define I32V_MAX(length) ((1 << (6 + (7 * ((length) - 1)))) - 1)
+#define I64V_MIN(length) -(1LL << (6 + (7 * ((length) - 1))))
+#define I64V_MAX(length) ((1LL << (6 + 7 * ((length) - 1))) - 1)
+
+#define I32V_IN_RANGE(value, length) \
+ ((value) >= I32V_MIN(length) && (value) <= I32V_MAX(length))
+#define I64V_IN_RANGE(value, length) \
+ ((value) >= I64V_MIN(length) && (value) <= I64V_MAX(length))
+
+#define WASM_NO_LOCALS 0
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+inline void CheckI32v(int32_t value, int length) {
+ DCHECK(length >= 1 && length <= 5);
+ DCHECK(length == 5 || I32V_IN_RANGE(value, length));
+}
+
+inline void CheckI64v(int64_t value, int length) {
+ DCHECK(length >= 1 && length <= 10);
+ DCHECK(length == 10 || I64V_IN_RANGE(value, length));
+}
+
+// A helper for encoding local declarations prepended to the body of a
+// function.
+class LocalDeclEncoder {
+ public:
+ // Prepend local declarations by creating a new buffer and copying data
+ // over. The new buffer must be delete[]'d by the caller.
+ void Prepend(const byte** start, const byte** end) const {
+ size_t size = (*end - *start);
+ byte* buffer = new byte[Size() + size];
+ size_t pos = Emit(buffer);
+ memcpy(buffer + pos, *start, size);
+ pos += size;
+ *start = buffer;
+ *end = buffer + pos;
+ }
+
+ size_t Emit(byte* buffer) const {
+ size_t pos = 0;
+ pos = WriteUint32v(buffer, pos, static_cast<uint32_t>(local_decls.size()));
+ for (size_t i = 0; i < local_decls.size(); i++) {
+ pos = WriteUint32v(buffer, pos, local_decls[i].first);
+ buffer[pos++] = WasmOpcodes::LocalTypeCodeFor(local_decls[i].second);
+ }
+ DCHECK_EQ(Size(), pos);
+ return pos;
+ }
+
+ // Add locals declarations to this helper. Return the index of the newly added
+ // local(s), with an optional adjustment for the parameters.
+ uint32_t AddLocals(uint32_t count, LocalType type,
+ FunctionSig* sig = nullptr) {
+ if (count == 0) {
+ return static_cast<uint32_t>((sig ? sig->parameter_count() : 0) +
+ local_decls.size());
+ }
+ size_t pos = local_decls.size();
+ if (local_decls.size() > 0 && local_decls.back().second == type) {
+ count += local_decls.back().first;
+ local_decls.pop_back();
+ }
+ local_decls.push_back(std::pair<uint32_t, LocalType>(count, type));
+ return static_cast<uint32_t>(pos + (sig ? sig->parameter_count() : 0));
+ }
+
+ size_t Size() const {
+ size_t size = SizeofUint32v(static_cast<uint32_t>(local_decls.size()));
+ for (auto p : local_decls) size += 1 + SizeofUint32v(p.first);
+ return size;
+ }
+
+ private:
+ std::vector<std::pair<uint32_t, LocalType>> local_decls;
+
+ size_t SizeofUint32v(uint32_t val) const {
+ size_t size = 1;
+ while (true) {
+ byte b = val & MASK_7;
+ if (b == val) return size;
+ size++;
+ val = val >> 7;
+ }
+ }
+
+ // TODO(titzer): lift encoding of u32v to a common place.
+ size_t WriteUint32v(byte* buffer, size_t pos, uint32_t val) const {
+ while (true) {
+ byte b = val & MASK_7;
+ if (b == val) {
+ buffer[pos++] = b;
+ break;
+ }
+ buffer[pos++] = 0x80 | b;
+ val = val >> 7;
+ }
+ return pos;
+ }
+};
+} // namespace wasm
+} // namespace internal
+} // namespace v8
+
+//------------------------------------------------------------------------------
+// Int32 Const operations
+//------------------------------------------------------------------------------
+#define WASM_I32V(val) kExprI32Const, U32V_5(val)
+
+#define WASM_I32V_1(val) \
+ static_cast<byte>(CheckI32v((val), 1), kExprI32Const), U32V_1(val)
+#define WASM_I32V_2(val) \
+ static_cast<byte>(CheckI32v((val), 2), kExprI32Const), U32V_2(val)
+#define WASM_I32V_3(val) \
+ static_cast<byte>(CheckI32v((val), 3), kExprI32Const), U32V_3(val)
+#define WASM_I32V_4(val) \
+ static_cast<byte>(CheckI32v((val), 4), kExprI32Const), U32V_4(val)
+#define WASM_I32V_5(val) \
+ static_cast<byte>(CheckI32v((val), 5), kExprI32Const), U32V_5(val)
+
+//------------------------------------------------------------------------------
+// Int64 Const operations
+//------------------------------------------------------------------------------
+#define WASM_I64V(val) \
+ kExprI64Const, \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 42) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 49) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 56) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 63) & MASK_7)
+
+#define WASM_I64V_1(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 1), kExprI64Const), \
+ static_cast<byte>(static_cast<int64_t>(val) & MASK_7)
+#define WASM_I64V_2(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 2), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 7) & MASK_7)
+#define WASM_I64V_3(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 3), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 14) & MASK_7)
+#define WASM_I64V_4(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 4), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 21) & MASK_7)
+#define WASM_I64V_5(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 5), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 28) & MASK_7)
+#define WASM_I64V_6(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 6), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 35) & MASK_7)
+#define WASM_I64V_7(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 7), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 42) & MASK_7)
+#define WASM_I64V_8(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 8), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 42) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 49) & MASK_7)
+#define WASM_I64V_9(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 9), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 42) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 49) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 56) & MASK_7)
+#define WASM_I64V_10(val) \
+ static_cast<byte>(CheckI64v(static_cast<int64_t>(val), 10), kExprI64Const), \
+ static_cast<byte>((static_cast<int64_t>(val) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 7) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 14) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 21) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 28) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 35) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 42) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 49) & MASK_7) | 0x80), \
+ static_cast<byte>(((static_cast<int64_t>(val) >> 56) & MASK_7) | 0x80), \
+ static_cast<byte>((static_cast<int64_t>(val) >> 63) & MASK_7)
+
#define WASM_F32(val) \
kExprF32Const, \
static_cast<byte>(bit_cast<int32_t>(static_cast<float>(val))), \
@@ -87,21 +329,19 @@
#define WASM_LOAD_MEM(type, index) \
static_cast<byte>( \
v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, false)), \
- v8::internal::wasm::WasmOpcodes::LoadStoreAccessOf(false), index
+ ZERO_ALIGNMENT, ZERO_OFFSET, index
#define WASM_STORE_MEM(type, index, val) \
static_cast<byte>( \
v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \
- v8::internal::wasm::WasmOpcodes::LoadStoreAccessOf(false), index, val
+ ZERO_ALIGNMENT, ZERO_OFFSET, index, val
#define WASM_LOAD_MEM_OFFSET(type, offset, index) \
static_cast<byte>( \
v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, false)), \
- v8::internal::wasm::WasmOpcodes::LoadStoreAccessOf(true), \
- static_cast<byte>(offset), index
+ ZERO_ALIGNMENT, U32V_1(offset), index
#define WASM_STORE_MEM_OFFSET(type, offset, index, val) \
static_cast<byte>( \
v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \
- v8::internal::wasm::WasmOpcodes::LoadStoreAccessOf(true), \
- static_cast<byte>(offset), index, val
+ ZERO_ALIGNMENT, U32V_1(offset), index, val
#define WASM_CALL_FUNCTION(index, ...) \
kExprCallFunction, static_cast<byte>(index), __VA_ARGS__
#define WASM_CALL_IMPORT(index, ...) \
@@ -112,7 +352,7 @@
#define WASM_CALL_IMPORT0(index) kExprCallImport, static_cast<byte>(index)
#define WASM_CALL_INDIRECT0(index, func) \
kExprCallIndirect, static_cast<byte>(index), func
-#define WASM_NOT(x) kExprBoolNot, x
+#define WASM_NOT(x) kExprI32Eqz, x
//------------------------------------------------------------------------------
// Constructs that are composed of multiple bytecodes.
@@ -144,6 +384,8 @@
#define WASM_I32_SHL(x, y) kExprI32Shl, x, y
#define WASM_I32_SHR(x, y) kExprI32ShrU, x, y
#define WASM_I32_SAR(x, y) kExprI32ShrS, x, y
+#define WASM_I32_ROR(x, y) kExprI32Ror, x, y
+#define WASM_I32_ROL(x, y) kExprI32Rol, x, y
#define WASM_I32_EQ(x, y) kExprI32Eq, x, y
#define WASM_I32_NE(x, y) kExprI32Ne, x, y
#define WASM_I32_LTS(x, y) kExprI32LtS, x, y
@@ -157,6 +399,7 @@
#define WASM_I32_CLZ(x) kExprI32Clz, x
#define WASM_I32_CTZ(x) kExprI32Ctz, x
#define WASM_I32_POPCNT(x) kExprI32Popcnt, x
+#define WASM_I32_EQZ(x) kExprI32Eqz, x
//------------------------------------------------------------------------------
// Int64 operations
@@ -174,6 +417,8 @@
#define WASM_I64_SHL(x, y) kExprI64Shl, x, y
#define WASM_I64_SHR(x, y) kExprI64ShrU, x, y
#define WASM_I64_SAR(x, y) kExprI64ShrS, x, y
+#define WASM_I64_ROR(x, y) kExprI64Ror, x, y
+#define WASM_I64_ROL(x, y) kExprI64Rol, x, y
#define WASM_I64_EQ(x, y) kExprI64Eq, x, y
#define WASM_I64_NE(x, y) kExprI64Ne, x, y
#define WASM_I64_LTS(x, y) kExprI64LtS, x, y
@@ -187,6 +432,7 @@
#define WASM_I64_CLZ(x) kExprI64Clz, x
#define WASM_I64_CTZ(x) kExprI64Ctz, x
#define WASM_I64_POPCNT(x) kExprI64Popcnt, x
+#define WASM_I64_EQZ(x) kExprI64Eqz, x
//------------------------------------------------------------------------------
// Float32 operations
diff --git a/deps/v8/src/wasm/wasm-module.cc b/deps/v8/src/wasm/wasm-module.cc
index 02d197c547..a1c2a7a3e1 100644
--- a/deps/v8/src/wasm/wasm-module.cc
+++ b/deps/v8/src/wasm/wasm-module.cc
@@ -19,13 +19,43 @@ namespace v8 {
namespace internal {
namespace wasm {
+static const char* wasmSections[] = {
+#define F(enumerator, string) string,
+ FOR_EACH_WASM_SECTION_TYPE(F)
+#undef F
+};
+
+static uint8_t wasmSectionsLengths[]{
+#define F(enumerator, string) sizeof(string) - 1,
+ FOR_EACH_WASM_SECTION_TYPE(F)
+#undef F
+};
+
+static_assert(sizeof(wasmSections) / sizeof(wasmSections[0]) ==
+ (size_t)WasmSection::Code::Max,
+ "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];
+}
+
std::ostream& operator<<(std::ostream& os, const WasmModule& module) {
os << "WASM module with ";
- os << (1 << module.min_mem_size_log2) << " min mem";
- os << (1 << module.max_mem_size_log2) << " max mem";
- if (module.functions) os << module.functions->size() << " functions";
- if (module.globals) os << module.functions->size() << " globals";
- if (module.data_segments) os << module.functions->size() << " data segments";
+ 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;
}
@@ -48,7 +78,9 @@ std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) {
os << "#" << pair.function_->func_index << ":";
if (pair.function_->name_offset > 0) {
if (pair.module_) {
- os << pair.module_->GetName(pair.function_->name_offset);
+ WasmName name = pair.module_->GetName(pair.function_->name_offset,
+ pair.function_->name_length);
+ os.write(name.name, name.length);
} else {
os << "+" << pair.function_->func_index;
}
@@ -91,15 +123,15 @@ class WasmLinker {
}
void Link(Handle<FixedArray> function_table,
- std::vector<uint16_t>* functions) {
+ std::vector<uint16_t>& functions) {
for (size_t i = 0; i < function_code_.size(); i++) {
LinkFunction(function_code_[i]);
}
- if (functions && !function_table.is_null()) {
- int table_size = static_cast<int>(functions->size());
+ if (!function_table.is_null()) {
+ int table_size = static_cast<int>(functions.size());
DCHECK_EQ(function_table->length(), table_size * 2);
for (int i = 0; i < table_size; i++) {
- function_table->set(i + table_size, *function_code_[functions->at(i)]);
+ function_table->set(i + table_size, *function_code_[functions[i]]);
}
}
}
@@ -151,11 +183,10 @@ const int kWasmModuleCodeTable = 1;
const int kWasmMemArrayBuffer = 2;
const int kWasmGlobalsArrayBuffer = 3;
-
-size_t AllocateGlobalsOffsets(std::vector<WasmGlobal>* globals) {
+size_t AllocateGlobalsOffsets(std::vector<WasmGlobal>& globals) {
uint32_t offset = 0;
- if (!globals) return 0;
- for (WasmGlobal& global : *globals) {
+ if (globals.size() == 0) return 0;
+ for (WasmGlobal& global : globals) {
byte size = WasmOpcodes::MemSize(global.type);
offset = (offset + size - 1) & ~(size - 1); // align
global.offset = offset;
@@ -166,8 +197,9 @@ size_t AllocateGlobalsOffsets(std::vector<WasmGlobal>* globals) {
void LoadDataSegments(WasmModule* module, byte* mem_addr, size_t mem_size) {
- for (const WasmDataSegment& segment : *module->data_segments) {
+ for (const WasmDataSegment& segment : module->data_segments) {
if (!segment.init) continue;
+ if (!segment.source_size) continue;
CHECK_LT(segment.dest_addr, mem_size);
CHECK_LE(segment.source_size, mem_size);
CHECK_LE(segment.dest_addr + segment.source_size, mem_size);
@@ -179,14 +211,13 @@ void LoadDataSegments(WasmModule* module, byte* mem_addr, size_t mem_size) {
Handle<FixedArray> BuildFunctionTable(Isolate* isolate, WasmModule* module) {
- if (!module->function_table || module->function_table->size() == 0) {
+ if (module->function_table.size() == 0) {
return Handle<FixedArray>::null();
}
- int table_size = static_cast<int>(module->function_table->size());
+ int table_size = static_cast<int>(module->function_table.size());
Handle<FixedArray> fixed = isolate->factory()->NewFixedArray(2 * table_size);
for (int i = 0; i < table_size; i++) {
- WasmFunction* function =
- &module->functions->at(module->function_table->at(i));
+ WasmFunction* function = &module->functions[module->function_table[i]];
fixed->set(i, Smi::FromInt(function->sig_index));
}
return fixed;
@@ -194,7 +225,7 @@ Handle<FixedArray> BuildFunctionTable(Isolate* isolate, WasmModule* module) {
Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size,
byte** backing_store) {
- if (size > (1 << WasmModule::kMaxMemSize)) {
+ if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) {
// TODO(titzer): lift restriction on maximum memory allocated here.
*backing_store = nullptr;
return Handle<JSArrayBuffer>::null();
@@ -236,12 +267,11 @@ bool AllocateMemory(ErrorThrower* thrower, Isolate* isolate,
DCHECK(instance->module);
DCHECK(instance->mem_buffer.is_null());
- if (instance->module->min_mem_size_log2 > WasmModule::kMaxMemSize) {
+ if (instance->module->min_mem_pages > WasmModule::kMaxMemPages) {
thrower->Error("Out of memory: wasm memory too large");
return false;
}
- instance->mem_size = static_cast<size_t>(1)
- << instance->module->min_mem_size_log2;
+ instance->mem_size = WasmModule::kPageSize * instance->module->min_mem_pages;
instance->mem_buffer =
NewArrayBuffer(isolate, instance->mem_size, &instance->mem_start);
if (!instance->mem_start) {
@@ -273,50 +303,75 @@ WasmModule::WasmModule()
: shared_isolate(nullptr),
module_start(nullptr),
module_end(nullptr),
- min_mem_size_log2(0),
- max_mem_size_log2(0),
+ min_mem_pages(0),
+ max_mem_pages(0),
mem_export(false),
mem_external(false),
start_function_index(-1),
- globals(nullptr),
- signatures(nullptr),
- functions(nullptr),
- data_segments(nullptr),
- function_table(nullptr),
- import_table(nullptr) {}
-
-WasmModule::~WasmModule() {
- if (globals) delete globals;
- if (signatures) delete signatures;
- if (functions) delete functions;
- if (data_segments) delete data_segments;
- if (function_table) delete function_table;
- if (import_table) delete import_table;
+ origin(kWasmOrigin) {}
+
+static MaybeHandle<JSFunction> ReportFFIError(ErrorThrower& thrower,
+ const char* error, uint32_t index,
+ wasm::WasmName module_name,
+ wasm::WasmName function_name) {
+ if (function_name.name) {
+ thrower.Error("Import #%d module=\"%.*s\" function=\"%.*s\" error: %s",
+ index, module_name.length, module_name.name,
+ function_name.length, function_name.name, error);
+ } else {
+ thrower.Error("Import #%d module=\"%.*s\" error: %s", index,
+ module_name.length, module_name.name, error);
+ }
+ thrower.Error("Import ");
+ return MaybeHandle<JSFunction>();
}
-static MaybeHandle<JSFunction> LookupFunction(ErrorThrower& thrower,
- Handle<JSObject> ffi,
- uint32_t index,
- Handle<String> name,
- const char* cstr) {
- if (!ffi.is_null()) {
- MaybeHandle<Object> result = Object::GetProperty(ffi, name);
- if (!result.is_null()) {
- Handle<Object> obj = result.ToHandleChecked();
- if (obj->IsJSFunction()) {
- return Handle<JSFunction>::cast(obj);
- } else {
- thrower.Error("FFI function #%d:%s is not a JSFunction.", index, cstr);
- return MaybeHandle<JSFunction>();
- }
- } else {
- thrower.Error("FFI function #%d:%s not found.", index, cstr);
- return MaybeHandle<JSFunction>();
+static MaybeHandle<JSFunction> LookupFunction(
+ ErrorThrower& thrower, Factory* factory, Handle<JSObject> ffi,
+ uint32_t index, wasm::WasmName module_name, wasm::WasmName function_name) {
+ if (ffi.is_null()) {
+ return ReportFFIError(thrower, "FFI is not an object", index, module_name,
+ function_name);
+ }
+
+ // Look up the module first.
+ Handle<String> name = factory->InternalizeUtf8String(
+ Vector<const char>(module_name.name, module_name.length));
+ MaybeHandle<Object> result = Object::GetProperty(ffi, 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.name) {
+ // Look up the function in the module.
+ Handle<String> name = factory->InternalizeUtf8String(
+ Vector<const char>(function_name.name, function_name.length));
+ MaybeHandle<Object> result = Object::GetProperty(module, name);
+ if (result.is_null()) {
+ return ReportFFIError(thrower, "function not found", index, module_name,
+ function_name);
}
+ function = result.ToHandleChecked();
} else {
- thrower.Error("FFI table is not an object.");
- return MaybeHandle<JSFunction>();
+ // No function specified. Use the "default export".
+ function = module;
+ }
+
+ if (!function->IsJSFunction()) {
+ return ReportFFIError(thrower, "not a function", index, module_name,
+ function_name);
}
+
+ return Handle<JSFunction>::cast(function);
}
// Instantiates a wasm module as a JSObject.
@@ -338,11 +393,10 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
JS_OBJECT_TYPE,
JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
WasmModuleInstance instance(this);
- std::vector<Handle<Code>> import_code;
instance.context = isolate->native_context();
instance.js_object = factory->NewJSObjectFromMap(map, TENURED);
Handle<FixedArray> code_table =
- factory->NewFixedArray(static_cast<int>(functions->size()), TENURED);
+ factory->NewFixedArray(static_cast<int>(functions.size()), TENURED);
instance.js_object->SetInternalField(kWasmModuleCodeTable, *code_table);
//-------------------------------------------------------------------------
@@ -359,13 +413,6 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
*instance.mem_buffer);
LoadDataSegments(this, instance.mem_start, instance.mem_size);
- if (mem_export) {
- // Export the memory as a named property.
- Handle<String> name = factory->InternalizeUtf8String("memory");
- JSObject::AddProperty(instance.js_object, name, instance.mem_buffer,
- READ_ONLY);
- }
-
//-------------------------------------------------------------------------
// Allocate the globals area if necessary.
//-------------------------------------------------------------------------
@@ -382,25 +429,27 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
//-------------------------------------------------------------------------
uint32_t index = 0;
instance.function_table = BuildFunctionTable(isolate, this);
- WasmLinker linker(isolate, functions->size());
+ WasmLinker linker(isolate, functions.size());
ModuleEnv module_env;
module_env.module = this;
module_env.instance = &instance;
module_env.linker = &linker;
- module_env.asm_js = false;
-
- if (import_table->size() > 0) {
- instance.import_code = &import_code;
- instance.import_code->reserve(import_table->size());
- for (const WasmImport& import : *import_table) {
- const char* cstr = GetName(import.function_name_offset);
- Handle<String> name = factory->InternalizeUtf8String(cstr);
- MaybeHandle<JSFunction> function =
- LookupFunction(thrower, ffi, index, name, cstr);
+ module_env.origin = origin;
+
+ if (import_table.size() > 0) {
+ instance.import_code.reserve(import_table.size());
+ for (const WasmImport& import : import_table) {
+ WasmName module_name =
+ GetNameOrNull(import.module_name_offset, import.module_name_length);
+ WasmName function_name = GetNameOrNull(import.function_name_offset,
+ import.function_name_length);
+ MaybeHandle<JSFunction> function = LookupFunction(
+ thrower, factory, ffi, index, module_name, function_name);
if (function.is_null()) return MaybeHandle<JSObject>();
Handle<Code> code = compiler::CompileWasmToJSWrapper(
- isolate, &module_env, function.ToHandleChecked(), import.sig, cstr);
- instance.import_code->push_back(code);
+ isolate, &module_env, function.ToHandleChecked(), import.sig,
+ module_name, function_name);
+ instance.import_code.push_back(code);
index++;
}
}
@@ -410,27 +459,32 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
//-------------------------------------------------------------------------
// First pass: compile each function and initialize the code table.
- index = 0;
- for (const WasmFunction& func : *functions) {
+ index = FLAG_skip_compiling_wasm_funcs;
+ while (index < functions.size()) {
+ const WasmFunction& func = functions[index];
if (thrower.error()) break;
DCHECK_EQ(index, func.func_index);
- const char* cstr = GetName(func.name_offset);
- Handle<String> name = factory->InternalizeUtf8String(cstr);
+ WasmName str = GetName(func.name_offset, func.name_length);
+ WasmName str_null = {nullptr, 0};
+ Handle<String> name = factory->InternalizeUtf8String(
+ Vector<const char>(str.name, str.length));
Handle<Code> code = Handle<Code>::null();
Handle<JSFunction> function = Handle<JSFunction>::null();
if (func.external) {
// Lookup external function in FFI object.
MaybeHandle<JSFunction> function =
- LookupFunction(thrower, ffi, index, name, cstr);
+ LookupFunction(thrower, factory, ffi, index, str, str_null);
if (function.is_null()) return MaybeHandle<JSObject>();
- code = compiler::CompileWasmToJSWrapper(
- isolate, &module_env, function.ToHandleChecked(), func.sig, cstr);
+ code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
+ function.ToHandleChecked(),
+ func.sig, str, str_null);
} else {
// Compile the function.
code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func);
if (code.is_null()) {
- thrower.Error("Compilation of #%d:%s failed.", index, cstr);
+ thrower.Error("Compilation of #%d:%.*s failed.", index, str.length,
+ str.name);
return MaybeHandle<JSObject>();
}
if (func.exported) {
@@ -455,6 +509,40 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
instance.js_object->SetInternalField(kWasmModuleFunctionTable,
Smi::FromInt(0));
+ //-------------------------------------------------------------------------
+ // Create and populate the exports object.
+ //-------------------------------------------------------------------------
+ if (export_table.size() > 0 || mem_export) {
+ index = 0;
+ // Create the "exports" object.
+ Handle<JSFunction> object_function = Handle<JSFunction>(
+ isolate->native_context()->object_function(), isolate);
+ Handle<JSObject> exports_object =
+ factory->NewJSObject(object_function, TENURED);
+ Handle<String> exports_name = factory->InternalizeUtf8String("exports");
+ JSObject::AddProperty(instance.js_object, exports_name, exports_object,
+ READ_ONLY);
+
+ // Compile wrappers and add them to the exports object.
+ for (const WasmExport& exp : export_table) {
+ if (thrower.error()) break;
+ WasmName str = GetName(exp.name_offset, exp.name_length);
+ Handle<String> name = factory->InternalizeUtf8String(
+ Vector<const char>(str.name, str.length));
+ Handle<Code> code = linker.GetFunctionCode(exp.func_index);
+ Handle<JSFunction> function = compiler::CompileJSToWasmWrapper(
+ isolate, &module_env, name, code, instance.js_object, exp.func_index);
+ JSObject::AddProperty(exports_object, name, function, READ_ONLY);
+ }
+
+ if (mem_export) {
+ // Export the memory as a named property.
+ Handle<String> name = factory->InternalizeUtf8String("memory");
+ JSObject::AddProperty(exports_object, name, instance.mem_buffer,
+ READ_ONLY);
+ }
+ }
+
// Run the start function if one was specified.
if (this->start_function_index >= 0) {
HandleScope scope(isolate);
@@ -480,18 +568,12 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
Handle<Code> ModuleEnv::GetFunctionCode(uint32_t index) {
DCHECK(IsValidFunction(index));
if (linker) return linker->GetFunctionCode(index);
- if (instance && instance->function_code) {
- return instance->function_code->at(index);
- }
- return Handle<Code>::null();
+ return instance ? instance->function_code[index] : Handle<Code>::null();
}
Handle<Code> ModuleEnv::GetImportCode(uint32_t index) {
DCHECK(IsValidImport(index));
- if (instance && instance->import_code) {
- return instance->import_code->at(index);
- }
- return Handle<Code>::null();
+ return instance ? instance->import_code[index] : Handle<Code>::null();
}
compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
@@ -499,7 +581,7 @@ compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
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.
- WasmFunction* function = &module->functions->at(index);
+ WasmFunction* function = &module->functions[index];
return GetWasmCallDescriptor(zone, function->sig);
}
@@ -507,12 +589,15 @@ compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end, bool asm_js) {
HandleScope scope(isolate);
- Zone zone;
+ Zone zone(isolate->allocator());
// Decode the module, but don't verify function bodies, since we'll
// be compiling them anyway.
- ModuleResult result =
- DecodeWasmModule(isolate, &zone, module_start, module_end, false, false);
+ ModuleResult result = DecodeWasmModule(isolate, &zone, module_start,
+ module_end, false, kWasmOrigin);
if (result.failed()) {
+ if (result.val) {
+ delete result.val;
+ }
// Module verification failed. throw.
std::ostringstream str;
str << "WASM.compileRun() failed: " << result;
@@ -546,18 +631,18 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module) {
instance.function_table = BuildFunctionTable(isolate, module);
// Create module environment.
- WasmLinker linker(isolate, module->functions->size());
+ WasmLinker linker(isolate, module->functions.size());
ModuleEnv module_env;
module_env.module = module;
module_env.instance = &instance;
module_env.linker = &linker;
- module_env.asm_js = false;
+ module_env.origin = module->origin;
// Compile all functions.
Handle<Code> main_code = Handle<Code>::null(); // record last code.
uint32_t index = 0;
int main_index = 0;
- for (const WasmFunction& func : *module->functions) {
+ for (const WasmFunction& func : module->functions) {
DCHECK_EQ(index, func.func_index);
if (!func.external) {
// Compile the function and install it in the code table.
diff --git a/deps/v8/src/wasm/wasm-module.h b/deps/v8/src/wasm/wasm-module.h
index 5f5777cebe..4e5aa78486 100644
--- a/deps/v8/src/wasm/wasm-module.h
+++ b/deps/v8/src/wasm/wasm-module.h
@@ -22,22 +22,81 @@ namespace wasm {
const size_t kMaxModuleSize = 1024 * 1024 * 1024;
const size_t kMaxFunctionSize = 128 * 1024;
const size_t kMaxStringSize = 256;
-
-enum WasmSectionDeclCode {
- kDeclMemory = 0x00,
- kDeclSignatures = 0x01,
- kDeclFunctions = 0x02,
- kDeclGlobals = 0x03,
- kDeclDataSegments = 0x04,
- kDeclFunctionTable = 0x05,
- kDeclEnd = 0x06,
- kDeclStartFunction = 0x07,
- kDeclImportTable = 0x08,
- kDeclWLL = 0x11,
+const uint32_t kWasmMagic = 0x6d736100;
+const uint32_t kWasmVersion = 0x0a;
+
+// 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(Memory, "memory") \
+ F(Signatures, "signatures") \
+ F(Functions, "functions") \
+ F(Globals, "globals") \
+ F(DataSegments, "data_segments") \
+ F(FunctionTable, "function_table") \
+ F(End, "end") \
+ F(StartFunction, "start_function") \
+ F(ImportTable, "import_table") \
+ F(ExportTable, "export_table") \
+ F(FunctionSignatures, "function_signatures") \
+ F(FunctionBodies, "function_bodies") \
+ F(Names, "names")
+
+// Contants for the above section types: {LEB128 length, characters...}.
+#define WASM_SECTION_MEMORY 6, 'm', 'e', 'm', 'o', 'r', 'y'
+#define WASM_SECTION_SIGNATURES \
+ 10, 's', 'i', 'g', 'n', 'a', 't', 'u', 'r', 'e', 's'
+#define WASM_SECTION_FUNCTIONS 9, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', 's'
+#define WASM_SECTION_GLOBALS 7, 'g', 'l', 'o', 'b', 'a', 'l', 's'
+#define WASM_SECTION_DATA_SEGMENTS \
+ 13, 'd', 'a', 't', 'a', '_', 's', 'e', 'g', 'm', 'e', 'n', 't', 's'
+#define WASM_SECTION_FUNCTION_TABLE \
+ 14, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', '_', 't', 'a', 'b', 'l', 'e'
+#define WASM_SECTION_END 3, 'e', 'n', 'd'
+#define WASM_SECTION_START_FUNCTION \
+ 14, 's', 't', 'a', 'r', 't', '_', 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n'
+#define WASM_SECTION_IMPORT_TABLE \
+ 12, 'i', 'm', 'p', 'o', 'r', 't', '_', 't', 'a', 'b', 'l', 'e'
+#define WASM_SECTION_EXPORT_TABLE \
+ 12, 'e', 'x', 'p', 'o', 'r', 't', '_', 't', 'a', 'b', 'l', 'e'
+#define WASM_SECTION_FUNCTION_SIGNATURES \
+ 19, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', '_', 's', 'i', 'g', 'n', 'a', \
+ 't', 'u', 'r', 'e', 's'
+#define WASM_SECTION_FUNCTION_BODIES \
+ 15, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', '_', 'b', 'o', 'd', 'i', 'e', 's'
+#define WASM_SECTION_NAMES 5, 'n', 'a', 'm', 'e', 's'
+
+// Constants for the above section headers' size (LEB128 + characters).
+#define WASM_SECTION_MEMORY_SIZE ((size_t)7)
+#define WASM_SECTION_SIGNATURES_SIZE ((size_t)11)
+#define WASM_SECTION_FUNCTIONS_SIZE ((size_t)10)
+#define WASM_SECTION_GLOBALS_SIZE ((size_t)8)
+#define WASM_SECTION_DATA_SEGMENTS_SIZE ((size_t)14)
+#define WASM_SECTION_FUNCTION_TABLE_SIZE ((size_t)15)
+#define WASM_SECTION_END_SIZE ((size_t)4)
+#define WASM_SECTION_START_FUNCTION_SIZE ((size_t)15)
+#define WASM_SECTION_IMPORT_TABLE_SIZE ((size_t)13)
+#define WASM_SECTION_EXPORT_TABLE_SIZE ((size_t)13)
+#define WASM_SECTION_FUNCTION_SIGNATURES_SIZE ((size_t)20)
+#define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)16)
+#define WASM_SECTION_NAMES_SIZE ((size_t)6)
+
+struct WasmSection {
+ enum class Code : uint32_t {
+#define F(enumerator, 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 size_t getNameLength(Code code);
};
-static const int kMaxModuleSectionCode = 0x11;
-
enum WasmFunctionDeclBit {
kDeclFunctionName = 0x01,
kDeclFunctionImport = 0x02,
@@ -47,15 +106,15 @@ enum WasmFunctionDeclBit {
// Constants for fixed-size elements within a module.
static const size_t kDeclMemorySize = 3;
-static const size_t kDeclGlobalSize = 6;
static const size_t kDeclDataSegmentSize = 13;
// Static representation of a WASM function.
struct WasmFunction {
FunctionSig* sig; // signature of the function.
uint32_t func_index; // index into the function table.
- uint16_t sig_index; // index into the signature table.
+ uint32_t sig_index; // index into the signature table.
uint32_t name_offset; // offset in the module bytes of the name, if any.
+ uint32_t name_length; // length in bytes of the name.
uint32_t code_start_offset; // offset in the module bytes of code start.
uint32_t code_end_offset; // offset in the module bytes of code end.
uint16_t local_i32_count; // number of i32 local variables.
@@ -69,14 +128,24 @@ struct WasmFunction {
// Static representation of an imported WASM function.
struct WasmImport {
FunctionSig* sig; // signature of the function.
- uint16_t sig_index; // index into the signature table.
+ 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.
};
// 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.
MachineType type; // type of the global.
uint32_t offset; // offset from beginning of globals area.
bool exported; // true if this global is exported.
@@ -90,35 +159,46 @@ struct WasmDataSegment {
bool init; // true if loaded upon instantiation.
};
+enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
+
// Static representation of a module.
struct WasmModule {
- static const uint8_t kMinMemSize = 12; // Minimum memory size = 4kb
- static const uint8_t kMaxMemSize = 30; // Maximum memory size = 1gb
+ static const uint32_t kPageSize = 0x10000; // Page size, 64kb.
+ static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb
+ static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb
Isolate* shared_isolate; // isolate for storing shared code.
const byte* module_start; // starting address for the module bytes.
const byte* module_end; // end address for the module bytes.
- uint8_t min_mem_size_log2; // minimum size of the memory (log base 2).
- uint8_t max_mem_size_log2; // maximum size of the memory (log base 2).
+ 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.
int start_function_index; // start function, if any.
+ ModuleOrigin origin; // origin of the module
- std::vector<WasmGlobal>* globals; // globals in this module.
- 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<uint16_t>* function_table; // function table.
- std::vector<WasmImport>* import_table; // import table.
+ std::vector<WasmGlobal> globals; // globals in this module.
+ 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<uint16_t> function_table; // function table.
+ std::vector<WasmImport> import_table; // import table.
+ std::vector<WasmExport> export_table; // export table.
WasmModule();
- ~WasmModule();
- // Get a pointer to a string stored in the module bytes representing a name.
- const char* GetName(uint32_t offset) const {
- if (offset == 0) return "<?>"; // no name.
- CHECK(BoundsCheck(offset, offset + 1));
- return reinterpret_cast<const char*>(module_start + offset);
+ // Get a string stored in the module bytes representing a name.
+ WasmName GetName(uint32_t offset, uint32_t length) const {
+ if (length == 0) return {"<?>", 3}; // no name.
+ CHECK(BoundsCheck(offset, offset + length));
+ return {reinterpret_cast<const char*>(module_start + offset), length};
+ }
+
+ // Get a string stored in the module bytes representing a name.
+ WasmName GetNameOrNull(uint32_t offset, uint32_t length) const {
+ if (length == 0) return {NULL, 0}; // no name.
+ CHECK(BoundsCheck(offset, offset + length));
+ return {reinterpret_cast<const char*>(module_start + offset), length};
}
// Checks the given offset range is contained within the module bytes.
@@ -141,8 +221,8 @@ struct WasmModuleInstance {
Handle<JSArrayBuffer> mem_buffer; // Handle to array buffer of memory.
Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals.
Handle<FixedArray> function_table; // indirect function table.
- std::vector<Handle<Code>>* function_code; // code objects for each function.
- std::vector<Handle<Code>>* import_code; // code objects for each import.
+ 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.
size_t mem_size; // size of the linear memory.
@@ -152,7 +232,6 @@ struct WasmModuleInstance {
explicit WasmModuleInstance(WasmModule* m)
: module(m),
- function_code(nullptr),
mem_start(nullptr),
mem_size(0),
globals_start(nullptr),
@@ -168,41 +247,42 @@ struct ModuleEnv {
WasmModule* module;
WasmModuleInstance* instance;
WasmLinker* linker;
- bool asm_js; // true if the module originated from asm.js.
+ ModuleOrigin origin;
bool IsValidGlobal(uint32_t index) {
- return module && index < module->globals->size();
+ return module && index < module->globals.size();
}
bool IsValidFunction(uint32_t index) {
- return module && index < module->functions->size();
+ return module && index < module->functions.size();
}
bool IsValidSignature(uint32_t index) {
- return module && index < module->signatures->size();
+ return module && index < module->signatures.size();
}
bool IsValidImport(uint32_t index) {
- return module && index < module->import_table->size();
+ return module && index < module->import_table.size();
}
MachineType GetGlobalType(uint32_t index) {
DCHECK(IsValidGlobal(index));
- return module->globals->at(index).type;
+ return module->globals[index].type;
}
FunctionSig* GetFunctionSignature(uint32_t index) {
DCHECK(IsValidFunction(index));
- return module->functions->at(index).sig;
+ return module->functions[index].sig;
}
FunctionSig* GetImportSignature(uint32_t index) {
DCHECK(IsValidImport(index));
- return module->import_table->at(index).sig;
+ return module->import_table[index].sig;
}
FunctionSig* GetSignature(uint32_t index) {
DCHECK(IsValidSignature(index));
- return module->signatures->at(index);
+ return module->signatures[index];
}
size_t FunctionTableSize() {
- return module && module->function_table ? module->function_table->size()
- : 0;
+ return module ? module->function_table.size() : 0;
}
+ bool asm_js() { return origin == kAsmJsOrigin; }
+
Handle<Code> GetFunctionCode(uint32_t index);
Handle<Code> GetImportCode(uint32_t index);
Handle<FixedArray> GetFunctionTable();
diff --git a/deps/v8/src/wasm/wasm-opcodes.cc b/deps/v8/src/wasm/wasm-opcodes.cc
index a609e03261..736c4d9609 100644
--- a/deps/v8/src/wasm/wasm-opcodes.cc
+++ b/deps/v8/src/wasm/wasm-opcodes.cc
@@ -66,6 +66,7 @@ static void InitSigTable() {
#define SET_SIG_TABLE(name, opcode, sig) \
kSimpleExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1;
FOREACH_SIMPLE_OPCODE(SET_SIG_TABLE);
+ FOREACH_ASMJS_COMPAT_OPCODE(SET_SIG_TABLE);
#undef SET_SIG_TABLE
}
@@ -103,6 +104,8 @@ bool WasmOpcodes::IsSupported(WasmOpcode opcode) {
case kExprI64Shl:
case kExprI64ShrU:
case kExprI64ShrS:
+ case kExprI64Ror:
+ case kExprI64Rol:
case kExprI64Eq:
case kExprI64Ne:
case kExprI64LtS:
diff --git a/deps/v8/src/wasm/wasm-opcodes.h b/deps/v8/src/wasm/wasm-opcodes.h
index 7cb9c00449..52f85aab0a 100644
--- a/deps/v8/src/wasm/wasm-opcodes.h
+++ b/deps/v8/src/wasm/wasm-opcodes.h
@@ -46,28 +46,14 @@ const LocalType kAstF64 = MachineRepresentation::kFloat64;
// We use kTagged here because kNone is already used by kAstStmt.
const LocalType kAstEnd = MachineRepresentation::kTagged;
-// Functionality related to encoding memory accesses.
-struct MemoryAccess {
- // Atomicity annotations for access to the memory and globals.
- enum Atomicity {
- kNone = 0, // non-atomic
- kSequential = 1, // sequential consistency
- kAcquire = 2, // acquire semantics
- kRelease = 3 // release semantics
- };
-
- // Alignment annotations for memory accesses.
- enum Alignment { kAligned = 0, kUnaligned = 1 };
-
- // Bitfields for the various annotations for memory accesses.
- typedef BitField<Alignment, 7, 1> AlignmentField;
- typedef BitField<Atomicity, 5, 2> AtomicityField;
- typedef BitField<bool, 4, 1> OffsetField;
-};
-
typedef Signature<LocalType> FunctionSig;
std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
+struct WasmName {
+ const char* name;
+ uint32_t length;
+};
+
// TODO(titzer): Renumber all the opcodes to fill in holes.
// Control expressions and blocks.
@@ -80,7 +66,7 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(Select, 0x05, _) \
V(Br, 0x06, _) \
V(BrIf, 0x07, _) \
- V(TableSwitch, 0x08, _) \
+ V(BrTable, 0x08, _) \
V(Return, 0x14, _) \
V(Unreachable, 0x15, _)
@@ -97,7 +83,8 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(StoreGlobal, 0x11, _) \
V(CallFunction, 0x12, _) \
V(CallIndirect, 0x13, _) \
- V(CallImport, 0x1F, _)
+ V(CallImport, 0x1F, _) \
+ V(DeclLocals, 0x1E, _)
// Load memory expressions.
#define FOREACH_LOAD_MEM_OPCODE(V) \
@@ -161,7 +148,7 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(I32Clz, 0x57, i_i) \
V(I32Ctz, 0x58, i_i) \
V(I32Popcnt, 0x59, i_i) \
- V(BoolNot, 0x5a, i_i) \
+ V(I32Eqz, 0x5a, i_i) \
V(I64Add, 0x5b, l_ll) \
V(I64Sub, 0x5c, l_ll) \
V(I64Mul, 0x5d, l_ll) \
@@ -188,6 +175,7 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(I64Clz, 0x72, l_l) \
V(I64Ctz, 0x73, l_l) \
V(I64Popcnt, 0x74, l_l) \
+ V(I64Eqz, 0xba, i_l) \
V(F32Add, 0x75, f_ff) \
V(F32Sub, 0x76, f_ff) \
V(F32Mul, 0x77, f_ff) \
@@ -252,7 +240,47 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
V(F64ConvertF32, 0xb2, d_f) \
V(F64ReinterpretI64, 0xb3, d_l) \
V(I32ReinterpretF32, 0xb4, i_f) \
- V(I64ReinterpretF64, 0xb5, l_d)
+ V(I64ReinterpretF64, 0xb5, l_d) \
+ V(I32Ror, 0xb6, i_ii) \
+ V(I32Rol, 0xb7, i_ii) \
+ V(I64Ror, 0xb8, l_ll) \
+ V(I64Rol, 0xb9, l_ll)
+
+// For compatibility with Asm.js.
+#define FOREACH_ASMJS_COMPAT_OPCODE(V) \
+ V(F64Acos, 0xc0, d_d) \
+ V(F64Asin, 0xc1, d_d) \
+ V(F64Atan, 0xc2, d_d) \
+ V(F64Cos, 0xc3, d_d) \
+ V(F64Sin, 0xc4, d_d) \
+ V(F64Tan, 0xc5, d_d) \
+ V(F64Exp, 0xc6, d_d) \
+ V(F64Log, 0xc7, d_d) \
+ V(F64Atan2, 0xc8, d_dd) \
+ V(F64Pow, 0xc9, d_dd) \
+ V(F64Mod, 0xca, d_dd)
+
+// TODO(titzer): sketch of asm-js compatibility bytecodes
+/* V(I32AsmjsDivS, 0xd0, i_ii) \ */
+/* V(I32AsmjsDivU, 0xd1, i_ii) \ */
+/* V(I32AsmjsRemS, 0xd2, i_ii) \ */
+/* V(I32AsmjsRemU, 0xd3, i_ii) \ */
+/* V(I32AsmjsLoad8S, 0xd4, i_i) \ */
+/* V(I32AsmjsLoad8U, 0xd5, i_i) \ */
+/* V(I32AsmjsLoad16S, 0xd6, i_i) \ */
+/* V(I32AsmjsLoad16U, 0xd7, i_i) \ */
+/* V(I32AsmjsLoad, 0xd8, i_i) \ */
+/* V(F32AsmjsLoad, 0xd9, f_i) \ */
+/* V(F64AsmjsLoad, 0xda, d_i) \ */
+/* V(I32AsmjsStore8, 0xdb, i_i) \ */
+/* V(I32AsmjsStore16, 0xdc, i_i) \ */
+/* V(I32AsmjsStore, 0xdd, i_ii) \ */
+/* V(F32AsmjsStore, 0xde, i_if) \ */
+/* V(F64AsmjsStore, 0xdf, i_id) \ */
+/* V(I32SAsmjsConvertF32, 0xe0, i_f) \ */
+/* V(I32UAsmjsConvertF32, 0xe1, i_f) \ */
+/* V(I32SAsmjsConvertF64, 0xe2, i_d) \ */
+/* V(I32SAsmjsConvertF64, 0xe3, i_d) */
// All opcodes.
#define FOREACH_OPCODE(V) \
@@ -261,7 +289,8 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
FOREACH_SIMPLE_OPCODE(V) \
FOREACH_STORE_MEM_OPCODE(V) \
FOREACH_LOAD_MEM_OPCODE(V) \
- FOREACH_MISC_MEM_OPCODE(V)
+ FOREACH_MISC_MEM_OPCODE(V) \
+ FOREACH_ASMJS_COMPAT_OPCODE(V)
// All signatures.
#define FOREACH_SIGNATURE(V) \
@@ -300,6 +329,19 @@ enum WasmOpcode {
#undef DECLARE_NAMED_ENUM
};
+// The reason for a trap.
+enum TrapReason {
+ kTrapUnreachable,
+ kTrapMemOutOfBounds,
+ kTrapDivByZero,
+ kTrapDivUnrepresentable,
+ kTrapRemByZero,
+ kTrapFloatUnrepresentable,
+ kTrapFuncInvalid,
+ kTrapFuncSigMismatch,
+ kTrapCount
+};
+
// A collection of opcode-related static methods.
class WasmOpcodes {
public:
@@ -428,10 +470,6 @@ class WasmOpcodes {
}
}
- static byte LoadStoreAccessOf(bool with_offset) {
- return MemoryAccess::OffsetField::encode(with_offset);
- }
-
static char ShortNameOf(LocalType type) {
switch (type) {
case kAstI32:
@@ -470,6 +508,29 @@ class WasmOpcodes {
return "<unknown>";
}
}
+
+ static const char* TrapReasonName(TrapReason reason) {
+ switch (reason) {
+ case kTrapUnreachable:
+ return "unreachable";
+ case kTrapMemOutOfBounds:
+ return "memory access out of bounds";
+ case kTrapDivByZero:
+ return "divide by zero";
+ case kTrapDivUnrepresentable:
+ return "divide result unrepresentable";
+ case kTrapRemByZero:
+ return "remainder by zero";
+ case kTrapFloatUnrepresentable:
+ return "integer result unrepresentable";
+ case kTrapFuncInvalid:
+ return "invalid function";
+ case kTrapFuncSigMismatch:
+ return "function signature mismatch";
+ default:
+ return "<?>";
+ }
+ }
};
} // namespace wasm
} // namespace internal