diff options
author | Ali Ijaz Sheikh <ofrobots@google.com> | 2016-04-07 14:06:55 -0700 |
---|---|---|
committer | Ali Ijaz Sheikh <ofrobots@google.com> | 2016-04-14 10:03:39 -0700 |
commit | 52af5c4eebf4de8638aef0338bd826656312a02a (patch) | |
tree | 628dc9fb0b558c3a73a2160706fef368876fe548 /deps/v8/src/wasm | |
parent | 6e3e8acc7cc7ebd3d67db5ade1247b8b558efe09 (diff) | |
download | node-new-52af5c4eebf4de8638aef0338bd826656312a02a.tar.gz |
deps: upgrade V8 to 5.0.71.32
* Pick up the branch head for V8 5.0 stable [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/+/3c67831
[2] https://chromium.googlesource.com/chromium/src/base/trace_event/common/+/4b09207e447ae5bd34643b4c6321bee7b76d35f9
Ref: https://github.com/nodejs/node/pull/5945
PR-URL: https://github.com/nodejs/node/pull/6111
Reviewed-By: targos - Michaƫl Zasso <mic.besace@gmail.com>
Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: indutny - Fedor Indutny <fedor.indutny@gmail.com>
Diffstat (limited to 'deps/v8/src/wasm')
-rw-r--r-- | deps/v8/src/wasm/asm-wasm-builder.cc | 440 | ||||
-rw-r--r-- | deps/v8/src/wasm/asm-wasm-builder.h | 5 | ||||
-rw-r--r-- | deps/v8/src/wasm/ast-decoder.cc | 979 | ||||
-rw-r--r-- | deps/v8/src/wasm/ast-decoder.h | 213 | ||||
-rw-r--r-- | deps/v8/src/wasm/decoder.h | 213 | ||||
-rw-r--r-- | deps/v8/src/wasm/encoder.cc | 82 | ||||
-rw-r--r-- | deps/v8/src/wasm/encoder.h | 19 | ||||
-rw-r--r-- | deps/v8/src/wasm/module-decoder.cc | 195 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-js.cc | 172 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-macro-gen.h | 9 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-module.cc | 420 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-module.h | 95 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-opcodes.cc | 14 | ||||
-rw-r--r-- | deps/v8/src/wasm/wasm-opcodes.h | 8 |
14 files changed, 1845 insertions, 1019 deletions
diff --git a/deps/v8/src/wasm/asm-wasm-builder.cc b/deps/v8/src/wasm/asm-wasm-builder.cc index 30f84642f8..ee5427b174 100644 --- a/deps/v8/src/wasm/asm-wasm-builder.cc +++ b/deps/v8/src/wasm/asm-wasm-builder.cc @@ -27,7 +27,8 @@ namespace wasm { class AsmWasmBuilderImpl : public AstVisitor { public: - AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal) + AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, FunctionLiteral* literal, + Handle<Object> foreign) : local_variables_(HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity, ZoneAllocationPolicy(zone)), @@ -44,17 +45,23 @@ class AsmWasmBuilderImpl : public AstVisitor { literal_(literal), isolate_(isolate), zone_(zone), + foreign_(foreign), cache_(TypeCache::Get()), breakable_blocks_(zone), block_size_(0), - init_function_index(0) { + init_function_index_(0), + next_table_index_(0), + function_tables_(HashMap::PointersMatch, + ZoneHashMap::kDefaultHashMapCapacity, + ZoneAllocationPolicy(zone)), + imported_function_table_(this) { InitializeAstVisitor(isolate); } void InitializeInitFunction() { unsigned char init[] = "__init__"; - init_function_index = builder_->AddFunction(); - current_function_builder_ = builder_->FunctionAt(init_function_index); + 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); @@ -70,7 +77,7 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitFunctionDeclaration(FunctionDeclaration* decl) { DCHECK(!in_function_); - DCHECK(current_function_builder_ == nullptr); + DCHECK_NULL(current_function_builder_); uint16_t index = LookupOrInsertFunction(decl->proxy()->var()); current_function_builder_ = builder_->FunctionAt(index); in_function_ = true; @@ -103,11 +110,15 @@ class AsmWasmBuilderImpl : public AstVisitor { } } } - DCHECK(in_function_); - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false, - static_cast<byte>(stmt->statements()->length())); - RECURSE(VisitStatements(stmt->statements())); - DCHECK(block_size_ >= 0); + if (in_function_) { + BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, + false, + static_cast<byte>(stmt->statements()->length())); + RECURSE(VisitStatements(stmt->statements())); + DCHECK(block_size_ >= 0); + } else { + RECURSE(VisitStatements(stmt->statements())); + } } class BlockVisitor { @@ -162,7 +173,7 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitContinueStatement(ContinueStatement* stmt) { DCHECK(in_function_); - DCHECK(stmt->target() != NULL); + DCHECK_NOT_NULL(stmt->target()); int i = static_cast<int>(breakable_blocks_.size()) - 1; int block_distance = 0; for (; i >= 0; i--) { @@ -183,7 +194,7 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitBreakStatement(BreakStatement* stmt) { DCHECK(in_function_); - DCHECK(stmt->target() != NULL); + DCHECK_NOT_NULL(stmt->target()); int i = static_cast<int>(breakable_blocks_.size()) - 1; int block_distance = 0; for (; i >= 0; i--) { @@ -229,7 +240,7 @@ class AsmWasmBuilderImpl : public AstVisitor { void CompileCase(CaseClause* clause, uint16_t fall_through, VariableProxy* tag) { Literal* label = clause->label()->AsLiteral(); - DCHECK(label != nullptr); + DCHECK_NOT_NULL(label); block_size_++; current_function_builder_->Emit(kExprIf); current_function_builder_->Emit(kExprI32Ior); @@ -247,7 +258,7 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitSwitchStatement(SwitchStatement* stmt) { VariableProxy* tag = stmt->tag()->AsVariableProxy(); - DCHECK(tag != NULL); + DCHECK_NOT_NULL(tag); BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false, 0); uint16_t fall_through = current_function_builder_->AddLocal(kAstI32); @@ -332,20 +343,20 @@ class AsmWasmBuilderImpl : public AstVisitor { Scope* scope = expr->scope(); if (in_function_) { if (expr->bounds().lower->IsFunction()) { - Type::FunctionType* func_type = expr->bounds().lower->AsFunction(); + FunctionType* func_type = expr->bounds().lower->AsFunction(); LocalType return_type = TypeFrom(func_type->Result()); current_function_builder_->ReturnType(return_type); for (int i = 0; i < expr->parameter_count(); i++) { LocalType type = TypeFrom(func_type->Parameter(i)); - DCHECK(type != kAstStmt); + DCHECK_NE(kAstStmt, type); LookupOrInsertLocal(scope->parameter(i), type); } } else { UNREACHABLE(); } } - RECURSE(VisitDeclarations(scope->declarations())); RECURSE(VisitStatements(expr->body())); + RECURSE(VisitDeclarations(scope->declarations())); } void VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) { @@ -363,35 +374,27 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitVariableProxy(VariableProxy* expr) { if (in_function_) { Variable* var = expr->var(); - if (var->is_function()) { - DCHECK(!is_set_op_); - std::vector<uint8_t> index = - UnsignedLEB128From(LookupOrInsertFunction(var)); - current_function_builder_->EmitCode( - &index[0], static_cast<uint32_t>(index.size())); - } else { - if (is_set_op_) { - if (var->IsContextSlot()) { - current_function_builder_->Emit(kExprStoreGlobal); - } else { - current_function_builder_->Emit(kExprSetLocal); - } - is_set_op_ = false; + if (is_set_op_) { + if (var->IsContextSlot()) { + current_function_builder_->Emit(kExprStoreGlobal); } else { - if (var->IsContextSlot()) { - current_function_builder_->Emit(kExprLoadGlobal); - } else { - current_function_builder_->Emit(kExprGetLocal); - } + current_function_builder_->Emit(kExprSetLocal); } - LocalType var_type = TypeOf(expr); - DCHECK(var_type != kAstStmt); + is_set_op_ = false; + } else { if (var->IsContextSlot()) { - AddLeb128(LookupOrInsertGlobal(var, var_type), false); + current_function_builder_->Emit(kExprLoadGlobal); } else { - AddLeb128(LookupOrInsertLocal(var, var_type), true); + current_function_builder_->Emit(kExprGetLocal); } } + LocalType var_type = TypeOf(expr); + DCHECK_NE(kAstStmt, var_type); + if (var->IsContextSlot()) { + AddLeb128(LookupOrInsertGlobal(var, var_type), false); + } else { + AddLeb128(LookupOrInsertLocal(var, var_type), true); + } } } @@ -433,10 +436,10 @@ class AsmWasmBuilderImpl : public AstVisitor { ObjectLiteralProperty* prop = props->at(i); DCHECK(marking_exported); VariableProxy* expr = prop->value()->AsVariableProxy(); - DCHECK(expr != nullptr); + DCHECK_NOT_NULL(expr); Variable* var = expr->var(); Literal* name = prop->key()->AsLiteral(); - DCHECK(name != nullptr); + DCHECK_NOT_NULL(name); DCHECK(name->IsPropertyName()); const AstRawString* raw_name = name->AsRawPropertyName(); if (var->is_function()) { @@ -451,7 +454,7 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitArrayLiteral(ArrayLiteral* expr) { UNREACHABLE(); } void LoadInitFunction() { - current_function_builder_ = builder_->FunctionAt(init_function_index); + current_function_builder_ = builder_->FunctionAt(init_function_index_); in_function_ = true; } @@ -460,11 +463,155 @@ class AsmWasmBuilderImpl : public AstVisitor { current_function_builder_ = nullptr; } + void AddFunctionTable(VariableProxy* table, ArrayLiteral* funcs) { + FunctionType* func_type = + funcs->bounds().lower->AsArray()->Element()->AsFunction(); + LocalType return_type = TypeFrom(func_type->Result()); + FunctionSig::Builder sig(zone(), return_type == kAstStmt ? 0 : 1, + func_type->Arity()); + if (return_type != kAstStmt) { + sig.AddReturn(static_cast<LocalType>(return_type)); + } + for (int i = 0; i < func_type->Arity(); i++) { + sig.AddParam(TypeFrom(func_type->Parameter(i))); + } + uint16_t signature_index = builder_->AddSignature(sig.Build()); + InsertFunctionTable(table->var(), next_table_index_, signature_index); + next_table_index_ += funcs->values()->length(); + for (int i = 0; i < funcs->values()->length(); i++) { + VariableProxy* func = funcs->values()->at(i)->AsVariableProxy(); + DCHECK_NOT_NULL(func); + builder_->AddIndirectFunction(LookupOrInsertFunction(func->var())); + } + } + + struct FunctionTableIndices : public ZoneObject { + uint32_t start_index; + uint16_t signature_index; + }; + + void InsertFunctionTable(Variable* v, uint32_t start_index, + uint16_t signature_index) { + FunctionTableIndices* container = new (zone()) FunctionTableIndices(); + container->start_index = start_index; + container->signature_index = signature_index; + ZoneHashMap::Entry* entry = function_tables_.LookupOrInsert( + v, ComputePointerHash(v), ZoneAllocationPolicy(zone())); + entry->value = container; + } + + FunctionTableIndices* LookupFunctionTable(Variable* v) { + ZoneHashMap::Entry* entry = + function_tables_.Lookup(v, ComputePointerHash(v)); + DCHECK_NOT_NULL(entry); + return reinterpret_cast<FunctionTableIndices*>(entry->value); + } + + class ImportedFunctionTable { + private: + class ImportedFunctionIndices : public ZoneObject { + public: + const unsigned char* name_; + int name_length_; + WasmModuleBuilder::SignatureMap signature_to_index_; + + ImportedFunctionIndices(const unsigned char* name, int name_length, + Zone* zone) + : name_(name), name_length_(name_length), signature_to_index_(zone) {} + }; + ZoneHashMap table_; + AsmWasmBuilderImpl* builder_; + + public: + explicit ImportedFunctionTable(AsmWasmBuilderImpl* builder) + : table_(HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity, + ZoneAllocationPolicy(builder->zone())), + builder_(builder) {} + + void AddImport(Variable* v, const unsigned char* name, int name_length) { + ImportedFunctionIndices* indices = new (builder_->zone()) + ImportedFunctionIndices(name, name_length, builder_->zone()); + ZoneHashMap::Entry* entry = table_.LookupOrInsert( + v, ComputePointerHash(v), ZoneAllocationPolicy(builder_->zone())); + entry->value = indices; + } + + uint16_t GetFunctionIndex(Variable* v, FunctionSig* sig) { + ZoneHashMap::Entry* entry = table_.Lookup(v, ComputePointerHash(v)); + DCHECK_NOT_NULL(entry); + ImportedFunctionIndices* indices = + reinterpret_cast<ImportedFunctionIndices*>(entry->value); + WasmModuleBuilder::SignatureMap::iterator pos = + indices->signature_to_index_.find(sig); + if (pos != indices->signature_to_index_.end()) { + return pos->second; + } else { + uint16_t index = builder_->builder_->AddFunction(); + indices->signature_to_index_[sig] = index; + WasmFunctionBuilder* function = builder_->builder_->FunctionAt(index); + function->External(1); + function->SetName(indices->name_, indices->name_length_); + if (sig->return_count() > 0) { + function->ReturnType(sig->GetReturn()); + } + for (size_t i = 0; i < sig->parameter_count(); i++) { + function->AddParam(sig->GetParam(i)); + } + return index; + } + } + }; + void VisitAssignment(Assignment* expr) { bool in_init = false; if (!in_function_) { + BinaryOperation* binop = expr->value()->AsBinaryOperation(); + if (binop != nullptr) { + Property* prop = binop->left()->AsProperty(); + DCHECK_NOT_NULL(prop); + LoadInitFunction(); + is_set_op_ = true; + RECURSE(Visit(expr->target())); + DCHECK(!is_set_op_); + if (binop->op() == Token::MUL) { + DCHECK(binop->right()->IsLiteral()); + DCHECK_EQ(1.0, binop->right()->AsLiteral()->raw_value()->AsNumber()); + DCHECK(binop->right()->AsLiteral()->raw_value()->ContainsDot()); + VisitForeignVariable(true, prop); + } else if (binop->op() == Token::BIT_OR) { + DCHECK(binop->right()->IsLiteral()); + DCHECK_EQ(0.0, binop->right()->AsLiteral()->raw_value()->AsNumber()); + DCHECK(!binop->right()->AsLiteral()->raw_value()->ContainsDot()); + VisitForeignVariable(false, prop); + } else { + UNREACHABLE(); + } + 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()) { + VariableProxy* target = expr->target()->AsVariableProxy(); + DCHECK_NOT_NULL(target); + AddFunctionTable(target, funcs); + } return; } in_init = true; @@ -493,10 +640,59 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitThrow(Throw* expr) { UNREACHABLE(); } + void VisitForeignVariable(bool is_float, Property* expr) { + DCHECK(expr->obj()->AsVariableProxy()); + DCHECK(VariableLocation::PARAMETER == + expr->obj()->AsVariableProxy()->var()->location()); + DCHECK_EQ(1, expr->obj()->AsVariableProxy()->var()->index()); + Literal* key_literal = expr->key()->AsLiteral(); + DCHECK_NOT_NULL(key_literal); + if (!key_literal->value().is_null() && !foreign_.is_null() && + foreign_->IsObject()) { + Handle<Name> name = + i::Object::ToName(isolate_, key_literal->value()).ToHandleChecked(); + MaybeHandle<Object> maybe_value = i::Object::GetProperty(foreign_, name); + if (!maybe_value.is_null()) { + Handle<Object> value = maybe_value.ToHandleChecked(); + if (is_float) { + MaybeHandle<Object> maybe_nvalue = i::Object::ToNumber(value); + if (!maybe_nvalue.is_null()) { + Handle<Object> nvalue = maybe_nvalue.ToHandleChecked(); + if (nvalue->IsNumber()) { + double val = nvalue->Number(); + byte code[] = {WASM_F64(val)}; + current_function_builder_->EmitCode(code, sizeof(code)); + return; + } + } + } else { + MaybeHandle<Object> maybe_nvalue = + i::Object::ToInt32(isolate_, value); + if (!maybe_nvalue.is_null()) { + Handle<Object> nvalue = maybe_nvalue.ToHandleChecked(); + if (nvalue->IsNumber()) { + int32_t val = static_cast<int32_t>(nvalue->Number()); + byte code[] = {WASM_I32(val)}; + current_function_builder_->EmitCode(code, sizeof(code)); + return; + } + } + } + } + } + if (is_float) { + byte code[] = {WASM_F64(std::numeric_limits<double>::quiet_NaN())}; + current_function_builder_->EmitCode(code, sizeof(code)); + } else { + byte code[] = {WASM_I32(0)}; + current_function_builder_->EmitCode(code, sizeof(code)); + } + } + void VisitProperty(Property* expr) { Expression* obj = expr->obj(); - DCHECK(obj->bounds().lower == obj->bounds().upper); - TypeImpl<ZoneTypeConfig>* type = obj->bounds().lower; + DCHECK_EQ(obj->bounds().lower, obj->bounds().upper); + Type* type = obj->bounds().lower; MachineType mtype; int size; if (type->Is(cache_.kUint8Array)) { @@ -533,29 +729,38 @@ class AsmWasmBuilderImpl : public AstVisitor { WasmOpcodes::LoadStoreOpcodeOf(mtype, is_set_op_), WasmOpcodes::LoadStoreAccessOf(false)); is_set_op_ = false; - Literal* value = expr->key()->AsLiteral(); - if (value) { - DCHECK(value->raw_value()->IsNumber()); - DCHECK(kAstI32 == TypeOf(value)); - int val = static_cast<int>(value->raw_value()->AsNumber()); - byte code[] = {WASM_I32(val * size)}; - current_function_builder_->EmitCode(code, sizeof(code)); - return; - } - BinaryOperation* binop = expr->key()->AsBinaryOperation(); - if (binop) { - DCHECK(Token::SAR == binop->op()); - DCHECK(binop->right()->AsLiteral()->raw_value()->IsNumber()); - DCHECK(kAstI32 == TypeOf(binop->right()->AsLiteral())); - DCHECK(size == - 1 << static_cast<int>( - binop->right()->AsLiteral()->raw_value()->AsNumber())); - // Mask bottom bits to match asm.js behavior. - current_function_builder_->Emit(kExprI32And); - byte code[] = {WASM_I8(~(size - 1))}; - current_function_builder_->EmitCode(code, sizeof(code)); - RECURSE(Visit(binop->left())); + if (size == 1) { + // Allow more general expression in byte arrays than the spec + // strictly permits. + // Early versions of Emscripten emit HEAP8[HEAP32[..]|0] in + // places that strictly should be HEAP8[HEAP32[..]>>0]. + RECURSE(Visit(expr->key())); return; + } else { + Literal* value = expr->key()->AsLiteral(); + if (value) { + 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)}; + current_function_builder_->EmitCode(code, sizeof(code)); + return; + } + BinaryOperation* binop = expr->key()->AsBinaryOperation(); + if (binop) { + DCHECK_EQ(Token::SAR, binop->op()); + DCHECK(binop->right()->AsLiteral()->raw_value()->IsNumber()); + DCHECK(kAstI32 == TypeOf(binop->right()->AsLiteral())); + DCHECK_EQ(size, + 1 << static_cast<int>( + binop->right()->AsLiteral()->raw_value()->AsNumber())); + // Mask bottom bits to match asm.js behavior. + current_function_builder_->Emit(kExprI32And); + byte code[] = {WASM_I8(~(size - 1))}; + current_function_builder_->EmitCode(code, sizeof(code)); + RECURSE(Visit(binop->left())); + return; + } } UNREACHABLE(); } @@ -565,18 +770,54 @@ class AsmWasmBuilderImpl : public AstVisitor { switch (call_type) { case Call::OTHER_CALL: { DCHECK(in_function_); - current_function_builder_->Emit(kExprCallFunction); - RECURSE(Visit(expr->expression())); - ZoneList<Expression*>* args = expr->arguments(); - for (int i = 0; i < args->length(); ++i) { - Expression* arg = args->at(i); - RECURSE(Visit(arg)); + uint16_t index; + VariableProxy* vp = expr->expression()->AsVariableProxy(); + if (vp != nullptr && + Type::Any()->Is(vp->bounds().lower->AsFunction()->Result())) { + LocalType return_type = TypeOf(expr); + ZoneList<Expression*>* args = expr->arguments(); + FunctionSig::Builder sig(zone(), return_type == kAstStmt ? 0 : 1, + args->length()); + if (return_type != kAstStmt) { + sig.AddReturn(return_type); + } + for (int i = 0; i < args->length(); i++) { + sig.AddParam(TypeOf(args->at(i))); + } + index = + imported_function_table_.GetFunctionIndex(vp->var(), sig.Build()); + } else { + index = LookupOrInsertFunction(vp->var()); } + current_function_builder_->Emit(kExprCallFunction); + std::vector<uint8_t> index_arr = UnsignedLEB128From(index); + current_function_builder_->EmitCode( + &index_arr[0], static_cast<uint32_t>(index_arr.size())); + break; + } + case Call::KEYED_PROPERTY_CALL: { + DCHECK(in_function_); + Property* p = expr->expression()->AsProperty(); + DCHECK_NOT_NULL(p); + 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_->Emit(kExprI32Add); + byte code[] = {WASM_I32(indices->start_index)}; + current_function_builder_->EmitCode(code, sizeof(code)); + RECURSE(Visit(p->key())); break; } default: UNREACHABLE(); } + ZoneList<Expression*>* args = expr->arguments(); + for (int i = 0; i < args->length(); ++i) { + Expression* arg = args->at(i); + RECURSE(Visit(arg)); + } } void VisitCallNew(CallNew* expr) { UNREACHABLE(); } @@ -586,7 +827,7 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::NOT: { - DCHECK(TypeOf(expr->expression()) == kAstI32); + DCHECK_EQ(kAstI32, TypeOf(expr->expression())); current_function_builder_->Emit(kExprBoolNot); break; } @@ -600,7 +841,7 @@ class AsmWasmBuilderImpl : public AstVisitor { bool MatchIntBinaryOperation(BinaryOperation* expr, Token::Value op, int32_t val) { - DCHECK(expr->right() != nullptr); + DCHECK_NOT_NULL(expr->right()); if (expr->op() == op && expr->right()->IsLiteral() && TypeOf(expr) == kAstI32) { Literal* right = expr->right()->AsLiteral(); @@ -614,7 +855,7 @@ class AsmWasmBuilderImpl : public AstVisitor { bool MatchDoubleBinaryOperation(BinaryOperation* expr, Token::Value op, double val) { - DCHECK(expr->right() != nullptr); + DCHECK_NOT_NULL(expr->right()); if (expr->op() == op && expr->right()->IsLiteral() && TypeOf(expr) == kAstF64) { Literal* right = expr->right()->AsLiteral(); @@ -629,8 +870,9 @@ class AsmWasmBuilderImpl : public AstVisitor { enum ConvertOperation { kNone, kAsIs, kToInt, kToDouble }; ConvertOperation MatchOr(BinaryOperation* expr) { - if (MatchIntBinaryOperation(expr, Token::BIT_OR, 0)) { - return (TypeOf(expr->left()) == kAstI32) ? kAsIs : kToInt; + if (MatchIntBinaryOperation(expr, Token::BIT_OR, 0) && + (TypeOf(expr->left()) == kAstI32)) { + return kAsIs; } else { return kNone; } @@ -647,12 +889,12 @@ class AsmWasmBuilderImpl : public AstVisitor { ConvertOperation MatchXor(BinaryOperation* expr) { if (MatchIntBinaryOperation(expr, Token::BIT_XOR, 0xffffffff)) { - DCHECK(TypeOf(expr->left()) == kAstI32); - DCHECK(TypeOf(expr->right()) == kAstI32); + DCHECK_EQ(kAstI32, TypeOf(expr->left())); + DCHECK_EQ(kAstI32, TypeOf(expr->right())); BinaryOperation* op = expr->left()->AsBinaryOperation(); if (op != nullptr) { if (MatchIntBinaryOperation(op, Token::BIT_XOR, 0xffffffff)) { - DCHECK(TypeOf(op->right()) == kAstI32); + DCHECK_EQ(kAstI32, TypeOf(op->right())); if (TypeOf(op->left()) != kAstI32) { return kToInt; } else { @@ -666,7 +908,7 @@ class AsmWasmBuilderImpl : public AstVisitor { ConvertOperation MatchMul(BinaryOperation* expr) { if (MatchDoubleBinaryOperation(expr, Token::MUL, 1.0)) { - DCHECK(TypeOf(expr->right()) == kAstF64); + DCHECK_EQ(kAstF64, TypeOf(expr->right())); if (TypeOf(expr->left()) != kAstF64) { return kToDouble; } else { @@ -768,6 +1010,7 @@ class AsmWasmBuilderImpl : public AstVisitor { BINOP_CASE(Token::MUL, Mul, NON_SIGNED_BINOP, true); BINOP_CASE(Token::DIV, Div, SIGNED_BINOP, false); BINOP_CASE(Token::BIT_OR, Ior, NON_SIGNED_INT_BINOP, true); + BINOP_CASE(Token::BIT_AND, And, NON_SIGNED_INT_BINOP, true); BINOP_CASE(Token::BIT_XOR, Xor, NON_SIGNED_INT_BINOP, true); BINOP_CASE(Token::SHL, Shl, NON_SIGNED_INT_BINOP, true); BINOP_CASE(Token::SAR, ShrS, NON_SIGNED_INT_BINOP, true); @@ -786,6 +1029,10 @@ class AsmWasmBuilderImpl : public AstVisitor { } break; } + case Token::COMMA: { + current_function_builder_->EmitWithU8(kExprBlock, 2); + break; + } default: UNREACHABLE(); } @@ -879,8 +1126,8 @@ class AsmWasmBuilderImpl : public AstVisitor { } TypeIndex TypeIndexOf(Expression* expr) { - DCHECK(expr->bounds().lower == expr->bounds().upper); - TypeImpl<ZoneTypeConfig>* type = expr->bounds().lower; + DCHECK_EQ(expr->bounds().lower, expr->bounds().upper); + Type* type = expr->bounds().lower; if (type->Is(cache_.kAsmFixnum)) { return kFixnum; } else if (type->Is(cache_.kAsmSigned)) { @@ -929,17 +1176,14 @@ class AsmWasmBuilderImpl : public AstVisitor { void VisitDoExpression(DoExpression* expr) { UNREACHABLE(); } - void VisitRewritableAssignmentExpression( - RewritableAssignmentExpression* expr) { - UNREACHABLE(); - } + void VisitRewritableExpression(RewritableExpression* expr) { UNREACHABLE(); } struct IndexContainer : public ZoneObject { uint16_t index; }; uint16_t LookupOrInsertLocal(Variable* v, LocalType type) { - DCHECK(current_function_builder_ != nullptr); + DCHECK_NOT_NULL(current_function_builder_); ZoneHashMap::Entry* entry = local_variables_.Lookup(v, ComputePointerHash(v)); if (entry == nullptr) { @@ -974,7 +1218,7 @@ class AsmWasmBuilderImpl : public AstVisitor { } uint16_t LookupOrInsertFunction(Variable* v) { - DCHECK(builder_ != nullptr); + DCHECK_NOT_NULL(builder_); ZoneHashMap::Entry* entry = functions_.Lookup(v, ComputePointerHash(v)); if (entry == nullptr) { uint16_t index = builder_->AddFunction(); @@ -988,11 +1232,11 @@ class AsmWasmBuilderImpl : public AstVisitor { } LocalType TypeOf(Expression* expr) { - DCHECK(expr->bounds().lower == expr->bounds().upper); + DCHECK_EQ(expr->bounds().lower, expr->bounds().upper); return TypeFrom(expr->bounds().lower); } - LocalType TypeFrom(TypeImpl<ZoneTypeConfig>* type) { + LocalType TypeFrom(Type* type) { if (type->Is(cache_.kAsmInt)) { return kAstI32; } else if (type->Is(cache_.kAsmFloat)) { @@ -1017,10 +1261,14 @@ class AsmWasmBuilderImpl : public AstVisitor { FunctionLiteral* literal_; Isolate* isolate_; Zone* zone_; + Handle<Object> foreign_; TypeCache const& cache_; ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_; int block_size_; - uint16_t init_function_index; + uint16_t init_function_index_; + uint32_t next_table_index_; + ZoneHashMap function_tables_; + ImportedFunctionTable imported_function_table_; DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); @@ -1029,13 +1277,13 @@ class AsmWasmBuilderImpl : public AstVisitor { }; AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone, - FunctionLiteral* literal) - : isolate_(isolate), zone_(zone), literal_(literal) {} + FunctionLiteral* literal, Handle<Object> foreign) + : isolate_(isolate), zone_(zone), literal_(literal), foreign_(foreign) {} // 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_); + AsmWasmBuilderImpl impl(isolate_, zone_, literal_, foreign_); 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 cb568db77c..9b761f9040 100644 --- a/deps/v8/src/wasm/asm-wasm-builder.h +++ b/deps/v8/src/wasm/asm-wasm-builder.h @@ -6,6 +6,7 @@ #define V8_WASM_ASM_WASM_BUILDER_H_ #include "src/allocation.h" +#include "src/objects.h" #include "src/wasm/encoder.h" #include "src/zone.h" @@ -18,13 +19,15 @@ namespace wasm { class AsmWasmBuilder { public: - explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root); + explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root, + Handle<Object> foreign); WasmModuleIndex* Run(); private: Isolate* isolate_; Zone* zone_; FunctionLiteral* literal_; + Handle<Object> foreign_; }; } // namespace wasm } // namespace internal diff --git a/deps/v8/src/wasm/ast-decoder.cc b/deps/v8/src/wasm/ast-decoder.cc index ffb815771a..c97c781c12 100644 --- a/deps/v8/src/wasm/ast-decoder.cc +++ b/deps/v8/src/wasm/ast-decoder.cc @@ -5,6 +5,7 @@ #include "src/base/platform/elapsed-timer.h" #include "src/signature.h" +#include "src/bit-vector.h" #include "src/flags.h" #include "src/handles.h" #include "src/zone-containers.h" @@ -40,7 +41,6 @@ struct Tree { WasmOpcode opcode() const { return static_cast<WasmOpcode>(*pc); } }; - // A production represents an incomplete decoded tree in the LR decoder. struct Production { Tree* tree; // the root of the syntax tree. @@ -97,13 +97,278 @@ struct IfEnv { #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; + } + + byte ByteOperand(const byte* pc, const char* msg = "missing 1-byte operand") { + if ((pc + sizeof(byte)) >= limit_) { + error(pc, msg); + return 0; + } + return pc[1]; + } + + uint32_t Uint32Operand(const byte* pc) { + if ((pc + sizeof(uint32_t)) >= limit_) { + error(pc, "missing 4-byte operand"); + return 0; + } + return read_u32(pc + 1); + } + + uint64_t Uint64Operand(const byte* pc) { + if ((pc + sizeof(uint64_t)) >= limit_) { + error(pc, "missing 8-byte operand"); + return 0; + } + return read_u64(pc + 1); + } + + inline bool Validate(const byte* pc, LocalIndexOperand& operand) { + if (operand.index < function_env_->total_locals) { + operand.type = function_env_->GetLocalType(operand.index); + return true; + } + error(pc, pc + 1, "invalid local index"); + return false; + } + + 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; + operand.type = WasmOpcodes::LocalTypeFor(operand.machine_type); + return true; + } + error(pc, pc + 1, "invalid global index"); + return false; + } + + 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; + return true; + } + error(pc, pc + 1, "invalid function index"); + return false; + } + + 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); + return true; + } + error(pc, pc + 1, "invalid signature index"); + return false; + } + + 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; + return true; + } + error(pc, pc + 1, "invalid signature index"); + return false; + } + + inline bool Validate(const byte* pc, BreakDepthOperand& operand, + ZoneVector<Block>& blocks) { + if (operand.depth < blocks.size()) { + operand.target = &blocks[blocks.size() - operand.depth - 1]; + return true; + } + error(pc, pc + 1, "invalid break depth"); + return false; + } + + bool Validate(const byte* pc, TableSwitchOperand& 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; + } + } + } + return true; + } + + int OpcodeArity(const byte* pc) { +#define DECLARE_ARITY(name, ...) \ + static const LocalType kTypes_##name[] = {__VA_ARGS__}; \ + static const int kArity_##name = \ + static_cast<int>(arraysize(kTypes_##name) - 1); + + FOREACH_SIGNATURE(DECLARE_ARITY); +#undef DECLARE_ARITY + + switch (static_cast<WasmOpcode>(*pc)) { + case kExprI8Const: + case kExprI32Const: + case kExprI64Const: + case kExprF64Const: + case kExprF32Const: + case kExprGetLocal: + case kExprLoadGlobal: + case kExprNop: + case kExprUnreachable: + return 0; + + case kExprBr: + case kExprStoreGlobal: + case kExprSetLocal: + return 1; + + case kExprIf: + case kExprBrIf: + return 2; + case kExprIfElse: + case kExprSelect: + return 3; + + case kExprBlock: + case kExprLoop: { + BlockCountOperand operand(this, pc); + return operand.count; + } + + case kExprCallFunction: { + FunctionIndexOperand operand(this, pc); + return static_cast<int>( + function_env_->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()); + } + case kExprCallImport: { + ImportIndexOperand operand(this, pc); + return static_cast<int>( + function_env_->module->GetImportSignature(operand.index) + ->parameter_count()); + } + case kExprReturn: { + return static_cast<int>(function_env_->sig->return_count()); + } + case kExprTableSwitch: { + TableSwitchOperand operand(this, pc); + return 1 + operand.case_count; + } + +#define DECLARE_OPCODE_CASE(name, opcode, sig) \ + case kExpr##name: \ + return kArity_##sig; + + FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) + FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) + FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE) + FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE) +#undef DECLARE_OPCODE_CASE + } + UNREACHABLE(); + return 0; + } + + int OpcodeLength(const byte* pc) { + switch (static_cast<WasmOpcode>(*pc)) { +#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: + FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) + FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) +#undef DECLARE_OPCODE_CASE + { + MemoryAccessOperand operand(this, pc); + return 1 + operand.length; + } + case kExprBlock: + case kExprLoop: { + BlockCountOperand operand(this, pc); + return 1 + operand.length; + } + case kExprBr: + case kExprBrIf: { + BreakDepthOperand operand(this, pc); + return 1 + operand.length; + } + case kExprStoreGlobal: + case kExprLoadGlobal: { + GlobalIndexOperand operand(this, pc); + return 1 + operand.length; + } + + case kExprCallFunction: { + FunctionIndexOperand operand(this, pc); + return 1 + operand.length; + } + case kExprCallIndirect: { + SignatureIndexOperand operand(this, pc); + return 1 + operand.length; + } + case kExprCallImport: { + ImportIndexOperand operand(this, pc); + return 1 + operand.length; + } + + case kExprSetLocal: + case kExprGetLocal: { + LocalIndexOperand operand(this, pc); + return 1 + operand.length; + } + case kExprTableSwitch: { + TableSwitchOperand operand(this, pc); + return 1 + operand.length; + } + case kExprI8Const: + return 2; + case kExprI32Const: + case kExprF32Const: + return 5; + case kExprI64Const: + case kExprF64Const: + return 9; + + default: + return 1; + } + } +}; + + // A shift-reduce-parser strategy for decoding Wasm code that uses an explicit // shift-reduce strategy with multiple internal stacks. -class LR_WasmDecoder : public Decoder { +class LR_WasmDecoder : public WasmDecoder { public: LR_WasmDecoder(Zone* zone, TFBuilder* builder) - : Decoder(nullptr, nullptr), - zone_(zone), + : zone_(zone), builder_(builder), trees_(zone), stack_(zone), @@ -127,8 +392,7 @@ class LR_WasmDecoder : public Decoder { } base_ = base; - Reset(pc, end); - function_env_ = function_env; + Reset(function_env, pc, end); InitSsaEnv(); DecodeFunctionBody(); @@ -151,15 +415,20 @@ class LR_WasmDecoder : public Decoder { } 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(" - decoding took %0.3f ms\n", ms); + PrintF("wasm-decode ok (%0.3f ms)\n\n", ms); + } else { + TRACE("wasm-decode ok\n\n"); } - TRACE("wasm-decode ok\n\n"); } else { TRACE("wasm-error module+%-6d func+%d: %s\n\n", baserel(error_pc_), startrel(error_pc_), error_msg_.get()); } + return toResult(tree); } @@ -172,7 +441,6 @@ class LR_WasmDecoder : public Decoder { TreeResult result_; SsaEnv* ssa_env_; - FunctionEnv* function_env_; ZoneVector<Tree*> trees_; ZoneVector<Production> stack_; @@ -199,30 +467,30 @@ class LR_WasmDecoder : public Decoder { ssa_env->locals[pos++] = builder_->Param(i, sig->GetParam(i)); } // Initialize int32 locals. - if (function_env_->local_int32_count > 0) { + if (function_env_->local_i32_count > 0) { TFNode* zero = builder_->Int32Constant(0); - for (uint32_t i = 0; i < function_env_->local_int32_count; i++) { + for (uint32_t i = 0; i < function_env_->local_i32_count; i++) { ssa_env->locals[pos++] = zero; } } // Initialize int64 locals. - if (function_env_->local_int64_count > 0) { + if (function_env_->local_i64_count > 0) { TFNode* zero = builder_->Int64Constant(0); - for (uint32_t i = 0; i < function_env_->local_int64_count; i++) { + for (uint32_t i = 0; i < function_env_->local_i64_count; i++) { ssa_env->locals[pos++] = zero; } } // Initialize float32 locals. - if (function_env_->local_float32_count > 0) { + if (function_env_->local_f32_count > 0) { TFNode* zero = builder_->Float32Constant(0); - for (uint32_t i = 0; i < function_env_->local_float32_count; i++) { + for (uint32_t i = 0; i < function_env_->local_f32_count; i++) { ssa_env->locals[pos++] = zero; } } // Initialize float64 locals. - if (function_env_->local_float64_count > 0) { + if (function_env_->local_f64_count > 0) { TFNode* zero = builder_->Float64Constant(0); - for (uint32_t i = 0; i < function_env_->local_float64_count; i++) { + for (uint32_t i = 0; i < function_env_->local_f64_count; i++) { ssa_env->locals[pos++] = zero; } } @@ -329,25 +597,25 @@ class LR_WasmDecoder : public Decoder { Leaf(kAstStmt); break; case kExprBlock: { - int length = Operand<uint8_t>(pc_); - if (length < 1) { + BlockCountOperand operand(this, pc_); + if (operand.count < 1) { Leaf(kAstStmt); } else { - Shift(kAstEnd, length); + Shift(kAstEnd, operand.count); // The break environment is the outer environment. SsaEnv* break_env = ssa_env_; PushBlock(break_env); SetEnv("block:start", Steal(break_env)); } - len = 2; + len = 1 + operand.length; break; } case kExprLoop: { - int length = Operand<uint8_t>(pc_); - if (length < 1) { + BlockCountOperand operand(this, pc_); + if (operand.count < 1) { Leaf(kAstStmt); } else { - Shift(kAstEnd, length); + Shift(kAstEnd, operand.count); // The break environment is the outer environment. SsaEnv* break_env = ssa_env_; PushBlock(break_env); @@ -359,7 +627,7 @@ class LR_WasmDecoder : public Decoder { PushBlock(cont_env); blocks_.back().stack_depth = -1; // no production for inner block. } - len = 2; + len = 1 + operand.length; break; } case kExprIf: @@ -372,59 +640,27 @@ class LR_WasmDecoder : public Decoder { Shift(kAstStmt, 3); // Result type is typeof(x) in {c ? x : y}. break; case kExprBr: { - uint32_t depth = Operand<uint8_t>(pc_); - Shift(kAstEnd, 1); - if (depth >= blocks_.size()) { - error("improperly nested branch"); + BreakDepthOperand operand(this, pc_); + if (Validate(pc_, operand, blocks_)) { + Shift(kAstEnd, 1); } - len = 2; + len = 1 + operand.length; break; } case kExprBrIf: { - uint32_t depth = Operand<uint8_t>(pc_); - Shift(kAstStmt, 2); - if (depth >= blocks_.size()) { - error("improperly nested conditional branch"); + BreakDepthOperand operand(this, pc_); + if (Validate(pc_, operand, blocks_)) { + Shift(kAstStmt, 2); } - len = 2; + len = 1 + operand.length; break; } case kExprTableSwitch: { - if (!checkAvailable(5)) { - error("expected #tableswitch <cases> <table>, fell off end"); - break; - } - uint16_t case_count = *reinterpret_cast<const uint16_t*>(pc_ + 1); - uint16_t table_count = *reinterpret_cast<const uint16_t*>(pc_ + 3); - len = 5 + table_count * 2; - - if (table_count == 0) { - error("tableswitch with 0 entries"); - break; - } - - if (!checkAvailable(len)) { - error("expected #tableswitch <cases> <table>, fell off end"); - break; - } - - Shift(kAstEnd, 1 + case_count); - - // Verify table. - for (int i = 0; i < table_count; i++) { - uint16_t target = - *reinterpret_cast<const uint16_t*>(pc_ + 5 + i * 2); - if (target >= 0x8000) { - size_t depth = target - 0x8000; - if (depth > blocks_.size()) { - error(pc_ + 5 + i * 2, "improper branch in tableswitch"); - } - } else { - if (target >= case_count) { - error(pc_ + 5 + i * 2, "invalid case target in tableswitch"); - } - } + TableSwitchOperand operand(this, pc_); + if (Validate(pc_, operand, blocks_.size())) { + Shift(kAstEnd, 1 + operand.case_count); } + len = 1 + operand.length; break; } case kExprReturn: { @@ -445,59 +681,66 @@ class LR_WasmDecoder : public Decoder { break; } case kExprI8Const: { - int32_t value = Operand<int8_t>(pc_); - Leaf(kAstI32, BUILD(Int32Constant, value)); - len = 2; + ImmI8Operand operand(this, pc_); + Leaf(kAstI32, BUILD(Int32Constant, operand.value)); + len = 1 + operand.length; break; } case kExprI32Const: { - int32_t value = Operand<int32_t>(pc_); - Leaf(kAstI32, BUILD(Int32Constant, value)); - len = 5; + ImmI32Operand operand(this, pc_); + Leaf(kAstI32, BUILD(Int32Constant, operand.value)); + len = 1 + operand.length; break; } case kExprI64Const: { - int64_t value = Operand<int64_t>(pc_); - Leaf(kAstI64, BUILD(Int64Constant, value)); - len = 9; + ImmI64Operand operand(this, pc_); + Leaf(kAstI64, BUILD(Int64Constant, operand.value)); + len = 1 + operand.length; break; } case kExprF32Const: { - float value = Operand<float>(pc_); - Leaf(kAstF32, BUILD(Float32Constant, value)); - len = 5; + ImmF32Operand operand(this, pc_); + Leaf(kAstF32, BUILD(Float32Constant, operand.value)); + len = 1 + operand.length; break; } case kExprF64Const: { - double value = Operand<double>(pc_); - Leaf(kAstF64, BUILD(Float64Constant, value)); - len = 9; + ImmF64Operand operand(this, pc_); + Leaf(kAstF64, BUILD(Float64Constant, operand.value)); + len = 1 + operand.length; break; } case kExprGetLocal: { - uint32_t index; - LocalType type = LocalOperand(pc_, &index, &len); - TFNode* val = - build() && type != kAstStmt ? ssa_env_->locals[index] : nullptr; - Leaf(type, val); + LocalIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + TFNode* val = build() ? ssa_env_->locals[operand.index] : nullptr; + Leaf(operand.type, val); + } + len = 1 + operand.length; break; } case kExprSetLocal: { - uint32_t index; - LocalType type = LocalOperand(pc_, &index, &len); - Shift(type, 1); + LocalIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + Shift(operand.type, 1); + } + len = 1 + operand.length; break; } case kExprLoadGlobal: { - uint32_t index; - LocalType type = GlobalOperand(pc_, &index, &len); - Leaf(type, BUILD(LoadGlobal, index)); + GlobalIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + Leaf(operand.type, BUILD(LoadGlobal, operand.index)); + } + len = 1 + operand.length; break; } case kExprStoreGlobal: { - uint32_t index; - LocalType type = GlobalOperand(pc_, &index, &len); - Shift(type, 1); + GlobalIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + Shift(operand.type, 1); + } + len = 1 + operand.length; break; } case kExprI32LoadMem8S: @@ -546,27 +789,36 @@ class LR_WasmDecoder : public Decoder { Shift(kAstI32, 1); break; case kExprCallFunction: { - uint32_t unused; - FunctionSig* sig = FunctionSigOperand(pc_, &unused, &len); - if (sig) { - LocalType type = - sig->return_count() == 0 ? kAstStmt : sig->GetReturn(); - Shift(type, static_cast<int>(sig->parameter_count())); - } else { - Leaf(kAstI32); // error + FunctionIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + LocalType type = operand.sig->return_count() == 0 + ? kAstStmt + : operand.sig->GetReturn(); + Shift(type, static_cast<int>(operand.sig->parameter_count())); } + len = 1 + operand.length; break; } case kExprCallIndirect: { - uint32_t unused; - FunctionSig* sig = SigOperand(pc_, &unused, &len); - if (sig) { - LocalType type = - sig->return_count() == 0 ? kAstStmt : sig->GetReturn(); - Shift(type, static_cast<int>(1 + sig->parameter_count())); - } else { - Leaf(kAstI32); // error + SignatureIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + LocalType type = operand.sig->return_count() == 0 + ? kAstStmt + : operand.sig->GetReturn(); + Shift(type, static_cast<int>(1 + operand.sig->parameter_count())); } + len = 1 + operand.length; + break; + } + case kExprCallImport: { + ImportIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + LocalType type = operand.sig->return_count() == 0 + ? kAstStmt + : operand.sig->GetReturn(); + Shift(type, static_cast<int>(operand.sig->parameter_count())); + } + len = 1 + operand.length; break; } default: @@ -589,19 +841,15 @@ class LR_WasmDecoder : public Decoder { } int DecodeLoadMem(const byte* pc, LocalType type) { - int length = 2; - uint32_t offset; - MemoryAccessOperand(pc, &length, &offset); + MemoryAccessOperand operand(this, pc); Shift(type, 1); - return length; + return 1 + operand.length; } int DecodeStoreMem(const byte* pc, LocalType type) { - int length = 2; - uint32_t offset; - MemoryAccessOperand(pc, &length, &offset); + MemoryAccessOperand operand(this, pc); Shift(type, 2); - return length; + return 1 + operand.length; } void AddImplicitReturnAtEnd() { @@ -747,26 +995,26 @@ class LR_WasmDecoder : public Decoder { } case kExprSelect: { if (p->index == 1) { - // Condition done. - TypeCheckLast(p, kAstI32); - } else if (p->index == 2) { // True expression done. p->tree->type = p->last()->type; if (p->tree->type == kAstStmt) { error(p->pc(), p->tree->children[1]->pc, "select operand should be expression"); } - } else { + } else if (p->index == 2) { // False expression done. - DCHECK(p->done()); TypeCheckLast(p, p->tree->type); + } else { + // Condition done. + DCHECK(p->done()); + TypeCheckLast(p, kAstI32); if (build()) { TFNode* controls[2]; - builder_->Branch(p->tree->children[0]->node, &controls[0], + builder_->Branch(p->tree->children[2]->node, &controls[0], &controls[1]); TFNode* merge = builder_->Merge(2, controls); - TFNode* vals[2] = {p->tree->children[1]->node, - p->tree->children[2]->node}; + TFNode* vals[2] = {p->tree->children[0]->node, + p->tree->children[1]->node}; TFNode* phi = builder_->Phi(p->tree->type, 2, vals, merge); p->tree->node = phi; ssa_env_->control = merge; @@ -775,64 +1023,44 @@ class LR_WasmDecoder : public Decoder { break; } case kExprBr: { - uint32_t depth = Operand<uint8_t>(p->pc()); - if (depth >= blocks_.size()) { - error("improperly nested branch"); - break; - } - Block* block = &blocks_[blocks_.size() - depth - 1]; - ReduceBreakToExprBlock(p, block); + BreakDepthOperand operand(this, p->pc()); + CHECK(Validate(p->pc(), operand, blocks_)); + ReduceBreakToExprBlock(p, operand.target); break; } case kExprBrIf: { - if (p->index == 1) { + if (p->done()) { TypeCheckLast(p, kAstI32); - } else if (p->done()) { - uint32_t depth = Operand<uint8_t>(p->pc()); - if (depth >= blocks_.size()) { - error("improperly nested branch"); - break; - } - Block* block = &blocks_[blocks_.size() - depth - 1]; + BreakDepthOperand operand(this, p->pc()); + CHECK(Validate(p->pc(), operand, blocks_)); SsaEnv* fenv = ssa_env_; SsaEnv* tenv = Split(fenv); - BUILD(Branch, p->tree->children[0]->node, &tenv->control, + BUILD(Branch, p->tree->children[1]->node, &tenv->control, &fenv->control); ssa_env_ = tenv; - ReduceBreakToExprBlock(p, block); + ReduceBreakToExprBlock(p, operand.target, p->tree->children[0]); ssa_env_ = fenv; } break; } case kExprTableSwitch: { - uint16_t table_count = *reinterpret_cast<const uint16_t*>(p->pc() + 3); - if (table_count == 1) { - // Degenerate switch with only a default target. - if (p->index == 1) { - SsaEnv* break_env = ssa_env_; - PushBlock(break_env); - SetEnv("switch:default", Steal(break_env)); - } - if (p->done()) { - Block* block = &blocks_.back(); - // fall through to the end. - ReduceBreakToExprBlock(p, block); - SetEnv("switch:end", block->ssa_env); - blocks_.pop_back(); - } - break; - } - if (p->index == 1) { // Switch key finished. TypeCheckLast(p, kAstI32); + if (failed()) break; + + TableSwitchOperand operand(this, p->pc()); + DCHECK(Validate(p->pc(), operand, blocks_.size())); - TFNode* sw = BUILD(Switch, table_count, p->last()->node); + // Build the switch only if it has more than just a default target. + bool build_switch = operand.table_count > 1; + TFNode* sw = nullptr; + if (build_switch) + sw = BUILD(Switch, operand.table_count, p->last()->node); // Allocate environments for each case. - uint16_t case_count = *reinterpret_cast<const uint16_t*>(p->pc() + 1); - SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(case_count); - for (int i = 0; i < case_count; i++) { + SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(operand.case_count); + for (uint32_t i = 0; i < operand.case_count; i++) { case_envs[i] = UnreachableEnv(); } @@ -843,13 +1071,15 @@ class LR_WasmDecoder : public Decoder { ssa_env_ = copy; // Build the environments for each case based on the table. - const uint16_t* table = - reinterpret_cast<const uint16_t*>(p->pc() + 5); - for (int i = 0; i < table_count; i++) { - uint16_t target = table[i]; - SsaEnv* env = Split(copy); - env->control = (i == table_count - 1) ? BUILD(IfDefault, sw) - : BUILD(IfValue, i, sw); + for (uint32_t i = 0; i < operand.table_count; i++) { + uint16_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; @@ -860,25 +1090,21 @@ class LR_WasmDecoder : public Decoder { Goto(env, case_envs[target]); } } + } - // Switch to the environment for the first case. - SetEnv("switch:case", case_envs[0]); + 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 { - // Switch case finished. - if (p->done()) { - // Last case. Fall through to the end. - Block* block = &blocks_.back(); - 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 (ssa_env_->go()) Goto(ssa_env_, next); - SetEnv("switch:case", next); - } + // 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); } break; } @@ -898,12 +1124,11 @@ class LR_WasmDecoder : public Decoder { break; } case kExprSetLocal: { - int unused = 0; - uint32_t index; - LocalType type = LocalOperand(p->pc(), &index, &unused); + LocalIndexOperand operand(this, p->pc()); + CHECK(Validate(p->pc(), operand)); Tree* val = p->last(); - if (type == val->type) { - if (build()) ssa_env_->locals[index] = val->node; + if (operand.type == val->type) { + if (build()) ssa_env_->locals[operand.index] = val->node; p->tree->node = val->node; } else { error(p->pc(), val->pc, "Typecheck failed in SetLocal"); @@ -911,12 +1136,11 @@ class LR_WasmDecoder : public Decoder { break; } case kExprStoreGlobal: { - int unused = 0; - uint32_t index; - LocalType type = GlobalOperand(p->pc(), &index, &unused); + GlobalIndexOperand operand(this, p->pc()); + CHECK(Validate(p->pc(), operand)); Tree* val = p->last(); - if (type == val->type) { - BUILD(StoreGlobal, index, val->node); + if (operand.type == val->type) { + BUILD(StoreGlobal, operand.index, val->node); p->tree->node = val->node; } else { error(p->pc(), val->pc, "Typecheck failed in StoreGlobal"); @@ -985,34 +1209,29 @@ class LR_WasmDecoder : public Decoder { return; case kExprCallFunction: { - int len; - uint32_t index; - FunctionSig* sig = FunctionSigOperand(p->pc(), &index, &len); - if (!sig) break; + FunctionIndexOperand operand(this, p->pc()); + CHECK(Validate(p->pc(), operand)); if (p->index > 0) { - TypeCheckLast(p, sig->GetParam(p->index - 1)); + TypeCheckLast(p, operand.sig->GetParam(p->index - 1)); } if (p->done() && build()) { uint32_t count = p->tree->count + 1; TFNode** buffer = builder_->Buffer(count); - FunctionSig* sig = FunctionSigOperand(p->pc(), &index, &len); - USE(sig); buffer[0] = nullptr; // reserved for code object. for (uint32_t i = 1; i < count; i++) { buffer[i] = p->tree->children[i - 1]->node; } - p->tree->node = builder_->CallDirect(index, buffer); + p->tree->node = builder_->CallDirect(operand.index, buffer); } break; } case kExprCallIndirect: { - int len; - uint32_t index; - FunctionSig* sig = SigOperand(p->pc(), &index, &len); + SignatureIndexOperand operand(this, p->pc()); + CHECK(Validate(p->pc(), operand)); if (p->index == 1) { TypeCheckLast(p, kAstI32); } else { - TypeCheckLast(p, sig->GetParam(p->index - 2)); + TypeCheckLast(p, operand.sig->GetParam(p->index - 2)); } if (p->done() && build()) { uint32_t count = p->tree->count; @@ -1020,7 +1239,24 @@ class LR_WasmDecoder : public Decoder { for (uint32_t i = 0; i < count; i++) { buffer[i] = p->tree->children[i]->node; } - p->tree->node = builder_->CallIndirect(index, buffer); + p->tree->node = builder_->CallIndirect(operand.index, buffer); + } + break; + } + case kExprCallImport: { + ImportIndexOperand operand(this, p->pc()); + CHECK(Validate(p->pc(), operand)); + if (p->index > 0) { + TypeCheckLast(p, operand.sig->GetParam(p->index - 1)); + } + if (p->done() && build()) { + uint32_t count = p->tree->count + 1; + TFNode** buffer = builder_->Buffer(count); + buffer[0] = nullptr; // reserved for code object. + for (uint32_t i = 1; i < count; i++) { + buffer[i] = p->tree->children[i - 1]->node; + } + p->tree->node = builder_->CallImport(operand.index, buffer); } break; } @@ -1030,13 +1266,17 @@ class LR_WasmDecoder : public Decoder { } void ReduceBreakToExprBlock(Production* p, Block* block) { + ReduceBreakToExprBlock(p, block, p->tree->count > 0 ? p->last() : nullptr); + } + + void ReduceBreakToExprBlock(Production* p, Block* block, Tree* val) { if (block->stack_depth < 0) { // This is the inner loop block, which does not have a value. Goto(ssa_env_, block->ssa_env); } else { // Merge the value into the production for the block. Production* bp = &stack_[block->stack_depth]; - MergeIntoProduction(bp, block->ssa_env, p->last()); + MergeIntoProduction(bp, block->ssa_env, val); } } @@ -1045,7 +1285,7 @@ class LR_WasmDecoder : public Decoder { bool first = target->state == SsaEnv::kUnreachable; Goto(ssa_env_, target); - if (expr->type == kAstEnd) return; + if (expr == nullptr || expr->type == kAstEnd) return; if (first) { // first merge to this environment; set the type and the node. @@ -1069,11 +1309,9 @@ class LR_WasmDecoder : public Decoder { DCHECK_EQ(1, p->index); TypeCheckLast(p, kAstI32); // index if (build()) { - int length = 0; - uint32_t offset = 0; - MemoryAccessOperand(p->pc(), &length, &offset); + MemoryAccessOperand operand(this, p->pc()); p->tree->node = - builder_->LoadMem(type, mem_type, p->last()->node, offset); + builder_->LoadMem(type, mem_type, p->last()->node, operand.offset); } } @@ -1084,11 +1322,10 @@ class LR_WasmDecoder : public Decoder { DCHECK_EQ(2, p->index); TypeCheckLast(p, type); if (build()) { - int length = 0; - uint32_t offset = 0; - MemoryAccessOperand(p->pc(), &length, &offset); + MemoryAccessOperand operand(this, p->pc()); TFNode* val = p->tree->children[1]->node; - builder_->StoreMem(mem_type, p->tree->children[0]->node, offset, val); + builder_->StoreMem(mem_type, p->tree->children[0]->node, operand.offset, + val); p->tree->node = val; } } @@ -1111,7 +1348,7 @@ class LR_WasmDecoder : public Decoder { void SetEnv(const char* reason, SsaEnv* env) { TRACE(" env = %p, block depth = %d, reason = %s", static_cast<void*>(env), static_cast<int>(blocks_.size()), reason); - if (env->control != nullptr && FLAG_trace_wasm_decoder) { + if (FLAG_trace_wasm_decoder && env && env->control) { TRACE(", control = "); compiler::WasmGraphBuilder::PrintDebugName(env->control); } @@ -1286,94 +1523,11 @@ class LR_WasmDecoder : public Decoder { return result; } - // Load an operand at [pc + 1]. - template <typename V> - V Operand(const byte* pc) { - if ((limit_ - pc) < static_cast<int>(1 + sizeof(V))) { - const char* msg = "Expected operand following opcode"; - switch (sizeof(V)) { - case 1: - msg = "Expected 1-byte operand following opcode"; - break; - case 2: - msg = "Expected 2-byte operand following opcode"; - break; - case 4: - msg = "Expected 4-byte operand following opcode"; - break; - default: - break; - } - error(pc, msg); - return -1; - } - return *reinterpret_cast<const V*>(pc + 1); - } - int EnvironmentCount() { if (builder_) return static_cast<int>(function_env_->GetLocalCount()); return 0; // if we aren't building a graph, don't bother with SSA renaming. } - LocalType LocalOperand(const byte* pc, uint32_t* index, int* length) { - *index = UnsignedLEB128Operand(pc, length); - if (function_env_->IsValidLocal(*index)) { - return function_env_->GetLocalType(*index); - } - error(pc, "invalid local variable index"); - return kAstStmt; - } - - LocalType GlobalOperand(const byte* pc, uint32_t* index, int* length) { - *index = UnsignedLEB128Operand(pc, length); - if (function_env_->module->IsValidGlobal(*index)) { - return WasmOpcodes::LocalTypeFor( - function_env_->module->GetGlobalType(*index)); - } - error(pc, "invalid global variable index"); - return kAstStmt; - } - - FunctionSig* FunctionSigOperand(const byte* pc, uint32_t* index, - int* length) { - *index = UnsignedLEB128Operand(pc, length); - if (function_env_->module->IsValidFunction(*index)) { - return function_env_->module->GetFunctionSignature(*index); - } - error(pc, "invalid function index"); - return nullptr; - } - - FunctionSig* SigOperand(const byte* pc, uint32_t* index, int* length) { - *index = UnsignedLEB128Operand(pc, length); - if (function_env_->module->IsValidSignature(*index)) { - return function_env_->module->GetSignature(*index); - } - error(pc, "invalid signature index"); - return nullptr; - } - - uint32_t UnsignedLEB128Operand(const byte* pc, int* length) { - uint32_t result = 0; - ReadUnsignedLEB128ErrorCode error_code = - ReadUnsignedLEB128Operand(pc + 1, limit_, length, &result); - if (error_code == kInvalidLEB128) error(pc, "invalid LEB128 varint"); - if (error_code == kMissingLEB128) error(pc, "expected LEB128 varint"); - (*length)++; - return result; - } - - void MemoryAccessOperand(const byte* pc, int* length, uint32_t* offset) { - byte bitfield = Operand<uint8_t>(pc); - if (MemoryAccess::OffsetField::decode(bitfield)) { - *offset = UnsignedLEB128Operand(pc + 1, length); - (*length)++; // to account for the memory access byte - } else { - *offset = 0; - *length = 2; - } - } - virtual void onFirstError() { limit_ = start_; // Terminate decoding loop. builder_ = nullptr; // Don't build any more nodes. @@ -1447,137 +1601,114 @@ ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte* pc, const byte* limit, int* length, uint32_t* result) { - *result = 0; - const byte* ptr = pc; - const byte* end = pc + 5; // maximum 5 bytes. - if (end > limit) end = limit; - int shift = 0; - byte b = 0; - while (ptr < end) { - b = *ptr++; - *result = *result | ((b & 0x7F) << shift); - if ((b & 0x80) == 0) break; - shift += 7; - } - DCHECK_LE(ptr - pc, 5); - *length = static_cast<int>(ptr - pc); - if (ptr == end && (b & 0x80)) { - return kInvalidLEB128; - } else if (*length == 0) { - return kMissingLEB128; - } else { - return kNoError; - } + Decoder decoder(pc, limit); + *result = decoder.checked_read_u32v(pc, 0, length); + if (decoder.ok()) return kNoError; + return (limit - pc) > 1 ? kInvalidLEB128 : kMissingLEB128; } +int OpcodeLength(const byte* pc, const byte* end) { + WasmDecoder decoder(nullptr, pc, end); + return decoder.OpcodeLength(pc); +} -int OpcodeLength(const byte* pc) { - switch (static_cast<WasmOpcode>(*pc)) { -#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: - FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) -#undef DECLARE_OPCODE_CASE +int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end) { + WasmDecoder decoder(env, pc, end); + return decoder.OpcodeArity(pc); +} - case kExprI8Const: - case kExprBlock: - case kExprLoop: - case kExprBr: - case kExprBrIf: - return 2; - case kExprI32Const: - case kExprF32Const: - return 5; - case kExprI64Const: - case kExprF64Const: - return 9; - case kExprStoreGlobal: - case kExprSetLocal: - case kExprLoadGlobal: - case kExprCallFunction: - case kExprCallIndirect: - case kExprGetLocal: { - int length; - uint32_t result = 0; - ReadUnsignedLEB128Operand(pc + 1, pc + 6, &length, &result); - return 1 + length; - } - case kExprTableSwitch: { - uint16_t table_count = *reinterpret_cast<const uint16_t*>(pc + 3); - return 5 + table_count * 2; +void PrintAst(FunctionEnv* env, const byte* start, const byte* end) { + WasmDecoder decoder(env, start, end); + const byte* pc = start; + std::vector<int> arity_stack; + while (pc < end) { + int arity = decoder.OpcodeArity(pc); + size_t length = decoder.OpcodeLength(pc); + + for (auto arity : arity_stack) { + printf(" "); + USE(arity); } - default: - return 1; + WasmOpcode opcode = static_cast<WasmOpcode>(*pc); + printf("k%s,", WasmOpcodes::OpcodeName(opcode)); + + for (size_t i = 1; i < length; i++) { + printf(" 0x%02x,", pc[i]); + } + pc += length; + printf("\n"); + + arity_stack.push_back(arity); + while (arity_stack.back() == 0) { + arity_stack.pop_back(); + if (arity_stack.empty()) break; + arity_stack.back()--; + } } } +// 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; + } -int OpcodeArity(FunctionEnv* env, const byte* pc) { -#define DECLARE_ARITY(name, ...) \ - static const LocalType kTypes_##name[] = {__VA_ARGS__}; \ - static const int kArity_##name = \ - static_cast<int>(arraysize(kTypes_##name) - 1); + BitVector* Analyze(const byte* pc, const byte* limit) { + Decoder::Reset(pc, limit); + if (pc_ >= limit_) return nullptr; + if (*pc_ != kExprLoop) return nullptr; - FOREACH_SIGNATURE(DECLARE_ARITY); -#undef DECLARE_ARITY + 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_); - switch (static_cast<WasmOpcode>(*pc)) { - case kExprI8Const: - case kExprI32Const: - case kExprI64Const: - case kExprF64Const: - case kExprF32Const: - case kExprGetLocal: - case kExprLoadGlobal: - case kExprNop: - case kExprUnreachable: - return 0; + // 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_); + } - case kExprBr: - case kExprStoreGlobal: - case kExprSetLocal: - return 1; - - case kExprIf: - case kExprBrIf: - return 2; - case kExprIfElse: - case kExprSelect: - return 3; - case kExprBlock: - case kExprLoop: - return *(pc + 1); - - case kExprCallFunction: { - int index = *(pc + 1); - return static_cast<int>( - env->module->GetFunctionSignature(index)->parameter_count()); - } - case kExprCallIndirect: { - int index = *(pc + 1); - return 1 + static_cast<int>( - env->module->GetSignature(index)->parameter_count()); - } - case kExprReturn: - return static_cast<int>(env->sig->return_count()); - case kExprTableSwitch: { - uint16_t case_count = *reinterpret_cast<const uint16_t*>(pc + 1); - return 1 + case_count; + 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; + } -#define DECLARE_OPCODE_CASE(name, opcode, sig) \ - case kExpr##name: \ - return kArity_##sig; + private: + Zone* zone_; +}; - FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE) -#undef DECLARE_OPCODE_CASE - } - UNREACHABLE(); - return 0; + +BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env, + const byte* start, const byte* end) { + LoopAssignmentAnalyzer analyzer(zone, env); + return analyzer.Analyze(start, end); } + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/deps/v8/src/wasm/ast-decoder.h b/deps/v8/src/wasm/ast-decoder.h index 5b95ad9f87..465bacaab8 100644 --- a/deps/v8/src/wasm/ast-decoder.h +++ b/deps/v8/src/wasm/ast-decoder.h @@ -6,18 +6,181 @@ #define V8_WASM_AST_DECODER_H_ #include "src/signature.h" +#include "src/wasm/decoder.h" #include "src/wasm/wasm-opcodes.h" #include "src/wasm/wasm-result.h" namespace v8 { namespace internal { +class BitVector; // forward declaration + namespace compiler { // external declarations from compiler. class WasmGraphBuilder; } namespace wasm { +// Helpers for decoding different kinds of operands which follow bytecodes. +struct LocalIndexOperand { + uint32_t index; + LocalType type; + int length; + + inline LocalIndexOperand(Decoder* decoder, const byte* pc) { + index = decoder->checked_read_u32v(pc, 1, &length, "local index"); + type = kAstStmt; + } +}; + +struct ImmI8Operand { + int8_t value; + int length; + inline ImmI8Operand(Decoder* decoder, const byte* pc) { + value = bit_cast<int8_t>(decoder->checked_read_u8(pc, 1, "immi8")); + length = 1; + } +}; + +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; + } +}; + +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; + } +}; + +struct ImmF32Operand { + float value; + int length; + inline ImmF32Operand(Decoder* decoder, const byte* pc) { + value = bit_cast<float>(decoder->checked_read_u32(pc, 1, "immf32")); + length = 4; + } +}; + +struct ImmF64Operand { + double value; + int length; + inline ImmF64Operand(Decoder* decoder, const byte* pc) { + value = bit_cast<double>(decoder->checked_read_u64(pc, 1, "immf64")); + length = 8; + } +}; + +struct GlobalIndexOperand { + uint32_t index; + LocalType type; + MachineType machine_type; + int length; + + inline GlobalIndexOperand(Decoder* decoder, const byte* pc) { + index = decoder->checked_read_u32v(pc, 1, &length, "global index"); + type = kAstStmt; + machine_type = MachineType::None(); + } +}; + +struct Block; +struct BreakDepthOperand { + uint32_t depth; + Block* target; + int length; + inline BreakDepthOperand(Decoder* decoder, const byte* pc) { + depth = decoder->checked_read_u8(pc, 1, "break depth"); + length = 1; + target = nullptr; + } +}; + +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; + } +}; + +struct SignatureIndexOperand { + uint32_t index; + FunctionSig* sig; + int length; + inline SignatureIndexOperand(Decoder* decoder, const byte* pc) { + index = decoder->checked_read_u32v(pc, 1, &length, "signature index"); + sig = nullptr; + } +}; + +struct FunctionIndexOperand { + uint32_t index; + FunctionSig* sig; + int length; + inline FunctionIndexOperand(Decoder* decoder, const byte* pc) { + index = decoder->checked_read_u32v(pc, 1, &length, "function index"); + sig = nullptr; + } +}; + +struct ImportIndexOperand { + uint32_t index; + FunctionSig* sig; + int length; + inline ImportIndexOperand(Decoder* decoder, const byte* pc) { + index = decoder->checked_read_u32v(pc, 1, &length, "import index"); + sig = nullptr; + } +}; + +struct TableSwitchOperand { + uint32_t case_count; + 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; + } 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; + } +}; + +struct MemoryAccessOperand { + bool aligned; + 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; + } + } +}; + typedef compiler::WasmGraphBuilder TFBuilder; struct ModuleEnv; // forward declaration of module interface. @@ -26,56 +189,55 @@ struct ModuleEnv; // forward declaration of module interface. struct FunctionEnv { ModuleEnv* module; // module environment FunctionSig* sig; // signature of this function - uint32_t local_int32_count; // number of int32 locals - uint32_t local_int64_count; // number of int64 locals - uint32_t local_float32_count; // number of float32 locals - uint32_t local_float64_count; // number of float64 locals + 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 - bool IsValidLocal(uint32_t index) { return index < total_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_int32_count) return kAstI32; - index -= local_int32_count; - if (index < local_int64_count) return kAstI64; - index -= local_int64_count; - if (index < local_float32_count) return kAstF32; - index -= local_float32_count; - if (index < local_float64_count) return kAstF64; + 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_int32_count += count; + local_i32_count += count; break; case kAstI64: - local_int64_count += count; + local_i64_count += count; break; case kAstF32: - local_float32_count += count; + local_f32_count += count; break; case kAstF64: - local_float64_count += count; + local_f64_count += count; break; default: UNREACHABLE(); } total_locals += count; - DCHECK(total_locals == - (sig->parameter_count() + local_int32_count + local_int64_count + - local_float32_count + local_float64_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_int32_count + local_int64_count + local_float32_count + - local_float64_count; + local_i32_count + local_i64_count + local_f32_count + + local_f64_count; } }; @@ -89,6 +251,8 @@ TreeResult VerifyWasmCode(FunctionEnv* env, const byte* base, const byte* start, 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); @@ -104,11 +268,14 @@ enum ReadUnsignedLEB128ErrorCode { kNoError, kInvalidLEB128, kMissingLEB128 }; ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte*, const byte*, int*, uint32_t*); +BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env, + const byte* start, const byte* end); + // Computes the length of the opcode at the given address. -int OpcodeLength(const byte* pc); +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); +int OpcodeArity(FunctionEnv* env, 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 698919d6a0..0e88eda022 100644 --- a/deps/v8/src/wasm/decoder.h +++ b/deps/v8/src/wasm/decoder.h @@ -24,6 +24,12 @@ namespace wasm { #define TRACE(...) #endif +#if !(V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_ARM) +#define UNALIGNED_ACCESS_OK 1 +#else +#define UNALIGNED_ACCESS_OK 0 +#endif + // A helper utility to decode bytes, integers, fields, varints, etc, from // a buffer of bytes. class Decoder { @@ -32,107 +38,188 @@ class Decoder { : start_(start), pc_(start), limit_(end), + end_(end), error_pc_(nullptr), error_pt_(nullptr) {} virtual ~Decoder() {} + inline bool check(const byte* base, int offset, int length, const char* msg) { + DCHECK_GE(base, start_); + if ((base + offset + length) > limit_) { + error(base, base + offset, msg); + return false; + } + return true; + } + + // Reads a single 8-bit byte, reporting an error if out of bounds. + inline uint8_t checked_read_u8(const byte* base, int offset, + const char* msg = "expected 1 byte") { + return check(base, offset, 1, msg) ? base[offset] : 0; + } + + // Reads 16-bit word, reporting an error if out of bounds. + inline uint16_t checked_read_u16(const byte* base, int offset, + const char* msg = "expected 2 bytes") { + return check(base, offset, 2, msg) ? read_u16(base + offset) : 0; + } + + // Reads 32-bit word, reporting an error if out of bounds. + inline uint32_t checked_read_u32(const byte* base, int offset, + const char* msg = "expected 4 bytes") { + return check(base, offset, 4, msg) ? read_u32(base + offset) : 0; + } + + // Reads 64-bit word, reporting an error if out of bounds. + inline uint64_t checked_read_u64(const byte* base, int offset, + const char* msg = "expected 8 bytes") { + return check(base, offset, 8, msg) ? read_u64(base + offset) : 0; + } + + 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 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; + } + 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 result; + } + + // Reads a single 16-bit unsigned integer (little endian). + inline uint16_t read_u16(const byte* ptr) { + DCHECK(ptr >= start_ && (ptr + 2) <= end_); +#if V8_TARGET_LITTLE_ENDIAN && UNALIGNED_ACCESS_OK + return *reinterpret_cast<const uint16_t*>(ptr); +#else + uint16_t b0 = ptr[0]; + uint16_t b1 = ptr[1]; + return (b1 << 8) | b0; +#endif + } + + // Reads a single 32-bit unsigned integer (little endian). + inline uint32_t read_u32(const byte* ptr) { + DCHECK(ptr >= start_ && (ptr + 4) <= end_); +#if V8_TARGET_LITTLE_ENDIAN && UNALIGNED_ACCESS_OK + return *reinterpret_cast<const uint32_t*>(ptr); +#else + uint32_t b0 = ptr[0]; + uint32_t b1 = ptr[1]; + uint32_t b2 = ptr[2]; + uint32_t b3 = ptr[3]; + return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; +#endif + } + + // Reads a single 64-bit unsigned integer (little endian). + inline uint64_t read_u64(const byte* ptr) { + DCHECK(ptr >= start_ && (ptr + 8) <= end_); +#if V8_TARGET_LITTLE_ENDIAN && UNALIGNED_ACCESS_OK + return *reinterpret_cast<const uint64_t*>(ptr); +#else + uint32_t b0 = ptr[0]; + uint32_t b1 = ptr[1]; + uint32_t b2 = ptr[2]; + uint32_t b3 = ptr[3]; + uint32_t low = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; + uint32_t b4 = ptr[4]; + uint32_t b5 = ptr[5]; + uint32_t b6 = ptr[6]; + uint32_t b7 = ptr[7]; + uint64_t high = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; + return (high << 32) | low; +#endif + } + // Reads a 8-bit unsigned integer (byte) and advances {pc_}. - uint8_t u8(const char* name = nullptr) { + uint8_t consume_u8(const char* name = nullptr) { TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_), name ? name : "uint8_t"); if (checkAvailable(1)) { byte val = *(pc_++); TRACE("%02x = %d\n", val, val); return val; - } else { - error("expected 1 byte, but fell off end"); - return traceOffEnd<uint8_t>(); } + return traceOffEnd<uint8_t>(); } // Reads a 16-bit unsigned integer (little endian) and advances {pc_}. - uint16_t u16(const char* name = nullptr) { + uint16_t consume_u16(const char* name = nullptr) { TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_), name ? name : "uint16_t"); if (checkAvailable(2)) { -#ifdef V8_TARGET_LITTLE_ENDIAN - byte b0 = pc_[0]; - byte b1 = pc_[1]; -#else - byte b1 = pc_[0]; - byte b0 = pc_[1]; -#endif - uint16_t val = static_cast<uint16_t>(b1 << 8) | b0; + uint16_t val = read_u16(pc_); TRACE("%02x %02x = %d\n", pc_[0], pc_[1], val); pc_ += 2; return val; - } else { - error("expected 2 bytes, but fell off end"); - return traceOffEnd<uint16_t>(); } + return traceOffEnd<uint16_t>(); } // Reads a single 32-bit unsigned integer (little endian) and advances {pc_}. - uint32_t u32(const char* name = nullptr) { + uint32_t consume_u32(const char* name = nullptr) { TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_), name ? name : "uint32_t"); if (checkAvailable(4)) { -#ifdef V8_TARGET_LITTLE_ENDIAN - byte b0 = pc_[0]; - byte b1 = pc_[1]; - byte b2 = pc_[2]; - byte b3 = pc_[3]; -#else - byte b3 = pc_[0]; - byte b2 = pc_[1]; - byte b1 = pc_[2]; - byte b0 = pc_[3]; -#endif - uint32_t val = static_cast<uint32_t>(b3 << 24) | - static_cast<uint32_t>(b2 << 16) | - static_cast<uint32_t>(b1 << 8) | b0; + uint32_t val = read_u32(pc_); TRACE("%02x %02x %02x %02x = %u\n", pc_[0], pc_[1], pc_[2], pc_[3], val); pc_ += 4; return val; - } else { - error("expected 4 bytes, but fell off end"); - return traceOffEnd<uint32_t>(); } + return traceOffEnd<uint32_t>(); } // Reads a LEB128 variable-length 32-bit integer and advances {pc_}. - uint32_t u32v(int* length, const char* name = nullptr) { + uint32_t consume_u32v(int* length, const char* name = nullptr) { TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_), name ? name : "varint"); - if (!checkAvailable(1)) { - error("expected at least 1 byte, but fell off end"); - return traceOffEnd<uint32_t>(); - } - - const byte* pos = pc_; - const byte* end = pc_ + 5; - if (end > limit_) end = limit_; + if (checkAvailable(1)) { + const byte* pos = pc_; + const byte* end = pc_ + 5; + if (end > limit_) end = limit_; - uint32_t result = 0; - int shift = 0; - byte b = 0; - while (pc_ < end) { - b = *pc_++; - TRACE("%02x ", b); - result = result | ((b & 0x7F) << shift); - if ((b & 0x80) == 0) break; - shift += 7; - } + uint32_t result = 0; + int shift = 0; + byte b = 0; + while (pc_ < end) { + b = *pc_++; + TRACE("%02x ", b); + result = result | ((b & 0x7F) << shift); + if ((b & 0x80) == 0) break; + shift += 7; + } - *length = static_cast<int>(pc_ - pos); - if (pc_ == end && (b & 0x80)) { - error(pc_ - 1, "varint too large"); - } else { - TRACE("= %u\n", result); + *length = static_cast<int>(pc_ - pos); + if (pc_ == end && (b & 0x80)) { + error(pc_ - 1, "varint too large"); + } else { + TRACE("= %u\n", result); + } + return result; } - return result; + return traceOffEnd<uint32_t>(); } // Check that at least {size} bytes exist between {pc_} and {limit_}. @@ -145,6 +232,12 @@ 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); } @@ -208,6 +301,7 @@ class Decoder { start_ = start; pc_ = start; limit_ = end; + end_ = end; error_pc_ = nullptr; error_pt_ = nullptr; error_msg_.Reset(nullptr); @@ -220,6 +314,7 @@ class Decoder { const byte* start_; const byte* pc_; const byte* limit_; + const byte* end_; const byte* error_pc_; const byte* error_pt_; base::SmartArrayPointer<char> error_msg_; diff --git a/deps/v8/src/wasm/encoder.cc b/deps/v8/src/wasm/encoder.cc index d8d36338b1..d80a275338 100644 --- a/deps/v8/src/wasm/encoder.cc +++ b/deps/v8/src/wasm/encoder.cc @@ -30,13 +30,13 @@ void EmitUint8(byte** b, uint8_t x) { void EmitUint16(byte** b, uint16_t x) { - Memory::uint16_at(*b) = x; + WriteUnalignedUInt16(*b, x); *b += 2; } void EmitUint32(byte** b, uint32_t x) { - Memory::uint32_at(*b) = x; + WriteUnalignedUInt32(*b, x); *b += 4; } @@ -121,12 +121,6 @@ void WasmFunctionBuilder::EmitWithU8(WasmOpcode opcode, const byte immediate) { } -void WasmFunctionBuilder::EmitWithLocal(WasmOpcode opcode) { - body_.push_back(static_cast<byte>(opcode)); - local_indices_.push_back(static_cast<uint32_t>(body_.size()) - 1); -} - - uint32_t WasmFunctionBuilder::EmitEditableImmediate(const byte immediate) { body_.push_back(immediate); return static_cast<uint32_t>(body_.size()) - 1; @@ -202,44 +196,44 @@ WasmFunctionEncoder* WasmFunctionBuilder::Build(Zone* zone, void WasmFunctionBuilder::IndexVars(WasmFunctionEncoder* e, uint16_t* var_index) const { uint16_t param = 0; - uint16_t int32 = 0; - uint16_t int64 = 0; - uint16_t float32 = 0; - uint16_t float64 = 0; + uint16_t i32 = 0; + uint16_t i64 = 0; + uint16_t f32 = 0; + uint16_t f64 = 0; for (size_t i = 0; i < locals_.size(); i++) { if (locals_.at(i).param_) { param++; } else if (locals_.at(i).type_ == kAstI32) { - int32++; + i32++; } else if (locals_.at(i).type_ == kAstI64) { - int64++; + i64++; } else if (locals_.at(i).type_ == kAstF32) { - float32++; + f32++; } else if (locals_.at(i).type_ == kAstF64) { - float64++; + f64++; } } - e->local_int32_count_ = int32; - e->local_int64_count_ = int64; - e->local_float32_count_ = float32; - e->local_float64_count_ = float64; - float64 = param + int32 + int64 + float32; - float32 = param + int32 + int64; - int64 = param + int32; - int32 = param; + e->local_i32_count_ = i32; + e->local_i64_count_ = i64; + e->local_f32_count_ = f32; + e->local_f64_count_ = f64; + f64 = param + i32 + i64 + f32; + f32 = param + i32 + i64; + i64 = param + i32; + i32 = param; param = 0; for (size_t i = 0; i < locals_.size(); i++) { if (locals_.at(i).param_) { e->params_.push_back(locals_.at(i).type_); var_index[i] = param++; } else if (locals_.at(i).type_ == kAstI32) { - var_index[i] = int32++; + var_index[i] = i32++; } else if (locals_.at(i).type_ == kAstI64) { - var_index[i] = int64++; + var_index[i] = i64++; } else if (locals_.at(i).type_ == kAstF32) { - var_index[i] = float32++; + var_index[i] = f32++; } else if (locals_.at(i).type_ == kAstF64) { - var_index[i] = float64++; + var_index[i] = f64++; } } } @@ -269,7 +263,7 @@ uint32_t WasmFunctionEncoder::BodySize(void) const { uint32_t WasmFunctionEncoder::NameSize() const { - return exported_ ? static_cast<uint32_t>(name_.size()) : 0; + return HasName() ? static_cast<uint32_t>(name_.size()) : 0; } @@ -291,10 +285,10 @@ void WasmFunctionEncoder::Serialize(byte* buffer, byte** header, } if (HasLocals()) { - EmitUint16(header, local_int32_count_); - EmitUint16(header, local_int64_count_); - EmitUint16(header, local_float32_count_); - EmitUint16(header, local_float64_count_); + EmitUint16(header, local_i32_count_); + EmitUint16(header, local_i64_count_); + EmitUint16(header, local_f32_count_); + EmitUint16(header, local_f64_count_); } if (!external_) { @@ -370,21 +364,21 @@ void WasmModuleBuilder::AddDataSegment(WasmDataSegmentEncoder* data) { } -int WasmModuleBuilder::CompareFunctionSigs::operator()(FunctionSig* a, - FunctionSig* b) const { - if (a->return_count() < b->return_count()) return -1; - if (a->return_count() > b->return_count()) return 1; - if (a->parameter_count() < b->parameter_count()) return -1; - if (a->parameter_count() > b->parameter_count()) return 1; +bool WasmModuleBuilder::CompareFunctionSigs::operator()(FunctionSig* a, + FunctionSig* b) const { + if (a->return_count() < b->return_count()) return true; + if (a->return_count() > b->return_count()) return false; + if (a->parameter_count() < b->parameter_count()) return true; + if (a->parameter_count() > b->parameter_count()) return false; for (size_t r = 0; r < a->return_count(); r++) { - if (a->GetReturn(r) < b->GetReturn(r)) return -1; - if (a->GetReturn(r) > b->GetReturn(r)) return 1; + if (a->GetReturn(r) < b->GetReturn(r)) return true; + if (a->GetReturn(r) > b->GetReturn(r)) return false; } for (size_t p = 0; p < a->parameter_count(); p++) { - if (a->GetParam(p) < b->GetParam(p)) return -1; - if (a->GetParam(p) > b->GetParam(p)) return 1; + if (a->GetParam(p) < b->GetParam(p)) return true; + if (a->GetParam(p) > b->GetParam(p)) return false; } - return 0; + return false; } diff --git a/deps/v8/src/wasm/encoder.h b/deps/v8/src/wasm/encoder.h index f0fabe998a..7b651bf95e 100644 --- a/deps/v8/src/wasm/encoder.h +++ b/deps/v8/src/wasm/encoder.h @@ -33,21 +33,21 @@ class WasmFunctionEncoder : public ZoneObject { friend class WasmFunctionBuilder; uint16_t signature_index_; ZoneVector<LocalType> params_; - uint16_t local_int32_count_; - uint16_t local_int64_count_; - uint16_t local_float32_count_; - uint16_t local_float64_count_; + uint16_t local_i32_count_; + uint16_t local_i64_count_; + uint16_t local_f32_count_; + uint16_t local_f64_count_; bool exported_; bool external_; ZoneVector<uint8_t> body_; ZoneVector<char> name_; bool HasLocals() const { - return (local_int32_count_ + local_int64_count_ + local_float32_count_ + - local_float64_count_) > 0; + return (local_i32_count_ + local_i64_count_ + local_f32_count_ + + local_f64_count_) > 0; } - bool HasName() const { return exported_ && name_.size() > 0; } + bool HasName() const { return (exported_ || external_) && name_.size() > 0; } }; class WasmFunctionBuilder : public ZoneObject { @@ -60,7 +60,6 @@ class WasmFunctionBuilder : public ZoneObject { const uint32_t* local_indices, uint32_t indices_size); void Emit(WasmOpcode opcode); void EmitWithU8(WasmOpcode opcode, const byte immediate); - void EmitWithLocal(WasmOpcode opcode); uint32_t EmitEditableImmediate(const byte immediate); void EditImmediate(uint32_t offset, const byte immediate); void Exported(uint8_t flag); @@ -134,12 +133,12 @@ class WasmModuleBuilder : public ZoneObject { void AddIndirectFunction(uint16_t index); WasmModuleWriter* Build(Zone* zone); - private: struct CompareFunctionSigs { - int operator()(FunctionSig* a, FunctionSig* b) const; + bool operator()(FunctionSig* a, FunctionSig* b) const; }; typedef ZoneMap<FunctionSig*, uint16_t, CompareFunctionSigs> SignatureMap; + private: Zone* zone_; ZoneVector<FunctionSig*> signatures_; ZoneVector<WasmFunctionBuilder*> functions_; diff --git a/deps/v8/src/wasm/module-decoder.cc b/deps/v8/src/wasm/module-decoder.cc index 24f39822f9..62b000da2b 100644 --- a/deps/v8/src/wasm/module-decoder.cc +++ b/deps/v8/src/wasm/module-decoder.cc @@ -54,6 +54,7 @@ class ModuleDecoder : public Decoder { 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>(); bool sections[kMaxModuleSectionCode]; memset(sections, 0, sizeof(sections)); @@ -62,7 +63,7 @@ class ModuleDecoder : public Decoder { while (pc_ < limit_) { TRACE("DecodeSection\n"); WasmSectionDeclCode section = - static_cast<WasmSectionDeclCode>(u8("section")); + static_cast<WasmSectionDeclCode>(consume_u8("section")); // Each section should appear at most once. if (section < kMaxModuleSectionCode) { CheckForPreviousSection(sections, section, false); @@ -75,20 +76,20 @@ class ModuleDecoder : public Decoder { limit_ = pc_; break; case kDeclMemory: - module->min_mem_size_log2 = u8("min memory"); - module->max_mem_size_log2 = u8("max memory"); - module->mem_export = u8("export memory") != 0; + module->min_mem_size_log2 = consume_u8("min memory"); + module->max_mem_size_log2 = consume_u8("max memory"); + module->mem_export = consume_u8("export memory") != 0; break; case kDeclSignatures: { int length; - uint32_t signatures_count = u32v(&length, "signatures count"); + uint32_t signatures_count = consume_u32v(&length, "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 = sig(); // read function sig. + FunctionSig* s = consume_sig(); // read function sig. module->signatures->push_back(s); } break; @@ -97,15 +98,12 @@ class ModuleDecoder : public Decoder { // Functions require a signature table first. CheckForPreviousSection(sections, kDeclSignatures, true); int length; - uint32_t functions_count = u32v(&length, "functions count"); + 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.globals_area = 0; - menv.mem_start = 0; - menv.mem_end = 0; - menv.function_code = nullptr; + menv.instance = nullptr; menv.asm_js = asm_js_; // Decode functions. for (uint32_t i = 0; i < functions_count; i++) { @@ -114,7 +112,7 @@ class ModuleDecoder : public Decoder { static_cast<int>(pc_ - start_)); module->functions->push_back( - {nullptr, 0, 0, 0, 0, 0, 0, false, false}); + {nullptr, i, 0, 0, 0, 0, 0, 0, false, false}); WasmFunction* function = &module->functions->back(); DecodeFunctionInModule(module, function, false); } @@ -133,7 +131,7 @@ class ModuleDecoder : public Decoder { } case kDeclGlobals: { int length; - uint32_t globals_count = u32v(&length, "globals count"); + uint32_t globals_count = consume_u32v(&length, "globals count"); module->globals->reserve(SafeReserve(globals_count)); // Decode globals. for (uint32_t i = 0; i < globals_count; i++) { @@ -148,7 +146,8 @@ class ModuleDecoder : public Decoder { } case kDeclDataSegments: { int length; - uint32_t data_segments_count = u32v(&length, "data segments count"); + uint32_t data_segments_count = + consume_u32v(&length, "data segments count"); module->data_segments->reserve(SafeReserve(data_segments_count)); // Decode data segments. for (uint32_t i = 0; i < data_segments_count; i++) { @@ -157,7 +156,7 @@ class ModuleDecoder : public Decoder { static_cast<int>(pc_ - start_)); module->data_segments->push_back({0, 0, 0}); WasmDataSegment* segment = &module->data_segments->back(); - DecodeDataSegmentInModule(segment); + DecodeDataSegmentInModule(module, segment); } break; } @@ -165,14 +164,15 @@ class ModuleDecoder : public Decoder { // An indirect function table requires functions first. CheckForPreviousSection(sections, kDeclFunctions, true); int length; - uint32_t function_table_count = u32v(&length, "function table count"); + uint32_t function_table_count = + consume_u32v(&length, "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 = u16(); + uint16_t index = consume_u16(); if (index >= module->functions->size()) { error(pc_ - 2, "invalid function index"); break; @@ -181,13 +181,66 @@ class ModuleDecoder : public Decoder { } break; } + case kDeclStartFunction: { + // Declares a start function for a module. + CheckForPreviousSection(sections, kDeclFunctions, true); + 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"); + break; + } + break; + } + case kDeclImportTable: { + // Declares an import table. + CheckForPreviousSection(sections, kDeclSignatures, true); + int length; + uint32_t import_table_count = + consume_u32v(&length, "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(); + + 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->module_name_offset = consume_string("import module name"); + import->function_name_offset = + consume_string("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 = u32v(&length, "section size"); + 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; @@ -249,14 +302,14 @@ class ModuleDecoder : public Decoder { FunctionResult DecodeSingleFunction(ModuleEnv* module_env, WasmFunction* function) { pc_ = start_; - function->sig = sig(); // read signature + 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_int32_count = u16(); // read u16 - function->local_int64_count = u16(); // read u16 - function->local_float32_count = u16(); // read u16 - function->local_float64_count = u16(); // read u16 + 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 @@ -271,7 +324,7 @@ class ModuleDecoder : public Decoder { // Decodes a single function signature at {start}. FunctionSig* DecodeFunctionSignature(const byte* start) { pc_ = start; - FunctionSig* result = sig(); + FunctionSig* result = consume_sig(); return ok() ? result : nullptr; } @@ -284,19 +337,19 @@ class ModuleDecoder : public Decoder { // Decodes a single global entry inside a module starting at {pc_}. void DecodeGlobalInModule(WasmGlobal* global) { - global->name_offset = string("global name"); + global->name_offset = consume_string("global name"); global->type = mem_type(); global->offset = 0; - global->exported = u8("exported") != 0; + global->exported = consume_u8("exported") != 0; } // Decodes a single function entry inside a module starting at {pc_}. void DecodeFunctionInModule(WasmModule* module, WasmFunction* function, bool verify_body = true) { - byte decl_bits = u8("function decl"); + byte decl_bits = consume_u8("function decl"); const byte* sigpos = pc_; - function->sig_index = u16("signature index"); + function->sig_index = consume_u16("signature index"); if (function->sig_index >= module->signatures->size()) { return error(sigpos, "invalid signature index"); @@ -313,7 +366,7 @@ class ModuleDecoder : public Decoder { (decl_bits & kDeclFunctionImport) == 0 ? " body" : ""); if (decl_bits & kDeclFunctionName) { - function->name_offset = string("function name"); + function->name_offset = consume_string("function name"); } function->exported = decl_bits & kDeclFunctionExport; @@ -325,13 +378,13 @@ class ModuleDecoder : public Decoder { } if (decl_bits & kDeclFunctionLocals) { - function->local_int32_count = u16("int32 count"); - function->local_int64_count = u16("int64 count"); - function->local_float32_count = u16("float32 count"); - function->local_float64_count = u16("float64 count"); + function->local_i32_count = consume_u16("i32 count"); + function->local_i64_count = consume_u16("i64 count"); + function->local_f32_count = consume_u16("f32 count"); + function->local_f64_count = consume_u16("f64 count"); } - uint16_t size = u16("body size"); + uint16_t size = consume_u16("body size"); if (ok()) { if ((pc_ + size) > limit_) { return error(pc_, limit_, @@ -345,35 +398,51 @@ class ModuleDecoder : public Decoder { } } + bool IsWithinLimit(uint32_t limit, uint32_t offset, uint32_t size) { + if (offset > limit) return false; + if ((offset + size) < offset) return false; // overflow + return (offset + size) <= limit; + } + // Decodes a single data segment entry inside a module starting at {pc_}. - void DecodeDataSegmentInModule(WasmDataSegment* segment) { - segment->dest_addr = - u32("destination"); // TODO(titzer): check it's within the memory size. - segment->source_offset = offset("source offset"); - segment->source_size = - u32("source size"); // TODO(titzer): check the size is reasonable. - segment->init = u8("init"); + 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"); + + // 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"); + } + + // Validate that the segment will fit into the (minimum) memory. + uint32_t memory_limit = + 1 << (module ? module->min_mem_size_log2 : WasmModule::kMaxMemSize); + if (!IsWithinLimit(memory_limit, segment->dest_addr, + segment->source_size)) { + error(pc_ - sizeof(uint32_t), "segment out of bounds of memory"); + } } // Verifies the body (code) of a given function. void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv, WasmFunction* function) { if (FLAG_trace_wasm_decode_time) { - // TODO(titzer): clean me up a bit. OFStream os(stdout); - os << "Verifying WASM function:"; - if (function->name_offset > 0) { - os << menv->module->GetName(function->name_offset); - } + os << "Verifying WASM function " << WasmFunctionName(function, menv) + << std::endl; os << std::endl; } FunctionEnv fenv; fenv.module = menv; fenv.sig = function->sig; - fenv.local_int32_count = function->local_int32_count; - fenv.local_int64_count = function->local_int64_count; - fenv.local_float32_count = function->local_float32_count; - fenv.local_float64_count = function->local_float64_count; + 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 = @@ -382,8 +451,7 @@ class ModuleDecoder : public Decoder { if (result.failed()) { // Wrap the error message from the function decoder. std::ostringstream str; - str << "in function #" << func_num << ": "; - // TODO(titzer): add function name for the user? + str << "in function " << WasmFunctionName(function, menv) << ": "; str << result; std::string strval = str.str(); const char* raw = strval.c_str(); @@ -400,8 +468,8 @@ class ModuleDecoder : public Decoder { // Reads a single 32-bit unsigned integer interpreted as an offset, checking // the offset is within bounds and advances. - uint32_t offset(const char* name = nullptr) { - uint32_t offset = u32(name ? name : "offset"); + uint32_t consume_offset(const char* name = nullptr) { + uint32_t offset = consume_u32(name ? name : "offset"); if (offset > static_cast<uint32_t>(limit_ - start_)) { error(pc_ - sizeof(uint32_t), "offset out of bounds of module"); } @@ -410,13 +478,14 @@ class ModuleDecoder : public Decoder { // Reads a single 32-bit unsigned integer interpreted as an offset into the // data and validating the string there and advances. - uint32_t string(const char* name = nullptr) { - return offset(name ? name : "string"); // TODO(titzer): validate string + uint32_t consume_string(const char* name = nullptr) { + // TODO(titzer): validate string + return consume_offset(name ? name : "string"); } // Reads a single 8-bit integer, interpreting it as a local type. - LocalType local_type() { - byte val = u8("local type"); + LocalType consume_local_type() { + byte val = consume_u8("local type"); LocalTypeCode t = static_cast<LocalTypeCode>(val); switch (t) { case kLocalVoid: @@ -437,7 +506,7 @@ class ModuleDecoder : public Decoder { // Reads a single 8-bit integer, interpreting it as a memory type. MachineType mem_type() { - byte val = u8("memory type"); + byte val = consume_u8("memory type"); MemTypeCode t = static_cast<MemTypeCode>(val); switch (t) { case kMemI8: @@ -467,14 +536,14 @@ class ModuleDecoder : public Decoder { } // Parses an inline function signature. - FunctionSig* sig() { - byte count = u8("param count"); - LocalType ret = local_type(); + FunctionSig* consume_sig() { + byte count = consume_u8("param count"); + LocalType ret = consume_local_type(); FunctionSig::Builder builder(module_zone, ret == kAstStmt ? 0 : 1, count); if (ret != kAstStmt) builder.AddReturn(ret); for (int i = 0; i < count; i++) { - LocalType param = local_type(); + LocalType param = consume_local_type(); if (param == kAstStmt) error(pc_ - 1, "invalid void parameter type"); builder.AddParam(param); } diff --git a/deps/v8/src/wasm/wasm-js.cc b/deps/v8/src/wasm/wasm-js.cc index 80d8bdb236..62a2676032 100644 --- a/deps/v8/src/wasm/wasm-js.cc +++ b/deps/v8/src/wasm/wasm-js.cc @@ -37,6 +37,7 @@ 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()) { thrower.Error("Argument 0 must be an array buffer"); return {nullptr, nullptr}; @@ -44,8 +45,6 @@ RawBuffer GetRawBufferArgument( Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]); ArrayBuffer::Contents contents = buffer->GetContents(); - // TODO(titzer): allow offsets into buffers, views, etc. - const byte* start = reinterpret_cast<const byte*>(contents.Data()); const byte* end = start + contents.ByteLength(); @@ -100,33 +99,8 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { if (result.val) delete result.val; } - -void CompileRun(const v8::FunctionCallbackInfo<v8::Value>& args) { - HandleScope scope(args.GetIsolate()); - i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); - ErrorThrower thrower(isolate, "WASM.compileRun()"); - - RawBuffer buffer = GetRawBufferArgument(thrower, args); - if (thrower.error()) return; - - // Decode and pre-verify the functions before compiling and running. - i::Zone zone; - internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( - isolate, &zone, buffer.start, buffer.end, true, false); - - if (result.failed()) { - thrower.Failed("", result); - } else { - // Success. Compile and run! - int32_t retval = i::wasm::CompileAndRunWasmModule(isolate, result.val); - args.GetReturnValue().Set(retval); - } - - if (result.val) delete result.val; -} - - -v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(i::ParseInfo* info) { +v8::internal::wasm::WasmModuleIndex* TranslateAsmModule( + i::ParseInfo* info, i::Handle<i::Object> foreign, ErrorThrower* thrower) { info->set_global(); info->set_lazy(false); info->set_allow_lazy_parsing(false); @@ -141,61 +115,79 @@ v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(i::ParseInfo* info) { v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()), info->literal()); + if (i::FLAG_enable_simd_asmjs) { + typer.set_allow_simd(true); + } if (!typer.Validate()) { + thrower->Error("Asm.js validation failed: %s", typer.error_message()); return nullptr; } auto module = v8::internal::wasm::AsmWasmBuilder( - info->isolate(), info->zone(), info->literal()) + 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); + } + } + return module; } -void AsmCompileRun(const v8::FunctionCallbackInfo<v8::Value>& args) { - HandleScope scope(args.GetIsolate()); +void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args, + const byte* start, const byte* end, + ErrorThrower* thrower, bool must_decode) { i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); - ErrorThrower thrower(isolate, "WASM.asmCompileRun()"); - if (args.Length() != 1) { - thrower.Error("Invalid argument count"); - return; - } - if (!args[0]->IsString()) { - thrower.Error("Invalid argument count"); - return; + i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null(); + if (args.Length() > 2 && args[2]->IsArrayBuffer()) { + Local<Object> obj = Local<Object>::Cast(args[2]); + i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj); + memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj)); } - i::Factory* factory = isolate->factory(); + // Decode but avoid a redundant pass over function bodies for verification. + // Verification will happen during compilation. i::Zone zone; - Local<String> source = Local<String>::Cast(args[0]); - i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source)); - i::ParseInfo info(&zone, script); + internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( + isolate, &zone, start, end, false, false); - auto module = TranslateAsmModule(&info); - if (module == nullptr) { - thrower.Error("Asm.js validation failed"); - return; + if (result.failed() && must_decode) { + thrower->Error("Asm.js converted module failed to decode"); + } else if (result.failed()) { + thrower->Failed("", result); + } else { + // Success. Instantiate the module and return the object. + i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null(); + if (args.Length() > 1 && args[1]->IsObject()) { + Local<Object> obj = Local<Object>::Cast(args[1]); + ffi = i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj)); + } + + i::MaybeHandle<i::JSObject> object = + result.val->Instantiate(isolate, ffi, memory); + + if (!object.is_null()) { + args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); + } } - int32_t result = v8::internal::wasm::CompileAndRunWasmModule( - isolate, module->Begin(), module->End(), true); - args.GetReturnValue().Set(result); + if (result.val) delete result.val; } -// TODO(aseemgarg): deal with arraybuffer and foreign functions void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) { HandleScope scope(args.GetIsolate()); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); ErrorThrower thrower(isolate, "WASM.instantiateModuleFromAsm()"); - if (args.Length() != 1) { - thrower.Error("Invalid argument count"); - return; - } if (!args[0]->IsString()) { - thrower.Error("Invalid argument count"); + thrower.Error("Asm module text should be a string"); return; } @@ -205,31 +197,18 @@ void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) { i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source)); i::ParseInfo info(&zone, script); - auto module = TranslateAsmModule(&info); - if (module == nullptr) { - thrower.Error("Asm.js validation failed"); - return; + i::Handle<i::Object> foreign; + if (args.Length() > 1 && args[1]->IsObject()) { + Local<Object> local_foreign = Local<Object>::Cast(args[1]); + foreign = v8::Utils::OpenHandle(*local_foreign); } - i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null(); - internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( - isolate, &zone, module->Begin(), module->End(), false, false); - - if (result.failed()) { - thrower.Failed("", result); - } else { - // Success. Instantiate the module and return the object. - i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null(); - - i::MaybeHandle<i::JSObject> object = - result.val->Instantiate(isolate, ffi, memory); - - if (!object.is_null()) { - args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); - } + auto module = TranslateAsmModule(&info, foreign, &thrower); + if (module == nullptr) { + return; } - if (result.val) delete result.val; + InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower, true); } @@ -241,38 +220,7 @@ void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) { RawBuffer buffer = GetRawBufferArgument(thrower, args); if (buffer.start == nullptr) return; - i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null(); - if (args.Length() > 2 && args[2]->IsArrayBuffer()) { - Local<Object> obj = Local<Object>::Cast(args[2]); - i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj); - memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj)); - } - - // Decode but avoid a redundant pass over function bodies for verification. - // Verification will happen during compilation. - i::Zone zone; - internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( - isolate, &zone, buffer.start, buffer.end, false, false); - - if (result.failed()) { - thrower.Failed("", result); - } else { - // Success. Instantiate the module and return the object. - i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null(); - if (args.Length() > 1 && args[1]->IsObject()) { - Local<Object> obj = Local<Object>::Cast(args[1]); - ffi = i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj)); - } - - i::MaybeHandle<i::JSObject> object = - result.val->Instantiate(isolate, ffi, memory); - - if (!object.is_null()) { - args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); - } - } - - if (result.val) delete result.val; + InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower, false); } } // namespace @@ -322,11 +270,9 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) { JSObject::AddProperty(global, name, wasm_object, attributes); // Install functions on the WASM object. - InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule); InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule); InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction); - InstallFunc(isolate, wasm_object, "compileRun", CompileRun); - InstallFunc(isolate, wasm_object, "asmCompileRun", AsmCompileRun); + InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule); InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm", InstantiateModuleFromAsm); } diff --git a/deps/v8/src/wasm/wasm-macro-gen.h b/deps/v8/src/wasm/wasm-macro-gen.h index 470804a73d..dd653c1740 100644 --- a/deps/v8/src/wasm/wasm-macro-gen.h +++ b/deps/v8/src/wasm/wasm-macro-gen.h @@ -22,10 +22,10 @@ #define WASM_SELECT(cond, tval, fval) kExprSelect, cond, tval, fval #define WASM_BR(depth) kExprBr, static_cast<byte>(depth), kExprNop #define WASM_BR_IF(depth, cond) \ - kExprBrIf, static_cast<byte>(depth), cond, kExprNop + kExprBrIf, static_cast<byte>(depth), kExprNop, cond #define WASM_BRV(depth, val) kExprBr, static_cast<byte>(depth), val -#define WASM_BRV_IF(depth, cond, val) \ - kExprBrIf, static_cast<byte>(depth), cond, val +#define WASM_BRV_IF(depth, val, cond) \ + kExprBrIf, static_cast<byte>(depth), val, cond #define WASM_BREAK(depth) kExprBr, static_cast<byte>(depth + 1), kExprNop #define WASM_CONTINUE(depth) kExprBr, static_cast<byte>(depth), kExprNop #define WASM_BREAKV(depth, val) kExprBr, static_cast<byte>(depth + 1), val @@ -104,9 +104,12 @@ static_cast<byte>(offset), index, val #define WASM_CALL_FUNCTION(index, ...) \ kExprCallFunction, static_cast<byte>(index), __VA_ARGS__ +#define WASM_CALL_IMPORT(index, ...) \ + kExprCallImport, static_cast<byte>(index), __VA_ARGS__ #define WASM_CALL_INDIRECT(index, func, ...) \ kExprCallIndirect, static_cast<byte>(index), func, __VA_ARGS__ #define WASM_CALL_FUNCTION0(index) kExprCallFunction, static_cast<byte>(index) +#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 diff --git a/deps/v8/src/wasm/wasm-module.cc b/deps/v8/src/wasm/wasm-module.cc index fd2428080b..02d197c547 100644 --- a/deps/v8/src/wasm/wasm-module.cc +++ b/deps/v8/src/wasm/wasm-module.cc @@ -31,33 +31,32 @@ std::ostream& operator<<(std::ostream& os, const WasmModule& module) { std::ostream& operator<<(std::ostream& os, const WasmFunction& function) { - os << "WASM function with signature "; + os << "WASM function with signature " << *function.sig; - // TODO(titzer): factor out rendering of signatures. - if (function.sig->return_count() == 0) os << "v"; - for (size_t i = 0; i < function.sig->return_count(); i++) { - os << WasmOpcodes::ShortNameOf(function.sig->GetReturn(i)); - } - os << "_"; - if (function.sig->parameter_count() == 0) os << "v"; - for (size_t i = 0; i < function.sig->parameter_count(); i++) { - os << WasmOpcodes::ShortNameOf(function.sig->GetParam(i)); - } os << " locals: "; - if (function.local_int32_count) - os << function.local_int32_count << " int32s "; - if (function.local_int64_count) - os << function.local_int64_count << " int64s "; - if (function.local_float32_count) - os << function.local_float32_count << " float32s "; - if (function.local_float64_count) - os << function.local_float64_count << " float64s "; + if (function.local_i32_count) os << function.local_i32_count << " i32s "; + if (function.local_i64_count) os << function.local_i64_count << " i64s "; + if (function.local_f32_count) os << function.local_f32_count << " f32s "; + if (function.local_f64_count) os << function.local_f64_count << " f64s "; os << " code bytes: " << (function.code_end_offset - function.code_start_offset); return os; } +std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) { + os << "#" << pair.function_->func_index << ":"; + if (pair.function_->name_offset > 0) { + if (pair.module_) { + os << pair.module_->GetName(pair.function_->name_offset); + } else { + os << "+" << pair.function_->func_index; + } + } else { + os << "?"; + } + return os; +} // A helper class for compiling multiple wasm functions that offers // placeholder code objects for calling functions that are not yet compiled. @@ -193,35 +192,98 @@ Handle<FixedArray> BuildFunctionTable(Isolate* isolate, WasmModule* module) { return fixed; } - -Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, int size, +Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size, byte** backing_store) { - void* memory = isolate->array_buffer_allocator()->Allocate(size); - if (!memory) return Handle<JSArrayBuffer>::null(); + if (size > (1 << WasmModule::kMaxMemSize)) { + // TODO(titzer): lift restriction on maximum memory allocated here. + *backing_store = nullptr; + return Handle<JSArrayBuffer>::null(); + } + void* memory = + isolate->array_buffer_allocator()->Allocate(static_cast<int>(size)); + if (!memory) { + *backing_store = nullptr; + return Handle<JSArrayBuffer>::null(); + } + *backing_store = reinterpret_cast<byte*>(memory); #if DEBUG // Double check the API allocator actually zero-initialized the memory. - for (int i = 0; i < size; i++) { - DCHECK_EQ(0, (*backing_store)[i]); + byte* bytes = reinterpret_cast<byte*>(*backing_store); + for (size_t i = 0; i < size; i++) { + DCHECK_EQ(0, bytes[i]); } #endif Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); - JSArrayBuffer::Setup(buffer, isolate, false, memory, size); + JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size)); buffer->set_is_neuterable(false); return buffer; } -} // namespace +// Set the memory for a module instance to be the {memory} array buffer. +void SetMemory(WasmModuleInstance* instance, Handle<JSArrayBuffer> memory) { + memory->set_is_neuterable(false); + instance->mem_start = reinterpret_cast<byte*>(memory->backing_store()); + instance->mem_size = memory->byte_length()->Number(); + instance->mem_buffer = memory; +} + +// Allocate memory for a module instance as a new JSArrayBuffer. +bool AllocateMemory(ErrorThrower* thrower, Isolate* isolate, + WasmModuleInstance* instance) { + DCHECK(instance->module); + DCHECK(instance->mem_buffer.is_null()); + + if (instance->module->min_mem_size_log2 > WasmModule::kMaxMemSize) { + 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_buffer = + NewArrayBuffer(isolate, instance->mem_size, &instance->mem_start); + if (!instance->mem_start) { + thrower->Error("Out of memory: wasm memory"); + instance->mem_size = 0; + return false; + } + return true; +} + +bool AllocateGlobals(ErrorThrower* thrower, Isolate* isolate, + WasmModuleInstance* instance) { + instance->globals_size = AllocateGlobalsOffsets(instance->module->globals); + + if (instance->globals_size > 0) { + instance->globals_buffer = NewArrayBuffer(isolate, instance->globals_size, + &instance->globals_start); + if (!instance->globals_start) { + // Not enough space for backing store of globals. + thrower->Error("Out of memory: wasm globals"); + return false; + } + } + return true; +} +} // namespace WasmModule::WasmModule() - : globals(nullptr), + : shared_isolate(nullptr), + module_start(nullptr), + module_end(nullptr), + min_mem_size_log2(0), + max_mem_size_log2(0), + mem_export(false), + mem_external(false), + start_function_index(-1), + globals(nullptr), signatures(nullptr), functions(nullptr), data_segments(nullptr), - function_table(nullptr) {} - + function_table(nullptr), + import_table(nullptr) {} WasmModule::~WasmModule() { if (globals) delete globals; @@ -229,8 +291,33 @@ WasmModule::~WasmModule() { if (functions) delete functions; if (data_segments) delete data_segments; if (function_table) delete function_table; + if (import_table) delete import_table; } +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>(); + } + } else { + thrower.Error("FFI table is not an object."); + return MaybeHandle<JSFunction>(); + } +} // Instantiates a wasm module as a JSObject. // * allocates a backing store of {mem_size} bytes. @@ -242,95 +329,91 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, Handle<JSArrayBuffer> memory) { this->shared_isolate = isolate; // TODO(titzer): have a real shared isolate. ErrorThrower thrower(isolate, "WasmModule::Instantiate()"); - Factory* factory = isolate->factory(); - // Memory is bigger than maximum supported size. - if (memory.is_null() && min_mem_size_log2 > kMaxMemSize) { - thrower.Error("Out of memory: wasm memory too large"); - return MaybeHandle<JSObject>(); - } + //------------------------------------------------------------------------- + // Allocate the instance and its JS counterpart. + //------------------------------------------------------------------------- Handle<Map> map = factory->NewMap( JS_OBJECT_TYPE, JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize); - - //------------------------------------------------------------------------- - // Allocate the module object. - //------------------------------------------------------------------------- - Handle<JSObject> module = factory->NewJSObjectFromMap(map, TENURED); + 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); + instance.js_object->SetInternalField(kWasmModuleCodeTable, *code_table); //------------------------------------------------------------------------- - // Allocate the linear memory. + // Allocate and initialize the linear memory. //------------------------------------------------------------------------- - uint32_t mem_size = 1 << min_mem_size_log2; - byte* mem_addr = nullptr; - Handle<JSArrayBuffer> mem_buffer; - if (!memory.is_null()) { - memory->set_is_neuterable(false); - mem_addr = reinterpret_cast<byte*>(memory->backing_store()); - mem_size = memory->byte_length()->Number(); - mem_buffer = memory; - } else { - mem_buffer = NewArrayBuffer(isolate, mem_size, &mem_addr); - if (!mem_addr) { - // Not enough space for backing store of memory - thrower.Error("Out of memory: wasm memory"); + if (memory.is_null()) { + if (!AllocateMemory(&thrower, isolate, &instance)) { return MaybeHandle<JSObject>(); } + } else { + SetMemory(&instance, memory); } - - // Load initialized data segments. - LoadDataSegments(this, mem_addr, mem_size); - - module->SetInternalField(kWasmMemArrayBuffer, *mem_buffer); + instance.js_object->SetInternalField(kWasmMemArrayBuffer, + *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(module, name, mem_buffer, READ_ONLY); + JSObject::AddProperty(instance.js_object, name, instance.mem_buffer, + READ_ONLY); } //------------------------------------------------------------------------- // Allocate the globals area if necessary. //------------------------------------------------------------------------- - size_t globals_size = AllocateGlobalsOffsets(globals); - byte* globals_addr = nullptr; - if (globals_size > 0) { - Handle<JSArrayBuffer> globals_buffer = - NewArrayBuffer(isolate, mem_size, &globals_addr); - if (!globals_addr) { - // Not enough space for backing store of globals. - thrower.Error("Out of memory: wasm globals"); - return MaybeHandle<JSObject>(); - } - - module->SetInternalField(kWasmGlobalsArrayBuffer, *globals_buffer); - } else { - module->SetInternalField(kWasmGlobalsArrayBuffer, Smi::FromInt(0)); + if (!AllocateGlobals(&thrower, isolate, &instance)) { + return MaybeHandle<JSObject>(); + } + if (!instance.globals_buffer.is_null()) { + instance.js_object->SetInternalField(kWasmGlobalsArrayBuffer, + *instance.globals_buffer); } //------------------------------------------------------------------------- - // Compile all functions in the module. + // Compile wrappers to imported functions. //------------------------------------------------------------------------- - int index = 0; + uint32_t index = 0; + instance.function_table = BuildFunctionTable(isolate, this); WasmLinker linker(isolate, functions->size()); ModuleEnv module_env; module_env.module = this; - module_env.mem_start = reinterpret_cast<uintptr_t>(mem_addr); - module_env.mem_end = reinterpret_cast<uintptr_t>(mem_addr) + mem_size; - module_env.globals_area = reinterpret_cast<uintptr_t>(globals_addr); + module_env.instance = &instance; module_env.linker = &linker; - module_env.function_code = nullptr; - module_env.function_table = BuildFunctionTable(isolate, this); - module_env.memory = memory; - module_env.context = isolate->native_context(); 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); + 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); + index++; + } + } + + //------------------------------------------------------------------------- + // Compile all functions in the module. + //------------------------------------------------------------------------- + // First pass: compile each function and initialize the code table. + index = 0; for (const WasmFunction& func : *functions) { if (thrower.error()) break; + DCHECK_EQ(index, func.func_index); const char* cstr = GetName(func.name_offset); Handle<String> name = factory->InternalizeUtf8String(cstr); @@ -338,38 +421,21 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, Handle<JSFunction> function = Handle<JSFunction>::null(); if (func.external) { // Lookup external function in FFI object. - if (!ffi.is_null()) { - MaybeHandle<Object> result = Object::GetProperty(ffi, name); - if (!result.is_null()) { - Handle<Object> obj = result.ToHandleChecked(); - if (obj->IsJSFunction()) { - function = Handle<JSFunction>::cast(obj); - code = compiler::CompileWasmToJSWrapper(isolate, &module_env, - function, index); - } else { - thrower.Error("FFI function #%d:%s is not a JSFunction.", index, - cstr); - return MaybeHandle<JSObject>(); - } - } else { - thrower.Error("FFI function #%d:%s not found.", index, cstr); - return MaybeHandle<JSObject>(); - } - } else { - thrower.Error("FFI table is not an object."); - return MaybeHandle<JSObject>(); - } + MaybeHandle<JSFunction> function = + LookupFunction(thrower, ffi, index, name, cstr); + if (function.is_null()) return MaybeHandle<JSObject>(); + code = compiler::CompileWasmToJSWrapper( + isolate, &module_env, function.ToHandleChecked(), func.sig, cstr); } else { // Compile the function. - code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func, - index); + code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func); if (code.is_null()) { thrower.Error("Compilation of #%d:%s failed.", index, cstr); return MaybeHandle<JSObject>(); } if (func.exported) { - function = compiler::CompileJSToWasmWrapper(isolate, &module_env, name, - code, module, index); + function = compiler::CompileJSToWasmWrapper( + isolate, &module_env, name, code, instance.js_object, index); } } if (!code.is_null()) { @@ -379,27 +445,54 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, } if (func.exported) { // Exported functions are installed as read-only properties on the module. - JSObject::AddProperty(module, name, function, READ_ONLY); + JSObject::AddProperty(instance.js_object, name, function, READ_ONLY); } index++; } // Second pass: patch all direct call sites. - linker.Link(module_env.function_table, this->function_table); - - module->SetInternalField(kWasmModuleFunctionTable, Smi::FromInt(0)); - module->SetInternalField(kWasmModuleCodeTable, *code_table); - return module; + linker.Link(instance.function_table, this->function_table); + instance.js_object->SetInternalField(kWasmModuleFunctionTable, + Smi::FromInt(0)); + + // Run the start function if one was specified. + if (this->start_function_index >= 0) { + HandleScope scope(isolate); + uint32_t index = static_cast<uint32_t>(this->start_function_index); + Handle<String> name = isolate->factory()->NewStringFromStaticChars("start"); + Handle<Code> code = linker.GetFunctionCode(index); + Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper( + isolate, &module_env, name, code, instance.js_object, index); + + // Call the JS function. + Handle<Object> undefined(isolate->heap()->undefined_value(), isolate); + MaybeHandle<Object> retval = + Execution::Call(isolate, jsfunc, undefined, 0, nullptr); + + if (retval.is_null()) { + thrower.Error("WASM.instantiateModule(): start function failed"); + } + } + return instance.js_object; } Handle<Code> ModuleEnv::GetFunctionCode(uint32_t index) { DCHECK(IsValidFunction(index)); if (linker) return linker->GetFunctionCode(index); - if (function_code) return function_code->at(index); + if (instance && instance->function_code) { + return instance->function_code->at(index); + } return 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(); +} compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone, uint32_t index) { @@ -436,43 +529,45 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module) { ErrorThrower thrower(isolate, "CompileAndRunWasmModule"); + WasmModuleInstance instance(module); - // Allocate temporary linear memory and globals. - size_t mem_size = 1 << module->min_mem_size_log2; - size_t globals_size = AllocateGlobalsOffsets(module->globals); + // Allocate and initialize the linear memory. + if (!AllocateMemory(&thrower, isolate, &instance)) { + return -1; + } + LoadDataSegments(module, instance.mem_start, instance.mem_size); - base::SmartArrayPointer<byte> mem_addr(new byte[mem_size]); - base::SmartArrayPointer<byte> globals_addr(new byte[globals_size]); + // Allocate the globals area if necessary. + if (!AllocateGlobals(&thrower, isolate, &instance)) { + return -1; + } - memset(mem_addr.get(), 0, mem_size); - memset(globals_addr.get(), 0, globals_size); + // Build the function table. + instance.function_table = BuildFunctionTable(isolate, module); // Create module environment. WasmLinker linker(isolate, module->functions->size()); ModuleEnv module_env; module_env.module = module; - module_env.mem_start = reinterpret_cast<uintptr_t>(mem_addr.get()); - module_env.mem_end = reinterpret_cast<uintptr_t>(mem_addr.get()) + mem_size; - module_env.globals_area = reinterpret_cast<uintptr_t>(globals_addr.get()); + module_env.instance = &instance; module_env.linker = &linker; - module_env.function_code = nullptr; - module_env.function_table = BuildFunctionTable(isolate, module); module_env.asm_js = false; - // Load data segments. - // TODO(titzer): throw instead of crashing if segments don't fit in memory? - LoadDataSegments(module, mem_addr.get(), mem_size); - // Compile all functions. Handle<Code> main_code = Handle<Code>::null(); // record last code. - int index = 0; + uint32_t index = 0; + int main_index = 0; 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. - Handle<Code> code = compiler::CompileWasmFunction( - thrower, isolate, &module_env, func, index); + Handle<Code> code = + compiler::CompileWasmFunction(thrower, isolate, &module_env, func); if (!code.is_null()) { - if (func.exported) main_code = code; + if (func.exported) { + main_code = code; + main_index = index; + } linker.Finish(index, code); } if (thrower.error()) return -1; @@ -480,30 +575,37 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module) { index++; } - if (!main_code.is_null()) { - linker.Link(module_env.function_table, module->function_table); -#if USE_SIMULATOR && V8_TARGET_ARCH_ARM64 - // Run the main code on arm64 simulator. - Simulator* simulator = Simulator::current(isolate); - Simulator::CallArgument args[] = {Simulator::CallArgument(0), - Simulator::CallArgument::End()}; - return static_cast<int32_t>(simulator->CallInt64(main_code->entry(), args)); -#elif USE_SIMULATOR - // Run the main code on simulator. - Simulator* simulator = Simulator::current(isolate); - return static_cast<int32_t>( - simulator->Call(main_code->entry(), 4, 0, 0, 0, 0)); -#else - // Run the main code as raw machine code. - int32_t (*raw_func)() = reinterpret_cast<int32_t (*)()>( - reinterpret_cast<uintptr_t>(main_code->entry())); - return raw_func(); -#endif - } else { - // No main code was found. - isolate->Throw(*isolate->factory()->NewStringFromStaticChars( - "WASM.compileRun() failed: no valid main code produced.")); + if (main_code.is_null()) { + thrower.Error("WASM.compileRun() failed: no main code found"); + return -1; + } + + linker.Link(instance.function_table, instance.module->function_table); + + // Wrap the main code so it can be called as a JS function. + Handle<String> name = isolate->factory()->NewStringFromStaticChars("main"); + Handle<JSObject> module_object = Handle<JSObject>(0, isolate); + Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper( + isolate, &module_env, name, main_code, module_object, main_index); + + // Call the JS function. + Handle<Object> undefined(isolate->heap()->undefined_value(), isolate); + MaybeHandle<Object> retval = + Execution::Call(isolate, jsfunc, undefined, 0, nullptr); + + // The result should be a number. + if (retval.is_null()) { + thrower.Error("WASM.compileRun() failed: Invocation was null"); + return -1; + } + Handle<Object> result = retval.ToHandleChecked(); + if (result->IsSmi()) { + return Smi::cast(*result)->value(); + } + if (result->IsHeapNumber()) { + return static_cast<int32_t>(HeapNumber::cast(*result)->value()); } + thrower.Error("WASM.compileRun() failed: Return value should be number"); return -1; } } // namespace wasm diff --git a/deps/v8/src/wasm/wasm-module.h b/deps/v8/src/wasm/wasm-module.h index 5e2ba58a44..5f5777cebe 100644 --- a/deps/v8/src/wasm/wasm-module.h +++ b/deps/v8/src/wasm/wasm-module.h @@ -30,11 +30,13 @@ enum WasmSectionDeclCode { kDeclGlobals = 0x03, kDeclDataSegments = 0x04, kDeclFunctionTable = 0x05, - kDeclWLL = 0x11, kDeclEnd = 0x06, + kDeclStartFunction = 0x07, + kDeclImportTable = 0x08, + kDeclWLL = 0x11, }; -static const int kMaxModuleSectionCode = 6; +static const int kMaxModuleSectionCode = 0x11; enum WasmFunctionDeclBit { kDeclFunctionName = 0x01, @@ -48,22 +50,29 @@ static const size_t kDeclMemorySize = 3; static const size_t kDeclGlobalSize = 6; static const size_t kDeclDataSegmentSize = 13; -// Static representation of a wasm function. +// 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 name_offset; // offset in the module bytes of the name, if any. 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_int32_count; // number of int32 local variables. - uint16_t local_int64_count; // number of int64 local variables. - uint16_t local_float32_count; // number of float32 local variables. - uint16_t local_float64_count; // number of float64 local variables. + uint16_t local_i32_count; // number of i32 local variables. + uint16_t local_i64_count; // number of i64 local variables. + uint16_t local_f32_count; // number of f32 local variables. + uint16_t local_f64_count; // number of f64 local variables. bool exported; // true if this function is exported. bool external; // true if this function is externally supplied. }; -struct ModuleEnv; // forward declaration of decoder interface. +// 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 module_name_offset; // offset in module bytes of the module name. + uint32_t function_name_offset; // offset in module bytes of the import name. +}; // Static representation of a wasm global variable. struct WasmGlobal { @@ -93,25 +102,27 @@ struct WasmModule { uint8_t max_mem_size_log2; // maximum size of the memory (log base 2). 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. 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. WasmModule(); ~WasmModule(); // Get a pointer to a string stored in the module bytes representing a name. - const char* GetName(uint32_t offset) { - CHECK(BoundsCheck(offset, offset + 1)); + 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); } // Checks the given offset range is contained within the module bytes. - bool BoundsCheck(uint32_t start, uint32_t end) { + bool BoundsCheck(uint32_t start, uint32_t end) const { size_t size = module_end - module_start; return start < size && end < size; } @@ -121,22 +132,42 @@ struct WasmModule { Handle<JSArrayBuffer> memory); }; +// An instantiated WASM module, including memory, function table, etc. +struct WasmModuleInstance { + WasmModule* module; // static representation of the module. + // -- Heap allocated -------------------------------------------------------- + Handle<JSObject> js_object; // JavaScript module object. + Handle<Context> context; // JavaScript native context. + 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. + // -- raw memory ------------------------------------------------------------ + byte* mem_start; // start of linear memory. + size_t mem_size; // size of the linear memory. + // -- raw globals ----------------------------------------------------------- + byte* globals_start; // start of the globals area. + size_t globals_size; // size of the globals area. + + explicit WasmModuleInstance(WasmModule* m) + : module(m), + function_code(nullptr), + mem_start(nullptr), + mem_size(0), + globals_start(nullptr), + globals_size(0) {} +}; + // forward declaration. class WasmLinker; // Interface provided to the decoder/graph builder which contains only // minimal information about the globals, functions, and function tables. struct ModuleEnv { - uintptr_t globals_area; // address of the globals area. - uintptr_t mem_start; // address of the start of linear memory. - uintptr_t mem_end; // address of the end of linear memory. - WasmModule* module; + WasmModuleInstance* instance; WasmLinker* linker; - std::vector<Handle<Code>>* function_code; - Handle<FixedArray> function_table; - Handle<JSArrayBuffer> memory; - Handle<Context> context; bool asm_js; // true if the module originated from asm.js. bool IsValidGlobal(uint32_t index) { @@ -148,6 +179,9 @@ struct ModuleEnv { bool IsValidSignature(uint32_t index) { return module && index < module->signatures->size(); } + bool IsValidImport(uint32_t index) { + return module && index < module->import_table->size(); + } MachineType GetGlobalType(uint32_t index) { DCHECK(IsValidGlobal(index)); return module->globals->at(index).type; @@ -156,23 +190,41 @@ struct ModuleEnv { DCHECK(IsValidFunction(index)); return module->functions->at(index).sig; } + FunctionSig* GetImportSignature(uint32_t index) { + DCHECK(IsValidImport(index)); + return module->import_table->at(index).sig; + } FunctionSig* GetSignature(uint32_t index) { DCHECK(IsValidSignature(index)); return module->signatures->at(index); } size_t FunctionTableSize() { - return module ? module->function_table->size() : 0; + return module && module->function_table ? module->function_table->size() + : 0; } Handle<Code> GetFunctionCode(uint32_t index); + Handle<Code> GetImportCode(uint32_t index); Handle<FixedArray> GetFunctionTable(); - compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone, FunctionSig* sig); + static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone, + FunctionSig* sig); + static compiler::CallDescriptor* GetI32WasmCallDescriptor( + Zone* zone, compiler::CallDescriptor* descriptor); compiler::CallDescriptor* GetCallDescriptor(Zone* zone, uint32_t index); }; +// A helper for printing out the names of functions. +struct WasmFunctionName { + const WasmFunction* function_; + const WasmModule* module_; + WasmFunctionName(const WasmFunction* function, const ModuleEnv* menv) + : function_(function), module_(menv ? menv->module : nullptr) {} +}; + std::ostream& operator<<(std::ostream& os, const WasmModule& module); std::ostream& operator<<(std::ostream& os, const WasmFunction& function); +std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name); typedef Result<WasmModule*> ModuleResult; typedef Result<WasmFunction*> FunctionResult; @@ -185,6 +237,7 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, // For testing. Decode, verify, and run the last exported function in the // given decoded module. int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module); + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/deps/v8/src/wasm/wasm-opcodes.cc b/deps/v8/src/wasm/wasm-opcodes.cc index 25eef034d7..a609e03261 100644 --- a/deps/v8/src/wasm/wasm-opcodes.cc +++ b/deps/v8/src/wasm/wasm-opcodes.cc @@ -25,6 +25,20 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { } +std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) { + if (sig.return_count() == 0) os << "v"; + for (size_t i = 0; i < sig.return_count(); i++) { + os << WasmOpcodes::ShortNameOf(sig.GetReturn(i)); + } + os << "_"; + if (sig.parameter_count() == 0) os << "v"; + for (size_t i = 0; i < sig.parameter_count(); i++) { + os << WasmOpcodes::ShortNameOf(sig.GetParam(i)); + } + return os; +} + + #define DECLARE_SIG_ENUM(name, ...) kSigEnum_##name, diff --git a/deps/v8/src/wasm/wasm-opcodes.h b/deps/v8/src/wasm/wasm-opcodes.h index ae2843a6c1..7cb9c00449 100644 --- a/deps/v8/src/wasm/wasm-opcodes.h +++ b/deps/v8/src/wasm/wasm-opcodes.h @@ -66,6 +66,9 @@ struct MemoryAccess { }; typedef Signature<LocalType> FunctionSig; +std::ostream& operator<<(std::ostream& os, const FunctionSig& function); + +// TODO(titzer): Renumber all the opcodes to fill in holes. // Control expressions and blocks. #define FOREACH_CONTROL_OPCODE(V) \ @@ -80,7 +83,6 @@ typedef Signature<LocalType> FunctionSig; V(TableSwitch, 0x08, _) \ V(Return, 0x14, _) \ V(Unreachable, 0x15, _) -// TODO(titzer): numbering // Constants, locals, globals, and calls. #define FOREACH_MISC_OPCODE(V) \ @@ -94,7 +96,8 @@ typedef Signature<LocalType> FunctionSig; V(LoadGlobal, 0x10, _) \ V(StoreGlobal, 0x11, _) \ V(CallFunction, 0x12, _) \ - V(CallIndirect, 0x13, _) + V(CallIndirect, 0x13, _) \ + V(CallImport, 0x1F, _) // Load memory expressions. #define FOREACH_LOAD_MEM_OPCODE(V) \ @@ -398,7 +401,6 @@ class WasmOpcodes { } } - // TODO(titzer): remove this method static WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { if (type == MachineType::Int8()) { return store ? kExprI32StoreMem8 : kExprI32LoadMem8S; |