diff options
author | Michaël Zasso <targos@protonmail.com> | 2016-05-27 16:37:42 +0200 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2016-06-29 09:04:28 +0200 |
commit | 2cc29517966de7257a2f1b34c58c77225a21e05d (patch) | |
tree | 210bd177df2f06eec16e1e22edafdbcbffe66f8a /deps/v8/src/wasm | |
parent | bbf3838c70aaec1dd296fa75ae334fd1c7866df3 (diff) | |
download | node-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.cc | 463 | ||||
-rw-r--r-- | deps/v8/src/wasm/asm-wasm-builder.h | 4 | ||||
-rw-r--r-- | deps/v8/src/wasm/ast-decoder.cc | 655 | ||||
-rw-r--r-- | deps/v8/src/wasm/ast-decoder.h | 169 | ||||
-rw-r--r-- | deps/v8/src/wasm/decoder.h | 146 | ||||
-rw-r--r-- | deps/v8/src/wasm/encoder.cc | 297 | ||||
-rw-r--r-- | deps/v8/src/wasm/encoder.h | 14 | ||||
-rw-r--r-- | deps/v8/src/wasm/module-decoder.cc | 514 | ||||
-rw-r--r-- | deps/v8/src/wasm/module-decoder.h | 2 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-external-refs.h | 181 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-js.cc | 107 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-macro-gen.h | 300 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-module.cc | 293 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-module.h | 174 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-opcodes.cc | 3 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-opcodes.h | 117 |
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( + §ion_string_leb_length, §ion_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 |