diff --git a/src/asmjs/asm-js.cc b/src/asmjs/asm-js.cc index 31a2337f45..0134c91fe3 100644 --- a/src/asmjs/asm-js.cc +++ b/src/asmjs/asm-js.cc @@ -207,7 +207,8 @@ MaybeHandle AsmJs::InstantiateAsmWasm(i::Isolate* isolate, ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation"); i::MaybeHandle maybe_module_object = - i::wasm::WasmModule::Instantiate(isolate, module, foreign, memory); + i::wasm::WasmModule::Instantiate(isolate, &thrower, module, foreign, + memory); if (maybe_module_object.is_null()) { return MaybeHandle(); } diff --git a/src/asmjs/asm-typer.cc b/src/asmjs/asm-typer.cc index 20b8d96053..a98c18b532 100644 --- a/src/asmjs/asm-typer.cc +++ b/src/asmjs/asm-typer.cc @@ -128,6 +128,7 @@ AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script, script_(script), root_(root), forward_definitions_(zone), + ffi_use_signatures_(zone), stdlib_types_(zone), stdlib_math_types_(zone), module_info_(VariableInfo::ForSpecialSymbol(zone_, kModule)), @@ -329,8 +330,8 @@ AsmTyper::VariableInfo* AsmTyper::ImportLookup(Property* import) { return i->second; } -AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) { - ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_; +AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) const { + const ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_; ZoneHashMap::Entry* entry = scope->Lookup(variable, ComputePointerHash(variable)); if (entry == nullptr && in_function_) { @@ -423,6 +424,8 @@ AsmType* AsmTyper::TypeOf(AstNode* node) const { return AsmType::None(); } +AsmType* AsmTyper::TypeOf(Variable* v) const { return Lookup(v)->type(); } + AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) { auto* var_info = Lookup(var); if (var_info == nullptr) { @@ -2306,9 +2309,20 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) { FAIL(call, "Calling something that's not a function."); } - if (callee_type->AsFFIType() != nullptr && - return_type == AsmType::Float()) { - FAIL(call, "Foreign functions can't return float."); + if (callee_type->AsFFIType() != nullptr) { + if (return_type == AsmType::Float()) { + FAIL(call, "Foreign functions can't return float."); + } + // Record FFI use signature, since the asm->wasm translator must know + // all uses up-front. + ffi_use_signatures_.emplace_back( + FFIUseSignature(call_var_proxy->var(), zone_)); + FFIUseSignature* sig = &ffi_use_signatures_.back(); + sig->return_type_ = return_type; + sig->arg_types_.reserve(args.size()); + for (size_t i = 0; i < args.size(); ++i) { + sig->arg_types_.emplace_back(args[i]); + } } if (!callee_type->CanBeInvokedWith(return_type, args)) { diff --git a/src/asmjs/asm-typer.h b/src/asmjs/asm-typer.h index 0d7aa25420..4fb402949d 100644 --- a/src/asmjs/asm-typer.h +++ b/src/asmjs/asm-typer.h @@ -73,12 +73,26 @@ class AsmTyper final { const char* error_message() const { return error_message_; } AsmType* TypeOf(AstNode* node) const; + AsmType* TypeOf(Variable* v) const; StandardMember VariableAsStandardMember(Variable* var); typedef std::unordered_set > StdlibSet; StdlibSet StdlibUses() const { return stdlib_uses_; } + // Each FFI import has a usage-site signature associated with it. + struct FFIUseSignature { + Variable* var; + ZoneVector arg_types_; + AsmType* return_type_; + FFIUseSignature(Variable* v, Zone* zone) + : var(v), arg_types_(zone), return_type_(nullptr) {} + }; + + const ZoneVector& FFIUseSignatures() { + return ffi_use_signatures_; + } + private: friend class v8::internal::wasm::AsmTyperHarnessBuilder; @@ -192,7 +206,7 @@ class AsmTyper final { // Lookup(Delta, Gamma, x) // // Delta is the global_scope_ member, and Gamma, local_scope_. - VariableInfo* Lookup(Variable* variable); + VariableInfo* Lookup(Variable* variable) const; // All of the ValidateXXX methods below return AsmType::None() in case of // validation failure. @@ -306,6 +320,7 @@ class AsmTyper final { AsmType* return_type_ = nullptr; ZoneVector forward_definitions_; + ZoneVector ffi_use_signatures_; ObjectTypeMap stdlib_types_; ObjectTypeMap stdlib_math_types_; diff --git a/src/asmjs/asm-wasm-builder.cc b/src/asmjs/asm-wasm-builder.cc index ac2585e6ea..d446c4e0a3 100644 --- a/src/asmjs/asm-wasm-builder.cc +++ b/src/asmjs/asm-wasm-builder.cc @@ -32,6 +32,7 @@ namespace wasm { } while (false) enum AsmScope { kModuleScope, kInitScope, kFuncScope, kExportScope }; +enum ValueFate { kDrop, kLeaveOnStack }; struct ForeignVariable { Handle name; @@ -61,8 +62,8 @@ class AsmWasmBuilderImpl final : public AstVisitor { typer_(typer), breakable_blocks_(zone), foreign_variables_(zone), - init_function_index_(0), - foreign_init_function_index_(0), + init_function_(nullptr), + foreign_init_function_(nullptr), next_table_index_(0), function_tables_(base::HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity, @@ -72,35 +73,33 @@ class AsmWasmBuilderImpl final : public AstVisitor { } void InitializeInitFunction() { - init_function_index_ = builder_->AddFunction(); FunctionSig::Builder b(zone(), 0, 0); - current_function_builder_ = builder_->FunctionAt(init_function_index_); - current_function_builder_->SetSignature(b.Build()); - builder_->MarkStartFunction(init_function_index_); - current_function_builder_ = nullptr; + init_function_ = builder_->AddFunction(b.Build()); + builder_->MarkStartFunction(init_function_); } void BuildForeignInitFunction() { - foreign_init_function_index_ = builder_->AddFunction(); + foreign_init_function_ = builder_->AddFunction(); FunctionSig::Builder b(zone(), 0, foreign_variables_.size()); for (auto i = foreign_variables_.begin(); i != foreign_variables_.end(); ++i) { b.AddParam(i->type); } - current_function_builder_ = - builder_->FunctionAt(foreign_init_function_index_); - current_function_builder_->SetExported(); - current_function_builder_->SetName( + foreign_init_function_->SetExported(); + std::string raw_name = "__foreign_init__"; + foreign_init_function_->SetName( AsmWasmBuilder::foreign_init_name, static_cast(strlen(AsmWasmBuilder::foreign_init_name))); - current_function_builder_->SetSignature(b.Build()); + + foreign_init_function_->SetName(raw_name.data(), + static_cast(raw_name.size())); + foreign_init_function_->SetSignature(b.Build()); for (size_t pos = 0; pos < foreign_variables_.size(); ++pos) { - current_function_builder_->EmitGetLocal(static_cast(pos)); + foreign_init_function_->EmitGetLocal(static_cast(pos)); ForeignVariable* fv = &foreign_variables_[pos]; uint32_t index = LookupOrInsertGlobal(fv->var, fv->type); - current_function_builder_->EmitWithVarInt(kExprSetGlobal, index); + foreign_init_function_->EmitWithVarInt(kExprSetGlobal, index); } - current_function_builder_ = nullptr; } i::Handle GetForeignArgs() { @@ -113,7 +112,20 @@ class AsmWasmBuilderImpl final : public AstVisitor { return ret; } + void BuildImports() { + for (const AsmTyper::FFIUseSignature& ffi : typer_->FFIUseSignatures()) { + size_t ret_count = ffi.return_type_ == AsmType::Void() ? 0 : 1; + FunctionSig::Builder b(zone_, ret_count, ffi.arg_types_.size()); + for (AsmType* arg : ffi.arg_types_) b.AddParam(TypeFrom(arg)); + if (ffi.return_type_ != AsmType::Void()) { + b.AddReturn(TypeFrom(ffi.return_type_)); + } + imported_function_table_.AddFunction(ffi.var, b.Build()); + } + } + void Build() { + BuildImports(); InitializeInitFunction(); RECURSE(VisitFunctionLiteral(literal_)); BuildForeignInitFunction(); @@ -124,8 +136,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { void VisitFunctionDeclaration(FunctionDeclaration* decl) { DCHECK_EQ(kModuleScope, scope_); DCHECK_NULL(current_function_builder_); - uint32_t index = LookupOrInsertFunction(decl->proxy()->var()); - current_function_builder_ = builder_->FunctionAt(index); + current_function_builder_ = LookupOrInsertFunction(decl->proxy()->var()); scope_ = kFuncScope; RECURSE(Visit(decl->fun())); scope_ = kModuleScope; @@ -157,8 +168,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { } } if (scope_ == kFuncScope) { - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, - false); + BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock); RECURSE(VisitStatements(stmt->statements())); } else { RECURSE(VisitStatements(stmt->statements())); @@ -171,10 +181,12 @@ class AsmWasmBuilderImpl final : public AstVisitor { public: BlockVisitor(AsmWasmBuilderImpl* builder, BreakableStatement* stmt, - WasmOpcode opcode, bool is_loop) + WasmOpcode opcode) : builder_(builder) { - builder_->breakable_blocks_.push_back(std::make_pair(stmt, is_loop)); - builder_->current_function_builder_->Emit(opcode); + builder_->breakable_blocks_.push_back( + std::make_pair(stmt, opcode == kExprLoop)); + // block and loops have a type immediate. + builder_->current_function_builder_->EmitWithU8(opcode, kLocalVoid); } ~BlockVisitor() { builder_->current_function_builder_->Emit(kExprEnd); @@ -183,7 +195,32 @@ class AsmWasmBuilderImpl final : public AstVisitor { }; void VisitExpressionStatement(ExpressionStatement* stmt) { - RECURSE(Visit(stmt->expression())); + VisitForEffect(stmt->expression()); + } + + void VisitForEffect(Expression* expr) { + if (expr->IsAssignment()) { + // Don't emit drops for assignments. Instead use SetLocal/GetLocal. + VisitAssignment(expr->AsAssignment(), kDrop); + return; + } + if (expr->IsCall()) { + // Only emit a drop if the call has a non-void return value. + if (VisitCallExpression(expr->AsCall()) && scope_ == kFuncScope) { + current_function_builder_->Emit(kExprDrop); + } + return; + } + if (expr->IsBinaryOperation()) { + BinaryOperation* binop = expr->AsBinaryOperation(); + if (binop->op() == Token::COMMA) { + VisitForEffect(binop->left()); + VisitForEffect(binop->right()); + return; + } + } + RECURSE(Visit(expr)); + if (scope_ == kFuncScope) current_function_builder_->Emit(kExprDrop); } void VisitEmptyStatement(EmptyStatement* stmt) {} @@ -193,7 +230,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { void VisitIfStatement(IfStatement* stmt) { DCHECK_EQ(kFuncScope, scope_); RECURSE(Visit(stmt->condition())); - current_function_builder_->Emit(kExprIf); + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); // WASM ifs come with implement blocks for both arms. breakable_blocks_.push_back(std::make_pair(nullptr, false)); if (stmt->HasThenStatement()) { @@ -207,48 +244,26 @@ class AsmWasmBuilderImpl final : public AstVisitor { breakable_blocks_.pop_back(); } - void VisitContinueStatement(ContinueStatement* stmt) { + void DoBreakOrContinue(BreakableStatement* target, bool is_continue) { DCHECK_EQ(kFuncScope, scope_); - DCHECK_NOT_NULL(stmt->target()); - int i = static_cast(breakable_blocks_.size()) - 1; - int block_distance = 0; - for (; i >= 0; i--) { + for (int i = static_cast(breakable_blocks_.size()) - 1; i >= 0; --i) { auto elem = breakable_blocks_.at(i); - if (elem.first == stmt->target()) { - DCHECK(elem.second); - break; - } else if (elem.second) { - block_distance += 2; - } else { - block_distance += 1; + if (elem.first == target && elem.second == is_continue) { + int block_distance = static_cast(breakable_blocks_.size() - i - 1); + current_function_builder_->Emit(kExprBr); + current_function_builder_->EmitVarInt(block_distance); + return; } } - DCHECK(i >= 0); - current_function_builder_->EmitWithU8(kExprBr, ARITY_0); - current_function_builder_->EmitVarInt(block_distance); + UNREACHABLE(); // statement not found + } + + void VisitContinueStatement(ContinueStatement* stmt) { + DoBreakOrContinue(stmt->target(), true); } void VisitBreakStatement(BreakStatement* stmt) { - DCHECK_EQ(kFuncScope, scope_); - DCHECK_NOT_NULL(stmt->target()); - int i = static_cast(breakable_blocks_.size()) - 1; - int block_distance = 0; - for (; i >= 0; i--) { - auto elem = breakable_blocks_.at(i); - if (elem.first == stmt->target()) { - if (elem.second) { - block_distance++; - } - break; - } else if (elem.second) { - block_distance += 2; - } else { - block_distance += 1; - } - } - DCHECK(i >= 0); - current_function_builder_->EmitWithU8(kExprBr, ARITY_0); - current_function_builder_->EmitVarInt(block_distance); + DoBreakOrContinue(stmt->target(), false); } void VisitReturnStatement(ReturnStatement* stmt) { @@ -258,9 +273,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { scope_ = kModuleScope; } else if (scope_ == kFuncScope) { RECURSE(Visit(stmt->expression())); - uint8_t arity = - TypeOf(stmt->expression()) == kAstStmt ? ARITY_0 : ARITY_1; - current_function_builder_->EmitWithU8(kExprReturn, arity); + current_function_builder_->Emit(kExprReturn); } else { UNREACHABLE(); } @@ -276,7 +289,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { VisitVariableProxy(tag); current_function_builder_->EmitI32Const(node->begin); current_function_builder_->Emit(kExprI32LtS); - current_function_builder_->Emit(kExprIf); + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); if_depth++; breakable_blocks_.push_back(std::make_pair(nullptr, false)); HandleCase(node->left, case_to_block, tag, default_block, if_depth); @@ -286,7 +299,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { VisitVariableProxy(tag); current_function_builder_->EmitI32Const(node->end); current_function_builder_->Emit(kExprI32GtS); - current_function_builder_->Emit(kExprIf); + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); if_depth++; breakable_blocks_.push_back(std::make_pair(nullptr, false)); HandleCase(node->right, case_to_block, tag, default_block, if_depth); @@ -296,9 +309,9 @@ class AsmWasmBuilderImpl final : public AstVisitor { VisitVariableProxy(tag); current_function_builder_->EmitI32Const(node->begin); current_function_builder_->Emit(kExprI32Eq); - current_function_builder_->Emit(kExprIf); + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); DCHECK(case_to_block.find(node->begin) != case_to_block.end()); - current_function_builder_->EmitWithU8(kExprBr, ARITY_0); + current_function_builder_->Emit(kExprBr); current_function_builder_->EmitVarInt(1 + if_depth + case_to_block[node->begin]); current_function_builder_->Emit(kExprEnd); @@ -310,22 +323,22 @@ class AsmWasmBuilderImpl final : public AstVisitor { } else { VisitVariableProxy(tag); } - current_function_builder_->EmitWithU8(kExprBrTable, ARITY_0); + current_function_builder_->Emit(kExprBrTable); current_function_builder_->EmitVarInt(node->end - node->begin + 1); - for (int v = node->begin; v <= node->end; v++) { + for (int v = node->begin; v <= node->end; ++v) { if (case_to_block.find(v) != case_to_block.end()) { - byte break_code[] = {BR_TARGET(if_depth + case_to_block[v])}; - current_function_builder_->EmitCode(break_code, sizeof(break_code)); + uint32_t target = if_depth + case_to_block[v]; + current_function_builder_->EmitVarInt(target); } else { - byte break_code[] = {BR_TARGET(if_depth + default_block)}; - current_function_builder_->EmitCode(break_code, sizeof(break_code)); + uint32_t target = if_depth + default_block; + current_function_builder_->EmitVarInt(target); } if (v == kMaxInt) { break; } } - byte break_code[] = {BR_TARGET(if_depth + default_block)}; - current_function_builder_->EmitCode(break_code, sizeof(break_code)); + uint32_t target = if_depth + default_block; + current_function_builder_->EmitVarInt(target); } while (if_depth-- != prev_if_depth) { @@ -342,14 +355,14 @@ class AsmWasmBuilderImpl final : public AstVisitor { if (case_count == 0) { return; } - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false); + BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock); ZoneVector blocks(zone_); ZoneVector cases(zone_); ZoneMap case_to_block(zone_); bool has_default = false; - for (int i = case_count - 1; i >= 0; i--) { + for (int i = case_count - 1; i >= 0; --i) { CaseClause* clause = clauses->at(i); - blocks.push_back(new BlockVisitor(this, nullptr, kExprBlock, false)); + blocks.push_back(new BlockVisitor(this, nullptr, kExprBlock)); if (!clause->is_default()) { Literal* label = clause->label()->AsLiteral(); Handle value = label->value(); @@ -366,12 +379,12 @@ class AsmWasmBuilderImpl final : public AstVisitor { } if (!has_default || case_count > 1) { int default_block = has_default ? case_count - 1 : case_count; - BlockVisitor switch_logic_block(this, nullptr, kExprBlock, false); + BlockVisitor switch_logic_block(this, nullptr, kExprBlock); CaseNode* root = OrderCases(&cases, zone_); HandleCase(root, case_to_block, tag, default_block, 0); if (root->left != nullptr || root->right != nullptr || root->begin == root->end) { - current_function_builder_->EmitWithU8(kExprBr, ARITY_0); + current_function_builder_->Emit(kExprBr); current_function_builder_->EmitVarInt(default_block); } } @@ -388,22 +401,24 @@ class AsmWasmBuilderImpl final : public AstVisitor { void VisitDoWhileStatement(DoWhileStatement* stmt) { DCHECK_EQ(kFuncScope, scope_); - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true); + BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock); + BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop); RECURSE(Visit(stmt->body())); RECURSE(Visit(stmt->cond())); - current_function_builder_->Emit(kExprIf); - current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 1); + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); + current_function_builder_->EmitWithU8(kExprBr, 1); current_function_builder_->Emit(kExprEnd); } void VisitWhileStatement(WhileStatement* stmt) { DCHECK_EQ(kFuncScope, scope_); - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true); + BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock); + BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop); RECURSE(Visit(stmt->cond())); breakable_blocks_.push_back(std::make_pair(nullptr, false)); - current_function_builder_->Emit(kExprIf); + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); RECURSE(Visit(stmt->body())); - current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 1); + current_function_builder_->EmitWithU8(kExprBr, 1); current_function_builder_->Emit(kExprEnd); breakable_blocks_.pop_back(); } @@ -413,13 +428,13 @@ class AsmWasmBuilderImpl final : public AstVisitor { if (stmt->init() != nullptr) { RECURSE(Visit(stmt->init())); } - BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true); + BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock); + BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop); if (stmt->cond() != nullptr) { RECURSE(Visit(stmt->cond())); current_function_builder_->Emit(kExprI32Eqz); - current_function_builder_->Emit(kExprIf); - current_function_builder_->Emit(kExprNop); - current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 2); + current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); + current_function_builder_->EmitWithU8(kExprBr, 2); current_function_builder_->Emit(kExprEnd); } if (stmt->body() != nullptr) { @@ -428,8 +443,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { if (stmt->next() != nullptr) { RECURSE(Visit(stmt->next())); } - current_function_builder_->Emit(kExprNop); - current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 0); + current_function_builder_->EmitWithU8(kExprBr, 0); } void VisitForInStatement(ForInStatement* stmt) { UNREACHABLE(); } @@ -446,19 +460,13 @@ class AsmWasmBuilderImpl final : public AstVisitor { DeclarationScope* scope = expr->scope(); if (scope_ == kFuncScope) { if (auto* func_type = typer_->TypeOf(expr)->AsFunctionType()) { - // Build the signature for the function. - LocalType return_type = TypeFrom(func_type->ReturnType()); + // Add the parameters for the function. const auto& arguments = func_type->Arguments(); - FunctionSig::Builder b(zone(), return_type == kAstStmt ? 0 : 1, - arguments.size()); - if (return_type != kAstStmt) b.AddReturn(return_type); for (int i = 0; i < expr->parameter_count(); ++i) { LocalType type = TypeFrom(arguments[i]); DCHECK_NE(kAstStmt, type); - b.AddParam(type); InsertParameter(scope->parameter(i), type, i); } - current_function_builder_->SetSignature(b.Build()); } else { UNREACHABLE(); } @@ -476,7 +484,24 @@ class AsmWasmBuilderImpl final : public AstVisitor { RECURSE(Visit(expr->condition())); // WASM ifs come with implicit blocks for both arms. breakable_blocks_.push_back(std::make_pair(nullptr, false)); - current_function_builder_->Emit(kExprIf); + LocalTypeCode type; + switch (TypeOf(expr)) { + case kAstI32: + type = kLocalI32; + break; + case kAstI64: + type = kLocalI64; + break; + case kAstF32: + type = kLocalF32; + break; + case kAstF64: + type = kLocalF64; + break; + default: + UNREACHABLE(); + } + current_function_builder_->EmitWithU8(kExprIf, type); RECURSE(Visit(expr->then_expression())); current_function_builder_->Emit(kExprElse); RECURSE(Visit(expr->else_expression())); @@ -554,9 +579,9 @@ class AsmWasmBuilderImpl final : public AstVisitor { } else if (scope_ == kExportScope) { Variable* var = expr->var(); DCHECK(var->is_function()); - uint32_t index = LookupOrInsertFunction(var); - builder_->FunctionAt(index)->SetExported(); - builder_->FunctionAt(index)->SetName( + WasmFunctionBuilder* function = LookupOrInsertFunction(var); + function->SetExported(); + function->SetName( AsmWasmBuilder::single_function_name, static_cast(strlen(AsmWasmBuilder::single_function_name))); } @@ -609,11 +634,10 @@ class AsmWasmBuilderImpl final : public AstVisitor { DCHECK(name->IsPropertyName()); const AstRawString* raw_name = name->AsRawPropertyName(); if (var->is_function()) { - uint32_t index = LookupOrInsertFunction(var); - builder_->FunctionAt(index)->SetExported(); - builder_->FunctionAt(index)->SetName( - reinterpret_cast(raw_name->raw_data()), - raw_name->length()); + WasmFunctionBuilder* function = LookupOrInsertFunction(var); + function->SetExported(); + function->SetName(reinterpret_cast(raw_name->raw_data()), + raw_name->length()); } } } @@ -621,7 +645,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { void VisitArrayLiteral(ArrayLiteral* expr) { UNREACHABLE(); } void LoadInitFunction() { - current_function_builder_ = builder_->FunctionAt(init_function_index_); + current_function_builder_ = init_function_; scope_ = kInitScope; } @@ -650,7 +674,8 @@ class AsmWasmBuilderImpl final : public AstVisitor { 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())); + builder_->AddIndirectFunction( + LookupOrInsertFunction(func->var())->func_index()); } } @@ -680,12 +705,11 @@ class AsmWasmBuilderImpl final : public AstVisitor { private: class ImportedFunctionIndices : public ZoneObject { public: - const char* name_; - int name_length_; + bool has_name_; WasmModuleBuilder::SignatureMap signature_to_index_; - ImportedFunctionIndices(const char* name, int name_length, Zone* zone) - : name_(name), name_length_(name_length), signature_to_index_(zone) {} + explicit ImportedFunctionIndices(Zone* zone) + : has_name_(false), signature_to_index_(zone) {} }; ZoneHashMap table_; AsmWasmBuilderImpl* builder_; @@ -697,29 +721,50 @@ class AsmWasmBuilderImpl final : public AstVisitor { ZoneAllocationPolicy(builder->zone())), builder_(builder) {} - void AddImport(Variable* v, const 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; + // Set the imported name of a variable. Must happen after all signatures + // (and thus import indices) are added for a given variable. + void SetImportName(Variable* v, const char* name, int name_length) { + auto indices = GetEntry(v); + if (indices) { + for (auto entry : indices->signature_to_index_) { + uint32_t index = entry.second; + builder_->builder_->SetImportName(index, name, name_length); + } + indices->has_name_ = true; + } } + // Get a function's index. Does not insert new entries. uint32_t GetFunctionIndex(Variable* v, FunctionSig* sig) { - ZoneHashMap::Entry* entry = table_.Lookup(v, ComputePointerHash(v)); - DCHECK_NOT_NULL(entry); - ImportedFunctionIndices* indices = - reinterpret_cast(entry->value); - WasmModuleBuilder::SignatureMap::iterator pos = - indices->signature_to_index_.find(sig); - if (pos != indices->signature_to_index_.end()) { - return pos->second; - } else { - uint32_t index = builder_->builder_->AddImport( - indices->name_, indices->name_length_, sig); - indices->signature_to_index_[sig] = index; - return index; + auto indices = GetEntry(v); + DCHECK_NOT_NULL(indices); + auto pos = indices->signature_to_index_.find(sig); + DCHECK(pos != indices->signature_to_index_.end()); + return pos->second; + } + + // Add a function and register it as an import with the builder. + void AddFunction(Variable* v, FunctionSig* sig) { + auto entry = table_.LookupOrInsert( + v, ComputePointerHash(v), ZoneAllocationPolicy(builder_->zone())); + if (entry->value == nullptr) { + entry->value = + new (builder_->zone()) ImportedFunctionIndices(builder_->zone()); } + auto indices = reinterpret_cast(entry->value); + DCHECK(!indices->has_name_); + auto pos = indices->signature_to_index_.find(sig); + if (pos == indices->signature_to_index_.end()) { + // A new import. Name is not known up front. + uint32_t index = builder_->builder_->AddImport(nullptr, 0, sig); + indices->signature_to_index_[sig] = index; + } + } + + ImportedFunctionIndices* GetEntry(Variable* v) { + auto entry = table_.Lookup(v, ComputePointerHash(v)); + if (entry == nullptr) return nullptr; + return reinterpret_cast(entry->value); } }; @@ -782,7 +827,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { RECURSE(Visit(value)); } - void EmitAssignment(Assignment* expr, MachineType type) { + void EmitAssignment(Assignment* expr, MachineType type, ValueFate fate) { // Match the left hand side of the assignment. VariableProxy* target_var = expr->target()->AsVariableProxy(); if (target_var != nullptr) { @@ -791,11 +836,19 @@ class AsmWasmBuilderImpl final : public AstVisitor { LocalType var_type = TypeOf(expr); DCHECK_NE(kAstStmt, var_type); if (var->IsContextSlot()) { - current_function_builder_->EmitWithVarInt( - kExprSetGlobal, LookupOrInsertGlobal(var, var_type)); + uint32_t index = LookupOrInsertGlobal(var, var_type); + current_function_builder_->EmitWithVarInt(kExprSetGlobal, index); + if (fate == kLeaveOnStack) { + current_function_builder_->EmitWithVarInt(kExprGetGlobal, index); + } } else { - current_function_builder_->EmitSetLocal( - LookupOrInsertLocal(var, var_type)); + if (fate == kDrop) { + current_function_builder_->EmitSetLocal( + LookupOrInsertLocal(var, var_type)); + } else { + current_function_builder_->EmitTeeLocal( + LookupOrInsertLocal(var, var_type)); + } } } @@ -807,6 +860,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { ->IsA(AsmType::Float32Array())) { current_function_builder_->Emit(kExprF32ConvertF64); } + // Note that unlike StoreMem, AsmjsStoreMem ignores out-of-bounds writes. WasmOpcode opcode; if (type == MachineType::Int8()) { opcode = kExprI32AsmjsStoreMem8; @@ -828,6 +882,10 @@ class AsmWasmBuilderImpl final : public AstVisitor { UNREACHABLE(); } current_function_builder_->Emit(opcode); + if (fate == kDrop) { + // Asm.js stores to memory leave their result on the stack. + current_function_builder_->Emit(kExprDrop); + } } if (target_var == nullptr && target_prop == nullptr) { @@ -836,6 +894,10 @@ class AsmWasmBuilderImpl final : public AstVisitor { } void VisitAssignment(Assignment* expr) { + VisitAssignment(expr, kLeaveOnStack); + } + + void VisitAssignment(Assignment* expr, ValueFate fate) { bool as_init = false; if (scope_ == kModuleScope) { // Skip extra assignment inserted by the parser when in this form: @@ -853,7 +915,7 @@ class AsmWasmBuilderImpl final : public AstVisitor { if (typer_->TypeOf(target)->AsFFIType() != nullptr) { const AstRawString* name = prop->key()->AsLiteral()->AsRawPropertyName(); - imported_function_table_.AddImport( + imported_function_table_.SetImportName( target->var(), reinterpret_cast(name->raw_data()), name->length()); } @@ -881,12 +943,12 @@ class AsmWasmBuilderImpl final : public AstVisitor { } if (as_init) LoadInitFunction(); - MachineType mtype; + MachineType mtype = MachineType::None(); bool is_nop = false; EmitAssignmentLhs(expr->target(), &mtype); EmitAssignmentRhs(expr->target(), expr->value(), &is_nop); if (!is_nop) { - EmitAssignment(expr, mtype); + EmitAssignment(expr, mtype, fate); } if (as_init) UnLoadInitFunction(); } @@ -1111,11 +1173,11 @@ class AsmWasmBuilderImpl final : public AstVisitor { // if set_local(tmp, x) < 0 Visit(call->arguments()->at(0)); - current_function_builder_->EmitSetLocal(tmp.index()); + current_function_builder_->EmitTeeLocal(tmp.index()); byte code[] = {WASM_I8(0)}; current_function_builder_->EmitCode(code, sizeof(code)); current_function_builder_->Emit(kExprI32LtS); - current_function_builder_->Emit(kExprIf); + current_function_builder_->EmitWithU8(kExprIf, kLocalI32); // then (0 - tmp) current_function_builder_->EmitCode(code, sizeof(code)); @@ -1147,13 +1209,13 @@ class AsmWasmBuilderImpl final : public AstVisitor { // if set_local(tmp_x, x) < set_local(tmp_y, y) Visit(call->arguments()->at(0)); - current_function_builder_->EmitSetLocal(tmp_x.index()); + current_function_builder_->EmitTeeLocal(tmp_x.index()); Visit(call->arguments()->at(1)); - current_function_builder_->EmitSetLocal(tmp_y.index()); + current_function_builder_->EmitTeeLocal(tmp_y.index()); current_function_builder_->Emit(kExprI32LeS); - current_function_builder_->Emit(kExprIf); + current_function_builder_->EmitWithU8(kExprIf, kLocalI32); // then tmp_x current_function_builder_->EmitGetLocal(tmp_x.index()); @@ -1183,13 +1245,13 @@ class AsmWasmBuilderImpl final : public AstVisitor { // if set_local(tmp_x, x) < set_local(tmp_y, y) Visit(call->arguments()->at(0)); - current_function_builder_->EmitSetLocal(tmp_x.index()); + current_function_builder_->EmitTeeLocal(tmp_x.index()); Visit(call->arguments()->at(1)); - current_function_builder_->EmitSetLocal(tmp_y.index()); + current_function_builder_->EmitTeeLocal(tmp_y.index()); current_function_builder_->Emit(kExprI32LeS); - current_function_builder_->Emit(kExprIf); + current_function_builder_->EmitWithU8(kExprIf, kLocalI32); // then tmp_y current_function_builder_->EmitGetLocal(tmp_y.index()); @@ -1275,18 +1337,20 @@ class AsmWasmBuilderImpl final : public AstVisitor { } } - void VisitCall(Call* expr) { + void VisitCall(Call* expr) { VisitCallExpression(expr); } + + bool VisitCallExpression(Call* expr) { Call::CallType call_type = expr->GetCallType(); + bool returns_value = true; switch (call_type) { case Call::OTHER_CALL: { DCHECK_EQ(kFuncScope, scope_); VariableProxy* proxy = expr->expression()->AsVariableProxy(); if (proxy != nullptr) { if (VisitStdlibFunction(expr, proxy)) { - return; + return true; } } - uint32_t index; VariableProxy* vp = expr->expression()->AsVariableProxy(); DCHECK_NOT_NULL(vp); if (typer_->TypeOf(vp)->AsFFIType() != nullptr) { @@ -1296,22 +1360,23 @@ class AsmWasmBuilderImpl final : public AstVisitor { args->length()); if (return_type != kAstStmt) { sig.AddReturn(return_type); + } else { + returns_value = false; } for (int i = 0; i < args->length(); ++i) { sig.AddParam(TypeOf(args->at(i))); } - index = + uint32_t index = imported_function_table_.GetFunctionIndex(vp->var(), sig.Build()); VisitCallArgs(expr); - current_function_builder_->Emit(kExprCallImport); - current_function_builder_->EmitVarInt(expr->arguments()->length()); + current_function_builder_->Emit(kExprCallFunction); current_function_builder_->EmitVarInt(index); } else { - index = LookupOrInsertFunction(vp->var()); + WasmFunctionBuilder* function = LookupOrInsertFunction(vp->var()); VisitCallArgs(expr); current_function_builder_->Emit(kExprCallFunction); - current_function_builder_->EmitVarInt(expr->arguments()->length()); - current_function_builder_->EmitVarInt(index); + current_function_builder_->EmitVarInt(function->func_index()); + returns_value = function->signature()->return_count() > 0; } break; } @@ -1322,18 +1387,28 @@ class AsmWasmBuilderImpl final : public AstVisitor { VariableProxy* var = p->obj()->AsVariableProxy(); DCHECK_NOT_NULL(var); FunctionTableIndices* indices = LookupFunctionTable(var->var()); - RECURSE(Visit(p->key())); + Visit(p->key()); // TODO(titzer): should use RECURSE() + + // We have to use a temporary for the correct order of evaluation. current_function_builder_->EmitI32Const(indices->start_index); current_function_builder_->Emit(kExprI32Add); + WasmTemporary tmp(current_function_builder_, kAstI32); + current_function_builder_->EmitSetLocal(tmp.index()); + VisitCallArgs(expr); + + current_function_builder_->EmitGetLocal(tmp.index()); current_function_builder_->Emit(kExprCallIndirect); - current_function_builder_->EmitVarInt(expr->arguments()->length()); current_function_builder_->EmitVarInt(indices->signature_index); + returns_value = + builder_->GetSignature(indices->signature_index)->return_count() > + 0; break; } default: UNREACHABLE(); } + return returns_value; } void VisitCallNew(CallNew* expr) { UNREACHABLE(); } @@ -1519,16 +1594,13 @@ class AsmWasmBuilderImpl final : public AstVisitor { RECURSE(Visit(GetLeft(expr))); } else { if (expr->op() == Token::COMMA) { - current_function_builder_->Emit(kExprBlock); + RECURSE(VisitForEffect(expr->left())); + RECURSE(Visit(expr->right())); + return; } - RECURSE(Visit(expr->left())); RECURSE(Visit(expr->right())); - if (expr->op() == Token::COMMA) { - current_function_builder_->Emit(kExprEnd); - } - switch (expr->op()) { BINOP_CASE(Token::ADD, Add, NON_SIGNED_BINOP, true); BINOP_CASE(Token::SUB, Sub, NON_SIGNED_BINOP, true); @@ -1728,18 +1800,33 @@ class AsmWasmBuilderImpl final : public AstVisitor { return (reinterpret_cast(entry->value))->index; } - uint32_t LookupOrInsertFunction(Variable* v) { + WasmFunctionBuilder* LookupOrInsertFunction(Variable* v) { DCHECK_NOT_NULL(builder_); ZoneHashMap::Entry* entry = functions_.Lookup(v, ComputePointerHash(v)); if (entry == nullptr) { - uint32_t index = builder_->AddFunction(); - IndexContainer* container = new (zone()) IndexContainer(); - container->index = index; + auto* func_type = typer_->TypeOf(v)->AsFunctionType(); + DCHECK_NOT_NULL(func_type); + // Build the signature for the function. + LocalType return_type = TypeFrom(func_type->ReturnType()); + const auto& arguments = func_type->Arguments(); + FunctionSig::Builder b(zone(), return_type == kAstStmt ? 0 : 1, + arguments.size()); + if (return_type != kAstStmt) b.AddReturn(return_type); + for (int i = 0; i < static_cast(arguments.size()); ++i) { + LocalType type = TypeFrom(arguments[i]); + DCHECK_NE(kAstStmt, type); + b.AddParam(type); + } + + WasmFunctionBuilder* function = builder_->AddFunction(b.Build()); entry = functions_.LookupOrInsert(v, ComputePointerHash(v), ZoneAllocationPolicy(zone())); - entry->value = container; + function->SetName( + reinterpret_cast(v->raw_name()->raw_data()), + v->raw_name()->length()); + entry->value = function; } - return (reinterpret_cast(entry->value))->index; + return (reinterpret_cast(entry->value)); } LocalType TypeOf(Expression* expr) { return TypeFrom(typer_->TypeOf(expr)); } @@ -1774,8 +1861,8 @@ class AsmWasmBuilderImpl final : public AstVisitor { AsmTyper* typer_; ZoneVector> breakable_blocks_; ZoneVector foreign_variables_; - uint32_t init_function_index_; - uint32_t foreign_init_function_index_; + WasmFunctionBuilder* init_function_; + WasmFunctionBuilder* foreign_init_function_; uint32_t next_table_index_; ZoneHashMap function_tables_; ImportedFunctionTable imported_function_table_; diff --git a/src/compiler/int64-lowering.cc b/src/compiler/int64-lowering.cc index 1b0aec4298..539a372504 100644 --- a/src/compiler/int64-lowering.cc +++ b/src/compiler/int64-lowering.cc @@ -778,6 +778,18 @@ void Int64Lowering::LowerNode(Node* node) { } break; } + case IrOpcode::kProjection: { + Node* call = node->InputAt(0); + DCHECK_EQ(IrOpcode::kCall, call->opcode()); + CallDescriptor* descriptor = + const_cast(CallDescriptorOf(call->op())); + for (size_t i = 0; i < descriptor->ReturnCount(); i++) { + if (descriptor->GetReturnType(i) == MachineType::Int64()) { + UNREACHABLE(); // TODO(titzer): implement multiple i64 returns. + } + } + break; + } case IrOpcode::kWord64ReverseBytes: { Node* input = node->InputAt(0); ReplaceNode(node, graph()->NewNode(machine()->Word32ReverseBytes().op(), diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 4e9c03a711..b1eb3e6acd 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -189,26 +189,29 @@ class WasmTrapHelper : public ZoneObject { Node* GetTrapValue(wasm::FunctionSig* sig) { if (sig->return_count() > 0) { - switch (sig->GetReturn()) { - case wasm::kAstI32: - return jsgraph()->Int32Constant(0xdeadbeef); - case wasm::kAstI64: - return jsgraph()->Int64Constant(0xdeadbeefdeadbeef); - case wasm::kAstF32: - return jsgraph()->Float32Constant(bit_cast(0xdeadbeef)); - case wasm::kAstF64: - return jsgraph()->Float64Constant( - bit_cast(0xdeadbeefdeadbeef)); - break; - default: - UNREACHABLE(); - return nullptr; - } + return GetTrapValue(sig->GetReturn()); } else { return jsgraph()->Int32Constant(0xdeadbeef); } } + Node* GetTrapValue(wasm::LocalType type) { + switch (type) { + case wasm::kAstI32: + return jsgraph()->Int32Constant(0xdeadbeef); + case wasm::kAstI64: + return jsgraph()->Int64Constant(0xdeadbeefdeadbeef); + case wasm::kAstF32: + return jsgraph()->Float32Constant(bit_cast(0xdeadbeef)); + case wasm::kAstF64: + return jsgraph()->Float64Constant(bit_cast(0xdeadbeefdeadbeef)); + break; + default: + UNREACHABLE(); + return nullptr; + } + } + private: WasmGraphBuilder* builder_; JSGraph* jsgraph_; @@ -993,16 +996,11 @@ Node* WasmGraphBuilder::Return(unsigned count, Node** vals) { DCHECK_NOT_NULL(*control_); DCHECK_NOT_NULL(*effect_); - if (count == 0) { - // Handle a return of void. - vals[0] = jsgraph()->Int32Constant(0); - count = 1; - } - Node** buf = Realloc(vals, count, count + 2); buf[count] = *effect_; buf[count + 1] = *control_; - Node* ret = graph()->NewNode(jsgraph()->common()->Return(), count + 2, vals); + Node* ret = + graph()->NewNode(jsgraph()->common()->Return(count), count + 2, vals); MergeControlToEnd(jsgraph(), ret); return ret; @@ -1994,8 +1992,8 @@ Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) { return call; } -Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args, - wasm::WasmCodePosition position) { +Node** WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args, + wasm::WasmCodePosition position) { const size_t params = sig->parameter_count(); const size_t extra = 2; // effect and control inputs. const size_t count = 1 + params + extra; @@ -2014,33 +2012,38 @@ Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args, SetSourcePosition(call, position); *effect_ = call; - return call; + size_t ret_count = sig->return_count(); + if (ret_count == 0) return nullptr; // No return value. + + Node** rets = Buffer(ret_count); + if (ret_count == 1) { + // Only a single return value. + rets[0] = call; + } else { + // Create projections for all return values. + for (size_t i = 0; i < ret_count; i++) { + rets[i] = graph()->NewNode(jsgraph()->common()->Projection(i), call, + graph()->start()); + } + } + return rets; } -Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, - wasm::WasmCodePosition position) { +Node** WasmGraphBuilder::CallDirect(uint32_t index, Node** args, + wasm::WasmCodePosition position) { DCHECK_NULL(args[0]); // Add code object as constant. - args[0] = HeapConstant(module_->GetCodeOrPlaceholder(index)); + Handle code = module_->GetFunctionCode(index); + DCHECK(!code.is_null()); + args[0] = HeapConstant(code); wasm::FunctionSig* sig = module_->GetFunctionSignature(index); return BuildWasmCall(sig, args, position); } -Node* WasmGraphBuilder::CallImport(uint32_t index, Node** args, - wasm::WasmCodePosition position) { - DCHECK_NULL(args[0]); - - // Add code object as constant. - args[0] = HeapConstant(module_->GetImportCode(index)); - wasm::FunctionSig* sig = module_->GetImportSignature(index); - - return BuildWasmCall(sig, args, position); -} - -Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, - wasm::WasmCodePosition position) { +Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, + wasm::WasmCodePosition position) { DCHECK_NOT_NULL(args[0]); DCHECK(module_ && module_->instance); @@ -2054,6 +2057,7 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, // Bounds check the index. uint32_t table_size = module_->IsValidTable(0) ? module_->GetTable(0)->max_size : 0; + wasm::FunctionSig* sig = module_->GetSignature(index); if (table_size > 0) { // Bounds check against the table size. Node* size = Uint32Constant(table_size); @@ -2062,7 +2066,11 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, } else { // No function table. Generate a trap and return a constant. trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position); - return trap_->GetTrapValue(module_->GetSignature(index)); + Node** rets = Buffer(sig->return_count()); + for (size_t i = 0; i < sig->return_count(); i++) { + rets[i] = trap_->GetTrapValue(sig->GetReturn(i)); + } + return rets; } Node* table = FunctionTable(0); @@ -2096,7 +2104,6 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, *effect_, *control_); args[0] = load_code; - wasm::FunctionSig* sig = module_->GetSignature(index); return BuildWasmCall(sig, args, position); } @@ -2693,6 +2700,11 @@ Node* WasmGraphBuilder::MemBuffer(uint32_t offset) { } } +Node* WasmGraphBuilder::CurrentMemoryPages() { + return graph()->NewNode(jsgraph()->machine()->Word32Shr(), MemSize(0), + jsgraph()->Int32Constant(16)); +} + Node* WasmGraphBuilder::MemSize(uint32_t offset) { DCHECK(module_ && module_->instance); uint32_t size = static_cast(module_->instance->mem_size); diff --git a/src/compiler/wasm-compiler.h b/src/compiler/wasm-compiler.h index e459936363..eb6c305445 100644 --- a/src/compiler/wasm-compiler.h +++ b/src/compiler/wasm-compiler.h @@ -153,12 +153,11 @@ class WasmGraphBuilder { Node* ReturnVoid(); Node* Unreachable(wasm::WasmCodePosition position); - Node* CallDirect(uint32_t index, Node** args, - wasm::WasmCodePosition position); - Node* CallImport(uint32_t index, Node** args, - wasm::WasmCodePosition position); - Node* CallIndirect(uint32_t index, Node** args, - wasm::WasmCodePosition position); + Node** CallDirect(uint32_t index, Node** args, + wasm::WasmCodePosition position); + Node** CallIndirect(uint32_t index, Node** args, + wasm::WasmCodePosition position); + void BuildJSToWasmWrapper(Handle wasm_code, wasm::FunctionSig* sig); void BuildWasmToJSWrapper(Handle target, wasm::FunctionSig* sig); @@ -170,7 +169,7 @@ class WasmGraphBuilder { //----------------------------------------------------------------------- // Operations that concern the linear memory. //----------------------------------------------------------------------- - Node* MemSize(uint32_t offset); + Node* CurrentMemoryPages(); Node* GetGlobal(uint32_t index); Node* SetGlobal(uint32_t index, Node* val); Node* LoadMem(wasm::LocalType type, MachineType memtype, Node* index, @@ -229,6 +228,7 @@ class WasmGraphBuilder { Graph* graph(); Node* String(const char* string); + Node* MemSize(uint32_t offset); Node* MemBuffer(uint32_t offset); void BoundsCheckMem(MachineType memtype, Node* index, uint32_t offset, wasm::WasmCodePosition position); @@ -240,8 +240,8 @@ class WasmGraphBuilder { Node* MaskShiftCount64(Node* node); Node* BuildCCall(MachineSignature* sig, Node** args); - Node* BuildWasmCall(wasm::FunctionSig* sig, Node** args, - wasm::WasmCodePosition position); + Node** BuildWasmCall(wasm::FunctionSig* sig, Node** args, + wasm::WasmCodePosition position); Node* BuildF32CopySign(Node* left, Node* right); Node* BuildF64CopySign(Node* left, Node* right); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 975a607ba9..ec7613ebb8 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -532,6 +532,8 @@ DEFINE_BOOL(wasm_simd_prototype, false, "enable prototype simd opcodes for wasm") DEFINE_BOOL(wasm_eh_prototype, false, "enable prototype exception handling opcodes for wasm") +DEFINE_BOOL(wasm_mv_prototype, false, + "enable prototype multi-value support for wasm") DEFINE_BOOL(wasm_trap_handler, false, "use signal handlers to catch out of bounds memory access in wasm" diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc index d762f6e3af..d2c346a3f7 100644 --- a/src/wasm/ast-decoder.cc +++ b/src/wasm/ast-decoder.cc @@ -36,6 +36,8 @@ namespace wasm { error("Invalid opcode (enable with --" #flag ")"); \ break; \ } +// TODO(titzer): this is only for intermediate migration. +#define IMPLICIT_FUNCTION_END 1 // An SsaEnv environment carries the current local variable renaming // as well as the current effect and control dependency in the TF graph. @@ -70,41 +72,68 @@ struct Value { struct Control; -// An entry on the control stack (i.e. if, block, loop, try). +struct MergeValues { + uint32_t arity; + union { + Value* array; + Value first; + } vals; // Either multiple values or a single value. + + Value& first() { + DCHECK_GT(arity, 0u); + return arity == 1 ? vals.first : vals.array[0]; + } +}; + +// IncomingBranch is used by exception handling code for managing finally's. +struct IncomingBranch { + int32_t token_value; + Control* target; + MergeValues merge; +}; + +static Value* NO_VALUE = nullptr; + +enum ControlKind { kControlIf, kControlBlock, kControlLoop, kControlTry }; + +// An entry on the control stack (i.e. if, block, loop). struct Control { const byte* pc; + ControlKind kind; int stack_depth; // stack height at the beginning of the construct. SsaEnv* end_env; // end environment for the construct. SsaEnv* false_env; // false environment (only for if). SsaEnv* catch_env; // catch environment (only for try). - TFNode* node; // result node for the construct. - LocalType type; // result type for the construct. - bool is_loop; // true if this is the inner label of a loop. - bool is_if() const { return *pc == kExprIf; } + // Values merged into the end of this control construct. + MergeValues merge; - bool is_try() const { return *pc == kExprTry; } + inline bool is_if() const { return kind == kControlIf; } + inline bool is_block() const { return kind == kControlBlock; } + inline bool is_loop() const { return kind == kControlLoop; } + inline bool is_try() const { return kind == kControlTry; } // Named constructors. static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env) { - return {pc, stack_depth, end_env, nullptr, - nullptr, nullptr, kAstEnd, false}; + return {pc, kControlBlock, stack_depth, end_env, + nullptr, nullptr, {0, {NO_VALUE}}}; } static Control If(const byte* pc, int stack_depth, SsaEnv* end_env, SsaEnv* false_env) { - return {pc, stack_depth, end_env, false_env, - nullptr, nullptr, kAstStmt, false}; + return {pc, kControlIf, stack_depth, end_env, + false_env, nullptr, {0, {NO_VALUE}}}; } static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env) { - return {pc, stack_depth, end_env, nullptr, nullptr, nullptr, kAstEnd, true}; + return {pc, kControlLoop, stack_depth, end_env, + nullptr, nullptr, {0, {NO_VALUE}}}; } static Control Try(const byte* pc, int stack_depth, SsaEnv* end_env, SsaEnv* catch_env) { - return {pc, stack_depth, end_env, nullptr, - catch_env, nullptr, kAstEnd, false}; + return {pc, kControlTry, stack_depth, end_env, + nullptr, catch_env, {0, {NO_VALUE}}}; } }; @@ -139,17 +168,18 @@ class WasmDecoder : public Decoder { } return true; } - error(pc, pc + 1, "invalid local index"); + error(pc, pc + 1, "invalid local index: %u", operand.index); return false; } inline bool Validate(const byte* pc, GlobalIndexOperand& operand) { ModuleEnv* m = module_; if (m && m->module && operand.index < m->module->globals.size()) { - operand.type = m->module->globals[operand.index].type; + operand.global = &m->module->globals[operand.index]; + operand.type = operand.global->type; return true; } - error(pc, pc + 1, "invalid global index"); + error(pc, pc + 1, "invalid global index: %u", operand.index); return false; } @@ -164,16 +194,9 @@ class WasmDecoder : public Decoder { inline bool Validate(const byte* pc, CallFunctionOperand& operand) { if (Complete(pc, operand)) { - uint32_t expected = static_cast(operand.sig->parameter_count()); - if (operand.arity != expected) { - error(pc, pc + 1, - "arity mismatch in direct function call (expected %u, got %u)", - expected, operand.arity); - return false; - } return true; } - error(pc, pc + 1, "invalid function index"); + error(pc, pc + 1, "invalid function index: %u", operand.index); return false; } @@ -188,162 +211,28 @@ class WasmDecoder : public Decoder { inline bool Validate(const byte* pc, CallIndirectOperand& operand) { if (Complete(pc, operand)) { - uint32_t expected = static_cast(operand.sig->parameter_count()); - if (operand.arity != expected) { - error(pc, pc + 1, - "arity mismatch in indirect function call (expected %u, got %u)", - expected, operand.arity); - return false; - } return true; } - error(pc, pc + 1, "invalid signature index"); - return false; - } - - inline bool Complete(const byte* pc, CallImportOperand& operand) { - ModuleEnv* m = module_; - if (m && m->module && operand.index < m->module->import_table.size()) { - operand.sig = m->module->import_table[operand.index].sig; - return true; - } - return false; - } - - inline bool Validate(const byte* pc, CallImportOperand& operand) { - if (Complete(pc, operand)) { - uint32_t expected = static_cast(operand.sig->parameter_count()); - if (operand.arity != expected) { - error(pc, pc + 1, "arity mismatch in import call (expected %u, got %u)", - expected, operand.arity); - return false; - } - return true; - } - error(pc, pc + 1, "invalid signature index"); + error(pc, pc + 1, "invalid signature index: #%u", operand.index); return false; } inline bool Validate(const byte* pc, BreakDepthOperand& operand, ZoneVector& control) { - if (operand.arity > 1) { - error(pc, pc + 1, "invalid arity for br or br_if"); - return false; - } if (operand.depth < control.size()) { operand.target = &control[control.size() - operand.depth - 1]; return true; } - error(pc, pc + 1, "invalid break depth"); + error(pc, pc + 1, "invalid break depth: %u", operand.depth); return false; } bool Validate(const byte* pc, BranchTableOperand& operand, size_t block_depth) { - if (operand.arity > 1) { - error(pc, pc + 1, "invalid arity for break"); - return false; - } - // Verify table. - for (uint32_t i = 0; i < operand.table_count + 1; ++i) { - uint32_t target = operand.read_entry(this, i); - if (target >= block_depth) { - error(operand.table + i * 2, "improper branch in br_table"); - return false; - } - } + // TODO(titzer): add extra redundant validation for br_table here? return true; } - unsigned OpcodeArity(const byte* pc) { -#define DECLARE_ARITY(name, ...) \ - static const LocalType kTypes_##name[] = {__VA_ARGS__}; \ - static const int kArity_##name = \ - static_cast(arraysize(kTypes_##name) - 1); - - FOREACH_SIGNATURE(DECLARE_ARITY); -#undef DECLARE_ARITY - - switch (static_cast(*pc)) { - case kExprI8Const: - case kExprI32Const: - case kExprI64Const: - case kExprF64Const: - case kExprF32Const: - case kExprGetLocal: - case kExprGetGlobal: - case kExprNop: - case kExprUnreachable: - case kExprEnd: - case kExprBlock: - case kExprThrow: - case kExprTry: - case kExprLoop: - return 0; - - case kExprSetGlobal: - case kExprSetLocal: - case kExprElse: - case kExprCatch: - return 1; - - case kExprBr: { - BreakDepthOperand operand(this, pc); - return operand.arity; - } - case kExprBrIf: { - BreakDepthOperand operand(this, pc); - return 1 + operand.arity; - } - case kExprBrTable: { - BranchTableOperand operand(this, pc); - return 1 + operand.arity; - } - - case kExprIf: - return 1; - case kExprSelect: - return 3; - - case kExprCallFunction: { - CallFunctionOperand operand(this, pc); - return operand.arity; - } - case kExprCallIndirect: { - CallIndirectOperand operand(this, pc); - return 1 + operand.arity; - } - case kExprCallImport: { - CallImportOperand operand(this, pc); - return operand.arity; - } - case kExprReturn: { - ReturnArityOperand operand(this, pc); - return operand.arity; - } - -#define DECLARE_OPCODE_CASE(name, opcode, sig) \ - case kExpr##name: \ - return kArity_##sig; - - FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_SIMPLE_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_ASMJS_COMPAT_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_SIMD_0_OPERAND_OPCODE(DECLARE_OPCODE_CASE) -#undef DECLARE_OPCODE_CASE -#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: - FOREACH_SIMD_1_OPERAND_OPCODE(DECLARE_OPCODE_CASE) -#undef DECLARE_OPCODE_CASE - return 1; - default: - UNREACHABLE(); - return 0; - } - } - unsigned OpcodeLength(const byte* pc) { switch (static_cast(*pc)) { #define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: @@ -373,12 +262,17 @@ class WasmDecoder : public Decoder { CallIndirectOperand operand(this, pc); return 1 + operand.length; } - case kExprCallImport: { - CallImportOperand operand(this, pc); + + case kExprTry: + case kExprIf: // fall thru + case kExprLoop: + case kExprBlock: { + BlockTypeOperand operand(this, pc); return 1 + operand.length; } case kExprSetLocal: + case kExprTeeLocal: case kExprGetLocal: case kExprCatch: { LocalIndexOperand operand(this, pc); @@ -386,7 +280,8 @@ class WasmDecoder : public Decoder { } case kExprBrTable: { BranchTableOperand operand(this, pc); - return 1 + operand.length; + BranchTableIterator iterator(this, operand); + return 1 + iterator.length(); } case kExprI32Const: { ImmI32Operand operand(this, pc); @@ -402,14 +297,6 @@ class WasmDecoder : public Decoder { return 5; case kExprF64Const: return 9; - case kExprReturn: { - ReturnArityOperand operand(this, pc); - return 1 + operand.length; - } -#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: - FOREACH_SIMD_0_OPERAND_OPCODE(DECLARE_OPCODE_CASE) { return 2; } - FOREACH_SIMD_1_OPERAND_OPCODE(DECLARE_OPCODE_CASE) { return 3; } -#undef DECLARE_OPCODE_CASE default: return 1; } @@ -427,7 +314,8 @@ class WasmFullDecoder : public WasmDecoder { base_(body.base), local_type_vec_(zone), stack_(zone), - control_(zone) { + control_(zone), + last_end_found_(false) { local_types_ = &local_type_vec_; } @@ -440,7 +328,7 @@ class WasmFullDecoder : public WasmDecoder { control_.clear(); if (end_ < pc_) { - error(pc_, "function body end < start"); + error("function body end < start"); return false; } @@ -450,23 +338,55 @@ class WasmFullDecoder : public WasmDecoder { if (failed()) return TraceFailed(); +#if IMPLICIT_FUNCTION_END + // With implicit end support (old style), the function block + // remains on the stack. Other control blocks are an error. + if (control_.size() > 1) { + error(pc_, control_.back().pc, "unterminated control structure"); + return TraceFailed(); + } + + // Assume an implicit end to the function body block. + if (control_.size() == 1) { + Control* c = &control_.back(); + if (ssa_env_->go()) { + FallThruTo(c); + } + + if (c->end_env->go()) { + // Push the end values onto the stack. + stack_.resize(c->stack_depth); + if (c->merge.arity == 1) { + stack_.push_back(c->merge.vals.first); + } else { + for (unsigned i = 0; i < c->merge.arity; i++) { + stack_.push_back(c->merge.vals.array[i]); + } + } + + TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn"); + SetEnv("function:end", c->end_env); + DoReturn(); + TRACE("\n"); + } + } +#else if (!control_.empty()) { error(pc_, control_.back().pc, "unterminated control structure"); return TraceFailed(); } - if (ssa_env_->go()) { - TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn"); - DoReturn(); - if (failed()) return TraceFailed(); - TRACE("\n"); + if (!last_end_found_) { + error("function body must end with \"end\" opcode."); + return false; } +#endif if (FLAG_trace_wasm_decode_time) { double ms = decode_timer.Elapsed().InMillisecondsF(); - PrintF("wasm-decode ok (%0.3f ms)\n\n", ms); + PrintF("wasm-decode %s (%0.3f ms)\n\n", ok() ? "ok" : "failed", ms); } else { - TRACE("wasm-decode ok\n\n"); + TRACE("wasm-decode %s\n\n", ok() ? "ok" : "failed"); } return true; @@ -519,6 +439,7 @@ class WasmFullDecoder : public WasmDecoder { ZoneVector local_type_vec_; // types of local variables. ZoneVector stack_; // stack of values. ZoneVector control_; // stack of blocks, loops, and ifs. + bool last_end_found_; inline bool build() { return builder_ && ssa_env_->go(); } @@ -639,6 +560,24 @@ class WasmFullDecoder : public WasmDecoder { reinterpret_cast(limit_), baserel(pc_), static_cast(limit_ - start_), builder_ ? "graph building" : ""); + { + // Set up initial function block. + SsaEnv* break_env = ssa_env_; + SetEnv("initial env", Steal(break_env)); + PushBlock(break_env); + Control* c = &control_.back(); + c->merge.arity = static_cast(sig_->return_count()); + + if (c->merge.arity == 1) { + c->merge.vals.first = {pc_, nullptr, sig_->GetReturn(0)}; + } else if (c->merge.arity > 1) { + c->merge.vals.array = zone_->NewArray(c->merge.arity); + for (unsigned i = 0; i < c->merge.arity; i++) { + c->merge.vals.array[i] = {pc_, nullptr, sig_->GetReturn(i)}; + } + } + } + if (pc_ >= limit_) return; // Nothing to do. while (true) { // decoding loop. @@ -656,13 +595,15 @@ class WasmFullDecoder : public WasmDecoder { // Complex bytecode. switch (opcode) { case kExprNop: - Push(kAstStmt, nullptr); break; case kExprBlock: { // The break environment is the outer environment. + BlockTypeOperand operand(this, pc_); SsaEnv* break_env = ssa_env_; PushBlock(break_env); SetEnv("block:start", Steal(break_env)); + SetBlockType(&control_.back(), operand); + len = 1 + operand.length; break; } case kExprThrow: { @@ -673,11 +614,14 @@ class WasmFullDecoder : public WasmDecoder { } case kExprTry: { CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); + BlockTypeOperand operand(this, pc_); SsaEnv* outer_env = ssa_env_; SsaEnv* try_env = Steal(outer_env); SsaEnv* catch_env = Split(try_env); PushTry(outer_env, catch_env); - SetEnv("try:start", try_env); + SetEnv("try_catch:start", try_env); + SetBlockType(&control_.back(), operand); + len = 1 + operand.length; break; } case kExprCatch: { @@ -686,18 +630,18 @@ class WasmFullDecoder : public WasmDecoder { len = 1 + operand.length; if (control_.empty()) { - error(pc_, "catch does not match a any try"); + error("catch does not match any try"); break; } Control* c = &control_.back(); if (!c->is_try()) { - error(pc_, "catch does not match a try"); + error("catch does not match any try"); break; } if (c->catch_env == nullptr) { - error(pc_, "catch already present for try with catch"); + error("catch already present for try with catch"); break; } @@ -716,23 +660,23 @@ class WasmFullDecoder : public WasmDecoder { } PopUpTo(c->stack_depth); - break; } case kExprLoop: { - // The break environment is the outer environment. - SsaEnv* break_env = ssa_env_; - PushBlock(break_env); - SsaEnv* finish_try_env = Steal(break_env); + BlockTypeOperand operand(this, pc_); + SsaEnv* finish_try_env = Steal(ssa_env_); // The continue environment is the inner environment. PrepareForLoop(pc_, finish_try_env); SetEnv("loop:start", Split(finish_try_env)); ssa_env_->SetNotMerged(); PushLoop(finish_try_env); + SetBlockType(&control_.back(), operand); + len = 1 + operand.length; break; } case kExprIf: { // Condition on top of stack. Split environments for branches. + BlockTypeOperand operand(this, pc_); Value cond = Pop(0, kAstI32); TFNode* if_true = nullptr; TFNode* if_false = nullptr; @@ -744,11 +688,13 @@ class WasmFullDecoder : public WasmDecoder { true_env->control = if_true; PushIf(end_env, false_env); SetEnv("if:true", true_env); + SetBlockType(&control_.back(), operand); + len = 1 + operand.length; break; } case kExprElse: { if (control_.empty()) { - error(pc_, "else does not match any if"); + error("else does not match any if"); break; } Control* c = &control_.back(); @@ -760,31 +706,38 @@ class WasmFullDecoder : public WasmDecoder { error(pc_, c->pc, "else already present for if"); break; } - Value val = PopUpTo(c->stack_depth); - MergeInto(c->end_env, &c->node, &c->type, val); + FallThruTo(c); // Switch to environment for false branch. + stack_.resize(c->stack_depth); SetEnv("if_else:false", c->false_env); c->false_env = nullptr; // record that an else is already seen break; } case kExprEnd: { if (control_.empty()) { - error(pc_, "end does not match any if, try, or block"); - break; + error("end does not match any if, try, or block"); + return; } const char* name = "block:end"; Control* c = &control_.back(); - Value val = PopUpTo(c->stack_depth); - if (c->is_loop) { - // Loops always push control in pairs. + if (c->is_loop()) { + // A loop just leaves the values on the stack. + TypeCheckLoopFallThru(c); PopControl(); - c = &control_.back(); - name = "loop:end"; - } else if (c->is_if()) { + SetEnv("loop:end", ssa_env_); + break; + } + if (c->is_if()) { if (c->false_env != nullptr) { // End the true branch of a one-armed if. Goto(c->false_env, c->end_env); - val = {val.pc, nullptr, kAstStmt}; + if (ssa_env_->go() && stack_.size() != c->stack_depth) { + error("end of if expected empty stack"); + stack_.resize(c->stack_depth); + } + if (c->merge.arity > 0) { + error("non-void one-armed if"); + } name = "if:merge"; } else { // End the false branch of a two-armed if. @@ -795,19 +748,39 @@ class WasmFullDecoder : public WasmDecoder { // validate that catch was seen. if (c->catch_env != nullptr) { - error(pc_, "missing catch in try"); + error("missing catch in try"); break; } } - - if (ssa_env_->go()) { - // Adds a fallthrough edge to the next control block. - MergeInto(c->end_env, &c->node, &c->type, val); - } + FallThruTo(c); SetEnv(name, c->end_env); + + // Push the end values onto the stack. stack_.resize(c->stack_depth); - Push(c->type, c->node); + if (c->merge.arity == 1) { + stack_.push_back(c->merge.vals.first); + } else { + for (unsigned i = 0; i < c->merge.arity; i++) { + stack_.push_back(c->merge.vals.array[i]); + } + } + PopControl(); + + if (control_.empty()) { + // If the last (implicit) control was popped, check we are at end. + if (pc_ + 1 != end_) { + error(pc_, pc_ + 1, "trailing code after function end"); + } + last_end_found_ = true; + if (ssa_env_->go()) { + // The result of the block is the return value. + TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn"); + DoReturn(); + TRACE("\n"); + } + return; + } break; } case kExprSelect: { @@ -816,7 +789,7 @@ class WasmFullDecoder : public WasmDecoder { Value tval = Pop(); if (tval.type == kAstStmt || tval.type != fval.type) { if (tval.type != kAstEnd && fval.type != kAstEnd) { - error(pc_, "type mismatch in select"); + error("type mismatch in select"); break; } } @@ -838,39 +811,33 @@ class WasmFullDecoder : public WasmDecoder { } case kExprBr: { BreakDepthOperand operand(this, pc_); - Value val = {pc_, nullptr, kAstStmt}; - if (operand.arity) val = Pop(); if (Validate(pc_, operand, control_)) { - BreakTo(operand.target, val); + BreakTo(operand.depth); } len = 1 + operand.length; - Push(kAstEnd, nullptr); + EndControl(); break; } case kExprBrIf: { BreakDepthOperand operand(this, pc_); - Value cond = Pop(operand.arity, kAstI32); - Value val = {pc_, nullptr, kAstStmt}; - if (operand.arity == 1) val = Pop(); + Value cond = Pop(0, kAstI32); if (ok() && Validate(pc_, operand, control_)) { SsaEnv* fenv = ssa_env_; SsaEnv* tenv = Split(fenv); fenv->SetNotMerged(); BUILD(Branch, cond.node, &tenv->control, &fenv->control); ssa_env_ = tenv; - BreakTo(operand.target, val); + BreakTo(operand.depth); ssa_env_ = fenv; } len = 1 + operand.length; - Push(kAstStmt, nullptr); break; } case kExprBrTable: { BranchTableOperand operand(this, pc_); + BranchTableIterator iterator(this, operand); if (Validate(pc_, operand, control_.size())) { - Value key = Pop(operand.arity, kAstI32); - Value val = {pc_, nullptr, kAstStmt}; - if (operand.arity == 1) val = Pop(); + Value key = Pop(0, kAstI32); if (failed()) break; SsaEnv* break_env = ssa_env_; @@ -880,42 +847,43 @@ class WasmFullDecoder : public WasmDecoder { SsaEnv* copy = Steal(break_env); ssa_env_ = copy; - for (uint32_t i = 0; i < operand.table_count + 1; ++i) { - uint16_t target = operand.read_entry(this, i); + while (iterator.has_next()) { + uint32_t i = iterator.cur_index(); + const byte* pos = iterator.pc(); + uint32_t target = iterator.next(); + if (target >= control_.size()) { + error(pos, "improper branch in br_table"); + break; + } ssa_env_ = Split(copy); ssa_env_->control = (i == operand.table_count) ? BUILD(IfDefault, sw) : BUILD(IfValue, i, sw); - int depth = target; - Control* c = &control_[control_.size() - depth - 1]; - MergeInto(c->end_env, &c->node, &c->type, val); + BreakTo(target); } } else { // Only a default target. Do the equivalent of br. - uint16_t target = operand.read_entry(this, 0); - int depth = target; - Control* c = &control_[control_.size() - depth - 1]; - MergeInto(c->end_env, &c->node, &c->type, val); + const byte* pos = iterator.pc(); + uint32_t target = iterator.next(); + if (target >= control_.size()) { + error(pos, "improper branch in br_table"); + break; + } + BreakTo(target); } // br_table ends the control flow like br. ssa_env_ = break_env; - Push(kAstStmt, nullptr); } - len = 1 + operand.length; + len = 1 + iterator.length(); break; } case kExprReturn: { - ReturnArityOperand operand(this, pc_); - if (operand.arity != sig_->return_count()) { - error(pc_, pc_ + 1, "arity mismatch in return"); - } DoReturn(); - len = 1 + operand.length; break; } case kExprUnreachable: { - Push(kAstEnd, BUILD(Unreachable, position())); - ssa_env_->Kill(SsaEnv::kControlEnd); + BUILD(Unreachable, position()); + EndControl(); break; } case kExprI8Const: { @@ -961,6 +929,15 @@ class WasmFullDecoder : public WasmDecoder { break; } case kExprSetLocal: { + LocalIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + Value val = Pop(0, local_type_vec_[operand.index]); + if (ssa_env_->locals) ssa_env_->locals[operand.index] = val.node; + } + len = 1 + operand.length; + break; + } + case kExprTeeLocal: { LocalIndexOperand operand(this, pc_); if (Validate(pc_, operand)) { Value val = Pop(0, local_type_vec_[operand.index]); @@ -970,6 +947,10 @@ class WasmFullDecoder : public WasmDecoder { len = 1 + operand.length; break; } + case kExprDrop: { + Pop(); + break; + } case kExprGetGlobal: { GlobalIndexOperand operand(this, pc_); if (Validate(pc_, operand)) { @@ -981,9 +962,13 @@ class WasmFullDecoder : public WasmDecoder { case kExprSetGlobal: { GlobalIndexOperand operand(this, pc_); if (Validate(pc_, operand)) { - Value val = Pop(0, operand.type); - BUILD(SetGlobal, operand.index, val.node); - Push(val.type, val.node); + if (operand.global->mutability) { + Value val = Pop(0, operand.type); + BUILD(SetGlobal, operand.index, val.node); + } else { + error(pc_, pc_ + 1, "immutable global #%u cannot be assigned", + operand.index); + } } len = 1 + operand.length; break; @@ -1003,7 +988,6 @@ class WasmFullDecoder : public WasmDecoder { case kExprI32LoadMem: len = DecodeLoadMem(kAstI32, MachineType::Int32()); break; - case kExprI64LoadMem8S: len = DecodeLoadMem(kAstI64, MachineType::Int8()); break; @@ -1067,15 +1051,15 @@ class WasmFullDecoder : public WasmDecoder { } break; case kExprMemorySize: - Push(kAstI32, BUILD(MemSize, 0)); + Push(kAstI32, BUILD(CurrentMemoryPages)); break; case kExprCallFunction: { CallFunctionOperand operand(this, pc_); if (Validate(pc_, operand)) { TFNode** buffer = PopArgs(operand.sig); - TFNode* call = + TFNode** rets = BUILD(CallDirect, operand.index, buffer, position()); - Push(GetReturnType(operand.sig), call); + PushReturns(operand.sig, rets); } len = 1 + operand.length; break; @@ -1083,23 +1067,12 @@ class WasmFullDecoder : public WasmDecoder { case kExprCallIndirect: { CallIndirectOperand operand(this, pc_); if (Validate(pc_, operand)) { - TFNode** buffer = PopArgs(operand.sig); Value index = Pop(0, kAstI32); - if (buffer) buffer[0] = index.node; - TFNode* call = - BUILD(CallIndirect, operand.index, buffer, position()); - Push(GetReturnType(operand.sig), call); - } - len = 1 + operand.length; - break; - } - case kExprCallImport: { - CallImportOperand operand(this, pc_); - if (Validate(pc_, operand)) { TFNode** buffer = PopArgs(operand.sig); - TFNode* call = - BUILD(CallImport, operand.index, buffer, position()); - Push(GetReturnType(operand.sig), call); + if (buffer) buffer[0] = index.node; + TFNode** rets = + BUILD(CallIndirect, operand.index, buffer, position()); + PushReturns(operand.sig, rets); } len = 1 + operand.length; break; @@ -1114,9 +1087,9 @@ class WasmFullDecoder : public WasmDecoder { len += DecodeSimdOpcode(opcode); break; } - default: + default: { // Deal with special asmjs opcodes. - if (module_->origin == kAsmJsOrigin) { + if (module_ && module_->origin == kAsmJsOrigin) { sig = WasmOpcodes::AsmjsSignature(opcode); if (sig) { BuildSimpleOperator(opcode, sig); @@ -1125,8 +1098,9 @@ class WasmFullDecoder : public WasmDecoder { error("Invalid opcode"); return; } + } } - } // end complex bytecode + } #if DEBUG if (FLAG_trace_wasm_decoder) { @@ -1150,7 +1124,8 @@ class WasmFullDecoder : public WasmDecoder { PrintF("[%u]", operand.index); break; } - case kExprSetLocal: { + case kExprSetLocal: // fallthru + case kExprTeeLocal: { LocalIndexOperand operand(this, val.pc); PrintF("[%u]", operand.index); break; @@ -1169,7 +1144,21 @@ class WasmFullDecoder : public WasmDecoder { return; } } // end decode loop - } // end DecodeFunctionBody() + } + + void EndControl() { ssa_env_->Kill(SsaEnv::kControlEnd); } + + void SetBlockType(Control* c, BlockTypeOperand& operand) { + c->merge.arity = operand.arity; + if (c->merge.arity == 1) { + c->merge.vals.first = {pc_, nullptr, operand.read_entry(0)}; + } else if (c->merge.arity > 1) { + c->merge.vals.array = zone_->NewArray(c->merge.arity); + for (unsigned i = 0; i < c->merge.arity; i++) { + c->merge.vals.array[i] = {pc_, nullptr, operand.read_entry(i)}; + } + } + } TFNode** PopArgs(FunctionSig* sig) { if (build()) { @@ -1233,7 +1222,6 @@ class WasmFullDecoder : public WasmDecoder { Value index = Pop(0, kAstI32); BUILD(StoreMem, mem_type, index.node, operand.offset, operand.alignment, val.node, position()); - Push(type, val.node); return 1 + operand.length; } @@ -1262,7 +1250,7 @@ class WasmFullDecoder : public WasmDecoder { TFNode* node = BUILD(SimdOp, opcode, inputs); Push(GetReturnType(sig), node); } else { - error(pc_, pc_, "invalid simd opcode"); + error("invalid simd opcode"); } } } @@ -1280,12 +1268,21 @@ class WasmFullDecoder : public WasmDecoder { if (buffer) buffer[i] = val.node; } - Push(kAstEnd, BUILD(Return, count, buffer)); - ssa_env_->Kill(SsaEnv::kControlEnd); + BUILD(Return, count, buffer); + EndControl(); } void Push(LocalType type, TFNode* node) { - stack_.push_back({pc_, node, type}); + if (type != kAstStmt && type != kAstEnd) { + stack_.push_back({pc_, node, type}); + } + } + + void PushReturns(FunctionSig* sig, TFNode** rets) { + for (size_t i = 0; i < sig->return_count(); i++) { + // When verifying only, then {rets} will be null, so push null. + Push(sig->GetReturn(i), rets ? rets[i] : nullptr); + } } const char* SafeOpcodeNameAt(const byte* pc) { @@ -1294,6 +1291,10 @@ class WasmFullDecoder : public WasmDecoder { } Value Pop(int index, LocalType expected) { + if (!ssa_env_->go()) { + // Unreachable code is essentially not typechecked. + return {pc_, nullptr, expected}; + } Value val = Pop(); if (val.type != expected) { if (val.type != kAstEnd) { @@ -1306,6 +1307,10 @@ class WasmFullDecoder : public WasmDecoder { } Value Pop() { + if (!ssa_env_->go()) { + // Unreachable code is essentially not typechecked. + return {pc_, nullptr, kAstEnd}; + } size_t limit = control_.empty() ? 0 : control_.back().stack_depth; if (stack_.size() <= limit) { Value val = {pc_, nullptr, kAstStmt}; @@ -1318,6 +1323,10 @@ class WasmFullDecoder : public WasmDecoder { } Value PopUpTo(int stack_depth) { + if (!ssa_env_->go()) { + // Unreachable code is essentially not typechecked. + return {pc_, nullptr, kAstEnd}; + } if (stack_depth == stack_.size()) { Value val = {pc_, nullptr, kAstStmt}; return val; @@ -1335,35 +1344,82 @@ class WasmFullDecoder : public WasmDecoder { int startrel(const byte* ptr) { return static_cast(ptr - start_); } - void BreakTo(Control* block, const Value& val) { - if (block->is_loop) { + void BreakTo(unsigned depth) { + if (!ssa_env_->go()) return; + Control* c = &control_[control_.size() - depth - 1]; + if (c->is_loop()) { // This is the inner loop block, which does not have a value. - Goto(ssa_env_, block->end_env); + Goto(ssa_env_, c->end_env); } else { - // Merge the value into the production for the block. - MergeInto(block->end_env, &block->node, &block->type, val); + // Merge the value(s) into the end of the block. + if (static_cast(c->stack_depth + c->merge.arity) > + stack_.size()) { + error( + pc_, pc_, + "expected at least %d values on the stack for br to @%d, found %d", + c->merge.arity, startrel(c->pc), + static_cast(stack_.size() - c->stack_depth)); + return; + } + MergeValuesInto(c); } } - void MergeInto(SsaEnv* target, TFNode** node, LocalType* type, - const Value& val) { + void FallThruTo(Control* c) { if (!ssa_env_->go()) return; - DCHECK_NE(kAstEnd, val.type); + // Merge the value(s) into the end of the block. + int arity = static_cast(c->merge.arity); + if (c->stack_depth + arity != stack_.size()) { + error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d", + arity, startrel(c->pc)); + return; + } + MergeValuesInto(c); + } + inline Value& GetMergeValueFromStack(Control* c, int i) { + return stack_[stack_.size() - c->merge.arity + i]; + } + + void TypeCheckLoopFallThru(Control* c) { + if (!ssa_env_->go()) return; + // Fallthru must match arity exactly. + int arity = static_cast(c->merge.arity); + if (c->stack_depth + arity != stack_.size()) { + error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d", + arity, startrel(c->pc)); + return; + } + // Typecheck the values left on the stack. + for (unsigned i = 0; i < c->merge.arity; i++) { + Value& val = GetMergeValueFromStack(c, i); + Value& old = + c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i]; + if (val.type != old.type) { + error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i, + WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type)); + return; + } + } + } + + void MergeValuesInto(Control* c) { + SsaEnv* target = c->end_env; bool first = target->state == SsaEnv::kUnreachable; Goto(ssa_env_, target); - if (first) { - // first merge to this environment; set the type and the node. - *type = val.type; - *node = val.node; - } else if (val.type == *type && val.type != kAstStmt) { - // merge with the existing value for this block. - *node = CreateOrMergeIntoPhi(*type, target->control, *node, val.node); - } else { - // types don't match, or block is already a stmt. - *type = kAstStmt; - *node = nullptr; + for (unsigned i = 0; i < c->merge.arity; i++) { + Value& val = GetMergeValueFromStack(c, i); + Value& old = + c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i]; + if (val.type != old.type) { + error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i, + WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type)); + return; + } + old.node = + first ? val.node : CreateOrMergeIntoPhi(old.type, target->control, + old.node, val.node); } } @@ -1592,10 +1648,11 @@ class WasmFullDecoder : public WasmDecoder { case kExprIf: case kExprBlock: case kExprTry: + length = OpcodeLength(pc); depth++; - DCHECK_EQ(1, OpcodeLength(pc)); break; - case kExprSetLocal: { + case kExprSetLocal: // fallthru + case kExprTeeLocal: { LocalIndexOperand operand(this, pc); if (assigned->length() > 0 && operand.index < static_cast(assigned->length())) { @@ -1688,11 +1745,6 @@ unsigned OpcodeLength(const byte* pc, const byte* end) { return decoder.OpcodeLength(pc); } -unsigned OpcodeArity(const byte* pc, const byte* end) { - WasmDecoder decoder(nullptr, nullptr, pc, end); - return decoder.OpcodeArity(pc); -} - void PrintAstForDebugging(const byte* start, const byte* end) { AccountingAllocator allocator; OFStream os(stdout); @@ -1758,66 +1810,57 @@ bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body, } switch (opcode) { - case kExprIf: case kExprElse: - case kExprLoop: - case kExprBlock: - case kExprTry: os << " // @" << i.pc_offset(); control_depth++; break; + case kExprLoop: + case kExprIf: + case kExprBlock: + case kExprTry: { + BlockTypeOperand operand(&i, i.pc()); + os << " // @" << i.pc_offset(); + for (unsigned i = 0; i < operand.arity; i++) { + os << " " << WasmOpcodes::TypeName(operand.read_entry(i)); + } + control_depth++; + break; + } case kExprEnd: os << " // @" << i.pc_offset(); control_depth--; break; case kExprBr: { BreakDepthOperand operand(&i, i.pc()); - os << " // arity=" << operand.arity << " depth=" << operand.depth; + os << " // depth=" << operand.depth; break; } case kExprBrIf: { BreakDepthOperand operand(&i, i.pc()); - os << " // arity=" << operand.arity << " depth" << operand.depth; + os << " // depth=" << operand.depth; break; } case kExprBrTable: { BranchTableOperand operand(&i, i.pc()); - os << " // arity=" << operand.arity - << " entries=" << operand.table_count; + os << " // entries=" << operand.table_count; break; } case kExprCallIndirect: { CallIndirectOperand operand(&i, i.pc()); + os << " // sig #" << operand.index; if (decoder.Complete(i.pc(), operand)) { - os << " // sig #" << operand.index << ": " << *operand.sig; - } else { - os << " // arity=" << operand.arity << " sig #" << operand.index; - } - break; - } - case kExprCallImport: { - CallImportOperand operand(&i, i.pc()); - if (decoder.Complete(i.pc(), operand)) { - os << " // import #" << operand.index << ": " << *operand.sig; - } else { - os << " // arity=" << operand.arity << " import #" << operand.index; + os << ": " << *operand.sig; } break; } case kExprCallFunction: { CallFunctionOperand operand(&i, i.pc()); + os << " // function #" << operand.index; if (decoder.Complete(i.pc(), operand)) { - os << " // function #" << operand.index << ": " << *operand.sig; - } else { - os << " // arity=" << operand.arity << " function #" << operand.index; + os << ": " << *operand.sig; } break; } - case kExprReturn: { - ReturnArityOperand operand(&i, i.pc()); - os << " // arity=" << operand.arity; - break; - } default: break; } diff --git a/src/wasm/ast-decoder.h b/src/wasm/ast-decoder.h index a6424f75be..395339a268 100644 --- a/src/wasm/ast-decoder.h +++ b/src/wasm/ast-decoder.h @@ -22,6 +22,7 @@ class WasmGraphBuilder; namespace wasm { const uint32_t kMaxNumWasmLocals = 8000000; +struct WasmGlobal; // Helpers for decoding different kinds of operands which follow bytecodes. struct LocalIndexOperand { @@ -81,39 +82,111 @@ struct ImmF64Operand { struct GlobalIndexOperand { uint32_t index; LocalType type; + const WasmGlobal* global; unsigned length; inline GlobalIndexOperand(Decoder* decoder, const byte* pc) { index = decoder->checked_read_u32v(pc, 1, &length, "global index"); + global = nullptr; type = kAstStmt; } }; +struct BlockTypeOperand { + uint32_t arity; + const byte* types; // pointer to encoded types for the block. + unsigned length; + + inline BlockTypeOperand(Decoder* decoder, const byte* pc) { + uint8_t val = decoder->checked_read_u8(pc, 1, "block type"); + LocalType type = kAstStmt; + length = 1; + arity = 0; + types = nullptr; + if (decode_local_type(val, &type)) { + arity = type == kAstStmt ? 0 : 1; + types = pc + 1; + } else { + // Handle multi-value blocks. + if (!FLAG_wasm_mv_prototype) { + decoder->error(pc, pc + 1, "invalid block arity > 1"); + return; + } + if (val != kMultivalBlock) { + decoder->error(pc, pc + 1, "invalid block type"); + return; + } + // Decode and check the types vector of the block. + unsigned len = 0; + uint32_t count = decoder->checked_read_u32v(pc, 2, &len, "block arity"); + // {count} is encoded as {arity-2}, so that a {0} count here corresponds + // to a block with 2 values. This makes invalid/redundant encodings + // impossible. + arity = count + 2; + length = 1 + len + arity; + types = pc + 1 + 1 + len; + + for (uint32_t i = 0; i < arity; i++) { + uint32_t offset = 1 + 1 + len + i; + val = decoder->checked_read_u8(pc, offset, "block type"); + decode_local_type(val, &type); + if (type == kAstStmt) { + decoder->error(pc, pc + offset, "invalid block type"); + return; + } + } + } + } + // Decode a byte representing a local type. Return {false} if the encoded + // byte was invalid or {kMultivalBlock}. + bool decode_local_type(uint8_t val, LocalType* result) { + switch (static_cast(val)) { + case kLocalVoid: + *result = kAstStmt; + return true; + case kLocalI32: + *result = kAstI32; + return true; + case kLocalI64: + *result = kAstI64; + return true; + case kLocalF32: + *result = kAstF32; + return true; + case kLocalF64: + *result = kAstF64; + return true; + default: + *result = kAstStmt; + return false; + } + } + LocalType read_entry(unsigned index) { + DCHECK_LT(index, arity); + LocalType result; + CHECK(decode_local_type(types[index], &result)); + return result; + } +}; + struct Control; struct BreakDepthOperand { - uint32_t arity; uint32_t depth; Control* target; unsigned length; inline BreakDepthOperand(Decoder* decoder, const byte* pc) { - unsigned len1 = 0; - unsigned len2 = 0; - arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); - depth = decoder->checked_read_u32v(pc, 1 + len1, &len2, "break depth"); - length = len1 + len2; + depth = decoder->checked_read_u32v(pc, 1, &length, "break depth"); target = nullptr; } }; struct CallIndirectOperand { - uint32_t arity; uint32_t index; FunctionSig* sig; unsigned length; inline CallIndirectOperand(Decoder* decoder, const byte* pc) { unsigned len1 = 0; unsigned len2 = 0; - arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "signature index"); length = len1 + len2; sig = nullptr; @@ -121,59 +194,32 @@ struct CallIndirectOperand { }; struct CallFunctionOperand { - uint32_t arity; uint32_t index; FunctionSig* sig; unsigned length; inline CallFunctionOperand(Decoder* decoder, const byte* pc) { unsigned len1 = 0; unsigned len2 = 0; - arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "function index"); length = len1 + len2; sig = nullptr; } }; -struct CallImportOperand { - uint32_t arity; - uint32_t index; - FunctionSig* sig; - unsigned length; - inline CallImportOperand(Decoder* decoder, const byte* pc) { - unsigned len1 = 0; - unsigned len2 = 0; - arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); - index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "import index"); - length = len1 + len2; - sig = nullptr; - } -}; - struct BranchTableOperand { - uint32_t arity; uint32_t table_count; + const byte* start; const byte* table; - unsigned length; inline BranchTableOperand(Decoder* decoder, const byte* pc) { + DCHECK_EQ(kExprBrTable, decoder->checked_read_u8(pc, 0, "opcode")); + start = pc + 1; unsigned len1 = 0; - unsigned len2 = 0; - arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); - table_count = - decoder->checked_read_u32v(pc, 1 + len1, &len2, "table count"); + table_count = decoder->checked_read_u32v(pc, 1, &len1, "table count"); if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 || - len1 + len2 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) { + len1 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) { decoder->error(pc, "branch table size overflow"); } - length = len1 + len2 + (table_count + 1) * sizeof(uint32_t); - - uint32_t table_start = 1 + len1 + len2; - if (decoder->check(pc, table_start, (table_count + 1) * sizeof(uint32_t), - "expected ")) { - table = pc + table_start; - } else { - table = nullptr; - } + table = pc + 1 + len1; } inline uint32_t read_entry(Decoder* decoder, unsigned i) { DCHECK(i <= table_count); @@ -181,6 +227,43 @@ struct BranchTableOperand { } }; +// A helper to iterate over a branch table. +class BranchTableIterator { + public: + unsigned cur_index() { return index_; } + bool has_next() { return index_ <= table_count_; } + uint32_t next() { + DCHECK(has_next()); + index_++; + unsigned length = 0; + uint32_t result = + decoder_->checked_read_u32v(pc_, 0, &length, "branch table entry"); + pc_ += length; + return result; + } + // length, including the length of the {BranchTableOperand}, but not the + // opcode. + unsigned length() { + while (has_next()) next(); + return static_cast(pc_ - start_); + } + const byte* pc() { return pc_; } + + BranchTableIterator(Decoder* decoder, BranchTableOperand& operand) + : decoder_(decoder), + start_(operand.start), + pc_(operand.table), + index_(0), + table_count_(operand.table_count) {} + + private: + Decoder* decoder_; + const byte* start_; + const byte* pc_; + uint32_t index_; // the current index. + uint32_t table_count_; // the count of entries, not including default. +}; + struct MemoryAccessOperand { uint32_t alignment; uint32_t offset; @@ -203,15 +286,6 @@ struct MemoryAccessOperand { } }; -struct ReturnArityOperand { - uint32_t arity; - unsigned length; - - inline ReturnArityOperand(Decoder* decoder, const byte* pc) { - arity = decoder->checked_read_u32v(pc, 1, &length, "return count"); - } -}; - typedef compiler::WasmGraphBuilder TFBuilder; struct ModuleEnv; // forward declaration of module interface. @@ -284,9 +358,6 @@ BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals, // Computes the length of the opcode at the given address. unsigned OpcodeLength(const byte* pc, const byte* end); -// Computes the arity (number of sub-nodes) of the opcode at the given address. -unsigned OpcodeArity(const byte* pc, const byte* end); - // A simple forward iterator for bytecodes. class BytecodeIterator : public Decoder { public: diff --git a/src/wasm/decoder.h b/src/wasm/decoder.h index 6a99ef7650..d5c9f43c57 100644 --- a/src/wasm/decoder.h +++ b/src/wasm/decoder.h @@ -208,6 +208,19 @@ class Decoder { // Consume {size} bytes and send them to the bit bucket, advancing {pc_}. void consume_bytes(int size) { + TRACE(" +%d %-20s: %d bytes\n", static_cast(pc_ - start_), "skip", + size); + if (checkAvailable(size)) { + pc_ += size; + } else { + pc_ = limit_; + } + } + + // Consume {size} bytes and send them to the bit bucket, advancing {pc_}. + void consume_bytes(uint32_t size, const char* name = "skip") { + TRACE(" +%d %-20s: %d bytes\n", static_cast(pc_ - start_), name, + size); if (checkAvailable(size)) { pc_ += size; } else { diff --git a/src/wasm/encoder.cc b/src/wasm/encoder.cc index a9b6c89792..6f91793f21 100644 --- a/src/wasm/encoder.cc +++ b/src/wasm/encoder.cc @@ -30,15 +30,11 @@ namespace v8 { namespace internal { namespace wasm { -// Emit a section name and the size as a padded varint that can be patched +// Emit a section code and the size as a padded varint that can be patched // later. -size_t EmitSection(WasmSection::Code code, ZoneBuffer& buffer) { - // Emit the section name. - const char* name = WasmSection::getName(code); - TRACE("emit section: %s\n", name); - size_t length = WasmSection::getNameLength(code); - buffer.write_size(length); // Section name string size. - buffer.write(reinterpret_cast(name), length); +size_t EmitSection(WasmSectionCode code, ZoneBuffer& buffer) { + // Emit the section code. + buffer.write_u8(code); // Emit a placeholder for the length. return buffer.reserve_u32v(); @@ -55,6 +51,8 @@ WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder) locals_(builder->zone()), signature_index_(0), exported_(0), + func_index_(static_cast(builder->imports_.size() + + builder->functions_.size())), body_(builder->zone()), name_(builder->zone()), i32_temps_(builder->zone()), @@ -90,6 +88,10 @@ void WasmFunctionBuilder::EmitSetLocal(uint32_t local_index) { EmitWithVarInt(kExprSetLocal, local_index); } +void WasmFunctionBuilder::EmitTeeLocal(uint32_t local_index) { + EmitWithVarInt(kExprTeeLocal, local_index); +} + void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size) { for (size_t i = 0; i < code_size; ++i) { body_.push_back(code[i]); @@ -143,14 +145,14 @@ void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const { buffer.write_u32v(signature_index_); } -void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer, - uint32_t func_index) const { +void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer) const { if (exported_) { - buffer.write_u32v(func_index); buffer.write_size(name_.size()); if (name_.size() > 0) { buffer.write(reinterpret_cast(&name_[0]), name_.size()); } + buffer.write_u8(kExternalFunction); + buffer.write_u32v(func_index_); } } @@ -175,7 +177,10 @@ WasmDataSegmentEncoder::WasmDataSegmentEncoder(Zone* zone, const byte* data, } void WasmDataSegmentEncoder::Write(ZoneBuffer& buffer) const { + buffer.write_u8(0); // linear memory zero + buffer.write_u8(kExprI32Const); buffer.write_u32v(dest_); + buffer.write_u8(kExprEnd); buffer.write_u32v(static_cast(data_.size())); buffer.write(&data_[0], data_.size()); } @@ -191,17 +196,11 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone) signature_map_(zone), start_function_index_(-1) {} -uint32_t WasmModuleBuilder::AddFunction() { +WasmFunctionBuilder* WasmModuleBuilder::AddFunction(FunctionSig* sig) { functions_.push_back(new (zone_) WasmFunctionBuilder(this)); - return static_cast(functions_.size() - 1); -} - -WasmFunctionBuilder* WasmModuleBuilder::FunctionAt(size_t index) { - if (functions_.size() > index) { - return functions_.at(index); - } else { - return nullptr; - } + // Add the signature if one was provided here. + if (sig) functions_.back()->SetSignature(sig); + return functions_.back(); } void WasmModuleBuilder::AddDataSegment(WasmDataSegmentEncoder* data) { @@ -243,16 +242,18 @@ void WasmModuleBuilder::AddIndirectFunction(uint32_t index) { uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length, FunctionSig* sig) { + DCHECK_EQ(0, functions_.size()); // imports must be added before functions! imports_.push_back({AddSignature(sig), name, name_length}); return static_cast(imports_.size() - 1); } -void WasmModuleBuilder::MarkStartFunction(uint32_t index) { - start_function_index_ = index; +void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) { + start_function_index_ = function->func_index(); } -uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported) { - globals_.push_back(std::make_pair(type, exported)); +uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported, + bool mutability) { + globals_.push_back(std::make_tuple(type, exported, mutability)); return static_cast(globals_.size() - 1); } @@ -266,7 +267,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const { // == Emit signatures ======================================================== if (signatures_.size() > 0) { - size_t start = EmitSection(WasmSection::Code::Signatures, buffer); + size_t start = EmitSection(kTypeSectionCode, buffer); buffer.write_size(signatures_.size()); for (FunctionSig* sig : signatures_) { @@ -283,86 +284,130 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const { FixupSection(buffer, start); } - // == Emit globals =========================================================== - if (globals_.size() > 0) { - size_t start = EmitSection(WasmSection::Code::Globals, buffer); - buffer.write_size(globals_.size()); - - for (auto global : globals_) { - buffer.write_u32v(0); // Length of the global name. - buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(global.first)); - buffer.write_u8(global.second); - } - FixupSection(buffer, start); - } - // == Emit imports =========================================================== if (imports_.size() > 0) { - size_t start = EmitSection(WasmSection::Code::ImportTable, buffer); + size_t start = EmitSection(kImportSectionCode, buffer); buffer.write_size(imports_.size()); for (auto import : imports_) { - buffer.write_u32v(import.sig_index); - buffer.write_u32v(import.name_length); - buffer.write(reinterpret_cast(import.name), + buffer.write_u32v(import.name_length); // module name length + buffer.write(reinterpret_cast(import.name), // module name import.name_length); - buffer.write_u32v(0); + buffer.write_u32v(0); // field name length + buffer.write_u8(kExternalFunction); + buffer.write_u32v(import.sig_index); } FixupSection(buffer, start); } // == Emit function signatures =============================================== + bool has_names = false; if (functions_.size() > 0) { - size_t start = EmitSection(WasmSection::Code::FunctionSignatures, buffer); + size_t start = EmitSection(kFunctionSectionCode, buffer); buffer.write_size(functions_.size()); for (auto function : functions_) { function->WriteSignature(buffer); if (function->exported()) exports++; + if (function->name_.size() > 0) has_names = true; } FixupSection(buffer, start); } // == emit function table ==================================================== if (indirect_functions_.size() > 0) { - size_t start = EmitSection(WasmSection::Code::FunctionTable, buffer); + size_t start = EmitSection(kTableSectionCode, buffer); + buffer.write_u8(1); // table count + buffer.write_u8(kWasmAnyFunctionTypeForm); + buffer.write_u8(kResizableMaximumFlag); + buffer.write_size(indirect_functions_.size()); buffer.write_size(indirect_functions_.size()); - - for (auto index : indirect_functions_) { - buffer.write_u32v(index); - } FixupSection(buffer, start); } // == emit memory declaration ================================================ { - size_t start = EmitSection(WasmSection::Code::Memory, buffer); + size_t start = EmitSection(kMemorySectionCode, buffer); + buffer.write_u8(1); // memory count + buffer.write_u32v(kResizableMaximumFlag); buffer.write_u32v(16); // min memory size buffer.write_u32v(16); // max memory size - buffer.write_u8(0); // memory export - static_assert(kDeclMemorySize == 3, "memory size must match emit above"); + FixupSection(buffer, start); + } + + // == Emit globals =========================================================== + if (globals_.size() > 0) { + size_t start = EmitSection(kGlobalSectionCode, buffer); + buffer.write_size(globals_.size()); + + for (auto global : globals_) { + static const int kLocalTypeIndex = 0; + static const int kMutabilityIndex = 2; + buffer.write_u8( + WasmOpcodes::LocalTypeCodeFor(std::get(global))); + buffer.write_u8(std::get(global)); + switch (std::get(global)) { + case kAstI32: { + static const byte code[] = {WASM_I32V_1(0)}; + buffer.write(code, sizeof(code)); + break; + } + case kAstF32: { + static const byte code[] = {WASM_F32(0)}; + buffer.write(code, sizeof(code)); + break; + } + case kAstI64: { + static const byte code[] = {WASM_I64V_1(0)}; + buffer.write(code, sizeof(code)); + break; + } + case kAstF64: { + static const byte code[] = {WASM_F64(0.0)}; + buffer.write(code, sizeof(code)); + break; + } + default: + UNREACHABLE(); + } + buffer.write_u8(kExprEnd); + } FixupSection(buffer, start); } // == emit exports =========================================================== if (exports > 0) { - size_t start = EmitSection(WasmSection::Code::ExportTable, buffer); + size_t start = EmitSection(kExportSectionCode, buffer); buffer.write_u32v(exports); - uint32_t index = 0; - for (auto function : functions_) { - function->WriteExport(buffer, index++); - } + for (auto function : functions_) function->WriteExport(buffer); FixupSection(buffer, start); } // == emit start function index ============================================== if (start_function_index_ >= 0) { - size_t start = EmitSection(WasmSection::Code::StartFunction, buffer); + size_t start = EmitSection(kStartSectionCode, buffer); buffer.write_u32v(start_function_index_); FixupSection(buffer, start); } + // == emit function table elements =========================================== + if (indirect_functions_.size() > 0) { + size_t start = EmitSection(kElementSectionCode, buffer); + buffer.write_u8(1); // count of entries + buffer.write_u8(0); // table index + buffer.write_u8(kExprI32Const); // offset + buffer.write_u32v(0); + buffer.write_u8(kExprEnd); + buffer.write_size(indirect_functions_.size()); // element count + + for (auto index : indirect_functions_) { + buffer.write_u32v(index); + } + + FixupSection(buffer, start); + } + // == emit code ============================================================== if (functions_.size() > 0) { - size_t start = EmitSection(WasmSection::Code::FunctionBodies, buffer); + size_t start = EmitSection(kCodeSectionCode, buffer); buffer.write_size(functions_.size()); for (auto function : functions_) { function->WriteBody(buffer); @@ -372,7 +417,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const { // == emit data segments ===================================================== if (data_segments_.size() > 0) { - size_t start = EmitSection(WasmSection::Code::DataSegments, buffer); + size_t start = EmitSection(kDataSectionCode, buffer); buffer.write_size(data_segments_.size()); for (auto segment : data_segments_) { @@ -380,6 +425,28 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const { } FixupSection(buffer, start); } + + // == Emit names ============================================================= + if (has_names) { + // Emit the section code. + buffer.write_u8(kUnknownSectionCode); + // Emit a placeholder for the length. + size_t start = buffer.reserve_u32v(); + // Emit the section string. + buffer.write_size(4); + buffer.write(reinterpret_cast("name"), 4); + // Emit the names. + buffer.write_size(functions_.size()); + for (auto function : functions_) { + buffer.write_size(function->name_.size()); + if (function->name_.size() > 0) { + buffer.write(reinterpret_cast(&function->name_[0]), + function->name_.size()); + } + buffer.write_u8(0); + } + FixupSection(buffer, start); + } } } // namespace wasm } // namespace internal diff --git a/src/wasm/encoder.h b/src/wasm/encoder.h index 8f0fd49bdd..f24a847700 100644 --- a/src/wasm/encoder.h +++ b/src/wasm/encoder.h @@ -121,19 +121,22 @@ class WasmFunctionBuilder : public ZoneObject { void Emit(WasmOpcode opcode); void EmitGetLocal(uint32_t index); void EmitSetLocal(uint32_t index); + void EmitTeeLocal(uint32_t index); void EmitI32Const(int32_t val); void EmitWithU8(WasmOpcode opcode, const byte immediate); void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2); void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate); void SetExported(); void SetName(const char* name, int name_length); - bool exported() { return exported_; } - // Writing methods. void WriteSignature(ZoneBuffer& buffer) const; - void WriteExport(ZoneBuffer& buffer, uint32_t func_index) const; + void WriteExport(ZoneBuffer& buffer) const; void WriteBody(ZoneBuffer& buffer) const; + bool exported() { return exported_; } + uint32_t func_index() { return func_index_; } + FunctionSig* signature(); + private: explicit WasmFunctionBuilder(WasmModuleBuilder* builder); friend class WasmModuleBuilder; @@ -142,6 +145,7 @@ class WasmFunctionBuilder : public ZoneObject { LocalDeclEncoder locals_; uint32_t signature_index_; bool exported_; + uint32_t func_index_; ZoneVector body_; ZoneVector name_; ZoneVector i32_temps_; @@ -212,14 +216,17 @@ class WasmModuleBuilder : public ZoneObject { explicit WasmModuleBuilder(Zone* zone); // Building methods. - uint32_t AddFunction(); - uint32_t AddGlobal(LocalType type, bool exported); - WasmFunctionBuilder* FunctionAt(size_t index); + uint32_t AddImport(const char* name, int name_length, FunctionSig* sig); + void SetImportName(uint32_t index, const char* name, int name_length) { + imports_[index].name = name; + imports_[index].name_length = name_length; + } + WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr); + uint32_t AddGlobal(LocalType type, bool exported, bool mutability = true); void AddDataSegment(WasmDataSegmentEncoder* data); uint32_t AddSignature(FunctionSig* sig); void AddIndirectFunction(uint32_t index); - void MarkStartFunction(uint32_t index); - uint32_t AddImport(const char* name, int name_length, FunctionSig* sig); + void MarkStartFunction(WasmFunctionBuilder* builder); // Writing methods. void WriteTo(ZoneBuffer& buffer) const; @@ -231,18 +238,25 @@ class WasmModuleBuilder : public ZoneObject { Zone* zone() { return zone_; } + FunctionSig* GetSignature(uint32_t index) { return signatures_[index]; } + private: + friend class WasmFunctionBuilder; Zone* zone_; ZoneVector signatures_; ZoneVector imports_; ZoneVector functions_; ZoneVector data_segments_; ZoneVector indirect_functions_; - ZoneVector> globals_; + ZoneVector> globals_; SignatureMap signature_map_; int start_function_index_; }; +inline FunctionSig* WasmFunctionBuilder::signature() { + return builder_->signatures_[signature_index_]; +} + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc index c7e7fa5c90..a365d58ed9 100644 --- a/src/wasm/module-decoder.cc +++ b/src/wasm/module-decoder.cc @@ -27,6 +27,138 @@ namespace wasm { namespace { +LocalType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { + switch (expr.kind) { + case WasmInitExpr::kNone: + return kAstStmt; + case WasmInitExpr::kGlobalIndex: + return expr.val.global_index < module->globals.size() + ? module->globals[expr.val.global_index].type + : kAstStmt; + case WasmInitExpr::kI32Const: + return kAstI32; + case WasmInitExpr::kI64Const: + return kAstI64; + case WasmInitExpr::kF32Const: + return kAstF32; + case WasmInitExpr::kF64Const: + return kAstF64; + default: + UNREACHABLE(); + return kAstStmt; + } +} + +// An iterator over the sections in a WASM binary module. +// Automatically skips all unknown sections. +class WasmSectionIterator { + public: + explicit WasmSectionIterator(Decoder& decoder) + : decoder_(decoder), + section_code_(kUnknownSectionCode), + section_start_(decoder.pc()), + section_end_(decoder.pc()) { + next(); + } + + inline bool more() const { + return section_code_ != kUnknownSectionCode && decoder_.more(); + } + + inline WasmSectionCode section_code() const { return section_code_; } + + inline const byte* section_start() const { return section_start_; } + + inline uint32_t section_length() const { + return static_cast(section_end_ - section_start_); + } + + inline const byte* section_end() const { return section_end_; } + + // Advances to the next section, checking that decoding the current section + // stopped at {section_end_}. + void advance() { + if (decoder_.pc() != section_end_) { + const char* msg = decoder_.pc() < section_end_ ? "shorter" : "longer"; + decoder_.error(decoder_.pc(), decoder_.pc(), + "section was %s than expected size " + "(%u bytes expected, %zu decoded)", + msg, section_length(), + static_cast(decoder_.pc() - section_start_)); + } + next(); + } + + private: + Decoder& decoder_; + WasmSectionCode section_code_; + const byte* section_start_; + const byte* section_end_; + + // Reads the section code/name at the current position and sets up + // the internal fields. + void next() { + while (true) { + if (!decoder_.more()) { + section_code_ = kUnknownSectionCode; + return; + } + uint8_t section_code = decoder_.consume_u8("section code"); + // Read and check the section size. + uint32_t section_length = decoder_.consume_u32v("section length"); + section_start_ = decoder_.pc(); + if (decoder_.checkAvailable(section_length)) { + // Get the limit of the section within the module. + section_end_ = section_start_ + section_length; + } else { + // The section would extend beyond the end of the module. + section_end_ = section_start_; + } + + if (section_code == kUnknownSectionCode) { + // Check for the known "names" section. + uint32_t string_length = decoder_.consume_u32v("section name length"); + const byte* section_name_start = decoder_.pc(); + decoder_.consume_bytes(string_length, "section name"); + if (decoder_.failed() || decoder_.pc() > section_end_) { + TRACE("Section name of length %u couldn't be read\n", string_length); + section_code_ = kUnknownSectionCode; + return; + } + + TRACE(" +%d section name : \"%.*s\"\n", + static_cast(section_name_start - decoder_.start()), + string_length < 20 ? string_length : 20, section_name_start); + + if (string_length == kNameStringLength && + strncmp(reinterpret_cast(section_name_start), + kNameString, kNameStringLength) == 0) { + section_code = kNameSectionCode; + } else { + section_code = kUnknownSectionCode; + } + } else if (!IsValidSectionCode(section_code)) { + decoder_.error(decoder_.pc(), decoder_.pc(), + "unknown section code #0x%02x", section_code); + section_code = kUnknownSectionCode; + } + section_code_ = static_cast(section_code); + + TRACE("Section: %s\n", SectionName(section_code_)); + if (section_code_ == kUnknownSectionCode && + section_end_ > decoder_.pc()) { + // skip to the end of the unknown section. + uint32_t remaining = + static_cast(section_end_ - decoder_.pc()); + decoder_.consume_bytes(remaining, "section payload"); + // fall through and continue to the next section. + } else { + return; + } + } + } +}; + // The main logic for decoding the bytes of a module. class ModuleDecoder : public Decoder { public: @@ -77,11 +209,9 @@ class ModuleDecoder : public Decoder { module->min_mem_pages = 0; module->max_mem_pages = 0; module->mem_export = false; - module->mem_external = false; module->origin = origin_; const byte* pos = pc_; - int current_order = 0; uint32_t magic_word = consume_u32("wasm magic"); #define BYTES(x) (x & 0xff), (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff if (magic_word != kWasmMagic) { @@ -89,7 +219,6 @@ class ModuleDecoder : public Decoder { "expected magic word %02x %02x %02x %02x, " "found %02x %02x %02x %02x", BYTES(kWasmMagic), BYTES(magic_word)); - goto done; } pos = pc_; @@ -100,296 +229,367 @@ class ModuleDecoder : public Decoder { "expected version %02x %02x %02x %02x, " "found %02x %02x %02x %02x", BYTES(kWasmVersion), BYTES(magic_version)); - goto done; } } - // Decode the module sections. - while (pc_ < limit_) { - TRACE("DecodeSection\n"); - pos = pc_; + WasmSectionIterator section_iter(*this); - // Read the section name. - uint32_t string_length = consume_u32v("section name length"); - const byte* section_name_start = pc_; - consume_bytes(string_length); - if (failed()) { - TRACE("Section name of length %u couldn't be read\n", string_length); - break; + // ===== Type section ==================================================== + if (section_iter.section_code() == kTypeSectionCode) { + uint32_t signatures_count = consume_u32v("signatures count"); + module->signatures.reserve(SafeReserve(signatures_count)); + for (uint32_t i = 0; ok() && i < signatures_count; ++i) { + TRACE("DecodeSignature[%d] module+%d\n", i, + static_cast(pc_ - start_)); + FunctionSig* s = consume_sig(); + module->signatures.push_back(s); } + section_iter.advance(); + } - TRACE(" +%d section name : \"%.*s\"\n", - static_cast(section_name_start - start_), - string_length < 20 ? string_length : 20, section_name_start); + // ===== Import section ================================================== + if (section_iter.section_code() == kImportSectionCode) { + uint32_t import_table_count = consume_u32v("import table count"); + module->import_table.reserve(SafeReserve(import_table_count)); + for (uint32_t i = 0; ok() && i < import_table_count; ++i) { + TRACE("DecodeImportTable[%d] module+%d\n", i, + static_cast(pc_ - start_)); - WasmSection::Code section = - WasmSection::lookup(section_name_start, string_length); - - // Read and check the section size. - uint32_t section_length = consume_u32v("section length"); - if (!checkAvailable(section_length)) { - // The section would extend beyond the end of the module. - break; - } - const byte* section_start = pc_; - const byte* expected_section_end = pc_ + section_length; - - current_order = CheckSectionOrder(current_order, section); - - switch (section) { - case WasmSection::Code::End: - // Terminate section decoding. - limit_ = pc_; - break; - case WasmSection::Code::Memory: { - module->min_mem_pages = consume_u32v("min memory"); - module->max_mem_pages = consume_u32v("max memory"); - module->mem_export = consume_u8("export memory") != 0; - break; + module->import_table.push_back({ + 0, // module_name_length + 0, // module_name_offset + 0, // field_name_offset + 0, // field_name_length + kExternalFunction, // kind + 0 // index + }); + WasmImport* import = &module->import_table.back(); + const byte* pos = pc_; + import->module_name_offset = + consume_string(&import->module_name_length, true); + if (import->module_name_length == 0) { + error(pos, "import module name cannot be NULL"); } - case WasmSection::Code::Signatures: { - uint32_t signatures_count = consume_u32v("signatures count"); - module->signatures.reserve(SafeReserve(signatures_count)); - // Decode signatures. - for (uint32_t i = 0; ok() && i < signatures_count; ++i) { - TRACE("DecodeSignature[%d] module+%d\n", i, - static_cast(pc_ - start_)); - FunctionSig* s = consume_sig(); - module->signatures.push_back(s); - } - break; - } - case WasmSection::Code::FunctionSignatures: { - uint32_t functions_count = consume_u32v("functions count"); - module->functions.reserve(SafeReserve(functions_count)); - for (uint32_t i = 0; ok() && i < functions_count; ++i) { - module->functions.push_back({nullptr, // sig - i, // func_index - 0, // sig_index - 0, // name_offset - 0, // name_length - 0, // code_start_offset - 0}); // code_end_offset + import->field_name_offset = + consume_string(&import->field_name_length, true); + + import->kind = static_cast(consume_u8("import kind")); + switch (import->kind) { + case kExternalFunction: { + // ===== Imported function ======================================= + import->index = static_cast(module->functions.size()); + module->num_imported_functions++; + module->functions.push_back({nullptr, // sig + import->index, // func_index + 0, // sig_index + 0, // name_offset + 0, // name_length + 0, // code_start_offset + 0, // code_end_offset + true, // imported + false}); // exported WasmFunction* function = &module->functions.back(); function->sig_index = consume_sig_index(module, &function->sig); - } - break; - } - case WasmSection::Code::FunctionBodies: { - const byte* pos = pc_; - uint32_t functions_count = consume_u32v("functions count"); - if (functions_count != module->functions.size()) { - error(pos, pos, "function body count %u mismatch (%u expected)", - functions_count, - static_cast(module->functions.size())); break; } - for (uint32_t i = 0; ok() && i < functions_count; ++i) { - WasmFunction* function = &module->functions[i]; - uint32_t size = consume_u32v("body size"); - function->code_start_offset = pc_offset(); - function->code_end_offset = pc_offset() + size; - - TRACE(" +%d %-20s: (%d bytes)\n", pc_offset(), "function body", - size); - pc_ += size; - if (pc_ > limit_) { - error(pc_, "function body extends beyond end of file"); - } - } - break; - } - case WasmSection::Code::Names: { - const byte* pos = pc_; - uint32_t functions_count = consume_u32v("functions count"); - if (functions_count != module->functions.size()) { - error(pos, pos, "function name count %u mismatch (%u expected)", - functions_count, - static_cast(module->functions.size())); + case kExternalTable: { + // ===== Imported table ========================================== + import->index = + static_cast(module->function_tables.size()); + module->function_tables.push_back( + {0, 0, std::vector(), true, false}); + expect_u8("element type", 0x20); + WasmIndirectFunctionTable* table = &module->function_tables.back(); + consume_resizable_limits("element count", "elements", kMaxUInt32, + &table->size, &table->max_size); break; } - - for (uint32_t i = 0; ok() && i < functions_count; ++i) { - WasmFunction* function = &module->functions[i]; - function->name_offset = - consume_string(&function->name_length, false); - - uint32_t local_names_count = consume_u32v("local names count"); - for (uint32_t j = 0; ok() && j < local_names_count; j++) { - uint32_t unused = 0; - uint32_t offset = consume_string(&unused, false); - USE(unused); - USE(offset); - } + case kExternalMemory: { + // ===== Imported memory ========================================= + // import->index = + // static_cast(module->memories.size()); + // TODO(titzer): imported memories + break; } - break; - } - case WasmSection::Code::Globals: { - uint32_t globals_count = consume_u32v("globals count"); - module->globals.reserve(SafeReserve(globals_count)); - // Decode globals. - for (uint32_t i = 0; ok() && i < globals_count; ++i) { - TRACE("DecodeGlobal[%d] module+%d\n", i, - static_cast(pc_ - start_)); - // Add an uninitialized global and pass a pointer to it. - module->globals.push_back({0, 0, kAstStmt, 0, false}); + case kExternalGlobal: { + // ===== Imported global ========================================= + import->index = static_cast(module->globals.size()); + module->globals.push_back( + {kAstStmt, false, NO_INIT, 0, true, false}); WasmGlobal* global = &module->globals.back(); - DecodeGlobalInModule(global); - } - break; - } - case WasmSection::Code::DataSegments: { - uint32_t data_segments_count = consume_u32v("data segments count"); - module->data_segments.reserve(SafeReserve(data_segments_count)); - // Decode data segments. - for (uint32_t i = 0; ok() && i < data_segments_count; ++i) { - TRACE("DecodeDataSegment[%d] module+%d\n", i, - static_cast(pc_ - start_)); - module->data_segments.push_back({0, // dest_addr - 0, // source_offset - 0, // source_size - false}); // init - WasmDataSegment* segment = &module->data_segments.back(); - DecodeDataSegmentInModule(module, segment); - } - break; - } - case WasmSection::Code::FunctionTable: { - // An indirect function table requires functions first. - CheckForFunctions(module, section); - // Assume only one table for now. - static const uint32_t kSupportedTableCount = 1; - module->function_tables.reserve(SafeReserve(kSupportedTableCount)); - // Decode function table. - for (uint32_t i = 0; ok() && i < kSupportedTableCount; ++i) { - TRACE("DecodeFunctionTable[%d] module+%d\n", i, - static_cast(pc_ - start_)); - module->function_tables.push_back({0, 0, std::vector()}); - DecodeFunctionTableInModule(module, &module->function_tables[i]); - } - break; - } - case WasmSection::Code::StartFunction: { - // Declares a start function for a module. - CheckForFunctions(module, section); - if (module->start_function_index >= 0) { - error("start function already declared"); + global->type = consume_value_type(); + global->mutability = consume_u8("mutability") != 0; break; } - WasmFunction* func; - const byte* pos = pc_; - module->start_function_index = consume_func_index(module, &func); - if (func && func->sig->parameter_count() > 0) { - error(pos, "invalid start function: non-zero parameter count"); + default: + error(pos, pos, "unknown import kind 0x%02x", import->kind); break; - } - break; } - case WasmSection::Code::ImportTable: { - uint32_t import_table_count = consume_u32v("import table count"); - module->import_table.reserve(SafeReserve(import_table_count)); - // Decode import table. - for (uint32_t i = 0; ok() && i < import_table_count; ++i) { - TRACE("DecodeImportTable[%d] module+%d\n", i, - static_cast(pc_ - start_)); - - module->import_table.push_back({nullptr, // sig - 0, // sig_index - 0, // module_name_offset - 0, // module_name_length - 0, // function_name_offset - 0}); // function_name_length - WasmImport* import = &module->import_table.back(); - - import->sig_index = consume_sig_index(module, &import->sig); - const byte* pos = pc_; - import->module_name_offset = - consume_string(&import->module_name_length, true); - if (import->module_name_length == 0) { - error(pos, "import module name cannot be NULL"); - } - import->function_name_offset = - consume_string(&import->function_name_length, true); - } - break; - } - case WasmSection::Code::ExportTable: { - // Declares an export table. - CheckForFunctions(module, section); - uint32_t export_table_count = consume_u32v("export table count"); - module->export_table.reserve(SafeReserve(export_table_count)); - // Decode export table. - for (uint32_t i = 0; ok() && i < export_table_count; ++i) { - TRACE("DecodeExportTable[%d] module+%d\n", i, - static_cast(pc_ - start_)); - - module->export_table.push_back({0, // func_index - 0, // name_offset - 0}); // name_length - WasmExport* exp = &module->export_table.back(); - - WasmFunction* func; - exp->func_index = consume_func_index(module, &func); - exp->name_offset = consume_string(&exp->name_length, true); - } - // Check for duplicate exports. - if (ok() && module->export_table.size() > 1) { - std::vector sorted_exports(module->export_table); - const byte* base = start_; - auto cmp_less = [base](const WasmExport& a, const WasmExport& b) { - // Return true if a < b. - uint32_t len = a.name_length; - if (len != b.name_length) return len < b.name_length; - return memcmp(base + a.name_offset, base + b.name_offset, len) < - 0; - }; - std::stable_sort(sorted_exports.begin(), sorted_exports.end(), - cmp_less); - auto it = sorted_exports.begin(); - WasmExport* last = &*it++; - for (auto end = sorted_exports.end(); it != end; last = &*it++) { - DCHECK(!cmp_less(*it, *last)); // Vector must be sorted. - if (!cmp_less(*last, *it)) { - const byte* pc = start_ + it->name_offset; - error(pc, pc, - "Duplicate export name '%.*s' for functions %d and %d", - it->name_length, pc, last->func_index, it->func_index); - break; - } - } - } - break; - } - case WasmSection::Code::Max: - // Skip unknown sections. - TRACE("Unknown section: '"); - for (uint32_t i = 0; i != string_length; ++i) { - TRACE("%c", *(section_name_start + i)); - } - TRACE("'\n"); - consume_bytes(section_length); - break; - } - - if (pc_ != expected_section_end) { - const char* diff = pc_ < expected_section_end ? "shorter" : "longer"; - size_t expected_length = static_cast(section_length); - size_t actual_length = static_cast(pc_ - section_start); - error(pc_, pc_, - "section \"%s\" %s (%zu bytes) than specified (%zu bytes)", - WasmSection::getName(section), diff, actual_length, - expected_length); - break; } + section_iter.advance(); } - done: - if (ok()) CalculateGlobalsOffsets(module); + // ===== Function section ================================================ + if (section_iter.section_code() == kFunctionSectionCode) { + uint32_t functions_count = consume_u32v("functions count"); + module->functions.reserve(SafeReserve(functions_count)); + module->num_declared_functions = functions_count; + for (uint32_t i = 0; ok() && i < functions_count; ++i) { + uint32_t func_index = static_cast(module->functions.size()); + module->functions.push_back({nullptr, // sig + func_index, // func_index + 0, // sig_index + 0, // name_offset + 0, // name_length + 0, // code_start_offset + 0, // code_end_offset + false, // imported + false}); // exported + WasmFunction* function = &module->functions.back(); + function->sig_index = consume_sig_index(module, &function->sig); + } + section_iter.advance(); + } + + // ===== Table section =================================================== + if (section_iter.section_code() == kTableSectionCode) { + const byte* pos = pc_; + uint32_t table_count = consume_u32v("table count"); + // Require at most one table for now. + if (table_count > 1) { + error(pos, pos, "invalid table count %d, maximum 1", table_count); + } + + for (uint32_t i = 0; ok() && i < table_count; i++) { + module->function_tables.push_back( + {0, 0, std::vector(), false, false}); + WasmIndirectFunctionTable* table = &module->function_tables.back(); + expect_u8("table type", kWasmAnyFunctionTypeForm); + consume_resizable_limits("table elements", "elements", kMaxUInt32, + &table->size, &table->max_size); + } + section_iter.advance(); + } + + // ===== Memory section ================================================== + if (section_iter.section_code() == kMemorySectionCode) { + const byte* pos = pc_; + uint32_t memory_count = consume_u32v("memory count"); + // Require at most one memory for now. + if (memory_count > 1) { + error(pos, pos, "invalid memory count %d, maximum 1", memory_count); + } + + for (uint32_t i = 0; ok() && i < memory_count; i++) { + consume_resizable_limits("memory", "pages", WasmModule::kMaxLegalPages, + &module->min_mem_pages, + &module->max_mem_pages); + } + section_iter.advance(); + } + + // ===== Global section ================================================== + if (section_iter.section_code() == kGlobalSectionCode) { + uint32_t globals_count = consume_u32v("globals count"); + module->globals.reserve(SafeReserve(globals_count)); + for (uint32_t i = 0; ok() && i < globals_count; ++i) { + TRACE("DecodeGlobal[%d] module+%d\n", i, + static_cast(pc_ - start_)); + // Add an uninitialized global and pass a pointer to it. + module->globals.push_back({kAstStmt, false, NO_INIT, 0, false, false}); + WasmGlobal* global = &module->globals.back(); + DecodeGlobalInModule(module, i, global); + } + section_iter.advance(); + } + + // ===== Export section ================================================== + if (section_iter.section_code() == kExportSectionCode) { + uint32_t export_table_count = consume_u32v("export table count"); + module->export_table.reserve(SafeReserve(export_table_count)); + for (uint32_t i = 0; ok() && i < export_table_count; ++i) { + TRACE("DecodeExportTable[%d] module+%d\n", i, + static_cast(pc_ - start_)); + + module->export_table.push_back({ + 0, // name_length + 0, // name_offset + kExternalFunction, // kind + 0 // index + }); + WasmExport* exp = &module->export_table.back(); + + exp->name_offset = consume_string(&exp->name_length, true); + const byte* pos = pc(); + exp->kind = static_cast(consume_u8("export kind")); + switch (exp->kind) { + case kExternalFunction: { + WasmFunction* func = nullptr; + exp->index = consume_func_index(module, &func); + module->num_exported_functions++; + if (func) func->exported = true; + break; + } + case kExternalTable: { + WasmIndirectFunctionTable* table = nullptr; + exp->index = consume_table_index(module, &table); + if (table) table->exported = true; + break; + } + case kExternalMemory: { + uint32_t index = consume_u32v("memory index"); + if (index != 0) error("invalid memory index != 0"); + module->mem_export = true; + break; + } + case kExternalGlobal: { + WasmGlobal* global = nullptr; + exp->index = consume_global_index(module, &global); + if (global) global->exported = true; + break; + } + default: + error(pos, pos, "invalid export kind 0x%02x", exp->kind); + break; + } + } + // Check for duplicate exports. + if (ok() && module->export_table.size() > 1) { + std::vector sorted_exports(module->export_table); + const byte* base = start_; + auto cmp_less = [base](const WasmExport& a, const WasmExport& b) { + // Return true if a < b. + if (a.name_length != b.name_length) { + return a.name_length < b.name_length; + } + return memcmp(base + a.name_offset, base + b.name_offset, + a.name_length) < 0; + }; + std::stable_sort(sorted_exports.begin(), sorted_exports.end(), + cmp_less); + auto it = sorted_exports.begin(); + WasmExport* last = &*it++; + for (auto end = sorted_exports.end(); it != end; last = &*it++) { + DCHECK(!cmp_less(*it, *last)); // Vector must be sorted. + if (!cmp_less(*last, *it)) { + const byte* pc = start_ + it->name_offset; + error(pc, pc, + "Duplicate export name '%.*s' for functions %d and %d", + it->name_length, pc, last->index, it->index); + break; + } + } + } + section_iter.advance(); + } + + // ===== Start section =================================================== + if (section_iter.section_code() == kStartSectionCode) { + WasmFunction* func; + const byte* pos = pc_; + module->start_function_index = consume_func_index(module, &func); + if (func && func->sig->parameter_count() > 0) { + error(pos, "invalid start function: non-zero parameter count"); + } + section_iter.advance(); + } + + // ===== Elements section ================================================ + if (section_iter.section_code() == kElementSectionCode) { + uint32_t element_count = consume_u32v("element count"); + for (uint32_t i = 0; ok() && i < element_count; ++i) { + uint32_t table_index = consume_u32v("table index"); + if (table_index != 0) error("illegal table index != 0"); + WasmInitExpr offset = consume_init_expr(module, kAstI32); + uint32_t num_elem = consume_u32v("number of elements"); + std::vector vector; + module->table_inits.push_back({table_index, offset, vector}); + WasmTableInit* init = &module->table_inits.back(); + init->entries.reserve(SafeReserve(num_elem)); + for (uint32_t j = 0; ok() && j < num_elem; j++) { + WasmFunction* func = nullptr; + init->entries.push_back(consume_func_index(module, &func)); + } + } + + section_iter.advance(); + } + + // ===== Code section ==================================================== + if (section_iter.section_code() == kCodeSectionCode) { + const byte* pos = pc_; + uint32_t functions_count = consume_u32v("functions count"); + if (functions_count != module->num_declared_functions) { + error(pos, pos, "function body count %u mismatch (%u expected)", + functions_count, module->num_declared_functions); + } + for (uint32_t i = 0; ok() && i < functions_count; ++i) { + WasmFunction* function = + &module->functions[i + module->num_imported_functions]; + uint32_t size = consume_u32v("body size"); + function->code_start_offset = pc_offset(); + function->code_end_offset = pc_offset() + size; + consume_bytes(size, "function body"); + } + section_iter.advance(); + } + + // ===== Data section ==================================================== + if (section_iter.section_code() == kDataSectionCode) { + uint32_t data_segments_count = consume_u32v("data segments count"); + module->data_segments.reserve(SafeReserve(data_segments_count)); + for (uint32_t i = 0; ok() && i < data_segments_count; ++i) { + TRACE("DecodeDataSegment[%d] module+%d\n", i, + static_cast(pc_ - start_)); + module->data_segments.push_back({ + NO_INIT, // dest_addr + 0, // source_offset + 0 // source_size + }); + WasmDataSegment* segment = &module->data_segments.back(); + DecodeDataSegmentInModule(module, segment); + } + section_iter.advance(); + } + + // ===== Name section ==================================================== + if (section_iter.section_code() == kNameSectionCode) { + const byte* pos = pc_; + uint32_t functions_count = consume_u32v("functions count"); + if (functions_count != module->num_declared_functions) { + error(pos, pos, "function name count %u mismatch (%u expected)", + functions_count, module->num_declared_functions); + } + + for (uint32_t i = 0; ok() && i < functions_count; ++i) { + WasmFunction* function = + &module->functions[i + module->num_imported_functions]; + function->name_offset = consume_string(&function->name_length, false); + + uint32_t local_names_count = consume_u32v("local names count"); + for (uint32_t j = 0; ok() && j < local_names_count; j++) { + uint32_t unused = 0; + uint32_t offset = consume_string(&unused, false); + USE(unused); + USE(offset); + } + } + section_iter.advance(); + } + + // ===== Remaining sections ============================================== + if (section_iter.more() && ok()) { + error(pc(), pc(), "unexpected section: %s", + SectionName(section_iter.section_code())); + } + + if (ok()) { + CalculateGlobalOffsets(module); + PreinitializeIndirectFunctionTables(module); + } const WasmModule* finished_module = module; ModuleResult result = toResult(finished_module); - if (FLAG_dump_wasm_module) { - DumpModule(module, result); - } + if (FLAG_dump_wasm_module) DumpModule(module, result); return result; } @@ -399,27 +599,6 @@ class ModuleDecoder : public Decoder { return count < kMaxReserve ? count : kMaxReserve; } - void CheckForFunctions(WasmModule* module, WasmSection::Code section) { - if (module->functions.size() == 0) { - error(pc_ - 1, nullptr, "functions must appear before section %s", - WasmSection::getName(section)); - } - } - - int CheckSectionOrder(int current_order, WasmSection::Code section) { - int next_order = WasmSection::getOrder(section); - if (next_order == 0) return current_order; - if (next_order == current_order) { - error(pc_, pc_, "section \"%s\" already defined", - WasmSection::getName(section)); - } - if (next_order < current_order) { - error(pc_, pc_, "section \"%s\" out of order", - WasmSection::getName(section)); - } - return next_order; - } - // Decodes a single anonymous function starting at {start_}. FunctionResult DecodeSingleFunction(ModuleEnv* module_env, WasmFunction* function) { @@ -445,6 +624,11 @@ class ModuleDecoder : public Decoder { return ok() ? result : nullptr; } + WasmInitExpr DecodeInitExpr(const byte* start) { + pc_ = start; + return consume_init_expr(nullptr, kAstStmt); + } + private: Zone* module_zone; ModuleResult result_; @@ -453,19 +637,28 @@ class ModuleDecoder : public Decoder { uint32_t off(const byte* ptr) { return static_cast(ptr - start_); } // Decodes a single global entry inside a module starting at {pc_}. - void DecodeGlobalInModule(WasmGlobal* global) { - global->name_offset = consume_string(&global->name_length, false); - if (ok() && - !unibrow::Utf8::Validate(start_ + global->name_offset, - global->name_length)) { - error("global name is not valid utf8"); + void DecodeGlobalInModule(WasmModule* module, uint32_t index, + WasmGlobal* global) { + global->type = consume_value_type(); + global->mutability = consume_u8("mutability") != 0; + const byte* pos = pc(); + global->init = consume_init_expr(module, kAstStmt); + switch (global->init.kind) { + case WasmInitExpr::kGlobalIndex: + if (global->init.val.global_index >= index) { + error("invalid global index in init expression"); + } else if (module->globals[index].type != global->type) { + error("type mismatch in global initialization"); + } + break; + default: + if (global->type != TypeOf(module, global->init)) { + error(pos, pos, + "type error in global initialization, expected %s, got %s", + WasmOpcodes::TypeName(global->type), + WasmOpcodes::TypeName(TypeOf(module, global->init))); + } } - global->type = consume_local_type(); - if (global->type == kAstStmt) { - error("invalid global type"); - } - global->offset = 0; - global->exported = consume_u8("exported") != 0; } bool IsWithinLimit(uint32_t limit, uint32_t offset, uint32_t size) { @@ -477,10 +670,10 @@ class ModuleDecoder : public Decoder { // Decodes a single data segment entry inside a module starting at {pc_}. void DecodeDataSegmentInModule(WasmModule* module, WasmDataSegment* segment) { const byte* start = pc_; - segment->dest_addr = consume_u32v("destination"); + expect_u8("linear memory index", 0); + segment->dest_addr = consume_init_expr(module, kAstI32); segment->source_size = consume_u32v("source size"); segment->source_offset = static_cast(pc_ - start_); - segment->init = true; // Validate the data is in the module. uint32_t module_limit = static_cast(limit_ - start_); @@ -489,40 +682,11 @@ class ModuleDecoder : public Decoder { error(start, "segment out of bounds of module"); } - // Validate that the segment will fit into the (minimum) memory. - uint32_t memory_limit = - WasmModule::kPageSize * (module ? module->min_mem_pages - : WasmModule::kMaxMemPages); - if (!IsWithinLimit(memory_limit, segment->dest_addr, - segment->source_size)) { - error(start, "segment out of bounds of memory"); - } - - consume_bytes(segment->source_size); - } - - // Decodes a single function table inside a module starting at {pc_}. - void DecodeFunctionTableInModule(WasmModule* module, - WasmIndirectFunctionTable* table) { - table->size = consume_u32v("function table entry count"); - table->max_size = table->size; - - if (table->max_size != table->size) { - error("invalid table maximum size"); - } - - for (uint32_t i = 0; ok() && i < table->size; ++i) { - uint16_t index = consume_u32v(); - if (index >= module->functions.size()) { - error(pc_ - sizeof(index), "invalid function index"); - break; - } - table->values.push_back(index); - } + consume_bytes(segment->source_size, "segment data"); } // Calculate individual global offsets and total size of globals table. - void CalculateGlobalsOffsets(WasmModule* module) { + void CalculateGlobalOffsets(WasmModule* module) { uint32_t offset = 0; if (module->globals.size() == 0) { module->globals_size = 0; @@ -538,6 +702,30 @@ class ModuleDecoder : public Decoder { module->globals_size = offset; } + // TODO(titzer): this only works without overlapping initializations from + // global bases for entries + void PreinitializeIndirectFunctionTables(WasmModule* module) { + // Fill all tables with invalid entries first. + for (WasmIndirectFunctionTable& table : module->function_tables) { + table.values.resize(table.size); + for (size_t i = 0; i < table.size; i++) { + table.values[i] = kInvalidFunctionIndex; + } + } + for (WasmTableInit& init : module->table_inits) { + if (init.offset.kind != WasmInitExpr::kI32Const) continue; + if (init.table_index >= module->function_tables.size()) continue; + WasmIndirectFunctionTable& table = + module->function_tables[init.table_index]; + for (size_t i = 0; i < init.entries.size(); i++) { + size_t index = i + init.offset.val.i32_const; + if (index < table.values.size()) { + table.values[index] = init.entries[i]; + } + } + } + } + // Verifies the body (code) of a given function. void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv, WasmFunction* function) { @@ -568,25 +756,14 @@ class ModuleDecoder : public Decoder { } } - // Reads a single 32-bit unsigned integer interpreted as an offset, checking - // the offset is within bounds and advances. - uint32_t consume_offset(const char* name = nullptr) { - uint32_t offset = consume_u32(name ? name : "offset"); - if (offset > static_cast(limit_ - start_)) { - error(pc_ - sizeof(uint32_t), "offset out of bounds of module"); - } - return offset; - } - // Reads a length-prefixed string, checking that it is within bounds. Returns // the offset of the string, and the length as an out parameter. uint32_t consume_string(uint32_t* length, bool validate_utf8) { *length = consume_u32v("string length"); uint32_t offset = pc_offset(); - TRACE(" +%u %-20s: (%u bytes)\n", offset, "string", *length); const byte* string_start = pc_; // Consume bytes before validation to guarantee that the string is not oob. - consume_bytes(*length); + consume_bytes(*length, "string"); if (ok() && validate_utf8 && !unibrow::Utf8::Validate(string_start, *length)) { error(string_start, "no valid UTF-8 string"); @@ -608,25 +785,134 @@ class ModuleDecoder : public Decoder { } uint32_t consume_func_index(WasmModule* module, WasmFunction** func) { + return consume_index("function index", module->functions, func); + } + + uint32_t consume_global_index(WasmModule* module, WasmGlobal** global) { + return consume_index("global index", module->globals, global); + } + + uint32_t consume_table_index(WasmModule* module, + WasmIndirectFunctionTable** table) { + return consume_index("table index", module->function_tables, table); + } + + template + uint32_t consume_index(const char* name, std::vector& vector, T** ptr) { const byte* pos = pc_; - uint32_t func_index = consume_u32v("function index"); - if (func_index >= module->functions.size()) { - error(pos, pos, "function index %u out of bounds (%d functions)", - func_index, static_cast(module->functions.size())); - *func = nullptr; + uint32_t index = consume_u32v(name); + if (index >= vector.size()) { + error(pos, pos, "%s %u out of bounds (%d entries)", name, index, + static_cast(vector.size())); + *ptr = nullptr; return 0; } - *func = &module->functions[func_index]; - return func_index; + *ptr = &vector[index]; + return index; + } + + void consume_resizable_limits(const char* name, const char* units, + uint32_t max_value, uint32_t* initial, + uint32_t* maximum) { + uint32_t flags = consume_u32v("resizable limits flags"); + const byte* pos = pc(); + *initial = consume_u32v("initial size"); + if (*initial > max_value) { + error(pos, pos, + "initial %s size (%u %s) is larger than maximum allowable (%u)", + name, *initial, units, max_value); + } + if (flags & 1) { + pos = pc(); + *maximum = consume_u32v("maximum size"); + if (*maximum > max_value) { + error(pos, pos, + "maximum %s size (%u %s) is larger than maximum allowable (%u)", + name, *maximum, units, max_value); + } + if (*maximum < *initial) { + error(pos, pos, "maximum %s size (%u %s) is less than initial (%u %s)", + name, *maximum, units, *initial, units); + } + } else { + *maximum = 0; + } + } + + bool expect_u8(const char* name, uint8_t expected) { + const byte* pos = pc(); + uint8_t value = consume_u8(name); + if (value != expected) { + error(pos, pos, "expected %s 0x%02x, got 0x%02x", name, expected, value); + return false; + } + return true; + } + + WasmInitExpr consume_init_expr(WasmModule* module, LocalType expected) { + const byte* pos = pc(); + uint8_t opcode = consume_u8("opcode"); + WasmInitExpr expr; + unsigned len = 0; + switch (opcode) { + case kExprGetGlobal: { + GlobalIndexOperand operand(this, pc() - 1); + expr.kind = WasmInitExpr::kGlobalIndex; + expr.val.global_index = operand.index; + len = operand.length; + break; + } + case kExprI32Const: { + ImmI32Operand operand(this, pc() - 1); + expr.kind = WasmInitExpr::kI32Const; + expr.val.i32_const = operand.value; + len = operand.length; + break; + } + case kExprF32Const: { + ImmF32Operand operand(this, pc() - 1); + expr.kind = WasmInitExpr::kF32Const; + expr.val.f32_const = operand.value; + len = operand.length; + break; + } + case kExprI64Const: { + ImmI64Operand operand(this, pc() - 1); + expr.kind = WasmInitExpr::kI64Const; + expr.val.i64_const = operand.value; + len = operand.length; + break; + } + case kExprF64Const: { + ImmF64Operand operand(this, pc() - 1); + expr.kind = WasmInitExpr::kF64Const; + expr.val.f64_const = operand.value; + len = operand.length; + break; + } + default: { + error("invalid opcode in initialization expression"); + expr.kind = WasmInitExpr::kNone; + expr.val.i32_const = 0; + } + } + consume_bytes(len, "init code"); + if (!expect_u8("end opcode", kExprEnd)) { + expr.kind = WasmInitExpr::kNone; + } + if (expected != kAstStmt && TypeOf(module, expr) != kAstI32) { + error(pos, pos, "type error in init expression, expected %s, got %s", + WasmOpcodes::TypeName(expected), + WasmOpcodes::TypeName(TypeOf(module, expr))); + } + return expr; } // Reads a single 8-bit integer, interpreting it as a local type. - LocalType consume_local_type() { - byte val = consume_u8("local type"); + LocalType consume_value_type() { + byte val = consume_u8("value type"); LocalTypeCode t = static_cast(val); switch (t) { - case kLocalVoid: - return kAstStmt; case kLocalI32: return kAstI32; case kLocalI64: @@ -645,19 +931,12 @@ class ModuleDecoder : public Decoder { // Parses a type entry, which is currently limited to functions only. FunctionSig* consume_sig() { - const byte* pos = pc_; - byte form = consume_u8("type form"); - if (form != kWasmFunctionTypeForm) { - error(pos, pos, "expected function type form (0x%02x), got: 0x%02x", - kWasmFunctionTypeForm, form); - return nullptr; - } + if (!expect_u8("type form", kWasmFunctionTypeForm)) return nullptr; // parse parameter types uint32_t param_count = consume_u32v("param count"); std::vector params; - for (uint32_t i = 0; i < param_count; ++i) { - LocalType param = consume_local_type(); - if (param == kAstStmt) error(pc_ - 1, "invalid void parameter type"); + for (uint32_t i = 0; ok() && i < param_count; ++i) { + LocalType param = consume_value_type(); params.push_back(param); } @@ -670,12 +949,16 @@ class ModuleDecoder : public Decoder { return nullptr; } std::vector returns; - for (uint32_t i = 0; i < return_count; ++i) { - LocalType ret = consume_local_type(); - if (ret == kAstStmt) error(pc_ - 1, "invalid void return type"); + for (uint32_t i = 0; ok() && i < return_count; ++i) { + LocalType ret = consume_value_type(); returns.push_back(ret); } + if (failed()) { + // Decoding failed, return void -> void + return new (module_zone) FunctionSig(0, 0, nullptr); + } + // FunctionSig stores the return types first. LocalType* buffer = module_zone->NewArray(param_count + return_count); @@ -714,7 +997,7 @@ class FunctionError : public FunctionResult { }; Vector FindSection(const byte* module_start, const byte* module_end, - WasmSection::Code code) { + WasmSectionCode code) { Decoder decoder(module_start, module_end); uint32_t magic_word = decoder.consume_u32("wasm magic"); @@ -723,24 +1006,14 @@ Vector FindSection(const byte* module_start, const byte* module_end, uint32_t magic_version = decoder.consume_u32("wasm version"); if (magic_version != kWasmVersion) decoder.error("wrong wasm version"); - while (decoder.more() && decoder.ok()) { - // Read the section name. - uint32_t string_length = decoder.consume_u32v("section name length"); - const byte* section_name_start = decoder.pc(); - decoder.consume_bytes(string_length); - if (decoder.failed()) break; - - WasmSection::Code section = - WasmSection::lookup(section_name_start, string_length); - - // Read and check the section size. - uint32_t section_length = decoder.consume_u32v("section length"); - - const byte* section_start = decoder.pc(); - decoder.consume_bytes(section_length); - if (section == code && decoder.ok()) { - return Vector(section_start, section_length); + WasmSectionIterator section_iter(decoder); + while (section_iter.more()) { + if (section_iter.section_code() == code) { + return Vector(section_iter.section_start(), + section_iter.section_length()); } + decoder.consume_bytes(section_iter.section_length(), "section payload"); + section_iter.advance(); } return Vector(); @@ -775,6 +1048,13 @@ FunctionSig* DecodeWasmSignatureForTesting(Zone* zone, const byte* start, return decoder.DecodeFunctionSignature(start); } +WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end) { + AccountingAllocator allocator; + Zone zone(&allocator); + ModuleDecoder decoder(&zone, start, end, kWasmOrigin); + return decoder.DecodeInitExpr(start); +} + FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, ModuleEnv* module_env, const byte* function_start, @@ -792,15 +1072,26 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, return decoder.DecodeSingleFunction(module_env, function); } -FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start, - const byte* module_end) { +FunctionOffsetsResult DecodeWasmFunctionOffsets( + const byte* module_start, const byte* module_end, + uint32_t num_imported_functions) { + // Find and decode the code section. Vector code_section = - FindSection(module_start, module_end, WasmSection::Code::FunctionBodies); + FindSection(module_start, module_end, kCodeSectionCode); Decoder decoder(code_section.start(), code_section.end()); - if (!code_section.start()) decoder.error("no code section"); + FunctionOffsets table; + if (!code_section.start()) { + decoder.error("no code section"); + return decoder.toResult(std::move(table)); + } + + // Reserve entries for the imported functions. + table.reserve(num_imported_functions); + for (uint32_t i = 0; i < num_imported_functions; i++) { + table.push_back(std::make_pair(0, 0)); + } uint32_t functions_count = decoder.consume_u32v("functions count"); - FunctionOffsets table; // Take care of invalid input here. if (functions_count < static_cast(code_section.length()) / 2) table.reserve(functions_count); diff --git a/src/wasm/module-decoder.h b/src/wasm/module-decoder.h index dd6bd3bc86..b1ef07d0f9 100644 --- a/src/wasm/module-decoder.h +++ b/src/wasm/module-decoder.h @@ -30,8 +30,11 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, ModuleEnv* env, // Extracts the function offset table from the wasm module bytes. // Returns a vector with entries, or failure if the wasm bytes // are detected as invalid. Note that this validation is not complete. -FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start, - const byte* module_end); +FunctionOffsetsResult DecodeWasmFunctionOffsets( + const byte* module_start, const byte* module_end, + uint32_t num_imported_functions); + +WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end); } // namespace wasm } // namespace internal diff --git a/src/wasm/wasm-debug.cc b/src/wasm/wasm-debug.cc index a112a51943..42a8e5f2ab 100644 --- a/src/wasm/wasm-debug.cc +++ b/src/wasm/wasm-debug.cc @@ -32,11 +32,15 @@ ByteArray *GetOrCreateFunctionOffsetTable(Handle debug_info) { FunctionOffsetsResult function_offsets; { DisallowHeapAllocation no_gc; + Handle wasm_object(debug_info->wasm_object(), isolate); + uint32_t num_imported_functions = + wasm::GetNumImportedFunctions(wasm_object); SeqOneByteString *wasm_bytes = wasm::GetWasmBytes(debug_info->wasm_object()); const byte *bytes_start = wasm_bytes->GetChars(); const byte *bytes_end = bytes_start + wasm_bytes->length(); - function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end); + function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end, + num_imported_functions); } DCHECK(function_offsets.ok()); size_t array_size = 2 * kIntSize * function_offsets.val.size(); diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc index 2db5e8cd92..2ac681eff2 100644 --- a/src/wasm/wasm-interpreter.cc +++ b/src/wasm/wasm-interpreter.cc @@ -722,54 +722,38 @@ class ControlTransfers : public ZoneObject { public: ControlTransferMap map_; - ControlTransfers(Zone* zone, size_t locals_encoded_size, const byte* start, - const byte* end) + ControlTransfers(Zone* zone, ModuleEnv* env, AstLocalDecls* locals, + const byte* start, const byte* end) : map_(zone) { - // A control reference including from PC, from value depth, and whether - // a value is explicitly passed (e.g. br/br_if/br_table with value). - struct CRef { - const byte* pc; - sp_t value_depth; - bool explicit_value; - }; - // Represents a control flow label. struct CLabel : public ZoneObject { const byte* target; - size_t value_depth; - ZoneVector refs; + ZoneVector refs; - CLabel(Zone* zone, size_t v) - : target(nullptr), value_depth(v), refs(zone) {} + explicit CLabel(Zone* zone) : target(nullptr), refs(zone) {} // Bind this label to the given PC. - void Bind(ControlTransferMap* map, const byte* start, const byte* pc, - bool expect_value) { + void Bind(ControlTransferMap* map, const byte* start, const byte* pc) { DCHECK_NULL(target); target = pc; - for (auto from : refs) { - auto pcdiff = static_cast(target - from.pc); - auto spdiff = static_cast(from.value_depth - value_depth); - ControlTransfer::StackAction action = ControlTransfer::kNoAction; - if (expect_value && !from.explicit_value) { - action = spdiff == 0 ? ControlTransfer::kPushVoid - : ControlTransfer::kPopAndRepush; - } - pc_t offset = static_cast(from.pc - start); - (*map)[offset] = {pcdiff, spdiff, action}; + for (auto from_pc : refs) { + auto pcdiff = static_cast(target - from_pc); + size_t offset = static_cast(from_pc - start); + (*map)[offset] = pcdiff; } } // Reference this label from the given location. - void Ref(ControlTransferMap* map, const byte* start, CRef from) { - DCHECK_GE(from.value_depth, value_depth); + void Ref(ControlTransferMap* map, const byte* start, + const byte* from_pc) { if (target) { - auto pcdiff = static_cast(target - from.pc); - auto spdiff = static_cast(from.value_depth - value_depth); - pc_t offset = static_cast(from.pc - start); - (*map)[offset] = {pcdiff, spdiff, ControlTransfer::kNoAction}; + // Target being bound before a reference means this is a loop. + DCHECK_EQ(kExprLoop, *target); + auto pcdiff = static_cast(target - from_pc); + size_t offset = static_cast(from_pc - start); + (*map)[offset] = pcdiff; } else { - refs.push_back(from); + refs.push_back(from_pc); } } }; @@ -780,122 +764,104 @@ class ControlTransfers : public ZoneObject { CLabel* end_label; CLabel* else_label; - void Ref(ControlTransferMap* map, const byte* start, const byte* from_pc, - size_t from_value_depth, bool explicit_value) { - end_label->Ref(map, start, {from_pc, from_value_depth, explicit_value}); + void Ref(ControlTransferMap* map, const byte* start, + const byte* from_pc) { + end_label->Ref(map, start, from_pc); } }; // Compute the ControlTransfer map. - // This works by maintaining a stack of control constructs similar to the + // This algorithm maintains a stack of control constructs similar to the // AST decoder. The {control_stack} allows matching {br,br_if,br_table} // bytecodes with their target, as well as determining whether the current // bytecodes are within the true or false block of an else. - // The value stack depth is tracked as {value_depth} and is needed to - // determine how many values to pop off the stack for explicit and - // implicit control flow. - std::vector control_stack; - size_t value_depth = 0; - for (BytecodeIterator i(start + locals_encoded_size, end); i.has_next(); - i.next()) { + CLabel* func_label = new (zone) CLabel(zone); + control_stack.push_back({start, func_label, nullptr}); + for (BytecodeIterator i(start, end, locals); i.has_next(); i.next()) { WasmOpcode opcode = i.current(); - TRACE("@%u: control %s (depth = %zu)\n", i.pc_offset(), - WasmOpcodes::OpcodeName(opcode), value_depth); + TRACE("@%u: control %s\n", i.pc_offset(), + WasmOpcodes::OpcodeName(opcode)); switch (opcode) { case kExprBlock: { - TRACE("control @%u $%zu: Block\n", i.pc_offset(), value_depth); - CLabel* label = new (zone) CLabel(zone, value_depth); + TRACE("control @%u: Block\n", i.pc_offset()); + CLabel* label = new (zone) CLabel(zone); control_stack.push_back({i.pc(), label, nullptr}); break; } case kExprLoop: { - TRACE("control @%u $%zu: Loop\n", i.pc_offset(), value_depth); - CLabel* label1 = new (zone) CLabel(zone, value_depth); - CLabel* label2 = new (zone) CLabel(zone, value_depth); - control_stack.push_back({i.pc(), label1, nullptr}); - control_stack.push_back({i.pc(), label2, nullptr}); - label2->Bind(&map_, start, i.pc(), false); + TRACE("control @%u: Loop\n", i.pc_offset()); + CLabel* label = new (zone) CLabel(zone); + control_stack.push_back({i.pc(), label, nullptr}); + label->Bind(&map_, start, i.pc()); break; } case kExprIf: { - TRACE("control @%u $%zu: If\n", i.pc_offset(), value_depth); - value_depth--; - CLabel* end_label = new (zone) CLabel(zone, value_depth); - CLabel* else_label = new (zone) CLabel(zone, value_depth); + TRACE("control @%u: If\n", i.pc_offset()); + CLabel* end_label = new (zone) CLabel(zone); + CLabel* else_label = new (zone) CLabel(zone); control_stack.push_back({i.pc(), end_label, else_label}); - else_label->Ref(&map_, start, {i.pc(), value_depth, false}); + else_label->Ref(&map_, start, i.pc()); break; } case kExprElse: { Control* c = &control_stack.back(); - TRACE("control @%u $%zu: Else\n", i.pc_offset(), value_depth); - c->end_label->Ref(&map_, start, {i.pc(), value_depth, false}); - value_depth = c->end_label->value_depth; + TRACE("control @%u: Else\n", i.pc_offset()); + c->end_label->Ref(&map_, start, i.pc()); DCHECK_NOT_NULL(c->else_label); - c->else_label->Bind(&map_, start, i.pc() + 1, false); + c->else_label->Bind(&map_, start, i.pc() + 1); c->else_label = nullptr; break; } case kExprEnd: { Control* c = &control_stack.back(); - TRACE("control @%u $%zu: End\n", i.pc_offset(), value_depth); + TRACE("control @%u: End\n", i.pc_offset()); if (c->end_label->target) { // only loops have bound labels. DCHECK_EQ(kExprLoop, *c->pc); - control_stack.pop_back(); - c = &control_stack.back(); + } else { + if (c->else_label) c->else_label->Bind(&map_, start, i.pc()); + c->end_label->Bind(&map_, start, i.pc() + 1); } - if (c->else_label) - c->else_label->Bind(&map_, start, i.pc() + 1, true); - c->end_label->Ref(&map_, start, {i.pc(), value_depth, false}); - c->end_label->Bind(&map_, start, i.pc() + 1, true); - value_depth = c->end_label->value_depth + 1; control_stack.pop_back(); break; } case kExprBr: { BreakDepthOperand operand(&i, i.pc()); - TRACE("control @%u $%zu: Br[arity=%u, depth=%u]\n", i.pc_offset(), - value_depth, operand.arity, operand.depth); - value_depth -= operand.arity; - control_stack[control_stack.size() - operand.depth - 1].Ref( - &map_, start, i.pc(), value_depth, operand.arity > 0); - value_depth++; + TRACE("control @%u: Br[depth=%u]\n", i.pc_offset(), operand.depth); + Control* c = &control_stack[control_stack.size() - operand.depth - 1]; + c->Ref(&map_, start, i.pc()); break; } case kExprBrIf: { BreakDepthOperand operand(&i, i.pc()); - TRACE("control @%u $%zu: BrIf[arity=%u, depth=%u]\n", i.pc_offset(), - value_depth, operand.arity, operand.depth); - value_depth -= (operand.arity + 1); - control_stack[control_stack.size() - operand.depth - 1].Ref( - &map_, start, i.pc(), value_depth, operand.arity > 0); - value_depth++; + TRACE("control @%u: BrIf[depth=%u]\n", i.pc_offset(), operand.depth); + Control* c = &control_stack[control_stack.size() - operand.depth - 1]; + c->Ref(&map_, start, i.pc()); break; } case kExprBrTable: { BranchTableOperand operand(&i, i.pc()); - TRACE("control @%u $%zu: BrTable[arity=%u count=%u]\n", i.pc_offset(), - value_depth, operand.arity, operand.table_count); - value_depth -= (operand.arity + 1); - for (uint32_t j = 0; j < operand.table_count + 1; ++j) { - uint32_t target = operand.read_entry(&i, j); - control_stack[control_stack.size() - target - 1].Ref( - &map_, start, i.pc() + j, value_depth, operand.arity > 0); + BranchTableIterator iterator(&i, operand); + TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(), + operand.table_count); + while (iterator.has_next()) { + uint32_t j = iterator.cur_index(); + uint32_t target = iterator.next(); + Control* c = &control_stack[control_stack.size() - target - 1]; + c->Ref(&map_, start, i.pc() + j); } - value_depth++; break; } default: { - value_depth = value_depth - OpcodeArity(i.pc(), end) + 1; break; } } } + if (!func_label->target) func_label->Bind(&map_, start, end); } - ControlTransfer Lookup(pc_t from) { + pcdiff_t Lookup(pc_t from) { auto result = map_.find(from); if (result == map_.end()) { V8_Fatal(__FILE__, __LINE__, "no control target for pc %zu", from); @@ -965,9 +931,9 @@ class CodeMap { if (code->targets == nullptr && code->start) { // Compute the control targets map and the local declarations. CHECK(DecodeLocalDecls(code->locals, code->start, code->end)); - code->targets = - new (zone_) ControlTransfers(zone_, code->locals.decls_encoded_size, - code->orig_start, code->orig_end); + ModuleEnv env = {module_, nullptr, kWasmOrigin}; + code->targets = new (zone_) ControlTransfers( + zone_, &env, &code->locals, code->orig_start, code->orig_end); } return code; } @@ -1006,6 +972,7 @@ class ThreadImpl : public WasmInterpreter::Thread { instance_(instance), stack_(zone), frames_(zone), + blocks_(zone), state_(WasmInterpreter::STOPPED), break_pc_(kInvalidPc), trap_reason_(kTrapCount) {} @@ -1026,6 +993,9 @@ class ThreadImpl : public WasmInterpreter::Thread { stack_.push_back(args[i]); } frames_.back().ret_pc = InitLocals(code); + blocks_.push_back( + {0, stack_.size(), frames_.size(), + static_cast(code->function->sig->return_count())}); TRACE(" => PushFrame(#%u @%zu)\n", code->function->func_index, frames_.back().ret_pc); } @@ -1074,11 +1044,11 @@ class ThreadImpl : public WasmInterpreter::Thread { return nullptr; } - virtual WasmVal GetReturnValue() { + virtual WasmVal GetReturnValue(int index) { if (state_ == WasmInterpreter::TRAPPED) return WasmVal(0xdeadbeef); CHECK_EQ(WasmInterpreter::FINISHED, state_); - CHECK_EQ(1, stack_.size()); - return stack_[0]; + CHECK_LT(static_cast(index), stack_.size()); + return stack_[index]; } virtual pc_t GetBreakpointPc() { return break_pc_; } @@ -1102,10 +1072,18 @@ class ThreadImpl : public WasmInterpreter::Thread { sp_t llimit() { return plimit() + code->locals.total_local_count; } }; + struct Block { + pc_t pc; + sp_t sp; + size_t fp; + unsigned arity; + }; + CodeMap* codemap_; WasmModuleInstance* instance_; ZoneVector stack_; ZoneVector frames_; + ZoneVector blocks_; WasmInterpreter::State state_; pc_t break_pc_; TrapReason trap_reason_; @@ -1130,6 +1108,9 @@ class ThreadImpl : public WasmInterpreter::Thread { DCHECK_GE(stack_.size(), arity); // The parameters will overlap the arguments already on the stack. frames_.push_back({code, 0, 0, stack_.size() - arity}); + blocks_.push_back( + {0, stack_.size(), frames_.size(), + static_cast(code->function->sig->return_count())}); frames_.back().ret_pc = InitLocals(code); TRACE(" => push func#%u @%zu\n", code->function->func_index, frames_.back().ret_pc); @@ -1168,21 +1149,38 @@ class ThreadImpl : public WasmInterpreter::Thread { bool SkipBreakpoint(InterpreterCode* code, pc_t pc) { if (pc == break_pc_) { + // Skip the previously hit breakpoint when resuming. break_pc_ = kInvalidPc; return true; } return false; } - bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, WasmVal val) { + int LookupTarget(InterpreterCode* code, pc_t pc) { + return static_cast(code->targets->Lookup(pc)); + } + + int DoBreak(InterpreterCode* code, pc_t pc, size_t depth) { + size_t bp = blocks_.size() - depth - 1; + Block* target = &blocks_[bp]; + DoStackTransfer(target->sp, target->arity); + blocks_.resize(bp); + return LookupTarget(code, pc); + } + + bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, size_t arity) { DCHECK_GT(frames_.size(), 0u); - stack_.resize(frames_.back().sp); + // Pop all blocks for this frame. + while (!blocks_.empty() && blocks_.back().fp == frames_.size()) { + blocks_.pop_back(); + } + + sp_t dest = frames_.back().sp; frames_.pop_back(); if (frames_.size() == 0) { - // A return from the top frame terminates the execution. + // A return from the last frame terminates the execution. state_ = WasmInterpreter::FINISHED; - stack_.clear(); - stack_.push_back(val); + DoStackTransfer(0, arity); TRACE(" => finish\n"); return false; } else { @@ -1191,16 +1189,8 @@ class ThreadImpl : public WasmInterpreter::Thread { *code = top->code; *pc = top->ret_pc; *limit = top->code->end - top->code->start; - if (top->code->start[top->call_pc] == kExprCallIndirect || - (top->code->orig_start && - top->code->orig_start[top->call_pc] == kExprCallIndirect)) { - // UGLY: An indirect call has the additional function index on the - // stack. - stack_.pop_back(); - } TRACE(" => pop func#%u @%zu\n", (*code)->function->func_index, *pc); - - stack_.push_back(val); + DoStackTransfer(dest, arity); return true; } } @@ -1211,31 +1201,21 @@ class ThreadImpl : public WasmInterpreter::Thread { *limit = target->end - target->start; } - // Adjust the program counter {pc} and the stack contents according to the - // code's precomputed control transfer map. Returns the different between - // the new pc and the old pc. - int DoControlTransfer(InterpreterCode* code, pc_t pc) { - auto target = code->targets->Lookup(pc); - switch (target.action) { - case ControlTransfer::kNoAction: - TRACE(" action [sp-%u]\n", target.spdiff); - PopN(target.spdiff); - break; - case ControlTransfer::kPopAndRepush: { - WasmVal val = Pop(); - TRACE(" action [pop x, sp-%u, push x]\n", target.spdiff - 1); - DCHECK_GE(target.spdiff, 1u); - PopN(target.spdiff - 1); - Push(pc, val); - break; - } - case ControlTransfer::kPushVoid: - TRACE(" action [sp-%u, push void]\n", target.spdiff); - PopN(target.spdiff); - Push(pc, WasmVal()); - break; + // Copies {arity} values on the top of the stack down the stack to {dest}, + // dropping the values in-between. + void DoStackTransfer(sp_t dest, size_t arity) { + // before: |---------------| pop_count | arity | + // ^ 0 ^ dest ^ stack_.size() + // + // after: |---------------| arity | + // ^ 0 ^ stack_.size() + DCHECK_LE(dest, stack_.size()); + DCHECK_LE(dest + arity, stack_.size()); + size_t pop_count = stack_.size() - dest - arity; + for (size_t i = 0; i < arity; i++) { + stack_[dest + i] = stack_[dest + pop_count + i]; } - return target.pcdiff; + stack_.resize(stack_.size() - pop_count); } void Execute(InterpreterCode* code, pc_t pc, int max) { @@ -1251,8 +1231,8 @@ class ThreadImpl : public WasmInterpreter::Thread { if (pc >= limit) { // Fell off end of code; do an implicit return. TRACE("@%-3zu: ImplicitReturn\n", pc); - WasmVal val = PopArity(code->function->sig->return_count()); - if (!DoReturn(&code, &pc, &limit, val)) return; + if (!DoReturn(&code, &pc, &limit, code->function->sig->return_count())) + return; decoder.Reset(code->start, code->end); continue; } @@ -1285,27 +1265,37 @@ class ThreadImpl : public WasmInterpreter::Thread { switch (orig) { case kExprNop: - Push(pc, WasmVal()); break; - case kExprBlock: + case kExprBlock: { + BlockTypeOperand operand(&decoder, code->at(pc)); + blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity}); + len = 1 + operand.length; + break; + } case kExprLoop: { - // Do nothing. + BlockTypeOperand operand(&decoder, code->at(pc)); + blocks_.push_back({pc, stack_.size(), frames_.size(), 0}); + len = 1 + operand.length; break; } case kExprIf: { + BlockTypeOperand operand(&decoder, code->at(pc)); WasmVal cond = Pop(); bool is_true = cond.to() != 0; + blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity}); if (is_true) { // fall through to the true block. + len = 1 + operand.length; TRACE(" true => fallthrough\n"); } else { - len = DoControlTransfer(code, pc); + len = LookupTarget(code, pc); TRACE(" false => @%zu\n", pc + len); } break; } case kExprElse: { - len = DoControlTransfer(code, pc); + blocks_.pop_back(); + len = LookupTarget(code, pc); TRACE(" end => @%zu\n", pc + len); break; } @@ -1318,42 +1308,34 @@ class ThreadImpl : public WasmInterpreter::Thread { } case kExprBr: { BreakDepthOperand operand(&decoder, code->at(pc)); - WasmVal val = PopArity(operand.arity); - len = DoControlTransfer(code, pc); + len = DoBreak(code, pc, operand.depth); TRACE(" br => @%zu\n", pc + len); - if (operand.arity > 0) Push(pc, val); break; } case kExprBrIf: { BreakDepthOperand operand(&decoder, code->at(pc)); WasmVal cond = Pop(); - WasmVal val = PopArity(operand.arity); bool is_true = cond.to() != 0; if (is_true) { - len = DoControlTransfer(code, pc); + len = DoBreak(code, pc, operand.depth); TRACE(" br_if => @%zu\n", pc + len); - if (operand.arity > 0) Push(pc, val); } else { TRACE(" false => fallthrough\n"); len = 1 + operand.length; - Push(pc, WasmVal()); } break; } case kExprBrTable: { BranchTableOperand operand(&decoder, code->at(pc)); uint32_t key = Pop().to(); - WasmVal val = PopArity(operand.arity); if (key >= operand.table_count) key = operand.table_count; - len = DoControlTransfer(code, pc + key) + key; - TRACE(" br[%u] => @%zu\n", key, pc + len); - if (operand.arity > 0) Push(pc, val); + len = key + DoBreak(code, pc + key, operand.table[key]); + TRACE(" br[%u] => @%zu\n", key, pc + key + len); break; } case kExprReturn: { - ReturnArityOperand operand(&decoder, code->at(pc)); - WasmVal val = PopArity(operand.arity); - if (!DoReturn(&code, &pc, &limit, val)) return; + size_t arity = code->function->sig->return_count(); + if (!DoReturn(&code, &pc, &limit, arity)) return; decoder.Reset(code->start, code->end); continue; } @@ -1362,8 +1344,7 @@ class ThreadImpl : public WasmInterpreter::Thread { return CommitPc(pc); } case kExprEnd: { - len = DoControlTransfer(code, pc); - DCHECK_EQ(1, len); + blocks_.pop_back(); break; } case kExprI8Const: { @@ -1403,6 +1384,13 @@ class ThreadImpl : public WasmInterpreter::Thread { break; } case kExprSetLocal: { + LocalIndexOperand operand(&decoder, code->at(pc)); + WasmVal val = Pop(); + stack_[frames_.back().sp + operand.index] = val; + len = 1 + operand.length; + break; + } + case kExprTeeLocal: { LocalIndexOperand operand(&decoder, code->at(pc)); WasmVal val = Pop(); stack_[frames_.back().sp + operand.index] = val; @@ -1410,6 +1398,10 @@ class ThreadImpl : public WasmInterpreter::Thread { len = 1 + operand.length; break; } + case kExprDrop: { + Pop(); + break; + } case kExprCallFunction: { CallFunctionOperand operand(&decoder, code->at(pc)); InterpreterCode* target = codemap()->GetCode(operand.index); @@ -1420,9 +1412,7 @@ class ThreadImpl : public WasmInterpreter::Thread { } case kExprCallIndirect: { CallIndirectOperand operand(&decoder, code->at(pc)); - size_t index = stack_.size() - operand.arity - 1; - DCHECK_LT(index, stack_.size()); - uint32_t entry_index = stack_[index].to(); + uint32_t entry_index = Pop().to(); // Assume only one table for now. DCHECK_LE(module()->function_tables.size(), 1u); InterpreterCode* target = codemap()->GetIndirectCode(0, entry_index); @@ -1437,10 +1427,6 @@ class ThreadImpl : public WasmInterpreter::Thread { decoder.Reset(code->start, code->end); continue; } - case kExprCallImport: { - UNIMPLEMENTED(); - break; - } case kExprGetGlobal: { GlobalIndexOperand operand(&decoder, code->at(pc)); const WasmGlobal* global = &module()->globals[operand.index]; @@ -1479,7 +1465,6 @@ class ThreadImpl : public WasmInterpreter::Thread { } else { UNREACHABLE(); } - Push(pc, val); len = 1 + operand.length; break; } @@ -1528,7 +1513,6 @@ class ThreadImpl : public WasmInterpreter::Thread { } \ byte* addr = instance()->mem_start + operand.offset + index; \ WriteLittleEndianValue(addr, static_cast(val.to())); \ - Push(pc, val); \ len = 1 + operand.length; \ break; \ } @@ -1594,7 +1578,8 @@ class ThreadImpl : public WasmInterpreter::Thread { break; } case kExprMemorySize: { - Push(pc, WasmVal(static_cast(instance()->mem_size))); + Push(pc, WasmVal(static_cast(instance()->mem_size / + WasmModule::kPageSize))); break; } #define EXECUTE_SIMPLE_BINOP(name, ctype, op) \ @@ -1669,7 +1654,7 @@ class ThreadImpl : public WasmInterpreter::Thread { void Push(pc_t pc, WasmVal val) { // TODO(titzer): store PC as well? - stack_.push_back(val); + if (val.type != kAstStmt) stack_.push_back(val); } void TraceStack(const char* phase, pc_t pc) { @@ -1850,7 +1835,7 @@ bool WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function, ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting( Zone* zone, const byte* start, const byte* end) { - ControlTransfers targets(zone, 0, start, end); + ControlTransfers targets(zone, nullptr, nullptr, start, end); return targets.map_; } diff --git a/src/wasm/wasm-interpreter.h b/src/wasm/wasm-interpreter.h index 91b592c0c1..1f6e2a7613 100644 --- a/src/wasm/wasm-interpreter.h +++ b/src/wasm/wasm-interpreter.h @@ -28,15 +28,7 @@ typedef uint32_t spdiff_t; const pc_t kInvalidPc = 0x80000000; -// Visible for testing. A {ControlTransfer} helps the interpreter figure out -// the target program counter and stack manipulations for a branch. -struct ControlTransfer { - enum StackAction { kNoAction, kPopAndRepush, kPushVoid }; - pcdiff_t pcdiff; // adjustment to the program counter (positive or negative). - spdiff_t spdiff; // number of elements to pop off the stack. - StackAction action; // action to perform on the stack. -}; -typedef ZoneMap ControlTransferMap; +typedef ZoneMap ControlTransferMap; // Macro for defining union members. #define FOREACH_UNION_MEMBER(V) \ @@ -132,7 +124,7 @@ class WasmInterpreter { virtual int GetFrameCount() = 0; virtual const WasmFrame* GetFrame(int index) = 0; virtual WasmFrame* GetMutableFrame(int index) = 0; - virtual WasmVal GetReturnValue() = 0; + virtual WasmVal GetReturnValue(int index = 0) = 0; // Thread-specific breakpoints. bool SetBreakpoint(const WasmFunction* function, int pc, bool enabled); @@ -189,9 +181,8 @@ class WasmInterpreter { bool SetFunctionCodeForTesting(const WasmFunction* function, const byte* start, const byte* end); - // Computes the control targets for the given bytecode as {pc offset, sp - // offset} - // pairs. Used internally in the interpreter, but exposed for testing. + // Computes the control transfers for the given bytecode. Used internally in + // the interpreter, but exposed for testing. static ControlTransferMap ComputeControlTransfersForTesting(Zone* zone, const byte* start, const byte* end); diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index 9494d4c0df..5d68cc433b 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -160,7 +160,7 @@ i::MaybeHandle InstantiateModule( } object = i::wasm::WasmModule::Instantiate( - isolate, module_object.ToHandleChecked(), ffi, memory); + isolate, thrower, module_object.ToHandleChecked(), ffi, memory); if (!object.is_null()) { args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); } @@ -296,10 +296,10 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo& args) { i::Handle mem_obj = v8::Utils::OpenHandle(*obj); memory = i::Handle(i::JSArrayBuffer::cast(*mem_obj)); } - i::MaybeHandle instance = - i::wasm::WasmModule::Instantiate(i_isolate, module_obj, ffi, memory); + i::MaybeHandle instance = i::wasm::WasmModule::Instantiate( + i_isolate, &thrower, module_obj, ffi, memory); if (instance.is_null()) { - thrower.Error("Could not instantiate module"); + if (!thrower.error()) thrower.Error("Could not instantiate module"); return; } v8::ReturnValue return_value = args.GetReturnValue(); @@ -388,10 +388,10 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo& args) { } i::Isolate* i_isolate = reinterpret_cast(isolate); - i::Handle table_cons( + i::Handle table_ctor( i_isolate->native_context()->wasm_table_constructor()); i::Handle table_obj = - i_isolate->factory()->NewJSObject(table_cons); + i_isolate->factory()->NewJSObject(table_ctor); i::Handle fixed_array = i_isolate->factory()->NewFixedArray(initial); i::Object* null = i_isolate->heap()->null_value(); @@ -406,6 +406,7 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo& args) { v8::ReturnValue return_value = args.GetReturnValue(); return_value.Set(Utils::ToLocal(table_obj)); } + void WebAssemblyMemory(const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); HandleScope scope(isolate); @@ -436,23 +437,14 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo& args) { } } i::Isolate* i_isolate = reinterpret_cast(isolate); - i::Handle memory_cons( - i_isolate->native_context()->wasm_memory_constructor()); - i::Handle memory_obj = - i_isolate->factory()->NewJSObject(memory_cons); i::Handle buffer = i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared); size_t size = static_cast(i::wasm::WasmModule::kPageSize) * static_cast(initial); i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size); - memory_obj->SetInternalField(0, *buffer); - memory_obj->SetInternalField( - 1, has_maximum - ? static_cast(i::Smi::FromInt(maximum)) - : static_cast(i_isolate->heap()->undefined_value())); - i::Handle memory_sym( - i_isolate->native_context()->wasm_memory_sym()); - i::Object::SetProperty(memory_obj, memory_sym, memory_obj, i::STRICT).Check(); + + i::Handle memory_obj = i::WasmJs::CreateWasmMemoryObject( + i_isolate, buffer, has_maximum, maximum); v8::ReturnValue return_value = args.GetReturnValue(); return_value.Set(Utils::ToLocal(memory_obj)); } @@ -492,6 +484,24 @@ void WebAssemblyMemoryGetBuffer( } } // namespace +i::Handle i::WasmJs::CreateWasmMemoryObject( + i::Isolate* i_isolate, i::Handle buffer, bool has_maximum, + int maximum) { + i::Handle memory_ctor( + i_isolate->native_context()->wasm_memory_constructor()); + i::Handle memory_obj = + i_isolate->factory()->NewJSObject(memory_ctor); + memory_obj->SetInternalField(0, *buffer); + memory_obj->SetInternalField( + 1, has_maximum + ? static_cast(i::Smi::FromInt(maximum)) + : static_cast(i_isolate->heap()->undefined_value())); + i::Handle memory_sym( + i_isolate->native_context()->wasm_memory_sym()); + i::Object::SetProperty(memory_obj, memory_sym, memory_obj, i::STRICT).Check(); + return memory_obj; +} + // TODO(titzer): we use the API to create the function template because the // internal guts are too ugly to replicate here. static i::Handle NewTemplate(i::Isolate* i_isolate, diff --git a/src/wasm/wasm-js.h b/src/wasm/wasm-js.h index 37e1aafc59..b686e7ebb9 100644 --- a/src/wasm/wasm-js.h +++ b/src/wasm/wasm-js.h @@ -24,6 +24,10 @@ class WasmJs { static void InstallWasmConstructors(Isolate* isolate, Handle global, Handle context); + + static Handle CreateWasmMemoryObject(Isolate* isolate, + Handle buffer, + bool has_maximum, int maximum); }; } // namespace internal diff --git a/src/wasm/wasm-macro-gen.h b/src/wasm/wasm-macro-gen.h index 6a5027bfad..fd10a3929a 100644 --- a/src/wasm/wasm-macro-gen.h +++ b/src/wasm/wasm-macro-gen.h @@ -17,17 +17,17 @@ #define WASM_MODULE_HEADER U32_LE(kWasmMagic), U32_LE(kWasmVersion) -#define SIG_INDEX(v) U16_LE(v) -// TODO(binji): make SIG_INDEX match this. #define IMPORT_SIG_INDEX(v) U32V_1(v) #define FUNC_INDEX(v) U32V_1(v) +#define TABLE_INDEX(v) U32V_1(v) #define NO_NAME U32V_1(0) #define NAME_LENGTH(v) U32V_1(v) +#define ENTRY_COUNT(v) U32V_1(v) #define ZERO_ALIGNMENT 0 #define ZERO_OFFSET 0 -#define BR_TARGET(v) U32_LE(v) +#define BR_TARGET(v) U32V_1(v) #define MASK_7 ((1 << 7) - 1) #define MASK_14 ((1 << 14) - 1) @@ -62,36 +62,76 @@ #define ARITY_0 0 #define ARITY_1 1 +#define ARITY_2 2 #define DEPTH_0 0 #define DEPTH_1 1 +#define DEPTH_2 2 +#define ARITY_2 2 + +#define WASM_BLOCK(...) kExprBlock, kLocalVoid, __VA_ARGS__, kExprEnd + +#define WASM_BLOCK_T(t, ...) \ + kExprBlock, static_cast(WasmOpcodes::LocalTypeCodeFor(t)), \ + __VA_ARGS__, kExprEnd + +#define WASM_BLOCK_TT(t1, t2, ...) \ + kExprBlock, kMultivalBlock, 0, \ + static_cast(WasmOpcodes::LocalTypeCodeFor(t1)), \ + static_cast(WasmOpcodes::LocalTypeCodeFor(t2)), __VA_ARGS__, \ + kExprEnd + +#define WASM_BLOCK_I(...) kExprBlock, kLocalI32, __VA_ARGS__, kExprEnd +#define WASM_BLOCK_L(...) kExprBlock, kLocalI64, __VA_ARGS__, kExprEnd +#define WASM_BLOCK_F(...) kExprBlock, kLocalF32, __VA_ARGS__, kExprEnd +#define WASM_BLOCK_D(...) kExprBlock, kLocalF64, __VA_ARGS__, kExprEnd + +#define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd + +#define WASM_LOOP(...) kExprLoop, kLocalVoid, __VA_ARGS__, kExprEnd +#define WASM_LOOP_I(...) kExprLoop, kLocalI32, __VA_ARGS__, kExprEnd +#define WASM_LOOP_L(...) kExprLoop, kLocalI64, __VA_ARGS__, kExprEnd +#define WASM_LOOP_F(...) kExprLoop, kLocalF32, __VA_ARGS__, kExprEnd +#define WASM_LOOP_D(...) kExprLoop, kLocalF64, __VA_ARGS__, kExprEnd + +#define WASM_IF(cond, tstmt) cond, kExprIf, kLocalVoid, tstmt, kExprEnd -#define WASM_BLOCK(...) kExprBlock, __VA_ARGS__, kExprEnd -#define WASM_INFINITE_LOOP kExprLoop, kExprBr, ARITY_0, DEPTH_0, kExprEnd -#define WASM_LOOP(...) kExprLoop, __VA_ARGS__, kExprEnd -#define WASM_IF(cond, tstmt) cond, kExprIf, tstmt, kExprEnd #define WASM_IF_ELSE(cond, tstmt, fstmt) \ - cond, kExprIf, tstmt, kExprElse, fstmt, kExprEnd + cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd + +#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \ + cond, kExprIf, static_cast(WasmOpcodes::LocalTypeCodeFor(t)), tstmt, \ + kExprElse, fstmt, kExprEnd + +#define WASM_IF_ELSE_TT(t1, t2, cond, tstmt, fstmt) \ + cond, kExprIf, kMultivalBlock, 0, \ + static_cast(WasmOpcodes::LocalTypeCodeFor(t1)), \ + static_cast(WasmOpcodes::LocalTypeCodeFor(t2)), tstmt, kExprElse, \ + fstmt, kExprEnd + +#define WASM_IF_ELSE_I(cond, tstmt, fstmt) \ + cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd +#define WASM_IF_ELSE_L(cond, tstmt, fstmt) \ + cond, kExprIf, kLocalI64, tstmt, kExprElse, fstmt, kExprEnd +#define WASM_IF_ELSE_F(cond, tstmt, fstmt) \ + cond, kExprIf, kLocalF32, tstmt, kExprElse, fstmt, kExprEnd +#define WASM_IF_ELSE_D(cond, tstmt, fstmt) \ + cond, kExprIf, kLocalF64, tstmt, kExprElse, fstmt, kExprEnd + #define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect -#define WASM_BR(depth) kExprBr, ARITY_0, static_cast(depth) -#define WASM_BR_IF(depth, cond) \ - cond, kExprBrIf, ARITY_0, static_cast(depth) -#define WASM_BRV(depth, val) val, kExprBr, ARITY_1, static_cast(depth) -#define WASM_BRV_IF(depth, val, cond) \ - val, cond, kExprBrIf, ARITY_1, static_cast(depth) -#define WASM_BREAK(depth) kExprBr, ARITY_0, static_cast(depth + 1) -#define WASM_CONTINUE(depth) kExprBr, ARITY_0, static_cast(depth) -#define WASM_BREAKV(depth, val) \ - val, kExprBr, ARITY_1, static_cast(depth + 1) -#define WASM_RETURN0 kExprReturn, ARITY_0 -#define WASM_RETURN1(val) val, kExprReturn, ARITY_1 -#define WASM_RETURNN(count, ...) __VA_ARGS__, kExprReturn, count + +#define WASM_RETURN0 kExprReturn +#define WASM_RETURN1(val) val, kExprReturn +#define WASM_RETURNN(count, ...) __VA_ARGS__, kExprReturn + +#define WASM_BR(depth) kExprBr, static_cast(depth) +#define WASM_BR_IF(depth, cond) cond, kExprBrIf, static_cast(depth) +#define WASM_BR_IFD(depth, val, cond) \ + val, cond, kExprBrIf, static_cast(depth), kExprDrop +#define WASM_CONTINUE(depth) kExprBr, static_cast(depth) #define WASM_UNREACHABLE kExprUnreachable #define WASM_BR_TABLE(key, count, ...) \ - key, kExprBrTable, ARITY_0, U32V_1(count), __VA_ARGS__ - -#define WASM_BR_TABLEV(val, key, count, ...) \ - val, key, kExprBrTable, ARITY_1, U32V_1(count), __VA_ARGS__ + key, kExprBrTable, U32V_1(count), __VA_ARGS__ #define WASM_CASE(x) static_cast(x), static_cast(x >> 8) #define WASM_CASE_BR(x) static_cast(x), static_cast(0x80 | (x) >> 8) @@ -343,6 +383,8 @@ class LocalDeclEncoder { static_cast(bit_cast(val) >> 56) #define WASM_GET_LOCAL(index) kExprGetLocal, static_cast(index) #define WASM_SET_LOCAL(index, val) val, kExprSetLocal, static_cast(index) +#define WASM_TEE_LOCAL(index, val) val, kExprTeeLocal, static_cast(index) +#define WASM_DROP kExprDrop #define WASM_GET_GLOBAL(index) kExprGetGlobal, static_cast(index) #define WASM_SET_GLOBAL(index, val) \ val, kExprSetGlobal, static_cast(index) @@ -374,49 +416,25 @@ class LocalDeclEncoder { v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \ alignment, ZERO_OFFSET -#define WASM_CALL_FUNCTION0(index) \ - kExprCallFunction, 0, static_cast(index) -#define WASM_CALL_FUNCTION1(index, a) \ - a, kExprCallFunction, 1, static_cast(index) -#define WASM_CALL_FUNCTION2(index, a, b) \ - a, b, kExprCallFunction, 2, static_cast(index) -#define WASM_CALL_FUNCTION3(index, a, b, c) \ - a, b, c, kExprCallFunction, 3, static_cast(index) -#define WASM_CALL_FUNCTION4(index, a, b, c, d) \ - a, b, c, d, kExprCallFunction, 4, static_cast(index) -#define WASM_CALL_FUNCTION5(index, a, b, c, d, e) \ - kExprCallFunction, 5, static_cast(index) -#define WASM_CALL_FUNCTIONN(arity, index, ...) \ - __VA_ARGS__, kExprCallFunction, arity, static_cast(index) - -#define WASM_CALL_IMPORT0(index) kExprCallImport, 0, static_cast(index) -#define WASM_CALL_IMPORT1(index, a) \ - a, kExprCallImport, 1, static_cast(index) -#define WASM_CALL_IMPORT2(index, a, b) \ - a, b, kExprCallImport, 2, static_cast(index) -#define WASM_CALL_IMPORT3(index, a, b, c) \ - a, b, c, kExprCallImport, 3, static_cast(index) -#define WASM_CALL_IMPORT4(index, a, b, c, d) \ - a, b, c, d, kExprCallImport, 4, static_cast(index) -#define WASM_CALL_IMPORT5(index, a, b, c, d, e) \ - a, b, c, d, e, kExprCallImport, 5, static_cast(index) -#define WASM_CALL_IMPORTN(arity, index, ...) \ - __VA_ARGS__, kExprCallImport, U32V_1(arity), static_cast(index), +#define WASM_CALL_FUNCTION0(index) kExprCallFunction, static_cast(index) +#define WASM_CALL_FUNCTION(index, ...) \ + __VA_ARGS__, kExprCallFunction, static_cast(index) +// TODO(titzer): change usages of these macros to put func last. #define WASM_CALL_INDIRECT0(index, func) \ - func, kExprCallIndirect, 0, static_cast(index) + func, kExprCallIndirect, static_cast(index) #define WASM_CALL_INDIRECT1(index, func, a) \ - func, a, kExprCallIndirect, 1, static_cast(index) + a, func, kExprCallIndirect, static_cast(index) #define WASM_CALL_INDIRECT2(index, func, a, b) \ - func, a, b, kExprCallIndirect, 2, static_cast(index) + a, b, func, kExprCallIndirect, static_cast(index) #define WASM_CALL_INDIRECT3(index, func, a, b, c) \ - func, a, b, c, kExprCallIndirect, 3, static_cast(index) + a, b, c, func, kExprCallIndirect, static_cast(index) #define WASM_CALL_INDIRECT4(index, func, a, b, c, d) \ - func, a, b, c, d, kExprCallIndirect, 4, static_cast(index) + a, b, c, d, func, kExprCallIndirect, static_cast(index) #define WASM_CALL_INDIRECT5(index, func, a, b, c, d, e) \ - func, a, b, c, d, e, kExprCallIndirect, 5, static_cast(index) + a, b, c, d, e, func, kExprCallIndirect, static_cast(index) #define WASM_CALL_INDIRECTN(arity, index, func, ...) \ - func, __VA_ARGS__, kExprCallIndirect, U32V_1(arity), static_cast(index) + __VA_ARGS__, func, kExprCallIndirect, static_cast(index) #define WASM_NOT(x) x, kExprI32Eqz #define WASM_SEQ(...) __VA_ARGS__ @@ -424,11 +442,16 @@ class LocalDeclEncoder { //------------------------------------------------------------------------------ // Constructs that are composed of multiple bytecodes. //------------------------------------------------------------------------------ -#define WASM_WHILE(x, y) \ - kExprLoop, x, kExprIf, y, kExprBr, ARITY_1, DEPTH_1, kExprEnd, kExprEnd +#define WASM_WHILE(x, y) \ + kExprLoop, kLocalVoid, x, kExprIf, kLocalVoid, y, kExprBr, DEPTH_1, \ + kExprEnd, kExprEnd #define WASM_INC_LOCAL(index) \ kExprGetLocal, static_cast(index), kExprI8Const, 1, kExprI32Add, \ - kExprSetLocal, static_cast(index) + kExprTeeLocal, static_cast(index) +#define WASM_INC_LOCAL_BYV(index, count) \ + kExprGetLocal, static_cast(index), kExprI8Const, \ + static_cast(count), kExprI32Add, kExprTeeLocal, \ + static_cast(index) #define WASM_INC_LOCAL_BY(index, count) \ kExprGetLocal, static_cast(index), kExprI8Const, \ static_cast(count), kExprI32Add, kExprSetLocal, \ @@ -611,4 +634,13 @@ class LocalDeclEncoder { #define SIZEOF_SIG_ENTRY_x_xx 6 #define SIZEOF_SIG_ENTRY_x_xxx 7 +#define WASM_BRV(depth, val) val, kExprBr, static_cast(depth) +#define WASM_BRV_IF(depth, val, cond) \ + val, cond, kExprBrIf, static_cast(depth) +#define WASM_BRV_IFD(depth, val, cond) \ + val, cond, kExprBrIf, static_cast(depth), kExprDrop +#define WASM_IFB(cond, ...) cond, kExprIf, kLocalVoid, __VA_ARGS__, kExprEnd +#define WASM_BR_TABLEV(val, key, count, ...) \ + val, key, kExprBrTable, U32V_1(count), __VA_ARGS__ + #endif // V8_WASM_MACRO_GEN_H_ diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc index 582e5eb07b..b2a954074a 100644 --- a/src/wasm/wasm-module.cc +++ b/src/wasm/wasm-module.cc @@ -18,6 +18,7 @@ #include "src/wasm/module-decoder.h" #include "src/wasm/wasm-debug.h" #include "src/wasm/wasm-function-name-table.h" +#include "src/wasm/wasm-js.h" #include "src/wasm/wasm-module.h" #include "src/wasm/wasm-result.h" @@ -27,6 +28,39 @@ namespace v8 { namespace internal { namespace wasm { +const char* SectionName(WasmSectionCode code) { + switch (code) { + case kUnknownSectionCode: + return "Unknown"; + case kTypeSectionCode: + return "Type"; + case kImportSectionCode: + return "Import"; + case kFunctionSectionCode: + return "Function"; + case kTableSectionCode: + return "Table"; + case kMemorySectionCode: + return "Memory"; + case kGlobalSectionCode: + return "Global"; + case kExportSectionCode: + return "Export"; + case kStartSectionCode: + return "Start"; + case kCodeSectionCode: + return "Code"; + case kElementSectionCode: + return "Element"; + case kDataSectionCode: + return "Data"; + case kNameSectionCode: + return "Name"; + default: + return ""; + } +} + enum JSFunctionExportInternalField { kInternalModuleInstance, kInternalArity, @@ -35,59 +69,6 @@ enum JSFunctionExportInternalField { static const int kPlaceholderMarker = 1000000000; -static const char* wasmSections[] = { -#define F(enumerator, order, string) string, - FOR_EACH_WASM_SECTION_TYPE(F) -#undef F - "" // entry for "Max" -}; - -static uint8_t wasmSectionsLengths[]{ -#define F(enumerator, order, string) sizeof(string) - 1, - FOR_EACH_WASM_SECTION_TYPE(F) -#undef F - 9 // entry for "Max" -}; - -static uint8_t wasmSectionsOrders[]{ -#define F(enumerator, order, string) order, - FOR_EACH_WASM_SECTION_TYPE(F) -#undef F - 0 // entry for "Max" -}; - -static_assert(sizeof(wasmSections) / sizeof(wasmSections[0]) == - (size_t)WasmSection::Code::Max + 1, - "expected enum WasmSection::Code to be monotonic from 0"); - -WasmSection::Code WasmSection::begin() { return (WasmSection::Code)0; } -WasmSection::Code WasmSection::end() { return WasmSection::Code::Max; } -WasmSection::Code WasmSection::next(WasmSection::Code code) { - return (WasmSection::Code)(1 + (uint32_t)code); -} - -const char* WasmSection::getName(WasmSection::Code code) { - return wasmSections[(size_t)code]; -} - -size_t WasmSection::getNameLength(WasmSection::Code code) { - return wasmSectionsLengths[(size_t)code]; -} - -int WasmSection::getOrder(WasmSection::Code code) { - return wasmSectionsOrders[(size_t)code]; -} - -WasmSection::Code WasmSection::lookup(const byte* string, uint32_t length) { - // TODO(jfb) Linear search, it may be better to do a common-prefix search. - for (Code i = begin(); i != end(); i = next(i)) { - if (getNameLength(i) == length && 0 == memcmp(getName(i), string, length)) { - return i; - } - } - return Code::Max; -} - std::ostream& operator<<(std::ostream& os, const WasmModule& module) { os << "WASM module with "; os << (module.min_mem_pages * module.kPageSize) << " min mem"; @@ -158,7 +139,7 @@ Object* GetOwningWasmInstance(Object* undefined, Code* code) { namespace { // Internal constants for the layout of the module object. -enum WasmInstanceFields { +enum WasmInstanceObjectFields { kWasmCompiledModule = 0, kWasmModuleFunctionTable, kWasmModuleCodeTable, @@ -169,6 +150,7 @@ enum WasmInstanceFields { kWasmFunctionNamesArray, kWasmModuleBytesString, kWasmDebugInfo, + kWasmNumImportedFunctions, kWasmModuleInternalFieldCount }; @@ -178,17 +160,15 @@ enum WasmInstanceFields { // the compiled module is either obtained from the current v8 instance, or from // a snapshot produced by a compatible (==identical) v8 instance, we simply // fail at instantiation time, in the face of invalid data. -enum CompiledWasmObjectFields { - kFunctions, // FixedArray of Code - kImportData, // maybe FixedArray of FixedArray respecting the - // WasmImportMetadata structure. - kImportMap, // FixedArray. The i-th element is the Code object used for - // import i - kExports, // maybe FixedArray of FixedArray of WasmExportMetadata - // structure - kStartupFunction, // maybe FixedArray of WasmExportMetadata structure +enum WasmCompiledModule { + kCodeTable, // FixedArray of Code + kImportData, // maybe FixedArray of FixedArray respecting the + // WasmImportData structure. + kExportData, // maybe FixedArray of FixedArray of WasmExportData + // structure + kStartupData, // maybe FixedArray of WasmExportData structure kTableOfIndirectFunctionTables, // maybe FixedArray of FixedArray of - // WasmIndirectFunctionTableMetadata + // WasmIndirectFunctionTableData kModuleBytes, // maybe String kFunctionNameTable, // maybe ByteArray kMinRequiredMemory, // Smi. an uint32_t @@ -197,33 +177,32 @@ enum CompiledWasmObjectFields { // WasmSegmentInfo structure kDataSegments, // maybe ByteArray. - kGlobalsSize, // Smi. an uint32_t - kMemSize, // Smi.an uint32_t - kMemStart, // MaybeHandle - kExportMem, // Smi. bool - kOrigin, // Smi. ModuleOrigin - kNextInstance, // WeakCell. See compiled code cloning. - kPrevInstance, // WeakCell. See compiled code cloning. - kOwningInstance, // WeakCell, pointing to the owning instance. - kModuleObject, // WeakCell, pointing to the module object. - kCompiledWasmObjectTableSize // Sentinel value. + kGlobalsSize, // Smi. an uint32_t + kMemSize, // Smi.an uint32_t + kMemStart, // MaybeHandle + kExportMem, // Smi. bool + kOrigin, // Smi. ModuleOrigin + kNextInstance, // WeakCell. See compiled code cloning. + kPrevInstance, // WeakCell. See compiled code cloning. + kOwningInstance, // WeakCell, pointing to the owning instance. + kModuleObject, // WeakCell, pointing to the module object. + kWasmCompiledModuleSize // Sentinel value. }; -enum WasmImportMetadata { - kModuleName, // String - kFunctionName, // maybe String - kOutputCount, // Smi. an uint32_t - kSignature, // ByteArray. A copy of the data in FunctionSig - kWasmImportDataTableSize // Sentinel value. +enum WasmImportData { + kModuleName, // String + kFunctionName, // maybe String + kOutputCount, // Smi. an uint32_t + kSignature, // ByteArray. A copy of the data in FunctionSig + kWasmImportDataSize // Sentinel value. }; -enum WasmExportMetadata { - kExportCode, // Code - kExportName, // String - kExportArity, // Smi, an int - kExportedFunctionIndex, // Smi, an uint32_t - kExportedSignature, // ByteArray. A copy of the data in FunctionSig - kWasmExportMetadataTableSize // Sentinel value. +enum WasmExportData { + kExportName, // String + kExportArity, // Smi, an int + kExportedFunctionIndex, // Smi, an uint32_t + kExportedSignature, // ByteArray. A copy of the data in FunctionSig + kWasmExportDataSize // Sentinel value. }; enum WasmSegmentInfo { @@ -232,10 +211,10 @@ enum WasmSegmentInfo { kWasmSegmentInfoSize // Sentinel value. }; -enum WasmIndirectFunctionTableMetadata { - kSize, // Smi. an uint32_t - kTable, // FixedArray of indirect function table - kWasmIndirectFunctionTableMetadataSize // Sentinel value. +enum WasmIndirectFunctionTableData { + kSize, // Smi. an uint32_t + kTable, // FixedArray of indirect function table + kWasmIndirectFunctionTableDataSize // Sentinel value. }; uint32_t GetMinModuleMemSize(const WasmModule* module) { @@ -279,7 +258,6 @@ void SaveDataSegmentInfo(Factory* factory, const WasmModule* module, static_cast(module->data_segments.size()), TENURED); uint32_t data_size = 0; for (const WasmDataSegment& segment : module->data_segments) { - if (!segment.init) continue; if (segment.source_size == 0) continue; data_size += segment.source_size; } @@ -288,11 +266,12 @@ void SaveDataSegmentInfo(Factory* factory, const WasmModule* module, uint32_t last_insertion_pos = 0; for (uint32_t i = 0; i < module->data_segments.size(); ++i) { const WasmDataSegment& segment = module->data_segments[i]; - if (!segment.init) continue; if (segment.source_size == 0) continue; Handle js_segment = factory->NewByteArray(kWasmSegmentInfoSize * sizeof(uint32_t), TENURED); - js_segment->set_int(kDestAddr, segment.dest_addr); + // TODO(titzer): add support for global offsets for dest_addr + CHECK_EQ(WasmInitExpr::kI32Const, segment.dest_addr.kind); + js_segment->set_int(kDestAddr, segment.dest_addr.val.i32_const); js_segment->set_int(kSourceSize, segment.source_size); segments->set(i, *js_segment); data->copy_in(last_insertion_pos, @@ -401,86 +380,67 @@ Handle CreatePlaceholder(Factory* factory, uint32_t index, return code; } -// TODO(mtrofin): remove when we stop relying on placeholders. -void InitializePlaceholders(Factory* factory, - std::vector>* placeholders, - size_t size) { - DCHECK(placeholders->empty()); - placeholders->reserve(size); - - for (uint32_t i = 0; i < size; ++i) { - placeholders->push_back(CreatePlaceholder(factory, i, Code::WASM_FUNCTION)); - } -} - -bool LinkFunction(Isolate* isolate, Handle unlinked, - Handle code_targets, - Code::Kind kind = Code::WASM_FUNCTION) { +bool LinkFunction(Handle unlinked, + std::vector>& code_table) { bool modified = false; int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); AllowDeferredHandleDereference embedding_raw_address; for (RelocIterator it(*unlinked, mode_mask); !it.done(); it.next()) { - Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); - if (target->kind() == kind && - target->constant_pool_offset() >= kPlaceholderMarker) { - // Patch direct calls to placeholder code objects. - uint32_t index = target->constant_pool_offset() - kPlaceholderMarker; - CHECK(index < static_cast(code_targets->length())); - Handle new_target = - code_targets->GetValueChecked(isolate, index); - if (target != *new_target) { - it.rinfo()->set_target_address(new_target->instruction_start(), - UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH); - modified = true; + RelocInfo::Mode mode = it.rinfo()->rmode(); + if (RelocInfo::IsCodeTarget(mode)) { + Code* target = + Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); + if (target->constant_pool_offset() < kPlaceholderMarker) continue; + switch (target->kind()) { + case Code::WASM_FUNCTION: // fall through + case Code::WASM_TO_JS_FUNCTION: // fall through + case Code::JS_TO_WASM_FUNCTION: { + // Patch direct calls to placeholder code objects. + uint32_t index = target->constant_pool_offset() - kPlaceholderMarker; + Handle new_target = code_table[index]; + if (target != *new_target) { + it.rinfo()->set_target_address(new_target->instruction_start(), + UPDATE_WRITE_BARRIER, + SKIP_ICACHE_FLUSH); + modified = true; + } + break; + } + default: + break; } } } return modified; } -void LinkModuleFunctions(Isolate* isolate, Handle functions) { - for (int i = 0; i < functions->length(); ++i) { - Handle code = functions->GetValueChecked(isolate, i); - LinkFunction(isolate, code, functions); - } -} - -void FlushAssemblyCache(Isolate* isolate, Handle functions) { +void FlushICache(Isolate* isolate, Handle functions) { for (int i = 0; i < functions->length(); ++i) { Handle code = functions->GetValueChecked(isolate, i); Assembler::FlushICache(isolate, code->instruction_start(), code->instruction_size()); } } - -void SetRuntimeSupport(Isolate* isolate, Handle js_object) { - Handle functions = Handle( - FixedArray::cast(js_object->GetInternalField(kWasmModuleCodeTable))); - Handle weak_link = isolate->factory()->NewWeakCell(js_object); - - for (int i = FLAG_skip_compiling_wasm_funcs; i < functions->length(); ++i) { - Handle code = functions->GetValueChecked(isolate, i); - Handle deopt_data = - isolate->factory()->NewFixedArray(2, TENURED); - deopt_data->set(0, *weak_link); - deopt_data->set(1, Smi::FromInt(static_cast(i))); - deopt_data->set_length(2); - code->set_deoptimization_data(*deopt_data); - } -} - } // namespace +uint32_t GetNumImportedFunctions(Handle wasm_object) { + return static_cast( + Smi::cast(wasm_object->GetInternalField(kWasmNumImportedFunctions)) + ->value()); +} + WasmModule::WasmModule(byte* module_start) : module_start(module_start), module_end(nullptr), min_mem_pages(0), max_mem_pages(0), mem_export(false), - mem_external(false), start_function_index(-1), origin(kWasmOrigin), globals_size(0), + num_imported_functions(0), + num_declared_functions(0), + num_exported_functions(0), pending_tasks(new base::Semaphore(0)) {} static MaybeHandle ReportFFIError( @@ -560,7 +520,7 @@ bool FetchAndExecuteCompilationUnit( DisallowHandleDereference no_deref; DisallowCodeDependencyChange no_dependency_change; - // - 1 because AtomicIntrement returns the value after the atomic increment. + // - 1 because AtomicIncrement returns the value after the atomic increment. size_t index = next_unit->Increment(1) - 1; if (index >= compilation_units->size()) { return false; @@ -569,10 +529,8 @@ bool FetchAndExecuteCompilationUnit( compiler::WasmCompilationUnit* unit = compilation_units->at(index); if (unit != nullptr) { unit->ExecuteCompilation(); - { - base::LockGuard guard(result_mutex); - executed_units->push(unit); - } + base::LockGuard guard(result_mutex); + executed_units->push(unit); } return true; } @@ -615,11 +573,6 @@ static void RecordStats(Isolate* isolate, Code* code) { code->relocation_info()->length()); } -static void RecordStats(Isolate* isolate, - const std::vector>& functions) { - for (Handle c : functions) RecordStats(isolate, *c); -} - static void RecordStats(Isolate* isolate, Handle functions) { DisallowHeapAllocation no_gc; for (int i = 0; i < functions->length(); ++i) { @@ -638,16 +591,16 @@ Address GetGlobalStartAddressFromCodeTemplate(Object* undefined, return old_address; } -Handle GetImportsMetadata(Factory* factory, - const WasmModule* module) { +Handle GetImportsData(Factory* factory, const WasmModule* module) { Handle ret = factory->NewFixedArray( static_cast(module->import_table.size()), TENURED); for (size_t i = 0; i < module->import_table.size(); ++i) { const WasmImport& import = module->import_table[i]; + if (import.kind != kExternalFunction) continue; WasmName module_name = module->GetNameOrNull(import.module_name_offset, import.module_name_length); - WasmName function_name = module->GetNameOrNull(import.function_name_offset, - import.function_name_length); + WasmName function_name = module->GetNameOrNull(import.field_name_offset, + import.field_name_length); Handle module_name_string = factory->InternalizeUtf8String(module_name); @@ -655,107 +608,97 @@ Handle GetImportsMetadata(Factory* factory, function_name.is_empty() ? Handle::null() : factory->InternalizeUtf8String(function_name); - Handle sig = - factory->NewByteArray(static_cast(import.sig->parameter_count() + - import.sig->return_count()), - TENURED); - sig->copy_in(0, reinterpret_cast(import.sig->raw_data()), + FunctionSig* fsig = module->functions[import.index].sig; + Handle sig = factory->NewByteArray( + static_cast(fsig->parameter_count() + fsig->return_count()), + TENURED); + sig->copy_in(0, reinterpret_cast(fsig->raw_data()), sig->length()); Handle encoded_import = - factory->NewFixedArray(kWasmImportDataTableSize, TENURED); + factory->NewFixedArray(kWasmImportDataSize, TENURED); encoded_import->set(kModuleName, *module_name_string); if (!function_name_string.is_null()) { encoded_import->set(kFunctionName, *function_name_string); } - encoded_import->set( - kOutputCount, - Smi::FromInt(static_cast(import.sig->return_count()))); + encoded_import->set(kOutputCount, + Smi::FromInt(static_cast(fsig->return_count()))); encoded_import->set(kSignature, *sig); ret->set(static_cast(i), *encoded_import); } return ret; } -bool CompileWrappersToImportedFunctions(Isolate* isolate, - const Handle ffi, - std::vector>& imports, - Handle import_data, - ErrorThrower* thrower) { - uint32_t import_count = static_cast(import_data->length()); - if (import_count > 0) { - imports.reserve(import_count); - for (uint32_t index = 0; index < import_count; ++index) { - Handle data = - import_data->GetValueChecked(isolate, index); - Handle module_name = - data->GetValueChecked(isolate, kModuleName); - MaybeHandle function_name = - data->GetValue(isolate, kFunctionName); +Handle CompileImportWrapper(Isolate* isolate, + const Handle ffi, int index, + Handle import_data, + ErrorThrower* thrower) { + Handle data = + import_data->GetValueChecked(isolate, index); + Handle module_name = + data->GetValueChecked(isolate, kModuleName); + MaybeHandle function_name = + data->GetValue(isolate, kFunctionName); - // TODO(mtrofin): this is an uint32_t, actually. We should rationalize - // it when we rationalize signed/unsigned stuff. - int ret_count = Smi::cast(data->get(kOutputCount))->value(); - CHECK(ret_count >= 0); - Handle sig_data = - data->GetValueChecked(isolate, kSignature); - int sig_data_size = sig_data->length(); - int param_count = sig_data_size - ret_count; - CHECK(param_count >= 0); + // TODO(mtrofin): this is an uint32_t, actually. We should rationalize + // it when we rationalize signed/unsigned stuff. + int ret_count = Smi::cast(data->get(kOutputCount))->value(); + CHECK_GE(ret_count, 0); + Handle sig_data = + data->GetValueChecked(isolate, kSignature); + int sig_data_size = sig_data->length(); + int param_count = sig_data_size - ret_count; + CHECK(param_count >= 0); - MaybeHandle function = LookupFunction( - thrower, isolate->factory(), ffi, index, module_name, function_name); - if (function.is_null()) return false; - Handle code; - Handle target = function.ToHandleChecked(); - bool isMatch = false; - Handle export_wrapper_code; - if (target->IsJSFunction()) { - Handle func = Handle::cast(target); - export_wrapper_code = handle(func->code()); - if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) { - int exported_param_count = - Smi::cast(func->GetInternalField(kInternalArity))->value(); - Handle exportedSig = Handle( - ByteArray::cast(func->GetInternalField(kInternalSignature))); - if (exported_param_count == param_count && - exportedSig->length() == sig_data->length() && - memcmp(exportedSig->data(), sig_data->data(), - exportedSig->length()) == 0) { - isMatch = true; - } - } + MaybeHandle function = LookupFunction( + thrower, isolate->factory(), ffi, index, module_name, function_name); + if (function.is_null()) return Handle::null(); + Handle code; + Handle target = function.ToHandleChecked(); + bool isMatch = false; + Handle export_wrapper_code; + if (target->IsJSFunction()) { + Handle func = Handle::cast(target); + export_wrapper_code = handle(func->code()); + if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) { + int exported_param_count = + Smi::cast(func->GetInternalField(kInternalArity))->value(); + Handle exportedSig = Handle( + ByteArray::cast(func->GetInternalField(kInternalSignature))); + if (exported_param_count == param_count && + exportedSig->length() == sig_data->length() && + memcmp(exportedSig->data(), sig_data->data(), + exportedSig->length()) == 0) { + isMatch = true; } - if (isMatch) { - int wasm_count = 0; - int const mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); - for (RelocIterator it(*export_wrapper_code, mask); !it.done(); - it.next()) { - RelocInfo* rinfo = it.rinfo(); - Address target_address = rinfo->target_address(); - Code* target = Code::GetCodeFromTargetAddress(target_address); - if (target->kind() == Code::WASM_FUNCTION) { - ++wasm_count; - code = handle(target); - } - } - DCHECK(wasm_count == 1); - } else { - // Copy the signature to avoid a raw pointer into a heap object when - // GC can happen. - Zone zone(isolate->allocator()); - MachineRepresentation* reps = - zone.NewArray(sig_data_size); - memcpy(reps, sig_data->data(), - sizeof(MachineRepresentation) * sig_data_size); - FunctionSig sig(ret_count, param_count, reps); - - code = compiler::CompileWasmToJSWrapper(isolate, target, &sig, index, - module_name, function_name); - } - imports.push_back(code); } } - return true; + if (isMatch) { + int wasm_count = 0; + int const mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); + for (RelocIterator it(*export_wrapper_code, mask); !it.done(); it.next()) { + RelocInfo* rinfo = it.rinfo(); + Address target_address = rinfo->target_address(); + Code* target = Code::GetCodeFromTargetAddress(target_address); + if (target->kind() == Code::WASM_FUNCTION) { + ++wasm_count; + code = handle(target); + } + } + DCHECK(wasm_count == 1); + return code; + } else { + // Copy the signature to avoid a raw pointer into a heap object when + // GC can happen. + Zone zone(isolate->allocator()); + MachineRepresentation* reps = + zone.NewArray(sig_data_size); + memcpy(reps, sig_data->data(), + sizeof(MachineRepresentation) * sig_data_size); + FunctionSig sig(ret_count, param_count, reps); + + return compiler::CompileWasmToJSWrapper(isolate, target, &sig, index, + module_name, function_name); + } } void InitializeParallelCompilation( @@ -763,8 +706,10 @@ void InitializeParallelCompilation( std::vector& compilation_units, ModuleEnv& module_env, ErrorThrower* thrower) { for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) { - compilation_units[i] = new compiler::WasmCompilationUnit( - thrower, isolate, &module_env, &functions[i], i); + const WasmFunction* func = &functions[i]; + compilation_units[i] = + func->imported ? nullptr : new compiler::WasmCompilationUnit( + thrower, isolate, &module_env, func, i); } } @@ -894,8 +839,8 @@ void CompileSequentially(Isolate* isolate, const WasmModule* module, for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < module->functions.size(); ++i) { const WasmFunction& func = module->functions[i]; + if (func.imported) continue; // Imports are compiled at instantiation time. - DCHECK_EQ(i, func.func_index); WasmName str = module->GetName(func.name_offset, func.name_length); Handle code = Handle::null(); // Compile the function. @@ -911,91 +856,8 @@ void CompileSequentially(Isolate* isolate, const WasmModule* module, } } -void SetDebugSupport(Factory* factory, Handle compiled_module, - Handle js_object) { - Isolate* isolate = compiled_module->GetIsolate(); - MaybeHandle module_bytes_string = - compiled_module->GetValue(isolate, kModuleBytes); - if (!module_bytes_string.is_null()) { - js_object->SetInternalField(kWasmModuleBytesString, - *module_bytes_string.ToHandleChecked()); - } - - MaybeHandle function_name_table = - compiled_module->GetValue(isolate, kFunctionNameTable); - if (!function_name_table.is_null()) { - js_object->SetInternalField(kWasmFunctionNamesArray, - *function_name_table.ToHandleChecked()); - } -} - -bool SetupGlobals(Isolate* isolate, MaybeHandle template_owner, - Handle compiled_module, Handle instance, - ErrorThrower* thrower) { - uint32_t globals_size = static_cast( - Smi::cast(compiled_module->get(kGlobalsSize))->value()); - if (globals_size > 0) { - Handle globals_buffer = - NewArrayBuffer(isolate, globals_size); - if (globals_buffer.is_null()) { - thrower->Error("Out of memory: wasm globals"); - return false; - } - Address old_address = - template_owner.is_null() - ? nullptr - : GetGlobalStartAddressFromCodeTemplate( - *isolate->factory()->undefined_value(), - JSObject::cast(*template_owner.ToHandleChecked())); - RelocateGlobals(instance, old_address, - static_cast
(globals_buffer->backing_store())); - instance->SetInternalField(kWasmGlobalsArrayBuffer, *globals_buffer); - } - return true; -} - -bool SetupInstanceHeap(Isolate* isolate, Handle compiled_module, - Handle instance, Handle memory, - ErrorThrower* thrower) { - uint32_t min_mem_pages = static_cast( - Smi::cast(compiled_module->get(kMinRequiredMemory))->value()); - isolate->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); - // TODO(wasm): re-enable counter for max_mem_pages when we use that field. - - if (memory.is_null() && min_mem_pages > 0) { - memory = AllocateMemory(thrower, isolate, min_mem_pages); - if (memory.is_null()) { - return false; - } - } - - if (!memory.is_null()) { - instance->SetInternalField(kWasmMemArrayBuffer, *memory); - Address mem_start = static_cast
(memory->backing_store()); - uint32_t mem_size = static_cast(memory->byte_length()->Number()); - uint32_t old_mem_size = static_cast( - compiled_module->GetValueChecked(isolate, kMemSize) - ->value()); - MaybeHandle old_mem = - compiled_module->GetValue(isolate, kMemStart); - Address old_mem_start = - old_mem.is_null() - ? nullptr - : static_cast
(old_mem.ToHandleChecked()->backing_store()); - RelocateInstanceCode(instance, old_mem_start, mem_start, old_mem_size, - mem_size); - LoadDataSegments(compiled_module, mem_start, mem_size); - compiled_module->GetValueChecked(isolate, kMemSize) - ->set_value(static_cast(mem_size)); - compiled_module->set(kMemStart, *memory); - } - return true; -} - -void FixupFunctionsAndImports(Handle old_functions, - Handle new_functions, - MaybeHandle maybe_old_imports, - MaybeHandle maybe_new_imports) { +void PatchDirectCalls(Handle old_functions, + Handle new_functions, int start) { DCHECK_EQ(new_functions->length(), old_functions->length()); DisallowHeapAllocation no_gc; @@ -1004,19 +866,9 @@ void FixupFunctionsAndImports(Handle old_functions, old_to_new_code.insert(std::make_pair(Code::cast(old_functions->get(i)), Code::cast(new_functions->get(i)))); } - DCHECK_EQ(maybe_old_imports.is_null(), maybe_new_imports.is_null()); - if (!maybe_old_imports.is_null()) { - Handle old_imports = maybe_old_imports.ToHandleChecked(); - Handle new_imports = maybe_new_imports.ToHandleChecked(); - DCHECK_EQ(new_imports->length(), old_imports->length()); - for (int i = 0; i < new_imports->length(); ++i) { - old_to_new_code.insert(std::make_pair(Code::cast(old_imports->get(i)), - Code::cast(new_imports->get(i)))); - } - } int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); AllowDeferredHandleDereference embedding_raw_address; - for (int i = 0; i < new_functions->length(); ++i) { + for (int i = start; i < new_functions->length(); ++i) { Code* wasm_function = Code::cast(new_functions->get(i)); for (RelocIterator it(wasm_function, mode_mask); !it.done(); it.next()) { Code* old_code = @@ -1026,107 +878,14 @@ void FixupFunctionsAndImports(Handle old_functions, auto found = old_to_new_code.find(old_code); DCHECK(found != old_to_new_code.end()); Code* new_code = found->second; - // Avoid redundant updates, expected for wasm functions, if we're at the - // first instance. - if (new_code == old_code) { - DCHECK(new_code->kind() == Code::WASM_FUNCTION); - continue; - } - it.rinfo()->set_target_address(new_code->instruction_start(), - UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH); - } - } - } -} - -bool SetupImports(Isolate* isolate, Handle compiled_module, - Handle instance, ErrorThrower* thrower, - Handle ffi) { - //------------------------------------------------------------------------- - // Compile wrappers to imported functions. - //------------------------------------------------------------------------- - std::vector> import_code; - MaybeHandle maybe_import_data = - compiled_module->GetValue(isolate, kImportData); - Handle import_data; - if (maybe_import_data.ToHandle(&import_data)) { - if (!CompileWrappersToImportedFunctions(isolate, ffi, import_code, - import_data, thrower)) { - return false; - } - } - - RecordStats(isolate, import_code); - if (import_code.empty()) return true; - - Handle new_imports = - isolate->factory()->NewFixedArray(static_cast(import_code.size())); - for (int i = 0; i < new_imports->length(); ++i) { - new_imports->set(i, *import_code[i]); - } - compiled_module->set(kImportMap, *new_imports); - return true; -} - -bool SetupExportsObject(Handle compiled_module, Isolate* isolate, - Handle instance, ErrorThrower* thrower) { - Factory* factory = isolate->factory(); - bool mem_export = - static_cast(Smi::cast(compiled_module->get(kExportMem))->value()); - ModuleOrigin origin = static_cast( - Smi::cast(compiled_module->get(kOrigin))->value()); - - MaybeHandle maybe_exports = - compiled_module->GetValue(isolate, kExports); - if (!maybe_exports.is_null() || mem_export) { - PropertyDescriptor desc; - desc.set_writable(false); - - Handle exports_object = instance; - if (origin == kWasmOrigin) { - // Create the "exports" object. - Handle object_function = Handle( - isolate->native_context()->object_function(), isolate); - exports_object = factory->NewJSObject(object_function, TENURED); - Handle exports_name = factory->InternalizeUtf8String("exports"); - JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY); - } - Handle exports; - if (maybe_exports.ToHandle(&exports)) { - int exports_size = exports->length(); - for (int i = 0; i < exports_size; ++i) { - if (thrower->error()) return false; - Handle export_metadata = - exports->GetValueChecked(isolate, i); - Handle export_code = - export_metadata->GetValueChecked(isolate, kExportCode); - RecordStats(isolate, *export_code); - Handle name = - export_metadata->GetValueChecked(isolate, kExportName); - int arity = Smi::cast(export_metadata->get(kExportArity))->value(); - MaybeHandle signature = - export_metadata->GetValue(isolate, kExportedSignature); - Handle function = WrapExportCodeAsJSFunction( - isolate, export_code, name, arity, signature, instance); - desc.set_value(function); - Maybe status = JSReceiver::DefineOwnProperty( - isolate, exports_object, name, &desc, Object::THROW_ON_ERROR); - if (!status.IsJust()) { - thrower->Error("export of %.*s failed.", name->length(), - name->ToCString().get()); - return false; + if (new_code != old_code) { + it.rinfo()->set_target_address(new_code->instruction_start(), + UPDATE_WRITE_BARRIER, + SKIP_ICACHE_FLUSH); } } } - if (mem_export) { - // Export the memory as a named property. - Handle name = factory->InternalizeUtf8String("memory"); - Handle memory = Handle( - JSArrayBuffer::cast(instance->GetInternalField(kWasmMemArrayBuffer))); - JSObject::AddProperty(exports_object, name, memory, READ_ONLY); - } } - return true; } #define GET_COMPILED_MODULE_WEAK_RELATION_OR_NULL(Field) \ @@ -1166,7 +925,7 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner, RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_SIZE_REFERENCE) | RelocInfo::ModeMask(RelocInfo::WASM_GLOBAL_REFERENCE); - Object* fct_obj = compiled_module->get(kFunctions); + Object* fct_obj = compiled_module->get(kCodeTable); if (fct_obj != nullptr && fct_obj != undefined && (old_mem_size > 0 || globals_start != nullptr)) { FixedArray* functions = FixedArray::cast(fct_obj); @@ -1269,7 +1028,7 @@ MaybeHandle WasmModule::CompileFunctions( temp_instance_for_compilation.function_tables[i] = values; Handle metadata = isolate->factory()->NewFixedArray( - kWasmIndirectFunctionTableMetadataSize, TENURED); + kWasmIndirectFunctionTableDataSize, TENURED); metadata->set(kSize, Smi::FromInt(function_tables[i].size)); metadata->set(kTable, *values); indirect_table.ToHandleChecked()->set(i, *metadata); @@ -1282,27 +1041,26 @@ MaybeHandle WasmModule::CompileFunctions( module_env.module = this; module_env.instance = &temp_instance_for_compilation; module_env.origin = origin; - InitializePlaceholders(factory, &module_env.placeholders, functions.size()); - Handle compiled_functions = - factory->NewFixedArray(static_cast(functions.size()), TENURED); + // The {code_table} array contains import wrappers and functions (which + // are both included in {functions.size()}, and export wrappers. + int code_table_size = + static_cast(functions.size() + num_exported_functions); + Handle code_table = + factory->NewFixedArray(static_cast(code_table_size), TENURED); - MaybeHandle maybe_imports; - if (import_table.size() > 0) { - temp_instance_for_compilation.import_code.resize(import_table.size()); - Handle imports = - factory->NewFixedArray(static_cast(import_table.size())); - for (uint32_t i = 0; i < import_table.size(); ++i) { - Handle placeholder = - CreatePlaceholder(factory, i, Code::WASM_TO_JS_FUNCTION); - temp_instance_for_compilation.import_code[i] = placeholder; - imports->set(i, *placeholder); - } - maybe_imports = imports; + // Initialize the code table with placeholders. + for (uint32_t i = 0; i < functions.size(); i++) { + Code::Kind kind = Code::WASM_FUNCTION; + if (i < num_imported_functions) kind = Code::WASM_TO_JS_FUNCTION; + Handle placeholder = CreatePlaceholder(factory, i, kind); + code_table->set(static_cast(i), *placeholder); + temp_instance_for_compilation.function_code[i] = placeholder; } + isolate->counters()->wasm_functions_per_module()->AddSample( static_cast(functions.size())); - if (FLAG_wasm_num_compilation_tasks != 0) { + if (!FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks != 0) { CompileInParallel(isolate, this, temp_instance_for_compilation.function_code, thrower, &module_env); @@ -1317,40 +1075,48 @@ MaybeHandle WasmModule::CompileFunctions( for (size_t i = FLAG_skip_compiling_wasm_funcs; i < temp_instance_for_compilation.function_code.size(); ++i) { Code* code = *temp_instance_for_compilation.function_code[i]; - compiled_functions->set(static_cast(i), code); + code_table->set(static_cast(i), code); } - LinkModuleFunctions(isolate, compiled_functions); - - // TODO(mtrofin): do we need to flush the cache here? - FlushAssemblyCache(isolate, compiled_functions); + // Link the functions in the module. + for (size_t i = FLAG_skip_compiling_wasm_funcs; + i < temp_instance_for_compilation.function_code.size(); ++i) { + Handle code = temp_instance_for_compilation.function_code[i]; + bool modified = + LinkFunction(code, temp_instance_for_compilation.function_code); + if (modified) { + // TODO(mtrofin): do we need to flush the cache here? + Assembler::FlushICache(isolate, code->instruction_start(), + code->instruction_size()); + } + } // Create the compiled module object, and populate with compiled functions // and information needed at instantiation time. This object needs to be // serializable. Instantiation may occur off a deserialized version of this // object. Handle ret = - factory->NewFixedArray(kCompiledWasmObjectTableSize, TENURED); - ret->set(kFunctions, *compiled_functions); + factory->NewFixedArray(kWasmCompiledModuleSize, TENURED); + ret->set(kCodeTable, *code_table); if (!indirect_table.is_null()) { ret->set(kTableOfIndirectFunctionTables, *indirect_table.ToHandleChecked()); } - if (!maybe_imports.is_null()) { - ret->set(kImportMap, *maybe_imports.ToHandleChecked()); - } - Handle import_data = GetImportsMetadata(factory, this); + Handle import_data = GetImportsData(factory, this); ret->set(kImportData, *import_data); - // Compile export functions. - int export_size = static_cast(export_table.size()); - Handle startup_fct; + // Compile exported function wrappers. + int export_size = static_cast(num_exported_functions); if (export_size > 0) { Handle exports = factory->NewFixedArray(export_size, TENURED); - for (int i = 0; i < export_size; ++i) { - Handle export_metadata = - factory->NewFixedArray(kWasmExportMetadataTableSize, TENURED); - const WasmExport& exp = export_table[i]; - FunctionSig* funcSig = functions[exp.func_index].sig; + int index = -1; + + for (const WasmExport& exp : export_table) { + if (exp.kind != kExternalFunction) + continue; // skip non-function exports. + index++; + Handle export_data = + factory->NewFixedArray(kWasmExportDataSize, TENURED); + FunctionSig* funcSig = functions[exp.index].sig; Handle exportedSig = factory->NewByteArray(static_cast(funcSig->parameter_count() + funcSig->return_count()), @@ -1358,45 +1124,34 @@ MaybeHandle WasmModule::CompileFunctions( exportedSig->copy_in(0, reinterpret_cast(funcSig->raw_data()), exportedSig->length()); - export_metadata->set(kExportedSignature, *exportedSig); + export_data->set(kExportedSignature, *exportedSig); WasmName str = GetName(exp.name_offset, exp.name_length); Handle name = factory->InternalizeUtf8String(str); - Handle code = - temp_instance_for_compilation.function_code[exp.func_index]; + Handle code = code_table->GetValueChecked(isolate, exp.index); Handle export_code = compiler::CompileJSToWasmWrapper( - isolate, &module_env, code, exp.func_index); + isolate, &module_env, code, exp.index); if (thrower->error()) return nothing; - export_metadata->set(kExportCode, *export_code); - export_metadata->set(kExportName, *name); - export_metadata->set( - kExportArity, Smi::FromInt(static_cast( - functions[exp.func_index].sig->parameter_count()))); - export_metadata->set(kExportedFunctionIndex, - Smi::FromInt(static_cast(exp.func_index))); - exports->set(i, *export_metadata); - if (exp.func_index == start_function_index) { - startup_fct = export_code; - } + export_data->set(kExportName, *name); + export_data->set(kExportArity, + Smi::FromInt(static_cast( + functions[exp.index].sig->parameter_count()))); + export_data->set(kExportedFunctionIndex, + Smi::FromInt(static_cast(exp.index))); + exports->set(index, *export_data); + code_table->set(static_cast(functions.size() + index), *export_code); } - ret->set(kExports, *exports); + ret->set(kExportData, *exports); } - // Compile startup function, if we haven't already. + // Record data for startup function. if (start_function_index >= 0) { - uint32_t index = static_cast(start_function_index); HandleScope scope(isolate); - if (startup_fct.is_null()) { - Handle code = temp_instance_for_compilation.function_code[index]; - DCHECK_EQ(0, functions[index].sig->parameter_count()); - startup_fct = - compiler::CompileJSToWasmWrapper(isolate, &module_env, code, index); - } - Handle metadata = - factory->NewFixedArray(kWasmExportMetadataTableSize, TENURED); - metadata->set(kExportCode, *startup_fct); - metadata->set(kExportArity, Smi::FromInt(0)); - metadata->set(kExportedFunctionIndex, Smi::FromInt(start_function_index)); - ret->set(kStartupFunction, *metadata); + Handle startup_data = + factory->NewFixedArray(kWasmExportDataSize, TENURED); + startup_data->set(kExportArity, Smi::FromInt(0)); + startup_data->set(kExportedFunctionIndex, + Smi::FromInt(start_function_index)); + ret->set(kStartupData, *startup_data); } // TODO(wasm): saving the module bytes for debugging is wasteful. We should @@ -1476,173 +1231,211 @@ Handle SetupIndirectFunctionTable( return cloned_indirect_tables; } -Handle CloneModuleForInstance(Isolate* isolate, - Handle module_object, - bool* template_is_owned, - Handle* module_template) { - Factory* factory = isolate->factory(); - - Handle original; - for (int i = 0; i < 2; ++i) { - original = handle(FixedArray::cast(module_object->GetInternalField(0))); - if (GetOwningInstance(*original) == nullptr) { - *template_is_owned = false; - *module_template = original; - return original; - } - if (i < 1) { - isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, - GarbageCollectionReason::kRuntime); - } - } - *template_is_owned = true; - *module_template = original; - - // We insert the latest clone in front. - Handle clone = factory->CopyFixedArray(original); - Handle weak_link_to_wasm_obj = - original->GetValueChecked(isolate, kModuleObject); - - clone->set(kModuleObject, *weak_link_to_wasm_obj); - Handle link_to_original = factory->NewWeakCell(original); - clone->set(kNextInstance, *link_to_original); - Handle link_to_clone = factory->NewWeakCell(clone); - original->set(kPrevInstance, *link_to_clone); - JSObject::cast(weak_link_to_wasm_obj->value())->SetInternalField(0, *clone); - - // Clone each wasm code object. - Handle orig_wasm_functions = - original->GetValueChecked(isolate, kFunctions); - Handle clone_wasm_functions = - factory->CopyFixedArray(orig_wasm_functions); - clone->set(kFunctions, *clone_wasm_functions); - for (int i = 0; i < clone_wasm_functions->length(); ++i) { - Handle orig_code = - clone_wasm_functions->GetValueChecked(isolate, i); - Handle cloned_code = factory->CopyCode(orig_code); - clone_wasm_functions->set(i, *cloned_code); - } - - MaybeHandle maybe_orig_exports = - original->GetValue(isolate, kExports); - Handle orig_exports; - if (maybe_orig_exports.ToHandle(&orig_exports)) { - Handle cloned_exports = factory->CopyFixedArray(orig_exports); - clone->set(kExports, *cloned_exports); - for (int i = 0; i < orig_exports->length(); ++i) { - Handle export_metadata = - orig_exports->GetValueChecked(isolate, i); - Handle clone_metadata = - factory->CopyFixedArray(export_metadata); - cloned_exports->set(i, *clone_metadata); - Handle orig_code = - export_metadata->GetValueChecked(isolate, kExportCode); - Handle cloned_code = factory->CopyCode(orig_code); - clone_metadata->set(kExportCode, *cloned_code); - // TODO(wasm): This is actually a uint32_t, but since FixedArray indexes - // in int, we are taking the risk of invalid values. - int exported_fct_index = - Smi::cast(export_metadata->get(kExportedFunctionIndex))->value(); - CHECK_GE(exported_fct_index, 0); - CHECK_LT(exported_fct_index, clone_wasm_functions->length()); - Handle new_target = clone_wasm_functions->GetValueChecked( - isolate, exported_fct_index); - PatchJSWrapper(isolate, cloned_code, new_target); - } - } - - MaybeHandle maybe_startup = - original->GetValue(isolate, kStartupFunction); - if (!maybe_startup.is_null()) { - Handle startup_metadata = - factory->CopyFixedArray(maybe_startup.ToHandleChecked()); - Handle startup_fct_clone = factory->CopyCode( - startup_metadata->GetValueChecked(isolate, kExportCode)); - startup_metadata->set(kExportCode, *startup_fct_clone); - clone->set(kStartupFunction, *startup_metadata); - // TODO(wasm): see todo above about int vs size_t indexing in FixedArray. - int startup_fct_index = - Smi::cast(startup_metadata->get(kExportedFunctionIndex))->value(); - CHECK_GE(startup_fct_index, 0); - CHECK_LT(startup_fct_index, clone_wasm_functions->length()); - Handle new_target = - clone_wasm_functions->GetValueChecked(isolate, startup_fct_index); - PatchJSWrapper(isolate, startup_fct_clone, new_target); - } - clone->set(kImportMap, *isolate->factory()->undefined_value()); - return clone; -} - -// Instantiates a wasm module as a JSObject. -// * allocates a backing store of {mem_size} bytes. -// * installs a named property "memory" for that buffer if exported -// * installs named properties on the object for exported functions -// * compiles wasm code to machine code +// Instantiates a WASM module, creating a WebAssembly.Instance from a +// WebAssembly.Module. MaybeHandle WasmModule::Instantiate(Isolate* isolate, + ErrorThrower* thrower, Handle module_object, Handle ffi, Handle memory) { + MaybeHandle nothing; HistogramTimerScope wasm_instantiate_module_time_scope( isolate->counters()->wasm_instantiate_module_time()); - ErrorThrower thrower(isolate, "WasmModule::Instantiate()"); Factory* factory = isolate->factory(); - bool template_is_owned = false; - Handle compiled_module_template; - Handle compiled_module = CloneModuleForInstance( - isolate, module_object, &template_is_owned, &compiled_module_template); + //-------------------------------------------------------------------------- + // Reuse the compiled module (if no owner), otherwise clone. + //-------------------------------------------------------------------------- + Handle compiled_module; + Handle code_table; + Handle old_code_table; + Handle owner; + { + Handle original( + FixedArray::cast(module_object->GetInternalField(0)), isolate); + // Always make a new copy of the code_table, since the old_code_table + // may still have placeholders for imports. + old_code_table = original->GetValueChecked(isolate, kCodeTable); + code_table = factory->CopyFixedArray(old_code_table); - MaybeHandle template_owner; - if (template_is_owned) { - Handle weak_owner = - compiled_module_template->GetValueChecked(isolate, - kOwningInstance); - template_owner = handle(JSObject::cast(weak_owner->value())); + WeakCell* tmp = GetOwningInstance(*original); + if (tmp != nullptr) { + // There is already an owner, clone everything. + owner = Handle(JSObject::cast(tmp->value()), isolate); + // Insert the latest clone in front. + compiled_module = factory->CopyFixedArray(original); + Handle weak_link_to_wasm_obj = + original->GetValueChecked(isolate, kModuleObject); + + compiled_module->set(kModuleObject, *weak_link_to_wasm_obj); + Handle link_to_original = factory->NewWeakCell(original); + compiled_module->set(kNextInstance, *link_to_original); + Handle link_to_clone = factory->NewWeakCell(compiled_module); + original->set(kPrevInstance, *link_to_clone); + JSObject::cast(weak_link_to_wasm_obj->value()) + ->SetInternalField(0, *compiled_module); + + // Clone the code for WASM functions and exports. + for (int i = 0; i < code_table->length(); ++i) { + Handle orig_code = code_table->GetValueChecked(isolate, i); + switch (orig_code->kind()) { + case Code::WASM_TO_JS_FUNCTION: + // Imports will be overwritten with newly compiled wrappers. + break; + case Code::JS_TO_WASM_FUNCTION: + case Code::WASM_FUNCTION: { + Handle code = factory->CopyCode(orig_code); + code_table->set(i, *code); + break; + } + default: + UNREACHABLE(); + } + } + RecordStats(isolate, code_table); + } else { + // There was no owner, so we can reuse the original. + compiled_module = original; + } + compiled_module->set(kCodeTable, *code_table); } - // These fields are compulsory. - Handle code_table = - compiled_module->GetValueChecked(isolate, kFunctions); - - RecordStats(isolate, code_table); - - MaybeHandle nothing; + //-------------------------------------------------------------------------- + // Allocate the instance object. + //-------------------------------------------------------------------------- Handle map = factory->NewMap( JS_OBJECT_TYPE, JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize); - Handle js_object = factory->NewJSObjectFromMap(map, TENURED); - js_object->SetInternalField(kWasmModuleCodeTable, *code_table); + Handle instance = factory->NewJSObjectFromMap(map, TENURED); + instance->SetInternalField(kWasmModuleCodeTable, *code_table); - // Remember the old imports, for the case when we are at the first instance - - // they will be replaced with the instance's actual imports in SetupImports. - MaybeHandle old_imports = - compiled_module_template->GetValue(isolate, kImportMap); - if (!(SetupInstanceHeap(isolate, compiled_module, js_object, memory, - &thrower) && - SetupGlobals(isolate, template_owner, compiled_module, js_object, - &thrower) && - SetupImports(isolate, compiled_module, js_object, &thrower, ffi) && - SetupExportsObject(compiled_module, isolate, js_object, &thrower))) { - return nothing; + //-------------------------------------------------------------------------- + // Set up the memory for the new instance. + //-------------------------------------------------------------------------- + MaybeHandle old_memory; + // TODO(titzer): handle imported memory properly. + + uint32_t min_mem_pages = static_cast( + Smi::cast(compiled_module->get(kMinRequiredMemory))->value()); + isolate->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); + // TODO(wasm): re-enable counter for max_mem_pages when we use that field. + + if (memory.is_null() && min_mem_pages > 0) { + memory = AllocateMemory(thrower, isolate, min_mem_pages); + if (memory.is_null()) return nothing; // failed to allocate memory } - FixupFunctionsAndImports( - compiled_module_template->GetValueChecked(isolate, - kFunctions), - code_table, old_imports, - compiled_module->GetValue(isolate, kImportMap)); + if (!memory.is_null()) { + instance->SetInternalField(kWasmMemArrayBuffer, *memory); + Address mem_start = static_cast
(memory->backing_store()); + uint32_t mem_size = static_cast(memory->byte_length()->Number()); + LoadDataSegments(compiled_module, mem_start, mem_size); - SetDebugSupport(factory, compiled_module, js_object); - SetRuntimeSupport(isolate, js_object); + uint32_t old_mem_size = static_cast( + compiled_module->GetValueChecked(isolate, kMemSize) + ->value()); + MaybeHandle old_mem = + compiled_module->GetValue(isolate, kMemStart); + Address old_mem_start = + old_mem.is_null() + ? nullptr + : static_cast
(old_mem.ToHandleChecked()->backing_store()); + RelocateInstanceCode(instance, old_mem_start, mem_start, old_mem_size, + mem_size); + compiled_module->GetValueChecked(isolate, kMemSize) + ->set_value(static_cast(mem_size)); + compiled_module->set(kMemStart, *memory); + } - FlushAssemblyCache(isolate, code_table); + //-------------------------------------------------------------------------- + // Set up the globals for the new instance. + //-------------------------------------------------------------------------- + MaybeHandle old_globals; + MaybeHandle globals; + uint32_t globals_size = static_cast( + Smi::cast(compiled_module->get(kGlobalsSize))->value()); + if (globals_size > 0) { + Handle global_buffer = NewArrayBuffer(isolate, globals_size); + globals = global_buffer; + if (globals.is_null()) { + thrower->Error("Out of memory: wasm globals"); + return nothing; + } + Address old_address = + owner.is_null() ? nullptr : GetGlobalStartAddressFromCodeTemplate( + *isolate->factory()->undefined_value(), + JSObject::cast(*owner)); + RelocateGlobals(instance, old_address, + static_cast
(global_buffer->backing_store())); + instance->SetInternalField(kWasmGlobalsArrayBuffer, *global_buffer); + } + //-------------------------------------------------------------------------- + // Compile the import wrappers for the new instance. + //-------------------------------------------------------------------------- + // TODO(titzer): handle imported globals and function tables. + Handle import_data; + int num_imported_functions = 0; + if (compiled_module->GetValue(isolate, kImportData) + .ToHandle(&import_data)) { + num_imported_functions = import_data->length(); + for (int index = 0; index < num_imported_functions; index++) { + Handle import_wrapper = + CompileImportWrapper(isolate, ffi, index, import_data, thrower); + if (thrower->error()) return nothing; + code_table->set(index, *import_wrapper); + RecordStats(isolate, *import_wrapper); + } + } + + //-------------------------------------------------------------------------- + // Set up the debug support for the new instance. + //-------------------------------------------------------------------------- + MaybeHandle module_bytes_string = + compiled_module->GetValue(isolate, kModuleBytes); + if (!module_bytes_string.is_null()) { + instance->SetInternalField(kWasmModuleBytesString, + *module_bytes_string.ToHandleChecked()); + } + + MaybeHandle function_name_table = + compiled_module->GetValue(isolate, kFunctionNameTable); + if (!function_name_table.is_null()) { + Handle handle = function_name_table.ToHandleChecked(); + instance->SetInternalField(kWasmFunctionNamesArray, *handle); + } + + { + Handle handle = factory->NewNumber(num_imported_functions); + instance->SetInternalField(kWasmNumImportedFunctions, *handle); + } + + //-------------------------------------------------------------------------- + // Set up the runtime support for the new instance. + //-------------------------------------------------------------------------- + Handle weak_link = isolate->factory()->NewWeakCell(instance); + + for (int i = num_imported_functions + FLAG_skip_compiling_wasm_funcs; + i < code_table->length(); ++i) { + Handle code = code_table->GetValueChecked(isolate, i); + if (code->kind() == Code::WASM_FUNCTION) { + Handle deopt_data = + isolate->factory()->NewFixedArray(2, TENURED); + deopt_data->set(0, *weak_link); + deopt_data->set(1, Smi::FromInt(static_cast(i))); + deopt_data->set_length(2); + code->set_deoptimization_data(*deopt_data); + } + } + + //-------------------------------------------------------------------------- + // Set up the indirect function tables for the new instance. + //-------------------------------------------------------------------------- { std::vector> functions( static_cast(code_table->length())); for (int i = 0; i < code_table->length(); ++i) { - functions[static_cast(i)] = - code_table->GetValueChecked(isolate, i); + functions[i] = code_table->GetValueChecked(isolate, i); } MaybeHandle maybe_indirect_tables = @@ -1651,11 +1444,9 @@ MaybeHandle WasmModule::Instantiate(Isolate* isolate, Handle indirect_tables_template; if (maybe_indirect_tables.ToHandle(&indirect_tables_template)) { Handle to_replace = - template_owner.is_null() - ? indirect_tables_template - : handle(FixedArray::cast( - template_owner.ToHandleChecked()->GetInternalField( - kWasmModuleFunctionTable))); + owner.is_null() ? indirect_tables_template + : handle(FixedArray::cast(owner->GetInternalField( + kWasmModuleFunctionTable))); Handle indirect_tables = SetupIndirectFunctionTable( isolate, code_table, indirect_tables_template, to_replace); for (int i = 0; i < indirect_tables->length(); ++i) { @@ -1666,24 +1457,104 @@ MaybeHandle WasmModule::Instantiate(Isolate* isolate, metadata->GetValueChecked(isolate, kTable); wasm::PopulateFunctionTable(table, size, &functions); } - js_object->SetInternalField(kWasmModuleFunctionTable, *indirect_tables); + instance->SetInternalField(kWasmModuleFunctionTable, *indirect_tables); } } + //-------------------------------------------------------------------------- + // Set up the exports object for the new instance. + //-------------------------------------------------------------------------- + bool mem_export = + static_cast(Smi::cast(compiled_module->get(kExportMem))->value()); + ModuleOrigin origin = static_cast( + Smi::cast(compiled_module->get(kOrigin))->value()); + + MaybeHandle maybe_exports = + compiled_module->GetValue(isolate, kExportData); + if (!maybe_exports.is_null() || mem_export) { + PropertyDescriptor desc; + desc.set_writable(false); + + Handle exports_object = instance; + if (origin == kWasmOrigin) { + // Create the "exports" object. + Handle object_function = Handle( + isolate->native_context()->object_function(), isolate); + exports_object = factory->NewJSObject(object_function, TENURED); + Handle exports_name = factory->InternalizeUtf8String("exports"); + JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY); + } + Handle exports; + int first_export = -1; + // TODO(wasm): another iteration over the code objects. + for (int i = 0; i < code_table->length(); i++) { + Handle code = code_table->GetValueChecked(isolate, i); + if (code->kind() == Code::JS_TO_WASM_FUNCTION) { + first_export = i; + break; + } + } + if (maybe_exports.ToHandle(&exports)) { + int export_size = exports->length(); + for (int i = 0; i < export_size; ++i) { + Handle export_data = + exports->GetValueChecked(isolate, i); + Handle name = + export_data->GetValueChecked(isolate, kExportName); + int arity = Smi::cast(export_data->get(kExportArity))->value(); + MaybeHandle signature = + export_data->GetValue(isolate, kExportedSignature); + Handle export_code = + code_table->GetValueChecked(isolate, first_export + i); + Handle function = WrapExportCodeAsJSFunction( + isolate, export_code, name, arity, signature, instance); + desc.set_value(function); + Maybe status = JSReceiver::DefineOwnProperty( + isolate, exports_object, name, &desc, Object::THROW_ON_ERROR); + if (!status.IsJust()) { + thrower->Error("export of %.*s failed.", name->length(), + name->ToCString().get()); + return nothing; + } + } + } + if (mem_export) { + // Export the memory as a named property. + Handle buffer = Handle( + JSArrayBuffer::cast(instance->GetInternalField(kWasmMemArrayBuffer))); + Handle memory_object = + WasmJs::CreateWasmMemoryObject(isolate, buffer, false, 0); + // TODO(titzer): export the memory with the correct name. + Handle name = factory->InternalizeUtf8String("memory"); + JSObject::AddProperty(exports_object, name, memory_object, READ_ONLY); + } + } + + if (num_imported_functions > 0 || !owner.is_null()) { + // If the code was cloned, or new imports were compiled, patch. + PatchDirectCalls(old_code_table, code_table, num_imported_functions); + } + + FlushICache(isolate, code_table); + + //-------------------------------------------------------------------------- // Run the start function if one was specified. - MaybeHandle maybe_startup_fct = - compiled_module->GetValue(isolate, kStartupFunction); - Handle metadata; - if (maybe_startup_fct.ToHandle(&metadata)) { + //-------------------------------------------------------------------------- + Handle startup_data; + if (compiled_module->GetValue(isolate, kStartupData) + .ToHandle(&startup_data)) { HandleScope scope(isolate); + int32_t start_index = + startup_data->GetValueChecked(isolate, kExportedFunctionIndex) + ->value(); Handle startup_code = - metadata->GetValueChecked(isolate, kExportCode); - int arity = Smi::cast(metadata->get(kExportArity))->value(); + code_table->GetValueChecked(isolate, start_index); + int arity = Smi::cast(startup_data->get(kExportArity))->value(); MaybeHandle startup_signature = - metadata->GetValue(isolate, kExportedSignature); + startup_data->GetValue(isolate, kExportedSignature); Handle startup_fct = WrapExportCodeAsJSFunction( isolate, startup_code, factory->InternalizeUtf8String("start"), arity, - startup_signature, js_object); + startup_signature, instance); RecordStats(isolate, *startup_code); // Call the JS function. Handle undefined = isolate->factory()->undefined_value(); @@ -1691,39 +1562,25 @@ MaybeHandle WasmModule::Instantiate(Isolate* isolate, Execution::Call(isolate, startup_fct, undefined, 0, nullptr); if (retval.is_null()) { - thrower.Error("WASM.instantiateModule(): start function failed"); + thrower->Error("WASM.instantiateModule(): start function failed"); return nothing; } } - DCHECK(wasm::IsWasmObject(*js_object)); + DCHECK(wasm::IsWasmObject(*instance)); if (!compiled_module->GetValue(isolate, kModuleObject).is_null()) { - js_object->SetInternalField(kWasmCompiledModule, *compiled_module); - Handle link_to_owner = factory->NewWeakCell(js_object); + instance->SetInternalField(kWasmCompiledModule, *compiled_module); + Handle link_to_owner = factory->NewWeakCell(instance); compiled_module->set(kOwningInstance, *link_to_owner); - Handle global_handle = - isolate->global_handles()->Create(*js_object); + Handle global_handle = isolate->global_handles()->Create(*instance); GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(), &InstanceFinalizer, v8::WeakCallbackType::kFinalizer); } - return js_object; -} - -// TODO(mtrofin): remove this once we move to WASM_DIRECT_CALL -Handle ModuleEnv::GetCodeOrPlaceholder(uint32_t index) const { - DCHECK(IsValidFunction(index)); - if (!placeholders.empty()) return placeholders[index]; - DCHECK_NOT_NULL(instance); - return instance->function_code[index]; -} - -Handle ModuleEnv::GetImportCode(uint32_t index) { - DCHECK(IsValidImport(index)); - return instance ? instance->import_code[index] : Handle::null(); + return instance; } compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone, diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h index 0b9bc9762e..2ef75ec14f 100644 --- a/src/wasm/wasm-module.h +++ b/src/wasm/wasm-module.h @@ -27,84 +27,73 @@ const size_t kMaxModuleSize = 1024 * 1024 * 1024; const size_t kMaxFunctionSize = 128 * 1024; const size_t kMaxStringSize = 256; const uint32_t kWasmMagic = 0x6d736100; -const uint32_t kWasmVersion = 0x0b; +const uint32_t kWasmVersion = 0x0c; const uint8_t kWasmFunctionTypeForm = 0x40; +const uint8_t kWasmAnyFunctionTypeForm = 0x20; -// WebAssembly sections are named as strings in the binary format, but -// internally V8 uses an enum to handle them. -// -// Entries have the form F(enumerator, string). -#define FOR_EACH_WASM_SECTION_TYPE(F) \ - F(Signatures, 1, "type") \ - F(ImportTable, 2, "import") \ - F(FunctionSignatures, 3, "function") \ - F(FunctionTable, 4, "table") \ - F(Memory, 5, "memory") \ - F(ExportTable, 6, "export") \ - F(StartFunction, 7, "start") \ - F(FunctionBodies, 8, "code") \ - F(DataSegments, 9, "data") \ - F(Names, 10, "name") \ - F(Globals, 0, "global") \ - F(End, 0, "end") +enum WasmSectionCode { + kUnknownSectionCode = 0, // code for unknown sections + kTypeSectionCode = 1, // Function signature declarations + kImportSectionCode = 2, // Import declarations + kFunctionSectionCode = 3, // Function declarations + kTableSectionCode = 4, // Indirect function table and other tables + kMemorySectionCode = 5, // Memory attributes + kGlobalSectionCode = 6, // Global declarations + kExportSectionCode = 7, // Exports + kStartSectionCode = 8, // Start function declaration + kElementSectionCode = 9, // Elements section + kCodeSectionCode = 10, // Function code + kDataSectionCode = 11, // Data segments + kNameSectionCode = 12, // Name section (encoded as a string) +}; -// Contants for the above section types: {LEB128 length, characters...}. -#define WASM_SECTION_MEMORY 6, 'm', 'e', 'm', 'o', 'r', 'y' -#define WASM_SECTION_SIGNATURES 4, 't', 'y', 'p', 'e' -#define WASM_SECTION_GLOBALS 6, 'g', 'l', 'o', 'b', 'a', 'l' -#define WASM_SECTION_DATA_SEGMENTS 4, 'd', 'a', 't', 'a' -#define WASM_SECTION_FUNCTION_TABLE 5, 't', 'a', 'b', 'l', 'e' -#define WASM_SECTION_END 3, 'e', 'n', 'd' -#define WASM_SECTION_START_FUNCTION 5, 's', 't', 'a', 'r', 't' -#define WASM_SECTION_IMPORT_TABLE 6, 'i', 'm', 'p', 'o', 'r', 't' -#define WASM_SECTION_EXPORT_TABLE 6, 'e', 'x', 'p', 'o', 'r', 't' -#define WASM_SECTION_FUNCTION_SIGNATURES \ - 8, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n' -#define WASM_SECTION_FUNCTION_BODIES 4, 'c', 'o', 'd', 'e' -#define WASM_SECTION_NAMES 4, 'n', 'a', 'm', 'e' +inline bool IsValidSectionCode(uint8_t byte) { + return kTypeSectionCode <= byte && byte <= kDataSectionCode; +} -// Constants for the above section headers' size (LEB128 + characters). -#define WASM_SECTION_MEMORY_SIZE ((size_t)7) -#define WASM_SECTION_SIGNATURES_SIZE ((size_t)5) -#define WASM_SECTION_GLOBALS_SIZE ((size_t)7) -#define WASM_SECTION_DATA_SEGMENTS_SIZE ((size_t)5) -#define WASM_SECTION_FUNCTION_TABLE_SIZE ((size_t)6) -#define WASM_SECTION_END_SIZE ((size_t)4) -#define WASM_SECTION_START_FUNCTION_SIZE ((size_t)6) -#define WASM_SECTION_IMPORT_TABLE_SIZE ((size_t)7) -#define WASM_SECTION_EXPORT_TABLE_SIZE ((size_t)7) -#define WASM_SECTION_FUNCTION_SIGNATURES_SIZE ((size_t)9) -#define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)5) -#define WASM_SECTION_NAMES_SIZE ((size_t)5) +const char* SectionName(WasmSectionCode code); + +static const char* kNameString = "name"; +static const size_t kNameStringLength = 4; class WasmDebugInfo; -struct WasmSection { - enum class Code : uint32_t { -#define F(enumerator, order, string) enumerator, - FOR_EACH_WASM_SECTION_TYPE(F) -#undef F - Max - }; - static WasmSection::Code begin(); - static WasmSection::Code end(); - static WasmSection::Code next(WasmSection::Code code); - static const char* getName(Code code); - static int getOrder(Code code); - static size_t getNameLength(Code code); - static WasmSection::Code lookup(const byte* string, uint32_t length); -}; - -enum WasmFunctionDeclBit { - kDeclFunctionName = 0x01, - kDeclFunctionExport = 0x08 -}; - // Constants for fixed-size elements within a module. -static const size_t kDeclMemorySize = 3; -static const size_t kDeclDataSegmentSize = 13; - static const uint32_t kMaxReturnCount = 1; +static const uint8_t kResizableMaximumFlag = 1; +static const int32_t kInvalidFunctionIndex = -1; + +enum WasmExternalKind { + kExternalFunction = 0, + kExternalTable = 1, + kExternalMemory = 2, + kExternalGlobal = 3 +}; + +// Representation of an initializer expression. +struct WasmInitExpr { + enum WasmInitKind { + kNone, + kGlobalIndex, + kI32Const, + kI64Const, + kF32Const, + kF64Const + } kind; + + union { + int32_t i32_const; + int64_t i64_const; + float f32_const; + double f64_const; + uint32_t global_index; + } val; +}; + +#define NO_INIT \ + { \ + WasmInitExpr::kNone, { 0u } \ + } // Static representation of a WASM function. struct WasmFunction { @@ -115,47 +104,59 @@ struct WasmFunction { uint32_t name_length; // length in bytes of the name. uint32_t code_start_offset; // offset in the module bytes of code start. uint32_t code_end_offset; // offset in the module bytes of code end. -}; - -// Static representation of an imported WASM function. -struct WasmImport { - FunctionSig* sig; // signature of the function. - uint32_t sig_index; // index into the signature table. - uint32_t module_name_offset; // offset in module bytes of the module name. - uint32_t module_name_length; // length in bytes of the module name. - uint32_t function_name_offset; // offset in module bytes of the import name. - uint32_t function_name_length; // length in bytes of the import name. -}; - -// Static representation of an exported WASM function. -struct WasmExport { - uint32_t func_index; // index into the function table. - uint32_t name_offset; // offset in module bytes of the name to export. - uint32_t name_length; // length in bytes of the exported name. + bool imported; + bool exported; }; // Static representation of a wasm global variable. struct WasmGlobal { - uint32_t name_offset; // offset in the module bytes of the name, if any. - uint32_t name_length; // length in bytes of the global name. LocalType type; // type of the global. - uint32_t offset; // offset from beginning of globals area. - bool exported; // true if this global is exported. + bool mutability; // {true} if mutable. + WasmInitExpr init; // the initialization expression of the global. + uint32_t offset; // offset into global memory. + bool imported; // true if imported. + bool exported; // true if exported. }; // Static representation of a wasm data segment. struct WasmDataSegment { - uint32_t dest_addr; // destination memory address of the data. + WasmInitExpr dest_addr; // destination memory address of the data. uint32_t source_offset; // start offset in the module bytes. uint32_t source_size; // end offset in the module bytes. - bool init; // true if loaded upon instantiation. }; // Static representation of a wasm indirect call table. struct WasmIndirectFunctionTable { - uint32_t size; // initial table size. - uint32_t max_size; // maximum table size. - std::vector values; // function table. + uint32_t size; // initial table size. + uint32_t max_size; // maximum table size. + std::vector values; // function table, -1 indicating invalid. + bool imported; // true if imported. + bool exported; // true if exported. +}; + +// Static representation of how to initialize a table. +struct WasmTableInit { + uint32_t table_index; + WasmInitExpr offset; + std::vector entries; +}; + +// Static representation of a WASM import. +struct WasmImport { + uint32_t module_name_length; // length in bytes of the module name. + uint32_t module_name_offset; // offset in module bytes of the module name. + uint32_t field_name_length; // length in bytes of the import name. + uint32_t field_name_offset; // offset in module bytes of the import name. + WasmExternalKind kind; // kind of the import. + uint32_t index; // index into the respective space. +}; + +// Static representation of a WASM export. +struct WasmExport { + uint32_t name_length; // length in bytes of the exported name. + uint32_t name_offset; // offset in module bytes of the name to export. + WasmExternalKind kind; // kind of the export. + uint32_t index; // index into the respective space. }; enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin }; @@ -163,6 +164,7 @@ enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin }; // Static representation of a module. struct WasmModule { static const uint32_t kPageSize = 0x10000; // Page size, 64kb. + static const uint32_t kMaxLegalPages = 65536; // Maximum legal pages static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb @@ -171,7 +173,6 @@ struct WasmModule { uint32_t min_mem_pages; // minimum size of the memory in 64k pages. uint32_t max_mem_pages; // maximum size of the memory in 64k pages. bool mem_export; // true if the memory is exported. - bool mem_external; // true if the memory is external. // TODO(wasm): reconcile start function index being an int with // the fact that we index on uint32_t, so we may technically not be // able to represent some start_function_index -es. @@ -180,12 +181,16 @@ struct WasmModule { std::vector globals; // globals in this module. uint32_t globals_size; // size of globals table. + uint32_t num_imported_functions; // number of imported functions. + uint32_t num_declared_functions; // number of declared functions. + uint32_t num_exported_functions; // number of exported functions. std::vector signatures; // signatures in this module. std::vector functions; // functions in this module. std::vector data_segments; // data segments in this module. std::vector function_tables; // function tables. std::vector import_table; // import table. std::vector export_table; // export table. + std::vector table_inits; // initializations of tables // We store the semaphore here to extend its lifetime. In Instantiate(Isolate* isolate, + ErrorThrower* thrower, Handle module_object, Handle ffi, Handle memory); @@ -255,7 +261,6 @@ struct WasmModuleInstance { Handle globals_buffer; // Handle to array buffer of globals. std::vector> function_tables; // indirect function tables. std::vector> function_code; // code objects for each function. - std::vector> import_code; // code objects for each import. // -- raw memory ------------------------------------------------------------ byte* mem_start; // start of linear memory. uint32_t mem_size; // size of the linear memory. @@ -266,7 +271,6 @@ struct WasmModuleInstance { : module(m), function_tables(m->function_tables.size()), function_code(m->functions.size()), - import_code(m->import_table.size()), mem_start(nullptr), mem_size(0), globals_start(nullptr) {} @@ -278,9 +282,6 @@ struct ModuleEnv { const WasmModule* module; WasmModuleInstance* instance; ModuleOrigin origin; - // TODO(mtrofin): remove this once we introduce WASM_DIRECT_CALL - // reloc infos. - std::vector> placeholders; bool IsValidGlobal(uint32_t index) const { return module && index < module->globals.size(); @@ -291,9 +292,6 @@ struct ModuleEnv { bool IsValidSignature(uint32_t index) const { return module && index < module->signatures.size(); } - bool IsValidImport(uint32_t index) const { - return module && index < module->import_table.size(); - } bool IsValidTable(uint32_t index) const { return module && index < module->function_tables.size(); } @@ -305,10 +303,6 @@ struct ModuleEnv { DCHECK(IsValidFunction(index)); return module->functions[index].sig; } - FunctionSig* GetImportSignature(uint32_t index) { - DCHECK(IsValidImport(index)); - return module->import_table[index].sig; - } FunctionSig* GetSignature(uint32_t index) { DCHECK(IsValidSignature(index)); return module->signatures[index]; @@ -320,8 +314,10 @@ struct ModuleEnv { bool asm_js() { return origin == kAsmJsOrigin; } - Handle GetCodeOrPlaceholder(uint32_t index) const; - Handle GetImportCode(uint32_t index); + Handle GetFunctionCode(uint32_t index) { + DCHECK_NOT_NULL(instance); + return instance->function_code[index]; + } static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone, FunctionSig* sig); @@ -408,6 +404,9 @@ MaybeHandle CreateModuleObjectFromBytes(Isolate* isolate, ErrorThrower* thrower, ModuleOrigin origin); +// Get the number of imported functions for a WASM instance. +uint32_t GetNumImportedFunctions(Handle wasm_object); + // Assumed to be called with a code object associated to a wasm module instance. // Intended to be called from runtime functions. // Returns undefined if the runtime support was not setup, nullptr if the diff --git a/src/wasm/wasm-opcodes.h b/src/wasm/wasm-opcodes.h index abd646490c..03827b2035 100644 --- a/src/wasm/wasm-opcodes.h +++ b/src/wasm/wasm-opcodes.h @@ -22,6 +22,9 @@ enum LocalTypeCode { kLocalS128 = 5 }; +// Type code for multi-value block types. +static const uint8_t kMultivalBlock = 0x41; + // We reuse the internal machine type to represent WebAssembly AST types. // A typedef improves readability without adding a whole new type system. typedef MachineRepresentation LocalType; @@ -44,7 +47,7 @@ const WasmCodePosition kNoCodePosition = -1; // Control expressions and blocks. #define FOREACH_CONTROL_OPCODE(V) \ - V(Nop, 0x00, _) \ + V(Unreachable, 0x00, _) \ V(Block, 0x01, _) \ V(Loop, 0x02, _) \ V(If, 0x03, _) \ @@ -54,7 +57,7 @@ const WasmCodePosition kNoCodePosition = -1; V(BrIf, 0x07, _) \ V(BrTable, 0x08, _) \ V(Return, 0x09, _) \ - V(Unreachable, 0x0a, _) \ + V(Nop, 0x0a, _) \ V(Throw, 0xfa, _) \ V(Try, 0xfb, _) \ V(Catch, 0xfe, _) \ @@ -68,9 +71,10 @@ const WasmCodePosition kNoCodePosition = -1; V(F32Const, 0x13, _) \ V(GetLocal, 0x14, _) \ V(SetLocal, 0x15, _) \ + V(TeeLocal, 0x19, _) \ + V(Drop, 0x0b, _) \ V(CallFunction, 0x16, _) \ V(CallIndirect, 0x17, _) \ - V(CallImport, 0x18, _) \ V(I8Const, 0xcb, _) \ V(GetGlobal, 0xbb, _) \ V(SetGlobal, 0xbc, _) @@ -497,6 +501,8 @@ class WasmOpcodes { return 1 << ElementSizeLog2Of(type.representation()); } + static byte MemSize(LocalType type) { return 1 << ElementSizeLog2Of(type); } + static LocalTypeCode LocalTypeCodeFor(LocalType type) { switch (type) { case kAstI32: diff --git a/src/wasm/wasm-result.h b/src/wasm/wasm-result.h index efe269a868..9df4194ac5 100644 --- a/src/wasm/wasm-result.h +++ b/src/wasm/wasm-result.h @@ -22,19 +22,7 @@ namespace wasm { // Error codes for programmatic checking of the decoder's verification. enum ErrorCode { kSuccess, - kError, // TODO(titzer): remove me - kOutOfMemory, // decoder ran out of memory - kEndOfCode, // end of code reached prematurely - kInvalidOpcode, // found invalid opcode - kUnreachableCode, // found unreachable code - kImproperContinue, // improperly nested continue - kImproperBreak, // improperly nested break - kReturnCount, // return count mismatch - kTypeError, // type mismatch - kInvalidLocalIndex, // invalid local - kInvalidGlobalIndex, // invalid global - kInvalidFunctionIndex, // invalid function - kInvalidMemType // invalid memory type + kError, // TODO(titzer): introduce real error codes }; // The overall result of decoding a function or a module. diff --git a/test/cctest/wasm/test-run-wasm-64.cc b/test/cctest/wasm/test-run-wasm-64.cc index 966524497e..8ccc03901e 100644 --- a/test/cctest/wasm/test-run-wasm-64.cc +++ b/test/cctest/wasm/test-run-wasm-64.cc @@ -32,7 +32,6 @@ #define asu64(x) static_cast(x) #define B2(a, b) kExprBlock, a, b, kExprEnd -#define B1(a) kExprBlock, a, kExprEnd // Can't bridge macro land with nested macros. #if V8_TARGET_ARCH_MIPS @@ -835,8 +834,8 @@ WASM_EXEC_TEST(CallI64Parameter) { WasmRunner r(&module); BUILD( r, - WASM_I32_CONVERT_I64(WASM_CALL_FUNCTIONN( - 19, index, WASM_I64V_9(0xbcd12340000000b), + WASM_I32_CONVERT_I64(WASM_CALL_FUNCTION( + index, WASM_I64V_9(0xbcd12340000000b), WASM_I64V_9(0xbcd12340000000c), WASM_I32V_1(0xd), WASM_I32_CONVERT_I64(WASM_I64V_9(0xbcd12340000000e)), WASM_I64V_9(0xbcd12340000000f), WASM_I64V_10(0xbcd1234000000010), @@ -1119,7 +1118,7 @@ WASM_EXEC_TEST(Call_Int64Sub) { // Build the caller function. WasmRunner r(&module, MachineType::Int64(), MachineType::Int64()); - BUILD(r, WASM_CALL_FUNCTION2(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); + BUILD(r, WASM_CALL_FUNCTION(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); FOR_INT32_INPUTS(i) { FOR_INT32_INPUTS(j) { @@ -1154,7 +1153,11 @@ WASM_EXEC_TEST(LoadStoreI64_sx) { ZERO_OFFSET, // -- kExprI64StoreMem, // -- ZERO_ALIGNMENT, // -- - ZERO_OFFSET // -- + ZERO_OFFSET, // -- + kExprI8Const, 0, // -- + loads[m], // -- + ZERO_ALIGNMENT, // -- + ZERO_OFFSET, // -- }; r.Build(code, code + arraysize(code)); @@ -1256,10 +1259,9 @@ WASM_EXEC_TEST(F64ReinterpretI64) { int64_t* memory = module.AddMemoryElems(8); WasmRunner r(&module, MachineType::Int64()); - BUILD(r, - WASM_BLOCK(WASM_STORE_MEM(MachineType::Float64(), WASM_ZERO, - WASM_F64_REINTERPRET_I64(WASM_GET_LOCAL(0))), - WASM_GET_LOCAL(0))); + BUILD(r, WASM_STORE_MEM(MachineType::Float64(), WASM_ZERO, + WASM_F64_REINTERPRET_I64(WASM_GET_LOCAL(0))), + WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { int64_t expected = static_cast(*i) * 0x300010001; @@ -1320,18 +1322,17 @@ WASM_EXEC_TEST(MemI64_Sum) { WasmRunner r(&module, MachineType::Int32()); const byte kSum = r.AllocateLocal(kAstI64); - BUILD(r, - WASM_BLOCK( - WASM_WHILE( - WASM_GET_LOCAL(0), - WASM_BLOCK( - WASM_SET_LOCAL( - kSum, WASM_I64_ADD(WASM_GET_LOCAL(kSum), - WASM_LOAD_MEM(MachineType::Int64(), - WASM_GET_LOCAL(0)))), - WASM_SET_LOCAL( - 0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(8))))), - WASM_GET_LOCAL(1))); + BUILD( + r, + WASM_WHILE( + WASM_GET_LOCAL(0), + WASM_BLOCK( + WASM_SET_LOCAL(kSum, + WASM_I64_ADD(WASM_GET_LOCAL(kSum), + WASM_LOAD_MEM(MachineType::Int64(), + WASM_GET_LOCAL(0)))), + WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(8))))), + WASM_GET_LOCAL(1)); // Run 4 trials. for (int i = 0; i < 3; i++) { @@ -1353,7 +1354,8 @@ WASM_EXEC_TEST(StoreMemI64_alignment) { for (byte i = 0; i <= 3; i++) { WasmRunner r(&module, MachineType::Int64()); BUILD(r, WASM_STORE_MEM_ALIGNMENT(MachineType::Int64(), WASM_ZERO, i, - WASM_GET_LOCAL(0))); + WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(0)); module.RandomizeMemory(1111); module.WriteMemory(&memory[0], 0); @@ -1371,10 +1373,10 @@ WASM_EXEC_TEST(I64Global) { int64_t* global = module.AddGlobal(kAstI64); WasmRunner r(&module, MachineType::Int32()); // global = global + p0 - BUILD(r, B2(WASM_SET_GLOBAL( - 0, WASM_I64_AND(WASM_GET_GLOBAL(0), - WASM_I64_SCONVERT_I32(WASM_GET_LOCAL(0)))), - WASM_ZERO)); + BUILD(r, WASM_SET_GLOBAL( + 0, WASM_I64_AND(WASM_GET_GLOBAL(0), + WASM_I64_SCONVERT_I32(WASM_GET_LOCAL(0)))), + WASM_ZERO); module.WriteMemory(global, 0xFFFFFFFFFFFFFFFFLL); for (int i = 9; i < 444444; i += 111111) { @@ -1477,11 +1479,11 @@ static void CompileCallIndirectMany(LocalType param) { WasmFunctionCompiler t(sig, &module); std::vector code; - ADD_CODE(code, kExprI8Const, 0); for (byte p = 0; p < num_params; p++) { ADD_CODE(code, kExprGetLocal, p); } - ADD_CODE(code, kExprCallIndirect, static_cast(num_params), 1); + ADD_CODE(code, kExprI8Const, 0); + ADD_CODE(code, kExprCallIndirect, 1); t.Build(&code[0], &code[0] + code.size()); t.Compile(); @@ -1540,8 +1542,7 @@ static void Run_WasmMixedCall_N(WasmExecutionMode execution_mode, int start) { } // Call the selector function. - ADD_CODE(code, kExprCallFunction, static_cast(num_params), - static_cast(index)); + ADD_CODE(code, kExprCallFunction, static_cast(index)); // Store the result in memory. ADD_CODE(code, diff --git a/test/cctest/wasm/test-run-wasm-interpreter.cc b/test/cctest/wasm/test-run-wasm-interpreter.cc index db5bb2a786..218ee956c0 100644 --- a/test/cctest/wasm/test-run-wasm-interpreter.cc +++ b/test/cctest/wasm/test-run-wasm-interpreter.cc @@ -36,7 +36,7 @@ TEST(Run_WasmInt8Const_i) { TEST(Run_WasmIfElse) { WasmRunner r(kExecuteInterpreted, MachineType::Int32()); - BUILD(r, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_I8(9), WASM_I8(10))); + BUILD(r, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_I8(9), WASM_I8(10))); CHECK_EQ(10, r.Call(0)); CHECK_EQ(9, r.Call(1)); } @@ -65,31 +65,39 @@ TEST(Run_WasmNopsN) { TEST(Run_WasmConstsN) { const int kMaxConsts = 10; - byte code[kMaxConsts * 2]; + byte code[kMaxConsts * 3]; + int32_t expected = 0; for (int count = 1; count < kMaxConsts; count++) { for (int i = 0; i < count; i++) { - code[i * 2] = kExprI8Const; - code[i * 2 + 1] = static_cast(count * 10 + i); + byte val = static_cast(count * 10 + i); + code[i * 3] = kExprI8Const; + code[i * 3 + 1] = val; + if (i == (count - 1)) { + code[i * 3 + 2] = kExprNop; + expected = val; + } else { + code[i * 3 + 2] = kExprDrop; + } } - byte expected = static_cast(count * 11 - 1); WasmRunner r(kExecuteInterpreted); - r.Build(code, code + (count * 2)); + r.Build(code, code + (count * 3)); CHECK_EQ(expected, r.Call()); } } TEST(Run_WasmBlocksN) { const int kMaxNops = 10; - const int kExtra = 4; + const int kExtra = 5; byte code[kMaxNops + kExtra]; for (int nops = 0; nops < kMaxNops; nops++) { byte expected = static_cast(30 + nops); memset(code, kExprNop, sizeof(code)); code[0] = kExprBlock; - code[1 + nops] = kExprI8Const; - code[1 + nops + 1] = expected; - code[1 + nops + 2] = kExprEnd; + code[1] = kLocalI32; + code[2 + nops] = kExprI8Const; + code[2 + nops + 1] = expected; + code[2 + nops + 2] = kExprEnd; WasmRunner r(kExecuteInterpreted); r.Build(code, code + nops + kExtra); @@ -106,14 +114,14 @@ TEST(Run_WasmBlockBreakN) { for (int index = 0; index < nops; index++) { memset(code, kExprNop, sizeof(code)); code[0] = kExprBlock; + code[1] = kLocalI32; code[sizeof(code) - 1] = kExprEnd; int expected = nops * 11 + index; - code[1 + index + 0] = kExprI8Const; - code[1 + index + 1] = static_cast(expected); - code[1 + index + 2] = kExprBr; - code[1 + index + 3] = ARITY_1; - code[1 + index + 4] = 0; + code[2 + index + 0] = kExprI8Const; + code[2 + index + 1] = static_cast(expected); + code[2 + index + 2] = kExprBr; + code[2 + index + 3] = 0; WasmRunner r(kExecuteInterpreted); r.Build(code, code + kMaxNops + kExtra); @@ -126,10 +134,10 @@ TEST(Run_Wasm_nested_ifs_i) { WasmRunner r(kExecuteInterpreted, MachineType::Int32(), MachineType::Int32()); - BUILD(r, WASM_IF_ELSE( + BUILD(r, WASM_IF_ELSE_I( WASM_GET_LOCAL(0), - WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_I8(11), WASM_I8(12)), - WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_I8(13), WASM_I8(14)))); + WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_I8(11), WASM_I8(12)), + WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_I8(13), WASM_I8(14)))); CHECK_EQ(11, r.Call(1, 1)); CHECK_EQ(12, r.Call(1, 0)); @@ -300,10 +308,10 @@ TEST(GrowMemoryPreservesData) { TestingModule module(kExecuteInterpreted); WasmRunner r(&module, MachineType::Uint32()); module.AddMemory(WasmModule::kPageSize); - BUILD(r, WASM_BLOCK(WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index), - WASM_I32V(value)), - WASM_GROW_MEMORY(WASM_GET_LOCAL(0)), - WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V(index)))); + BUILD(r, WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index), + WASM_I32V(value)), + WASM_GROW_MEMORY(WASM_GET_LOCAL(0)), WASM_DROP, + WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V(index))); CHECK_EQ(value, r.Call(1)); } @@ -312,7 +320,7 @@ TEST(GrowMemoryInvalidSize) { // Grow memory by an invalid amount without initial memory. TestingModule module(kExecuteInterpreted); WasmRunner r(&module, MachineType::Uint32()); - BUILD(r, WASM_BLOCK(WASM_GROW_MEMORY(WASM_GET_LOCAL(0)))); + BUILD(r, WASM_GROW_MEMORY(WASM_GET_LOCAL(0))); CHECK_EQ(-1, r.Call(1048575)); } { @@ -320,7 +328,7 @@ TEST(GrowMemoryInvalidSize) { TestingModule module(kExecuteInterpreted); WasmRunner r(&module, MachineType::Uint32()); module.AddMemory(WasmModule::kPageSize); - BUILD(r, WASM_BLOCK(WASM_GROW_MEMORY(WASM_GET_LOCAL(0)))); + BUILD(r, WASM_GROW_MEMORY(WASM_GET_LOCAL(0))); CHECK_EQ(-1, r.Call(1048575)); } } diff --git a/test/cctest/wasm/test-run-wasm-js.cc b/test/cctest/wasm/test-run-wasm-js.cc index 9dfba74ecc..5fbd1fa972 100644 --- a/test/cctest/wasm/test-run-wasm-js.cc +++ b/test/cctest/wasm/test-run-wasm-js.cc @@ -152,7 +152,7 @@ TEST(Run_CallJS_Add_jswrapped) { WasmFunctionCompiler t(sigs.i_i(), &module); uint32_t js_index = module.AddJsFunction(sigs.i_i(), "(function(a) { return a + 99; })"); - BUILD(t, WASM_CALL_FUNCTION1(js_index, WASM_GET_LOCAL(0))); + BUILD(t, WASM_CALL_FUNCTION(js_index, WASM_GET_LOCAL(0))); Handle jsfunc = module.WrapCode(t.CompileAndAdd()); @@ -182,8 +182,7 @@ void RunJSSelectTest(int which) { ADD_CODE(code, WASM_F64(inputs.arg_d(i))); } - ADD_CODE(code, kExprCallFunction, static_cast(num_params), - static_cast(js_index)); + ADD_CODE(code, kExprCallFunction, static_cast(js_index)); size_t end = code.size(); code.push_back(0); @@ -420,7 +419,7 @@ void RunJSSelectAlignTest(int num_args, int num_params) { ADD_CODE(code, WASM_GET_LOCAL(i)); } - ADD_CODE(code, kExprCallFunction, static_cast(num_params), 0); + ADD_CODE(code, kExprCallFunction, 0); size_t end = code.size(); code.push_back(0); diff --git a/test/cctest/wasm/test-run-wasm-module.cc b/test/cctest/wasm/test-run-wasm-module.cc index 27ef35632b..28ba602940 100644 --- a/test/cctest/wasm/test-run-wasm-module.cc +++ b/test/cctest/wasm/test-run-wasm-module.cc @@ -53,9 +53,7 @@ TEST(Run_WasmModule_Return114) { Zone zone(&allocator); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); - uint16_t f_index = builder->AddFunction(); - WasmFunctionBuilder* f = builder->FunctionAt(f_index); - f->SetSignature(sigs.i_v()); + WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); ExportAsMain(f); byte code[] = {WASM_I8(kReturnValue)}; f->EmitCode(code, sizeof(code)); @@ -69,21 +67,18 @@ TEST(Run_WasmModule_CallAdd) { WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); - uint16_t f1_index = builder->AddFunction(); - WasmFunctionBuilder* f = builder->FunctionAt(f1_index); - f->SetSignature(sigs.i_ii()); + WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_ii()); uint16_t param1 = 0; uint16_t param2 = 1; byte code1[] = {WASM_I32_ADD(WASM_GET_LOCAL(param1), WASM_GET_LOCAL(param2))}; - f->EmitCode(code1, sizeof(code1)); + f1->EmitCode(code1, sizeof(code1)); - uint16_t f2_index = builder->AddFunction(); - f = builder->FunctionAt(f2_index); - f->SetSignature(sigs.i_v()); + WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v()); - ExportAsMain(f); - byte code2[] = {WASM_CALL_FUNCTION2(f1_index, WASM_I8(77), WASM_I8(22))}; - f->EmitCode(code2, sizeof(code2)); + ExportAsMain(f2); + byte code2[] = { + WASM_CALL_FUNCTION(f1->func_index(), WASM_I8(77), WASM_I8(22))}; + f2->EmitCode(code2, sizeof(code2)); TestModule(&zone, builder, 99); } @@ -94,9 +89,7 @@ TEST(Run_WasmModule_ReadLoadedDataSegment) { TestSignatures sigs; WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); - uint16_t f_index = builder->AddFunction(); - WasmFunctionBuilder* f = builder->FunctionAt(f_index); - f->SetSignature(sigs.i_v()); + WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); ExportAsMain(f); byte code[] = { @@ -115,18 +108,16 @@ TEST(Run_WasmModule_CheckMemoryIsZero) { TestSignatures sigs; WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); - uint16_t f_index = builder->AddFunction(); - WasmFunctionBuilder* f = builder->FunctionAt(f_index); - f->SetSignature(sigs.i_v()); + WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); uint16_t localIndex = f->AddLocal(kAstI32); ExportAsMain(f); - byte code[] = {WASM_BLOCK( + byte code[] = {WASM_BLOCK_I( WASM_WHILE( WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I32V_3(kCheckSize)), WASM_IF_ELSE( WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(localIndex)), - WASM_BRV(2, WASM_I8(-1)), WASM_INC_LOCAL_BY(localIndex, 4))), + WASM_BRV(3, WASM_I8(-1)), WASM_INC_LOCAL_BY(localIndex, 4))), WASM_I8(11))}; f->EmitCode(code, sizeof(code)); TestModule(&zone, builder, 11); @@ -138,20 +129,18 @@ TEST(Run_WasmModule_CallMain_recursive) { TestSignatures sigs; WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); - uint16_t f_index = builder->AddFunction(); - WasmFunctionBuilder* f = builder->FunctionAt(f_index); - f->SetSignature(sigs.i_v()); + WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); uint16_t localIndex = f->AddLocal(kAstI32); ExportAsMain(f); - byte code[] = {WASM_BLOCK( + byte code[] = { WASM_SET_LOCAL(localIndex, WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)), - WASM_IF_ELSE(WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I8(5)), - WASM_BLOCK(WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, + WASM_IF_ELSE_I(WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I8(5)), + WASM_SEQ(WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, WASM_INC_LOCAL(localIndex)), - WASM_BRV(1, WASM_CALL_FUNCTION0(0))), - WASM_BRV(0, WASM_I8(55))))}; + WASM_CALL_FUNCTION0(0)), + WASM_I8(55))}; f->EmitCode(code, sizeof(code)); TestModule(&zone, builder, 55); } @@ -164,20 +153,16 @@ TEST(Run_WasmModule_Global) { WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); uint32_t global1 = builder->AddGlobal(kAstI32, 0); uint32_t global2 = builder->AddGlobal(kAstI32, 0); - uint16_t f1_index = builder->AddFunction(); - WasmFunctionBuilder* f = builder->FunctionAt(f1_index); - f->SetSignature(sigs.i_v()); + WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v()); byte code1[] = { WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))}; - f->EmitCode(code1, sizeof(code1)); - uint16_t f2_index = builder->AddFunction(); - f = builder->FunctionAt(f2_index); - f->SetSignature(sigs.i_v()); - ExportAsMain(f); + f1->EmitCode(code1, sizeof(code1)); + WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v()); + ExportAsMain(f2); byte code2[] = {WASM_SET_GLOBAL(global1, WASM_I32V_1(56)), WASM_SET_GLOBAL(global2, WASM_I32V_1(41)), - WASM_RETURN1(WASM_CALL_FUNCTION0(f1_index))}; - f->EmitCode(code2, sizeof(code2)); + WASM_RETURN1(WASM_CALL_FUNCTION0(f1->func_index()))}; + f2->EmitCode(code2, sizeof(code2)); TestModule(&zone, builder, 97); } @@ -187,11 +172,9 @@ TEST(Run_WasmModule_Serialization) { Zone zone(&allocator); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); - uint16_t f_index = builder->AddFunction(); TestSignatures sigs; - WasmFunctionBuilder* f = builder->FunctionAt(f_index); - f->SetSignature(sigs.i_i()); + WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i()); byte code[] = {WASM_GET_LOCAL(0), kExprI32Const, 1, kExprI32Add}; f->EmitCode(code, sizeof(code)); ExportAs(f, kFunctionName); @@ -244,7 +227,7 @@ TEST(Run_WasmModule_Serialization) { Handle module_object = Handle::cast(v8::Utils::OpenHandle(*compiled_module)); Handle instance = - WasmModule::Instantiate(isolate, module_object, + WasmModule::Instantiate(isolate, &thrower, module_object, Handle::null(), Handle::null()) .ToHandleChecked(); @@ -258,19 +241,16 @@ TEST(Run_WasmModule_Serialization) { } TEST(Run_WasmModule_MemSize_GrowMem) { - static const int kPageSize = 0x10000; // Initial memory size = 16 + GrowMemory(10) - static const int kExpectedValue = kPageSize * 26; + static const int kExpectedValue = 26; TestSignatures sigs; v8::internal::AccountingAllocator allocator; Zone zone(&allocator); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); - uint16_t f_index = builder->AddFunction(); - WasmFunctionBuilder* f = builder->FunctionAt(f_index); - f->SetSignature(sigs.i_v()); + WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); ExportAsMain(f); - byte code[] = {WASM_GROW_MEMORY(WASM_I8(10)), WASM_MEMORY_SIZE}; + byte code[] = {WASM_GROW_MEMORY(WASM_I8(10)), WASM_DROP, WASM_MEMORY_SIZE}; f->EmitCode(code, sizeof(code)); TestModule(&zone, builder, kExpectedValue); } @@ -280,12 +260,10 @@ TEST(Run_WasmModule_GrowMemoryInIf) { v8::internal::AccountingAllocator allocator; Zone zone(&allocator); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); - uint16_t f_index = builder->AddFunction(); - WasmFunctionBuilder* f = builder->FunctionAt(f_index); - f->SetSignature(sigs.i_v()); + WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); ExportAsMain(f); - byte code[] = {WASM_IF_ELSE(WASM_I32V(0), WASM_GROW_MEMORY(WASM_I32V(1)), - WASM_I32V(12))}; + byte code[] = {WASM_IF_ELSE_I(WASM_I32V(0), WASM_GROW_MEMORY(WASM_I32V(1)), + WASM_I32V(12))}; f->EmitCode(code, sizeof(code)); TestModule(&zone, builder, 12); } diff --git a/test/cctest/wasm/test-run-wasm-relocation.cc b/test/cctest/wasm/test-run-wasm-relocation.cc index 65b1d57bc1..e3a28f611b 100644 --- a/test/cctest/wasm/test-run-wasm-relocation.cc +++ b/test/cctest/wasm/test-run-wasm-relocation.cc @@ -19,42 +19,43 @@ using namespace v8::internal::compiler; TEST_BODY(float, F32, WASM_F32_ADD) \ TEST_BODY(double, F64, WASM_F64_ADD) -#define LOAD_SET_GLOBAL_TEST_BODY(C_TYPE, MACHINE_TYPE, ADD) \ - TEST(WasmRelocateGlobal##MACHINE_TYPE) { \ - TestingModule module(kExecuteCompiled); \ - module.AddGlobal(kAst##MACHINE_TYPE); \ - module.AddGlobal(kAst##MACHINE_TYPE); \ - \ - WasmRunner r(&module, \ - WasmOpcodes::MachineTypeFor(kAst##MACHINE_TYPE)); \ - \ - /* global = global + p0 */ \ - BUILD(r, WASM_SET_GLOBAL(1, ADD(WASM_GET_GLOBAL(0), WASM_GET_LOCAL(0)))); \ - CHECK_EQ(1, module.instance->function_code.size()); \ - \ - int filter = 1 << RelocInfo::WASM_GLOBAL_REFERENCE; \ - \ - Handle code = module.instance->function_code[0]; \ - \ - Address old_start = module.instance->globals_start; \ - Address new_start = old_start + 1; \ - \ - Address old_addresses[2]; \ - uint32_t address_index = 0U; \ - for (RelocIterator it(*code, filter); !it.done(); it.next()) { \ - old_addresses[address_index] = it.rinfo()->wasm_global_reference(); \ - it.rinfo()->update_wasm_global_reference(old_start, new_start); \ - ++address_index; \ - } \ - CHECK_EQ(2U, address_index); \ - \ - address_index = 0U; \ - for (RelocIterator it(*code, filter); !it.done(); it.next()) { \ - CHECK_EQ(old_addresses[address_index] + 1, \ - it.rinfo()->wasm_global_reference()); \ - ++address_index; \ - } \ - CHECK_EQ(2U, address_index); \ +#define LOAD_SET_GLOBAL_TEST_BODY(C_TYPE, MACHINE_TYPE, ADD) \ + TEST(WasmRelocateGlobal##MACHINE_TYPE) { \ + TestingModule module(kExecuteCompiled); \ + module.AddGlobal(kAst##MACHINE_TYPE); \ + module.AddGlobal(kAst##MACHINE_TYPE); \ + \ + WasmRunner r(&module, \ + WasmOpcodes::MachineTypeFor(kAst##MACHINE_TYPE)); \ + \ + /* global = global + p0 */ \ + BUILD(r, WASM_SET_GLOBAL(1, ADD(WASM_GET_GLOBAL(0), WASM_GET_LOCAL(0))), \ + WASM_GET_GLOBAL(0)); \ + CHECK_EQ(1, module.instance->function_code.size()); \ + \ + int filter = 1 << RelocInfo::WASM_GLOBAL_REFERENCE; \ + \ + Handle code = module.instance->function_code[0]; \ + \ + Address old_start = module.instance->globals_start; \ + Address new_start = old_start + 1; \ + \ + Address old_addresses[4]; \ + uint32_t address_index = 0U; \ + for (RelocIterator it(*code, filter); !it.done(); it.next()) { \ + old_addresses[address_index] = it.rinfo()->wasm_global_reference(); \ + it.rinfo()->update_wasm_global_reference(old_start, new_start); \ + ++address_index; \ + } \ + CHECK_LE(address_index, 4U); \ + \ + address_index = 0U; \ + for (RelocIterator it(*code, filter); !it.done(); it.next()) { \ + CHECK_EQ(old_addresses[address_index] + 1, \ + it.rinfo()->wasm_global_reference()); \ + ++address_index; \ + } \ + CHECK_LE(address_index, 4U); \ } FOREACH_TYPE(LOAD_SET_GLOBAL_TEST_BODY) diff --git a/test/cctest/wasm/test-run-wasm.cc b/test/cctest/wasm/test-run-wasm.cc index 48dcad5176..422ced9be9 100644 --- a/test/cctest/wasm/test-run-wasm.cc +++ b/test/cctest/wasm/test-run-wasm.cc @@ -21,10 +21,11 @@ using namespace v8::internal::compiler; using namespace v8::internal::wasm; // for even shorter tests. -#define B2(a, b) kExprBlock, a, b, kExprEnd -#define B1(a) kExprBlock, a, kExprEnd -#define RET(x) x, kExprReturn, 1 -#define RET_I8(x) kExprI8Const, x, kExprReturn, 1 +#define B1(a) WASM_BLOCK(a) +#define B2(a, b) WASM_BLOCK(a, b) +#define B3(a, b, c) WASM_BLOCK(a, b, c) +#define RET(x) x, kExprReturn +#define RET_I8(x) kExprI8Const, x, kExprReturn WASM_EXEC_TEST(Int8Const) { WasmRunner r(execution_mode); @@ -34,11 +35,11 @@ WASM_EXEC_TEST(Int8Const) { CHECK_EQ(kExpectedValue, r.Call()); } -WASM_EXEC_TEST(Int8Const_fallthru1) { +WASM_EXEC_TEST(Int8Const_end) { WasmRunner r(execution_mode); - const byte kExpectedValue = 122; - // kExpectedValue - BUILD(r, WASM_I8(kExpectedValue)); + const byte kExpectedValue = 121; + // return(kExpectedValue) + BUILD(r, WASM_I8(kExpectedValue), kExprEnd); CHECK_EQ(kExpectedValue, r.Call()); } @@ -46,7 +47,7 @@ WASM_EXEC_TEST(Int8Const_fallthru2) { WasmRunner r(execution_mode); const byte kExpectedValue = 123; // -99 kExpectedValue - BUILD(r, WASM_I8(-99), WASM_I8(kExpectedValue)); + BUILD(r, WASM_I8(-99), WASM_DROP, WASM_I8(kExpectedValue)); CHECK_EQ(kExpectedValue, r.Call()); } @@ -78,12 +79,20 @@ WASM_EXEC_TEST(Int32Const_many) { } } -WASM_EXEC_TEST(MemorySize) { +WASM_EXEC_TEST(MemorySize1) { TestingModule module(execution_mode); WasmRunner r(&module); - module.AddMemory(1024); + module.AddMemory(WasmModule::kPageSize * 1); BUILD(r, kExprMemorySize); - CHECK_EQ(1024, r.Call()); + CHECK_EQ(1, r.Call()); +} + +WASM_EXEC_TEST(MemorySize2) { + TestingModule module(execution_mode); + WasmRunner r(&module); + module.AddMemory(WasmModule::kPageSize * 3); + BUILD(r, kExprMemorySize); + CHECK_EQ(3, r.Call()); } WASM_EXEC_TEST(Int32Param0) { @@ -129,11 +138,11 @@ WASM_EXEC_TEST(Int32Add_P_fallthru) { FOR_INT32_INPUTS(i) { CHECK_EQ(*i + 13, r.Call(*i)); } } -WASM_EXEC_TEST(Int32Add_P2) { +static void RunInt32AddTest(WasmExecutionMode execution_mode, const byte* code, + size_t size) { WasmRunner r(execution_mode, MachineType::Int32(), MachineType::Int32()); - // p0 + p1 - BUILD(r, WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); + r.Build(code, code + size); FOR_INT32_INPUTS(i) { FOR_INT32_INPUTS(j) { int32_t expected = static_cast(static_cast(*i) + @@ -143,6 +152,40 @@ WASM_EXEC_TEST(Int32Add_P2) { } } +WASM_EXEC_TEST(Int32Add_P2) { + FLAG_wasm_mv_prototype = true; + static const byte code[] = { + WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))}; + RunInt32AddTest(execution_mode, code, sizeof(code)); +} + +WASM_EXEC_TEST(Int32Add_block1) { + FLAG_wasm_mv_prototype = true; + static const byte code[] = { + WASM_BLOCK_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + kExprI32Add}; + RunInt32AddTest(execution_mode, code, sizeof(code)); +} + +WASM_EXEC_TEST(Int32Add_block2) { + FLAG_wasm_mv_prototype = true; + static const byte code[] = { + WASM_BLOCK_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + kExprBr, DEPTH_0), + kExprI32Add}; + RunInt32AddTest(execution_mode, code, sizeof(code)); +} + +WASM_EXEC_TEST(Int32Add_multi_if) { + FLAG_wasm_mv_prototype = true; + static const byte code[] = { + WASM_IF_ELSE_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + kExprI32Add}; + RunInt32AddTest(execution_mode, code, sizeof(code)); +} + WASM_EXEC_TEST(Float32Add) { WasmRunner r(execution_mode); // int(11.5f + 44.5f) @@ -433,14 +476,18 @@ WASM_EXEC_TEST(Int32DivS_trap_effect) { module.AddMemoryElems(8); WasmRunner r(&module, MachineType::Int32(), MachineType::Int32()); - BUILD(r, - WASM_IF_ELSE(WASM_GET_LOCAL(0), - WASM_I32_DIVS(WASM_STORE_MEM(MachineType::Int8(), - WASM_ZERO, WASM_GET_LOCAL(0)), - WASM_GET_LOCAL(1)), - WASM_I32_DIVS(WASM_STORE_MEM(MachineType::Int8(), - WASM_ZERO, WASM_GET_LOCAL(0)), - WASM_GET_LOCAL(1)))); + BUILD(r, WASM_IF_ELSE_I( + WASM_GET_LOCAL(0), + WASM_I32_DIVS( + WASM_BLOCK_I(WASM_STORE_MEM(MachineType::Int8(), WASM_ZERO, + WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(1)), + WASM_I32_DIVS( + WASM_BLOCK_I(WASM_STORE_MEM(MachineType::Int8(), WASM_ZERO, + WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(1)))); CHECK_EQ(0, r.Call(0, 100)); CHECK_TRAP(r.Call(8, 0)); CHECK_TRAP(r.Call(4, 0)); @@ -618,50 +665,52 @@ WASM_EXEC_TEST(Float64Neg) { WASM_EXEC_TEST(IfElse_P) { WasmRunner r(execution_mode, MachineType::Int32()); // if (p0) return 11; else return 22; - BUILD(r, WASM_IF_ELSE(WASM_GET_LOCAL(0), // -- - WASM_I8(11), // -- - WASM_I8(22))); // -- + BUILD(r, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), // -- + WASM_I8(11), // -- + WASM_I8(22))); // -- FOR_INT32_INPUTS(i) { int32_t expected = *i ? 11 : 22; CHECK_EQ(expected, r.Call(*i)); } } +#define EMPTY WASM_EXEC_TEST(If_empty1) { WasmRunner r(execution_mode, MachineType::Uint32(), MachineType::Uint32()); - BUILD(r, WASM_GET_LOCAL(0), kExprIf, kExprEnd, WASM_GET_LOCAL(1)); + BUILD(r, WASM_GET_LOCAL(0), kExprIf, kLocalVoid, kExprEnd, WASM_GET_LOCAL(1)); FOR_UINT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i - 9, *i)); } } WASM_EXEC_TEST(IfElse_empty1) { WasmRunner r(execution_mode, MachineType::Uint32(), MachineType::Uint32()); - BUILD(r, WASM_GET_LOCAL(0), kExprIf, kExprElse, kExprEnd, WASM_GET_LOCAL(1)); + BUILD(r, WASM_GET_LOCAL(0), kExprIf, kLocalVoid, kExprElse, kExprEnd, + WASM_GET_LOCAL(1)); FOR_UINT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i - 8, *i)); } } WASM_EXEC_TEST(IfElse_empty2) { WasmRunner r(execution_mode, MachineType::Uint32(), MachineType::Uint32()); - BUILD(r, WASM_GET_LOCAL(0), kExprIf, WASM_ZERO, kExprElse, kExprEnd, - WASM_GET_LOCAL(1)); + BUILD(r, WASM_GET_LOCAL(0), kExprIf, kLocalVoid, WASM_NOP, kExprElse, + kExprEnd, WASM_GET_LOCAL(1)); FOR_UINT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i - 7, *i)); } } WASM_EXEC_TEST(IfElse_empty3) { WasmRunner r(execution_mode, MachineType::Uint32(), MachineType::Uint32()); - BUILD(r, WASM_GET_LOCAL(0), kExprIf, kExprElse, WASM_ZERO, kExprEnd, - WASM_GET_LOCAL(1)); + BUILD(r, WASM_GET_LOCAL(0), kExprIf, kLocalVoid, kExprElse, WASM_NOP, + kExprEnd, WASM_GET_LOCAL(1)); FOR_UINT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i - 6, *i)); } } -WASM_EXEC_TEST(If_chain) { +WASM_EXEC_TEST(If_chain1) { WasmRunner r(execution_mode, MachineType::Int32()); // if (p0) 13; if (p0) 14; 15 - BUILD(r, WASM_IF(WASM_GET_LOCAL(0), WASM_I8(13)), - WASM_IF(WASM_GET_LOCAL(0), WASM_I8(14)), WASM_I8(15)); + BUILD(r, WASM_IF(WASM_GET_LOCAL(0), WASM_NOP), + WASM_IF(WASM_GET_LOCAL(0), WASM_NOP), WASM_I8(15)); FOR_INT32_INPUTS(i) { CHECK_EQ(15, r.Call(*i)); } } @@ -680,13 +729,22 @@ WASM_EXEC_TEST(If_chain_set) { WASM_EXEC_TEST(IfElse_Unreachable1) { WasmRunner r(execution_mode); - // if (0) unreachable; else return 22; - BUILD(r, WASM_IF_ELSE(WASM_ZERO, // -- - WASM_UNREACHABLE, // -- - WASM_I8(27))); // -- + // 0 ? unreachable : 27 + BUILD(r, WASM_IF_ELSE_I(WASM_ZERO, // -- + WASM_UNREACHABLE, // -- + WASM_I8(27))); // -- CHECK_EQ(27, r.Call()); } +WASM_EXEC_TEST(IfElse_Unreachable2) { + WasmRunner r(execution_mode); + // 1 ? 28 : unreachable + BUILD(r, WASM_IF_ELSE_I(WASM_I8(1), // -- + WASM_I8(28), // -- + WASM_UNREACHABLE)); // -- + CHECK_EQ(28, r.Call()); +} + WASM_EXEC_TEST(Return12) { WasmRunner r(execution_mode); @@ -697,7 +755,7 @@ WASM_EXEC_TEST(Return12) { WASM_EXEC_TEST(Return17) { WasmRunner r(execution_mode); - BUILD(r, B1(RET_I8(17))); + BUILD(r, WASM_BLOCK(RET_I8(17))); CHECK_EQ(17, r.Call()); } @@ -754,10 +812,10 @@ WASM_EXEC_TEST(Select) { WASM_EXEC_TEST(Select_strict1) { WasmRunner r(execution_mode, MachineType::Int32()); // select(a=0, a=1, a=2); return a - BUILD(r, B2(WASM_SELECT(WASM_SET_LOCAL(0, WASM_I8(0)), - WASM_SET_LOCAL(0, WASM_I8(1)), - WASM_SET_LOCAL(0, WASM_I8(2))), - WASM_GET_LOCAL(0))); + BUILD(r, WASM_SELECT(WASM_TEE_LOCAL(0, WASM_I8(0)), + WASM_TEE_LOCAL(0, WASM_I8(1)), + WASM_TEE_LOCAL(0, WASM_I8(2))), + WASM_DROP, WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { CHECK_EQ(2, r.Call(*i)); } } @@ -766,8 +824,8 @@ WASM_EXEC_TEST(Select_strict2) { r.AllocateLocal(kAstI32); r.AllocateLocal(kAstI32); // select(b=5, c=6, a) - BUILD(r, WASM_SELECT(WASM_SET_LOCAL(1, WASM_I8(5)), - WASM_SET_LOCAL(2, WASM_I8(6)), WASM_GET_LOCAL(0))); + BUILD(r, WASM_SELECT(WASM_TEE_LOCAL(1, WASM_I8(5)), + WASM_TEE_LOCAL(2, WASM_I8(6)), WASM_GET_LOCAL(0))); FOR_INT32_INPUTS(i) { int32_t expected = *i ? 5 : 6; CHECK_EQ(expected, r.Call(*i)); @@ -779,9 +837,9 @@ WASM_EXEC_TEST(Select_strict3) { r.AllocateLocal(kAstI32); r.AllocateLocal(kAstI32); // select(b=5, c=6, a=b) - BUILD(r, WASM_SELECT(WASM_SET_LOCAL(1, WASM_I8(5)), - WASM_SET_LOCAL(2, WASM_I8(6)), - WASM_SET_LOCAL(0, WASM_GET_LOCAL(1)))); + BUILD(r, WASM_SELECT(WASM_TEE_LOCAL(1, WASM_I8(5)), + WASM_TEE_LOCAL(2, WASM_I8(6)), + WASM_TEE_LOCAL(0, WASM_GET_LOCAL(1)))); FOR_INT32_INPUTS(i) { int32_t expected = 5; CHECK_EQ(expected, r.Call(*i)); @@ -790,26 +848,38 @@ WASM_EXEC_TEST(Select_strict3) { WASM_EXEC_TEST(BrIf_strict) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD( - r, - B2(B1(WASM_BRV_IF(0, WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_I8(99)))), - WASM_GET_LOCAL(0))); + BUILD(r, WASM_BLOCK_I(WASM_BRV_IF(0, WASM_GET_LOCAL(0), + WASM_TEE_LOCAL(0, WASM_I8(99))))); - FOR_INT32_INPUTS(i) { CHECK_EQ(99, r.Call(*i)); } + FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } +} + +WASM_EXEC_TEST(Br_height) { + WasmRunner r(execution_mode, MachineType::Int32()); + BUILD(r, + WASM_BLOCK_I( + WASM_BLOCK_I(WASM_BRV_IFD(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(0)), + WASM_RETURN1(WASM_I8(9)), WASM_I8(7), WASM_I8(7)), + WASM_BRV(0, WASM_I8(8)))); + + for (int32_t i = 0; i < 5; i++) { + int32_t expected = i != 0 ? 8 : 9; + CHECK_EQ(expected, r.Call(i)); + } } WASM_EXEC_TEST(BrTable0a) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, - B2(B1(WASM_BR_TABLE(WASM_GET_LOCAL(0), 0, BR_TARGET(0))), WASM_I8(91))); + BUILD(r, B1(B1(WASM_BR_TABLE(WASM_GET_LOCAL(0), 0, BR_TARGET(0)))), + WASM_I8(91)); FOR_INT32_INPUTS(i) { CHECK_EQ(91, r.Call(*i)); } } WASM_EXEC_TEST(BrTable0b) { WasmRunner r(execution_mode, MachineType::Int32()); BUILD(r, - B2(B1(WASM_BR_TABLE(WASM_GET_LOCAL(0), 1, BR_TARGET(0), BR_TARGET(0))), - WASM_I8(92))); + B1(B1(WASM_BR_TABLE(WASM_GET_LOCAL(0), 1, BR_TARGET(0), BR_TARGET(0)))), + WASM_I8(92)); FOR_INT32_INPUTS(i) { CHECK_EQ(92, r.Call(*i)); } } @@ -817,9 +887,9 @@ WASM_EXEC_TEST(BrTable0c) { WasmRunner r(execution_mode, MachineType::Int32()); BUILD( r, - B2(B2(B1(WASM_BR_TABLE(WASM_GET_LOCAL(0), 1, BR_TARGET(0), BR_TARGET(1))), - RET_I8(76)), - WASM_I8(77))); + B1(B2(B1(WASM_BR_TABLE(WASM_GET_LOCAL(0), 1, BR_TARGET(0), BR_TARGET(1))), + RET_I8(76))), + WASM_I8(77)); FOR_INT32_INPUTS(i) { int32_t expected = *i == 0 ? 76 : 77; CHECK_EQ(expected, r.Call(*i)); @@ -834,9 +904,10 @@ WASM_EXEC_TEST(BrTable1) { WASM_EXEC_TEST(BrTable_loop) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_LOOP(WASM_BR_TABLE(WASM_INC_LOCAL_BY(0, 1), 2, BR_TARGET(2), - BR_TARGET(1), BR_TARGET(0))), - RET_I8(99)), + BUILD(r, + B2(B1(WASM_LOOP(WASM_BR_TABLE(WASM_INC_LOCAL_BYV(0, 1), 2, BR_TARGET(2), + BR_TARGET(1), BR_TARGET(0)))), + RET_I8(99)), WASM_I8(98)); CHECK_EQ(99, r.Call(0)); CHECK_EQ(98, r.Call(-1)); @@ -981,10 +1052,9 @@ WASM_EXEC_TEST(I32ReinterpretF32) { int32_t* memory = module.AddMemoryElems(8); WasmRunner r(&module, MachineType::Int32()); - BUILD(r, - WASM_BLOCK(WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, - WASM_F32_REINTERPRET_I32(WASM_GET_LOCAL(0))), - WASM_I8(107))); + BUILD(r, WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, + WASM_F32_REINTERPRET_I32(WASM_GET_LOCAL(0))), + WASM_I8(107)); FOR_INT32_INPUTS(i) { int32_t expected = *i; @@ -993,13 +1063,14 @@ WASM_EXEC_TEST(I32ReinterpretF32) { } } -WASM_EXEC_TEST(ReturnStore) { +WASM_EXEC_TEST(LoadStoreLoad) { TestingModule module(execution_mode); int32_t* memory = module.AddMemoryElems(8); WasmRunner r(&module); BUILD(r, WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, - WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO))); + WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)), + WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)); FOR_INT32_INPUTS(i) { int32_t expected = *i; @@ -1021,7 +1092,7 @@ WASM_EXEC_TEST(VoidReturn1) { const int32_t kExpected = -414444; // Build the calling function. WasmRunner r(&module); - BUILD(r, B2(WASM_CALL_FUNCTION0(index), WASM_I32V_3(kExpected))); + BUILD(r, WASM_CALL_FUNCTION0(index), WASM_I32V_3(kExpected)); int32_t result = r.Call(); CHECK_EQ(kExpected, result); @@ -1039,15 +1110,27 @@ WASM_EXEC_TEST(VoidReturn2) { const int32_t kExpected = -414444; // Build the calling function. WasmRunner r(&module); - BUILD(r, B2(WASM_CALL_FUNCTION0(index), WASM_I32V_3(kExpected))); + BUILD(r, B1(WASM_CALL_FUNCTION0(index)), WASM_I32V_3(kExpected)); int32_t result = r.Call(); CHECK_EQ(kExpected, result); } +WASM_EXEC_TEST(BrEmpty) { + WasmRunner r(execution_mode, MachineType::Int32()); + BUILD(r, WASM_BRV(0, WASM_GET_LOCAL(0))); + FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } +} + +WASM_EXEC_TEST(BrIfEmpty) { + WasmRunner r(execution_mode, MachineType::Int32()); + BUILD(r, WASM_BRV_IF(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); + FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } +} + WASM_EXEC_TEST(Block_empty) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, kExprBlock, kExprEnd, WASM_GET_LOCAL(0)); + BUILD(r, kExprBlock, kLocalVoid, kExprEnd, WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } } @@ -1059,27 +1142,45 @@ WASM_EXEC_TEST(Block_empty_br1) { WASM_EXEC_TEST(Block_empty_brif1) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B1(WASM_BR_IF(0, WASM_ZERO)), WASM_GET_LOCAL(0)); + BUILD(r, WASM_BLOCK(WASM_BR_IF(0, WASM_ZERO)), WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } } WASM_EXEC_TEST(Block_empty_brif2) { WasmRunner r(execution_mode, MachineType::Uint32(), MachineType::Uint32()); - BUILD(r, B1(WASM_BR_IF(0, WASM_GET_LOCAL(1))), WASM_GET_LOCAL(0)); + BUILD(r, WASM_BLOCK(WASM_BR_IF(0, WASM_GET_LOCAL(1))), WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i, *i + 1)); } } +WASM_EXEC_TEST(Block_i) { + WasmRunner r(execution_mode, MachineType::Int32()); + BUILD(r, WASM_BLOCK_I(WASM_GET_LOCAL(0))); + FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } +} + +WASM_EXEC_TEST(Block_f) { + WasmRunner r(execution_mode, MachineType::Float32()); + BUILD(r, WASM_BLOCK_F(WASM_GET_LOCAL(0))); + FOR_FLOAT32_INPUTS(i) { CHECK_FLOAT_EQ(*i, r.Call(*i)); } +} + +WASM_EXEC_TEST(Block_d) { + WasmRunner r(execution_mode, MachineType::Float64()); + BUILD(r, WASM_BLOCK_D(WASM_GET_LOCAL(0))); + FOR_FLOAT64_INPUTS(i) { CHECK_FLOAT_EQ(*i, r.Call(*i)); } +} + WASM_EXEC_TEST(Block_br2) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B1(WASM_BRV(0, WASM_GET_LOCAL(0)))); + BUILD(r, WASM_BLOCK_I(WASM_BRV(0, WASM_GET_LOCAL(0)))); FOR_UINT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } } WASM_EXEC_TEST(Block_If_P) { WasmRunner r(execution_mode, MachineType::Int32()); - // { if (p0) return 51; return 52; } - BUILD(r, B2( // -- + // block { if (p0) break 51; 52; } + BUILD(r, WASM_BLOCK_I( // -- WASM_IF(WASM_GET_LOCAL(0), // -- WASM_BRV(1, WASM_I8(51))), // -- WASM_I8(52))); // -- @@ -1091,32 +1192,64 @@ WASM_EXEC_TEST(Block_If_P) { WASM_EXEC_TEST(Loop_empty) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, kExprLoop, kExprEnd, WASM_GET_LOCAL(0)); + BUILD(r, kExprLoop, kLocalVoid, kExprEnd, WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } } +WASM_EXEC_TEST(Loop_i) { + WasmRunner r(execution_mode, MachineType::Int32()); + BUILD(r, WASM_LOOP_I(WASM_GET_LOCAL(0))); + FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } +} + +WASM_EXEC_TEST(Loop_f) { + WasmRunner r(execution_mode, MachineType::Float32()); + BUILD(r, WASM_LOOP_F(WASM_GET_LOCAL(0))); + FOR_FLOAT32_INPUTS(i) { CHECK_FLOAT_EQ(*i, r.Call(*i)); } +} + +WASM_EXEC_TEST(Loop_d) { + WasmRunner r(execution_mode, MachineType::Float64()); + BUILD(r, WASM_LOOP_D(WASM_GET_LOCAL(0))); + FOR_FLOAT64_INPUTS(i) { CHECK_FLOAT_EQ(*i, r.Call(*i)); } +} + WASM_EXEC_TEST(Loop_empty_br1) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, WASM_LOOP(WASM_BR(1)), WASM_GET_LOCAL(0)); + BUILD(r, B1(WASM_LOOP(WASM_BR(1))), WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } } WASM_EXEC_TEST(Loop_empty_brif1) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, WASM_LOOP(WASM_BR_IF(1, WASM_ZERO)), WASM_GET_LOCAL(0)); + BUILD(r, B1(WASM_LOOP(WASM_BR_IF(1, WASM_ZERO))), WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); } } WASM_EXEC_TEST(Loop_empty_brif2) { WasmRunner r(execution_mode, MachineType::Uint32(), MachineType::Uint32()); - BUILD(r, WASM_LOOP(WASM_BR_IF(1, WASM_GET_LOCAL(1))), WASM_GET_LOCAL(0)); + BUILD(r, WASM_LOOP_I(WASM_BRV_IF(1, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))); FOR_UINT32_INPUTS(i) { CHECK_EQ(*i, r.Call(*i, *i + 1)); } } +WASM_EXEC_TEST(Loop_empty_brif3) { + WasmRunner r(execution_mode, MachineType::Uint32(), + MachineType::Uint32(), MachineType::Uint32()); + BUILD(r, WASM_LOOP(WASM_BRV_IFD(1, WASM_GET_LOCAL(2), WASM_GET_LOCAL(0))), + WASM_GET_LOCAL(1)); + FOR_UINT32_INPUTS(i) { + FOR_UINT32_INPUTS(j) { + CHECK_EQ(*i, r.Call(0, *i, *j)); + CHECK_EQ(*j, r.Call(1, *i, *j)); + } + } +} + WASM_EXEC_TEST(Block_BrIf_P) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_BRV_IF(0, WASM_I8(51), WASM_GET_LOCAL(0)), WASM_I8(52))); + BUILD(r, WASM_BLOCK_I(WASM_BRV_IFD(0, WASM_I8(51), WASM_GET_LOCAL(0)), + WASM_I8(52))); FOR_INT32_INPUTS(i) { int32_t expected = *i ? 51 : 52; CHECK_EQ(expected, r.Call(*i)); @@ -1126,11 +1259,11 @@ WASM_EXEC_TEST(Block_BrIf_P) { WASM_EXEC_TEST(Block_IfElse_P_assign) { WasmRunner r(execution_mode, MachineType::Int32()); // { if (p0) p0 = 71; else p0 = 72; return p0; } - BUILD(r, B2( // -- - WASM_IF_ELSE(WASM_GET_LOCAL(0), // -- - WASM_SET_LOCAL(0, WASM_I8(71)), // -- - WASM_SET_LOCAL(0, WASM_I8(72))), // -- - WASM_GET_LOCAL(0))); + BUILD(r, // -- + WASM_IF_ELSE(WASM_GET_LOCAL(0), // -- + WASM_SET_LOCAL(0, WASM_I8(71)), // -- + WASM_SET_LOCAL(0, WASM_I8(72))), // -- + WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { int32_t expected = *i ? 71 : 72; CHECK_EQ(expected, r.Call(*i)); @@ -1153,9 +1286,8 @@ WASM_EXEC_TEST(Block_IfElse_P_return) { WASM_EXEC_TEST(Block_If_P_assign) { WasmRunner r(execution_mode, MachineType::Int32()); // { if (p0) p0 = 61; p0; } - BUILD(r, - WASM_BLOCK(WASM_IF(WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_I8(61))), - WASM_GET_LOCAL(0))); + BUILD(r, WASM_IF(WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_I8(61))), + WASM_GET_LOCAL(0)); FOR_INT32_INPUTS(i) { int32_t expected = *i ? 61 : *i; CHECK_EQ(expected, r.Call(*i)); @@ -1172,21 +1304,9 @@ WASM_EXEC_TEST(DanglingAssign) { WASM_EXEC_TEST(ExprIf_P) { WasmRunner r(execution_mode, MachineType::Int32()); // p0 ? 11 : 22; - BUILD(r, WASM_IF_ELSE(WASM_GET_LOCAL(0), // -- - WASM_I8(11), // -- - WASM_I8(22))); // -- - FOR_INT32_INPUTS(i) { - int32_t expected = *i ? 11 : 22; - CHECK_EQ(expected, r.Call(*i)); - } -} - -WASM_EXEC_TEST(ExprIf_P_fallthru) { - WasmRunner r(execution_mode, MachineType::Int32()); - // p0 ? 11 : 22; - BUILD(r, WASM_IF_ELSE(WASM_GET_LOCAL(0), // -- - WASM_I8(11), // -- - WASM_I8(22))); // -- + BUILD(r, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), // -- + WASM_I8(11), // -- + WASM_I8(22))); // -- FOR_INT32_INPUTS(i) { int32_t expected = *i ? 11 : 22; CHECK_EQ(expected, r.Call(*i)); @@ -1195,12 +1315,11 @@ WASM_EXEC_TEST(ExprIf_P_fallthru) { WASM_EXEC_TEST(CountDown) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, WASM_BLOCK( - WASM_LOOP(WASM_IF( - WASM_GET_LOCAL(0), - WASM_BRV(1, WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), - WASM_I8(1)))))), - WASM_GET_LOCAL(0))); + BUILD(r, WASM_LOOP(WASM_IFB( + WASM_GET_LOCAL(0), + WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(1))), + WASM_BR(1))), + WASM_GET_LOCAL(0)); CHECK_EQ(0, r.Call(1)); CHECK_EQ(0, r.Call(10)); CHECK_EQ(0, r.Call(100)); @@ -1208,12 +1327,12 @@ WASM_EXEC_TEST(CountDown) { WASM_EXEC_TEST(CountDown_fallthru) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, WASM_BLOCK( - WASM_LOOP(WASM_IF(WASM_NOT(WASM_GET_LOCAL(0)), WASM_BREAK(1)), - WASM_SET_LOCAL( - 0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(1))), - WASM_CONTINUE(0)), - WASM_GET_LOCAL(0))); + BUILD(r, WASM_LOOP( + WASM_IF(WASM_NOT(WASM_GET_LOCAL(0)), + WASM_BRV(2, WASM_GET_LOCAL(0))), + WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(1))), + WASM_CONTINUE(0)), + WASM_GET_LOCAL(0)); CHECK_EQ(0, r.Call(1)); CHECK_EQ(0, r.Call(10)); CHECK_EQ(0, r.Call(100)); @@ -1221,41 +1340,42 @@ WASM_EXEC_TEST(CountDown_fallthru) { WASM_EXEC_TEST(WhileCountDown) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, - WASM_BLOCK(WASM_WHILE(WASM_GET_LOCAL(0), - WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), - WASM_I8(1)))), - WASM_GET_LOCAL(0))); + BUILD(r, WASM_WHILE( + WASM_GET_LOCAL(0), + WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(1)))), + WASM_GET_LOCAL(0)); CHECK_EQ(0, r.Call(1)); CHECK_EQ(0, r.Call(10)); CHECK_EQ(0, r.Call(100)); } WASM_EXEC_TEST(Loop_if_break1) { - WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_LOOP(WASM_IF(WASM_GET_LOCAL(0), WASM_BREAK(1)), - WASM_SET_LOCAL(0, WASM_I8(99))), - WASM_GET_LOCAL(0))); - CHECK_EQ(99, r.Call(0)); - CHECK_EQ(3, r.Call(3)); - CHECK_EQ(10000, r.Call(10000)); - CHECK_EQ(-29, r.Call(-29)); + WasmRunner r(execution_mode, MachineType::Int32(), + MachineType::Int32()); + BUILD(r, WASM_LOOP(WASM_IF(WASM_GET_LOCAL(0), WASM_BRV(2, WASM_GET_LOCAL(1))), + WASM_SET_LOCAL(0, WASM_I8(99))), + WASM_GET_LOCAL(0)); + CHECK_EQ(99, r.Call(0, 11)); + CHECK_EQ(65, r.Call(3, 65)); + CHECK_EQ(10001, r.Call(10000, 10001)); + CHECK_EQ(-29, r.Call(-28, -29)); } WASM_EXEC_TEST(Loop_if_break2) { - WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_LOOP(WASM_BR_IF(1, WASM_GET_LOCAL(0)), - WASM_SET_LOCAL(0, WASM_I8(99))), - WASM_GET_LOCAL(0))); - CHECK_EQ(99, r.Call(0)); - CHECK_EQ(3, r.Call(3)); - CHECK_EQ(10000, r.Call(10000)); - CHECK_EQ(-29, r.Call(-29)); + WasmRunner r(execution_mode, MachineType::Int32(), + MachineType::Int32()); + BUILD(r, WASM_LOOP(WASM_BRV_IF(1, WASM_GET_LOCAL(1), WASM_GET_LOCAL(0)), + WASM_DROP, WASM_SET_LOCAL(0, WASM_I8(99))), + WASM_GET_LOCAL(0)); + CHECK_EQ(99, r.Call(0, 33)); + CHECK_EQ(3, r.Call(1, 3)); + CHECK_EQ(10000, r.Call(99, 10000)); + CHECK_EQ(-29, r.Call(-11, -29)); } WASM_EXEC_TEST(Loop_if_break_fallthru) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B1(WASM_LOOP(WASM_IF(WASM_GET_LOCAL(0), WASM_BREAK(1)), + BUILD(r, B1(WASM_LOOP(WASM_IF(WASM_GET_LOCAL(0), WASM_BR(2)), WASM_SET_LOCAL(0, WASM_I8(93)))), WASM_GET_LOCAL(0)); CHECK_EQ(93, r.Call(0)); @@ -1264,6 +1384,17 @@ WASM_EXEC_TEST(Loop_if_break_fallthru) { CHECK_EQ(-22, r.Call(-22)); } +WASM_EXEC_TEST(Loop_if_break_fallthru2) { + WasmRunner r(execution_mode, MachineType::Int32()); + BUILD(r, B1(B1(WASM_LOOP(WASM_IF(WASM_GET_LOCAL(0), WASM_BR(2)), + WASM_SET_LOCAL(0, WASM_I8(93))))), + WASM_GET_LOCAL(0)); + CHECK_EQ(93, r.Call(0)); + CHECK_EQ(3, r.Call(3)); + CHECK_EQ(10001, r.Call(10001)); + CHECK_EQ(-22, r.Call(-22)); +} + WASM_EXEC_TEST(IfBreak1) { WasmRunner r(execution_mode, MachineType::Int32()); BUILD(r, WASM_IF(WASM_GET_LOCAL(0), WASM_SEQ(WASM_BR(0), WASM_UNREACHABLE)), @@ -1356,7 +1487,7 @@ WASM_EXEC_TEST(LoadMem_offset_oob) { uint32_t boundary = 24 - WasmOpcodes::MemSize(machineTypes[m]); BUILD(r, WASM_LOAD_MEM_OFFSET(machineTypes[m], 8, WASM_GET_LOCAL(0)), - WASM_ZERO); + WASM_DROP, WASM_ZERO); CHECK_EQ(0, r.Call(boundary)); // in bounds. @@ -1444,7 +1575,8 @@ WASM_EXEC_TEST(StoreMemI32_alignment) { for (byte i = 0; i <= 2; ++i) { WasmRunner r(&module, MachineType::Int32()); BUILD(r, WASM_STORE_MEM_ALIGNMENT(MachineType::Int32(), WASM_ZERO, i, - WASM_GET_LOCAL(0))); + WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(0)); module.RandomizeMemory(1111); memory[0] = 0; @@ -1460,7 +1592,8 @@ WASM_EXEC_TEST(StoreMemI32_offset) { const int32_t kWritten = 0xaabbccdd; BUILD(r, WASM_STORE_MEM_OFFSET(MachineType::Int32(), 4, WASM_GET_LOCAL(0), - WASM_I32V_5(kWritten))); + WASM_I32V_5(kWritten)), + WASM_I32V_5(kWritten)); for (int i = 0; i < 2; ++i) { module.RandomizeMemory(1111); @@ -1526,18 +1659,17 @@ WASM_EXEC_TEST(MemI32_Sum) { WasmRunner r(&module, MachineType::Int32()); const byte kSum = r.AllocateLocal(kAstI32); - BUILD(r, - WASM_BLOCK( - WASM_WHILE( - WASM_GET_LOCAL(0), - WASM_BLOCK( - WASM_SET_LOCAL( - kSum, WASM_I32_ADD(WASM_GET_LOCAL(kSum), - WASM_LOAD_MEM(MachineType::Int32(), - WASM_GET_LOCAL(0)))), - WASM_SET_LOCAL( - 0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(4))))), - WASM_GET_LOCAL(1))); + BUILD( + r, + WASM_WHILE( + WASM_GET_LOCAL(0), + WASM_BLOCK( + WASM_SET_LOCAL(kSum, + WASM_I32_ADD(WASM_GET_LOCAL(kSum), + WASM_LOAD_MEM(MachineType::Int32(), + WASM_GET_LOCAL(0)))), + WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(4))))), + WASM_GET_LOCAL(1)); // Run 4 trials. for (int i = 0; i < 3; ++i) { @@ -1557,10 +1689,24 @@ WASM_EXEC_TEST(CheckMachIntsZero) { module.AddMemoryElems(kNumElems); WasmRunner r(&module, MachineType::Int32()); - BUILD(r, kExprLoop, kExprGetLocal, 0, kExprIf, kExprGetLocal, 0, - kExprI32LoadMem, 0, 0, kExprIf, kExprI8Const, 255, kExprReturn, ARITY_1, - kExprEnd, kExprGetLocal, 0, kExprI8Const, 4, kExprI32Sub, kExprSetLocal, - 0, kExprBr, ARITY_1, DEPTH_0, kExprEnd, kExprEnd, kExprI8Const, 0); + BUILD(r, // -- + /**/ kExprLoop, kLocalVoid, // -- + /* */ kExprGetLocal, 0, // -- + /* */ kExprIf, kLocalVoid, // -- + /* */ kExprGetLocal, 0, // -- + /* */ kExprI32LoadMem, 0, 0, // -- + /* */ kExprIf, kLocalVoid, // -- + /* */ kExprI8Const, 255, // -- + /* */ kExprReturn, // -- + /* */ kExprEnd, // -- + /* */ kExprGetLocal, 0, // -- + /* */ kExprI8Const, 4, // -- + /* */ kExprI32Sub, // -- + /* */ kExprTeeLocal, 0, // -- + /* */ kExprBr, DEPTH_0, // -- + /* */ kExprEnd, // -- + /**/ kExprEnd, // -- + /**/ kExprI8Const, 0); // -- module.BlankMemory(); CHECK_EQ(0, r.Call((kNumElems - 1) * 4)); @@ -1579,20 +1725,18 @@ WASM_EXEC_TEST(MemF32_Sum) { WasmRunner r(&module, MachineType::Int32()); const byte kSum = r.AllocateLocal(kAstF32); - BUILD(r, - WASM_BLOCK( - WASM_WHILE( - WASM_GET_LOCAL(0), - WASM_BLOCK( - WASM_SET_LOCAL( - kSum, WASM_F32_ADD(WASM_GET_LOCAL(kSum), - WASM_LOAD_MEM(MachineType::Float32(), - WASM_GET_LOCAL(0)))), - WASM_SET_LOCAL( - 0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(4))))), - WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, - WASM_GET_LOCAL(kSum)), - WASM_GET_LOCAL(0))); + BUILD( + r, + WASM_WHILE( + WASM_GET_LOCAL(0), + WASM_BLOCK( + WASM_SET_LOCAL(kSum, + WASM_F32_ADD(WASM_GET_LOCAL(kSum), + WASM_LOAD_MEM(MachineType::Float32(), + WASM_GET_LOCAL(0)))), + WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(4))))), + WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, WASM_GET_LOCAL(kSum)), + WASM_GET_LOCAL(0)); CHECK_EQ(0, r.Call(4 * (kSize - 1))); CHECK_NE(-99.25f, module.ReadMemory(&buffer[0])); @@ -1611,19 +1755,17 @@ T GenerateAndRunFold(WasmExecutionMode execution_mode, WasmOpcode binop, WasmRunner r(&module, MachineType::Int32()); const byte kAccum = r.AllocateLocal(astType); - BUILD(r, WASM_BLOCK( - WASM_SET_LOCAL(kAccum, WASM_LOAD_MEM(memType, WASM_ZERO)), - WASM_WHILE( - WASM_GET_LOCAL(0), - WASM_BLOCK( - WASM_SET_LOCAL( + BUILD(r, WASM_SET_LOCAL(kAccum, WASM_LOAD_MEM(memType, WASM_ZERO)), + WASM_WHILE( + WASM_GET_LOCAL(0), + WASM_BLOCK(WASM_SET_LOCAL( kAccum, WASM_BINOP(binop, WASM_GET_LOCAL(kAccum), WASM_LOAD_MEM( memType, WASM_GET_LOCAL(0)))), WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(sizeof(T)))))), - WASM_STORE_MEM(memType, WASM_ZERO, WASM_GET_LOCAL(kAccum)), - WASM_GET_LOCAL(0))); + WASM_STORE_MEM(memType, WASM_ZERO, WASM_GET_LOCAL(kAccum)), + WASM_GET_LOCAL(0)); r.Call(static_cast(sizeof(T) * (size - 1))); return module.ReadMemory(&memory[0]); } @@ -1649,19 +1791,20 @@ WASM_EXEC_TEST(Build_Wasm_Infinite_Loop_effect) { WasmRunner r(&module, MachineType::Int32()); // Only build the graph and compile, don't run. - BUILD(r, WASM_LOOP(WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO))); + BUILD(r, WASM_LOOP(WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO), WASM_DROP), + WASM_ZERO); } WASM_EXEC_TEST(Unreachable0a) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_BRV(0, WASM_I8(9)), RET(WASM_GET_LOCAL(0)))); + BUILD(r, WASM_BLOCK_I(WASM_BRV(0, WASM_I8(9)), RET(WASM_GET_LOCAL(0)))); CHECK_EQ(9, r.Call(0)); CHECK_EQ(9, r.Call(1)); } WASM_EXEC_TEST(Unreachable0b) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_BRV(0, WASM_I8(7)), WASM_UNREACHABLE)); + BUILD(r, WASM_BLOCK_I(WASM_BRV(0, WASM_I8(7)), WASM_UNREACHABLE)); CHECK_EQ(7, r.Call(0)); CHECK_EQ(7, r.Call(1)); } @@ -1694,31 +1837,32 @@ TEST(Build_Wasm_UnreachableIf2) { WASM_EXEC_TEST(Unreachable_Load) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_BRV(0, WASM_GET_LOCAL(0)), - WASM_LOAD_MEM(MachineType::Int8(), WASM_GET_LOCAL(0)))); + BUILD(r, WASM_BLOCK_I(WASM_BRV(0, WASM_GET_LOCAL(0)), + WASM_LOAD_MEM(MachineType::Int8(), WASM_GET_LOCAL(0)))); CHECK_EQ(11, r.Call(11)); CHECK_EQ(21, r.Call(21)); } WASM_EXEC_TEST(Infinite_Loop_not_taken1) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_IF(WASM_GET_LOCAL(0), WASM_INFINITE_LOOP), WASM_I8(45))); + BUILD(r, WASM_IF(WASM_GET_LOCAL(0), WASM_INFINITE_LOOP), WASM_I8(45)); // Run the code, but don't go into the infinite loop. CHECK_EQ(45, r.Call(0)); } WASM_EXEC_TEST(Infinite_Loop_not_taken2) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B1(WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_BRV(1, WASM_I8(45)), - WASM_INFINITE_LOOP))); + BUILD(r, + WASM_BLOCK_I(WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_BRV(1, WASM_I8(45)), + WASM_INFINITE_LOOP))); // Run the code, but don't go into the infinite loop. CHECK_EQ(45, r.Call(1)); } WASM_EXEC_TEST(Infinite_Loop_not_taken2_brif) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, - B2(WASM_BRV_IF(0, WASM_I8(45), WASM_GET_LOCAL(0)), WASM_INFINITE_LOOP)); + BUILD(r, WASM_BLOCK_I(WASM_BRV_IF(0, WASM_I8(45), WASM_GET_LOCAL(0)), + WASM_INFINITE_LOOP)); // Run the code, but don't go into the infinite loop. CHECK_EQ(45, r.Call(1)); } @@ -1821,8 +1965,9 @@ WASM_EXEC_TEST(Int32Global) { int32_t* global = module.AddGlobal(kAstI32); WasmRunner r(&module, MachineType::Int32()); // global = global + p0 - BUILD(r, WASM_SET_GLOBAL( - 0, WASM_I32_ADD(WASM_GET_GLOBAL(0), WASM_GET_LOCAL(0)))); + BUILD(r, + WASM_SET_GLOBAL(0, WASM_I32_ADD(WASM_GET_GLOBAL(0), WASM_GET_LOCAL(0))), + WASM_ZERO); *global = 116; for (int i = 9; i < 444444; i += 111111) { @@ -1843,7 +1988,8 @@ WASM_EXEC_TEST(Int32Globals_DontAlias) { // global = global + p0 WasmRunner r(&module, MachineType::Int32()); BUILD(r, WASM_SET_GLOBAL( - g, WASM_I32_ADD(WASM_GET_GLOBAL(g), WASM_GET_LOCAL(0)))); + g, WASM_I32_ADD(WASM_GET_GLOBAL(g), WASM_GET_LOCAL(0))), + WASM_GET_GLOBAL(g)); // Check that reading/writing global number {g} doesn't alter the others. *globals[g] = 116 * g; @@ -1851,7 +1997,8 @@ WASM_EXEC_TEST(Int32Globals_DontAlias) { for (int i = 9; i < 444444; i += 111113) { int32_t sum = *globals[g] + i; for (int j = 0; j < kNumGlobals; ++j) before[j] = *globals[j]; - r.Call(i); + int32_t result = r.Call(i); + CHECK_EQ(sum, result); for (int j = 0; j < kNumGlobals; ++j) { int32_t expected = j == g ? sum : before[j]; CHECK_EQ(expected, *globals[j]); @@ -1865,10 +2012,10 @@ WASM_EXEC_TEST(Float32Global) { float* global = module.AddGlobal(kAstF32); WasmRunner r(&module, MachineType::Int32()); // global = global + p0 - BUILD(r, B2(WASM_SET_GLOBAL( - 0, WASM_F32_ADD(WASM_GET_GLOBAL(0), - WASM_F32_SCONVERT_I32(WASM_GET_LOCAL(0)))), - WASM_ZERO)); + BUILD(r, WASM_SET_GLOBAL( + 0, WASM_F32_ADD(WASM_GET_GLOBAL(0), + WASM_F32_SCONVERT_I32(WASM_GET_LOCAL(0)))), + WASM_ZERO); *global = 1.25; for (int i = 9; i < 4444; i += 1111) { @@ -1883,10 +2030,10 @@ WASM_EXEC_TEST(Float64Global) { double* global = module.AddGlobal(kAstF64); WasmRunner r(&module, MachineType::Int32()); // global = global + p0 - BUILD(r, B2(WASM_SET_GLOBAL( - 0, WASM_F64_ADD(WASM_GET_GLOBAL(0), - WASM_F64_SCONVERT_I32(WASM_GET_LOCAL(0)))), - WASM_ZERO)); + BUILD(r, WASM_SET_GLOBAL( + 0, WASM_F64_ADD(WASM_GET_GLOBAL(0), + WASM_F64_SCONVERT_I32(WASM_GET_LOCAL(0)))), + WASM_ZERO); *global = 1.25; for (int i = 9; i < 4444; i += 1111) { @@ -1908,14 +2055,11 @@ WASM_EXEC_TEST(MixedGlobals) { WasmRunner r(&module, MachineType::Int32()); - BUILD( - r, - WASM_BLOCK( - WASM_SET_GLOBAL(1, WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)), - WASM_SET_GLOBAL(2, WASM_LOAD_MEM(MachineType::Uint32(), WASM_ZERO)), - WASM_SET_GLOBAL(3, WASM_LOAD_MEM(MachineType::Float32(), WASM_ZERO)), - WASM_SET_GLOBAL(4, WASM_LOAD_MEM(MachineType::Float64(), WASM_ZERO)), - WASM_ZERO)); + BUILD(r, WASM_SET_GLOBAL(1, WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)), + WASM_SET_GLOBAL(2, WASM_LOAD_MEM(MachineType::Uint32(), WASM_ZERO)), + WASM_SET_GLOBAL(3, WASM_LOAD_MEM(MachineType::Float32(), WASM_ZERO)), + WASM_SET_GLOBAL(4, WASM_LOAD_MEM(MachineType::Float64(), WASM_ZERO)), + WASM_ZERO); memory[0] = 0xaa; memory[1] = 0xcc; @@ -1964,8 +2108,8 @@ WASM_EXEC_TEST(CallF32StackParameter) { // Build the calling function. WasmRunner r(&module); - BUILD(r, WASM_CALL_FUNCTIONN( - 19, index, WASM_F32(1.0f), WASM_F32(2.0f), WASM_F32(4.0f), + BUILD(r, WASM_CALL_FUNCTION( + index, WASM_F32(1.0f), WASM_F32(2.0f), WASM_F32(4.0f), WASM_F32(8.0f), WASM_F32(16.0f), WASM_F32(32.0f), WASM_F32(64.0f), WASM_F32(128.0f), WASM_F32(256.0f), WASM_F32(1.5f), WASM_F32(2.5f), WASM_F32(4.5f), WASM_F32(8.5f), @@ -1988,13 +2132,13 @@ WASM_EXEC_TEST(CallF64StackParameter) { // Build the calling function. WasmRunner r(&module); - BUILD(r, WASM_CALL_FUNCTIONN(19, index, WASM_F64(1.0), WASM_F64(2.0), - WASM_F64(4.0), WASM_F64(8.0), WASM_F64(16.0), - WASM_F64(32.0), WASM_F64(64.0), WASM_F64(128.0), - WASM_F64(256.0), WASM_F64(1.5), WASM_F64(2.5), - WASM_F64(4.5), WASM_F64(8.5), WASM_F64(16.5), - WASM_F64(32.5), WASM_F64(64.5), WASM_F64(128.5), - WASM_F64(256.5), WASM_F64(512.5))); + BUILD(r, WASM_CALL_FUNCTION(index, WASM_F64(1.0), WASM_F64(2.0), + WASM_F64(4.0), WASM_F64(8.0), WASM_F64(16.0), + WASM_F64(32.0), WASM_F64(64.0), WASM_F64(128.0), + WASM_F64(256.0), WASM_F64(1.5), WASM_F64(2.5), + WASM_F64(4.5), WASM_F64(8.5), WASM_F64(16.5), + WASM_F64(32.5), WASM_F64(64.5), WASM_F64(128.5), + WASM_F64(256.5), WASM_F64(512.5))); float result = r.Call(); CHECK_EQ(256.5, result); @@ -2035,7 +2179,7 @@ WASM_EXEC_TEST(Call_Int32Add) { // Build the caller function. WasmRunner r(&module, MachineType::Int32(), MachineType::Int32()); - BUILD(r, WASM_CALL_FUNCTION2(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); + BUILD(r, WASM_CALL_FUNCTION(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); FOR_INT32_INPUTS(i) { FOR_INT32_INPUTS(j) { @@ -2057,7 +2201,7 @@ WASM_EXEC_TEST(Call_Float32Sub) { // Builder the caller function. WasmRunner r(&module, MachineType::Float32(), MachineType::Float32()); - BUILD(r, WASM_CALL_FUNCTION2(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); + BUILD(r, WASM_CALL_FUNCTION(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); FOR_FLOAT32_INPUTS(i) { FOR_FLOAT32_INPUTS(j) { CHECK_FLOAT_EQ(*i - *j, r.Call(*i, *j)); } @@ -2069,13 +2213,11 @@ WASM_EXEC_TEST(Call_Float64Sub) { double* memory = module.AddMemoryElems(16); WasmRunner r(&module); - BUILD(r, - WASM_BLOCK(WASM_STORE_MEM( - MachineType::Float64(), WASM_ZERO, - WASM_F64_SUB( - WASM_LOAD_MEM(MachineType::Float64(), WASM_ZERO), - WASM_LOAD_MEM(MachineType::Float64(), WASM_I8(8)))), - WASM_I8(107))); + BUILD(r, WASM_STORE_MEM( + MachineType::Float64(), WASM_ZERO, + WASM_F64_SUB(WASM_LOAD_MEM(MachineType::Float64(), WASM_ZERO), + WASM_LOAD_MEM(MachineType::Float64(), WASM_I8(8)))), + WASM_I8(107)); FOR_FLOAT64_INPUTS(i) { FOR_FLOAT64_INPUTS(j) { @@ -2149,8 +2291,7 @@ static void Run_WasmMixedCall_N(WasmExecutionMode execution_mode, int start) { } // Call the selector function. - ADD_CODE(code, kExprCallFunction, static_cast(num_params), - static_cast(index)); + ADD_CODE(code, kExprCallFunction, static_cast(index)); // Store the result in memory. ADD_CODE(code, @@ -2192,71 +2333,149 @@ WASM_EXEC_TEST(AddCall) { WasmRunner r(&module, MachineType::Int32()); byte local = r.AllocateLocal(kAstI32); - BUILD(r, B2(WASM_SET_LOCAL(local, WASM_I8(99)), - WASM_I32_ADD( - WASM_CALL_FUNCTION2(t1.function_index(), WASM_GET_LOCAL(0), - WASM_GET_LOCAL(0)), - WASM_CALL_FUNCTION2(t1.function_index(), WASM_GET_LOCAL(1), - WASM_GET_LOCAL(local))))); + BUILD(r, WASM_SET_LOCAL(local, WASM_I8(99)), + WASM_I32_ADD(WASM_CALL_FUNCTION(t1.function_index(), WASM_GET_LOCAL(0), + WASM_GET_LOCAL(0)), + WASM_CALL_FUNCTION(t1.function_index(), WASM_GET_LOCAL(1), + WASM_GET_LOCAL(local)))); CHECK_EQ(198, r.Call(0)); CHECK_EQ(200, r.Call(1)); CHECK_EQ(100, r.Call(-49)); } -WASM_EXEC_TEST(CountDown_expr) { - WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, WASM_LOOP( - WASM_IF(WASM_NOT(WASM_GET_LOCAL(0)), - WASM_BREAKV(1, WASM_GET_LOCAL(0))), - WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(1))), - WASM_CONTINUE(0))); - CHECK_EQ(0, r.Call(1)); - CHECK_EQ(0, r.Call(10)); - CHECK_EQ(0, r.Call(100)); +WASM_EXEC_TEST(MultiReturnSub) { + FLAG_wasm_mv_prototype = true; + LocalType storage[] = {kAstI32, kAstI32, kAstI32, kAstI32}; + FunctionSig sig_ii_ii(2, 2, storage); + TestingModule module(execution_mode); + WasmFunctionCompiler t1(&sig_ii_ii, &module); + BUILD(t1, WASM_GET_LOCAL(1), WASM_GET_LOCAL(0)); + t1.CompileAndAdd(); + + WasmRunner r(&module, MachineType::Int32(), MachineType::Int32()); + BUILD(r, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), kExprCallFunction, 0, + kExprI32Sub); + + FOR_INT32_INPUTS(i) { + FOR_INT32_INPUTS(j) { + int32_t expected = static_cast(static_cast(*j) - + static_cast(*i)); + CHECK_EQ(expected, r.Call(*i, *j)); + } + } +} + +template +void RunMultiReturnSelect(WasmExecutionMode execution_mode, LocalType type, + const T* inputs) { + FLAG_wasm_mv_prototype = true; + LocalType storage[] = {type, type, type, type, type, type}; + const size_t kNumReturns = 2; + const size_t kNumParams = arraysize(storage) - kNumReturns; + FunctionSig sig(kNumReturns, kNumParams, storage); + + for (size_t i = 0; i < kNumParams; i++) { + for (size_t j = 0; j < kNumParams; j++) { + for (int k = 0; k < 2; k++) { + TestingModule module(execution_mode); + WasmFunctionCompiler r1(&sig, &module); + + BUILD(r1, WASM_GET_LOCAL(i), WASM_GET_LOCAL(j)); + r1.CompileAndAdd(); + + MachineType machine_type = WasmOpcodes::MachineTypeFor(type); + WasmRunner r2(&module, machine_type, machine_type, machine_type, + machine_type); + + if (k == 0) { + BUILD(r2, WASM_CALL_FUNCTION(r1.function_index(), WASM_GET_LOCAL(0), + WASM_GET_LOCAL(1), WASM_GET_LOCAL(2), + WASM_GET_LOCAL(3)), + WASM_DROP); + } else { + BUILD(r2, WASM_CALL_FUNCTION(r1.function_index(), WASM_GET_LOCAL(0), + WASM_GET_LOCAL(1), WASM_GET_LOCAL(2), + WASM_GET_LOCAL(3)), + kExprSetLocal, 0, WASM_DROP, WASM_GET_LOCAL(0)); + } + + T expected = inputs[k == 0 ? i : j]; + CHECK_EQ(expected, r2.Call(inputs[0], inputs[1], inputs[2], inputs[3])); + } + } + } +} + +WASM_EXEC_TEST(MultiReturnSelect_i32) { + static const int32_t inputs[] = {3333333, 4444444, -55555555, -7777777}; + RunMultiReturnSelect(execution_mode, kAstI32, inputs); +} + +WASM_EXEC_TEST(MultiReturnSelect_f32) { + static const float inputs[] = {33.33333f, 444.4444f, -55555.555f, -77777.77f}; + RunMultiReturnSelect(execution_mode, kAstF32, inputs); +} + +WASM_EXEC_TEST(MultiReturnSelect_i64) { +#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 + // TODO(titzer): implement int64-lowering for multiple return values + static const int64_t inputs[] = {33333338888, 44444446666, -555555553333, + -77777771111}; + RunMultiReturnSelect(execution_mode, kAstI64, inputs); +#endif +} + +WASM_EXEC_TEST(MultiReturnSelect_f64) { + static const double inputs[] = {3.333333, 44444.44, -55.555555, -7777.777}; + RunMultiReturnSelect(execution_mode, kAstF64, inputs); } WASM_EXEC_TEST(ExprBlock2a) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_IF(WASM_GET_LOCAL(0), WASM_BRV(1, WASM_I8(1))), WASM_I8(1))); + BUILD(r, WASM_BLOCK_I(WASM_IF(WASM_GET_LOCAL(0), WASM_BRV(1, WASM_I8(1))), + WASM_I8(1))); CHECK_EQ(1, r.Call(0)); CHECK_EQ(1, r.Call(1)); } WASM_EXEC_TEST(ExprBlock2b) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_IF(WASM_GET_LOCAL(0), WASM_BRV(1, WASM_I8(1))), WASM_I8(2))); + BUILD(r, WASM_BLOCK_I(WASM_IF(WASM_GET_LOCAL(0), WASM_BRV(1, WASM_I8(1))), + WASM_I8(2))); CHECK_EQ(2, r.Call(0)); CHECK_EQ(1, r.Call(1)); } WASM_EXEC_TEST(ExprBlock2c) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_BRV_IF(0, WASM_I8(1), WASM_GET_LOCAL(0)), WASM_I8(1))); + BUILD(r, WASM_BLOCK_I(WASM_BRV_IFD(0, WASM_I8(1), WASM_GET_LOCAL(0)), + WASM_I8(1))); CHECK_EQ(1, r.Call(0)); CHECK_EQ(1, r.Call(1)); } WASM_EXEC_TEST(ExprBlock2d) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B2(WASM_BRV_IF(0, WASM_I8(1), WASM_GET_LOCAL(0)), WASM_I8(2))); + BUILD(r, WASM_BLOCK_I(WASM_BRV_IFD(0, WASM_I8(1), WASM_GET_LOCAL(0)), + WASM_I8(2))); CHECK_EQ(2, r.Call(0)); CHECK_EQ(1, r.Call(1)); } WASM_EXEC_TEST(ExprBlock_ManualSwitch) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, WASM_BLOCK(WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(1)), - WASM_BRV(1, WASM_I8(11))), - WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(2)), - WASM_BRV(1, WASM_I8(12))), - WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(3)), - WASM_BRV(1, WASM_I8(13))), - WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(4)), - WASM_BRV(1, WASM_I8(14))), - WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(5)), - WASM_BRV(1, WASM_I8(15))), - WASM_I8(99))); + BUILD(r, WASM_BLOCK_I(WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(1)), + WASM_BRV(1, WASM_I8(11))), + WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(2)), + WASM_BRV(1, WASM_I8(12))), + WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(3)), + WASM_BRV(1, WASM_I8(13))), + WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(4)), + WASM_BRV(1, WASM_I8(14))), + WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(5)), + WASM_BRV(1, WASM_I8(15))), + WASM_I8(99))); CHECK_EQ(99, r.Call(0)); CHECK_EQ(11, r.Call(1)); CHECK_EQ(12, r.Call(2)); @@ -2268,17 +2487,18 @@ WASM_EXEC_TEST(ExprBlock_ManualSwitch) { WASM_EXEC_TEST(ExprBlock_ManualSwitch_brif) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, WASM_BLOCK(WASM_BRV_IF(0, WASM_I8(11), + BUILD(r, + WASM_BLOCK_I(WASM_BRV_IFD(0, WASM_I8(11), WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(1))), - WASM_BRV_IF(0, WASM_I8(12), + WASM_BRV_IFD(0, WASM_I8(12), WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(2))), - WASM_BRV_IF(0, WASM_I8(13), + WASM_BRV_IFD(0, WASM_I8(13), WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(3))), - WASM_BRV_IF(0, WASM_I8(14), + WASM_BRV_IFD(0, WASM_I8(14), WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(4))), - WASM_BRV_IF(0, WASM_I8(15), + WASM_BRV_IFD(0, WASM_I8(15), WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(5))), - WASM_I8(99))); + WASM_I8(99))); CHECK_EQ(99, r.Call(0)); CHECK_EQ(11, r.Call(1)); CHECK_EQ(12, r.Call(2)); @@ -2288,14 +2508,14 @@ WASM_EXEC_TEST(ExprBlock_ManualSwitch_brif) { CHECK_EQ(99, r.Call(6)); } -WASM_EXEC_TEST(nested_ifs) { +WASM_EXEC_TEST(If_nested) { WasmRunner r(execution_mode, MachineType::Int32(), MachineType::Int32()); - BUILD(r, WASM_IF_ELSE( + BUILD(r, WASM_IF_ELSE_I( WASM_GET_LOCAL(0), - WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_I8(11), WASM_I8(12)), - WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_I8(13), WASM_I8(14)))); + WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_I8(11), WASM_I8(12)), + WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_I8(13), WASM_I8(14)))); CHECK_EQ(11, r.Call(1, 1)); CHECK_EQ(12, r.Call(1, 0)); @@ -2306,8 +2526,9 @@ WASM_EXEC_TEST(nested_ifs) { WASM_EXEC_TEST(ExprBlock_if) { WasmRunner r(execution_mode, MachineType::Int32()); - BUILD(r, B1(WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_BRV(0, WASM_I8(11)), - WASM_BRV(1, WASM_I8(14))))); + BUILD(r, + WASM_BLOCK_I(WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_BRV(0, WASM_I8(11)), + WASM_BRV(1, WASM_I8(14))))); CHECK_EQ(11, r.Call(1)); CHECK_EQ(14, r.Call(0)); @@ -2317,29 +2538,12 @@ WASM_EXEC_TEST(ExprBlock_nested_ifs) { WasmRunner r(execution_mode, MachineType::Int32(), MachineType::Int32()); - BUILD(r, WASM_BLOCK(WASM_IF_ELSE( + BUILD(r, WASM_BLOCK_I(WASM_IF_ELSE_I( WASM_GET_LOCAL(0), - WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_BRV(0, WASM_I8(11)), - WASM_BRV(1, WASM_I8(12))), - WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_BRV(0, WASM_I8(13)), - WASM_BRV(1, WASM_I8(14)))))); - - CHECK_EQ(11, r.Call(1, 1)); - CHECK_EQ(12, r.Call(1, 0)); - CHECK_EQ(13, r.Call(0, 1)); - CHECK_EQ(14, r.Call(0, 0)); -} - -WASM_EXEC_TEST(ExprLoop_nested_ifs) { - WasmRunner r(execution_mode, MachineType::Int32(), - MachineType::Int32()); - - BUILD(r, WASM_LOOP(WASM_IF_ELSE( - WASM_GET_LOCAL(0), - WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_BRV(1, WASM_I8(11)), - WASM_BRV(3, WASM_I8(12))), - WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_BRV(1, WASM_I8(13)), - WASM_BRV(3, WASM_I8(14)))))); + WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_BRV(0, WASM_I8(11)), + WASM_BRV(1, WASM_I8(12))), + WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_BRV(0, WASM_I8(13)), + WASM_BRV(1, WASM_I8(14)))))); CHECK_EQ(11, r.Call(1, 1)); CHECK_EQ(12, r.Call(1, 0)); @@ -2654,11 +2858,11 @@ static void CompileCallIndirectMany(LocalType param) { WasmFunctionCompiler t(sig, &module); std::vector code; - ADD_CODE(code, kExprI8Const, 0); for (byte p = 0; p < num_params; ++p) { ADD_CODE(code, kExprGetLocal, p); } - ADD_CODE(code, kExprCallIndirect, static_cast(num_params), 1); + ADD_CODE(code, kExprI8Const, 0); + ADD_CODE(code, kExprCallIndirect, 1); t.Build(&code[0], &code[0] + code.size()); t.Compile(); @@ -2674,7 +2878,8 @@ TEST(Compile_Wasm_CallIndirect_Many_f64) { CompileCallIndirectMany(kAstF64); } WASM_EXEC_TEST(Int32RemS_dead) { WasmRunner r(execution_mode, MachineType::Int32(), MachineType::Int32()); - BUILD(r, WASM_I32_REMS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), WASM_ZERO); + BUILD(r, WASM_I32_REMS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), WASM_DROP, + WASM_ZERO); const int32_t kMin = std::numeric_limits::min(); CHECK_EQ(0, r.Call(133, 100)); CHECK_EQ(0, r.Call(kMin, -1)); diff --git a/test/cctest/wasm/test-wasm-function-name-table.cc b/test/cctest/wasm/test-wasm-function-name-table.cc index 1ae78dcb4e..9a4394204b 100644 --- a/test/cctest/wasm/test-wasm-function-name-table.cc +++ b/test/cctest/wasm/test-wasm-function-name-table.cc @@ -41,9 +41,9 @@ void testFunctionNameTable(Vector> names) { name.start() + name.length()); // Make every second function name null-terminated. if (func_index % 2) all_names.push_back('\0'); - module.functions.push_back({nullptr, 0, 0, - static_cast(name_offset), - static_cast(name.length()), 0, 0}); + module.functions.push_back( + {nullptr, 0, 0, static_cast(name_offset), + static_cast(name.length()), 0, 0, false, false}); ++func_index; } diff --git a/test/cctest/wasm/test-wasm-trap-position.cc b/test/cctest/wasm/test-wasm-trap-position.cc index 30f5d48a07..f32494bd60 100644 --- a/test/cctest/wasm/test-wasm-trap-position.cc +++ b/test/cctest/wasm/test-wasm-trap-position.cc @@ -104,8 +104,9 @@ TEST(IllegalLoad) { WasmFunctionCompiler comp1(sigs.v_v(), &module, ArrayVector("mem_oob")); // Set the execution context, such that a runtime error can be thrown. comp1.SetModuleContext(); - BUILD(comp1, WASM_IF(WASM_ONE, - WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(-3)))); + BUILD(comp1, WASM_IF(WASM_ONE, WASM_SEQ(WASM_LOAD_MEM(MachineType::Int32(), + WASM_I32V_1(-3)), + WASM_DROP))); uint32_t wasm_index = comp1.CompileAndAdd(); WasmFunctionCompiler comp2(sigs.v_v(), &module, ArrayVector("call_mem_oob")); @@ -131,7 +132,7 @@ TEST(IllegalLoad) { // The column is 1-based, so add 1 to the actual byte offset. ExceptionInfo expected_exceptions[] = { - {"", static_cast(wasm_index), 7}, // -- + {"", static_cast(wasm_index), 8}, // -- {"", static_cast(wasm_index_2), 3}, // -- {"callFn", 1, 24} // -- }; diff --git a/test/cctest/wasm/wasm-run-utils.h b/test/cctest/wasm/wasm-run-utils.h index 1b1467f9b9..93fcb89dba 100644 --- a/test/cctest/wasm/wasm-run-utils.h +++ b/test/cctest/wasm/wasm-run-utils.h @@ -181,7 +181,7 @@ class TestingModule : public ModuleEnv { module_.functions.reserve(kMaxFunctions); } uint32_t index = static_cast(module->functions.size()); - module_.functions.push_back({sig, index, 0, 0, 0, 0, 0}); + module_.functions.push_back({sig, index, 0, 0, 0, 0, 0, false, false}); instance->function_code.push_back(code); if (interpreter_) { const WasmFunction* function = &module->functions.back(); @@ -230,7 +230,7 @@ class TestingModule : public ModuleEnv { void AddIndirectFunctionTable(uint16_t* functions, uint32_t table_size) { module_.function_tables.push_back( - {table_size, table_size, std::vector()}); + {table_size, table_size, std::vector(), false, false}); for (uint32_t i = 0; i < table_size; ++i) { module_.function_tables.back().values.push_back(functions[i]); } @@ -267,7 +267,8 @@ class TestingModule : public ModuleEnv { const WasmGlobal* AddGlobal(LocalType type) { byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type)); global_offset = (global_offset + size - 1) & ~(size - 1); // align - module_.globals.push_back({0, 0, type, global_offset, false}); + module_.globals.push_back( + {type, true, NO_INIT, global_offset, false, false}); global_offset += size; // limit number of globals. CHECK_LT(global_offset, kMaxGlobalsSize); @@ -283,6 +284,13 @@ inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module, DecodeResult result = BuildTFGraph(zone->allocator(), &builder, module, sig, start, end); if (result.failed()) { + if (!FLAG_trace_wasm_decoder) { + // Retry the compilation with the tracing flag on, to help in debugging. + FLAG_trace_wasm_decoder = true; + result = + BuildTFGraph(zone->allocator(), &builder, module, sig, start, end); + } + ptrdiff_t pc = result.error_pc - result.start; ptrdiff_t pt = result.error_pt - result.start; std::ostringstream str; diff --git a/test/common/wasm/wasm-module-runner.cc b/test/common/wasm/wasm-module-runner.cc index 7bbacc77c1..809425bfe3 100644 --- a/test/common/wasm/wasm-module-runner.cc +++ b/test/common/wasm/wasm-module-runner.cc @@ -67,8 +67,8 @@ const Handle InstantiateModuleForTesting(Isolate* isolate, ModuleOrigin::kWasmOrigin); if (module_object.is_null()) return Handle::null(); MaybeHandle maybe_instance = WasmModule::Instantiate( - isolate, module_object.ToHandleChecked(), Handle::null(), - Handle::null()); + isolate, thrower, module_object.ToHandleChecked(), + Handle::null(), Handle::null()); Handle instance; if (!maybe_instance.ToHandle(&instance)) { return Handle::null(); diff --git a/test/fuzzer/wasm-code.cc b/test/fuzzer/wasm-code.cc index 75ce838fd2..f463ea8bb1 100644 --- a/test/fuzzer/wasm-code.cc +++ b/test/fuzzer/wasm-code.cc @@ -42,9 +42,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { WasmModuleBuilder builder(&zone); - uint16_t f1_index = builder.AddFunction(); - WasmFunctionBuilder* f = builder.FunctionAt(f1_index); - f->SetSignature(sigs.i_iii()); + v8::internal::wasm::WasmFunctionBuilder* f = + builder.AddFunction(sigs.i_iii()); f->EmitCode(data, static_cast(size)); f->SetExported(); f->SetName("main", 4); diff --git a/test/fuzzer/wasm-data-section.cc b/test/fuzzer/wasm-data-section.cc index 75d4043a9d..2fbdbcd906 100644 --- a/test/fuzzer/wasm-data-section.cc +++ b/test/fuzzer/wasm-data-section.cc @@ -5,6 +5,5 @@ #include "test/fuzzer/wasm-section-fuzzers.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::DataSegments, - data, size); + return fuzz_wasm_section(v8::internal::wasm::kDataSectionCode, data, size); } diff --git a/test/fuzzer/wasm-function-sigs-section.cc b/test/fuzzer/wasm-function-sigs-section.cc index 1f685f6ea4..a0b66e12d0 100644 --- a/test/fuzzer/wasm-function-sigs-section.cc +++ b/test/fuzzer/wasm-function-sigs-section.cc @@ -5,6 +5,6 @@ #include "test/fuzzer/wasm-section-fuzzers.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - return fuzz_wasm_section( - v8::internal::wasm::WasmSection::Code::FunctionSignatures, data, size); + return fuzz_wasm_section(v8::internal::wasm::kFunctionSectionCode, data, + size); } diff --git a/test/fuzzer/wasm-globals-section.cc b/test/fuzzer/wasm-globals-section.cc index b530a9a2bb..a58ef0f2b5 100644 --- a/test/fuzzer/wasm-globals-section.cc +++ b/test/fuzzer/wasm-globals-section.cc @@ -5,6 +5,5 @@ #include "test/fuzzer/wasm-section-fuzzers.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Globals, data, - size); + return fuzz_wasm_section(v8::internal::wasm::kGlobalSectionCode, data, size); } diff --git a/test/fuzzer/wasm-imports-section.cc b/test/fuzzer/wasm-imports-section.cc index d7e01dd21f..d6513e59dc 100644 --- a/test/fuzzer/wasm-imports-section.cc +++ b/test/fuzzer/wasm-imports-section.cc @@ -5,6 +5,5 @@ #include "test/fuzzer/wasm-section-fuzzers.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::ImportTable, - data, size); + return fuzz_wasm_section(v8::internal::wasm::kImportSectionCode, data, size); } diff --git a/test/fuzzer/wasm-memory-section.cc b/test/fuzzer/wasm-memory-section.cc index 012cf657e5..77065f1729 100644 --- a/test/fuzzer/wasm-memory-section.cc +++ b/test/fuzzer/wasm-memory-section.cc @@ -5,6 +5,5 @@ #include "test/fuzzer/wasm-section-fuzzers.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Memory, data, - size); + return fuzz_wasm_section(v8::internal::wasm::kMemorySectionCode, data, size); } diff --git a/test/fuzzer/wasm-names-section.cc b/test/fuzzer/wasm-names-section.cc index 9a7989dac1..01846823ff 100644 --- a/test/fuzzer/wasm-names-section.cc +++ b/test/fuzzer/wasm-names-section.cc @@ -5,6 +5,6 @@ #include "test/fuzzer/wasm-section-fuzzers.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Names, data, - size); + // TODO(titzer): Names section requires a preceding function section. + return fuzz_wasm_section(v8::internal::wasm::kNameSectionCode, data, size); } diff --git a/test/fuzzer/wasm-section-fuzzers.cc b/test/fuzzer/wasm-section-fuzzers.cc index 63bad10bee..b9be28df94 100644 --- a/test/fuzzer/wasm-section-fuzzers.cc +++ b/test/fuzzer/wasm-section-fuzzers.cc @@ -15,7 +15,7 @@ using namespace v8::internal::wasm; -int fuzz_wasm_section(WasmSection::Code section, const uint8_t* data, +int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data, size_t size) { v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get(); v8::Isolate* isolate = support->GetIsolate(); @@ -38,12 +38,18 @@ int fuzz_wasm_section(WasmSection::Code section, const uint8_t* data, ZoneBuffer buffer(&zone); buffer.write_u32(kWasmMagic); buffer.write_u32(kWasmVersion); - const char* name = WasmSection::getName(section); - size_t length = WasmSection::getNameLength(section); - buffer.write_size(length); // Section name string size. - buffer.write(reinterpret_cast(name), length); - buffer.write_u32v(static_cast(size)); - buffer.write(data, size); + if (section == kNameSectionCode) { + buffer.write_u8(kUnknownSectionCode); + buffer.write_size(size + kNameStringLength + 1); + buffer.write_u8(kNameStringLength); + buffer.write(reinterpret_cast(kNameString), + kNameStringLength); + buffer.write(data, size); + } else { + buffer.write_u8(section); + buffer.write_size(size); + buffer.write(data, size); + } ErrorThrower thrower(i_isolate, "decoder"); diff --git a/test/fuzzer/wasm-section-fuzzers.h b/test/fuzzer/wasm-section-fuzzers.h index f09544112e..a28ada134e 100644 --- a/test/fuzzer/wasm-section-fuzzers.h +++ b/test/fuzzer/wasm-section-fuzzers.h @@ -10,7 +10,7 @@ #include "src/wasm/wasm-module.h" -int fuzz_wasm_section(v8::internal::wasm::WasmSection::Code section, +int fuzz_wasm_section(v8::internal::wasm::WasmSectionCode section, const uint8_t* data, size_t size); #endif // WASM_SECTION_FUZZERS_H_ diff --git a/test/fuzzer/wasm-types-section.cc b/test/fuzzer/wasm-types-section.cc index 40cadb5710..7d5fe65277 100644 --- a/test/fuzzer/wasm-types-section.cc +++ b/test/fuzzer/wasm-types-section.cc @@ -5,6 +5,5 @@ #include "test/fuzzer/wasm-section-fuzzers.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Signatures, - data, size); + return fuzz_wasm_section(v8::internal::wasm::kTypeSectionCode, data, size); } diff --git a/test/mjsunit/wasm/asm-wasm-expr.js b/test/mjsunit/wasm/asm-wasm-expr.js new file mode 100644 index 0000000000..3b20826fe7 --- /dev/null +++ b/test/mjsunit/wasm/asm-wasm-expr.js @@ -0,0 +1,151 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --validate-asm --allow-natives-syntax + +var selectedTest = undefined; +//selectedTest = 16; + +function skip(a) { + return selectedTest != undefined ? a != selectedTest : false; +} + +const assign_in_stmt = [ + "if (E) =", + "if (=) E", + "if (E) E; else =", + "for (=; E; S) S", + "for (E; =; S) S", + "for (E; E; =) E", + "for (E; E; E) =", + "do { = } while(E)", + "do { S } while (=)", +]; +const assign_in_expr = [ + "i32_func(=)", + "(=) ? E : E", + "E ? (=) : E", + "E ? E : (=)", + "(=) + E", + "E + (=)", + "imul(=, E)", + "imul(E, =)", + "~(=)", + "(=) | 0", + "(=), E", + "E, (=)", + "E, E, (=)", + "E, (=), E", + "(=), E, E", +]; + +const stdlib = { + Math: Math, + Int8Array: Int8Array, + Int16Array: Int16Array, + Int32Array: Int32Array, + Uint8Array: Uint8Array, + Uint16Array: Uint16Array, + Uint32Array: Uint32Array, + Float32Array: Float32Array, + Float64Array: Float64Array, +}; + +const buffer = new ArrayBuffer(65536); + +// Template for a module. +function MODULE_TEMPLATE(stdlib, foreign, buffer) { + "use asm"; + var imul = stdlib.Math.imul; + var fround = stdlib.Math.fround; + var M = new stdlib.Int32Array(buffer); + var G = 0; + + function void_func() {} + function i32_func(a) { + a = a | 0; + return a | 0; + } + + FUNC_DECL + return {main: main}; +} + +// Template for main function. +{ + function main(i32, f32, f64) { + i32 = i32 | 0; + f32 = fround(f32); + f64 = +f64; + FUNC_BODY + } +} + +function RunAsmJsTest(asmfunc, expect) { + var asm_source = asmfunc.toString(); + var nonasm_source = asm_source.replace(new RegExp("use asm"), ""); + + print("Testing " + asmfunc.name + " (js)..."); + var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer); + expect(js_module); + + print("Testing " + asmfunc.name + " (asm.js)..."); + var asm_module = asmfunc(stdlib, {}, buffer); + assertTrue(%IsAsmWasmCode(asmfunc)); + expect(asm_module); +} + +var test = 0; + +function DoTheTests(expr, assign, stmt) { + // ==== Expression assignment tests ======================================== + for (let e of assign_in_expr) { + if (skip(++test)) continue; + var orig = e; + e = e.replace(/=/g, assign); + e = e.replace(/E/g, expr); + e = e.replace(/S/g, stmt); + var str = main.toString().replace("FUNC_BODY", "return (" + e + ") | 0;"); + var asm_source = MODULE_TEMPLATE.toString().replace("FUNC_DECL", str); + // TODO(titzer): a verbosity API for these kinds of tests? + // print(asm_source); + + doTest(asm_source, "(" + test + ") " + e); + } + + // ==== Statement assignment tests ========================================= + for (let e of assign_in_stmt) { + if (skip(++test)) continue; + var orig = e; + e = e.replace(/=/g, assign); + e = e.replace(/E/g, expr); + e = e.replace(/S/g, stmt); + var str = main.toString().replace("FUNC_BODY", e + "; return 0;"); + var asm_source = MODULE_TEMPLATE.toString().replace("FUNC_DECL", str); +// print(asm_source); + + doTest(asm_source, "(" + test + ") " + e); + } + + function doTest(asm_source, orig) { + var nonasm_source = asm_source.replace(new RegExp("use asm"), ""); + print("Testing JS: " + orig); + var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer); + expect(js_module); + + var asmfunc = eval("(" + asm_source + ")"); + + print("Testing ASMJS: " + orig); + var asm_module = asmfunc(stdlib, {}, buffer); + assertTrue(%IsAsmWasmCode(asmfunc)); + expect(asm_module); + } + + function expect(module) { module.main(0, 0, 0); print(" ok"); return true; } +} + +DoTheTests("(i32 | 0)", "i32 = 0", "void_func()"); +DoTheTests("G", "G = 0", "void_func()"); +DoTheTests("G", "G = 0", "G"); +DoTheTests("(M[0] | 0)", "M[0] = 0", "void_func()"); diff --git a/test/mjsunit/wasm/asm-wasm.js b/test/mjsunit/wasm/asm-wasm.js index f316e6837f..6aaea614fc 100644 --- a/test/mjsunit/wasm/asm-wasm.js +++ b/test/mjsunit/wasm/asm-wasm.js @@ -988,6 +988,7 @@ function TestFunctionTable(stdlib, foreign, buffer) { return {caller:caller}; } +print("TestFunctionTable..."); var module = TestFunctionTable(stdlib); assertEquals(55, module.caller(0, 0, 33, 22)); assertEquals(11, module.caller(0, 1, 33, 22)); @@ -1040,6 +1041,7 @@ function TestForeignFunctions() { assertEquals(103, module.caller(23, 103)); } +print("TestForeignFunctions..."); TestForeignFunctions(); diff --git a/test/mjsunit/wasm/calls.js b/test/mjsunit/wasm/calls.js index 4da0501cf2..b0feda8c80 100644 --- a/test/mjsunit/wasm/calls.js +++ b/test/mjsunit/wasm/calls.js @@ -20,13 +20,16 @@ function assertModule(module, memsize) { assertFalse(mem === null); assertFalse(mem === 0); assertEquals("object", typeof mem); - assertTrue(mem instanceof ArrayBuffer); + assertTrue(mem instanceof WebAssembly.Memory); + var buf = mem.buffer; + assertTrue(buf instanceof ArrayBuffer); + assertEquals(memsize, buf.byteLength); for (var i = 0; i < 4; i++) { module.exports.memory = 0; // should be ignored - assertEquals(mem, module.exports.memory); + mem.buffer = 0; // should be ignored + assertSame(mem, module.exports.memory); + assertSame(buf, mem.buffer); } - - assertEquals(memsize, module.exports.memory.byteLength); } function assertFunction(module, func) { diff --git a/test/mjsunit/wasm/compiled-module-management.js b/test/mjsunit/wasm/compiled-module-management.js index 5b64e00f41..a1bd2ce3c8 100644 --- a/test/mjsunit/wasm/compiled-module-management.js +++ b/test/mjsunit/wasm/compiled-module-management.js @@ -17,7 +17,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); builder.addImport("getValue", kSig_i); builder.addFunction("f", kSig_i) .addBody([ - kExprCallImport, kArity0, 0 + kExprCallFunction, 0 ]).exportFunc(); var module = new WebAssembly.Module(builder.toBuffer()); diff --git a/test/mjsunit/wasm/compiled-module-serialization.js b/test/mjsunit/wasm/compiled-module-serialization.js index 00b9d19597..aa36b71882 100644 --- a/test/mjsunit/wasm/compiled-module-serialization.js +++ b/test/mjsunit/wasm/compiled-module-serialization.js @@ -17,13 +17,13 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); builder.addFunction("main", kSig_i_i) .addBody([ - kExprI32Const, 1, kExprGetLocal, 0, kExprI32LoadMem, 0, 0, - kExprCallIndirect, kArity1, signature, + kExprI32Const, 1, + kExprCallIndirect, signature, kExprGetLocal,0, kExprI32LoadMem,0, 0, - kExprCallImport, kArity0, 0, + kExprCallFunction, 0, kExprI32Add ]).exportFunc(); @@ -32,8 +32,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); builder.addFunction("_wrap_writer", signature) .addBody([ kExprGetLocal, 0, - kExprCallImport, kArity1, 1]); - builder.appendToTable([0, 1]); + kExprCallFunction, 1]); + builder.appendToTable([2, 3]); var module = new WebAssembly.Module(builder.toBuffer()); diff --git a/test/mjsunit/wasm/debug-disassembly.js b/test/mjsunit/wasm/debug-disassembly.js index 976098a853..ac09d4af96 100644 --- a/test/mjsunit/wasm/debug-disassembly.js +++ b/test/mjsunit/wasm/debug-disassembly.js @@ -45,10 +45,10 @@ function listener(event, exec_state, event_data, data) { assertTrue(!!line, "line number must occur in disassembly"); assertTrue(line.length > columnNr, "column number must be valid"); var expected_string; - if (name.endsWith("/0")) { + if (name.endsWith("/1")) { // Function 0 calls the imported function. - expected_string = "kExprCallImport,"; - } else if (name.endsWith("/1")) { + expected_string = "kExprCallFunction,"; + } else if (name.endsWith("/2")) { // Function 1 calls function 0. expected_string = "kExprCallFunction,"; } else { @@ -76,7 +76,7 @@ var builder = new WasmModuleBuilder(); builder.addImport("func", kSig_v_v); builder.addFunction("call_import", kSig_v_v) - .addBody([kExprCallImport, kArity0, 0]) + .addBody([kExprCallFunction, 0]) .exportFunc(); // Add a bit of unneccessary code to increase the byte offset. @@ -87,8 +87,8 @@ builder.addFunction("call_call_import", kSig_v_v) kExprI32Const, (-7 & 0x7f), kExprSetLocal, 1, kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add, kExprI64UConvertI32, kExprI64Const, 0, - kExprI64Ne, kExprIf, - kExprCallFunction, kArity0, 0, + kExprI64Ne, kExprIf, kAstStmt, + kExprCallFunction, 1, kExprEnd ]) .exportFunc(); diff --git a/test/mjsunit/wasm/ensure-wasm-binaries-up-to-date.js b/test/mjsunit/wasm/ensure-wasm-binaries-up-to-date.js index 3fab8c65b1..26bdf8ece8 100644 --- a/test/mjsunit/wasm/ensure-wasm-binaries-up-to-date.js +++ b/test/mjsunit/wasm/ensure-wasm-binaries-up-to-date.js @@ -7,6 +7,27 @@ // Ensure checked in wasm binaries used by integration tests from v8 hosts // (such as chromium) are up to date. +(function print_incrementer() { + if (true) return; // remove to regenerate the module + + load('test/mjsunit/wasm/wasm-constants.js'); + load('test/mjsunit/wasm/wasm-module-builder.js'); + + var module = new WasmModuleBuilder(); + module.addFunction(undefined, kSig_i_i) + .addBody([kExprGetLocal, 0, kExprI32Const, 1, kExprI32Add]) + .exportAs("increment"); + + var buffer = module.toBuffer(true); + var view = new Uint8Array(buffer); + + print("const unsigned char module[] = {"); + for (var i = 0; i < buffer.byteLength; i++) { + print(" " + view[i] + ","); + } + print("};"); +})(); + (function ensure_incrementer() { var buff = readbuffer("test/mjsunit/wasm/incrementer.wasm"); var mod = new WebAssembly.Module(buff); diff --git a/test/mjsunit/wasm/exceptions.js b/test/mjsunit/wasm/exceptions.js index 07b0bf4e0e..b8d56ff6b5 100644 --- a/test/mjsunit/wasm/exceptions.js +++ b/test/mjsunit/wasm/exceptions.js @@ -15,9 +15,9 @@ var module = (function () { kExprGetLocal, 0, kExprI32Const, 0, kExprI32Ne, - kExprIf, + kExprIf, kAstStmt, kExprGetLocal, 0, - kExprThrow, + kExprThrow, kAstStmt, kExprEnd, kExprI32Const, 1 ]) @@ -26,7 +26,7 @@ var module = (function () { builder.addFunction("throw_20", kSig_v_v) .addBody([ kExprI32Const, 20, - kExprThrow + kExprThrow, kAstStmt ]) .exportFunc() @@ -43,7 +43,7 @@ var module = (function () { kExprI32Mul, kExprI32Const, 20, kExprI32Sub, - kExprThrow + kExprThrow, kAstStmt ]) .exportFunc() diff --git a/test/mjsunit/wasm/export-table.js b/test/mjsunit/wasm/export-table.js index 2084ddfc0a..6d21cf5790 100644 --- a/test/mjsunit/wasm/export-table.js +++ b/test/mjsunit/wasm/export-table.js @@ -15,7 +15,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); .addBody([ kExprI8Const, kReturnValue, - kExprReturn, kArity1 + kExprReturn ]) .exportFunc(); @@ -36,7 +36,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); .addBody([ kExprI8Const, kReturnValue, - kExprReturn, kArity1 + kExprReturn ]) .exportAs("blah") .exportAs("foo"); @@ -61,7 +61,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); .addBody([ kExprI8Const, kReturnValue, - kExprReturn, kArity1 + kExprReturn ]) .exportAs("0"); diff --git a/test/mjsunit/wasm/ffi-error.js b/test/mjsunit/wasm/ffi-error.js index 100f501af4..6d4787e70a 100644 --- a/test/mjsunit/wasm/ffi-error.js +++ b/test/mjsunit/wasm/ffi-error.js @@ -16,7 +16,7 @@ function testCallFFI(ffi) { .addBody([ kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallFunction, kArity2, 0, // -- + kExprCallFunction, 0, // -- ]) // -- .exportFunc(); @@ -106,7 +106,7 @@ assertThrows(function() { .addBody([ kExprGetLocal, 0, kExprI64SConvertI32, - kExprCallImport, kArity1, index // -- + kExprCallFunction, index // -- ]) // -- .exportFunc(); var func = function() {return {};}; diff --git a/test/mjsunit/wasm/ffi.js b/test/mjsunit/wasm/ffi.js index e84f038e68..5966ec8c5e 100644 --- a/test/mjsunit/wasm/ffi.js +++ b/test/mjsunit/wasm/ffi.js @@ -16,7 +16,7 @@ function testCallFFI(func, check) { .addBody([ kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallImport, kArity2, 0 // -- + kExprCallFunction, 0 // -- ]) // -- .exportFunc(); @@ -80,7 +80,7 @@ print("Constructor"); .addBody([ kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallImport, kArity2, 0 // -- + kExprCallFunction, 0 // -- ]) // -- .exportFunc(); @@ -98,7 +98,7 @@ print("Native function"); builder.addImport("func", sig_index); builder.addFunction("main", sig_index) .addBody([ - kExprCallImport, kArity0, 0 // -- + kExprCallFunction, 0 // -- ]) // -- .exportFunc(); @@ -247,7 +247,7 @@ function testCallBinopVoid(type, func, check) { .addBody([ kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallImport, kArity2, 0, // -- + kExprCallFunction, 0, // -- kExprI8Const, 99 // -- ]) // -- .exportFunc() @@ -302,11 +302,11 @@ function testCallPrint() { builder.addImport("print", makeSig_v_x(kAstF64)); builder.addFunction("main", makeSig_v_x(kAstF64)) .addBody([ - kExprI8Const, 97, // -- - kExprCallImport, kArity1, 0, // -- - kExprGetLocal, 0, // -- - kExprCallImport, kArity1, 1 // -- - ]) // -- + kExprI8Const, 97, // -- + kExprCallFunction, 0, // -- + kExprGetLocal, 0, // -- + kExprCallFunction, 1 // -- + ]) // -- .exportFunc() var main = builder.instantiate({print: print}).exports.main; diff --git a/test/mjsunit/wasm/frame-inspection.js b/test/mjsunit/wasm/frame-inspection.js index 4d342e6cae..9d45239e4a 100644 --- a/test/mjsunit/wasm/frame-inspection.js +++ b/test/mjsunit/wasm/frame-inspection.js @@ -52,11 +52,11 @@ var builder = new WasmModuleBuilder(); builder.addImport("func", kSig_v_v); builder.addFunction("wasm_1", kSig_v_v) - .addBody([kExprNop, kExprCallFunction, kArity0, 1]) + .addBody([kExprNop, kExprCallFunction, 2]) .exportAs("main"); builder.addFunction("wasm_2", kSig_v_v) - .addBody([kExprCallImport, kArity0, 0]); + .addBody([kExprCallFunction, 0]); function call_debugger() { debugger; diff --git a/test/mjsunit/wasm/function-names.js b/test/mjsunit/wasm/function-names.js index 15771d8470..94919b5e6c 100644 --- a/test/mjsunit/wasm/function-names.js +++ b/test/mjsunit/wasm/function-names.js @@ -19,11 +19,11 @@ var expected_names = ["exec_unreachable", "☠", null, for (var func_name of func_names) { last_func_index = builder.addFunction(func_name, kSig_v_v) - .addBody([kExprCallFunction, kArity0, last_func_index]).index; + .addBody([kExprCallFunction, last_func_index]).index; } builder.addFunction("main", kSig_v_v) - .addBody([kExprCallFunction, kArity0, last_func_index]) + .addBody([kExprCallFunction, last_func_index]) .exportFunc(); var module = builder.instantiate(); diff --git a/test/mjsunit/wasm/gc-frame.js b/test/mjsunit/wasm/gc-frame.js index 9c37fe485f..9e9aa98999 100644 --- a/test/mjsunit/wasm/gc-frame.js +++ b/test/mjsunit/wasm/gc-frame.js @@ -27,7 +27,8 @@ function makeFFI(func, t) { kExprGetLocal, 7, // -- kExprGetLocal, 8, // -- kExprGetLocal, 9, // -- - kExprCallImport, 10, 0, // -- + kExprCallFunction, 0, // -- + kExprDrop, // -- kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- kExprGetLocal, 2, // -- @@ -38,7 +39,7 @@ function makeFFI(func, t) { kExprGetLocal, 7, // -- kExprGetLocal, 8, // -- kExprGetLocal, 9, // -- - kExprCallImport, 10, 0 // -- + kExprCallFunction, 0, // -- ]) // -- .exportFunc(); diff --git a/test/mjsunit/wasm/grow-memory.js b/test/mjsunit/wasm/grow-memory.js index 858a19e3de..116c69358c 100644 --- a/test/mjsunit/wasm/grow-memory.js +++ b/test/mjsunit/wasm/grow-memory.js @@ -18,19 +18,22 @@ function genGrowMemoryBuilder() { .addBody([kExprGetLocal, 0, kExprI32LoadMem, 0, 0]) .exportFunc(); builder.addFunction("store", kSig_i_ii) - .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem, 0, 0]) + .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem, 0, 0, + kExprGetLocal, 1]) .exportFunc(); builder.addFunction("load16", kSig_i_i) .addBody([kExprGetLocal, 0, kExprI32LoadMem16U, 0, 0]) .exportFunc(); builder.addFunction("store16", kSig_i_ii) - .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem16, 0, 0]) + .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem16, 0, 0, + kExprGetLocal, 1]) .exportFunc(); builder.addFunction("load8", kSig_i_i) .addBody([kExprGetLocal, 0, kExprI32LoadMem8U, 0, 0]) .exportFunc(); builder.addFunction("store8", kSig_i_ii) - .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem8, 0, 0]) + .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem8, 0, 0, + kExprGetLocal, 1]) .exportFunc(); return builder; } @@ -244,9 +247,9 @@ function testGrowMemoryCurrentMemory() { var module = builder.instantiate(); function growMem(pages) { return module.exports.grow_memory(pages); } function MemSize() { return module.exports.memory_size(); } - assertEquals(65536, MemSize()); + assertEquals(1, MemSize()); assertEquals(1, growMem(1)); - assertEquals(131072, MemSize()); + assertEquals(2, MemSize()); } testGrowMemoryCurrentMemory(); diff --git a/test/mjsunit/wasm/import-table.js b/test/mjsunit/wasm/import-table.js index 8680addf61..aa836d6eac 100644 --- a/test/mjsunit/wasm/import-table.js +++ b/test/mjsunit/wasm/import-table.js @@ -16,7 +16,7 @@ function testCallImport(func, check) { .addBody([ kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallImport, 2, 0]) // -- + kExprCallFunction, 0]) // -- .exportAs("main"); var main = builder.instantiate({func: func}).exports.main; @@ -191,7 +191,7 @@ function testCallBinopVoid(type, func, check) { .addBody([ kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallImport, 2, 0, // -- + kExprCallFunction, 0, // -- kExprI8Const, 99, // -- ]) .exportFunc("main"); @@ -246,9 +246,9 @@ function testCallPrint() { builder.addFunction("main", makeSig_r_x(kAstF64, kAstF64)) .addBody([ kExprI8Const, 97, // -- - kExprCallImport, kArity1, 0, // -- + kExprCallFunction, 0, // -- kExprGetLocal, 0, // -- - kExprCallImport, kArity1, 1 // -- + kExprCallFunction, 1 // -- ]) .exportFunc(); @@ -270,8 +270,8 @@ function testCallImport2(foo, bar, expected) { builder.addImport("bar", kSig_i); builder.addFunction("main", kSig_i) .addBody([ - kExprCallImport, kArity0, 0, // -- - kExprCallImport, kArity0, 1, // -- + kExprCallFunction, 0, // -- + kExprCallFunction, 1, // -- kExprI32Add, // -- ]) // -- .exportFunc(); diff --git a/test/mjsunit/wasm/incrementer.wasm b/test/mjsunit/wasm/incrementer.wasm index f80f7ad597..30b51c2e1b 100644 Binary files a/test/mjsunit/wasm/incrementer.wasm and b/test/mjsunit/wasm/incrementer.wasm differ diff --git a/test/mjsunit/wasm/indirect-calls.js b/test/mjsunit/wasm/indirect-calls.js index 1e87c6f823..26021bb74d 100644 --- a/test/mjsunit/wasm/indirect-calls.js +++ b/test/mjsunit/wasm/indirect-calls.js @@ -14,7 +14,7 @@ var module = (function () { builder.addImport("add", sig_index); builder.addFunction("add", sig_index) .addBody([ - kExprGetLocal, 0, kExprGetLocal, 1, kExprCallImport, kArity2, 0 + kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0 ]); builder.addFunction("sub", sig_index) .addBody([ @@ -24,13 +24,13 @@ var module = (function () { ]); builder.addFunction("main", kSig_i_iii) .addBody([ - kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, - kExprCallIndirect, kArity2, sig_index + kExprGetLocal, 0, + kExprCallIndirect, sig_index ]) .exportFunc() - builder.appendToTable([0, 1, 2]); + builder.appendToTable([1, 2, 3]); return builder.instantiate({add: function(a, b) { return a + b | 0; }}); })(); @@ -47,3 +47,40 @@ assertEquals(19, module.exports.main(0, 12, 7)); assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)"); assertTraps(kTrapFuncInvalid, "module.exports.main(3, 12, 33)"); + + +module = (function () { + var builder = new WasmModuleBuilder(); + + var sig_i_ii = builder.addType(kSig_i_ii); + var sig_i_i = builder.addType(kSig_i_i); + builder.addImport("mul", sig_i_ii); + builder.addFunction("add", sig_i_ii) + .addBody([ + kExprGetLocal, 0, // -- + kExprGetLocal, 1, // -- + kExprI32Add // -- + ]); + builder.addFunction("popcnt", sig_i_i) + .addBody([ + kExprGetLocal, 0, // -- + kExprI32Popcnt // -- + ]); + builder.addFunction("main", kSig_i_iii) + .addBody([ + kExprGetLocal, 1, + kExprGetLocal, 2, + kExprGetLocal, 0, + kExprCallIndirect, sig_i_ii + ]) + .exportFunc() + builder.appendToTable([0, 1, 2, 3]); + + return builder.instantiate({mul: function(a, b) { return a * b | 0; }}); +})(); + +assertEquals(-6, module.exports.main(0, -2, 3)); +assertEquals(99, module.exports.main(1, 22, 77)); +assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)"); +assertTraps(kTrapFuncSigMismatch, "module.exports.main(3, 12, 33)"); +assertTraps(kTrapFuncInvalid, "module.exports.main(4, 12, 33)"); diff --git a/test/mjsunit/wasm/instance-gc.js b/test/mjsunit/wasm/instance-gc.js new file mode 100644 index 0000000000..1713f27b99 --- /dev/null +++ b/test/mjsunit/wasm/instance-gc.js @@ -0,0 +1,122 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-wasm --expose-gc + +load("test/mjsunit/wasm/wasm-constants.js"); +load("test/mjsunit/wasm/wasm-module-builder.js"); + +let nogc = () => {}; + +function newModule() { + let builder = new WasmModuleBuilder(); + builder.addMemory(1, 1, true); + builder.addFunction("main", kSig_i) + .addBody([kExprI32Const, 0, kExprI32LoadMem, 0, 0]) + .exportFunc(); + + return new WebAssembly.Module(builder.toBuffer()); +} + +function newInstance(module, val) { + var instance = new WebAssembly.Instance(module); + var view = new Int32Array(instance.exports.memory.buffer); + view[0] = val; + return instance; +} + +function TestSingleLiveInstance(gc) { + let module = newModule(); + + print("TestSingleLiveInstance..."); + for (var i = 0; i < 5; i++) { + (() => { // don't leak references between iterations. + print(" [" + i + "]..."); + gc(); + var instance = newInstance(module, i + 99); + assertEquals(i + 99, instance.exports.main()); + })(); + } +} + +TestSingleLiveInstance(nogc); +TestSingleLiveInstance(gc); + +function TestMultiInstance(gc) { + let module = newModule(); + + print("TestMultiInstance..."); + // Note: compute the root instances in another function to be + // sure that {roots} really is the only set of roots to the instances. + let roots = (() => { return [ + newInstance(module, 33), + newInstance(module, 4444), + newInstance(module, 555555) + ];})(); + + (() => { // don't leak references! + print(" [0]..."); + gc(); + assertEquals(33, roots[0].exports.main()); + roots[0] = null; + })(); + + (() => { // don't leak references! + print(" [1]..."); + gc(); + assertEquals(4444, roots[1].exports.main()); + roots[1] = null; + })(); + + (() => { // don't leak references! + print(" [2]..."); + gc(); + assertEquals(555555, roots[2].exports.main()); + roots[2] = null; + })(); +} + +TestMultiInstance(nogc); +TestMultiInstance(gc); + +function TestReclaimingCompiledModule() { + let module = newModule(); + + print("TestReclaimingCompiledModule..."); + let roots = (() => { return [ + newInstance(module, 7777), + newInstance(module, 8888), + ];})(); + + (() => { // don't leak references! + print(" [0]..."); + assertEquals(7777, roots[0].exports.main()); + assertEquals(8888, roots[1].exports.main()); + roots[1] = null; + })(); + + (() => { // don't leak references! + print(" [1]..."); + gc(); + roots[1] = newInstance(module, 9999); + assertEquals(7777, roots[0].exports.main()); + assertEquals(9999, roots[1].exports.main()); + roots[0] = null; + roots[1] = null; + })(); + + (() => { // don't leak references! + print(" [2]..."); + gc(); + roots[0] = newInstance(module, 11111); + roots[1] = newInstance(module, 22222); + assertEquals(11111, roots[0].exports.main()); + assertEquals(22222, roots[1].exports.main()); + roots[0] = null; + roots[1] = null; + })(); +} + +TestReclaimingCompiledModule(nogc); +TestReclaimingCompiledModule(gc); diff --git a/test/mjsunit/wasm/instantiate-module-basic.js b/test/mjsunit/wasm/instantiate-module-basic.js index 04fc903010..b83e813189 100644 --- a/test/mjsunit/wasm/instantiate-module-basic.js +++ b/test/mjsunit/wasm/instantiate-module-basic.js @@ -31,14 +31,17 @@ function CheckInstance(instance) { assertFalse(mem === null); assertFalse(mem === 0); assertEquals("object", typeof mem); - assertTrue(mem instanceof ArrayBuffer); - for (let i = 0; i < 4; i++) { + assertTrue(mem instanceof WebAssembly.Memory); + var buf = mem.buffer; + assertTrue(buf instanceof ArrayBuffer); + assertEquals(65536, buf.byteLength); + for (var i = 0; i < 4; i++) { instance.exports.memory = 0; // should be ignored + mem.buffer = 0; // should be ignored assertSame(mem, instance.exports.memory); + assertSame(buf, mem.buffer); } - assertEquals(65536, instance.exports.memory.byteLength); - // Check the properties of the main function. let main = instance.exports.main; assertFalse(main === undefined); @@ -61,6 +64,7 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); // Negative tests. (function InvalidModules() { + print("InvalidModules..."); let invalid_cases = [undefined, 1, "", "a", {some:1, obj: "b"}]; let len = invalid_cases.length; for (var i = 0; i < len; ++i) { @@ -75,9 +79,10 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); // Compile async an invalid blob. (function InvalidBinaryAsyncCompilation() { + print("InvalidBinaryAsyncCompilation..."); let builder = new WasmModuleBuilder(); builder.addFunction("f", kSig_i_i) - .addBody([kExprCallImport, kArity0, 0]); + .addBody([kExprCallFunction, 0]); let promise = WebAssembly.compile(builder.toBuffer()); promise .then(compiled => @@ -87,6 +92,7 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); // Multiple instances tests. (function ManyInstances() { + print("ManyInstances..."); let compiled_module = new WebAssembly.Module(buffer); let instance_1 = new WebAssembly.Instance(compiled_module); let instance_2 = new WebAssembly.Instance(compiled_module); @@ -94,6 +100,7 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); })(); (function ManyInstancesAsync() { + print("ManyInstancesAsync..."); let promise = WebAssembly.compile(buffer); promise.then(compiled_module => { let instance_1 = new WebAssembly.Instance(compiled_module); @@ -103,6 +110,7 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); })(); (function InstancesAreIsolatedFromEachother() { + print("InstancesAreIsolatedFromEachother..."); var builder = new WasmModuleBuilder(); builder.addMemory(1,1, true); var kSig_v_i = makeSig([kAstI32], []); @@ -112,13 +120,13 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); builder.addFunction("main", kSig_i_i) .addBody([ - kExprI32Const, 1, kExprGetLocal, 0, kExprI32LoadMem, 0, 0, - kExprCallIndirect, kArity1, signature, + kExprI32Const, 1, + kExprCallIndirect, signature, kExprGetLocal,0, kExprI32LoadMem,0, 0, - kExprCallImport, kArity0, 0, + kExprCallFunction, 0, kExprI32Add ]).exportFunc(); @@ -127,8 +135,8 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); builder.addFunction("_wrap_writer", signature) .addBody([ kExprGetLocal, 0, - kExprCallImport, kArity1, 1]); - builder.appendToTable([0, 1]); + kExprCallFunction, 1]); + builder.appendToTable([2, 3]); var module = new WebAssembly.Module(builder.toBuffer()); @@ -155,6 +163,7 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); })(); (function GlobalsArePrivateToTheInstance() { + print("GlobalsArePrivateToTheInstance..."); var builder = new WasmModuleBuilder(); builder.addGlobal(kAstI32); builder.addFunction("read", kSig_i_v) @@ -179,6 +188,7 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module))); (function InstanceMemoryIsIsolated() { + print("InstanceMemoryIsIsolated..."); var builder = new WasmModuleBuilder(); builder.addMemory(1,1, true); diff --git a/test/mjsunit/wasm/module-memory.js b/test/mjsunit/wasm/module-memory.js index a5e5f42488..6707f08164 100644 --- a/test/mjsunit/wasm/module-memory.js +++ b/test/mjsunit/wasm/module-memory.js @@ -15,33 +15,36 @@ function genModule(memory) { builder.addMemory(1, 1, true); builder.addFunction("main", kSig_i_i) .addBody([ - // main body: while(i) { if(mem[i]) return -1; i -= 4; } return 0; - kExprLoop, - kExprGetLocal,0, - kExprIf, - kExprGetLocal,0, - kExprI32LoadMem,0,0, - kExprIf, - kExprI8Const,255, - kExprReturn, kArity1, - kExprEnd, - kExprGetLocal,0, - kExprI8Const,4, - kExprI32Sub, - kExprSetLocal,0, - kExprBr, kArity1, 1, - kExprEnd, - kExprEnd, - kExprI8Const,0 + // main body: while(i) { if(mem[i]) return -1; i -= 4; } return 0; + // TODO(titzer): this manual bytecode has a copy of test-run-wasm.cc + /**/ kExprLoop, kAstStmt, // -- + /* */ kExprGetLocal, 0, // -- + /* */ kExprIf, kAstStmt, // -- + /* */ kExprGetLocal, 0, // -- + /* */ kExprI32LoadMem, 0, 0, // -- + /* */ kExprIf, kAstStmt, // -- + /* */ kExprI8Const, 255, // -- + /* */ kExprReturn, // -- + /* */ kExprEnd, // -- + /* */ kExprGetLocal, 0, // -- + /* */ kExprI8Const, 4, // -- + /* */ kExprI32Sub, // -- + /* */ kExprSetLocal, 0, // -- + /* */ kExprBr, 1, // -- + /* */ kExprEnd, // -- + /* */ kExprEnd, // -- + /**/ kExprI8Const, 0 // -- ]) .exportFunc(); - - return builder.instantiate(null, memory); + var module = builder.instantiate(null, memory); + assertTrue(module.exports.memory instanceof WebAssembly.Memory); + if (memory != null) assertEquals(memory, module.exports.memory.buffer); + return module; } function testPokeMemory() { var module = genModule(null); - var buffer = module.exports.memory; + var buffer = module.exports.memory.buffer; var main = module.exports.main; assertEquals(kMemSize, buffer.byteLength); @@ -66,9 +69,13 @@ function testPokeMemory() { testPokeMemory(); +function genAndGetMain(buffer) { + return genModule(buffer).exports.main; // to prevent intermediates living +} + function testSurvivalAcrossGc() { - var checker = genModule(null).exports.main; - for (var i = 0; i < 5; i++) { + var checker = genAndGetMain(null); + for (var i = 0; i < 3; i++) { print("gc run ", i); assertEquals(0, checker(kMemSize - 4)); gc(); @@ -110,8 +117,8 @@ testPokeOuterMemory(); function testOuterMemorySurvivalAcrossGc() { var buffer = new ArrayBuffer(kMemSize); - var checker = genModule(buffer).exports.main; - for (var i = 0; i < 5; i++) { + var checker = genAndGetMain(buffer); + for (var i = 0; i < 3; i++) { print("gc run ", i); assertEquals(0, checker(kMemSize - 4)); gc(); @@ -133,7 +140,9 @@ function testOOBThrows() { kExprGetLocal, 0, kExprGetLocal, 1, kExprI32LoadMem, 0, 0, - kExprI32StoreMem, 0, 0 + kExprI32StoreMem, 0, 0, + kExprGetLocal, 1, + kExprI32LoadMem, 0, 0, ]) .exportFunc(); diff --git a/test/mjsunit/wasm/parallel_compilation.js b/test/mjsunit/wasm/parallel_compilation.js index 23c5658dcd..208232cfd4 100644 --- a/test/mjsunit/wasm/parallel_compilation.js +++ b/test/mjsunit/wasm/parallel_compilation.js @@ -20,13 +20,16 @@ function assertModule(module, memsize) { assertFalse(mem === null); assertFalse(mem === 0); assertEquals("object", typeof mem); - assertTrue(mem instanceof ArrayBuffer); + assertTrue(mem instanceof WebAssembly.Memory); + var buf = mem.buffer; + assertTrue(buf instanceof ArrayBuffer); + assertEquals(memsize, buf.byteLength); for (var i = 0; i < 4; i++) { module.exports.memory = 0; // should be ignored - assertEquals(mem, module.exports.memory); + mem.buffer = 0; // should be ignored + assertSame(mem, module.exports.memory); + assertSame(buf, mem.buffer); } - - assertEquals(memsize, module.exports.memory.byteLength); } function assertFunction(module, func) { @@ -84,7 +87,7 @@ function assertFunction(module, func) { .addBody([ // -- kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallFunction, kArity2, f[i >>> 1].index]) // -- + kExprCallFunction, f[i >>> 1].index]) // -- .exportFunc() } var module = builder.instantiate(); diff --git a/test/mjsunit/wasm/receiver.js b/test/mjsunit/wasm/receiver.js index c0070f8b91..97a6d94c9b 100644 --- a/test/mjsunit/wasm/receiver.js +++ b/test/mjsunit/wasm/receiver.js @@ -16,7 +16,7 @@ function testCallImport(func, expected, a, b) { .addBody([ kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallImport, 2, 0]) // -- + kExprCallFunction, 0]) // -- .exportAs("main"); var main = builder.instantiate({func: func}).exports.main; diff --git a/test/mjsunit/wasm/stack.js b/test/mjsunit/wasm/stack.js index 0197b77caf..71038507db 100644 --- a/test/mjsunit/wasm/stack.js +++ b/test/mjsunit/wasm/stack.js @@ -45,7 +45,7 @@ var builder = new WasmModuleBuilder(); builder.addImport("func", kSig_v_v); builder.addFunction("main", kSig_v_v) - .addBody([kExprCallImport, kArity0, 0]) + .addBody([kExprCallFunction, 0]) .exportAs("main"); builder.addFunction("exec_unreachable", kSig_v_v) @@ -53,14 +53,14 @@ builder.addFunction("exec_unreachable", kSig_v_v) .exportAs("exec_unreachable"); // Make this function unnamed, just to test also this case. -var mem_oob_func = builder.addFunction(undefined, kSig_v_v) +var mem_oob_func = builder.addFunction(undefined, kSig_i_v) // Access the memory at offset -1, to provoke a trap. .addBody([kExprI32Const, 0x7f, kExprI32LoadMem8S, 0, 0]) .exportAs("mem_out_of_bounds"); // Call the mem_out_of_bounds function, in order to have two WASM stack frames. -builder.addFunction("call_mem_out_of_bounds", kSig_v_v) - .addBody([kExprCallFunction, kArity0, mem_oob_func.index]) +builder.addFunction("call_mem_out_of_bounds", kSig_i_v) + .addBody([kExprCallFunction, mem_oob_func.index]) .exportAs("call_mem_out_of_bounds"); var module = builder.instantiate({func: STACK}); @@ -69,7 +69,7 @@ var module = builder.instantiate({func: STACK}); var expected_string = "Error\n" + // The line numbers below will change as this test gains / loses lines.. " at STACK (stack.js:39:11)\n" + // -- - " at main ([0]+1)\n" + // -- + " at main ([1]+1)\n" + // -- " at testSimpleStack (stack.js:76:18)\n" + // -- " at stack.js:78:3"; // -- @@ -89,7 +89,7 @@ Error.prepareStackTrace = function(error, frames) { verifyStack(stack, [ // isWasm function line pos file [ false, "STACK", 39, 0, "stack.js"], - [ true, "main", 0, 1, null], + [ true, "main", 1, 1, null], [ false, "testStackFrames", 87, 0, "stack.js"], [ false, null, 96, 0, "stack.js"] ]); @@ -103,7 +103,7 @@ Error.prepareStackTrace = function(error, frames) { assertContains("unreachable", e.message); verifyStack(e.stack, [ // isWasm function line pos file - [ true, "exec_unreachable", 1, 1, null], + [ true, "exec_unreachable", 2, 1, null], [ false, "testWasmUnreachable", 100, 0, "stack.js"], [ false, null, 111, 0, "stack.js"] ]); @@ -118,8 +118,8 @@ Error.prepareStackTrace = function(error, frames) { assertContains("out of bounds", e.message); verifyStack(e.stack, [ // isWasm function line pos file - [ true, "", 2, 3, null], - [ true, "call_mem_out_of_bounds", 3, 1, null], + [ true, "", 3, 3, null], + [ true, "call_mem_out_of_bounds", 4, 1, null], [ false, "testWasmMemOutOfBounds", 115, 0, "stack.js"], [ false, null, 127, 0, "stack.js"] ]); @@ -135,7 +135,7 @@ Error.prepareStackTrace = function(error, frames) { builder.addFunction("recursion", sig_index) .addBody([ kExprI32Const, 0, - kExprCallIndirect, kArity0, sig_index + kExprCallIndirect, sig_index ]) .exportFunc() builder.appendToTable([0]); diff --git a/test/mjsunit/wasm/stackwalk.js b/test/mjsunit/wasm/stackwalk.js index 913269fdf4..cd560ec62b 100644 --- a/test/mjsunit/wasm/stackwalk.js +++ b/test/mjsunit/wasm/stackwalk.js @@ -16,7 +16,7 @@ function makeFFI(func) { .addBody([ kExprGetLocal, 0, // -- kExprGetLocal, 1, // -- - kExprCallImport, kArity2, 0, // -- + kExprCallFunction, 0, // -- ]) .exportFunc() diff --git a/test/mjsunit/wasm/start-function.js b/test/mjsunit/wasm/start-function.js index c4d299e871..f0fbd081ac 100644 --- a/test/mjsunit/wasm/start-function.js +++ b/test/mjsunit/wasm/start-function.js @@ -65,8 +65,8 @@ assertFails(kSig_i_dd, [kExprGetLocal, 0]); var func = builder.addFunction("", kSig_v_v) .addBody([kExprNop]); - builder.addExplicitSection([kDeclStart, 0]); - builder.addExplicitSection([kDeclStart, 0]); + builder.addExplicitSection([kStartSectionCode, 0]); + builder.addExplicitSection([kStartSectionCode, 0]); assertThrows(builder.instantiate); })(); @@ -84,7 +84,7 @@ assertFails(kSig_i_dd, [kExprGetLocal, 0]); builder.addStart(func.index); var module = builder.instantiate(); - var memory = module.exports.memory; + var memory = module.exports.memory.buffer; var view = new Int8Array(memory); assertEquals(77, view[0]); })(); @@ -102,7 +102,7 @@ assertFails(kSig_i_dd, [kExprGetLocal, 0]); builder.addImport("foo", sig_index); var func = builder.addFunction("", sig_index) - .addBody([kExprCallImport, kArity0, 0]); + .addBody([kExprCallFunction, 0]); builder.addStart(func.index); diff --git a/test/mjsunit/wasm/test-import-export-wrapper.js b/test/mjsunit/wasm/test-import-export-wrapper.js index e180611818..df03aec9f5 100644 --- a/test/mjsunit/wasm/test-import-export-wrapper.js +++ b/test/mjsunit/wasm/test-import-export-wrapper.js @@ -26,8 +26,8 @@ var expect_no_elison = 1; .addFunction("second_export", sig_index) .addBody([ kExprGetLocal, 0, - kExprCallImport, kArity1, 0, - kExprReturn, kArity1 + kExprCallFunction, 0, + kExprReturn ]) .exportFunc(); @@ -39,8 +39,8 @@ var expect_no_elison = 1; .addFunction("first_export", sig_index) .addBody([ kExprGetLocal, 0, - kExprCallFunction, kArity1, 1, - kExprReturn, kArity1 + kExprCallFunction, 2, + kExprReturn ]) .exportFunc(); first_module @@ -49,8 +49,8 @@ var expect_no_elison = 1; kExprI32Const, 1, kExprGetLocal, 0, kExprI32Add, - kExprCallImport, kArity1, 0, - kExprReturn, kArity1 + kExprCallFunction, 0, + kExprReturn ]); var f = second_module @@ -83,8 +83,8 @@ var expect_no_elison = 1; .addFunction("second_export", sig_index_1) .addBody([ kExprGetLocal, 0, - kExprCallImport, kArity1, 0, - kExprReturn, kArity1 + kExprCallFunction, 0, + kExprReturn ]) .exportFunc(); @@ -97,8 +97,8 @@ var expect_no_elison = 1; .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, - kExprCallFunction, kArity2, 1, - kExprReturn, kArity1 + kExprCallFunction, 2, + kExprReturn ]) .exportFunc(); first_module @@ -106,8 +106,8 @@ var expect_no_elison = 1; .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, - kExprCallImport, kArity2, 0, - kExprReturn, kArity1 + kExprCallFunction, 0, + kExprReturn ]); var f = second_module @@ -142,8 +142,8 @@ var expect_no_elison = 1; kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, - kExprCallImport, kArity3, 0, - kExprReturn, kArity1 + kExprCallFunction, 0, + kExprReturn ]) .exportFunc(); @@ -156,8 +156,8 @@ var expect_no_elison = 1; .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, - kExprCallFunction, kArity2, 1, - kExprReturn, kArity1 + kExprCallFunction, 2, + kExprReturn ]) .exportFunc(); first_module @@ -165,8 +165,8 @@ var expect_no_elison = 1; .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, - kExprCallImport, kArity2, 0, - kExprReturn, kArity1 + kExprCallFunction, 0, + kExprReturn ]); var f = second_module @@ -200,8 +200,8 @@ var expect_no_elison = 1; .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, - kExprCallImport, kArity2, 0, - kExprReturn, kArity1 + kExprCallFunction, 0, + kExprReturn ]) .exportFunc(); @@ -214,8 +214,8 @@ var expect_no_elison = 1; .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, - kExprCallFunction, kArity2, 1, - kExprReturn, kArity1 + kExprCallFunction, 2, + kExprReturn ]) .exportFunc(); first_module @@ -223,8 +223,8 @@ var expect_no_elison = 1; .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, - kExprCallImport, kArity2, 0, - kExprReturn, kArity1 + kExprCallFunction, 0, + kExprReturn ]); var f = second_module diff --git a/test/mjsunit/wasm/test-wasm-module-builder.js b/test/mjsunit/wasm/test-wasm-module-builder.js index 72d5a7aaa4..b1a2309770 100644 --- a/test/mjsunit/wasm/test-wasm-module-builder.js +++ b/test/mjsunit/wasm/test-wasm-module-builder.js @@ -7,7 +7,7 @@ load('test/mjsunit/wasm/wasm-constants.js'); load('test/mjsunit/wasm/wasm-module-builder.js'); -var debug = false; +var debug = true; (function BasicTest() { var module = new WasmModuleBuilder(); @@ -25,7 +25,7 @@ var debug = false; var module = new WasmModuleBuilder(); var index = module.addImport("print", makeSig_v_x(kAstI32)); module.addFunction("foo", kSig_v_v) - .addBody([kExprI8Const, 13, kExprCallImport, kArity1, index]) + .addBody([kExprI8Const, 13, kExprCallFunction, index]) .exportAs("main"); var buffer = module.toBuffer(debug); @@ -38,7 +38,7 @@ var debug = false; var module = new WasmModuleBuilder(); module.addFunction(undefined, kSig_i_i) .addLocals({i32_count: 1}) - .addBody([kExprGetLocal, 0, kExprSetLocal, 1]) + .addBody([kExprGetLocal, 0, kExprSetLocal, 1, kExprGetLocal, 1]) .exportAs("main"); var buffer = module.toBuffer(debug); @@ -60,7 +60,7 @@ var debug = false; var module = new WasmModuleBuilder(); module.addFunction(undefined, makeSig_r_x(p.type, p.type)) .addLocals(p.locals) - .addBody([kExprGetLocal, 0, kExprSetLocal, 1]) + .addBody([kExprGetLocal, 0, kExprSetLocal, 1, kExprGetLocal, 1]) .exportAs("main"); var buffer = module.toBuffer(debug); @@ -75,7 +75,7 @@ var debug = false; module.addFunction("add", kSig_i_ii) .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add]); module.addFunction("main", kSig_i_ii) - .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, kArity2, 0]) + .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0]) .exportAs("main"); var instance = module.instantiate(); @@ -89,7 +89,7 @@ var debug = false; .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add]); module.addFunction("main", kSig_i_iii) .addBody([kExprGetLocal, - 0, kExprGetLocal, 1, kExprGetLocal, 2, kExprCallIndirect, kArity2, 0]) + 1, kExprGetLocal, 2, kExprGetLocal, 0, kExprCallIndirect, 0]) .exportAs("main"); module.appendToTable([0]); @@ -143,7 +143,7 @@ var debug = false; var module = new WasmModuleBuilder(); var index = module.addImportWithModule("mod", "print", makeSig_v_x(kAstI32)); module.addFunction("foo", kSig_v_v) - .addBody([kExprI8Const, 19, kExprCallImport, kArity1, index]) + .addBody([kExprI8Const, 19, kExprCallFunction, index]) .exportAs("main"); var buffer = module.toBuffer(debug); diff --git a/test/mjsunit/wasm/trap-location.js b/test/mjsunit/wasm/trap-location.js index 0440af9ccc..bc8214f9b2 100644 --- a/test/mjsunit/wasm/trap-location.js +++ b/test/mjsunit/wasm/trap-location.js @@ -30,29 +30,29 @@ var sig_index = builder.addType(kSig_i_v) builder.addFunction("main", kSig_i_i) .addBody([ // offset 1 - kExprBlock, + kExprBlock, kAstI32, kExprGetLocal, 0, kExprI32Const, 2, kExprI32LtU, - kExprIf, - // offset 8 + kExprIf, kAstStmt, + // offset 9 kExprI32Const, 0x7e /* -2 */, kExprGetLocal, 0, kExprI32DivU, - // offset 13 + // offset 15 kExprI32LoadMem, 0, 0, - kExprBr, 1, 1, + kExprBr, 1, kExprEnd, - // offset 20 + // offset 21 kExprGetLocal, 0, kExprI32Const, 2, kExprI32Eq, - kExprIf, + kExprIf, kAstStmt, kExprUnreachable, kExprEnd, - // offset 28 - kExprGetLocal, 0, - kExprCallIndirect, kArity0, sig_index, + // offset 30 + kExprGetLocal, 0, + kExprCallIndirect, sig_index, kExprEnd, ]) .exportAs("main"); @@ -72,7 +72,7 @@ function testWasmTrap(value, reason, position) { } // The actual tests: -testWasmTrap(0, kTrapDivByZero, 12); -testWasmTrap(1, kTrapMemOutOfBounds, 13); -testWasmTrap(2, kTrapUnreachable, 26); -testWasmTrap(3, kTrapFuncInvalid, 30); +testWasmTrap(0, kTrapDivByZero, 14); +testWasmTrap(1, kTrapMemOutOfBounds, 15); +testWasmTrap(2, kTrapUnreachable, 28); +testWasmTrap(3, kTrapFuncInvalid, 32); diff --git a/test/mjsunit/wasm/unicode-validation.js b/test/mjsunit/wasm/unicode-validation.js index ceaed0364f..29d1f73d94 100644 --- a/test/mjsunit/wasm/unicode-validation.js +++ b/test/mjsunit/wasm/unicode-validation.js @@ -49,7 +49,7 @@ function checkImportsAndExports(imported_module_name, imported_function_name, kSig_v_v); builder.addFunction(internal_function_name, kSig_v_v) - .addBody([kExprCallImport, kArity0, 0]) + .addBody([kExprCallFunction, 0]) .exportAs(exported_function_name); // sanity check: does javascript agree with out shouldThrow annotation? diff --git a/test/mjsunit/wasm/verify-function-simple.js b/test/mjsunit/wasm/verify-function-simple.js index 31c23a6b69..1ac25143d7 100644 --- a/test/mjsunit/wasm/verify-function-simple.js +++ b/test/mjsunit/wasm/verify-function-simple.js @@ -25,7 +25,7 @@ try { var data = bytes( kWasmFunctionTypeForm, 0, 1, kAstI32, // signature kDeclNoLocals, // -- - kExprBlock, kExprNop, kExprNop, kExprEnd // body + kExprBlock, kAstStmt, kExprNop, kExprNop, kExprEnd // body ); Wasm.verifyFunction(data); diff --git a/test/mjsunit/wasm/wasm-constants.js b/test/mjsunit/wasm/wasm-constants.js index 5bcdb64a95..26969eb830 100644 --- a/test/mjsunit/wasm/wasm-constants.js +++ b/test/mjsunit/wasm/wasm-constants.js @@ -21,7 +21,7 @@ var kWasmH1 = 0x61; var kWasmH2 = 0x73; var kWasmH3 = 0x6d; -var kWasmV0 = 11; +var kWasmV0 = 0xC; var kWasmV1 = 0; var kWasmV2 = 0; var kWasmV3 = 0; @@ -51,30 +51,24 @@ function bytesWithHeader() { var kDeclNoLocals = 0; // Section declaration constants -var kDeclMemory = 0x00; -var kDeclTypes = 0x01; -var kDeclFunctions = 0x02; -var kDeclGlobals = 0x03; -var kDeclData = 0x04; -var kDeclTable = 0x05; -var kDeclEnd = 0x06; -var kDeclStart = 0x07; -var kDeclImports = 0x08; -var kDeclExports = 0x09; -var kDeclFunctions = 0x0a; -var kDeclCode = 0x0b; -var kDeclNames = 0x0c; +var kUnknownSectionCode = 0; +var kTypeSectionCode = 1; // Function signature declarations +var kImportSectionCode = 2; // Import declarations +var kFunctionSectionCode = 3; // Function declarations +var kTableSectionCode = 4; // Indirect function table and other tables +var kMemorySectionCode = 5; // Memory attributes +var kGlobalSectionCode = 6; // Global declarations +var kExportSectionCode = 7; // Exports +var kStartSectionCode = 8; // Start function declaration +var kElementSectionCode = 9; // Elements section +var kCodeSectionCode = 10; // Function code +var kDataSectionCode = 11; // Data segments +var kNameSectionCode = 12; // Name section (encoded as string) -var kArity0 = 0; -var kArity1 = 1; -var kArity2 = 2; -var kArity3 = 3; var kWasmFunctionTypeForm = 0x40; +var kWasmAnyFunctionTypeForm = 0x20; -var section_names = [ - "memory", "type", "old_function", "global", "data", - "table", "end", "start", "import", "export", - "function", "code", "name"]; +var kResizableMaximumFlag = 1; // Function declaration flags var kDeclFunctionName = 0x01; @@ -89,6 +83,11 @@ var kAstI64 = 2; var kAstF32 = 3; var kAstF64 = 4; +var kExternalFunction = 0; +var kExternalTable = 1; +var kExternalMemory = 2; +var kExternalGlobal = 3; + // Useful signatures var kSig_i = makeSig([], [kAstI32]); var kSig_d = makeSig([], [kAstF64]); @@ -133,7 +132,8 @@ function makeSig_r_xx(r, x) { } // Opcodes -var kExprNop = 0x00; +var kExprUnreachable = 0x00; +var kExprNop = 0x0a; var kExprBlock = 0x01; var kExprLoop = 0x02; var kExprIf = 0x03; @@ -143,9 +143,10 @@ var kExprBr = 0x06; var kExprBrIf = 0x07; var kExprBrTable = 0x08; var kExprReturn = 0x09; -var kExprUnreachable = 0x0a; var kExprThrow = 0xfa; var kExprEnd = 0x0f; +var kExprTeeLocal = 0x19; +var kExprDrop = 0x0b; var kExprI32Const = 0x10; var kExprI64Const = 0x11; @@ -155,7 +156,6 @@ var kExprGetLocal = 0x14; var kExprSetLocal = 0x15; var kExprCallFunction = 0x16; var kExprCallIndirect = 0x17; -var kExprCallImport = 0x18; var kExprI8Const = 0xcb; var kExprGetGlobal = 0xbb; var kExprSetGlobal = 0xbc; diff --git a/test/mjsunit/wasm/wasm-module-builder.js b/test/mjsunit/wasm/wasm-module-builder.js index f0cf9d18d3..7b77a8c9b1 100644 --- a/test/mjsunit/wasm/wasm-module-builder.js +++ b/test/mjsunit/wasm/wasm-module-builder.js @@ -61,7 +61,7 @@ class Binary extends Array { emit_section(section_code, content_generator) { // Emit section name. - this.emit_string(section_names[section_code]); + this.emit_u8(section_code); // Emit the section to a temporary buffer: its full length isn't know yet. let section = new Binary; content_generator(section); @@ -147,7 +147,7 @@ class WasmModuleBuilder { addFunction(name, type) { let type_index = (typeof type) == "number" ? type : this.addType(type); let func = new WasmFunctionBuilder(name, type_index); - func.index = this.functions.length; + func.index = this.functions.length + this.imports.length; this.functions.push(func); return func; } @@ -182,7 +182,7 @@ class WasmModuleBuilder { // Add type section if (wasm.types.length > 0) { if (debug) print("emitting types @ " + binary.length); - binary.emit_section(kDeclTypes, section => { + binary.emit_section(kTypeSectionCode, section => { section.emit_varint(wasm.types.length); for (let type of wasm.types) { section.emit_u8(kWasmFunctionTypeForm); @@ -198,27 +198,16 @@ class WasmModuleBuilder { }); } - if (wasm.globals.length > 0) { - if (debug) print ("emitting globals @ " + binary.length); - binary.emit_section(kDeclGlobals, section => { - section.emit_varint(wasm.globals.length); - for (let global_type of wasm.globals) { - section.emit_varint(0); // length of global name - section.emit_u8(global_type); - section.emit_u8(false); // we do not support exported globals - } - }); - } - // Add imports section if (wasm.imports.length > 0) { if (debug) print("emitting imports @ " + binary.length); - binary.emit_section(kDeclImports, section => { + binary.emit_section(kImportSectionCode, section => { section.emit_varint(wasm.imports.length); for (let imp of wasm.imports) { - section.emit_varint(imp.type); section.emit_string(imp.module); section.emit_string(imp.name || ''); + section.emit_u8(kExternalFunction); + section.emit_varint(imp.type); } }); } @@ -229,7 +218,7 @@ class WasmModuleBuilder { let exports = 0; if (wasm.functions.length > 0) { if (debug) print("emitting function decls @ " + binary.length); - binary.emit_section(kDeclFunctions, section => { + binary.emit_section(kFunctionSectionCode, section => { section.emit_varint(wasm.functions.length); for (let func of wasm.functions) { has_names = has_names || (func.name != undefined && @@ -243,56 +232,108 @@ class WasmModuleBuilder { // Add table. if (wasm.table.length > 0) { if (debug) print("emitting table @ " + binary.length); - binary.emit_section(kDeclTable, section => { + binary.emit_section(kTableSectionCode, section => { + section.emit_u8(1); // one table entry + section.emit_u8(kWasmAnyFunctionTypeForm); + section.emit_u8(1); + section.emit_varint(wasm.table.length); section.emit_varint(wasm.table.length); - if (wasm.pad !== null) { - if (debug) print("emitting table padding @ " + binary.length); - section.emit_varint(wasm.pad); - } - for (let index of wasm.table) { - section.emit_varint(index); - } }); } // Add memory section if (wasm.memory != undefined) { if (debug) print("emitting memory @ " + binary.length); - binary.emit_section(kDeclMemory, section => { + binary.emit_section(kMemorySectionCode, section => { + section.emit_u8(1); // one memory entry + section.emit_varint(kResizableMaximumFlag); section.emit_varint(wasm.memory.min); section.emit_varint(wasm.memory.max); - section.emit_u8(wasm.memory.exp ? 1 : 0); }); } + // Add global section. + if (wasm.globals.length > 0) { + if (debug) print ("emitting globals @ " + binary.length); + binary.emit_section(kGlobalSectionCode, section => { + section.emit_varint(wasm.globals.length); + for (let global_type of wasm.globals) { + section.emit_u8(global_type); + section.emit_u8(true); // mutable + switch (global_type) { + case kAstI32: + section.emit_u8(kExprI32Const); + section.emit_u8(0); + break; + case kAstI64: + section.emit_u8(kExprI64Const); + section.emit_u8(0); + break; + case kAstF32: + section.emit_u8(kExprF32Const); + section.emit_u32(0); + break; + case kAstF64: + section.emit_u8(kExprI32Const); + section.emit_u32(0); + section.emit_u32(0); + break; + } + section.emit_u8(kExprEnd); // end of init expression + } + }); + } // Add export table. - if (exports > 0) { + var mem_export = (wasm.memory != undefined && wasm.memory.exp); + if (exports > 0 || mem_export) { if (debug) print("emitting exports @ " + binary.length); - binary.emit_section(kDeclExports, section => { - section.emit_varint(exports); + binary.emit_section(kExportSectionCode, section => { + section.emit_varint(exports + (mem_export ? 1 : 0)); for (let func of wasm.functions) { for (let exp of func.exports) { - section.emit_varint(func.index); section.emit_string(exp); + section.emit_u8(kExternalFunction); + section.emit_varint(func.index); } } + if (mem_export) { + section.emit_string("memory"); + section.emit_u8(kExternalMemory); + section.emit_u8(0); + } }); } // Add start function section. if (wasm.start_index != undefined) { if (debug) print("emitting start function @ " + binary.length); - binary.emit_section(kDeclStart, section => { + binary.emit_section(kStartSectionCode, section => { section.emit_varint(wasm.start_index); }); } + // Add table elements. + if (wasm.table.length > 0) { + if (debug) print("emitting table @ " + binary.length); + binary.emit_section(kElementSectionCode, section => { + section.emit_u8(1); + section.emit_u8(0); // table index + section.emit_u8(kExprI32Const); + section.emit_u8(0); + section.emit_u8(kExprEnd); + section.emit_varint(wasm.table.length); + for (let index of wasm.table) { + section.emit_varint(index); + } + }); + } + // Add function bodies. if (wasm.functions.length > 0) { // emit function bodies if (debug) print("emitting code @ " + binary.length); - binary.emit_section(kDeclCode, section => { + binary.emit_section(kCodeSectionCode, section => { section.emit_varint(wasm.functions.length); for (let func of wasm.functions) { // Function body length will be patched later. @@ -331,10 +372,13 @@ class WasmModuleBuilder { // Add data segments. if (wasm.segments.length > 0) { if (debug) print("emitting data segments @ " + binary.length); - binary.emit_section(kDeclData, section => { + binary.emit_section(kDataSectionCode, section => { section.emit_varint(wasm.segments.length); for (let seg of wasm.segments) { + section.emit_u8(0); // linear memory index 0 + section.emit_u8(kExprI32Const); section.emit_varint(seg.addr); + section.emit_u8(kExprEnd); section.emit_varint(seg.data.length); section.emit_bytes(seg.data); } @@ -350,7 +394,8 @@ class WasmModuleBuilder { // Add function names. if (has_names) { if (debug) print("emitting names @ " + binary.length); - binary.emit_section(kDeclNames, section => { + binary.emit_section(kUnknownSectionCode, section => { + section.emit_string("name"); section.emit_varint(wasm.functions.length); for (let func of wasm.functions) { var name = func.name == undefined ? "" : func.name; diff --git a/test/unittests/wasm/ast-decoder-unittest.cc b/test/unittests/wasm/ast-decoder-unittest.cc index 9109dbf332..c1468ab29f 100644 --- a/test/unittests/wasm/ast-decoder-unittest.cc +++ b/test/unittests/wasm/ast-decoder-unittest.cc @@ -19,13 +19,17 @@ namespace v8 { namespace internal { namespace wasm { -#define B1(a) kExprBlock, a, kExprEnd -#define B2(a, b) kExprBlock, a, b, kExprEnd -#define B3(a, b, c) kExprBlock, a, b, c, kExprEnd +#define B1(a) WASM_BLOCK(a) +#define B2(a, b) WASM_BLOCK(a, b) +#define B3(a, b, c) WASM_BLOCK(a, b, c) + +#define WASM_IF_OP kExprIf, kLocalVoid +#define WASM_LOOP_OP kExprLoop, kLocalVoid static const byte kCodeGetLocal0[] = {kExprGetLocal, 0}; static const byte kCodeGetLocal1[] = {kExprGetLocal, 1}; static const byte kCodeSetLocal0[] = {WASM_SET_LOCAL(0, WASM_ZERO)}; +static const byte kCodeTeeLocal0[] = {WASM_TEE_LOCAL(0, WASM_ZERO)}; static const LocalType kLocalTypes[] = {kAstI32, kAstI64, kAstF32, kAstF64}; static const MachineType machineTypes[] = { @@ -41,28 +45,39 @@ static const WasmOpcode kInt32BinopOpcodes[] = { kExprI32LeS, kExprI32LtU, kExprI32LeU}; #define WASM_BRV_IF_ZERO(depth, val) \ - val, WASM_ZERO, kExprBrIf, ARITY_1, static_cast(depth) + val, WASM_ZERO, kExprBrIf, static_cast(depth) -#define EXPECT_VERIFIES(env, x) Verify(kSuccess, env, x, x + arraysize(x)) +#define EXPECT_VERIFIES_C(sig, x) \ + Verify(kSuccess, sigs.sig(), x, x + arraysize(x)) -#define EXPECT_FAILURE(env, x) Verify(kError, env, x, x + arraysize(x)) +#define EXPECT_FAILURE_C(sig, x) Verify(kError, sigs.sig(), x, x + arraysize(x)) -#define EXPECT_VERIFIES_INLINE(env, ...) \ +#define EXPECT_VERIFIES_SC(sig, x) Verify(kSuccess, sig, x, x + arraysize(x)) + +#define EXPECT_FAILURE_SC(sig, x) Verify(kError, sig, x, x + arraysize(x)) + +#define EXPECT_VERIFIES_S(env, ...) \ do { \ static byte code[] = {__VA_ARGS__}; \ Verify(kSuccess, env, code, code + arraysize(code)); \ } while (false) -#define EXPECT_FAILURE_INLINE(env, ...) \ +#define EXPECT_FAILURE_S(env, ...) \ do { \ static byte code[] = {__VA_ARGS__}; \ Verify(kError, env, code, code + arraysize(code)); \ } while (false) -#define VERIFY(...) \ +#define EXPECT_VERIFIES(sig, ...) \ do { \ static const byte code[] = {__VA_ARGS__}; \ - Verify(kSuccess, sigs.v_i(), code, code + sizeof(code)); \ + Verify(kSuccess, sigs.sig(), code, code + sizeof(code)); \ + } while (false) + +#define EXPECT_FAILURE(sig, ...) \ + do { \ + static const byte code[] = {__VA_ARGS__}; \ + Verify(kError, sigs.sig(), code, code + sizeof(code)); \ } while (false) static bool old_eh_flag; @@ -113,14 +128,14 @@ class AstDecoderTest : public TestWithZone { if (result.error_pt) str << ", pt = +" << pt; } } - FATAL(str.str().c_str()); + EXPECT_TRUE(false) << str.str().c_str(); } } void TestBinop(WasmOpcode opcode, FunctionSig* success) { // op(local[0], local[1]) byte code[] = {WASM_BINOP(opcode, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))}; - EXPECT_VERIFIES(success, code); + EXPECT_VERIFIES_SC(success, code); // Try all combinations of return and parameter types. for (size_t i = 0; i < arraysize(kLocalTypes); i++) { @@ -132,7 +147,7 @@ class AstDecoderTest : public TestWithZone { types[2] != success->GetParam(1)) { // Test signature mismatch. FunctionSig sig(1, 2, types); - EXPECT_FAILURE(&sig, code); + EXPECT_FAILURE_SC(&sig, code); } } } @@ -149,7 +164,7 @@ class AstDecoderTest : public TestWithZone { { LocalType types[] = {ret_type, param_type}; FunctionSig sig(1, 1, types); - EXPECT_VERIFIES(&sig, code); + EXPECT_VERIFIES_SC(&sig, code); } // Try all combinations of return and parameter types. @@ -159,7 +174,7 @@ class AstDecoderTest : public TestWithZone { if (types[0] != ret_type || types[1] != param_type) { // Test signature mismatch. FunctionSig sig(1, 1, types); - EXPECT_FAILURE(&sig, code); + EXPECT_FAILURE_SC(&sig, code); } } } @@ -170,7 +185,7 @@ TEST_F(AstDecoderTest, Int8Const) { byte code[] = {kExprI8Const, 0}; for (int i = -128; i < 128; i++) { code[1] = static_cast(i); - EXPECT_VERIFIES(sigs.i_i(), code); + EXPECT_VERIFIES_C(i_i, code); } } @@ -182,13 +197,16 @@ TEST_F(AstDecoderTest, EmptyFunction) { TEST_F(AstDecoderTest, IncompleteIf1) { byte code[] = {kExprIf}; - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, Int8Const_fallthru) { - byte code[] = {kExprI8Const, 0, kExprI8Const, 1}; - EXPECT_VERIFIES(sigs.i_i(), code); + EXPECT_VERIFIES(i_i, WASM_I32V_1(0)); +} + +TEST_F(AstDecoderTest, Int8Const_fallthru2) { + EXPECT_FAILURE(i_i, WASM_I32V_1(0), WASM_I32V_1(1)); } TEST_F(AstDecoderTest, Int32Const) { @@ -196,20 +214,15 @@ TEST_F(AstDecoderTest, Int32Const) { for (int32_t i = kMinInt; i < kMaxInt - kInc; i = i + kInc) { // TODO(binji): expand test for other sized int32s; 1 through 5 bytes. byte code[] = {WASM_I32V(i)}; - EXPECT_VERIFIES(sigs.i_i(), code); + EXPECT_VERIFIES_C(i_i, code); } } -TEST_F(AstDecoderTest, Int8Const_fallthru2) { - byte code[] = {WASM_I8(0), WASM_I32V_4(0x1122334)}; - EXPECT_VERIFIES(sigs.i_i(), code); -} - TEST_F(AstDecoderTest, Int64Const) { const int kInc = 4498211; for (int32_t i = kMinInt; i < kMaxInt - kInc; i = i + kInc) { byte code[] = {WASM_I64V((static_cast(i) << 32) | i)}; - EXPECT_VERIFIES(sigs.l_l(), code); + EXPECT_VERIFIES_C(l_l, code); } } @@ -218,7 +231,7 @@ TEST_F(AstDecoderTest, Float32Const) { float* ptr = reinterpret_cast(code + 1); for (int i = 0; i < 30; i++) { WriteLittleEndianValue(ptr, i * -7.75f); - EXPECT_VERIFIES(sigs.f_ff(), code); + EXPECT_VERIFIES_C(f_ff, code); } } @@ -227,7 +240,7 @@ TEST_F(AstDecoderTest, Float64Const) { double* ptr = reinterpret_cast(code + 1); for (int i = 0; i < 30; i++) { WriteLittleEndianValue(ptr, i * 33.45); - EXPECT_VERIFIES(sigs.d_dd(), code); + EXPECT_VERIFIES_C(d_dd, code); } } @@ -240,24 +253,24 @@ TEST_F(AstDecoderTest, Int32Const_off_end) { } TEST_F(AstDecoderTest, GetLocal0_param) { - EXPECT_VERIFIES(sigs.i_i(), kCodeGetLocal0); + EXPECT_VERIFIES_C(i_i, kCodeGetLocal0); } TEST_F(AstDecoderTest, GetLocal0_local) { AddLocals(kAstI32, 1); - EXPECT_VERIFIES(sigs.i_v(), kCodeGetLocal0); + EXPECT_VERIFIES_C(i_v, kCodeGetLocal0); } TEST_F(AstDecoderTest, TooManyLocals) { AddLocals(kAstI32, 4034986500); - EXPECT_FAILURE(sigs.i_v(), kCodeGetLocal0); + EXPECT_FAILURE_C(i_v, kCodeGetLocal0); } TEST_F(AstDecoderTest, GetLocal0_param_n) { FunctionSig* array[] = {sigs.i_i(), sigs.i_ii(), sigs.i_iii()}; for (size_t i = 0; i < arraysize(array); i++) { - EXPECT_VERIFIES(array[i], kCodeGetLocal0); + EXPECT_VERIFIES_SC(array[i], kCodeGetLocal0); } } @@ -266,555 +279,618 @@ TEST_F(AstDecoderTest, GetLocalN_local) { AddLocals(kAstI32, 1); for (byte j = 0; j < i; j++) { byte code[] = {kExprGetLocal, j}; - EXPECT_VERIFIES(sigs.i_v(), code); + EXPECT_VERIFIES_C(i_v, code); } } } TEST_F(AstDecoderTest, GetLocal0_fail_no_params) { - EXPECT_FAILURE(sigs.i_v(), kCodeGetLocal0); + EXPECT_FAILURE_C(i_v, kCodeGetLocal0); } TEST_F(AstDecoderTest, GetLocal1_fail_no_locals) { - EXPECT_FAILURE(sigs.i_i(), kCodeGetLocal1); + EXPECT_FAILURE_C(i_i, kCodeGetLocal1); } TEST_F(AstDecoderTest, GetLocal_off_end) { static const byte code[] = {kExprGetLocal}; - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, NumLocalBelowLimit) { AddLocals(kAstI32, kMaxNumWasmLocals - 1); - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_NOP); + EXPECT_VERIFIES(v_v, WASM_NOP); } TEST_F(AstDecoderTest, NumLocalAtLimit) { AddLocals(kAstI32, kMaxNumWasmLocals); - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_NOP); + EXPECT_VERIFIES(v_v, WASM_NOP); } TEST_F(AstDecoderTest, NumLocalAboveLimit) { AddLocals(kAstI32, kMaxNumWasmLocals + 1); - EXPECT_FAILURE_INLINE(sigs.v_v(), WASM_NOP); + EXPECT_FAILURE(v_v, WASM_NOP); } TEST_F(AstDecoderTest, GetLocal_varint) { const int kMaxLocals = kMaxNumWasmLocals; AddLocals(kAstI32, kMaxLocals); - for (int index = 0; index < kMaxLocals; index = index * 11 + 5) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), kExprGetLocal, U32V_1(index)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), kExprGetLocal, U32V_2(index)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), kExprGetLocal, U32V_3(index)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), kExprGetLocal, U32V_4(index)); - } + EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_1(66)); + EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_2(7777)); + EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_3(888888)); + EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_4(3999999)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), kExprGetLocal, U32V_5(kMaxLocals - 1)); + EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_5(kMaxLocals - 1)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), kExprGetLocal, U32V_4(kMaxLocals - 1)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), kExprGetLocal, U32V_4(kMaxLocals)); - EXPECT_FAILURE_INLINE(sigs.i_i(), kExprGetLocal, U32V_4(kMaxLocals + 1)); + EXPECT_FAILURE(i_i, kExprGetLocal, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); - EXPECT_FAILURE_INLINE(sigs.i_v(), kExprGetLocal, U32V_4(kMaxLocals)); - EXPECT_FAILURE_INLINE(sigs.i_v(), kExprGetLocal, U32V_4(kMaxLocals + 1)); + EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_4(kMaxLocals - 1)); + EXPECT_VERIFIES(i_i, kExprGetLocal, U32V_4(kMaxLocals)); + EXPECT_FAILURE(i_i, kExprGetLocal, U32V_4(kMaxLocals + 1)); + + EXPECT_FAILURE(i_v, kExprGetLocal, U32V_4(kMaxLocals)); + EXPECT_FAILURE(i_v, kExprGetLocal, U32V_4(kMaxLocals + 1)); } TEST_F(AstDecoderTest, Binops_off_end) { byte code1[] = {0}; // [opcode] for (size_t i = 0; i < arraysize(kInt32BinopOpcodes); i++) { code1[0] = kInt32BinopOpcodes[i]; - EXPECT_FAILURE(sigs.i_i(), code1); + EXPECT_FAILURE_C(i_i, code1); } byte code3[] = {kExprGetLocal, 0, 0}; // [expr] [opcode] for (size_t i = 0; i < arraysize(kInt32BinopOpcodes); i++) { code3[2] = kInt32BinopOpcodes[i]; - EXPECT_FAILURE(sigs.i_i(), code3); + EXPECT_FAILURE_C(i_i, code3); } byte code4[] = {kExprGetLocal, 0, 0, 0}; // [expr] [opcode] [opcode] for (size_t i = 0; i < arraysize(kInt32BinopOpcodes); i++) { code4[2] = kInt32BinopOpcodes[i]; code4[3] = kInt32BinopOpcodes[i]; - EXPECT_FAILURE(sigs.i_i(), code4); + EXPECT_FAILURE_C(i_i, code4); } } TEST_F(AstDecoderTest, BinopsAcrossBlock1) { static const byte code[] = {WASM_ZERO, kExprBlock, WASM_ZERO, kExprI32Add, kExprEnd}; - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, BinopsAcrossBlock2) { static const byte code[] = {WASM_ZERO, WASM_ZERO, kExprBlock, kExprI32Add, kExprEnd}; - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, BinopsAcrossBlock3) { static const byte code[] = {WASM_ZERO, WASM_ZERO, kExprIf, kExprI32Add, kExprElse, kExprI32Add, kExprEnd}; - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, Nop) { static const byte code[] = {kExprNop}; - EXPECT_VERIFIES(sigs.v_v(), code); + EXPECT_VERIFIES_C(v_v, code); +} + +TEST_F(AstDecoderTest, SetLocal0_void) { + EXPECT_FAILURE(i_i, WASM_SET_LOCAL(0, WASM_ZERO)); } TEST_F(AstDecoderTest, SetLocal0_param) { - EXPECT_VERIFIES(sigs.i_i(), kCodeSetLocal0); - EXPECT_FAILURE(sigs.f_ff(), kCodeSetLocal0); - EXPECT_FAILURE(sigs.d_dd(), kCodeSetLocal0); + EXPECT_FAILURE_C(i_i, kCodeSetLocal0); + EXPECT_FAILURE_C(f_ff, kCodeSetLocal0); + EXPECT_FAILURE_C(d_dd, kCodeSetLocal0); +} + +TEST_F(AstDecoderTest, TeeLocal0_param) { + EXPECT_VERIFIES_C(i_i, kCodeTeeLocal0); + EXPECT_FAILURE_C(f_ff, kCodeTeeLocal0); + EXPECT_FAILURE_C(d_dd, kCodeTeeLocal0); } TEST_F(AstDecoderTest, SetLocal0_local) { - EXPECT_FAILURE(sigs.i_v(), kCodeSetLocal0); + EXPECT_FAILURE_C(i_v, kCodeSetLocal0); + EXPECT_FAILURE_C(v_v, kCodeSetLocal0); AddLocals(kAstI32, 1); - EXPECT_VERIFIES(sigs.i_v(), kCodeSetLocal0); + EXPECT_FAILURE_C(i_v, kCodeSetLocal0); + EXPECT_VERIFIES_C(v_v, kCodeSetLocal0); } -TEST_F(AstDecoderTest, SetLocalN_local) { +TEST_F(AstDecoderTest, TeeLocal0_local) { + EXPECT_FAILURE_C(i_v, kCodeTeeLocal0); + AddLocals(kAstI32, 1); + EXPECT_VERIFIES_C(i_v, kCodeTeeLocal0); +} + +TEST_F(AstDecoderTest, TeeLocalN_local) { for (byte i = 1; i < 8; i++) { AddLocals(kAstI32, 1); for (byte j = 0; j < i; j++) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_SET_LOCAL(j, WASM_I8(i))); + EXPECT_FAILURE(v_v, WASM_TEE_LOCAL(j, WASM_I8(i))); + EXPECT_VERIFIES(i_i, WASM_TEE_LOCAL(j, WASM_I8(i))); } } } TEST_F(AstDecoderTest, BlockN) { const int kMaxSize = 200; - byte buffer[kMaxSize + 2]; + byte buffer[kMaxSize + 3]; for (int i = 0; i <= kMaxSize; i++) { memset(buffer, kExprNop, sizeof(buffer)); buffer[0] = kExprBlock; - buffer[i + 1] = kExprEnd; - Verify(kSuccess, sigs.v_i(), buffer, buffer + i + 2); + buffer[1] = kLocalVoid; + buffer[i + 2] = kExprEnd; + Verify(kSuccess, sigs.v_i(), buffer, buffer + i + 3); } } +#define WASM_EMPTY_BLOCK kExprBlock, kLocalVoid, kExprEnd + TEST_F(AstDecoderTest, Block0) { - static const byte code[] = {kExprBlock, kExprEnd}; - EXPECT_VERIFIES(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + static const byte code[] = {WASM_EMPTY_BLOCK}; + EXPECT_VERIFIES_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, Block0_fallthru1) { - static const byte code[] = {kExprBlock, kExprBlock, kExprEnd, kExprEnd}; - EXPECT_VERIFIES(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + static const byte code[] = {WASM_BLOCK(WASM_EMPTY_BLOCK)}; + EXPECT_VERIFIES_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, Block0Block0) { - static const byte code[] = {kExprBlock, kExprEnd, kExprBlock, kExprEnd}; - EXPECT_VERIFIES(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + static const byte code[] = {WASM_EMPTY_BLOCK, WASM_EMPTY_BLOCK}; + EXPECT_VERIFIES_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); +} + +TEST_F(AstDecoderTest, Block0_end) { + EXPECT_VERIFIES(v_v, WASM_EMPTY_BLOCK, kExprEnd); } TEST_F(AstDecoderTest, Block0_end_end) { - static const byte code[] = {kExprBlock, kExprEnd, kExprEnd}; - EXPECT_FAILURE(sigs.v_v(), code); + EXPECT_FAILURE(v_v, WASM_EMPTY_BLOCK, kExprEnd, kExprEnd); } TEST_F(AstDecoderTest, Block1) { - byte code[] = {B1(WASM_SET_LOCAL(0, WASM_ZERO))}; - EXPECT_VERIFIES(sigs.i_i(), code); - EXPECT_VERIFIES(sigs.v_i(), code); - EXPECT_FAILURE(sigs.d_dd(), code); + byte code[] = {WASM_BLOCK_I(WASM_GET_LOCAL(0))}; + EXPECT_VERIFIES_C(i_i, code); + EXPECT_FAILURE_C(v_i, code); + EXPECT_FAILURE_C(d_dd, code); + EXPECT_FAILURE_C(i_f, code); + EXPECT_FAILURE_C(i_d, code); } TEST_F(AstDecoderTest, Block1_i) { - byte code[] = {B1(WASM_ZERO)}; - EXPECT_VERIFIES(sigs.i_i(), code); - EXPECT_FAILURE(sigs.f_ff(), code); - EXPECT_FAILURE(sigs.d_dd(), code); - EXPECT_FAILURE(sigs.l_ll(), code); + byte code[] = {WASM_BLOCK_I(WASM_ZERO)}; + EXPECT_VERIFIES_C(i_i, code); + EXPECT_FAILURE_C(f_ff, code); + EXPECT_FAILURE_C(d_dd, code); + EXPECT_FAILURE_C(l_ll, code); } TEST_F(AstDecoderTest, Block1_f) { - byte code[] = {B1(WASM_F32(0))}; - EXPECT_FAILURE(sigs.i_i(), code); - EXPECT_VERIFIES(sigs.f_ff(), code); - EXPECT_FAILURE(sigs.d_dd(), code); - EXPECT_FAILURE(sigs.l_ll(), code); + byte code[] = {WASM_BLOCK_F(WASM_F32(0))}; + EXPECT_FAILURE_C(i_i, code); + EXPECT_VERIFIES_C(f_ff, code); + EXPECT_FAILURE_C(d_dd, code); + EXPECT_FAILURE_C(l_ll, code); } TEST_F(AstDecoderTest, Block1_continue) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), B1(WASM_BR(0))); - EXPECT_FAILURE_INLINE(sigs.v_v(), B1(WASM_BR(1))); - EXPECT_FAILURE_INLINE(sigs.v_v(), B1(WASM_BR(2))); - EXPECT_FAILURE_INLINE(sigs.v_v(), B1(WASM_BR(3))); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_BR(0))); } TEST_F(AstDecoderTest, Block1_br) { - EXPECT_FAILURE_INLINE(sigs.v_v(), kExprBlock, kExprBr, ARITY_1, DEPTH_0, - kExprEnd); - EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprBlock, kExprBr, ARITY_0, DEPTH_0, - kExprEnd); + EXPECT_VERIFIES(v_v, B1(WASM_BR(0))); + EXPECT_VERIFIES(v_v, B1(WASM_BR(1))); + EXPECT_FAILURE(v_v, B1(WASM_BR(2))); } TEST_F(AstDecoderTest, Block2_br) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), B2(WASM_NOP, WASM_BR(0))); - EXPECT_VERIFIES_INLINE(sigs.v_v(), B2(WASM_BR(0), WASM_NOP)); - EXPECT_VERIFIES_INLINE(sigs.v_v(), B2(WASM_BR(0), WASM_BR(0))); + EXPECT_VERIFIES(v_v, B2(WASM_NOP, WASM_BR(0))); + EXPECT_VERIFIES(v_v, B2(WASM_BR(0), WASM_NOP)); + EXPECT_VERIFIES(v_v, B2(WASM_BR(0), WASM_BR(0))); } TEST_F(AstDecoderTest, Block2) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), - B2(WASM_NOP, WASM_SET_LOCAL(0, WASM_ZERO))); - EXPECT_FAILURE_INLINE(sigs.i_i(), B2(WASM_SET_LOCAL(0, WASM_ZERO), WASM_NOP)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), B2(WASM_SET_LOCAL(0, WASM_ZERO), - WASM_SET_LOCAL(0, WASM_ZERO))); + EXPECT_FAILURE(i_i, WASM_BLOCK(WASM_NOP, WASM_NOP)); + EXPECT_FAILURE(i_i, WASM_BLOCK_I(WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_NOP, WASM_ZERO)); + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_ZERO, WASM_NOP)); + EXPECT_FAILURE(i_i, WASM_BLOCK_I(WASM_ZERO, WASM_ZERO)); } TEST_F(AstDecoderTest, Block2b) { - byte code[] = {B2(WASM_SET_LOCAL(0, WASM_ZERO), WASM_ZERO)}; - EXPECT_VERIFIES(sigs.i_i(), code); - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.f_ff(), code); + byte code[] = {WASM_BLOCK_I(WASM_SET_LOCAL(0, WASM_ZERO), WASM_ZERO)}; + EXPECT_VERIFIES_C(i_i, code); + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(f_ff, code); } TEST_F(AstDecoderTest, Block2_fallthru) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), B2(WASM_SET_LOCAL(0, WASM_ZERO), - WASM_SET_LOCAL(0, WASM_ZERO)), - WASM_I8(23)); + EXPECT_VERIFIES( + i_i, B2(WASM_SET_LOCAL(0, WASM_ZERO), WASM_SET_LOCAL(0, WASM_ZERO)), + WASM_I8(23)); } TEST_F(AstDecoderTest, Block3) { - EXPECT_VERIFIES_INLINE( - sigs.i_i(), B3(WASM_SET_LOCAL(0, WASM_ZERO), WASM_SET_LOCAL(0, WASM_ZERO), - WASM_I8(11))); + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_SET_LOCAL(0, WASM_ZERO), + WASM_SET_LOCAL(0, WASM_ZERO), WASM_I8(11))); } TEST_F(AstDecoderTest, Block5) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), B1(WASM_GET_LOCAL(0))); + EXPECT_FAILURE(v_i, WASM_BLOCK(WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.v_i(), B2(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); + EXPECT_FAILURE(v_i, WASM_BLOCK(WASM_ZERO, WASM_ZERO)); - EXPECT_VERIFIES_INLINE( - sigs.v_i(), B3(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); + EXPECT_FAILURE(v_i, WASM_BLOCK(WASM_ZERO, WASM_ZERO, WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.v_i(), - WASM_BLOCK(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), - WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); + EXPECT_FAILURE(v_i, WASM_BLOCK(WASM_ZERO, WASM_ZERO, WASM_ZERO, WASM_ZERO)); - EXPECT_VERIFIES_INLINE( - sigs.v_i(), - WASM_BLOCK(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), - WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); + EXPECT_FAILURE( + v_i, WASM_BLOCK(WASM_ZERO, WASM_ZERO, WASM_ZERO, WASM_ZERO, WASM_ZERO)); +} + +TEST_F(AstDecoderTest, BlockType) { + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_GET_LOCAL(0))); + EXPECT_VERIFIES(l_l, WASM_BLOCK_L(WASM_GET_LOCAL(0))); + EXPECT_VERIFIES(f_f, WASM_BLOCK_F(WASM_GET_LOCAL(0))); + EXPECT_VERIFIES(d_d, WASM_BLOCK_D(WASM_GET_LOCAL(0))); +} + +TEST_F(AstDecoderTest, BlockType_fail) { + EXPECT_FAILURE(i_i, WASM_BLOCK_L(WASM_I64V_1(0))); + EXPECT_FAILURE(i_i, WASM_BLOCK_F(WASM_F32(0.0))); + EXPECT_FAILURE(i_i, WASM_BLOCK_D(WASM_F64(1.1))); + + EXPECT_FAILURE(l_l, WASM_BLOCK_I(WASM_ZERO)); + EXPECT_FAILURE(l_l, WASM_BLOCK_F(WASM_F32(0.0))); + EXPECT_FAILURE(l_l, WASM_BLOCK_D(WASM_F64(1.1))); + + EXPECT_FAILURE(f_ff, WASM_BLOCK_I(WASM_ZERO)); + EXPECT_FAILURE(f_ff, WASM_BLOCK_L(WASM_I64V_1(0))); + EXPECT_FAILURE(f_ff, WASM_BLOCK_D(WASM_F64(1.1))); + + EXPECT_FAILURE(d_dd, WASM_BLOCK_I(WASM_ZERO)); + EXPECT_FAILURE(d_dd, WASM_BLOCK_L(WASM_I64V_1(0))); + EXPECT_FAILURE(d_dd, WASM_BLOCK_F(WASM_F32(0.0))); } TEST_F(AstDecoderTest, BlockF32) { - static const byte code[] = {kExprBlock, kExprF32Const, 0, 0, 0, 0, kExprEnd}; - EXPECT_VERIFIES(sigs.f_ff(), code); - EXPECT_FAILURE(sigs.i_i(), code); - EXPECT_FAILURE(sigs.d_dd(), code); + static const byte code[] = {WASM_BLOCK_F(kExprF32Const, 0, 0, 0, 0)}; + EXPECT_VERIFIES_C(f_ff, code); + EXPECT_FAILURE_C(i_i, code); + EXPECT_FAILURE_C(d_dd, code); } TEST_F(AstDecoderTest, BlockN_off_end) { - byte code[] = {kExprBlock, kExprNop, kExprNop, kExprNop, kExprNop, kExprEnd}; - EXPECT_VERIFIES(sigs.v_v(), code); + byte code[] = {WASM_BLOCK(kExprNop, kExprNop, kExprNop, kExprNop)}; + EXPECT_VERIFIES_C(v_v, code); for (size_t i = 1; i < arraysize(code); i++) { Verify(kError, sigs.v_v(), code, code + i); } } TEST_F(AstDecoderTest, Block2_continue) { - static const byte code[] = {kExprBlock, kExprBr, ARITY_0, - DEPTH_1, kExprNop, kExprEnd}; - EXPECT_FAILURE(sigs.v_v(), code); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_NOP, WASM_BR(0))); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_NOP, WASM_BR(1))); + EXPECT_FAILURE(v_v, WASM_LOOP(WASM_NOP, WASM_BR(2))); +} + +TEST_F(AstDecoderTest, Block3_continue) { + EXPECT_VERIFIES(v_v, B1(WASM_LOOP(WASM_NOP, WASM_BR(0)))); + EXPECT_VERIFIES(v_v, B1(WASM_LOOP(WASM_NOP, WASM_BR(1)))); + EXPECT_VERIFIES(v_v, B1(WASM_LOOP(WASM_NOP, WASM_BR(2)))); + EXPECT_FAILURE(v_v, B1(WASM_LOOP(WASM_NOP, WASM_BR(3)))); } TEST_F(AstDecoderTest, NestedBlock_return) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), B1(B1(WASM_RETURN1(WASM_ZERO)))); -} - -TEST_F(AstDecoderTest, BlockBinop) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_I32_AND(B1(WASM_I8(1)), WASM_I8(2))); + EXPECT_VERIFIES(i_i, B1(B1(WASM_RETURN1(WASM_ZERO)))); } TEST_F(AstDecoderTest, BlockBrBinop) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), - WASM_I32_AND(B1(WASM_BRV(0, WASM_I8(1))), WASM_I8(2))); + EXPECT_VERIFIES( + i_i, WASM_I32_AND(WASM_BLOCK_I(WASM_BRV(0, WASM_I8(1))), WASM_I8(2))); } TEST_F(AstDecoderTest, If_empty1) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_ZERO, kExprIf, kExprEnd); + EXPECT_VERIFIES(v_v, WASM_ZERO, WASM_IF_OP, kExprEnd); } TEST_F(AstDecoderTest, If_empty2) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_ZERO, kExprIf, kExprElse, kExprEnd); + EXPECT_VERIFIES(v_v, WASM_ZERO, WASM_IF_OP, kExprElse, kExprEnd); } TEST_F(AstDecoderTest, If_empty3) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_ZERO, kExprIf, WASM_ZERO, kExprElse, - kExprEnd); + EXPECT_VERIFIES(v_v, WASM_ZERO, WASM_IF_OP, WASM_NOP, kExprElse, kExprEnd); + EXPECT_FAILURE(v_v, WASM_ZERO, WASM_IF_OP, WASM_ZERO, kExprElse, kExprEnd); } TEST_F(AstDecoderTest, If_empty4) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_ZERO, kExprIf, kExprElse, WASM_ZERO, - kExprEnd); + EXPECT_VERIFIES(v_v, WASM_ZERO, WASM_IF_OP, kExprElse, WASM_NOP, kExprEnd); + EXPECT_FAILURE(v_v, WASM_ZERO, WASM_IF_OP, kExprElse, WASM_ZERO, kExprEnd); } TEST_F(AstDecoderTest, If_empty_stack) { byte code[] = {kExprIf}; - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, If_incomplete1) { byte code[] = {kExprI8Const, 0, kExprIf}; - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, If_incomplete2) { byte code[] = {kExprI8Const, 0, kExprIf, kExprNop}; - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, If_else_else) { - byte code[] = {kExprI8Const, 0, kExprIf, kExprElse, kExprElse, kExprEnd}; - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + byte code[] = {kExprI8Const, 0, WASM_IF_OP, kExprElse, kExprElse, kExprEnd}; + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, IfEmpty) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), kExprGetLocal, 0, kExprIf, kExprEnd); + EXPECT_VERIFIES(v_i, kExprGetLocal, 0, WASM_IF_OP, kExprEnd); } TEST_F(AstDecoderTest, IfSet) { - EXPECT_VERIFIES_INLINE( - sigs.v_i(), WASM_IF(WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_ZERO))); - EXPECT_VERIFIES_INLINE( - sigs.v_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_ZERO), WASM_NOP)); + EXPECT_VERIFIES(v_i, + WASM_IF(WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_ZERO))); + EXPECT_VERIFIES(v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), + WASM_SET_LOCAL(0, WASM_ZERO), WASM_NOP)); } TEST_F(AstDecoderTest, IfElseEmpty) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), WASM_GET_LOCAL(0), kExprIf, kExprElse, - kExprEnd); - EXPECT_VERIFIES_INLINE(sigs.v_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(v_i, WASM_GET_LOCAL(0), WASM_IF_OP, kExprElse, kExprEnd); + EXPECT_VERIFIES(v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP)); } TEST_F(AstDecoderTest, IfElseUnreachable1) { - EXPECT_VERIFIES_INLINE( - sigs.i_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_UNREACHABLE, WASM_GET_LOCAL(0))); - EXPECT_VERIFIES_INLINE( - sigs.i_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_UNREACHABLE)); + EXPECT_VERIFIES(i_i, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_UNREACHABLE, + WASM_GET_LOCAL(0))); + EXPECT_VERIFIES(i_i, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), + WASM_UNREACHABLE)); } TEST_F(AstDecoderTest, IfElseUnreachable2) { static const byte code[] = { - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_UNREACHABLE, WASM_GET_LOCAL(0))}; + WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_UNREACHABLE, WASM_GET_LOCAL(0))}; for (size_t i = 0; i < arraysize(kLocalTypes); i++) { LocalType types[] = {kAstI32, kLocalTypes[i]}; FunctionSig sig(1, 1, types); if (kLocalTypes[i] == kAstI32) { - EXPECT_VERIFIES(&sig, code); + EXPECT_VERIFIES_SC(&sig, code); } else { - EXPECT_FAILURE(&sig, code); + EXPECT_FAILURE_SC(&sig, code); } } } TEST_F(AstDecoderTest, IfBreak) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), WASM_IF(WASM_GET_LOCAL(0), WASM_BR(0))); - EXPECT_FAILURE_INLINE(sigs.v_i(), WASM_IF(WASM_GET_LOCAL(0), WASM_BR(1))); + EXPECT_VERIFIES(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_BR(0))); + EXPECT_VERIFIES(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_BR(1))); + EXPECT_FAILURE(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_BR(2))); } TEST_F(AstDecoderTest, IfElseBreak) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_BR(0))); - EXPECT_FAILURE_INLINE(sigs.v_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_BR(1))); + EXPECT_VERIFIES(v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_BR(0))); + EXPECT_VERIFIES(v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_BR(1))); + EXPECT_FAILURE(v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_BR(2))); } TEST_F(AstDecoderTest, Block_else) { byte code[] = {kExprI8Const, 0, kExprBlock, kExprElse, kExprEnd}; - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, IfNop) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), WASM_IF(WASM_GET_LOCAL(0), WASM_NOP)); + EXPECT_VERIFIES(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_NOP)); } TEST_F(AstDecoderTest, IfNopElseNop) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP)); } TEST_F(AstDecoderTest, If_end_end) { - static const byte code[] = {kExprGetLocal, 0, kExprIf, kExprEnd, kExprEnd}; - EXPECT_FAILURE(sigs.v_i(), code); + static const byte code[] = {kExprGetLocal, 0, WASM_IF_OP, kExprEnd, kExprEnd}; + EXPECT_VERIFIES_C(v_i, code); } -TEST_F(AstDecoderTest, If_falloff) { +TEST_F(AstDecoderTest, If_end_end_end) { + static const byte code[] = {kExprGetLocal, 0, WASM_IF_OP, + kExprEnd, kExprEnd, kExprEnd}; + EXPECT_FAILURE_C(v_i, code); +} + +TEST_F(AstDecoderTest, If_falloff1) { static const byte code[] = {kExprGetLocal, 0, kExprIf}; - EXPECT_FAILURE(sigs.v_i(), code); + EXPECT_FAILURE_C(v_i, code); +} + +TEST_F(AstDecoderTest, If_falloff2) { + static const byte code[] = {kExprGetLocal, 0, WASM_IF_OP}; + EXPECT_FAILURE_C(v_i, code); } TEST_F(AstDecoderTest, IfElse_falloff) { - static const byte code[] = {kExprGetLocal, 0, kExprIf, kExprNop, kExprElse}; - EXPECT_FAILURE(sigs.v_i(), code); + static const byte code[] = {kExprGetLocal, 0, WASM_IF_OP, kExprNop, + kExprElse}; + EXPECT_FAILURE_C(v_i, code); } TEST_F(AstDecoderTest, IfElseNop) { - EXPECT_VERIFIES_INLINE( - sigs.v_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_ZERO), WASM_NOP)); + EXPECT_VERIFIES(v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), + WASM_SET_LOCAL(0, WASM_ZERO), WASM_NOP)); } TEST_F(AstDecoderTest, IfBlock1) { - EXPECT_VERIFIES_INLINE( - sigs.v_i(), WASM_IF_ELSE(WASM_GET_LOCAL(0), - B1(WASM_SET_LOCAL(0, WASM_ZERO)), WASM_NOP)); + EXPECT_VERIFIES( + v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), B1(WASM_SET_LOCAL(0, WASM_ZERO)), + WASM_NOP)); } TEST_F(AstDecoderTest, IfBlock1b) { - EXPECT_VERIFIES_INLINE( - sigs.v_i(), WASM_IF(WASM_GET_LOCAL(0), B1(WASM_SET_LOCAL(0, WASM_ZERO)))); + EXPECT_VERIFIES(v_i, + WASM_IF(WASM_GET_LOCAL(0), B1(WASM_SET_LOCAL(0, WASM_ZERO)))); } TEST_F(AstDecoderTest, IfBlock2a) { - EXPECT_VERIFIES_INLINE( - sigs.v_i(), WASM_IF(WASM_GET_LOCAL(0), B2(WASM_SET_LOCAL(0, WASM_ZERO), + EXPECT_VERIFIES(v_i, + WASM_IF(WASM_GET_LOCAL(0), B2(WASM_SET_LOCAL(0, WASM_ZERO), WASM_SET_LOCAL(0, WASM_ZERO)))); } TEST_F(AstDecoderTest, IfBlock2b) { - EXPECT_VERIFIES_INLINE( - sigs.v_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), B2(WASM_SET_LOCAL(0, WASM_ZERO), - WASM_SET_LOCAL(0, WASM_ZERO)), - WASM_NOP)); + EXPECT_VERIFIES( + v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), B2(WASM_SET_LOCAL(0, WASM_ZERO), + WASM_SET_LOCAL(0, WASM_ZERO)), + WASM_NOP)); } TEST_F(AstDecoderTest, IfElseSet) { - EXPECT_VERIFIES_INLINE( - sigs.v_i(), WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_ZERO), + EXPECT_VERIFIES(v_i, + WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_SET_LOCAL(0, WASM_ZERO), WASM_SET_LOCAL(0, WASM_I8(1)))); } -TEST_F(AstDecoderTest, Loop0) { - static const byte code[] = {kExprLoop, kExprEnd}; - EXPECT_VERIFIES(sigs.v_v(), code); -} +TEST_F(AstDecoderTest, Loop0) { EXPECT_VERIFIES(v_v, WASM_LOOP_OP, kExprEnd); } TEST_F(AstDecoderTest, Loop1) { static const byte code[] = {WASM_LOOP(WASM_SET_LOCAL(0, WASM_ZERO))}; - EXPECT_VERIFIES(sigs.v_i(), code); - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.f_ff(), code); + EXPECT_VERIFIES_C(v_i, code); + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(f_ff, code); } TEST_F(AstDecoderTest, Loop2) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), WASM_LOOP(WASM_SET_LOCAL(0, WASM_ZERO), - WASM_SET_LOCAL(0, WASM_ZERO))); + EXPECT_VERIFIES(v_i, WASM_LOOP(WASM_SET_LOCAL(0, WASM_ZERO), + WASM_SET_LOCAL(0, WASM_ZERO))); } TEST_F(AstDecoderTest, Loop1_continue) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BR(0))); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_BR(0))); } TEST_F(AstDecoderTest, Loop1_break) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BR(1))); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_BR(1))); } TEST_F(AstDecoderTest, Loop2_continue) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), - WASM_LOOP(WASM_SET_LOCAL(0, WASM_ZERO), WASM_BR(0))); + EXPECT_VERIFIES(v_i, WASM_LOOP(WASM_SET_LOCAL(0, WASM_ZERO), WASM_BR(0))); } TEST_F(AstDecoderTest, Loop2_break) { - EXPECT_VERIFIES_INLINE(sigs.v_i(), - WASM_LOOP(WASM_SET_LOCAL(0, WASM_ZERO), WASM_BR(1))); + EXPECT_VERIFIES(v_i, WASM_LOOP(WASM_SET_LOCAL(0, WASM_ZERO), WASM_BR(1))); } -TEST_F(AstDecoderTest, ExprLoop0) { - static const byte code[] = {kExprLoop, kExprEnd}; - EXPECT_VERIFIES(sigs.v_v(), code); +TEST_F(AstDecoderTest, InfiniteLoop) { + EXPECT_VERIFIES(i_i, WASM_LOOP(WASM_BR(0))); + EXPECT_VERIFIES(i_i, WASM_LOOP(WASM_BRV(1, WASM_ZERO))); } -TEST_F(AstDecoderTest, ExprLoop1a) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_LOOP(WASM_BRV(0, WASM_ZERO))); +TEST_F(AstDecoderTest, Loop2_unreachable) { + EXPECT_VERIFIES(i_i, WASM_LOOP(WASM_BR(0), WASM_NOP)); } -TEST_F(AstDecoderTest, ExprLoop1b) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_LOOP(WASM_BRV(1, WASM_ZERO))); - EXPECT_FAILURE_INLINE(sigs.f_ff(), WASM_LOOP(WASM_BRV(1, WASM_ZERO))); +TEST_F(AstDecoderTest, LoopType) { + EXPECT_VERIFIES(i_i, WASM_LOOP_I(WASM_GET_LOCAL(0))); + EXPECT_VERIFIES(l_l, WASM_LOOP_L(WASM_GET_LOCAL(0))); + EXPECT_VERIFIES(f_f, WASM_LOOP_F(WASM_GET_LOCAL(0))); + EXPECT_VERIFIES(d_d, WASM_LOOP_D(WASM_GET_LOCAL(0))); } -TEST_F(AstDecoderTest, ExprLoop2_unreachable) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_LOOP(WASM_BR(0), WASM_NOP)); +TEST_F(AstDecoderTest, LoopType_void) { + EXPECT_FAILURE(v_v, WASM_LOOP_I(WASM_ZERO)); + EXPECT_FAILURE(v_v, WASM_LOOP_L(WASM_I64V_1(0))); + EXPECT_FAILURE(v_v, WASM_LOOP_F(WASM_F32(0.0))); + EXPECT_FAILURE(v_v, WASM_LOOP_D(WASM_F64(1.1))); +} + +TEST_F(AstDecoderTest, LoopType_fail) { + EXPECT_FAILURE(i_i, WASM_LOOP_L(WASM_I64V_1(0))); + EXPECT_FAILURE(i_i, WASM_LOOP_F(WASM_F32(0.0))); + EXPECT_FAILURE(i_i, WASM_LOOP_D(WASM_F64(1.1))); + + EXPECT_FAILURE(l_l, WASM_LOOP_I(WASM_ZERO)); + EXPECT_FAILURE(l_l, WASM_LOOP_F(WASM_F32(0.0))); + EXPECT_FAILURE(l_l, WASM_LOOP_D(WASM_F64(1.1))); + + EXPECT_FAILURE(f_ff, WASM_LOOP_I(WASM_ZERO)); + EXPECT_FAILURE(f_ff, WASM_LOOP_L(WASM_I64V_1(0))); + EXPECT_FAILURE(f_ff, WASM_LOOP_D(WASM_F64(1.1))); + + EXPECT_FAILURE(d_dd, WASM_LOOP_I(WASM_ZERO)); + EXPECT_FAILURE(d_dd, WASM_LOOP_L(WASM_I64V_1(0))); + EXPECT_FAILURE(d_dd, WASM_LOOP_F(WASM_F32(0.0))); } TEST_F(AstDecoderTest, ReturnVoid1) { static const byte code[] = {kExprNop}; - EXPECT_VERIFIES(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); - EXPECT_FAILURE(sigs.i_f(), code); + EXPECT_VERIFIES_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); + EXPECT_FAILURE_C(i_f, code); } TEST_F(AstDecoderTest, ReturnVoid2) { - static const byte code[] = {kExprBlock, kExprBr, ARITY_0, DEPTH_0, kExprEnd}; - EXPECT_VERIFIES(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); - EXPECT_FAILURE(sigs.i_f(), code); + static const byte code[] = {WASM_BLOCK(WASM_BR(0))}; + EXPECT_VERIFIES_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); + EXPECT_FAILURE_C(i_f, code); } TEST_F(AstDecoderTest, ReturnVoid3) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprI8Const, 0); - EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprI32Const, 0, 0, 0, 0); - EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprI64Const, 0, 0, 0, 0, 0, 0, 0, 0); - EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprF32Const, 0, 0, 0, 0); - EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprF64Const, 0, 0, 0, 0, 0, 0, 0, 0); + EXPECT_FAILURE(v_v, kExprI8Const, 0); + EXPECT_FAILURE(v_v, kExprI32Const, 0); + EXPECT_FAILURE(v_v, kExprI64Const, 0); + EXPECT_FAILURE(v_v, kExprF32Const, 0, 0, 0, 0); + EXPECT_FAILURE(v_v, kExprF64Const, 0, 0, 0, 0, 0, 0, 0, 0); - EXPECT_VERIFIES_INLINE(sigs.v_i(), kExprGetLocal, 0); + EXPECT_FAILURE(v_i, kExprGetLocal, 0); } TEST_F(AstDecoderTest, Unreachable1) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprUnreachable); - EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprUnreachable, kExprUnreachable); - EXPECT_VERIFIES_INLINE(sigs.v_v(), B2(WASM_UNREACHABLE, WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.v_v(), B2(WASM_BR(0), WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_UNREACHABLE, WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BR(0), WASM_ZERO)); + EXPECT_VERIFIES(v_v, kExprUnreachable); + EXPECT_VERIFIES(v_v, kExprUnreachable, kExprUnreachable); + EXPECT_VERIFIES(v_v, B2(WASM_UNREACHABLE, WASM_ZERO)); + EXPECT_VERIFIES(v_v, B2(WASM_BR(0), WASM_ZERO)); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_UNREACHABLE, WASM_ZERO)); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_BR(0), WASM_ZERO)); } TEST_F(AstDecoderTest, Unreachable_binop) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_I32_AND(WASM_ZERO, WASM_UNREACHABLE)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_I32_AND(WASM_UNREACHABLE, WASM_ZERO)); + EXPECT_VERIFIES(i_i, WASM_I32_AND(WASM_ZERO, WASM_UNREACHABLE)); + EXPECT_VERIFIES(i_i, WASM_I32_AND(WASM_UNREACHABLE, WASM_ZERO)); } TEST_F(AstDecoderTest, Unreachable_select) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), - WASM_SELECT(WASM_UNREACHABLE, WASM_ZERO, WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), - WASM_SELECT(WASM_ZERO, WASM_UNREACHABLE, WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), - WASM_SELECT(WASM_ZERO, WASM_ZERO, WASM_UNREACHABLE)); + EXPECT_VERIFIES(i_i, WASM_SELECT(WASM_UNREACHABLE, WASM_ZERO, WASM_ZERO)); + EXPECT_VERIFIES(i_i, WASM_SELECT(WASM_ZERO, WASM_UNREACHABLE, WASM_ZERO)); + EXPECT_VERIFIES(i_i, WASM_SELECT(WASM_ZERO, WASM_ZERO, WASM_UNREACHABLE)); } TEST_F(AstDecoderTest, If1) { - EXPECT_VERIFIES_INLINE( - sigs.i_i(), WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_I8(9), WASM_I8(8))); - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_I8(9), - WASM_GET_LOCAL(0))); - EXPECT_VERIFIES_INLINE( - sigs.i_i(), - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_I8(8))); + EXPECT_VERIFIES(i_i, + WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_I8(9), WASM_I8(8))); + EXPECT_VERIFIES( + i_i, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_I8(9), WASM_GET_LOCAL(0))); + EXPECT_VERIFIES( + i_i, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_I8(8))); } TEST_F(AstDecoderTest, If_off_end) { @@ -828,55 +904,56 @@ TEST_F(AstDecoderTest, If_off_end) { TEST_F(AstDecoderTest, If_type1) { // float|double ? 1 : 2 static const byte kCode[] = { - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_I8(0), WASM_I8(2))}; - EXPECT_VERIFIES(sigs.i_i(), kCode); - EXPECT_FAILURE(sigs.i_f(), kCode); - EXPECT_FAILURE(sigs.i_d(), kCode); + WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_I8(0), WASM_I8(2))}; + EXPECT_VERIFIES_C(i_i, kCode); + EXPECT_FAILURE_C(i_f, kCode); + EXPECT_FAILURE_C(i_d, kCode); } TEST_F(AstDecoderTest, If_type2) { // 1 ? float|double : 2 static const byte kCode[] = { - WASM_IF_ELSE(WASM_I8(1), WASM_GET_LOCAL(0), WASM_I8(1))}; - EXPECT_VERIFIES(sigs.i_i(), kCode); - EXPECT_FAILURE(sigs.i_f(), kCode); - EXPECT_FAILURE(sigs.i_d(), kCode); + WASM_IF_ELSE_I(WASM_I8(1), WASM_GET_LOCAL(0), WASM_I8(1))}; + EXPECT_VERIFIES_C(i_i, kCode); + EXPECT_FAILURE_C(i_f, kCode); + EXPECT_FAILURE_C(i_d, kCode); } TEST_F(AstDecoderTest, If_type3) { // stmt ? 0 : 1 - static const byte kCode[] = {WASM_IF_ELSE(WASM_NOP, WASM_I8(0), WASM_I8(1))}; - EXPECT_FAILURE(sigs.i_i(), kCode); - EXPECT_FAILURE(sigs.i_f(), kCode); - EXPECT_FAILURE(sigs.i_d(), kCode); + static const byte kCode[] = { + WASM_IF_ELSE_I(WASM_NOP, WASM_I8(0), WASM_I8(1))}; + EXPECT_FAILURE_C(i_i, kCode); + EXPECT_FAILURE_C(i_f, kCode); + EXPECT_FAILURE_C(i_d, kCode); } TEST_F(AstDecoderTest, If_type4) { // 0 ? stmt : 1 static const byte kCode[] = { - WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_I8(1))}; - EXPECT_FAILURE(sigs.i_i(), kCode); - EXPECT_FAILURE(sigs.i_f(), kCode); - EXPECT_FAILURE(sigs.i_d(), kCode); + WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_NOP, WASM_I8(1))}; + EXPECT_FAILURE_C(i_i, kCode); + EXPECT_FAILURE_C(i_f, kCode); + EXPECT_FAILURE_C(i_d, kCode); } TEST_F(AstDecoderTest, If_type5) { // 0 ? 1 : stmt - static const byte kCode[] = {WASM_IF_ELSE(WASM_ZERO, WASM_I8(1), WASM_NOP)}; - EXPECT_FAILURE(sigs.i_i(), kCode); - EXPECT_FAILURE(sigs.i_f(), kCode); - EXPECT_FAILURE(sigs.i_d(), kCode); + static const byte kCode[] = {WASM_IF_ELSE_I(WASM_ZERO, WASM_I8(1), WASM_NOP)}; + EXPECT_FAILURE_C(i_i, kCode); + EXPECT_FAILURE_C(i_f, kCode); + EXPECT_FAILURE_C(i_d, kCode); } TEST_F(AstDecoderTest, Int64Local_param) { - EXPECT_VERIFIES(sigs.l_l(), kCodeGetLocal0); + EXPECT_VERIFIES_C(l_l, kCodeGetLocal0); } TEST_F(AstDecoderTest, Int64Locals) { for (byte i = 1; i < 8; i++) { AddLocals(kAstI64, 1); for (byte j = 0; j < i; j++) { - EXPECT_VERIFIES_INLINE(sigs.l_v(), WASM_GET_LOCAL(j)); + EXPECT_VERIFIES(l_v, WASM_GET_LOCAL(j)); } } } @@ -938,132 +1015,120 @@ TEST_F(AstDecoderTest, TypeConversions) { } TEST_F(AstDecoderTest, MacrosStmt) { - VERIFY(WASM_SET_LOCAL(0, WASM_I32V_3(87348))); - VERIFY(WASM_STORE_MEM(MachineType::Int32(), WASM_I8(24), WASM_I8(40))); - VERIFY(WASM_IF(WASM_GET_LOCAL(0), WASM_NOP)); - VERIFY(WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP)); - VERIFY(WASM_NOP); - VERIFY(B1(WASM_NOP)); - VERIFY(WASM_LOOP(WASM_NOP)); - VERIFY(WASM_LOOP(WASM_BREAK(0))); - VERIFY(WASM_LOOP(WASM_CONTINUE(0))); -} - -TEST_F(AstDecoderTest, MacrosBreak) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BREAK(0))); - - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_LOOP(WASM_BREAKV(0, WASM_ZERO))); - EXPECT_VERIFIES_INLINE(sigs.l_l(), WASM_LOOP(WASM_BREAKV(0, WASM_I64V_1(0)))); - EXPECT_VERIFIES_INLINE(sigs.f_ff(), WASM_LOOP(WASM_BREAKV(0, WASM_F32(0.0)))); - EXPECT_VERIFIES_INLINE(sigs.d_dd(), WASM_LOOP(WASM_BREAKV(0, WASM_F64(0.0)))); + EXPECT_VERIFIES(v_i, WASM_SET_LOCAL(0, WASM_I32V_3(87348))); + EXPECT_VERIFIES( + v_i, WASM_STORE_MEM(MachineType::Int32(), WASM_I8(24), WASM_I8(40))); + EXPECT_VERIFIES(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_NOP)); + EXPECT_VERIFIES(v_i, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(v_v, WASM_NOP); + EXPECT_VERIFIES(v_v, B1(WASM_NOP)); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_NOP)); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_BR(0))); } TEST_F(AstDecoderTest, MacrosContinue) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_CONTINUE(0))); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_CONTINUE(0))); } TEST_F(AstDecoderTest, MacrosVariadic) { - VERIFY(B2(WASM_NOP, WASM_NOP)); - VERIFY(B3(WASM_NOP, WASM_NOP, WASM_NOP)); - VERIFY(WASM_LOOP(WASM_NOP, WASM_NOP)); - VERIFY(WASM_LOOP(WASM_NOP, WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(v_v, B2(WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(v_v, B3(WASM_NOP, WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(v_v, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_NOP)); } TEST_F(AstDecoderTest, MacrosNestedBlocks) { - VERIFY(B2(WASM_NOP, B2(WASM_NOP, WASM_NOP))); - VERIFY(B3(WASM_NOP, // -- - B2(WASM_NOP, WASM_NOP), // -- - B2(WASM_NOP, WASM_NOP))); // -- - VERIFY(B1(B1(B2(WASM_NOP, WASM_NOP)))); + EXPECT_VERIFIES(v_v, B2(WASM_NOP, B2(WASM_NOP, WASM_NOP))); + EXPECT_VERIFIES(v_v, B3(WASM_NOP, // -- + B2(WASM_NOP, WASM_NOP), // -- + B2(WASM_NOP, WASM_NOP))); // -- + EXPECT_VERIFIES(v_v, B1(B1(B2(WASM_NOP, WASM_NOP)))); } TEST_F(AstDecoderTest, MultipleReturn) { static LocalType kIntTypes5[] = {kAstI32, kAstI32, kAstI32, kAstI32, kAstI32}; FunctionSig sig_ii_v(2, 0, kIntTypes5); - EXPECT_VERIFIES_INLINE(&sig_ii_v, WASM_RETURNN(2, WASM_ZERO, WASM_ONE)); - EXPECT_FAILURE_INLINE(&sig_ii_v, WASM_RETURNN(1, WASM_ZERO)); + EXPECT_VERIFIES_S(&sig_ii_v, WASM_RETURNN(2, WASM_ZERO, WASM_ONE)); + EXPECT_FAILURE_S(&sig_ii_v, WASM_RETURNN(1, WASM_ZERO)); FunctionSig sig_iii_v(3, 0, kIntTypes5); - EXPECT_VERIFIES_INLINE(&sig_iii_v, - WASM_RETURNN(3, WASM_ZERO, WASM_ONE, WASM_I8(44))); - EXPECT_FAILURE_INLINE(&sig_iii_v, WASM_RETURNN(2, WASM_ZERO, WASM_ONE)); + EXPECT_VERIFIES_S(&sig_iii_v, + WASM_RETURNN(3, WASM_ZERO, WASM_ONE, WASM_I8(44))); + EXPECT_FAILURE_S(&sig_iii_v, WASM_RETURNN(2, WASM_ZERO, WASM_ONE)); } TEST_F(AstDecoderTest, MultipleReturn_fallthru) { static LocalType kIntTypes5[] = {kAstI32, kAstI32, kAstI32, kAstI32, kAstI32}; FunctionSig sig_ii_v(2, 0, kIntTypes5); - EXPECT_VERIFIES_INLINE(&sig_ii_v, WASM_ZERO, WASM_ONE); - EXPECT_FAILURE_INLINE(&sig_ii_v, WASM_ZERO); + EXPECT_VERIFIES_S(&sig_ii_v, WASM_ZERO, WASM_ONE); + EXPECT_FAILURE_S(&sig_ii_v, WASM_ZERO); FunctionSig sig_iii_v(3, 0, kIntTypes5); - EXPECT_VERIFIES_INLINE(&sig_iii_v, WASM_ZERO, WASM_ONE, WASM_I8(44)); - EXPECT_FAILURE_INLINE(&sig_iii_v, WASM_ZERO, WASM_ONE); + EXPECT_VERIFIES_S(&sig_iii_v, WASM_ZERO, WASM_ONE, WASM_I8(44)); + EXPECT_FAILURE_S(&sig_iii_v, WASM_ZERO, WASM_ONE); } TEST_F(AstDecoderTest, MacrosInt32) { - VERIFY(WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_I8(12))); - VERIFY(WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(13))); - VERIFY(WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_I8(14))); - VERIFY(WASM_I32_DIVS(WASM_GET_LOCAL(0), WASM_I8(15))); - VERIFY(WASM_I32_DIVU(WASM_GET_LOCAL(0), WASM_I8(16))); - VERIFY(WASM_I32_REMS(WASM_GET_LOCAL(0), WASM_I8(17))); - VERIFY(WASM_I32_REMU(WASM_GET_LOCAL(0), WASM_I8(18))); - VERIFY(WASM_I32_AND(WASM_GET_LOCAL(0), WASM_I8(19))); - VERIFY(WASM_I32_IOR(WASM_GET_LOCAL(0), WASM_I8(20))); - VERIFY(WASM_I32_XOR(WASM_GET_LOCAL(0), WASM_I8(21))); - VERIFY(WASM_I32_SHL(WASM_GET_LOCAL(0), WASM_I8(22))); - VERIFY(WASM_I32_SHR(WASM_GET_LOCAL(0), WASM_I8(23))); - VERIFY(WASM_I32_SAR(WASM_GET_LOCAL(0), WASM_I8(24))); - VERIFY(WASM_I32_ROR(WASM_GET_LOCAL(0), WASM_I8(24))); - VERIFY(WASM_I32_ROL(WASM_GET_LOCAL(0), WASM_I8(24))); - VERIFY(WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(25))); - VERIFY(WASM_I32_NE(WASM_GET_LOCAL(0), WASM_I8(25))); + EXPECT_VERIFIES(i_i, WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_I8(12))); + EXPECT_VERIFIES(i_i, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(13))); + EXPECT_VERIFIES(i_i, WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_I8(14))); + EXPECT_VERIFIES(i_i, WASM_I32_DIVS(WASM_GET_LOCAL(0), WASM_I8(15))); + EXPECT_VERIFIES(i_i, WASM_I32_DIVU(WASM_GET_LOCAL(0), WASM_I8(16))); + EXPECT_VERIFIES(i_i, WASM_I32_REMS(WASM_GET_LOCAL(0), WASM_I8(17))); + EXPECT_VERIFIES(i_i, WASM_I32_REMU(WASM_GET_LOCAL(0), WASM_I8(18))); + EXPECT_VERIFIES(i_i, WASM_I32_AND(WASM_GET_LOCAL(0), WASM_I8(19))); + EXPECT_VERIFIES(i_i, WASM_I32_IOR(WASM_GET_LOCAL(0), WASM_I8(20))); + EXPECT_VERIFIES(i_i, WASM_I32_XOR(WASM_GET_LOCAL(0), WASM_I8(21))); + EXPECT_VERIFIES(i_i, WASM_I32_SHL(WASM_GET_LOCAL(0), WASM_I8(22))); + EXPECT_VERIFIES(i_i, WASM_I32_SHR(WASM_GET_LOCAL(0), WASM_I8(23))); + EXPECT_VERIFIES(i_i, WASM_I32_SAR(WASM_GET_LOCAL(0), WASM_I8(24))); + EXPECT_VERIFIES(i_i, WASM_I32_ROR(WASM_GET_LOCAL(0), WASM_I8(24))); + EXPECT_VERIFIES(i_i, WASM_I32_ROL(WASM_GET_LOCAL(0), WASM_I8(24))); + EXPECT_VERIFIES(i_i, WASM_I32_EQ(WASM_GET_LOCAL(0), WASM_I8(25))); + EXPECT_VERIFIES(i_i, WASM_I32_NE(WASM_GET_LOCAL(0), WASM_I8(25))); - VERIFY(WASM_I32_LTS(WASM_GET_LOCAL(0), WASM_I8(26))); - VERIFY(WASM_I32_LES(WASM_GET_LOCAL(0), WASM_I8(27))); - VERIFY(WASM_I32_LTU(WASM_GET_LOCAL(0), WASM_I8(28))); - VERIFY(WASM_I32_LEU(WASM_GET_LOCAL(0), WASM_I8(29))); + EXPECT_VERIFIES(i_i, WASM_I32_LTS(WASM_GET_LOCAL(0), WASM_I8(26))); + EXPECT_VERIFIES(i_i, WASM_I32_LES(WASM_GET_LOCAL(0), WASM_I8(27))); + EXPECT_VERIFIES(i_i, WASM_I32_LTU(WASM_GET_LOCAL(0), WASM_I8(28))); + EXPECT_VERIFIES(i_i, WASM_I32_LEU(WASM_GET_LOCAL(0), WASM_I8(29))); - VERIFY(WASM_I32_GTS(WASM_GET_LOCAL(0), WASM_I8(26))); - VERIFY(WASM_I32_GES(WASM_GET_LOCAL(0), WASM_I8(27))); - VERIFY(WASM_I32_GTU(WASM_GET_LOCAL(0), WASM_I8(28))); - VERIFY(WASM_I32_GEU(WASM_GET_LOCAL(0), WASM_I8(29))); + EXPECT_VERIFIES(i_i, WASM_I32_GTS(WASM_GET_LOCAL(0), WASM_I8(26))); + EXPECT_VERIFIES(i_i, WASM_I32_GES(WASM_GET_LOCAL(0), WASM_I8(27))); + EXPECT_VERIFIES(i_i, WASM_I32_GTU(WASM_GET_LOCAL(0), WASM_I8(28))); + EXPECT_VERIFIES(i_i, WASM_I32_GEU(WASM_GET_LOCAL(0), WASM_I8(29))); } TEST_F(AstDecoderTest, MacrosInt64) { -#define VERIFY_L_LL(...) EXPECT_VERIFIES_INLINE(sigs.l_ll(), __VA_ARGS__) -#define VERIFY_I_LL(...) EXPECT_VERIFIES_INLINE(sigs.i_ll(), __VA_ARGS__) + EXPECT_VERIFIES(l_ll, WASM_I64_ADD(WASM_GET_LOCAL(0), WASM_I64V_1(12))); + EXPECT_VERIFIES(l_ll, WASM_I64_SUB(WASM_GET_LOCAL(0), WASM_I64V_1(13))); + EXPECT_VERIFIES(l_ll, WASM_I64_MUL(WASM_GET_LOCAL(0), WASM_I64V_1(14))); + EXPECT_VERIFIES(l_ll, WASM_I64_DIVS(WASM_GET_LOCAL(0), WASM_I64V_1(15))); + EXPECT_VERIFIES(l_ll, WASM_I64_DIVU(WASM_GET_LOCAL(0), WASM_I64V_1(16))); + EXPECT_VERIFIES(l_ll, WASM_I64_REMS(WASM_GET_LOCAL(0), WASM_I64V_1(17))); + EXPECT_VERIFIES(l_ll, WASM_I64_REMU(WASM_GET_LOCAL(0), WASM_I64V_1(18))); + EXPECT_VERIFIES(l_ll, WASM_I64_AND(WASM_GET_LOCAL(0), WASM_I64V_1(19))); + EXPECT_VERIFIES(l_ll, WASM_I64_IOR(WASM_GET_LOCAL(0), WASM_I64V_1(20))); + EXPECT_VERIFIES(l_ll, WASM_I64_XOR(WASM_GET_LOCAL(0), WASM_I64V_1(21))); - VERIFY_L_LL(WASM_I64_ADD(WASM_GET_LOCAL(0), WASM_I64V_1(12))); - VERIFY_L_LL(WASM_I64_SUB(WASM_GET_LOCAL(0), WASM_I64V_1(13))); - VERIFY_L_LL(WASM_I64_MUL(WASM_GET_LOCAL(0), WASM_I64V_1(14))); - VERIFY_L_LL(WASM_I64_DIVS(WASM_GET_LOCAL(0), WASM_I64V_1(15))); - VERIFY_L_LL(WASM_I64_DIVU(WASM_GET_LOCAL(0), WASM_I64V_1(16))); - VERIFY_L_LL(WASM_I64_REMS(WASM_GET_LOCAL(0), WASM_I64V_1(17))); - VERIFY_L_LL(WASM_I64_REMU(WASM_GET_LOCAL(0), WASM_I64V_1(18))); - VERIFY_L_LL(WASM_I64_AND(WASM_GET_LOCAL(0), WASM_I64V_1(19))); - VERIFY_L_LL(WASM_I64_IOR(WASM_GET_LOCAL(0), WASM_I64V_1(20))); - VERIFY_L_LL(WASM_I64_XOR(WASM_GET_LOCAL(0), WASM_I64V_1(21))); + EXPECT_VERIFIES(l_ll, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_I64V_1(22))); + EXPECT_VERIFIES(l_ll, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_I64V_1(23))); + EXPECT_VERIFIES(l_ll, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_I64V_1(24))); + EXPECT_VERIFIES(l_ll, WASM_I64_ROR(WASM_GET_LOCAL(0), WASM_I64V_1(24))); + EXPECT_VERIFIES(l_ll, WASM_I64_ROL(WASM_GET_LOCAL(0), WASM_I64V_1(24))); - VERIFY_L_LL(WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_I64V_1(22))); - VERIFY_L_LL(WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_I64V_1(23))); - VERIFY_L_LL(WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_I64V_1(24))); - VERIFY_L_LL(WASM_I64_ROR(WASM_GET_LOCAL(0), WASM_I64V_1(24))); - VERIFY_L_LL(WASM_I64_ROL(WASM_GET_LOCAL(0), WASM_I64V_1(24))); + EXPECT_VERIFIES(i_ll, WASM_I64_LTS(WASM_GET_LOCAL(0), WASM_I64V_1(26))); + EXPECT_VERIFIES(i_ll, WASM_I64_LES(WASM_GET_LOCAL(0), WASM_I64V_1(27))); + EXPECT_VERIFIES(i_ll, WASM_I64_LTU(WASM_GET_LOCAL(0), WASM_I64V_1(28))); + EXPECT_VERIFIES(i_ll, WASM_I64_LEU(WASM_GET_LOCAL(0), WASM_I64V_1(29))); - VERIFY_I_LL(WASM_I64_LTS(WASM_GET_LOCAL(0), WASM_I64V_1(26))); - VERIFY_I_LL(WASM_I64_LES(WASM_GET_LOCAL(0), WASM_I64V_1(27))); - VERIFY_I_LL(WASM_I64_LTU(WASM_GET_LOCAL(0), WASM_I64V_1(28))); - VERIFY_I_LL(WASM_I64_LEU(WASM_GET_LOCAL(0), WASM_I64V_1(29))); + EXPECT_VERIFIES(i_ll, WASM_I64_GTS(WASM_GET_LOCAL(0), WASM_I64V_1(26))); + EXPECT_VERIFIES(i_ll, WASM_I64_GES(WASM_GET_LOCAL(0), WASM_I64V_1(27))); + EXPECT_VERIFIES(i_ll, WASM_I64_GTU(WASM_GET_LOCAL(0), WASM_I64V_1(28))); + EXPECT_VERIFIES(i_ll, WASM_I64_GEU(WASM_GET_LOCAL(0), WASM_I64V_1(29))); - VERIFY_I_LL(WASM_I64_GTS(WASM_GET_LOCAL(0), WASM_I64V_1(26))); - VERIFY_I_LL(WASM_I64_GES(WASM_GET_LOCAL(0), WASM_I64V_1(27))); - VERIFY_I_LL(WASM_I64_GTU(WASM_GET_LOCAL(0), WASM_I64V_1(28))); - VERIFY_I_LL(WASM_I64_GEU(WASM_GET_LOCAL(0), WASM_I64V_1(29))); - - VERIFY_I_LL(WASM_I64_EQ(WASM_GET_LOCAL(0), WASM_I64V_1(25))); - VERIFY_I_LL(WASM_I64_NE(WASM_GET_LOCAL(0), WASM_I64V_1(25))); + EXPECT_VERIFIES(i_ll, WASM_I64_EQ(WASM_GET_LOCAL(0), WASM_I64V_1(25))); + EXPECT_VERIFIES(i_ll, WASM_I64_NE(WASM_GET_LOCAL(0), WASM_I64V_1(25))); } TEST_F(AstDecoderTest, AllSimpleExpressions) { @@ -1085,15 +1150,15 @@ TEST_F(AstDecoderTest, AllSimpleExpressions) { TEST_F(AstDecoderTest, MemorySize) { byte code[] = {kExprMemorySize}; - EXPECT_VERIFIES(sigs.i_i(), code); - EXPECT_FAILURE(sigs.f_ff(), code); + EXPECT_VERIFIES_C(i_i, code); + EXPECT_FAILURE_C(f_ff, code); } TEST_F(AstDecoderTest, LoadMemOffset) { for (int offset = 0; offset < 128; offset += 7) { byte code[] = {kExprI8Const, 0, kExprI32LoadMem, ZERO_ALIGNMENT, static_cast(offset)}; - EXPECT_VERIFIES(sigs.i_i(), code); + EXPECT_VERIFIES_C(i_i, code); } } @@ -1120,12 +1185,12 @@ TEST_F(AstDecoderTest, LoadMemAlignment) { for (int i = 0; i < arraysize(values); i++) { for (byte alignment = 0; alignment <= 4; alignment++) { - byte code[] = {kExprI8Const, 0, static_cast(values[i].instruction), - alignment, ZERO_OFFSET}; + byte code[] = {WASM_ZERO, static_cast(values[i].instruction), + alignment, ZERO_OFFSET, WASM_DROP}; if (static_cast(alignment) <= values[i].maximum_aligment) { - EXPECT_VERIFIES(sigs.v_i(), code); + EXPECT_VERIFIES_C(v_i, code); } else { - EXPECT_FAILURE(sigs.v_i(), code); + EXPECT_FAILURE_C(v_i, code); } } } @@ -1135,10 +1200,15 @@ TEST_F(AstDecoderTest, StoreMemOffset) { for (int offset = 0; offset < 128; offset += 7) { byte code[] = {WASM_STORE_MEM_OFFSET(MachineType::Int32(), offset, WASM_ZERO, WASM_ZERO)}; - EXPECT_VERIFIES(sigs.i_i(), code); + EXPECT_VERIFIES_C(v_i, code); } } +TEST_F(AstDecoderTest, StoreMemOffset_void) { + EXPECT_FAILURE(i_i, WASM_STORE_MEM_OFFSET(MachineType::Int32(), 0, WASM_ZERO, + WASM_ZERO)); +} + #define BYTE0(x) ((x)&0x7F) #define BYTE1(x) ((x >> 7) & 0x7F) #define BYTE2(x) ((x >> 14) & 0x7F) @@ -1150,25 +1220,25 @@ TEST_F(AstDecoderTest, StoreMemOffset) { #define VARINT4(x) BYTE0(x) | 0x80, BYTE1(x) | 0x80, BYTE2(x) | 0x80, BYTE3(x) TEST_F(AstDecoderTest, LoadMemOffset_varint) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT, - VARINT1(0x45)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT, - VARINT2(0x3999)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT, - VARINT3(0x344445)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT, - VARINT4(0x36666667)); + EXPECT_VERIFIES(i_i, WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT, + VARINT1(0x45)); + EXPECT_VERIFIES(i_i, WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT, + VARINT2(0x3999)); + EXPECT_VERIFIES(i_i, WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT, + VARINT3(0x344445)); + EXPECT_VERIFIES(i_i, WASM_ZERO, kExprI32LoadMem, ZERO_ALIGNMENT, + VARINT4(0x36666667)); } TEST_F(AstDecoderTest, StoreMemOffset_varint) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_ZERO, WASM_ZERO, kExprI32StoreMem, - ZERO_ALIGNMENT, VARINT1(0x33)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_ZERO, WASM_ZERO, kExprI32StoreMem, - ZERO_ALIGNMENT, VARINT2(0x1111)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_ZERO, WASM_ZERO, kExprI32StoreMem, - ZERO_ALIGNMENT, VARINT3(0x222222)); - EXPECT_VERIFIES_INLINE(sigs.i_i(), WASM_ZERO, WASM_ZERO, kExprI32StoreMem, - ZERO_ALIGNMENT, VARINT4(0x44444444)); + EXPECT_VERIFIES(v_i, WASM_ZERO, WASM_ZERO, kExprI32StoreMem, ZERO_ALIGNMENT, + VARINT1(0x33)); + EXPECT_VERIFIES(v_i, WASM_ZERO, WASM_ZERO, kExprI32StoreMem, ZERO_ALIGNMENT, + VARINT2(0x1111)); + EXPECT_VERIFIES(v_i, WASM_ZERO, WASM_ZERO, kExprI32StoreMem, ZERO_ALIGNMENT, + VARINT3(0x222222)); + EXPECT_VERIFIES(v_i, WASM_ZERO, WASM_ZERO, kExprI32StoreMem, ZERO_ALIGNMENT, + VARINT4(0x44444444)); } TEST_F(AstDecoderTest, AllLoadMemCombinations) { @@ -1179,9 +1249,9 @@ TEST_F(AstDecoderTest, AllLoadMemCombinations) { byte code[] = {WASM_LOAD_MEM(mem_type, WASM_ZERO)}; FunctionSig sig(1, 0, &local_type); if (local_type == WasmOpcodes::LocalTypeFor(mem_type)) { - EXPECT_VERIFIES(&sig, code); + EXPECT_VERIFIES_SC(&sig, code); } else { - EXPECT_FAILURE(&sig, code); + EXPECT_FAILURE_SC(&sig, code); } } } @@ -1195,9 +1265,9 @@ TEST_F(AstDecoderTest, AllStoreMemCombinations) { byte code[] = {WASM_STORE_MEM(mem_type, WASM_ZERO, WASM_GET_LOCAL(0))}; FunctionSig sig(0, 1, &local_type); if (local_type == WasmOpcodes::LocalTypeFor(mem_type)) { - EXPECT_VERIFIES(&sig, code); + EXPECT_VERIFIES_SC(&sig, code); } else { - EXPECT_FAILURE(&sig, code); + EXPECT_FAILURE_SC(&sig, code); } } } @@ -1212,8 +1282,8 @@ class TestModuleEnv : public ModuleEnv { instance = nullptr; module = &mod; } - byte AddGlobal(LocalType type) { - mod.globals.push_back({0, 0, type, 0, false}); + byte AddGlobal(LocalType type, bool mutability = true) { + mod.globals.push_back({type, mutability, NO_INIT, 0, false, false}); CHECK(mod.globals.size() <= 127); return static_cast(mod.globals.size() - 1); } @@ -1223,25 +1293,22 @@ class TestModuleEnv : public ModuleEnv { return static_cast(mod.signatures.size() - 1); } byte AddFunction(FunctionSig* sig) { - mod.functions.push_back({sig, // sig - 0, // func_index - 0, // sig_index - 0, // name_offset - 0, // name_length - 0, // code_start_offset - 0}); // code_end_offset + mod.functions.push_back({sig, // sig + 0, // func_index + 0, // sig_index + 0, // name_offset + 0, // name_length + 0, // code_start_offset + 0, // code_end_offset + false, // import + false}); // export CHECK(mod.functions.size() <= 127); return static_cast(mod.functions.size() - 1); } byte AddImport(FunctionSig* sig) { - mod.import_table.push_back({sig, // sig - 0, // sig_index - 0, // module_name_offset - 0, // module_name_length - 0, // function_name_offset - 0}); // function_name_length - CHECK(mod.import_table.size() <= 127); - return static_cast(mod.import_table.size() - 1); + byte result = AddFunction(sig); + mod.functions[result].imported = true; + return result; } private: @@ -1258,9 +1325,9 @@ TEST_F(AstDecoderTest, SimpleCalls) { module_env.AddFunction(sigs.i_i()); module_env.AddFunction(sigs.i_ii()); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_FUNCTION0(0)); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_FUNCTION1(1, WASM_I8(27))); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_FUNCTION2(2, WASM_I8(37), WASM_I8(77))); + EXPECT_VERIFIES_S(sig, WASM_CALL_FUNCTION0(0)); + EXPECT_VERIFIES_S(sig, WASM_CALL_FUNCTION(1, WASM_I8(27))); + EXPECT_VERIFIES_S(sig, WASM_CALL_FUNCTION(2, WASM_I8(37), WASM_I8(77))); } TEST_F(AstDecoderTest, CallsWithTooFewArguments) { @@ -1272,9 +1339,9 @@ TEST_F(AstDecoderTest, CallsWithTooFewArguments) { module_env.AddFunction(sigs.i_ii()); module_env.AddFunction(sigs.f_ff()); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION0(0)); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(1, WASM_ZERO)); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(2, WASM_GET_LOCAL(0))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION0(0)); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(1, WASM_ZERO)); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(2, WASM_GET_LOCAL(0))); } TEST_F(AstDecoderTest, CallsWithMismatchedSigs2) { @@ -1284,9 +1351,9 @@ TEST_F(AstDecoderTest, CallsWithMismatchedSigs2) { module_env.AddFunction(sigs.i_i()); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(0, WASM_I64V_1(17))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(0, WASM_F32(17.1))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(0, WASM_F64(17.1))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(0, WASM_I64V_1(17))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(0, WASM_F32(17.1))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(0, WASM_F64(17.1))); } TEST_F(AstDecoderTest, CallsWithMismatchedSigs3) { @@ -1296,15 +1363,59 @@ TEST_F(AstDecoderTest, CallsWithMismatchedSigs3) { module_env.AddFunction(sigs.i_f()); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(0, WASM_I8(17))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(0, WASM_I64V_1(27))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(0, WASM_F64(37.2))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(0, WASM_I8(17))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(0, WASM_I64V_1(27))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(0, WASM_F64(37.2))); module_env.AddFunction(sigs.i_d()); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(1, WASM_I8(16))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(1, WASM_I64V_1(16))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_FUNCTION1(1, WASM_F32(17.6))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(1, WASM_I8(16))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(1, WASM_I64V_1(16))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(1, WASM_F32(17.6))); +} + +TEST_F(AstDecoderTest, MultiReturn) { + FLAG_wasm_mv_prototype = true; + LocalType storage[] = {kAstI32, kAstI32}; + FunctionSig sig_ii_v(2, 0, storage); + FunctionSig sig_v_ii(0, 2, storage); + TestModuleEnv module_env; + module = &module_env; + + module_env.AddFunction(&sig_v_ii); + module_env.AddFunction(&sig_ii_v); + + EXPECT_VERIFIES_S(&sig_ii_v, WASM_CALL_FUNCTION0(1)); + EXPECT_VERIFIES(v_v, WASM_CALL_FUNCTION0(1), WASM_DROP, WASM_DROP); + EXPECT_VERIFIES(v_v, WASM_CALL_FUNCTION0(1), kExprCallFunction, 0); +} + +TEST_F(AstDecoderTest, MultiReturnType) { + FLAG_wasm_mv_prototype = true; + for (size_t a = 0; a < arraysize(kLocalTypes); a++) { + for (size_t b = 0; b < arraysize(kLocalTypes); b++) { + for (size_t c = 0; c < arraysize(kLocalTypes); c++) { + for (size_t d = 0; d < arraysize(kLocalTypes); d++) { + LocalType storage_ab[] = {kLocalTypes[a], kLocalTypes[b]}; + FunctionSig sig_ab_v(2, 0, storage_ab); + LocalType storage_cd[] = {kLocalTypes[c], kLocalTypes[d]}; + FunctionSig sig_cd_v(2, 0, storage_cd); + + TestModuleEnv module_env; + module = &module_env; + module_env.AddFunction(&sig_cd_v); + + EXPECT_VERIFIES_S(&sig_cd_v, WASM_CALL_FUNCTION0(0)); + + if (a == c && b == d) { + EXPECT_VERIFIES_S(&sig_ab_v, WASM_CALL_FUNCTION0(0)); + } else { + EXPECT_FAILURE_S(&sig_ab_v, WASM_CALL_FUNCTION0(0)); + } + } + } + } + } } TEST_F(AstDecoderTest, SimpleIndirectCalls) { @@ -1316,9 +1427,9 @@ TEST_F(AstDecoderTest, SimpleIndirectCalls) { byte f1 = module_env.AddSignature(sigs.i_i()); byte f2 = module_env.AddSignature(sigs.i_ii()); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_INDIRECT0(f0, WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_INDIRECT1(f1, WASM_ZERO, WASM_I8(22))); - EXPECT_VERIFIES_INLINE( + EXPECT_VERIFIES_S(sig, WASM_CALL_INDIRECT0(f0, WASM_ZERO)); + EXPECT_VERIFIES_S(sig, WASM_CALL_INDIRECT1(f1, WASM_ZERO, WASM_I8(22))); + EXPECT_VERIFIES_S( sig, WASM_CALL_INDIRECT2(f2, WASM_ZERO, WASM_I8(32), WASM_I8(72))); } @@ -1327,15 +1438,15 @@ TEST_F(AstDecoderTest, IndirectCallsOutOfBounds) { TestModuleEnv module_env; module = &module_env; - EXPECT_FAILURE_INLINE(sig, WASM_CALL_INDIRECT0(0, WASM_ZERO)); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT0(0, WASM_ZERO)); module_env.AddSignature(sigs.i_v()); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_INDIRECT0(0, WASM_ZERO)); + EXPECT_VERIFIES_S(sig, WASM_CALL_INDIRECT0(0, WASM_ZERO)); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_INDIRECT1(1, WASM_ZERO, WASM_I8(22))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT1(1, WASM_ZERO, WASM_I8(22))); module_env.AddSignature(sigs.i_i()); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_INDIRECT1(1, WASM_ZERO, WASM_I8(27))); + EXPECT_VERIFIES_S(sig, WASM_CALL_INDIRECT1(1, WASM_ZERO, WASM_I8(27))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_INDIRECT1(2, WASM_ZERO, WASM_I8(27))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT1(2, WASM_ZERO, WASM_I8(27))); } TEST_F(AstDecoderTest, IndirectCallsWithMismatchedSigs3) { @@ -1345,23 +1456,19 @@ TEST_F(AstDecoderTest, IndirectCallsWithMismatchedSigs3) { byte f0 = module_env.AddFunction(sigs.i_f()); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_INDIRECT1(f0, WASM_ZERO, WASM_I8(17))); - EXPECT_FAILURE_INLINE(sig, - WASM_CALL_INDIRECT1(f0, WASM_ZERO, WASM_I64V_1(27))); - EXPECT_FAILURE_INLINE(sig, - WASM_CALL_INDIRECT1(f0, WASM_ZERO, WASM_F64(37.2))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT1(f0, WASM_ZERO, WASM_I8(17))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT1(f0, WASM_ZERO, WASM_I64V_1(27))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT1(f0, WASM_ZERO, WASM_F64(37.2))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_INDIRECT0(f0, WASM_I8(17))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_INDIRECT0(f0, WASM_I64V_1(27))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_INDIRECT0(f0, WASM_F64(37.2))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT0(f0, WASM_I8(17))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT0(f0, WASM_I64V_1(27))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT0(f0, WASM_F64(37.2))); byte f1 = module_env.AddFunction(sigs.i_d()); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_INDIRECT1(f1, WASM_ZERO, WASM_I8(16))); - EXPECT_FAILURE_INLINE(sig, - WASM_CALL_INDIRECT1(f1, WASM_ZERO, WASM_I64V_1(16))); - EXPECT_FAILURE_INLINE(sig, - WASM_CALL_INDIRECT1(f1, WASM_ZERO, WASM_F32(17.6))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT1(f1, WASM_ZERO, WASM_I8(16))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT1(f1, WASM_ZERO, WASM_I64V_1(16))); + EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT1(f1, WASM_ZERO, WASM_F32(17.6))); } TEST_F(AstDecoderTest, SimpleImportCalls) { @@ -1373,9 +1480,9 @@ TEST_F(AstDecoderTest, SimpleImportCalls) { byte f1 = module_env.AddImport(sigs.i_i()); byte f2 = module_env.AddImport(sigs.i_ii()); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_IMPORT0(f0)); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_IMPORT1(f1, WASM_I8(22))); - EXPECT_VERIFIES_INLINE(sig, WASM_CALL_IMPORT2(f2, WASM_I8(32), WASM_I8(72))); + EXPECT_VERIFIES_S(sig, WASM_CALL_FUNCTION0(f0)); + EXPECT_VERIFIES_S(sig, WASM_CALL_FUNCTION(f1, WASM_I8(22))); + EXPECT_VERIFIES_S(sig, WASM_CALL_FUNCTION(f2, WASM_I8(32), WASM_I8(72))); } TEST_F(AstDecoderTest, ImportCallsWithMismatchedSigs3) { @@ -1385,17 +1492,17 @@ TEST_F(AstDecoderTest, ImportCallsWithMismatchedSigs3) { byte f0 = module_env.AddImport(sigs.i_f()); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_IMPORT0(f0)); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_IMPORT1(f0, WASM_I8(17))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_IMPORT1(f0, WASM_I64V_1(27))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_IMPORT1(f0, WASM_F64(37.2))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION0(f0)); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(f0, WASM_I8(17))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(f0, WASM_I64V_1(27))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(f0, WASM_F64(37.2))); byte f1 = module_env.AddImport(sigs.i_d()); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_IMPORT0(f1)); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_IMPORT1(f1, WASM_I8(16))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_IMPORT1(f1, WASM_I64V_1(16))); - EXPECT_FAILURE_INLINE(sig, WASM_CALL_IMPORT1(f1, WASM_F32(17.6))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION0(f1)); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(f1, WASM_I8(16))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(f1, WASM_I64V_1(16))); + EXPECT_FAILURE_S(sig, WASM_CALL_FUNCTION(f1, WASM_F32(17.6))); } TEST_F(AstDecoderTest, Int32Globals) { @@ -1405,8 +1512,21 @@ TEST_F(AstDecoderTest, Int32Globals) { module_env.AddGlobal(kAstI32); - EXPECT_VERIFIES_INLINE(sig, WASM_GET_GLOBAL(0)); - EXPECT_VERIFIES_INLINE(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); + EXPECT_VERIFIES_S(sig, WASM_GET_GLOBAL(0)); + EXPECT_FAILURE_S(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); + EXPECT_VERIFIES_S(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0)), WASM_ZERO); +} + +TEST_F(AstDecoderTest, ImmutableGlobal) { + FunctionSig* sig = sigs.v_v(); + TestModuleEnv module_env; + module = &module_env; + + uint32_t g0 = module_env.AddGlobal(kAstI32, true); + uint32_t g1 = module_env.AddGlobal(kAstI32, false); + + EXPECT_VERIFIES_S(sig, WASM_SET_GLOBAL(g0, WASM_ZERO)); + EXPECT_FAILURE_S(sig, WASM_SET_GLOBAL(g1, WASM_ZERO)); } TEST_F(AstDecoderTest, Int32Globals_fail) { @@ -1419,15 +1539,15 @@ TEST_F(AstDecoderTest, Int32Globals_fail) { module_env.AddGlobal(kAstF32); module_env.AddGlobal(kAstF64); - EXPECT_FAILURE_INLINE(sig, WASM_GET_GLOBAL(0)); - EXPECT_FAILURE_INLINE(sig, WASM_GET_GLOBAL(1)); - EXPECT_FAILURE_INLINE(sig, WASM_GET_GLOBAL(2)); - EXPECT_FAILURE_INLINE(sig, WASM_GET_GLOBAL(3)); + EXPECT_FAILURE_S(sig, WASM_GET_GLOBAL(0)); + EXPECT_FAILURE_S(sig, WASM_GET_GLOBAL(1)); + EXPECT_FAILURE_S(sig, WASM_GET_GLOBAL(2)); + EXPECT_FAILURE_S(sig, WASM_GET_GLOBAL(3)); - EXPECT_FAILURE_INLINE(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE(sig, WASM_SET_GLOBAL(1, WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE(sig, WASM_SET_GLOBAL(2, WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE(sig, WASM_SET_GLOBAL(3, WASM_GET_LOCAL(0))); + EXPECT_FAILURE_S(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0)), WASM_ZERO); + EXPECT_FAILURE_S(sig, WASM_SET_GLOBAL(1, WASM_GET_LOCAL(0)), WASM_ZERO); + EXPECT_FAILURE_S(sig, WASM_SET_GLOBAL(2, WASM_GET_LOCAL(0)), WASM_ZERO); + EXPECT_FAILURE_S(sig, WASM_SET_GLOBAL(3, WASM_GET_LOCAL(0)), WASM_ZERO); } TEST_F(AstDecoderTest, Int64Globals) { @@ -1438,11 +1558,13 @@ TEST_F(AstDecoderTest, Int64Globals) { module_env.AddGlobal(kAstI64); module_env.AddGlobal(kAstI64); - EXPECT_VERIFIES_INLINE(sig, WASM_GET_GLOBAL(0)); - EXPECT_VERIFIES_INLINE(sig, WASM_GET_GLOBAL(1)); + EXPECT_VERIFIES_S(sig, WASM_GET_GLOBAL(0)); + EXPECT_VERIFIES_S(sig, WASM_GET_GLOBAL(1)); - EXPECT_VERIFIES_INLINE(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); - EXPECT_VERIFIES_INLINE(sig, WASM_SET_GLOBAL(1, WASM_GET_LOCAL(0))); + EXPECT_VERIFIES_S(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(0)); + EXPECT_VERIFIES_S(sig, WASM_SET_GLOBAL(1, WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(0)); } TEST_F(AstDecoderTest, Float32Globals) { @@ -1452,8 +1574,9 @@ TEST_F(AstDecoderTest, Float32Globals) { module_env.AddGlobal(kAstF32); - EXPECT_VERIFIES_INLINE(sig, WASM_GET_GLOBAL(0)); - EXPECT_VERIFIES_INLINE(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); + EXPECT_VERIFIES_S(sig, WASM_GET_GLOBAL(0)); + EXPECT_VERIFIES_S(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(0)); } TEST_F(AstDecoderTest, Float64Globals) { @@ -1463,8 +1586,9 @@ TEST_F(AstDecoderTest, Float64Globals) { module_env.AddGlobal(kAstF64); - EXPECT_VERIFIES_INLINE(sig, WASM_GET_GLOBAL(0)); - EXPECT_VERIFIES_INLINE(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); + EXPECT_VERIFIES_S(sig, WASM_GET_GLOBAL(0)); + EXPECT_VERIFIES_S(sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(0)); } TEST_F(AstDecoderTest, AllGetGlobalCombinations) { @@ -1477,9 +1601,9 @@ TEST_F(AstDecoderTest, AllGetGlobalCombinations) { module = &module_env; module_env.AddGlobal(global_type); if (local_type == global_type) { - EXPECT_VERIFIES_INLINE(&sig, WASM_GET_GLOBAL(0)); + EXPECT_VERIFIES_S(&sig, WASM_GET_GLOBAL(0)); } else { - EXPECT_FAILURE_INLINE(&sig, WASM_GET_GLOBAL(0)); + EXPECT_FAILURE_S(&sig, WASM_GET_GLOBAL(0)); } } } @@ -1495,9 +1619,9 @@ TEST_F(AstDecoderTest, AllSetGlobalCombinations) { module = &module_env; module_env.AddGlobal(global_type); if (local_type == global_type) { - EXPECT_VERIFIES_INLINE(&sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); + EXPECT_VERIFIES_S(&sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); } else { - EXPECT_FAILURE_INLINE(&sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); + EXPECT_FAILURE_S(&sig, WASM_SET_GLOBAL(0, WASM_GET_LOCAL(0))); } } } @@ -1509,8 +1633,8 @@ TEST_F(AstDecoderTest, WasmGrowMemory) { module->origin = kWasmOrigin; byte code[] = {WASM_UNOP(kExprGrowMemory, WASM_GET_LOCAL(0))}; - EXPECT_VERIFIES(sigs.i_i(), code); - EXPECT_FAILURE(sigs.i_d(), code); + EXPECT_VERIFIES_C(i_i, code); + EXPECT_FAILURE_C(i_d, code); } TEST_F(AstDecoderTest, AsmJsGrowMemory) { @@ -1519,7 +1643,7 @@ TEST_F(AstDecoderTest, AsmJsGrowMemory) { module->origin = kAsmJsOrigin; byte code[] = {WASM_UNOP(kExprGrowMemory, WASM_GET_LOCAL(0))}; - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, AsmJsBinOpsCheckOrigin) { @@ -1561,7 +1685,7 @@ TEST_F(AstDecoderTest, AsmJsBinOpsCheckOrigin) { for (int i = 0; i < arraysize(AsmJsBinOps); i++) { byte code[] = { WASM_BINOP(AsmJsBinOps[i].op, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))}; - EXPECT_FAILURE(AsmJsBinOps[i].sig, code); + EXPECT_FAILURE_SC(AsmJsBinOps[i].sig, code); } } } @@ -1608,284 +1732,304 @@ TEST_F(AstDecoderTest, AsmJsUnOpsCheckOrigin) { module->origin = kWasmOrigin; for (int i = 0; i < arraysize(AsmJsUnOps); i++) { byte code[] = {WASM_UNOP(AsmJsUnOps[i].op, WASM_GET_LOCAL(0))}; - EXPECT_FAILURE(AsmJsUnOps[i].sig, code); + EXPECT_FAILURE_SC(AsmJsUnOps[i].sig, code); } } } TEST_F(AstDecoderTest, BreakEnd) { - EXPECT_VERIFIES_INLINE(sigs.i_i(), - B1(WASM_I32_ADD(WASM_BRV(0, WASM_ZERO), WASM_ZERO))); - EXPECT_VERIFIES_INLINE(sigs.i_i(), - B1(WASM_I32_ADD(WASM_ZERO, WASM_BRV(0, WASM_ZERO)))); + EXPECT_VERIFIES( + i_i, WASM_BLOCK_I(WASM_I32_ADD(WASM_BRV(0, WASM_ZERO), WASM_ZERO))); + EXPECT_VERIFIES( + i_i, WASM_BLOCK_I(WASM_I32_ADD(WASM_ZERO, WASM_BRV(0, WASM_ZERO)))); } TEST_F(AstDecoderTest, BreakIfBinop) { - EXPECT_FAILURE_INLINE( - sigs.i_i(), WASM_BLOCK(WASM_I32_ADD(WASM_BRV_IF(0, WASM_ZERO, WASM_ZERO), - WASM_ZERO))); - EXPECT_FAILURE_INLINE(sigs.i_i(), - WASM_BLOCK(WASM_I32_ADD( - WASM_ZERO, WASM_BRV_IF(0, WASM_ZERO, WASM_ZERO)))); + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_I32_ADD( + WASM_BRV_IF(0, WASM_ZERO, WASM_ZERO), WASM_ZERO))); + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_I32_ADD( + WASM_ZERO, WASM_BRV_IF(0, WASM_ZERO, WASM_ZERO)))); + EXPECT_VERIFIES_S( + sigs.f_ff(), + WASM_BLOCK_F(WASM_F32_ABS(WASM_BRV_IF(0, WASM_F32(0.0f), WASM_ZERO)))); +} + +TEST_F(AstDecoderTest, BreakIfBinop_fail) { + EXPECT_FAILURE_S( + sigs.f_ff(), + WASM_BLOCK_F(WASM_F32_ABS(WASM_BRV_IF(0, WASM_ZERO, WASM_ZERO)))); + EXPECT_FAILURE_S( + sigs.i_i(), + WASM_BLOCK_I(WASM_F32_ABS(WASM_BRV_IF(0, WASM_F32(0.0f), WASM_ZERO)))); } TEST_F(AstDecoderTest, BreakNesting1) { for (int i = 0; i < 5; i++) { // (block[2] (loop[2] (if (get p) break[N]) (set p 1)) p) - byte code[] = {WASM_BLOCK( + byte code[] = {WASM_BLOCK_I( WASM_LOOP(WASM_IF(WASM_GET_LOCAL(0), WASM_BRV(i + 1, WASM_ZERO)), WASM_SET_LOCAL(0, WASM_I8(1))), - WASM_GET_LOCAL(0))}; + WASM_ZERO)}; if (i < 3) { - EXPECT_VERIFIES(sigs.i_i(), code); + EXPECT_VERIFIES_C(i_i, code); } else { - EXPECT_FAILURE(sigs.i_i(), code); + EXPECT_FAILURE_C(i_i, code); } } } TEST_F(AstDecoderTest, BreakNesting2) { - AddLocals(kAstI32, 1); - for (int i = 0; i < 5; i++) { - // (block[2] (loop[2] (if 0 break[N]) (set p 1)) (return p)) (11) - byte code[] = {B1(WASM_LOOP(WASM_IF(WASM_ZERO, WASM_BREAK(i + 1)), - WASM_SET_LOCAL(0, WASM_I8(1)))), - WASM_I8(11)}; - if (i < 2) { - EXPECT_VERIFIES(sigs.v_v(), code); + for (int i = 0; i < 7; i++) { + byte code[] = {B1(WASM_LOOP(WASM_IF(WASM_ZERO, WASM_BR(i)), WASM_NOP))}; + if (i <= 3) { + EXPECT_VERIFIES_C(v_v, code); } else { - EXPECT_FAILURE(sigs.v_v(), code); + EXPECT_FAILURE_C(v_v, code); } } } TEST_F(AstDecoderTest, BreakNesting3) { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 7; i++) { // (block[1] (loop[1] (block[1] (if 0 break[N]) byte code[] = { - WASM_BLOCK(WASM_LOOP(B1(WASM_IF(WASM_ZERO, WASM_BREAK(i + 1)))))}; - if (i < 3) { - EXPECT_VERIFIES(sigs.v_v(), code); + WASM_BLOCK(WASM_LOOP(B1(WASM_IF(WASM_ZERO, WASM_BR(i + 1)))))}; + if (i < 4) { + EXPECT_VERIFIES_C(v_v, code); } else { - EXPECT_FAILURE(sigs.v_v(), code); + EXPECT_FAILURE_C(v_v, code); } } } TEST_F(AstDecoderTest, BreaksWithMultipleTypes) { - EXPECT_FAILURE_INLINE(sigs.i_i(), - B2(WASM_BRV_IF_ZERO(0, WASM_I8(7)), WASM_F32(7.7))); + EXPECT_FAILURE(i_i, B2(WASM_BRV_IF_ZERO(0, WASM_I8(7)), WASM_F32(7.7))); - EXPECT_FAILURE_INLINE(sigs.i_i(), B2(WASM_BRV_IF_ZERO(0, WASM_I8(7)), - WASM_BRV_IF_ZERO(0, WASM_F32(7.7)))); - EXPECT_FAILURE_INLINE(sigs.i_i(), B3(WASM_BRV_IF_ZERO(0, WASM_I8(8)), - WASM_BRV_IF_ZERO(0, WASM_I8(0)), - WASM_BRV_IF_ZERO(0, WASM_F32(7.7)))); - EXPECT_FAILURE_INLINE(sigs.i_i(), B3(WASM_BRV_IF_ZERO(0, WASM_I8(9)), - WASM_BRV_IF_ZERO(0, WASM_F32(7.7)), - WASM_BRV_IF_ZERO(0, WASM_I8(11)))); + EXPECT_FAILURE(i_i, B2(WASM_BRV_IF_ZERO(0, WASM_I8(7)), + WASM_BRV_IF_ZERO(0, WASM_F32(7.7)))); + EXPECT_FAILURE( + i_i, B3(WASM_BRV_IF_ZERO(0, WASM_I8(8)), WASM_BRV_IF_ZERO(0, WASM_I8(0)), + WASM_BRV_IF_ZERO(0, WASM_F32(7.7)))); + EXPECT_FAILURE(i_i, B3(WASM_BRV_IF_ZERO(0, WASM_I8(9)), + WASM_BRV_IF_ZERO(0, WASM_F32(7.7)), + WASM_BRV_IF_ZERO(0, WASM_I8(11)))); } TEST_F(AstDecoderTest, BreakNesting_6_levels) { for (int mask = 0; mask < 64; mask++) { for (int i = 0; i < 14; i++) { - byte code[] = { - kExprBlock, // -- - kExprBlock, // -- - kExprBlock, // -- - kExprBlock, // -- - kExprBlock, // -- - kExprBlock, // -- - kExprBr, ARITY_0, static_cast(i), // -- - kExprEnd, // -- - kExprEnd, // -- - kExprEnd, // -- - kExprEnd, // -- - kExprEnd, // -- - kExprEnd // -- - }; + byte code[] = {WASM_BLOCK(WASM_BLOCK( + WASM_BLOCK(WASM_BLOCK(WASM_BLOCK(WASM_BLOCK(WASM_BR(i)))))))}; int depth = 6; - for (int l = 0; l < 6; l++) { - if (mask & (1 << l)) { - code[l] = kExprLoop; - depth++; + int m = mask; + for (size_t pos = 0; pos < sizeof(code); pos++) { + if (code[pos] != kExprBlock) continue; + if (m & 1) { + code[pos] = kExprLoop; + code[pos + 1] = kLocalVoid; } + m >>= 1; } - if (i < depth) { - EXPECT_VERIFIES(sigs.v_v(), code); + if (i <= depth) { + EXPECT_VERIFIES_C(v_v, code); } else { - EXPECT_FAILURE(sigs.v_v(), code); + EXPECT_FAILURE_C(v_v, code); } } } } -TEST_F(AstDecoderTest, ExprBreak_TypeCheck) { +TEST_F(AstDecoderTest, Break_TypeCheck) { FunctionSig* sigarray[] = {sigs.i_i(), sigs.l_l(), sigs.f_ff(), sigs.d_dd()}; for (size_t i = 0; i < arraysize(sigarray); i++) { FunctionSig* sig = sigarray[i]; // unify X and X => OK - EXPECT_VERIFIES_INLINE( - sig, B2(WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_GET_LOCAL(0))), - WASM_GET_LOCAL(0))); + byte code[] = {WASM_BLOCK_T( + sig->GetReturn(), WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_GET_LOCAL(0))), + WASM_GET_LOCAL(0))}; + EXPECT_VERIFIES_SC(sig, code); } // unify i32 and f32 => fail - EXPECT_FAILURE_INLINE( - sigs.i_i(), - B2(WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_ZERO)), WASM_F32(1.2))); + EXPECT_FAILURE(i_i, WASM_BLOCK_I(WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_ZERO)), + WASM_F32(1.2))); // unify f64 and f64 => OK - EXPECT_VERIFIES_INLINE( - sigs.d_dd(), - B2(WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_GET_LOCAL(0))), WASM_F64(1.2))); + EXPECT_VERIFIES( + d_dd, WASM_BLOCK_D(WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_GET_LOCAL(0))), + WASM_F64(1.2))); } -TEST_F(AstDecoderTest, ExprBreak_TypeCheckAll) { - byte code1[] = {WASM_BLOCK(WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_GET_LOCAL(0))), - WASM_GET_LOCAL(1))}; - byte code2[] = {B2(WASM_IF(WASM_ZERO, WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0))), - WASM_GET_LOCAL(1))}; - +TEST_F(AstDecoderTest, Break_TypeCheckAll1) { for (size_t i = 0; i < arraysize(kLocalTypes); i++) { for (size_t j = 0; j < arraysize(kLocalTypes); j++) { LocalType storage[] = {kLocalTypes[i], kLocalTypes[i], kLocalTypes[j]}; FunctionSig sig(1, 2, storage); + byte code[] = {WASM_BLOCK_T( + sig.GetReturn(), WASM_IF(WASM_ZERO, WASM_BRV(0, WASM_GET_LOCAL(0))), + WASM_GET_LOCAL(1))}; if (i == j) { - EXPECT_VERIFIES(&sig, code1); - EXPECT_VERIFIES(&sig, code2); + EXPECT_VERIFIES_SC(&sig, code); } else { - EXPECT_FAILURE(&sig, code1); - EXPECT_FAILURE(&sig, code2); + EXPECT_FAILURE_SC(&sig, code); } } } } -TEST_F(AstDecoderTest, ExprBr_Unify) { +TEST_F(AstDecoderTest, Break_TypeCheckAll2) { + for (size_t i = 0; i < arraysize(kLocalTypes); i++) { + for (size_t j = 0; j < arraysize(kLocalTypes); j++) { + LocalType storage[] = {kLocalTypes[i], kLocalTypes[i], kLocalTypes[j]}; + FunctionSig sig(1, 2, storage); + byte code[] = {WASM_IF_ELSE_T(sig.GetReturn(0), WASM_ZERO, + WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)), + WASM_GET_LOCAL(1))}; + + if (i == j) { + EXPECT_VERIFIES_SC(&sig, code); + } else { + EXPECT_FAILURE_SC(&sig, code); + } + } + } +} + +TEST_F(AstDecoderTest, Break_TypeCheckAll3) { + for (size_t i = 0; i < arraysize(kLocalTypes); i++) { + for (size_t j = 0; j < arraysize(kLocalTypes); j++) { + LocalType storage[] = {kLocalTypes[i], kLocalTypes[i], kLocalTypes[j]}; + FunctionSig sig(1, 2, storage); + byte code[] = {WASM_IF_ELSE_T(sig.GetReturn(), WASM_ZERO, + WASM_GET_LOCAL(1), + WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)))}; + + if (i == j) { + EXPECT_VERIFIES_SC(&sig, code); + } else { + EXPECT_FAILURE_SC(&sig, code); + } + } + } +} + +TEST_F(AstDecoderTest, Break_Unify) { for (int which = 0; which < 2; which++) { for (size_t i = 0; i < arraysize(kLocalTypes); i++) { LocalType type = kLocalTypes[i]; LocalType storage[] = {kAstI32, kAstI32, type}; FunctionSig sig(1, 2, storage); - byte code1[] = {B2(WASM_IF(WASM_ZERO, WASM_BRV(1, WASM_GET_LOCAL(which))), - WASM_GET_LOCAL(which ^ 1))}; - byte code2[] = { - WASM_LOOP(WASM_IF(WASM_ZERO, WASM_BRV(2, WASM_GET_LOCAL(which))), - WASM_GET_LOCAL(which ^ 1))}; + byte code1[] = {WASM_BLOCK_T( + type, WASM_IF(WASM_ZERO, WASM_BRV(1, WASM_GET_LOCAL(which))), + WASM_GET_LOCAL(which ^ 1))}; if (type == kAstI32) { - EXPECT_VERIFIES(&sig, code1); - EXPECT_VERIFIES(&sig, code2); + EXPECT_VERIFIES_SC(&sig, code1); } else { - EXPECT_FAILURE(&sig, code1); - EXPECT_FAILURE(&sig, code2); + EXPECT_FAILURE_SC(&sig, code1); } } } } -TEST_F(AstDecoderTest, ExprBrIf_cond_type) { - byte code[] = {B1(WASM_BRV_IF(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))}; +TEST_F(AstDecoderTest, BreakIf_cond_type) { for (size_t i = 0; i < arraysize(kLocalTypes); i++) { for (size_t j = 0; j < arraysize(kLocalTypes); j++) { - LocalType types[] = {kLocalTypes[i], kLocalTypes[j]}; - FunctionSig sig(0, 2, types); + LocalType types[] = {kLocalTypes[i], kLocalTypes[i], kLocalTypes[j]}; + FunctionSig sig(1, 2, types); + byte code[] = {WASM_BLOCK_T( + types[0], WASM_BRV_IF(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))}; - if (types[1] == kAstI32) { - EXPECT_VERIFIES(&sig, code); + if (types[2] == kAstI32) { + EXPECT_VERIFIES_SC(&sig, code); } else { - EXPECT_FAILURE(&sig, code); + EXPECT_FAILURE_SC(&sig, code); } } } } -TEST_F(AstDecoderTest, ExprBrIf_val_type) { - byte code[] = {B2(WASM_BRV_IF(0, WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)), - WASM_GET_LOCAL(0))}; +TEST_F(AstDecoderTest, BreakIf_val_type) { for (size_t i = 0; i < arraysize(kLocalTypes); i++) { for (size_t j = 0; j < arraysize(kLocalTypes); j++) { LocalType types[] = {kLocalTypes[i], kLocalTypes[i], kLocalTypes[j], kAstI32}; FunctionSig sig(1, 3, types); + byte code[] = {WASM_BLOCK_T( + types[1], WASM_BRV_IF(0, WASM_GET_LOCAL(1), WASM_GET_LOCAL(2)), + WASM_DROP, WASM_GET_LOCAL(0))}; if (i == j) { - EXPECT_VERIFIES(&sig, code); + EXPECT_VERIFIES_SC(&sig, code); } else { - EXPECT_FAILURE(&sig, code); + EXPECT_FAILURE_SC(&sig, code); } } } } -TEST_F(AstDecoderTest, ExprBrIf_Unify) { +TEST_F(AstDecoderTest, BreakIf_Unify) { for (int which = 0; which < 2; which++) { for (size_t i = 0; i < arraysize(kLocalTypes); i++) { LocalType type = kLocalTypes[i]; LocalType storage[] = {kAstI32, kAstI32, type}; FunctionSig sig(1, 2, storage); - - byte code1[] = {B2(WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(which)), - WASM_GET_LOCAL(which ^ 1))}; - byte code2[] = {WASM_LOOP(WASM_BRV_IF_ZERO(1, WASM_GET_LOCAL(which)), - WASM_GET_LOCAL(which ^ 1))}; + byte code[] = {WASM_BLOCK_I(WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(which)), + WASM_DROP, WASM_GET_LOCAL(which ^ 1))}; if (type == kAstI32) { - EXPECT_VERIFIES(&sig, code1); - EXPECT_VERIFIES(&sig, code2); + EXPECT_VERIFIES_SC(&sig, code); } else { - EXPECT_FAILURE(&sig, code1); - EXPECT_FAILURE(&sig, code2); + EXPECT_FAILURE_SC(&sig, code); } } } } TEST_F(AstDecoderTest, BrTable0) { - static byte code[] = {kExprNop, kExprBrTable, 0, 0}; - EXPECT_FAILURE(sigs.v_v(), code); + static byte code[] = {kExprBrTable, 0, BR_TARGET(0)}; + EXPECT_FAILURE_C(v_v, code); } TEST_F(AstDecoderTest, BrTable0b) { - static byte code[] = {kExprNop, kExprI32Const, 11, kExprBrTable, 0, 0}; - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + static byte code[] = {kExprI32Const, 11, kExprBrTable, 0, BR_TARGET(0)}; + EXPECT_VERIFIES_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, BrTable0c) { - static byte code[] = {kExprNop, kExprI32Const, 11, kExprBrTable, 0, 1, 0, 0}; - EXPECT_FAILURE(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); + static byte code[] = {kExprI32Const, 11, kExprBrTable, 0, BR_TARGET(1)}; + EXPECT_FAILURE_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); } TEST_F(AstDecoderTest, BrTable1a) { static byte code[] = {B1(WASM_BR_TABLE(WASM_I8(67), 0, BR_TARGET(0)))}; - EXPECT_VERIFIES(sigs.v_v(), code); + EXPECT_VERIFIES_C(v_v, code); } TEST_F(AstDecoderTest, BrTable1b) { static byte code[] = {B1(WASM_BR_TABLE(WASM_ZERO, 0, BR_TARGET(0)))}; - EXPECT_VERIFIES(sigs.v_v(), code); - EXPECT_FAILURE(sigs.i_i(), code); - EXPECT_FAILURE(sigs.f_ff(), code); - EXPECT_FAILURE(sigs.d_dd(), code); + EXPECT_VERIFIES_C(v_v, code); + EXPECT_FAILURE_C(i_i, code); + EXPECT_FAILURE_C(f_ff, code); + EXPECT_FAILURE_C(d_dd, code); } TEST_F(AstDecoderTest, BrTable2a) { static byte code[] = { B1(WASM_BR_TABLE(WASM_I8(67), 1, BR_TARGET(0), BR_TARGET(0)))}; - EXPECT_VERIFIES(sigs.v_v(), code); + EXPECT_VERIFIES_C(v_v, code); } TEST_F(AstDecoderTest, BrTable2b) { static byte code[] = {WASM_BLOCK( WASM_BLOCK(WASM_BR_TABLE(WASM_I8(67), 1, BR_TARGET(0), BR_TARGET(1))))}; - EXPECT_VERIFIES(sigs.v_v(), code); + EXPECT_VERIFIES_C(v_v, code); } TEST_F(AstDecoderTest, BrTable_off_end) { @@ -1898,63 +2042,74 @@ TEST_F(AstDecoderTest, BrTable_off_end) { TEST_F(AstDecoderTest, BrTable_invalid_br1) { for (int depth = 0; depth < 4; depth++) { byte code[] = {B1(WASM_BR_TABLE(WASM_GET_LOCAL(0), 0, BR_TARGET(depth)))}; - if (depth == 0) { - EXPECT_VERIFIES(sigs.v_i(), code); + if (depth <= 1) { + EXPECT_VERIFIES_C(v_i, code); } else { - EXPECT_FAILURE(sigs.v_i(), code); + EXPECT_FAILURE_C(v_i, code); } } } TEST_F(AstDecoderTest, BrTable_invalid_br2) { - for (int depth = 0; depth < 4; depth++) { + for (int depth = 0; depth < 7; depth++) { byte code[] = { WASM_LOOP(WASM_BR_TABLE(WASM_GET_LOCAL(0), 0, BR_TARGET(depth)))}; - if (depth <= 1) { - EXPECT_VERIFIES(sigs.v_i(), code); + if (depth < 2) { + EXPECT_VERIFIES_C(v_i, code); } else { - EXPECT_FAILURE(sigs.v_i(), code); + EXPECT_FAILURE_C(v_i, code); } } } -TEST_F(AstDecoderTest, ExprBreakNesting1) { - EXPECT_VERIFIES_INLINE(sigs.v_v(), B1(WASM_BRV(0, WASM_ZERO))); - EXPECT_VERIFIES_INLINE(sigs.v_v(), B1(WASM_BR(0))); - EXPECT_VERIFIES_INLINE(sigs.v_v(), B1(WASM_BRV_IF(0, WASM_ZERO, WASM_ZERO))); - EXPECT_VERIFIES_INLINE(sigs.v_v(), B1(WASM_BR_IF(0, WASM_ZERO))); +TEST_F(AstDecoderTest, Brv1) { + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_BRV(0, WASM_ZERO))); + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_LOOP(WASM_BRV(2, WASM_ZERO)))); +} - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BRV(0, WASM_ZERO))); - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BR(0))); - EXPECT_VERIFIES_INLINE(sigs.v_v(), - WASM_LOOP(WASM_BRV_IF(0, WASM_ZERO, WASM_ZERO))); - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BR_IF(0, WASM_ZERO))); +TEST_F(AstDecoderTest, Brv1_type) { + EXPECT_VERIFIES(i_ii, WASM_BLOCK_I(WASM_BRV(0, WASM_GET_LOCAL(0)))); + EXPECT_VERIFIES(l_ll, WASM_BLOCK_L(WASM_BRV(0, WASM_GET_LOCAL(0)))); + EXPECT_VERIFIES(f_ff, WASM_BLOCK_F(WASM_BRV(0, WASM_GET_LOCAL(0)))); + EXPECT_VERIFIES(d_dd, WASM_BLOCK_D(WASM_BRV(0, WASM_GET_LOCAL(0)))); +} - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BRV(1, WASM_ZERO))); - EXPECT_VERIFIES_INLINE(sigs.v_v(), WASM_LOOP(WASM_BR(1))); +TEST_F(AstDecoderTest, Brv1_type_n) { + EXPECT_FAILURE(i_f, WASM_BLOCK_I(WASM_BRV(0, WASM_GET_LOCAL(0)))); + EXPECT_FAILURE(i_d, WASM_BLOCK_I(WASM_BRV(0, WASM_GET_LOCAL(0)))); +} + +TEST_F(AstDecoderTest, BrvIf1) { + EXPECT_VERIFIES(i_v, WASM_BLOCK_I(WASM_BRV_IF_ZERO(0, WASM_ZERO))); +} + +TEST_F(AstDecoderTest, BrvIf1_type) { + EXPECT_VERIFIES(i_i, WASM_BLOCK_I(WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)))); + EXPECT_VERIFIES(l_l, WASM_BLOCK_L(WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)))); + EXPECT_VERIFIES(f_ff, WASM_BLOCK_F(WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)))); + EXPECT_VERIFIES(d_dd, WASM_BLOCK_D(WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)))); +} + +TEST_F(AstDecoderTest, BrvIf1_type_n) { + EXPECT_FAILURE(i_f, WASM_BLOCK_I(WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)))); + EXPECT_FAILURE(i_d, WASM_BLOCK_I(WASM_BRV_IF_ZERO(0, WASM_GET_LOCAL(0)))); } TEST_F(AstDecoderTest, Select) { - EXPECT_VERIFIES_INLINE( - sigs.i_i(), WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.f_ff(), - WASM_SELECT(WASM_F32(0.0), WASM_F32(0.0), WASM_ZERO)); - EXPECT_VERIFIES_INLINE(sigs.d_dd(), - WASM_SELECT(WASM_F64(0.0), WASM_F64(0.0), WASM_ZERO)); - EXPECT_VERIFIES_INLINE( - sigs.l_l(), WASM_SELECT(WASM_I64V_1(0), WASM_I64V_1(0), WASM_ZERO)); + EXPECT_VERIFIES(i_i, + WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_ZERO)); + EXPECT_VERIFIES(f_ff, WASM_SELECT(WASM_F32(0.0), WASM_F32(0.0), WASM_ZERO)); + EXPECT_VERIFIES(d_dd, WASM_SELECT(WASM_F64(0.0), WASM_F64(0.0), WASM_ZERO)); + EXPECT_VERIFIES(l_l, WASM_SELECT(WASM_I64V_1(0), WASM_I64V_1(0), WASM_ZERO)); } TEST_F(AstDecoderTest, Select_fail1) { - EXPECT_FAILURE_INLINE( - sigs.i_i(), - WASM_SELECT(WASM_F32(0.0), WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE( - sigs.i_i(), - WASM_SELECT(WASM_GET_LOCAL(0), WASM_F32(0.0), WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE( - sigs.i_i(), - WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_F32(0.0))); + EXPECT_FAILURE( + i_i, WASM_SELECT(WASM_F32(0.0), WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); + EXPECT_FAILURE( + i_i, WASM_SELECT(WASM_GET_LOCAL(0), WASM_F32(0.0), WASM_GET_LOCAL(0))); + EXPECT_FAILURE( + i_i, WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_F32(0.0))); } TEST_F(AstDecoderTest, Select_fail2) { @@ -1965,64 +2120,159 @@ TEST_F(AstDecoderTest, Select_fail2) { LocalType types[] = {type, kAstI32, type}; FunctionSig sig(1, 2, types); - EXPECT_VERIFIES_INLINE( - &sig, - WASM_SELECT(WASM_GET_LOCAL(1), WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))); + EXPECT_VERIFIES_S(&sig, WASM_SELECT(WASM_GET_LOCAL(1), WASM_GET_LOCAL(1), + WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE( - &sig, - WASM_SELECT(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); + EXPECT_FAILURE_S(&sig, WASM_SELECT(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0), + WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE( - &sig, - WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))); + EXPECT_FAILURE_S(&sig, WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE( - &sig, - WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); + EXPECT_FAILURE_S(&sig, WASM_SELECT(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), + WASM_GET_LOCAL(1))); } } TEST_F(AstDecoderTest, Select_TypeCheck) { - EXPECT_FAILURE_INLINE( - sigs.i_i(), - WASM_SELECT(WASM_F32(9.9), WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); + EXPECT_FAILURE( + i_i, WASM_SELECT(WASM_F32(9.9), WASM_GET_LOCAL(0), WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE( - sigs.i_i(), - WASM_SELECT(WASM_GET_LOCAL(0), WASM_F64(0.25), WASM_GET_LOCAL(0))); + EXPECT_FAILURE( + i_i, WASM_SELECT(WASM_GET_LOCAL(0), WASM_F64(0.25), WASM_GET_LOCAL(0))); - EXPECT_FAILURE_INLINE( - sigs.i_i(), - WASM_SELECT(WASM_F32(9.9), WASM_GET_LOCAL(0), WASM_I64V_1(0))); + EXPECT_FAILURE(i_i, + WASM_SELECT(WASM_F32(9.9), WASM_GET_LOCAL(0), WASM_I64V_1(0))); } TEST_F(AstDecoderTest, Throw) { FLAG_wasm_eh_prototype = true; - EXPECT_VERIFIES_INLINE(sigs.v_i(), WASM_GET_LOCAL(0), kExprThrow); + EXPECT_VERIFIES(v_i, WASM_GET_LOCAL(0), kExprThrow); - EXPECT_FAILURE_INLINE(sigs.i_d(), WASM_GET_LOCAL(0), kExprThrow, - WASM_I32V(0)); - EXPECT_FAILURE_INLINE(sigs.i_f(), WASM_GET_LOCAL(0), kExprThrow, - WASM_I32V(0)); - EXPECT_FAILURE_INLINE(sigs.l_l(), WASM_GET_LOCAL(0), kExprThrow, - WASM_I64V(0)); + EXPECT_FAILURE(i_d, WASM_GET_LOCAL(0), kExprThrow, WASM_I32V(0)); + EXPECT_FAILURE(i_f, WASM_GET_LOCAL(0), kExprThrow, WASM_I32V(0)); + EXPECT_FAILURE(l_l, WASM_GET_LOCAL(0), kExprThrow, WASM_I64V(0)); } +#define WASM_TRY_OP kExprTry, kLocalVoid + #define WASM_CATCH(local) kExprCatch, static_cast(local) + TEST_F(AstDecoderTest, TryCatch) { FLAG_wasm_eh_prototype = true; - EXPECT_VERIFIES_INLINE(sigs.v_i(), kExprTry, WASM_CATCH(0), kExprEnd); + EXPECT_VERIFIES(v_i, WASM_TRY_OP, WASM_CATCH(0), kExprEnd); // Missing catch. - EXPECT_FAILURE_INLINE(sigs.v_v(), kExprTry, kExprEnd); + EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprEnd); // Missing end. - EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTry, WASM_CATCH(0)); + EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0)); // Double catch. - EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTry, WASM_CATCH(0), WASM_CATCH(0), - kExprEnd); + EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0), WASM_CATCH(0), kExprEnd); +} + +TEST_F(AstDecoderTest, MultiValBlock1) { + FLAG_wasm_mv_prototype = true; + EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), + WASM_GET_LOCAL(1)), + kExprI32Add); +} + +TEST_F(AstDecoderTest, MultiValBlock2) { + FLAG_wasm_mv_prototype = true; + EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), + WASM_GET_LOCAL(1)), + WASM_I32_ADD(WASM_NOP, WASM_NOP)); +} + +TEST_F(AstDecoderTest, MultiValBlockBr1) { + FLAG_wasm_mv_prototype = true; + EXPECT_FAILURE(i_ii, + WASM_BLOCK_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), WASM_BR(0)), + kExprI32Add); + EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), + WASM_GET_LOCAL(1), WASM_BR(0)), + kExprI32Add); +} + +TEST_F(AstDecoderTest, MultiValIf1) { + FLAG_wasm_mv_prototype = true; + EXPECT_FAILURE( + i_ii, WASM_IF_ELSE_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0)), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + kExprI32Add); + EXPECT_FAILURE(i_ii, + WASM_IF_ELSE_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_SEQ(WASM_GET_LOCAL(1))), + kExprI32Add); + EXPECT_VERIFIES( + i_ii, WASM_IF_ELSE_TT(kAstI32, kAstI32, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + kExprI32Add); +} + +class BranchTableIteratorTest : public TestWithZone { + public: + BranchTableIteratorTest() : TestWithZone() {} + void CheckBrTableSize(const byte* start, const byte* end) { + Decoder decoder(start, end); + BranchTableOperand operand(&decoder, start); + BranchTableIterator iterator(&decoder, operand); + EXPECT_EQ(end - start - 1, iterator.length()); + EXPECT_TRUE(decoder.ok()); + } + void CheckBrTableError(const byte* start, const byte* end) { + Decoder decoder(start, end); + BranchTableOperand operand(&decoder, start); + BranchTableIterator iterator(&decoder, operand); + iterator.length(); + EXPECT_FALSE(decoder.ok()); + } +}; + +#define CHECK_BR_TABLE_LENGTH(...) \ + { \ + static byte code[] = {kExprBrTable, __VA_ARGS__}; \ + CheckBrTableSize(code, code + sizeof(code)); \ + } + +#define CHECK_BR_TABLE_ERROR(...) \ + { \ + static byte code[] = {kExprBrTable, __VA_ARGS__}; \ + CheckBrTableError(code, code + sizeof(code)); \ + } + +TEST_F(BranchTableIteratorTest, count0) { + CHECK_BR_TABLE_LENGTH(0, U32V_1(1)); + CHECK_BR_TABLE_LENGTH(0, U32V_2(200)); + CHECK_BR_TABLE_LENGTH(0, U32V_3(30000)); + CHECK_BR_TABLE_LENGTH(0, U32V_4(400000)); + + CHECK_BR_TABLE_LENGTH(0, U32V_1(2)); + CHECK_BR_TABLE_LENGTH(0, U32V_2(300)); + CHECK_BR_TABLE_LENGTH(0, U32V_3(40000)); + CHECK_BR_TABLE_LENGTH(0, U32V_4(500000)); +} + +TEST_F(BranchTableIteratorTest, count1) { + CHECK_BR_TABLE_LENGTH(1, U32V_1(1), U32V_1(6)); + CHECK_BR_TABLE_LENGTH(1, U32V_2(200), U32V_1(8)); + CHECK_BR_TABLE_LENGTH(1, U32V_3(30000), U32V_1(9)); + CHECK_BR_TABLE_LENGTH(1, U32V_4(400000), U32V_1(11)); + + CHECK_BR_TABLE_LENGTH(1, U32V_1(2), U32V_2(6)); + CHECK_BR_TABLE_LENGTH(1, U32V_2(300), U32V_2(7)); + CHECK_BR_TABLE_LENGTH(1, U32V_3(40000), U32V_2(8)); + CHECK_BR_TABLE_LENGTH(1, U32V_4(500000), U32V_2(9)); +} + +TEST_F(BranchTableIteratorTest, error0) { + CHECK_BR_TABLE_ERROR(0); + CHECK_BR_TABLE_ERROR(1, U32V_1(33)); } class WasmOpcodeLengthTest : public TestWithZone { @@ -2044,16 +2294,16 @@ class WasmOpcodeLengthTest : public TestWithZone { TEST_F(WasmOpcodeLengthTest, Statements) { EXPECT_LENGTH(1, kExprNop); - EXPECT_LENGTH(1, kExprBlock); - EXPECT_LENGTH(1, kExprLoop); - EXPECT_LENGTH(1, kExprIf); + EXPECT_LENGTH(2, kExprBlock); + EXPECT_LENGTH(2, kExprLoop); + EXPECT_LENGTH(2, kExprIf); EXPECT_LENGTH(1, kExprElse); EXPECT_LENGTH(1, kExprEnd); EXPECT_LENGTH(1, kExprSelect); - EXPECT_LENGTH(3, kExprBr); - EXPECT_LENGTH(3, kExprBrIf); + EXPECT_LENGTH(2, kExprBr); + EXPECT_LENGTH(2, kExprBrIf); EXPECT_LENGTH(1, kExprThrow); - EXPECT_LENGTH(1, kExprTry); + EXPECT_LENGTH(2, kExprTry); EXPECT_LENGTH(2, kExprCatch); } @@ -2065,14 +2315,8 @@ TEST_F(WasmOpcodeLengthTest, MiscExpressions) { EXPECT_LENGTH(2, kExprSetLocal); EXPECT_LENGTH(2, kExprGetGlobal); EXPECT_LENGTH(2, kExprSetGlobal); - EXPECT_LENGTH(3, kExprCallFunction); - EXPECT_LENGTH(3, kExprCallImport); - EXPECT_LENGTH(3, kExprCallIndirect); - EXPECT_LENGTH(1, kExprIf); - EXPECT_LENGTH(1, kExprBlock); - EXPECT_LENGTH(1, kExprLoop); - EXPECT_LENGTH(3, kExprBr); - EXPECT_LENGTH(3, kExprBrIf); + EXPECT_LENGTH(2, kExprCallFunction); + EXPECT_LENGTH(2, kExprCallIndirect); } TEST_F(WasmOpcodeLengthTest, I32Const) { @@ -2256,248 +2500,6 @@ TEST_F(WasmOpcodeLengthTest, SimpleExpressions) { EXPECT_LENGTH(1, kExprI64ReinterpretF64); } -class WasmOpcodeArityTest : public TestWithZone { - public: - WasmOpcodeArityTest() : TestWithZone() {} -}; - -#define EXPECT_ARITY(expected, ...) \ - { \ - static const byte code[] = {__VA_ARGS__}; \ - EXPECT_EQ(expected, OpcodeArity(code, code + sizeof(code))); \ - } - -TEST_F(WasmOpcodeArityTest, Control) { - EXPECT_ARITY(0, kExprNop); - - EXPECT_ARITY(0, kExprBlock, 0); - EXPECT_ARITY(0, kExprBlock, 1); - EXPECT_ARITY(0, kExprBlock, 2); - EXPECT_ARITY(0, kExprBlock, 5); - EXPECT_ARITY(0, kExprBlock, 10); - - EXPECT_ARITY(0, kExprLoop, 0); - EXPECT_ARITY(0, kExprLoop, 1); - EXPECT_ARITY(0, kExprLoop, 2); - EXPECT_ARITY(0, kExprLoop, 7); - EXPECT_ARITY(0, kExprLoop, 11); - - EXPECT_ARITY(3, kExprSelect); - - EXPECT_ARITY(0, kExprBr); - EXPECT_ARITY(1, kExprBrIf); - EXPECT_ARITY(1, kExprBrTable); - - EXPECT_ARITY(1, kExprBr, ARITY_1); - EXPECT_ARITY(2, kExprBrIf, ARITY_1); - EXPECT_ARITY(2, kExprBrTable, ARITY_1); - - { - EXPECT_ARITY(0, kExprReturn, ARITY_0); - EXPECT_ARITY(1, kExprReturn, ARITY_1); - } - - EXPECT_ARITY(0, kExprThrow); - EXPECT_ARITY(0, kExprTry); - EXPECT_ARITY(1, kExprCatch, 2); -} - -TEST_F(WasmOpcodeArityTest, Misc) { - EXPECT_ARITY(0, kExprI8Const); - EXPECT_ARITY(0, kExprI32Const); - EXPECT_ARITY(0, kExprF32Const); - EXPECT_ARITY(0, kExprI64Const); - EXPECT_ARITY(0, kExprF64Const); - EXPECT_ARITY(0, kExprGetLocal); - EXPECT_ARITY(1, kExprSetLocal); - EXPECT_ARITY(0, kExprGetGlobal); - EXPECT_ARITY(1, kExprSetGlobal); -} - -TEST_F(WasmOpcodeArityTest, Calls) { - { - EXPECT_ARITY(2, kExprCallFunction, 2, 0); - EXPECT_ARITY(2, kExprCallImport, 2, 0); - EXPECT_ARITY(3, kExprCallIndirect, 2, 0); - - EXPECT_ARITY(1, kExprBr, ARITY_1); - EXPECT_ARITY(2, kExprBrIf, ARITY_1); - EXPECT_ARITY(2, kExprBrTable, ARITY_1); - - EXPECT_ARITY(0, kExprBr, ARITY_0); - EXPECT_ARITY(1, kExprBrIf, ARITY_0); - EXPECT_ARITY(1, kExprBrTable, ARITY_0); - } - - { - EXPECT_ARITY(1, kExprCallFunction, ARITY_1, 1); - EXPECT_ARITY(1, kExprCallImport, ARITY_1, 1); - EXPECT_ARITY(2, kExprCallIndirect, ARITY_1, 1); - - EXPECT_ARITY(1, kExprBr, ARITY_1); - EXPECT_ARITY(2, kExprBrIf, ARITY_1); - EXPECT_ARITY(2, kExprBrTable, ARITY_1); - - EXPECT_ARITY(0, kExprBr, ARITY_0); - EXPECT_ARITY(1, kExprBrIf, ARITY_0); - EXPECT_ARITY(1, kExprBrTable, ARITY_0); - } -} - -TEST_F(WasmOpcodeArityTest, LoadsAndStores) { - EXPECT_ARITY(1, kExprI32LoadMem8S); - EXPECT_ARITY(1, kExprI32LoadMem8U); - EXPECT_ARITY(1, kExprI32LoadMem16S); - EXPECT_ARITY(1, kExprI32LoadMem16U); - EXPECT_ARITY(1, kExprI32LoadMem); - - EXPECT_ARITY(1, kExprI64LoadMem8S); - EXPECT_ARITY(1, kExprI64LoadMem8U); - EXPECT_ARITY(1, kExprI64LoadMem16S); - EXPECT_ARITY(1, kExprI64LoadMem16U); - EXPECT_ARITY(1, kExprI64LoadMem32S); - EXPECT_ARITY(1, kExprI64LoadMem32U); - EXPECT_ARITY(1, kExprI64LoadMem); - EXPECT_ARITY(1, kExprF32LoadMem); - EXPECT_ARITY(1, kExprF64LoadMem); - - EXPECT_ARITY(2, kExprI32StoreMem8); - EXPECT_ARITY(2, kExprI32StoreMem16); - EXPECT_ARITY(2, kExprI32StoreMem); - EXPECT_ARITY(2, kExprI64StoreMem8); - EXPECT_ARITY(2, kExprI64StoreMem16); - EXPECT_ARITY(2, kExprI64StoreMem32); - EXPECT_ARITY(2, kExprI64StoreMem); - EXPECT_ARITY(2, kExprF32StoreMem); - EXPECT_ARITY(2, kExprF64StoreMem); -} - -TEST_F(WasmOpcodeArityTest, MiscMemExpressions) { - EXPECT_ARITY(0, kExprMemorySize); - EXPECT_ARITY(1, kExprGrowMemory); -} - -TEST_F(WasmOpcodeArityTest, SimpleExpressions) { - EXPECT_ARITY(2, kExprI32Add); - EXPECT_ARITY(2, kExprI32Sub); - EXPECT_ARITY(2, kExprI32Mul); - EXPECT_ARITY(2, kExprI32DivS); - EXPECT_ARITY(2, kExprI32DivU); - EXPECT_ARITY(2, kExprI32RemS); - EXPECT_ARITY(2, kExprI32RemU); - EXPECT_ARITY(2, kExprI32And); - EXPECT_ARITY(2, kExprI32Ior); - EXPECT_ARITY(2, kExprI32Xor); - EXPECT_ARITY(2, kExprI32Shl); - EXPECT_ARITY(2, kExprI32ShrU); - EXPECT_ARITY(2, kExprI32ShrS); - EXPECT_ARITY(2, kExprI32Eq); - EXPECT_ARITY(2, kExprI32Ne); - EXPECT_ARITY(2, kExprI32LtS); - EXPECT_ARITY(2, kExprI32LeS); - EXPECT_ARITY(2, kExprI32LtU); - EXPECT_ARITY(2, kExprI32LeU); - EXPECT_ARITY(2, kExprI32GtS); - EXPECT_ARITY(2, kExprI32GeS); - EXPECT_ARITY(2, kExprI32GtU); - EXPECT_ARITY(2, kExprI32GeU); - EXPECT_ARITY(1, kExprI32Clz); - EXPECT_ARITY(1, kExprI32Ctz); - EXPECT_ARITY(1, kExprI32Popcnt); - EXPECT_ARITY(1, kExprI32Eqz); - EXPECT_ARITY(2, kExprI64Add); - EXPECT_ARITY(2, kExprI64Sub); - EXPECT_ARITY(2, kExprI64Mul); - EXPECT_ARITY(2, kExprI64DivS); - EXPECT_ARITY(2, kExprI64DivU); - EXPECT_ARITY(2, kExprI64RemS); - EXPECT_ARITY(2, kExprI64RemU); - EXPECT_ARITY(2, kExprI64And); - EXPECT_ARITY(2, kExprI64Ior); - EXPECT_ARITY(2, kExprI64Xor); - EXPECT_ARITY(2, kExprI64Shl); - EXPECT_ARITY(2, kExprI64ShrU); - EXPECT_ARITY(2, kExprI64ShrS); - EXPECT_ARITY(2, kExprI64Eq); - EXPECT_ARITY(2, kExprI64Ne); - EXPECT_ARITY(2, kExprI64LtS); - EXPECT_ARITY(2, kExprI64LeS); - EXPECT_ARITY(2, kExprI64LtU); - EXPECT_ARITY(2, kExprI64LeU); - EXPECT_ARITY(2, kExprI64GtS); - EXPECT_ARITY(2, kExprI64GeS); - EXPECT_ARITY(2, kExprI64GtU); - EXPECT_ARITY(2, kExprI64GeU); - EXPECT_ARITY(1, kExprI64Clz); - EXPECT_ARITY(1, kExprI64Ctz); - EXPECT_ARITY(1, kExprI64Popcnt); - EXPECT_ARITY(2, kExprF32Add); - EXPECT_ARITY(2, kExprF32Sub); - EXPECT_ARITY(2, kExprF32Mul); - EXPECT_ARITY(2, kExprF32Div); - EXPECT_ARITY(2, kExprF32Min); - EXPECT_ARITY(2, kExprF32Max); - EXPECT_ARITY(1, kExprF32Abs); - EXPECT_ARITY(1, kExprF32Neg); - EXPECT_ARITY(2, kExprF32CopySign); - EXPECT_ARITY(1, kExprF32Ceil); - EXPECT_ARITY(1, kExprF32Floor); - EXPECT_ARITY(1, kExprF32Trunc); - EXPECT_ARITY(1, kExprF32NearestInt); - EXPECT_ARITY(1, kExprF32Sqrt); - EXPECT_ARITY(2, kExprF32Eq); - EXPECT_ARITY(2, kExprF32Ne); - EXPECT_ARITY(2, kExprF32Lt); - EXPECT_ARITY(2, kExprF32Le); - EXPECT_ARITY(2, kExprF32Gt); - EXPECT_ARITY(2, kExprF32Ge); - EXPECT_ARITY(2, kExprF64Add); - EXPECT_ARITY(2, kExprF64Sub); - EXPECT_ARITY(2, kExprF64Mul); - EXPECT_ARITY(2, kExprF64Div); - EXPECT_ARITY(2, kExprF64Min); - EXPECT_ARITY(2, kExprF64Max); - EXPECT_ARITY(1, kExprF64Abs); - EXPECT_ARITY(1, kExprF64Neg); - EXPECT_ARITY(2, kExprF64CopySign); - EXPECT_ARITY(1, kExprF64Ceil); - EXPECT_ARITY(1, kExprF64Floor); - EXPECT_ARITY(1, kExprF64Trunc); - EXPECT_ARITY(1, kExprF64NearestInt); - EXPECT_ARITY(1, kExprF64Sqrt); - EXPECT_ARITY(2, kExprF64Eq); - EXPECT_ARITY(2, kExprF64Ne); - EXPECT_ARITY(2, kExprF64Lt); - EXPECT_ARITY(2, kExprF64Le); - EXPECT_ARITY(2, kExprF64Gt); - EXPECT_ARITY(2, kExprF64Ge); - EXPECT_ARITY(1, kExprI32SConvertF32); - EXPECT_ARITY(1, kExprI32SConvertF64); - EXPECT_ARITY(1, kExprI32UConvertF32); - EXPECT_ARITY(1, kExprI32UConvertF64); - EXPECT_ARITY(1, kExprI32ConvertI64); - EXPECT_ARITY(1, kExprI64SConvertF32); - EXPECT_ARITY(1, kExprI64SConvertF64); - EXPECT_ARITY(1, kExprI64UConvertF32); - EXPECT_ARITY(1, kExprI64UConvertF64); - EXPECT_ARITY(1, kExprI64SConvertI32); - EXPECT_ARITY(1, kExprI64UConvertI32); - EXPECT_ARITY(1, kExprF32SConvertI32); - EXPECT_ARITY(1, kExprF32UConvertI32); - EXPECT_ARITY(1, kExprF32SConvertI64); - EXPECT_ARITY(1, kExprF32UConvertI64); - EXPECT_ARITY(1, kExprF32ConvertF64); - EXPECT_ARITY(1, kExprF32ReinterpretI32); - EXPECT_ARITY(1, kExprF64SConvertI32); - EXPECT_ARITY(1, kExprF64UConvertI32); - EXPECT_ARITY(1, kExprF64SConvertI64); - EXPECT_ARITY(1, kExprF64UConvertI64); - EXPECT_ARITY(1, kExprF64ConvertF32); - EXPECT_ARITY(1, kExprF64ReinterpretI64); - EXPECT_ARITY(1, kExprI32ReinterpretF32); - EXPECT_ARITY(1, kExprI64ReinterpretF64); -} - typedef ZoneVector LocalTypeMap; class LocalDeclDecoderTest : public TestWithZone { diff --git a/test/unittests/wasm/control-transfer-unittest.cc b/test/unittests/wasm/control-transfer-unittest.cc index 2b67f12ef5..ab2d937758 100644 --- a/test/unittests/wasm/control-transfer-unittest.cc +++ b/test/unittests/wasm/control-transfer-unittest.cc @@ -24,30 +24,28 @@ namespace wasm { #define B2(a, b) kExprBlock, a, b, kExprEnd #define B3(a, b, c) kExprBlock, a, b, c, kExprEnd -struct ExpectedTarget { +#define TRANSFER_VOID 0 +#define TRANSFER_ONE 1 + +struct ExpectedPcDelta { pc_t pc; - ControlTransfer expected; + pcdiff_t expected; }; // For nicer error messages. -class ControlTransferMatcher : public MatcherInterface { +class ControlTransferMatcher : public MatcherInterface { public: - explicit ControlTransferMatcher(pc_t pc, const ControlTransfer& expected) + explicit ControlTransferMatcher(pc_t pc, const pcdiff_t& expected) : pc_(pc), expected_(expected) {} void DescribeTo(std::ostream* os) const override { - *os << "@" << pc_ << " {pcdiff = " << expected_.pcdiff - << ", spdiff = " << expected_.spdiff - << ", action = " << expected_.action << "}"; + *os << "@" << pc_ << " pcdiff = " << expected_; } - bool MatchAndExplain(const ControlTransfer& input, + bool MatchAndExplain(const pcdiff_t& input, MatchResultListener* listener) const override { - if (input.pcdiff != expected_.pcdiff || input.spdiff != expected_.spdiff || - input.action != expected_.action) { - *listener << "@" << pc_ << " {pcdiff = " << input.pcdiff - << ", spdiff = " << input.spdiff - << ", action = " << input.action << "}"; + if (input != expected_) { + *listener << "@" << pc_ << " pcdiff = " << input; return false; } return true; @@ -55,36 +53,43 @@ class ControlTransferMatcher : public MatcherInterface { private: pc_t pc_; - const ControlTransfer& expected_; + const pcdiff_t& expected_; }; class ControlTransferTest : public TestWithZone { public: - void CheckControlTransfers(const byte* start, const byte* end, - ExpectedTarget* expected_targets, - size_t num_targets) { + void CheckPcDeltas(const byte* start, const byte* end, + ExpectedPcDelta* expected_deltas, size_t num_targets) { ControlTransferMap map = WasmInterpreter::ComputeControlTransfersForTesting(zone(), start, end); // Check all control targets in the map. for (size_t i = 0; i < num_targets; i++) { - pc_t pc = expected_targets[i].pc; + pc_t pc = expected_deltas[i].pc; auto it = map.find(pc); if (it == map.end()) { - printf("expected control target @ +%zu\n", pc); - EXPECT_TRUE(false); + EXPECT_TRUE(false) << "expected control target @ " << pc; } else { - ControlTransfer& expected = expected_targets[i].expected; - ControlTransfer& target = it->second; + pcdiff_t expected = expected_deltas[i].expected; + pcdiff_t& target = it->second; EXPECT_THAT(target, MakeMatcher(new ControlTransferMatcher(pc, expected))); } } + // Check there are no other control targets. + CheckNoOtherTargets(start, end, map, expected_deltas, + num_targets); + } + + template + void CheckNoOtherTargets(const byte* start, const byte* end, + ControlTransferMap& map, T* targets, + size_t num_targets) { // Check there are no other control targets. for (pc_t pc = 0; start + pc < end; pc++) { bool found = false; for (size_t i = 0; i < num_targets; i++) { - if (expected_targets[i].pc == pc) { + if (targets[i].pc == pc) { found = true; break; } @@ -98,125 +103,128 @@ class ControlTransferTest : public TestWithZone { } }; -// Macro for simplifying tests below. -#define EXPECT_TARGETS(...) \ - do { \ - ExpectedTarget pairs[] = {__VA_ARGS__}; \ - CheckControlTransfers(code, code + sizeof(code), pairs, arraysize(pairs)); \ +#define EXPECT_PC_DELTAS(...) \ + do { \ + ExpectedPcDelta pairs[] = {__VA_ARGS__}; \ + CheckPcDeltas(code, code + sizeof(code), pairs, arraysize(pairs)); \ } while (false) TEST_F(ControlTransferTest, SimpleIf) { byte code[] = { kExprI32Const, // @0 - 0, // +1 + 0, // @1 kExprIf, // @2 - kExprEnd // @3 + kLocalVoid, // @3 + kExprEnd // @4 }; - EXPECT_TARGETS({2, {2, 0, ControlTransfer::kPushVoid}}, // -- - {3, {1, 0, ControlTransfer::kPushVoid}}); + EXPECT_PC_DELTAS({2, 2}); } TEST_F(ControlTransferTest, SimpleIf1) { byte code[] = { kExprI32Const, // @0 - 0, // +1 + 0, // @1 kExprIf, // @2 - kExprNop, // @3 - kExprEnd // @4 + kLocalVoid, // @3 + kExprNop, // @4 + kExprEnd // @5 }; - EXPECT_TARGETS({2, {3, 0, ControlTransfer::kPushVoid}}, // -- - {4, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 3}); } TEST_F(ControlTransferTest, SimpleIf2) { byte code[] = { kExprI32Const, // @0 - 0, // +1 + 0, // @1 kExprIf, // @2 - kExprNop, // @3 + kLocalVoid, // @3 kExprNop, // @4 - kExprEnd // @5 + kExprNop, // @5 + kExprEnd // @6 }; - EXPECT_TARGETS({2, {4, 0, ControlTransfer::kPushVoid}}, // -- - {5, {1, 2, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 4}); } TEST_F(ControlTransferTest, SimpleIfElse) { byte code[] = { kExprI32Const, // @0 - 0, // +1 + 0, // @1 kExprIf, // @2 - kExprElse, // @3 - kExprEnd // @4 + kLocalVoid, // @3 + kExprElse, // @4 + kExprEnd // @5 }; - EXPECT_TARGETS({2, {2, 0, ControlTransfer::kNoAction}}, // -- - {3, {2, 0, ControlTransfer::kPushVoid}}, // -- - {4, {1, 0, ControlTransfer::kPushVoid}}); + EXPECT_PC_DELTAS({2, 3}, {4, 2}); +} + +TEST_F(ControlTransferTest, SimpleIfElse_v1) { + byte code[] = { + kExprI32Const, // @0 + 0, // @1 + kExprIf, // @2 + kLocalVoid, // @3 + kExprI8Const, // @4 + 0, // @5 + kExprElse, // @6 + kExprI8Const, // @7 + 0, // @8 + kExprEnd // @9 + }; + EXPECT_PC_DELTAS({2, 5}, {6, 4}); } TEST_F(ControlTransferTest, SimpleIfElse1) { byte code[] = { kExprI32Const, // @0 - 0, // +1 + 0, // @1 kExprIf, // @2 - kExprNop, // @3 + kLocalVoid, // @3 kExprElse, // @4 kExprNop, // @5 kExprEnd // @6 }; - EXPECT_TARGETS({2, {3, 0, ControlTransfer::kNoAction}}, // -- - {4, {3, 1, ControlTransfer::kPopAndRepush}}, // -- - {6, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 3}, {4, 3}); } TEST_F(ControlTransferTest, IfBr) { byte code[] = { kExprI32Const, // @0 - 0, // +1 + 0, // @1 kExprIf, // @2 - kExprBr, // @3 - ARITY_0, // +1 - 0, // +1 + kLocalVoid, // @3 + kExprBr, // @4 + 0, // @5 kExprEnd // @6 }; - EXPECT_TARGETS({2, {5, 0, ControlTransfer::kPushVoid}}, // -- - {3, {4, 0, ControlTransfer::kPushVoid}}, // -- - {6, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 4}, {4, 3}); } TEST_F(ControlTransferTest, IfBrElse) { byte code[] = { kExprI32Const, // @0 - 0, // +1 + 0, // @1 kExprIf, // @2 - kExprBr, // @3 - ARITY_0, // +1 - 0, // +1 + kLocalVoid, // @3 + kExprBr, // @4 + 0, // @5 kExprElse, // @6 kExprEnd // @7 }; - EXPECT_TARGETS({2, {5, 0, ControlTransfer::kNoAction}}, // -- - {3, {5, 0, ControlTransfer::kPushVoid}}, // -- - {6, {2, 1, ControlTransfer::kPopAndRepush}}, // -- - {7, {1, 0, ControlTransfer::kPushVoid}}); + EXPECT_PC_DELTAS({2, 5}, {4, 4}, {6, 2}); } TEST_F(ControlTransferTest, IfElseBr) { byte code[] = { kExprI32Const, // @0 - 0, // +1 + 0, // @1 kExprIf, // @2 - kExprNop, // @3 + kLocalVoid, // @3 kExprElse, // @4 kExprBr, // @5 - ARITY_0, // +1 - 0, // +1 - kExprEnd // @8 + 0, // @6 + kExprEnd // @7 }; - EXPECT_TARGETS({2, {3, 0, ControlTransfer::kNoAction}}, // -- - {4, {5, 1, ControlTransfer::kPopAndRepush}}, // -- - {5, {4, 0, ControlTransfer::kPushVoid}}, // -- - {8, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 3}, {4, 4}, {5, 3}); } TEST_F(ControlTransferTest, BlockEmpty) { @@ -224,177 +232,233 @@ TEST_F(ControlTransferTest, BlockEmpty) { kExprBlock, // @0 kExprEnd // @1 }; - EXPECT_TARGETS({1, {1, 0, ControlTransfer::kPushVoid}}); + CheckPcDeltas(code, code + sizeof(code), nullptr, 0); } TEST_F(ControlTransferTest, Br0) { byte code[] = { kExprBlock, // @0 - kExprBr, // @1 - ARITY_0, // +1 - 0, // +1 + kLocalVoid, // @1 + kExprBr, // @2 + 0, // @3 kExprEnd // @4 }; - EXPECT_TARGETS({1, {4, 0, ControlTransfer::kPushVoid}}, - {4, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 3}); } TEST_F(ControlTransferTest, Br1) { byte code[] = { kExprBlock, // @0 - kExprNop, // @1 - kExprBr, // @2 - ARITY_0, // +1 - 0, // +1 + kLocalVoid, // @1 + kExprNop, // @2 + kExprBr, // @3 + 0, // @4 kExprEnd // @5 }; - EXPECT_TARGETS({2, {4, 1, ControlTransfer::kPopAndRepush}}, // -- - {5, {1, 2, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({3, 3}); +} + +TEST_F(ControlTransferTest, Br_v1a) { + byte code[] = { + kExprBlock, // @0 + kLocalVoid, // @1 + kExprI8Const, // @2 + 0, // @3 + kExprBr, // @4 + 0, // @5 + kExprEnd // @6 + }; + EXPECT_PC_DELTAS({4, 3}); +} + +TEST_F(ControlTransferTest, Br_v1b) { + byte code[] = { + kExprBlock, // @0 + kLocalVoid, // @1 + kExprI8Const, // @2 + 0, // @3 + kExprBr, // @4 + 0, // @5 + kExprEnd // @6 + }; + EXPECT_PC_DELTAS({4, 3}); +} + +TEST_F(ControlTransferTest, Br_v1c) { + byte code[] = { + kExprI8Const, // @0 + 0, // @1 + kExprBlock, // @2 + kLocalVoid, // @3 + kExprBr, // @4 + 0, // @5 + kExprEnd // @6 + }; + EXPECT_PC_DELTAS({4, 3}); } TEST_F(ControlTransferTest, Br2) { byte code[] = { kExprBlock, // @0 - kExprNop, // @1 + kLocalVoid, // @1 kExprNop, // @2 - kExprBr, // @3 - ARITY_0, // +1 - 0, // +1 + kExprNop, // @3 + kExprBr, // @4 + 0, // @5 kExprEnd // @6 }; - EXPECT_TARGETS({3, {4, 2, ControlTransfer::kPopAndRepush}}, // -- - {6, {1, 3, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({4, 3}); } TEST_F(ControlTransferTest, Br0b) { byte code[] = { kExprBlock, // @0 - kExprBr, // @1 - ARITY_0, // +1 - 0, // +1 + kLocalVoid, // @1 + kExprBr, // @2 + 0, // @3 kExprNop, // @4 kExprEnd // @5 }; - EXPECT_TARGETS({1, {5, 0, ControlTransfer::kPushVoid}}, // -- - {5, {1, 2, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 4}); } TEST_F(ControlTransferTest, Br0c) { byte code[] = { kExprBlock, // @0 - kExprBr, // @1 - ARITY_0, // +1 - 0, // +1 + kLocalVoid, // @1 + kExprBr, // @2 + 0, // @3 kExprNop, // @4 kExprNop, // @5 kExprEnd // @6 }; - EXPECT_TARGETS({1, {6, 0, ControlTransfer::kPushVoid}}, // -- - {6, {1, 3, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 5}); } TEST_F(ControlTransferTest, SimpleLoop1) { byte code[] = { - kExprLoop, // @0 - kExprBr, // @1 - ARITY_0, // +1 - 0, // +1 - kExprEnd // @4 + kExprLoop, // @0 + kLocalVoid, // @1 + kExprBr, // @2 + 0, // @3 + kExprEnd // @4 }; - EXPECT_TARGETS({1, {-1, 0, ControlTransfer::kNoAction}}, // -- - {4, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, -2}); } TEST_F(ControlTransferTest, SimpleLoop2) { byte code[] = { - kExprLoop, // @0 - kExprNop, // @1 - kExprBr, // @2 - ARITY_0, // +1 - 0, // +1 - kExprEnd // @5 + kExprLoop, // @0 + kLocalVoid, // @1 + kExprNop, // @2 + kExprBr, // @3 + 0, // @4 + kExprEnd // @5 }; - EXPECT_TARGETS({2, {-2, 1, ControlTransfer::kNoAction}}, // -- - {5, {1, 2, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({3, -3}); } TEST_F(ControlTransferTest, SimpleLoopExit1) { byte code[] = { - kExprLoop, // @0 - kExprBr, // @1 - ARITY_0, // +1 - 1, // +1 - kExprEnd // @4 + kExprLoop, // @0 + kLocalVoid, // @1 + kExprBr, // @2 + 1, // @3 + kExprEnd // @4 }; - EXPECT_TARGETS({1, {4, 0, ControlTransfer::kPushVoid}}, // -- - {4, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({2, 3}); } TEST_F(ControlTransferTest, SimpleLoopExit2) { byte code[] = { - kExprLoop, // @0 - kExprNop, // @1 - kExprBr, // @2 - ARITY_0, // +1 - 1, // +1 - kExprEnd // @5 + kExprLoop, // @0 + kLocalVoid, // @1 + kExprNop, // @2 + kExprBr, // @3 + 1, // @4 + kExprEnd // @5 }; - EXPECT_TARGETS({2, {4, 1, ControlTransfer::kPopAndRepush}}, // -- - {5, {1, 2, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({3, 3}); } TEST_F(ControlTransferTest, BrTable0) { byte code[] = { kExprBlock, // @0 - kExprI8Const, // @1 - 0, // +1 - kExprBrTable, // @3 - ARITY_0, // +1 - 0, // +1 - U32_LE(0), // +4 - kExprEnd // @10 + kLocalVoid, // @1 + kExprI8Const, // @2 + 0, // @3 + kExprBrTable, // @4 + 0, // @5 + U32V_1(0), // @6 + kExprEnd // @7 }; - EXPECT_TARGETS({3, {8, 0, ControlTransfer::kPushVoid}}, // -- - {10, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({4, 4}); +} + +TEST_F(ControlTransferTest, BrTable0_v1a) { + byte code[] = { + kExprBlock, // @0 + kLocalVoid, // @1 + kExprI8Const, // @2 + 0, // @3 + kExprI8Const, // @4 + 0, // @5 + kExprBrTable, // @6 + 0, // @7 + U32V_1(0), // @8 + kExprEnd // @9 + }; + EXPECT_PC_DELTAS({6, 4}); +} + +TEST_F(ControlTransferTest, BrTable0_v1b) { + byte code[] = { + kExprBlock, // @0 + kLocalVoid, // @1 + kExprI8Const, // @2 + 0, // @3 + kExprI8Const, // @4 + 0, // @5 + kExprBrTable, // @6 + 0, // @7 + U32V_1(0), // @8 + kExprEnd // @9 + }; + EXPECT_PC_DELTAS({6, 4}); } TEST_F(ControlTransferTest, BrTable1) { byte code[] = { kExprBlock, // @0 - kExprI8Const, // @1 - 0, // +1 - kExprBrTable, // @3 - ARITY_0, // +1 - 1, // +1 - U32_LE(0), // +4 - U32_LE(0), // +4 - kExprEnd // @14 + kLocalVoid, // @1 + kExprI8Const, // @2 + 0, // @3 + kExprBrTable, // @4 + 1, // @5 + U32V_1(0), // @6 + U32V_1(0), // @7 + kExprEnd // @8 }; - EXPECT_TARGETS({3, {12, 0, ControlTransfer::kPushVoid}}, // -- - {4, {11, 0, ControlTransfer::kPushVoid}}, // -- - {14, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({4, 5}, {5, 4}); } TEST_F(ControlTransferTest, BrTable2) { byte code[] = { kExprBlock, // @0 - kExprBlock, // @1 - kExprI8Const, // @2 - 0, // +1 - kExprBrTable, // @4 - ARITY_0, // +1 - 2, // +1 - U32_LE(0), // +4 - U32_LE(0), // +4 - U32_LE(1), // +4 - kExprEnd, // @19 - kExprEnd // @19 + kLocalVoid, // @1 + kExprBlock, // @2 + kLocalVoid, // @3 + kExprI8Const, // @4 + 0, // @5 + kExprBrTable, // @6 + 2, // @7 + U32V_1(0), // @8 + U32V_1(0), // @9 + U32V_1(1), // @10 + kExprEnd, // @11 + kExprEnd // @12 }; - EXPECT_TARGETS({4, {16, 0, ControlTransfer::kPushVoid}}, // -- - {5, {15, 0, ControlTransfer::kPushVoid}}, // -- - {6, {15, 0, ControlTransfer::kPushVoid}}, // -- - {19, {1, 1, ControlTransfer::kPopAndRepush}}, // -- - {20, {1, 1, ControlTransfer::kPopAndRepush}}); + EXPECT_PC_DELTAS({6, 6}, {7, 5}, {8, 5}); } } // namespace wasm diff --git a/test/unittests/wasm/loop-assignment-analysis-unittest.cc b/test/unittests/wasm/loop-assignment-analysis-unittest.cc index 9c89d5df58..f4e4be5fb2 100644 --- a/test/unittests/wasm/loop-assignment-analysis-unittest.cc +++ b/test/unittests/wasm/loop-assignment-analysis-unittest.cc @@ -39,7 +39,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Empty0) { } TEST_F(WasmLoopAssignmentAnalyzerTest, Empty1) { - byte code[] = {kExprLoop, 0}; + byte code[] = {kExprLoop, kLocalVoid, 0}; for (int i = 0; i < 5; i++) { BitVector* assigned = Analyze(code, code + arraysize(code)); for (int j = 0; j < assigned->length(); j++) { @@ -60,6 +60,17 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, One) { } } +TEST_F(WasmLoopAssignmentAnalyzerTest, TeeOne) { + num_locals = 5; + for (int i = 0; i < 5; i++) { + byte code[] = {WASM_LOOP(WASM_TEE_LOCAL(i, WASM_ZERO))}; + BitVector* assigned = Analyze(code, code + arraysize(code)); + for (int j = 0; j < assigned->length(); j++) { + CHECK_EQ(j == i, assigned->Contains(j)); + } + } +} + TEST_F(WasmLoopAssignmentAnalyzerTest, OneBeyond) { num_locals = 5; for (int i = 0; i < 5; i++) { @@ -98,24 +109,10 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, NestedIf) { } } -static byte LEBByte(uint32_t val, byte which) { - byte b = (val >> (which * 7)) & 0x7F; - if (val >> ((which + 1) * 7)) b |= 0x80; - return b; -} - TEST_F(WasmLoopAssignmentAnalyzerTest, BigLocal) { num_locals = 65000; for (int i = 13; i < 65000; i = static_cast(i * 1.5)) { - byte code[] = {kExprLoop, - 1, - kExprSetLocal, - LEBByte(i, 0), - LEBByte(i, 1), - LEBByte(i, 2), - 11, - 12, - 13}; + byte code[] = {WASM_LOOP(WASM_I8(11), kExprSetLocal, U32V_3(i))}; BitVector* assigned = Analyze(code, code + arraysize(code)); for (int j = 0; j < assigned->length(); j++) { @@ -172,7 +169,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) { WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, WASM_GET_LOCAL(kSum)), WASM_GET_LOCAL(kIter))}; - BitVector* assigned = Analyze(code + 1, code + arraysize(code)); + BitVector* assigned = Analyze(code + 2, code + arraysize(code)); for (int j = 0; j < assigned->length(); j++) { bool expected = j == kIter || j == kSum; CHECK_EQ(expected, assigned->Contains(j)); @@ -180,9 +177,9 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) { } TEST_F(WasmLoopAssignmentAnalyzerTest, Malformed) { - byte code[] = {kExprLoop, kExprF32Neg, kExprBrTable, 0x0e, 'h', 'e', - 'l', 'l', 'o', ',', ' ', 'w', - 'o', 'r', 'l', 'd', '!'}; + byte code[] = {kExprLoop, kLocalVoid, kExprF32Neg, kExprBrTable, 0x0e, 'h', + 'e', 'l', 'l', 'o', ',', ' ', + 'w', 'o', 'r', 'l', 'd', '!'}; BitVector* assigned = Analyze(code, code + arraysize(code)); CHECK_NULL(assigned); } diff --git a/test/unittests/wasm/module-decoder-unittest.cc b/test/unittests/wasm/module-decoder-unittest.cc index 28ec527fbe..74157d57f7 100644 --- a/test/unittests/wasm/module-decoder-unittest.cc +++ b/test/unittests/wasm/module-decoder-unittest.cc @@ -14,7 +14,16 @@ namespace v8 { namespace internal { namespace wasm { -#define EMPTY_FUNCTION(sig_index) 0, SIG_INDEX(sig_index), U16_LE(0) +#define WASM_INIT_EXPR_I32V_1(val) WASM_I32V_1(val), kExprEnd +#define WASM_INIT_EXPR_I32V_2(val) WASM_I32V_2(val), kExprEnd +#define WASM_INIT_EXPR_I32V_3(val) WASM_I32V_3(val), kExprEnd +#define WASM_INIT_EXPR_I32V_4(val) WASM_I32V_4(val), kExprEnd +#define WASM_INIT_EXPR_I32V_5(val) WASM_I32V_5(val), kExprEnd +#define WASM_INIT_EXPR_F32(val) WASM_F32(val), kExprEnd +#define WASM_INIT_EXPR_I64(val) WASM_I64(val), kExprEnd +#define WASM_INIT_EXPR_F64(val) WASM_F64(val), kExprEnd +#define WASM_INIT_EXPR_GLOBAL(index) WASM_GET_GLOBAL(index), kExprEnd + #define SIZEOF_EMPTY_FUNCTION ((size_t)5) #define EMPTY_BODY 0 #define SIZEOF_EMPTY_BODY ((size_t)1) @@ -23,64 +32,64 @@ namespace wasm { #define SIG_ENTRY_i_i SIG_ENTRY_x_x(kLocalI32, kLocalI32) -#define UNKNOWN_EMPTY_SECTION_NAME 1, '\0' -#define UNKNOWN_SECTION_NAME 4, 'l', 'u', 'l', 'z' +#define UNKNOWN_SECTION(size) 0, U32V_1(size + 5), 4, 'l', 'u', 'l', 'z' -#define SECTION(NAME, EXTRA_SIZE) WASM_SECTION_##NAME, U32V_1(EXTRA_SIZE) +#define SECTION(name, size) k##name##SectionCode, U32V_1(size) #define SIGNATURES_SECTION(count, ...) \ - SECTION(SIGNATURES, 1 + 3 * (count)), U32V_1(count), __VA_ARGS__ + SECTION(Type, 1 + 3 * (count)), U32V_1(count), __VA_ARGS__ #define FUNCTION_SIGNATURES_SECTION(count, ...) \ - SECTION(FUNCTION_SIGNATURES, 1 + (count)), U32V_1(count), __VA_ARGS__ + SECTION(Function, 1 + (count)), U32V_1(count), __VA_ARGS__ #define FOO_STRING 3, 'f', 'o', 'o' #define NO_LOCAL_NAMES 0 -#define EMPTY_SIGNATURES_SECTION SECTION(SIGNATURES, 1), 0 -#define EMPTY_FUNCTION_SIGNATURES_SECTION SECTION(FUNCTION_SIGNATURES, 1), 0 -#define EMPTY_FUNCTION_BODIES_SECTION SECTION(FUNCTION_BODIES, 1), 0 -#define EMPTY_NAMES_SECTION SECTION(NAMES, 1), 0 +#define EMPTY_SIGNATURES_SECTION SECTION(Type, 1), 0 +#define EMPTY_FUNCTION_SIGNATURES_SECTION SECTION(Function, 1), 0 +#define EMPTY_FUNCTION_BODIES_SECTION SECTION(Code, 1), 0 +#define SECTION_NAMES(size) \ + kUnknownSectionCode, U32V_1(size + 5), 4, 'n', 'a', 'm', 'e' +#define EMPTY_NAMES_SECTION SECTION_NAMES(1), 0 #define X1(...) __VA_ARGS__ #define X2(...) __VA_ARGS__, __VA_ARGS__ #define X3(...) __VA_ARGS__, __VA_ARGS__, __VA_ARGS__ #define X4(...) __VA_ARGS__, __VA_ARGS__, __VA_ARGS__, __VA_ARGS__ -#define ONE_EMPTY_FUNCTION WASM_SECTION_FUNCTION_SIGNATURES, 1 + 1 * 1, 1, X1(0) +#define ONE_EMPTY_FUNCTION SECTION(Function, 1 + 1 * 1), 1, X1(0) -#define TWO_EMPTY_FUNCTIONS \ - WASM_SECTION_FUNCTION_SIGNATURES, 1 + 2 * 1, 2, X2(0) +#define TWO_EMPTY_FUNCTIONS SECTION(Function, 1 + 2 * 1), 2, X2(0) -#define THREE_EMPTY_FUNCTIONS \ - WASM_SECTION_FUNCTION_SIGNATURES, 1 + 3 * 1, 3, X3(0) +#define THREE_EMPTY_FUNCTIONS SECTION(Function, 1 + 3 * 1), 3, X3(0) -#define FOUR_EMPTY_FUNCTIONS \ - WASM_SECTION_FUNCTION_SIGNATURES, 1 + 4 * 1, 4, X4(0) +#define FOUR_EMPTY_FUNCTIONS SECTION(Function, 1 + 4 * 1), 4, X4(0) -#define ONE_EMPTY_BODY \ - WASM_SECTION_FUNCTION_BODIES, 1 + 1 * (1 + SIZEOF_EMPTY_BODY), 1, \ - X1(SIZEOF_EMPTY_BODY, EMPTY_BODY) +#define ONE_EMPTY_BODY \ + SECTION(Code, 1 + 1 * (1 + SIZEOF_EMPTY_BODY)) \ + , 1, X1(SIZEOF_EMPTY_BODY, EMPTY_BODY) -#define TWO_EMPTY_BODIES \ - WASM_SECTION_FUNCTION_BODIES, 1 + 2 * (1 + SIZEOF_EMPTY_BODY), 2, \ - X2(SIZEOF_EMPTY_BODY, EMPTY_BODY) +#define TWO_EMPTY_BODIES \ + SECTION(Code, 1 + 2 * (1 + SIZEOF_EMPTY_BODY)) \ + , 2, X2(SIZEOF_EMPTY_BODY, EMPTY_BODY) -#define THREE_EMPTY_BODIES \ - WASM_SECTION_FUNCTION_BODIES, 1 + 3 * (1 + SIZEOF_EMPTY_BODY), 3, \ - X3(SIZEOF_EMPTY_BODY, EMPTY_BODY) +#define THREE_EMPTY_BODIES \ + SECTION(Code, 1 + 3 * (1 + SIZEOF_EMPTY_BODY)) \ + , 3, X3(SIZEOF_EMPTY_BODY, EMPTY_BODY) -#define FOUR_EMPTY_BODIES \ - WASM_SECTION_FUNCTION_BODIES, 1 + 4 * (1 + SIZEOF_EMPTY_BODY), 4, \ - X4(SIZEOF_EMPTY_BODY, EMPTY_BODY) +#define FOUR_EMPTY_BODIES \ + SECTION(Code, 1 + 4 * (1 + SIZEOF_EMPTY_BODY)) \ + , 4, X4(SIZEOF_EMPTY_BODY, EMPTY_BODY) #define SIGNATURES_SECTION_VOID_VOID \ - SECTION(SIGNATURES, 1 + SIZEOF_SIG_ENTRY_v_v), 1, SIG_ENTRY_v_v + SECTION(Type, 1 + SIZEOF_SIG_ENTRY_v_v), 1, SIG_ENTRY_v_v -#define EXPECT_VERIFIES(data) \ - do { \ - ModuleResult result = DecodeModule(data, data + arraysize(data)); \ - EXPECT_TRUE(result.ok()); \ - if (result.val) delete result.val; \ +#define LINEAR_MEMORY_INDEX_0 0 + +#define EXPECT_VERIFIES(data) \ + do { \ + ModuleResult result = DecodeModule(data, data + sizeof(data)); \ + EXPECT_TRUE(result.ok()); \ + if (result.val) delete result.val; \ } while (false) #define EXPECT_FAILURE_LEN(data, length) \ @@ -149,8 +158,7 @@ class WasmModuleVerifyTest : public TestWithIsolateAndZone { TEST_F(WasmModuleVerifyTest, WrongMagic) { for (uint32_t x = 1; x; x <<= 1) { - const byte data[] = {U32_LE(kWasmMagic ^ x), U32_LE(kWasmVersion), - SECTION(END, 0)}; + const byte data[] = {U32_LE(kWasmMagic ^ x), U32_LE(kWasmVersion)}; ModuleResult result = DecodeModuleNoHeader(data, data + sizeof(data)); EXPECT_FALSE(result.ok()); if (result.val) delete result.val; @@ -159,8 +167,7 @@ TEST_F(WasmModuleVerifyTest, WrongMagic) { TEST_F(WasmModuleVerifyTest, WrongVersion) { for (uint32_t x = 1; x; x <<= 1) { - const byte data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion ^ x), - SECTION(END, 0)}; + const byte data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion ^ x)}; ModuleResult result = DecodeModuleNoHeader(data, data + sizeof(data)); EXPECT_FALSE(result.ok()); if (result.val) delete result.val; @@ -168,23 +175,23 @@ TEST_F(WasmModuleVerifyTest, WrongVersion) { } TEST_F(WasmModuleVerifyTest, DecodeEmpty) { - static const byte data[] = {SECTION(END, 0)}; - EXPECT_VERIFIES(data); + ModuleResult result = DecodeModule(nullptr, 0); + EXPECT_TRUE(result.ok()); + if (result.val) delete result.val; } TEST_F(WasmModuleVerifyTest, OneGlobal) { static const byte data[] = { - SECTION(GLOBALS, 5), // -- + SECTION(Global, 6), // -- 1, - NAME_LENGTH(1), - 'g', // name - kLocalI32, // local type - 0, // exported + kLocalI32, // local type + 0, // immutable + WASM_INIT_EXPR_I32V_1(13) // init }; { // Should decode to exactly one global. - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(1, result.val->globals.size()); EXPECT_EQ(0, result.val->functions.size()); @@ -192,10 +199,11 @@ TEST_F(WasmModuleVerifyTest, OneGlobal) { const WasmGlobal* global = &result.val->globals.back(); - EXPECT_EQ(1, global->name_length); EXPECT_EQ(kAstI32, global->type); EXPECT_EQ(0, global->offset); - EXPECT_FALSE(global->exported); + EXPECT_EQ(false, global->mutability); + EXPECT_EQ(WasmInitExpr::kI32Const, global->init.kind); + EXPECT_EQ(13, global->init.val.i32_const); if (result.val) delete result.val; } @@ -205,12 +213,11 @@ TEST_F(WasmModuleVerifyTest, OneGlobal) { TEST_F(WasmModuleVerifyTest, Global_invalid_type) { static const byte data[] = { - SECTION(GLOBALS, 5), // -- + SECTION(Global, 6), // -- 1, - NAME_LENGTH(1), - 'g', // name - 64, // invalid memory type - 0, // exported + 64, // invalid memory type + 1, // mutable + WASM_INIT_EXPR_I32V_1(33), // init }; ModuleResult result = DecodeModule(data, data + sizeof(data)); @@ -220,12 +227,11 @@ TEST_F(WasmModuleVerifyTest, Global_invalid_type) { TEST_F(WasmModuleVerifyTest, Global_invalid_type2) { static const byte data[] = { - SECTION(GLOBALS, 5), // -- + SECTION(Global, 6), // -- 1, - NAME_LENGTH(1), - 'g', // name - kLocalVoid, // invalid memory type - 0, // exported + kLocalVoid, // invalid memory type + 1, // mutable + WASM_INIT_EXPR_I32V_1(33), // init }; ModuleResult result = DecodeModule(data, data + sizeof(data)); @@ -235,10 +241,10 @@ TEST_F(WasmModuleVerifyTest, Global_invalid_type2) { TEST_F(WasmModuleVerifyTest, ZeroGlobals) { static const byte data[] = { - SECTION(GLOBALS, 1), // -- - 0, // declare 0 globals + SECTION(Global, 1), // -- + 0, // declare 0 globals }; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); if (result.val) delete result.val; } @@ -259,15 +265,15 @@ static void AppendUint32v(std::vector& buffer, uint32_t val) { TEST_F(WasmModuleVerifyTest, NGlobals) { static const byte data[] = { - NO_NAME, // name length - kLocalF32, // memory type - 0, // exported + kLocalF32, // memory type + 0, // immutable + WASM_INIT_EXPR_F32(7.7), // init }; for (uint32_t i = 0; i < 1000000; i = i * 13 + 1) { std::vector buffer; size_t size = SizeOfVarInt(i) + i * sizeof(data); - const byte globals[] = {WASM_SECTION_GLOBALS, U32V_5(size)}; + const byte globals[] = {kGlobalSectionCode, U32V_5(size)}; for (size_t g = 0; g != sizeof(globals); ++g) { buffer.push_back(globals[g]); } @@ -282,74 +288,48 @@ TEST_F(WasmModuleVerifyTest, NGlobals) { } } -TEST_F(WasmModuleVerifyTest, GlobalWithInvalidNameOffset) { - static const byte data[] = { - SECTION(GLOBALS, 7), - 1, // declare one global - NO_NAME, // name offset - 33, // memory type - 0, // exported - }; - - EXPECT_FAILURE(data); -} - -TEST_F(WasmModuleVerifyTest, GlobalWithInvalidNameLength) { - static const byte data[] = { - SECTION(GLOBALS, 5), // -- - 1, - NAME_LENGTH(56), // invalid length - 'g', // name - kLocalI32, // memory type - 0, // exported - }; - EXPECT_FAILURE(data); -} - TEST_F(WasmModuleVerifyTest, GlobalWithInvalidMemoryType) { - static const byte data[] = { - SECTION(GLOBALS, 7), - 1, // declare one global - NO_NAME, // name offset - 33, // memory type - 0, // exported - }; + static const byte data[] = {SECTION(Global, 7), + 33, // memory type + 0, // exported + WASM_INIT_EXPR_I32V_1(1)}; EXPECT_FAILURE(data); } TEST_F(WasmModuleVerifyTest, TwoGlobals) { static const byte data[] = { - SECTION(GLOBALS, 7), + SECTION(Global, 21), 2, - NO_NAME, // #0: name length kLocalF32, // type - 0, // exported - NO_NAME, // #1: name length + 0, // immutable + WASM_INIT_EXPR_F32(22.0), kLocalF64, // type - 1, // exported + 1, // mutable + WASM_INIT_EXPR_F64(23.0), }; { // Should decode to exactly two globals. - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(2, result.val->globals.size()); EXPECT_EQ(0, result.val->functions.size()); EXPECT_EQ(0, result.val->data_segments.size()); const WasmGlobal* g0 = &result.val->globals[0]; - const WasmGlobal* g1 = &result.val->globals[1]; - EXPECT_EQ(0, g0->name_length); EXPECT_EQ(kAstF32, g0->type); EXPECT_EQ(0, g0->offset); - EXPECT_FALSE(g0->exported); + EXPECT_EQ(false, g0->mutability); + EXPECT_EQ(WasmInitExpr::kF32Const, g0->init.kind); + + const WasmGlobal* g1 = &result.val->globals[1]; - EXPECT_EQ(0, g1->name_length); EXPECT_EQ(kAstF64, g1->type); EXPECT_EQ(8, g1->offset); - EXPECT_TRUE(g1->exported); + EXPECT_EQ(true, g1->mutability); + EXPECT_EQ(WasmInitExpr::kF64Const, g1->init.kind); if (result.val) delete result.val; } @@ -364,23 +344,23 @@ TEST_F(WasmModuleVerifyTest, OneSignature) { } { - static const byte data[] = {SECTION(SIGNATURES, 1 + SIZEOF_SIG_ENTRY_x_x), - 1, SIG_ENTRY_i_i}; + static const byte data[] = {SECTION(Type, 1 + SIZEOF_SIG_ENTRY_x_x), 1, + SIG_ENTRY_i_i}; EXPECT_VERIFIES(data); } } TEST_F(WasmModuleVerifyTest, MultipleSignatures) { static const byte data[] = { - SECTION(SIGNATURES, 1 + SIZEOF_SIG_ENTRY_v_v + SIZEOF_SIG_ENTRY_x_x + - SIZEOF_SIG_ENTRY_x_xx), // -- + SECTION(Type, 1 + SIZEOF_SIG_ENTRY_v_v + SIZEOF_SIG_ENTRY_x_x + + SIZEOF_SIG_ENTRY_x_xx), // -- 3, // -- SIG_ENTRY_v_v, // void -> void SIG_ENTRY_x_x(kLocalI32, kLocalF32), // f32 -> i32 SIG_ENTRY_x_xx(kLocalI32, kLocalF64, kLocalF64), // f64,f64 -> i32 }; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(3, result.val->signatures.size()); if (result.val->signatures.size() == 3) { @@ -398,16 +378,18 @@ TEST_F(WasmModuleVerifyTest, MultipleSignatures) { } TEST_F(WasmModuleVerifyTest, OneDataSegment) { - const byte kDataSegmentSourceOffset = 30; + const byte kDataSegmentSourceOffset = 24; const byte data[] = { - SECTION(MEMORY, 3), + SECTION(Memory, 4), + ENTRY_COUNT(1), + kResizableMaximumFlag, 28, 28, - 1, - SECTION(DATA_SEGMENTS, 8), - 1, - U32V_3(0x9bbaa), // dest addr - U32V_1(3), // source size + SECTION(Data, 11), + ENTRY_COUNT(1), + LINEAR_MEMORY_INDEX_0, + WASM_INIT_EXPR_I32V_3(0x9bbaa), // dest addr + U32V_1(3), // source size 'a', 'b', 'c' // data bytes @@ -415,7 +397,7 @@ TEST_F(WasmModuleVerifyTest, OneDataSegment) { { EXPECT_VERIFIES(data); - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(0, result.val->globals.size()); EXPECT_EQ(0, result.val->functions.size()); @@ -423,36 +405,39 @@ TEST_F(WasmModuleVerifyTest, OneDataSegment) { const WasmDataSegment* segment = &result.val->data_segments.back(); - EXPECT_EQ(0x9bbaa, segment->dest_addr); + EXPECT_EQ(WasmInitExpr::kI32Const, segment->dest_addr.kind); + EXPECT_EQ(0x9bbaa, segment->dest_addr.val.i32_const); EXPECT_EQ(kDataSegmentSourceOffset, segment->source_offset); EXPECT_EQ(3, segment->source_size); - EXPECT_TRUE(segment->init); if (result.val) delete result.val; } - EXPECT_OFF_END_FAILURE(data, 13, sizeof(data)); + EXPECT_OFF_END_FAILURE(data, 14, sizeof(data)); } TEST_F(WasmModuleVerifyTest, TwoDataSegments) { - const byte kDataSegment0SourceOffset = 30; - const byte kDataSegment1SourceOffset = 30 + 8; + const byte kDataSegment0SourceOffset = 24; + const byte kDataSegment1SourceOffset = kDataSegment0SourceOffset + 11; const byte data[] = { - SECTION(MEMORY, 3), + SECTION(Memory, 4), + ENTRY_COUNT(1), + kResizableMaximumFlag, 28, 28, - 1, - SECTION(DATA_SEGMENTS, 23), - 2, // segment count - U32V_3(0x7ffee), // #0: dest addr - U32V_1(4), // source size + SECTION(Data, 29), + ENTRY_COUNT(2), // segment count + LINEAR_MEMORY_INDEX_0, + WASM_INIT_EXPR_I32V_3(0x7ffee), // #0: dest addr + U32V_1(4), // source size 1, 2, 3, - 4, // data bytes - U32V_3(0x6ddcc), // #1: dest addr - U32V_1(10), // source size + 4, // data bytes + LINEAR_MEMORY_INDEX_0, + WASM_INIT_EXPR_I32V_3(0x6ddcc), // #1: dest addr + U32V_1(10), // source size 1, 2, 3, @@ -466,7 +451,7 @@ TEST_F(WasmModuleVerifyTest, TwoDataSegments) { }; { - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(0, result.val->globals.size()); EXPECT_EQ(0, result.val->functions.size()); @@ -475,61 +460,79 @@ TEST_F(WasmModuleVerifyTest, TwoDataSegments) { const WasmDataSegment* s0 = &result.val->data_segments[0]; const WasmDataSegment* s1 = &result.val->data_segments[1]; - EXPECT_EQ(0x7ffee, s0->dest_addr); + EXPECT_EQ(WasmInitExpr::kI32Const, s0->dest_addr.kind); + EXPECT_EQ(0x7ffee, s0->dest_addr.val.i32_const); EXPECT_EQ(kDataSegment0SourceOffset, s0->source_offset); EXPECT_EQ(4, s0->source_size); - EXPECT_TRUE(s0->init); - EXPECT_EQ(0x6ddcc, s1->dest_addr); + EXPECT_EQ(WasmInitExpr::kI32Const, s1->dest_addr.kind); + EXPECT_EQ(0x6ddcc, s1->dest_addr.val.i32_const); EXPECT_EQ(kDataSegment1SourceOffset, s1->source_offset); EXPECT_EQ(10, s1->source_size); - EXPECT_TRUE(s1->init); if (result.val) delete result.val; } - EXPECT_OFF_END_FAILURE(data, 13, sizeof(data)); + EXPECT_OFF_END_FAILURE(data, 14, sizeof(data)); } -TEST_F(WasmModuleVerifyTest, DataSegmentWithInvalidDest) { - const int source_size = 3; +TEST_F(WasmModuleVerifyTest, DataSegment_wrong_init_type) { + const byte data[] = { + SECTION(Memory, 4), + ENTRY_COUNT(1), + kResizableMaximumFlag, + 28, + 28, + SECTION(Data, 11), + ENTRY_COUNT(1), + LINEAR_MEMORY_INDEX_0, + WASM_INIT_EXPR_F64(9.9), // dest addr + U32V_1(3), // source size + 'a', + 'b', + 'c' // data bytes + }; - for (byte mem_pages = 1; mem_pages < 16; mem_pages++) { - int mem_size = mem_pages * 0x10000; // 64k pages. - - for (int dest_addr = mem_size - source_size; - dest_addr < mem_size + source_size; dest_addr++) { - byte data[] = {SECTION(MEMORY, 3), - mem_pages, - mem_pages, - 1, - SECTION(DATA_SEGMENTS, 8), - 1, - U32V_3(dest_addr), - U32V_1(source_size), - 'a', - 'b', - 'c'}; - - if (dest_addr <= (mem_size - source_size)) { - EXPECT_VERIFIES(data); - } else { - EXPECT_FAILURE(data); - } - } - } + EXPECT_FAILURE(data); } TEST_F(WasmModuleVerifyTest, OneIndirectFunction) { static const byte data[] = { - // sig#0 ------------------------------------------------------- + // sig#0 --------------------------------------------------------------- SIGNATURES_SECTION_VOID_VOID, - // funcs ------------------------------------------------------ + // funcs --------------------------------------------------------------- ONE_EMPTY_FUNCTION, - // indirect table ---------------------------------------------- - SECTION(FUNCTION_TABLE, 2), 1, U32V_1(0)}; + // table declaration --------------------------------------------------- + SECTION(Table, 4), ENTRY_COUNT(1), kWasmAnyFunctionTypeForm, 0, 1}; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); + EXPECT_OK(result); + if (result.ok()) { + EXPECT_EQ(1, result.val->signatures.size()); + EXPECT_EQ(1, result.val->functions.size()); + EXPECT_EQ(1, result.val->function_tables.size()); + EXPECT_EQ(1, result.val->function_tables[0].values.size()); + EXPECT_EQ(-1, result.val->function_tables[0].values[0]); + } + if (result.val) delete result.val; +} + +TEST_F(WasmModuleVerifyTest, OneIndirectFunction_one_entry) { + static const byte data[] = { + // sig#0 --------------------------------------------------------------- + SIGNATURES_SECTION_VOID_VOID, + // funcs --------------------------------------------------------------- + ONE_EMPTY_FUNCTION, + // table declaration --------------------------------------------------- + SECTION(Table, 4), ENTRY_COUNT(1), kWasmAnyFunctionTypeForm, 0, 1, + // elements ------------------------------------------------------------ + SECTION(Element, 7), + 1, // entry count + TABLE_INDEX(0), WASM_INIT_EXPR_I32V_1(0), + 1, // elements count + FUNC_INDEX(0)}; + + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); if (result.ok()) { EXPECT_EQ(1, result.val->signatures.size()); @@ -544,25 +547,30 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) { TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) { static const byte data[] = { // sig#0 ------------------------------------------------------- - SECTION(SIGNATURES, 1 + SIZEOF_SIG_ENTRY_v_v + SIZEOF_SIG_ENTRY_v_x), + SECTION(Type, 1 + SIZEOF_SIG_ENTRY_v_v + SIZEOF_SIG_ENTRY_v_x), 2, // -- SIG_ENTRY_v_v, // void -> void SIG_ENTRY_v_x(kLocalI32), // void -> i32 // funcs ------------------------------------------------------ FOUR_EMPTY_FUNCTIONS, - // indirect table ---------------------------------------------- - SECTION(FUNCTION_TABLE, 9), 8, - U32V_1(0), // -- - U32V_1(1), // -- - U32V_1(2), // -- - U32V_1(3), // -- - U32V_1(0), // -- - U32V_1(1), // -- - U32V_1(2), // -- - U32V_1(3), // -- + // table declaration ------------------------------------------- + SECTION(Table, 4), ENTRY_COUNT(1), kWasmAnyFunctionTypeForm, 0, 8, + // table elements ---------------------------------------------- + SECTION(Element, 14), + 1, // entry count + TABLE_INDEX(0), WASM_INIT_EXPR_I32V_1(0), + 8, // elements count + FUNC_INDEX(0), // -- + FUNC_INDEX(1), // -- + FUNC_INDEX(2), // -- + FUNC_INDEX(3), // -- + FUNC_INDEX(0), // -- + FUNC_INDEX(1), // -- + FUNC_INDEX(2), // -- + FUNC_INDEX(3), // -- FOUR_EMPTY_BODIES}; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); if (result.ok()) { EXPECT_EQ(2, result.val->signatures.size()); @@ -581,7 +589,7 @@ TEST_F(WasmModuleVerifyTest, IndirectFunctionNoFunctions) { // sig#0 ------------------------------------------------------- SIGNATURES_SECTION_VOID_VOID, // indirect table ---------------------------------------------- - SECTION(FUNCTION_TABLE, 3), 1, 0, 0, + SECTION(Table, 4), ENTRY_COUNT(1), 1, 0, 0, }; EXPECT_FAILURE(data); @@ -594,7 +602,7 @@ TEST_F(WasmModuleVerifyTest, IndirectFunctionInvalidIndex) { // functions --------------------------------------------------- ONE_EMPTY_FUNCTION, // indirect table ---------------------------------------------- - SECTION(FUNCTION_TABLE, 3), 1, 1, 0, + SECTION(Table, 4), ENTRY_COUNT(1), 1, 1, 0, }; EXPECT_FAILURE(data); @@ -607,7 +615,7 @@ TEST_F(WasmSignatureDecodeTest, Ok_v_v) { v8::internal::AccountingAllocator allocator; Zone zone(&allocator); FunctionSig* sig = - DecodeWasmSignatureForTesting(&zone, data, data + arraysize(data)); + DecodeWasmSignatureForTesting(&zone, data, data + sizeof(data)); EXPECT_TRUE(sig != nullptr); EXPECT_EQ(0, sig->parameter_count()); @@ -619,7 +627,7 @@ TEST_F(WasmSignatureDecodeTest, Ok_t_v) { LocalTypePair ret_type = kLocalTypes[i]; const byte data[] = {SIG_ENTRY_x(ret_type.code)}; FunctionSig* sig = - DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data)); + DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data)); EXPECT_TRUE(sig != nullptr); EXPECT_EQ(0, sig->parameter_count()); @@ -633,7 +641,7 @@ TEST_F(WasmSignatureDecodeTest, Ok_v_t) { LocalTypePair param_type = kLocalTypes[i]; const byte data[] = {SIG_ENTRY_v_x(param_type.code)}; FunctionSig* sig = - DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data)); + DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data)); EXPECT_TRUE(sig != nullptr); EXPECT_EQ(1, sig->parameter_count()); @@ -649,7 +657,7 @@ TEST_F(WasmSignatureDecodeTest, Ok_t_t) { LocalTypePair param_type = kLocalTypes[j]; const byte data[] = {SIG_ENTRY_x_x(ret_type.code, param_type.code)}; FunctionSig* sig = - DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data)); + DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data)); EXPECT_TRUE(sig != nullptr); EXPECT_EQ(1, sig->parameter_count()); @@ -668,7 +676,7 @@ TEST_F(WasmSignatureDecodeTest, Ok_i_tt) { const byte data[] = { SIG_ENTRY_x_xx(kLocalI32, p0_type.code, p1_type.code)}; FunctionSig* sig = - DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data)); + DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data)); EXPECT_TRUE(sig != nullptr); EXPECT_EQ(2, sig->parameter_count()); @@ -699,7 +707,7 @@ TEST_F(WasmSignatureDecodeTest, Fail_invalid_type) { byte data[] = {SIG_ENTRY_x_xx(kLocalI32, kLocalI32, kLocalI32)}; data[i] = kInvalidType; FunctionSig* sig = - DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data)); + DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data)); EXPECT_EQ(nullptr, sig); } } @@ -707,21 +715,21 @@ TEST_F(WasmSignatureDecodeTest, Fail_invalid_type) { TEST_F(WasmSignatureDecodeTest, Fail_invalid_ret_type1) { static const byte data[] = {SIG_ENTRY_x_x(kLocalVoid, kLocalI32)}; FunctionSig* sig = - DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data)); + DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data)); EXPECT_EQ(nullptr, sig); } TEST_F(WasmSignatureDecodeTest, Fail_invalid_param_type1) { static const byte data[] = {SIG_ENTRY_x_x(kLocalI32, kLocalVoid)}; FunctionSig* sig = - DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data)); + DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data)); EXPECT_EQ(nullptr, sig); } TEST_F(WasmSignatureDecodeTest, Fail_invalid_param_type2) { static const byte data[] = {SIG_ENTRY_x_xx(kLocalI32, kLocalI32, kLocalVoid)}; FunctionSig* sig = - DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data)); + DecodeWasmSignatureForTesting(zone(), data, data + sizeof(data)); EXPECT_EQ(nullptr, sig); } @@ -742,8 +750,8 @@ TEST_F(WasmFunctionVerifyTest, Ok_v_v_empty) { kExprNop // body }; - FunctionResult result = DecodeWasmFunction(isolate(), zone(), nullptr, data, - data + arraysize(data)); + FunctionResult result = + DecodeWasmFunction(isolate(), zone(), nullptr, data, data + sizeof(data)); EXPECT_OK(result); if (result.val && result.ok()) { @@ -752,7 +760,7 @@ TEST_F(WasmFunctionVerifyTest, Ok_v_v_empty) { EXPECT_EQ(0, function->sig->return_count()); EXPECT_EQ(0, function->name_offset); EXPECT_EQ(SIZEOF_SIG_ENTRY_v_v, function->code_start_offset); - EXPECT_EQ(arraysize(data), function->code_end_offset); + EXPECT_EQ(sizeof(data), function->code_end_offset); // TODO(titzer): verify encoding of local declarations } @@ -766,6 +774,7 @@ TEST_F(WasmModuleVerifyTest, SectionWithoutNameLength) { TEST_F(WasmModuleVerifyTest, TheLoneliestOfValidModulesTheTrulyEmptyOne) { const byte data[] = { + 0, // unknown section code. 0, // Empty section name. // No section name, no content, nothing but sadness. 0, // No section content. @@ -775,15 +784,14 @@ TEST_F(WasmModuleVerifyTest, TheLoneliestOfValidModulesTheTrulyEmptyOne) { TEST_F(WasmModuleVerifyTest, OnlyUnknownSectionEmpty) { const byte data[] = { - UNKNOWN_SECTION_NAME, 0, + UNKNOWN_SECTION(0), }; EXPECT_VERIFIES(data); } TEST_F(WasmModuleVerifyTest, OnlyUnknownSectionNonEmpty) { const byte data[] = { - UNKNOWN_SECTION_NAME, - 5, // section size + UNKNOWN_SECTION(5), 0xff, 0xff, 0xff, @@ -798,9 +806,7 @@ TEST_F(WasmModuleVerifyTest, SignatureFollowedByEmptyUnknownSection) { // signatures SIGNATURES_SECTION_VOID_VOID, // ----------------------------------------------------------- - UNKNOWN_SECTION_NAME, - 0 // empty section - }; + UNKNOWN_SECTION(0)}; EXPECT_VERIFIES(data); } @@ -809,29 +815,14 @@ TEST_F(WasmModuleVerifyTest, SignatureFollowedByUnknownSection) { // signatures SIGNATURES_SECTION_VOID_VOID, // ----------------------------------------------------------- - UNKNOWN_SECTION_NAME, - 5, // section size - 0xff, 0xff, 0xff, 0xff, 0xff, - }; - EXPECT_VERIFIES(data); -} - -TEST_F(WasmModuleVerifyTest, SignatureFollowedByUnknownSectionWithLongLEB) { - const byte data[] = { - // signatures - SIGNATURES_SECTION_VOID_VOID, - // ----------------------------------------------------------- - UNKNOWN_SECTION_NAME, 0x81, 0x80, 0x80, 0x80, - 0x00, // section size: 1 but in a 5-byte LEB - 0, + UNKNOWN_SECTION(5), 0xff, 0xff, 0xff, 0xff, 0xff, }; EXPECT_VERIFIES(data); } TEST_F(WasmModuleVerifyTest, UnknownSectionOverflow) { static const byte data[] = { - UNKNOWN_EMPTY_SECTION_NAME, - 9, // section size + UNKNOWN_SECTION(9), 1, 2, 3, @@ -848,12 +839,7 @@ TEST_F(WasmModuleVerifyTest, UnknownSectionOverflow) { TEST_F(WasmModuleVerifyTest, UnknownSectionUnderflow) { static const byte data[] = { - UNKNOWN_EMPTY_SECTION_NAME, - 0xff, - 0xff, - 0xff, - 0xff, - 0x0f, // Section size LEB128 0xffffffff + UNKNOWN_SECTION(333), 1, 2, 3, @@ -862,35 +848,17 @@ TEST_F(WasmModuleVerifyTest, UnknownSectionUnderflow) { EXPECT_FAILURE(data); } -TEST_F(WasmModuleVerifyTest, UnknownSectionLoop) { - // Would infinite loop decoding if wrapping and allowed. - static const byte data[] = { - UNKNOWN_EMPTY_SECTION_NAME, - 1, - 2, - 3, - 4, // 4 byte section - 0xfa, - 0xff, - 0xff, - 0xff, - 0x0f, // Section size LEB128 0xfffffffa - }; - EXPECT_FAILURE(data); -} - TEST_F(WasmModuleVerifyTest, UnknownSectionSkipped) { static const byte data[] = { - UNKNOWN_EMPTY_SECTION_NAME, - 1, // section size + UNKNOWN_SECTION(1), 0, // one byte section - SECTION(GLOBALS, 4), + SECTION(Global, 6), 1, - 0, // name length - kLocalI32, // memory type - 0, // exported + kLocalI32, // memory type + 0, // exported + WASM_INIT_EXPR_I32V_1(33), // init }; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(1, result.val->globals.size()); @@ -899,42 +867,41 @@ TEST_F(WasmModuleVerifyTest, UnknownSectionSkipped) { const WasmGlobal* global = &result.val->globals.back(); - EXPECT_EQ(0, global->name_length); EXPECT_EQ(kAstI32, global->type); EXPECT_EQ(0, global->offset); - EXPECT_FALSE(global->exported); if (result.val) delete result.val; } TEST_F(WasmModuleVerifyTest, ImportTable_empty) { - static const byte data[] = {SECTION(SIGNATURES, 1), 0, - SECTION(IMPORT_TABLE, 1), 0}; + static const byte data[] = {SECTION(Type, 1), 0, SECTION(Import, 1), 0}; EXPECT_VERIFIES(data); } TEST_F(WasmModuleVerifyTest, ImportTable_nosigs1) { - static const byte data[] = {SECTION(IMPORT_TABLE, 1), 0}; + static const byte data[] = {SECTION(Import, 1), 0}; EXPECT_VERIFIES(data); } TEST_F(WasmModuleVerifyTest, ImportTable_nosigs2) { static const byte data[] = { - SECTION(IMPORT_TABLE, 6), 1, // sig table - IMPORT_SIG_INDEX(0), // sig index - NAME_LENGTH(1), 'm', // module name - NAME_LENGTH(1), 'f', // function name + SECTION(Import, 6), 1, // sig table + NAME_LENGTH(1), 'm', // module name + NAME_LENGTH(1), 'f', // function name + kExternalFunction, // import kind + IMPORT_SIG_INDEX(0), // sig index }; EXPECT_FAILURE(data); } TEST_F(WasmModuleVerifyTest, ImportTable_invalid_sig) { static const byte data[] = { - SECTION(SIGNATURES, 1), 0, // -- - SECTION(IMPORT_TABLE, 6), 1, // -- - IMPORT_SIG_INDEX(0), // sig index - NAME_LENGTH(1), 'm', // module name - NAME_LENGTH(1), 'f', // function name + SECTION(Type, 1), 0, // -- + SECTION(Import, 6), 1, // -- + NAME_LENGTH(1), 'm', // module name + NAME_LENGTH(1), 'f', // function name + kExternalFunction, // import kind + IMPORT_SIG_INDEX(0), // sig index }; EXPECT_FAILURE(data); } @@ -943,13 +910,14 @@ TEST_F(WasmModuleVerifyTest, ImportTable_one_sig) { static const byte data[] = { // signatures SIGNATURES_SECTION_VOID_VOID, - SECTION(IMPORT_TABLE, 6), - 1, // -- - IMPORT_SIG_INDEX(0), // sig index + SECTION(Import, 7), + 1, // -- NAME_LENGTH(1), 'm', // module name NAME_LENGTH(1), - 'f', // function name + 'f', // function name + kExternalFunction, // import kind + IMPORT_SIG_INDEX(0), // sig index }; EXPECT_VERIFIES(data); } @@ -957,13 +925,14 @@ TEST_F(WasmModuleVerifyTest, ImportTable_one_sig) { TEST_F(WasmModuleVerifyTest, ImportTable_invalid_module) { static const byte data[] = { // signatures - SIGNATURES_SECTION_VOID_VOID, - SECTION(IMPORT_TABLE, 6), - 1, // -- - IMPORT_SIG_INDEX(0), // sig index - NO_NAME, // module name - NAME_LENGTH(1), - 'f' // function name + SIGNATURES_SECTION_VOID_VOID, // -- + SECTION(Import, 7), // -- + 1, // -- + NO_NAME, // module name + NAME_LENGTH(1), // -- + 'f', // function name + kExternalFunction, // import kind + IMPORT_SIG_INDEX(0), // sig index }; EXPECT_FAILURE(data); } @@ -972,26 +941,27 @@ TEST_F(WasmModuleVerifyTest, ImportTable_off_end) { static const byte data[] = { // signatures SIGNATURES_SECTION_VOID_VOID, - SECTION(IMPORT_TABLE, 6), + SECTION(Import, 6), 1, - IMPORT_SIG_INDEX(0), // sig index NAME_LENGTH(1), 'm', // module name NAME_LENGTH(1), - 'f', // function name + 'f', // function name + kExternalFunction, // import kind + IMPORT_SIG_INDEX(0), // sig index }; EXPECT_OFF_END_FAILURE(data, 16, sizeof(data)); } TEST_F(WasmModuleVerifyTest, ExportTable_empty1) { - static const byte data[] = {// signatures - SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION, - SECTION(EXPORT_TABLE, 1), - 0, // -- + static const byte data[] = { // signatures + SIGNATURES_SECTION_VOID_VOID, // -- + ONE_EMPTY_FUNCTION, SECTION(Export, 1), // -- + 0, // -- ONE_EMPTY_BODY}; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(1, result.val->functions.size()); @@ -1002,34 +972,27 @@ TEST_F(WasmModuleVerifyTest, ExportTable_empty1) { TEST_F(WasmModuleVerifyTest, ExportTable_empty2) { static const byte data[] = { - SECTION(SIGNATURES, 1), 0, SECTION(EXPORT_TABLE, 1), 0 // -- + SECTION(Type, 1), 0, SECTION(Export, 1), 0 // -- }; - // TODO(titzer): current behavior treats empty functions section as missing. - EXPECT_FAILURE(data); -} - -TEST_F(WasmModuleVerifyTest, ExportTable_NoFunctions1) { - static const byte data[] = { - SECTION(SIGNATURES, 1), 0, SECTION(EXPORT_TABLE, 1), 0 // -- - }; - EXPECT_FAILURE(data); + EXPECT_VERIFIES(data); } TEST_F(WasmModuleVerifyTest, ExportTable_NoFunctions2) { - static const byte data[] = {SECTION(EXPORT_TABLE, 1), 0}; - EXPECT_FAILURE(data); + static const byte data[] = {SECTION(Export, 1), 0}; + EXPECT_VERIFIES(data); } TEST_F(WasmModuleVerifyTest, ExportTableOne) { static const byte data[] = {// signatures SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION, - SECTION(EXPORT_TABLE, 3), - 1, // exports - FUNC_INDEX(0), // -- - NO_NAME, // -- + SECTION(Export, 4), + 1, // exports + NO_NAME, // -- + kExternalFunction, // -- + FUNC_INDEX(0), // -- ONE_EMPTY_BODY}; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(1, result.val->functions.size()); @@ -1042,12 +1005,12 @@ TEST_F(WasmModuleVerifyTest, ExportNameWithInvalidStringLength) { static const byte data[] = {// signatures SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION, - SECTION(EXPORT_TABLE, 12), - 1, // exports - FUNC_INDEX(0), // -- - NAME_LENGTH(84), // invalid string length - 'e', // -- - ONE_EMPTY_BODY}; + SECTION(Export, 12), + 1, // exports + NAME_LENGTH(84), // invalid string length + 'e', // -- + kExternalFunction, // -- + FUNC_INDEX(0)}; EXPECT_FAILURE(data); } @@ -1056,22 +1019,24 @@ TEST_F(WasmModuleVerifyTest, ExportTableTwo) { static const byte data[] = {// signatures SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION, - SECTION(EXPORT_TABLE, 12), - 2, // exports - FUNC_INDEX(0), // -- + SECTION(Export, 14), + 2, // exports NAME_LENGTH(4), 'n', 'a', 'm', - 'e', // -- + 'e', // -- + kExternalFunction, FUNC_INDEX(0), // -- NAME_LENGTH(3), 'n', 'o', - 'm', // -- + 'm', // -- + kExternalFunction, // -- + FUNC_INDEX(0), // -- ONE_EMPTY_BODY}; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(1, result.val->functions.size()); @@ -1084,19 +1049,22 @@ TEST_F(WasmModuleVerifyTest, ExportTableThree) { static const byte data[] = {// signatures SIGNATURES_SECTION_VOID_VOID, THREE_EMPTY_FUNCTIONS, - SECTION(EXPORT_TABLE, 10), - 3, // exports + SECTION(Export, 13), + 3, // exports + NAME_LENGTH(1), + 'a', // -- + kExternalFunction, FUNC_INDEX(0), // -- NAME_LENGTH(1), - 'a', // -- + 'b', // -- + kExternalFunction, FUNC_INDEX(1), // -- NAME_LENGTH(1), - 'b', // -- - FUNC_INDEX(2), // -- - NAME_LENGTH(1), 'c', // -- + kExternalFunction, + FUNC_INDEX(2), // -- THREE_EMPTY_BODIES}; - ModuleResult result = DecodeModule(data, data + arraysize(data)); + ModuleResult result = DecodeModule(data, data + sizeof(data)); EXPECT_OK(result); EXPECT_EQ(3, result.val->functions.size()); @@ -1110,12 +1078,13 @@ TEST_F(WasmModuleVerifyTest, ExportTableThreeOne) { const byte data[] = {// signatures SIGNATURES_SECTION_VOID_VOID, THREE_EMPTY_FUNCTIONS, - SECTION(EXPORT_TABLE, 5), - 1, // exports - FUNC_INDEX(i), // -- + SECTION(Export, 6), + 1, // exports NAME_LENGTH(2), 'e', 'x', // -- + kExternalFunction, + FUNC_INDEX(i), // -- THREE_EMPTY_BODIES}; if (i < 3) { @@ -1131,10 +1100,11 @@ TEST_F(WasmModuleVerifyTest, ExportTableOne_off_end) { // signatures SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION, - SECTION(EXPORT_TABLE, 1 + 6), - 1, // exports + SECTION(Export, 1 + 6), + 1, // exports + NO_NAME, // -- + kExternalFunction, FUNC_INDEX(0), // -- - NO_NAME // -- }; for (int length = 33; length < sizeof(data); length++) { @@ -1146,9 +1116,9 @@ TEST_F(WasmModuleVerifyTest, ExportTableOne_off_end) { TEST_F(WasmModuleVerifyTest, FunctionSignatures_empty) { static const byte data[] = { - SECTION(SIGNATURES, 1), 0, // -- - SECTION(FUNCTION_SIGNATURES, 1), 0 // -- - }; // -- + SECTION(Type, 1), 0, // -- + SECTION(Function, 1), 0 // -- + }; // -- EXPECT_VERIFIES(data); } @@ -1162,10 +1132,10 @@ TEST_F(WasmModuleVerifyTest, FunctionSignatures_one) { TEST_F(WasmModuleVerifyTest, Regression_648070) { static const byte data[] = { - SECTION(SIGNATURES, 1), 0, // -- - SECTION(FUNCTION_SIGNATURES, 5), // -- - U32V_5(3500228624) // function count = 3500228624 - }; // -- + SECTION(Type, 1), 0, // -- + SECTION(Function, 5), // -- + U32V_5(3500228624) // function count = 3500228624 + }; // -- EXPECT_FAILURE(data); } @@ -1180,40 +1150,40 @@ TEST_F(WasmModuleVerifyTest, FunctionBodies_empty) { TEST_F(WasmModuleVerifyTest, FunctionBodies_one_empty) { static const byte data[] = { - SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- - FUNCTION_SIGNATURES_SECTION(1, 0), // -- - SECTION(FUNCTION_BODIES, 1 + SIZEOF_EMPTY_BODY), 1, EMPTY_BODY // -- + SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- + FUNCTION_SIGNATURES_SECTION(1, 0), // -- + SECTION(Code, 1 + SIZEOF_EMPTY_BODY), 1, EMPTY_BODY // -- }; EXPECT_VERIFIES(data); } TEST_F(WasmModuleVerifyTest, FunctionBodies_one_nop) { static const byte data[] = { - SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- - FUNCTION_SIGNATURES_SECTION(1, 0), // -- - SECTION(FUNCTION_BODIES, 1 + SIZEOF_NOP_BODY), 1, NOP_BODY // -- + SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- + FUNCTION_SIGNATURES_SECTION(1, 0), // -- + SECTION(Code, 1 + SIZEOF_NOP_BODY), 1, NOP_BODY // -- }; EXPECT_VERIFIES(data); } TEST_F(WasmModuleVerifyTest, FunctionBodies_count_mismatch1) { static const byte data[] = { - SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- - FUNCTION_SIGNATURES_SECTION(2, 0, 0), // -- - SECTION(FUNCTION_BODIES, 1 + SIZEOF_EMPTY_BODY), 1, // -- - EMPTY_BODY // -- + SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- + FUNCTION_SIGNATURES_SECTION(2, 0, 0), // -- + SECTION(Code, 1 + SIZEOF_EMPTY_BODY), 1, // -- + EMPTY_BODY // -- }; EXPECT_FAILURE(data); } TEST_F(WasmModuleVerifyTest, FunctionBodies_count_mismatch2) { static const byte data[] = { - SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- - FUNCTION_SIGNATURES_SECTION(1, 0), // -- - SECTION(FUNCTION_BODIES, 1 + 2 * SIZEOF_NOP_BODY), // -- - 2, // -- - NOP_BODY, // -- - NOP_BODY // -- + SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- + FUNCTION_SIGNATURES_SECTION(1, 0), // -- + SECTION(Code, 1 + 2 * SIZEOF_NOP_BODY), // -- + ENTRY_COUNT(2), // -- + NOP_BODY, // -- + NOP_BODY // -- }; EXPECT_FAILURE(data); } @@ -1229,11 +1199,11 @@ TEST_F(WasmModuleVerifyTest, Names_one_empty) { static const byte data[] = { SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- FUNCTION_SIGNATURES_SECTION(1, 0), // -- - SECTION(FUNCTION_BODIES, 1 + SIZEOF_EMPTY_BODY), - 1, + SECTION(Code, 1 + SIZEOF_EMPTY_BODY), + ENTRY_COUNT(1), EMPTY_BODY, // -- - SECTION(NAMES, 1 + 5), - 1, + SECTION_NAMES(1 + 5), + ENTRY_COUNT(1), FOO_STRING, NO_LOCAL_NAMES // -- }; @@ -1242,14 +1212,14 @@ TEST_F(WasmModuleVerifyTest, Names_one_empty) { TEST_F(WasmModuleVerifyTest, Names_two_empty) { static const byte data[] = { - SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- - FUNCTION_SIGNATURES_SECTION(2, 0, 0), // -- - SECTION(FUNCTION_BODIES, 1 + 2 * SIZEOF_EMPTY_BODY), // -- - 2, + SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // -- + FUNCTION_SIGNATURES_SECTION(2, 0, 0), // -- + SECTION(Code, 1 + 2 * SIZEOF_EMPTY_BODY), // -- + ENTRY_COUNT(2), EMPTY_BODY, EMPTY_BODY, // -- - SECTION(NAMES, 1 + 10), - 2, // -- + SECTION_NAMES(1 + 10), + ENTRY_COUNT(2), // -- FOO_STRING, NO_LOCAL_NAMES, // -- FOO_STRING, @@ -1258,6 +1228,66 @@ TEST_F(WasmModuleVerifyTest, Names_two_empty) { EXPECT_VERIFIES(data); } +#define EXPECT_INIT_EXPR(Type, type, value, ...) \ + { \ + static const byte data[] = {__VA_ARGS__, kExprEnd}; \ + WasmInitExpr expr = \ + DecodeWasmInitExprForTesting(data, data + sizeof(data)); \ + EXPECT_EQ(WasmInitExpr::k##Type##Const, expr.kind); \ + EXPECT_EQ(value, expr.val.type##_const); \ + } + +TEST_F(WasmModuleVerifyTest, InitExpr_i32) { + EXPECT_INIT_EXPR(I32, i32, 33, WASM_I32V_1(33)); + EXPECT_INIT_EXPR(I32, i32, -21, WASM_I32V_1(-21)); + EXPECT_INIT_EXPR(I32, i32, 437, WASM_I32V_2(437)); + EXPECT_INIT_EXPR(I32, i32, 77777, WASM_I32V_3(77777)); +} + +TEST_F(WasmModuleVerifyTest, InitExpr_f32) { + EXPECT_INIT_EXPR(F32, f32, static_cast(13.1), WASM_F32(13.1)); + EXPECT_INIT_EXPR(F32, f32, static_cast(-21.1), WASM_F32(-21.1)); + EXPECT_INIT_EXPR(F32, f32, static_cast(437.2), WASM_F32(437.2)); + EXPECT_INIT_EXPR(F32, f32, static_cast(77777.3), WASM_F32(77777.3)); +} + +TEST_F(WasmModuleVerifyTest, InitExpr_i64) { + EXPECT_INIT_EXPR(I64, i64, 33, WASM_I64V_1(33)); + EXPECT_INIT_EXPR(I64, i64, -21, WASM_I64V_2(-21)); + EXPECT_INIT_EXPR(I64, i64, 437, WASM_I64V_5(437)); + EXPECT_INIT_EXPR(I64, i64, 77777, WASM_I64V_7(77777)); +} + +TEST_F(WasmModuleVerifyTest, InitExpr_f64) { + EXPECT_INIT_EXPR(F64, f64, 83.22, WASM_F64(83.22)); + EXPECT_INIT_EXPR(F64, f64, -771.3, WASM_F64(-771.3)); + EXPECT_INIT_EXPR(F64, f64, 43703.0, WASM_F64(43703.0)); + EXPECT_INIT_EXPR(F64, f64, 77999.1, WASM_F64(77999.1)); +} + +#define EXPECT_INIT_EXPR_FAIL(...) \ + { \ + static const byte data[] = {__VA_ARGS__, kExprEnd}; \ + WasmInitExpr expr = \ + DecodeWasmInitExprForTesting(data, data + sizeof(data)); \ + EXPECT_EQ(WasmInitExpr::kNone, expr.kind); \ + } + +TEST_F(WasmModuleVerifyTest, InitExpr_illegal) { + EXPECT_INIT_EXPR_FAIL(WASM_I32V_1(0), WASM_I32V_1(0)); + EXPECT_INIT_EXPR_FAIL(WASM_GET_LOCAL(0)); + EXPECT_INIT_EXPR_FAIL(WASM_SET_LOCAL(0, WASM_I32V_1(0))); + EXPECT_INIT_EXPR_FAIL(WASM_I32_ADD(WASM_I32V_1(0), WASM_I32V_1(0))); + EXPECT_INIT_EXPR_FAIL(WASM_IF_ELSE(WASM_ZERO, WASM_ZERO, WASM_ZERO)); +} + +TEST_F(WasmModuleVerifyTest, InitExpr_global) { + static const byte data[] = {WASM_INIT_EXPR_GLOBAL(37)}; + WasmInitExpr expr = DecodeWasmInitExprForTesting(data, data + sizeof(data)); + EXPECT_EQ(WasmInitExpr::kGlobalIndex, expr.kind); + EXPECT_EQ(37, expr.val.global_index); +} + } // namespace wasm } // namespace internal } // namespace v8 diff --git a/test/unittests/wasm/wasm-macro-gen-unittest.cc b/test/unittests/wasm/wasm-macro-gen-unittest.cc index 2b782f5dc7..0399835d01 100644 --- a/test/unittests/wasm/wasm-macro-gen-unittest.cc +++ b/test/unittests/wasm/wasm-macro-gen-unittest.cc @@ -56,45 +56,44 @@ TEST_F(WasmMacroGenTest, Statements) { EXPECT_SIZE(7, WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, WASM_ZERO)); - EXPECT_SIZE(5, WASM_IF(WASM_ZERO, WASM_NOP)); + EXPECT_SIZE(6, WASM_IF(WASM_ZERO, WASM_NOP)); - EXPECT_SIZE(7, WASM_IF_ELSE(WASM_ZERO, WASM_NOP, WASM_NOP)); + EXPECT_SIZE(8, WASM_IF_ELSE(WASM_ZERO, WASM_NOP, WASM_NOP)); EXPECT_SIZE(5, WASM_SELECT(WASM_ZERO, WASM_NOP, WASM_NOP)); - EXPECT_SIZE(3, WASM_BR(0)); - EXPECT_SIZE(5, WASM_BR_IF(0, WASM_ZERO)); + EXPECT_SIZE(2, WASM_BR(0)); + EXPECT_SIZE(4, WASM_BR_IF(0, WASM_ZERO)); - EXPECT_SIZE(3, WASM_BLOCK(WASM_NOP)); - EXPECT_SIZE(4, WASM_BLOCK(WASM_NOP, WASM_NOP)); - EXPECT_SIZE(5, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_NOP)); + EXPECT_SIZE(4, WASM_BLOCK(WASM_NOP)); + EXPECT_SIZE(5, WASM_BLOCK(WASM_NOP, WASM_NOP)); + EXPECT_SIZE(6, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_NOP)); EXPECT_SIZE(5, WASM_INFINITE_LOOP); - EXPECT_SIZE(3, WASM_LOOP(WASM_NOP)); - EXPECT_SIZE(4, WASM_LOOP(WASM_NOP, WASM_NOP)); - EXPECT_SIZE(5, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_NOP)); + EXPECT_SIZE(4, WASM_LOOP(WASM_NOP)); + EXPECT_SIZE(5, WASM_LOOP(WASM_NOP, WASM_NOP)); + EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_NOP)); EXPECT_SIZE(5, WASM_LOOP(WASM_BR(0))); EXPECT_SIZE(7, WASM_LOOP(WASM_BR_IF(0, WASM_ZERO))); - EXPECT_SIZE(2, WASM_RETURN0); - EXPECT_SIZE(4, WASM_RETURN1(WASM_ZERO)); + EXPECT_SIZE(1, WASM_RETURN0); + EXPECT_SIZE(3, WASM_RETURN1(WASM_ZERO)); EXPECT_SIZE(1, WASM_UNREACHABLE); } TEST_F(WasmMacroGenTest, MacroStatements) { - EXPECT_SIZE(10, WASM_WHILE(WASM_I8(0), WASM_NOP)); + EXPECT_SIZE(11, WASM_WHILE(WASM_I8(0), WASM_NOP)); EXPECT_SIZE(7, WASM_INC_LOCAL(0)); EXPECT_SIZE(7, WASM_INC_LOCAL_BY(0, 3)); - EXPECT_SIZE(3, WASM_BREAK(0)); - EXPECT_SIZE(3, WASM_CONTINUE(0)); + EXPECT_SIZE(2, WASM_CONTINUE(0)); } TEST_F(WasmMacroGenTest, BrTable) { - EXPECT_SIZE(9, WASM_BR_TABLE(WASM_ZERO, 1, BR_TARGET(1))); - EXPECT_SIZE(11, WASM_BR_TABLEV(WASM_ZERO, WASM_ZERO, 1, BR_TARGET(1))); + EXPECT_SIZE(5, WASM_BR_TABLE(WASM_ZERO, 1, BR_TARGET(0))); + EXPECT_SIZE(6, WASM_BR_TABLE(WASM_ZERO, 2, BR_TARGET(0), BR_TARGET(0))); } TEST_F(WasmMacroGenTest, Expressions) { @@ -110,43 +109,34 @@ TEST_F(WasmMacroGenTest, Expressions) { EXPECT_SIZE(3, WASM_NOT(WASM_ZERO)); - EXPECT_SIZE(5, WASM_BRV(1, WASM_ZERO)); - EXPECT_SIZE(7, WASM_BRV_IF(1, WASM_ZERO, WASM_ZERO)); + EXPECT_SIZE(4, WASM_BRV(1, WASM_ZERO)); + EXPECT_SIZE(6, WASM_BRV_IF(1, WASM_ZERO, WASM_ZERO)); - EXPECT_SIZE(4, WASM_BLOCK(WASM_ZERO)); - EXPECT_SIZE(5, WASM_BLOCK(WASM_NOP, WASM_ZERO)); - EXPECT_SIZE(6, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_ZERO)); + EXPECT_SIZE(5, WASM_BLOCK(WASM_ZERO)); + EXPECT_SIZE(6, WASM_BLOCK(WASM_NOP, WASM_ZERO)); + EXPECT_SIZE(7, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_ZERO)); - EXPECT_SIZE(4, WASM_LOOP(WASM_ZERO)); - EXPECT_SIZE(5, WASM_LOOP(WASM_NOP, WASM_ZERO)); - EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_ZERO)); + EXPECT_SIZE(5, WASM_LOOP(WASM_ZERO)); + EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_ZERO)); + EXPECT_SIZE(7, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_ZERO)); } TEST_F(WasmMacroGenTest, CallFunction) { - EXPECT_SIZE(3, WASM_CALL_FUNCTION0(0)); - EXPECT_SIZE(3, WASM_CALL_FUNCTION0(1)); - EXPECT_SIZE(3, WASM_CALL_FUNCTION0(11)); + EXPECT_SIZE(2, WASM_CALL_FUNCTION0(0)); + EXPECT_SIZE(2, WASM_CALL_FUNCTION0(1)); + EXPECT_SIZE(2, WASM_CALL_FUNCTION0(11)); - EXPECT_SIZE(5, WASM_CALL_FUNCTION1(0, WASM_ZERO)); - EXPECT_SIZE(7, WASM_CALL_FUNCTION2(1, WASM_ZERO, WASM_ZERO)); -} - -TEST_F(WasmMacroGenTest, CallImport) { - EXPECT_SIZE(3, WASM_CALL_IMPORT0(0)); - EXPECT_SIZE(3, WASM_CALL_IMPORT0(1)); - EXPECT_SIZE(3, WASM_CALL_IMPORT0(11)); - - EXPECT_SIZE(5, WASM_CALL_IMPORT1(0, WASM_ZERO)); - EXPECT_SIZE(7, WASM_CALL_IMPORT2(1, WASM_ZERO, WASM_ZERO)); + EXPECT_SIZE(4, WASM_CALL_FUNCTION(0, WASM_ZERO)); + EXPECT_SIZE(6, WASM_CALL_FUNCTION(1, WASM_ZERO, WASM_ZERO)); } TEST_F(WasmMacroGenTest, CallIndirect) { - EXPECT_SIZE(5, WASM_CALL_INDIRECT0(0, WASM_ZERO)); - EXPECT_SIZE(5, WASM_CALL_INDIRECT0(1, WASM_ZERO)); - EXPECT_SIZE(5, WASM_CALL_INDIRECT0(11, WASM_ZERO)); + EXPECT_SIZE(4, WASM_CALL_INDIRECT0(0, WASM_ZERO)); + EXPECT_SIZE(4, WASM_CALL_INDIRECT0(1, WASM_ZERO)); + EXPECT_SIZE(4, WASM_CALL_INDIRECT0(11, WASM_ZERO)); - EXPECT_SIZE(7, WASM_CALL_INDIRECT1(0, WASM_ZERO, WASM_ZERO)); - EXPECT_SIZE(9, WASM_CALL_INDIRECT2(1, WASM_ZERO, WASM_ZERO, WASM_ZERO)); + EXPECT_SIZE(6, WASM_CALL_INDIRECT1(0, WASM_ZERO, WASM_ZERO)); + EXPECT_SIZE(8, WASM_CALL_INDIRECT2(1, WASM_ZERO, WASM_ZERO, WASM_ZERO)); } TEST_F(WasmMacroGenTest, Int32Ops) {