Revert of [wasm] Master CL for Binary 0xC changes. (patchset #26 id:490001 of https://codereview.chromium.org/2345593003/ )

Reason for revert:
Main suspect for tsan:
https://build.chromium.org/p/client.v8/builders/V8%20Linux64%20TSAN/builds/11893

Also changes layout tests:
https://build.chromium.org/p/client.v8.fyi/builders/V8-Blink%20Linux%2064/builds/10036

+mips builder:
https://build.chromium.org/p/client.v8.ports/builders/V8%20Mips%20-%20builder/builds/4032

Original issue's description:
> [wasm] Master CL for Binary 0xC changes.
>
> [0xC] Convert to stack machine semantics.
> [0xC] Use section codes instead of names.
> [0xC] Add elements section decoding.
> [0xC] Decoding of globals section.
> [0xC] Decoding of memory section.
> [0xC] Decoding of imports section.
> [0xC] Decoding of exports section.
> [0xC] Decoding of data section.
> [0xC] Remove CallImport bytecode.
> [0xC] Function bodies have an implicit block.
> [0xC] Remove the bottom label from loops.
> [0xC] Add signatures to blocks.
> [0xC] Remove arities from branches.
> Add tests for init expression decoding.
> Rework compilation of import wrappers and how they are patched.
> Rework function indices in debugging.
> Fix ASM->WASM builder for stack machine.
> Reorganize asm.js foreign functions due to import indices change.
>
> R=ahaas@chromium.org,rossberg@chromium.org,bradnelson@chromium.org
> BUG=chromium:575167
> LOG=Y
>
> Committed: https://crrev.com/76eb976a67273b8c03c744f64ad850b0432554b9
> Cr-Commit-Position: refs/heads/master@{#39678}

TBR=ahaas@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org,rossberg@chromium.org,bradnelson@google.com,titzer@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=chromium:575167

Review-Url: https://codereview.chromium.org/2361053004
Cr-Commit-Position: refs/heads/master@{#39685}
This commit is contained in:
machenbach 2016-09-23 10:58:07 -07:00 committed by Commit bot
parent a767d8f956
commit e1eee748dd
83 changed files with 4406 additions and 5618 deletions

View File

@ -207,8 +207,7 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation"); ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
i::MaybeHandle<i::JSObject> maybe_module_object = i::MaybeHandle<i::JSObject> maybe_module_object =
i::wasm::WasmModule::Instantiate(isolate, &thrower, module, foreign, i::wasm::WasmModule::Instantiate(isolate, module, foreign, memory);
memory);
if (maybe_module_object.is_null()) { if (maybe_module_object.is_null()) {
return MaybeHandle<Object>(); return MaybeHandle<Object>();
} }

View File

@ -128,7 +128,6 @@ AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
script_(script), script_(script),
root_(root), root_(root),
forward_definitions_(zone), forward_definitions_(zone),
ffi_use_signatures_(zone),
stdlib_types_(zone), stdlib_types_(zone),
stdlib_math_types_(zone), stdlib_math_types_(zone),
module_info_(VariableInfo::ForSpecialSymbol(zone_, kModule)), module_info_(VariableInfo::ForSpecialSymbol(zone_, kModule)),
@ -330,8 +329,8 @@ AsmTyper::VariableInfo* AsmTyper::ImportLookup(Property* import) {
return i->second; return i->second;
} }
AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) const { AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) {
const ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_; ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
ZoneHashMap::Entry* entry = ZoneHashMap::Entry* entry =
scope->Lookup(variable, ComputePointerHash(variable)); scope->Lookup(variable, ComputePointerHash(variable));
if (entry == nullptr && in_function_) { if (entry == nullptr && in_function_) {
@ -424,8 +423,6 @@ AsmType* AsmTyper::TypeOf(AstNode* node) const {
return AsmType::None(); return AsmType::None();
} }
AsmType* AsmTyper::TypeOf(Variable* v) const { return Lookup(v)->type(); }
AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) { AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) {
auto* var_info = Lookup(var); auto* var_info = Lookup(var);
if (var_info == nullptr) { if (var_info == nullptr) {
@ -2309,20 +2306,9 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
FAIL(call, "Calling something that's not a function."); FAIL(call, "Calling something that's not a function.");
} }
if (callee_type->AsFFIType() != nullptr) { if (callee_type->AsFFIType() != nullptr &&
if (return_type == AsmType::Float()) { return_type == AsmType::Float()) {
FAIL(call, "Foreign functions can't return 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)) { if (!callee_type->CanBeInvokedWith(return_type, args)) {

View File

@ -73,26 +73,12 @@ class AsmTyper final {
const char* error_message() const { return error_message_; } const char* error_message() const { return error_message_; }
AsmType* TypeOf(AstNode* node) const; AsmType* TypeOf(AstNode* node) const;
AsmType* TypeOf(Variable* v) const;
StandardMember VariableAsStandardMember(Variable* var); StandardMember VariableAsStandardMember(Variable* var);
typedef std::unordered_set<StandardMember, std::hash<int> > StdlibSet; typedef std::unordered_set<StandardMember, std::hash<int> > StdlibSet;
StdlibSet StdlibUses() const { return stdlib_uses_; } StdlibSet StdlibUses() const { return stdlib_uses_; }
// Each FFI import has a usage-site signature associated with it.
struct FFIUseSignature {
Variable* var;
ZoneVector<AsmType*> arg_types_;
AsmType* return_type_;
FFIUseSignature(Variable* v, Zone* zone)
: var(v), arg_types_(zone), return_type_(nullptr) {}
};
const ZoneVector<FFIUseSignature>& FFIUseSignatures() {
return ffi_use_signatures_;
}
private: private:
friend class v8::internal::wasm::AsmTyperHarnessBuilder; friend class v8::internal::wasm::AsmTyperHarnessBuilder;
@ -206,7 +192,7 @@ class AsmTyper final {
// Lookup(Delta, Gamma, x) // Lookup(Delta, Gamma, x)
// //
// Delta is the global_scope_ member, and Gamma, local_scope_. // Delta is the global_scope_ member, and Gamma, local_scope_.
VariableInfo* Lookup(Variable* variable) const; VariableInfo* Lookup(Variable* variable);
// All of the ValidateXXX methods below return AsmType::None() in case of // All of the ValidateXXX methods below return AsmType::None() in case of
// validation failure. // validation failure.
@ -320,7 +306,6 @@ class AsmTyper final {
AsmType* return_type_ = nullptr; AsmType* return_type_ = nullptr;
ZoneVector<VariableInfo*> forward_definitions_; ZoneVector<VariableInfo*> forward_definitions_;
ZoneVector<FFIUseSignature> ffi_use_signatures_;
ObjectTypeMap stdlib_types_; ObjectTypeMap stdlib_types_;
ObjectTypeMap stdlib_math_types_; ObjectTypeMap stdlib_math_types_;

View File

@ -32,7 +32,6 @@ namespace wasm {
} while (false) } while (false)
enum AsmScope { kModuleScope, kInitScope, kFuncScope, kExportScope }; enum AsmScope { kModuleScope, kInitScope, kFuncScope, kExportScope };
enum ValueFate { kDrop, kLeaveOnStack };
struct ForeignVariable { struct ForeignVariable {
Handle<Name> name; Handle<Name> name;
@ -62,8 +61,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
typer_(typer), typer_(typer),
breakable_blocks_(zone), breakable_blocks_(zone),
foreign_variables_(zone), foreign_variables_(zone),
init_function_(nullptr), init_function_index_(0),
foreign_init_function_(nullptr), foreign_init_function_index_(0),
next_table_index_(0), next_table_index_(0),
function_tables_(base::HashMap::PointersMatch, function_tables_(base::HashMap::PointersMatch,
ZoneHashMap::kDefaultHashMapCapacity, ZoneHashMap::kDefaultHashMapCapacity,
@ -73,33 +72,35 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
} }
void InitializeInitFunction() { void InitializeInitFunction() {
init_function_index_ = builder_->AddFunction();
FunctionSig::Builder b(zone(), 0, 0); FunctionSig::Builder b(zone(), 0, 0);
init_function_ = builder_->AddFunction(b.Build()); current_function_builder_ = builder_->FunctionAt(init_function_index_);
builder_->MarkStartFunction(init_function_); current_function_builder_->SetSignature(b.Build());
builder_->MarkStartFunction(init_function_index_);
current_function_builder_ = nullptr;
} }
void BuildForeignInitFunction() { void BuildForeignInitFunction() {
foreign_init_function_ = builder_->AddFunction(); foreign_init_function_index_ = builder_->AddFunction();
FunctionSig::Builder b(zone(), 0, foreign_variables_.size()); FunctionSig::Builder b(zone(), 0, foreign_variables_.size());
for (auto i = foreign_variables_.begin(); i != foreign_variables_.end(); for (auto i = foreign_variables_.begin(); i != foreign_variables_.end();
++i) { ++i) {
b.AddParam(i->type); b.AddParam(i->type);
} }
foreign_init_function_->SetExported(); current_function_builder_ =
std::string raw_name = "__foreign_init__"; builder_->FunctionAt(foreign_init_function_index_);
foreign_init_function_->SetName( current_function_builder_->SetExported();
current_function_builder_->SetName(
AsmWasmBuilder::foreign_init_name, AsmWasmBuilder::foreign_init_name,
static_cast<int>(strlen(AsmWasmBuilder::foreign_init_name))); static_cast<int>(strlen(AsmWasmBuilder::foreign_init_name)));
current_function_builder_->SetSignature(b.Build());
foreign_init_function_->SetName(raw_name.data(),
static_cast<int>(raw_name.size()));
foreign_init_function_->SetSignature(b.Build());
for (size_t pos = 0; pos < foreign_variables_.size(); ++pos) { for (size_t pos = 0; pos < foreign_variables_.size(); ++pos) {
foreign_init_function_->EmitGetLocal(static_cast<uint32_t>(pos)); current_function_builder_->EmitGetLocal(static_cast<uint32_t>(pos));
ForeignVariable* fv = &foreign_variables_[pos]; ForeignVariable* fv = &foreign_variables_[pos];
uint32_t index = LookupOrInsertGlobal(fv->var, fv->type); uint32_t index = LookupOrInsertGlobal(fv->var, fv->type);
foreign_init_function_->EmitWithVarInt(kExprSetGlobal, index); current_function_builder_->EmitWithVarInt(kExprSetGlobal, index);
} }
current_function_builder_ = nullptr;
} }
i::Handle<i::FixedArray> GetForeignArgs() { i::Handle<i::FixedArray> GetForeignArgs() {
@ -112,20 +113,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
return ret; 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() { void Build() {
BuildImports();
InitializeInitFunction(); InitializeInitFunction();
RECURSE(VisitFunctionLiteral(literal_)); RECURSE(VisitFunctionLiteral(literal_));
BuildForeignInitFunction(); BuildForeignInitFunction();
@ -136,7 +124,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
void VisitFunctionDeclaration(FunctionDeclaration* decl) { void VisitFunctionDeclaration(FunctionDeclaration* decl) {
DCHECK_EQ(kModuleScope, scope_); DCHECK_EQ(kModuleScope, scope_);
DCHECK_NULL(current_function_builder_); DCHECK_NULL(current_function_builder_);
current_function_builder_ = LookupOrInsertFunction(decl->proxy()->var()); uint32_t index = LookupOrInsertFunction(decl->proxy()->var());
current_function_builder_ = builder_->FunctionAt(index);
scope_ = kFuncScope; scope_ = kFuncScope;
RECURSE(Visit(decl->fun())); RECURSE(Visit(decl->fun()));
scope_ = kModuleScope; scope_ = kModuleScope;
@ -168,7 +157,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
} }
} }
if (scope_ == kFuncScope) { if (scope_ == kFuncScope) {
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock); BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock,
false);
RECURSE(VisitStatements(stmt->statements())); RECURSE(VisitStatements(stmt->statements()));
} else { } else {
RECURSE(VisitStatements(stmt->statements())); RECURSE(VisitStatements(stmt->statements()));
@ -181,12 +171,10 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
public: public:
BlockVisitor(AsmWasmBuilderImpl* builder, BreakableStatement* stmt, BlockVisitor(AsmWasmBuilderImpl* builder, BreakableStatement* stmt,
WasmOpcode opcode) WasmOpcode opcode, bool is_loop)
: builder_(builder) { : builder_(builder) {
builder_->breakable_blocks_.push_back( builder_->breakable_blocks_.push_back(std::make_pair(stmt, is_loop));
std::make_pair(stmt, opcode == kExprLoop)); builder_->current_function_builder_->Emit(opcode);
// block and loops have a type immediate.
builder_->current_function_builder_->EmitWithU8(opcode, kLocalVoid);
} }
~BlockVisitor() { ~BlockVisitor() {
builder_->current_function_builder_->Emit(kExprEnd); builder_->current_function_builder_->Emit(kExprEnd);
@ -195,32 +183,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
}; };
void VisitExpressionStatement(ExpressionStatement* stmt) { void VisitExpressionStatement(ExpressionStatement* stmt) {
VisitForEffect(stmt->expression()); RECURSE(Visit(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) {} void VisitEmptyStatement(EmptyStatement* stmt) {}
@ -230,7 +193,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
void VisitIfStatement(IfStatement* stmt) { void VisitIfStatement(IfStatement* stmt) {
DCHECK_EQ(kFuncScope, scope_); DCHECK_EQ(kFuncScope, scope_);
RECURSE(Visit(stmt->condition())); RECURSE(Visit(stmt->condition()));
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); current_function_builder_->Emit(kExprIf);
// WASM ifs come with implement blocks for both arms. // WASM ifs come with implement blocks for both arms.
breakable_blocks_.push_back(std::make_pair(nullptr, false)); breakable_blocks_.push_back(std::make_pair(nullptr, false));
if (stmt->HasThenStatement()) { if (stmt->HasThenStatement()) {
@ -244,26 +207,48 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
breakable_blocks_.pop_back(); breakable_blocks_.pop_back();
} }
void DoBreakOrContinue(BreakableStatement* target, bool is_continue) { void VisitContinueStatement(ContinueStatement* stmt) {
DCHECK_EQ(kFuncScope, scope_); DCHECK_EQ(kFuncScope, scope_);
for (int i = static_cast<int>(breakable_blocks_.size()) - 1; i >= 0; --i) { DCHECK_NOT_NULL(stmt->target());
int i = static_cast<int>(breakable_blocks_.size()) - 1;
int block_distance = 0;
for (; i >= 0; i--) {
auto elem = breakable_blocks_.at(i); auto elem = breakable_blocks_.at(i);
if (elem.first == target && elem.second == is_continue) { if (elem.first == stmt->target()) {
int block_distance = static_cast<int>(breakable_blocks_.size() - i - 1); DCHECK(elem.second);
current_function_builder_->Emit(kExprBr); break;
current_function_builder_->EmitVarInt(block_distance); } else if (elem.second) {
return; block_distance += 2;
} else {
block_distance += 1;
} }
} }
UNREACHABLE(); // statement not found DCHECK(i >= 0);
} current_function_builder_->EmitWithU8(kExprBr, ARITY_0);
current_function_builder_->EmitVarInt(block_distance);
void VisitContinueStatement(ContinueStatement* stmt) {
DoBreakOrContinue(stmt->target(), true);
} }
void VisitBreakStatement(BreakStatement* stmt) { void VisitBreakStatement(BreakStatement* stmt) {
DoBreakOrContinue(stmt->target(), false); DCHECK_EQ(kFuncScope, scope_);
DCHECK_NOT_NULL(stmt->target());
int i = static_cast<int>(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);
} }
void VisitReturnStatement(ReturnStatement* stmt) { void VisitReturnStatement(ReturnStatement* stmt) {
@ -273,7 +258,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
scope_ = kModuleScope; scope_ = kModuleScope;
} else if (scope_ == kFuncScope) { } else if (scope_ == kFuncScope) {
RECURSE(Visit(stmt->expression())); RECURSE(Visit(stmt->expression()));
current_function_builder_->Emit(kExprReturn); uint8_t arity =
TypeOf(stmt->expression()) == kAstStmt ? ARITY_0 : ARITY_1;
current_function_builder_->EmitWithU8(kExprReturn, arity);
} else { } else {
UNREACHABLE(); UNREACHABLE();
} }
@ -289,7 +276,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
VisitVariableProxy(tag); VisitVariableProxy(tag);
current_function_builder_->EmitI32Const(node->begin); current_function_builder_->EmitI32Const(node->begin);
current_function_builder_->Emit(kExprI32LtS); current_function_builder_->Emit(kExprI32LtS);
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); current_function_builder_->Emit(kExprIf);
if_depth++; if_depth++;
breakable_blocks_.push_back(std::make_pair(nullptr, false)); breakable_blocks_.push_back(std::make_pair(nullptr, false));
HandleCase(node->left, case_to_block, tag, default_block, if_depth); HandleCase(node->left, case_to_block, tag, default_block, if_depth);
@ -299,7 +286,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
VisitVariableProxy(tag); VisitVariableProxy(tag);
current_function_builder_->EmitI32Const(node->end); current_function_builder_->EmitI32Const(node->end);
current_function_builder_->Emit(kExprI32GtS); current_function_builder_->Emit(kExprI32GtS);
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); current_function_builder_->Emit(kExprIf);
if_depth++; if_depth++;
breakable_blocks_.push_back(std::make_pair(nullptr, false)); breakable_blocks_.push_back(std::make_pair(nullptr, false));
HandleCase(node->right, case_to_block, tag, default_block, if_depth); HandleCase(node->right, case_to_block, tag, default_block, if_depth);
@ -309,9 +296,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
VisitVariableProxy(tag); VisitVariableProxy(tag);
current_function_builder_->EmitI32Const(node->begin); current_function_builder_->EmitI32Const(node->begin);
current_function_builder_->Emit(kExprI32Eq); current_function_builder_->Emit(kExprI32Eq);
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); current_function_builder_->Emit(kExprIf);
DCHECK(case_to_block.find(node->begin) != case_to_block.end()); DCHECK(case_to_block.find(node->begin) != case_to_block.end());
current_function_builder_->Emit(kExprBr); current_function_builder_->EmitWithU8(kExprBr, ARITY_0);
current_function_builder_->EmitVarInt(1 + if_depth + current_function_builder_->EmitVarInt(1 + if_depth +
case_to_block[node->begin]); case_to_block[node->begin]);
current_function_builder_->Emit(kExprEnd); current_function_builder_->Emit(kExprEnd);
@ -323,22 +310,22 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
} else { } else {
VisitVariableProxy(tag); VisitVariableProxy(tag);
} }
current_function_builder_->Emit(kExprBrTable); current_function_builder_->EmitWithU8(kExprBrTable, ARITY_0);
current_function_builder_->EmitVarInt(node->end - node->begin + 1); 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()) { if (case_to_block.find(v) != case_to_block.end()) {
uint32_t target = if_depth + case_to_block[v]; byte break_code[] = {BR_TARGET(if_depth + case_to_block[v])};
current_function_builder_->EmitVarInt(target); current_function_builder_->EmitCode(break_code, sizeof(break_code));
} else { } else {
uint32_t target = if_depth + default_block; byte break_code[] = {BR_TARGET(if_depth + default_block)};
current_function_builder_->EmitVarInt(target); current_function_builder_->EmitCode(break_code, sizeof(break_code));
} }
if (v == kMaxInt) { if (v == kMaxInt) {
break; break;
} }
} }
uint32_t target = if_depth + default_block; byte break_code[] = {BR_TARGET(if_depth + default_block)};
current_function_builder_->EmitVarInt(target); current_function_builder_->EmitCode(break_code, sizeof(break_code));
} }
while (if_depth-- != prev_if_depth) { while (if_depth-- != prev_if_depth) {
@ -355,14 +342,14 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
if (case_count == 0) { if (case_count == 0) {
return; return;
} }
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock); BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false);
ZoneVector<BlockVisitor*> blocks(zone_); ZoneVector<BlockVisitor*> blocks(zone_);
ZoneVector<int32_t> cases(zone_); ZoneVector<int32_t> cases(zone_);
ZoneMap<int, unsigned int> case_to_block(zone_); ZoneMap<int, unsigned int> case_to_block(zone_);
bool has_default = false; 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); CaseClause* clause = clauses->at(i);
blocks.push_back(new BlockVisitor(this, nullptr, kExprBlock)); blocks.push_back(new BlockVisitor(this, nullptr, kExprBlock, false));
if (!clause->is_default()) { if (!clause->is_default()) {
Literal* label = clause->label()->AsLiteral(); Literal* label = clause->label()->AsLiteral();
Handle<Object> value = label->value(); Handle<Object> value = label->value();
@ -379,12 +366,12 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
} }
if (!has_default || case_count > 1) { if (!has_default || case_count > 1) {
int default_block = has_default ? case_count - 1 : case_count; int default_block = has_default ? case_count - 1 : case_count;
BlockVisitor switch_logic_block(this, nullptr, kExprBlock); BlockVisitor switch_logic_block(this, nullptr, kExprBlock, false);
CaseNode* root = OrderCases(&cases, zone_); CaseNode* root = OrderCases(&cases, zone_);
HandleCase(root, case_to_block, tag, default_block, 0); HandleCase(root, case_to_block, tag, default_block, 0);
if (root->left != nullptr || root->right != nullptr || if (root->left != nullptr || root->right != nullptr ||
root->begin == root->end) { root->begin == root->end) {
current_function_builder_->Emit(kExprBr); current_function_builder_->EmitWithU8(kExprBr, ARITY_0);
current_function_builder_->EmitVarInt(default_block); current_function_builder_->EmitVarInt(default_block);
} }
} }
@ -401,24 +388,22 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
void VisitDoWhileStatement(DoWhileStatement* stmt) { void VisitDoWhileStatement(DoWhileStatement* stmt) {
DCHECK_EQ(kFuncScope, scope_); DCHECK_EQ(kFuncScope, scope_);
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock); BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true);
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
RECURSE(Visit(stmt->body())); RECURSE(Visit(stmt->body()));
RECURSE(Visit(stmt->cond())); RECURSE(Visit(stmt->cond()));
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); current_function_builder_->Emit(kExprIf);
current_function_builder_->EmitWithU8(kExprBr, 1); current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 1);
current_function_builder_->Emit(kExprEnd); current_function_builder_->Emit(kExprEnd);
} }
void VisitWhileStatement(WhileStatement* stmt) { void VisitWhileStatement(WhileStatement* stmt) {
DCHECK_EQ(kFuncScope, scope_); DCHECK_EQ(kFuncScope, scope_);
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock); BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true);
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
RECURSE(Visit(stmt->cond())); RECURSE(Visit(stmt->cond()));
breakable_blocks_.push_back(std::make_pair(nullptr, false)); breakable_blocks_.push_back(std::make_pair(nullptr, false));
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); current_function_builder_->Emit(kExprIf);
RECURSE(Visit(stmt->body())); RECURSE(Visit(stmt->body()));
current_function_builder_->EmitWithU8(kExprBr, 1); current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 1);
current_function_builder_->Emit(kExprEnd); current_function_builder_->Emit(kExprEnd);
breakable_blocks_.pop_back(); breakable_blocks_.pop_back();
} }
@ -428,13 +413,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
if (stmt->init() != nullptr) { if (stmt->init() != nullptr) {
RECURSE(Visit(stmt->init())); RECURSE(Visit(stmt->init()));
} }
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock); BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true);
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
if (stmt->cond() != nullptr) { if (stmt->cond() != nullptr) {
RECURSE(Visit(stmt->cond())); RECURSE(Visit(stmt->cond()));
current_function_builder_->Emit(kExprI32Eqz); current_function_builder_->Emit(kExprI32Eqz);
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid); current_function_builder_->Emit(kExprIf);
current_function_builder_->EmitWithU8(kExprBr, 2); current_function_builder_->Emit(kExprNop);
current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 2);
current_function_builder_->Emit(kExprEnd); current_function_builder_->Emit(kExprEnd);
} }
if (stmt->body() != nullptr) { if (stmt->body() != nullptr) {
@ -443,7 +428,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
if (stmt->next() != nullptr) { if (stmt->next() != nullptr) {
RECURSE(Visit(stmt->next())); RECURSE(Visit(stmt->next()));
} }
current_function_builder_->EmitWithU8(kExprBr, 0); current_function_builder_->Emit(kExprNop);
current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 0);
} }
void VisitForInStatement(ForInStatement* stmt) { UNREACHABLE(); } void VisitForInStatement(ForInStatement* stmt) { UNREACHABLE(); }
@ -460,13 +446,19 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
DeclarationScope* scope = expr->scope(); DeclarationScope* scope = expr->scope();
if (scope_ == kFuncScope) { if (scope_ == kFuncScope) {
if (auto* func_type = typer_->TypeOf(expr)->AsFunctionType()) { if (auto* func_type = typer_->TypeOf(expr)->AsFunctionType()) {
// Add the parameters for the function. // Build the signature for the function.
LocalType return_type = TypeFrom(func_type->ReturnType());
const auto& arguments = func_type->Arguments(); 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) { for (int i = 0; i < expr->parameter_count(); ++i) {
LocalType type = TypeFrom(arguments[i]); LocalType type = TypeFrom(arguments[i]);
DCHECK_NE(kAstStmt, type); DCHECK_NE(kAstStmt, type);
b.AddParam(type);
InsertParameter(scope->parameter(i), type, i); InsertParameter(scope->parameter(i), type, i);
} }
current_function_builder_->SetSignature(b.Build());
} else { } else {
UNREACHABLE(); UNREACHABLE();
} }
@ -484,24 +476,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
RECURSE(Visit(expr->condition())); RECURSE(Visit(expr->condition()));
// WASM ifs come with implicit blocks for both arms. // WASM ifs come with implicit blocks for both arms.
breakable_blocks_.push_back(std::make_pair(nullptr, false)); breakable_blocks_.push_back(std::make_pair(nullptr, false));
LocalTypeCode type; current_function_builder_->Emit(kExprIf);
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())); RECURSE(Visit(expr->then_expression()));
current_function_builder_->Emit(kExprElse); current_function_builder_->Emit(kExprElse);
RECURSE(Visit(expr->else_expression())); RECURSE(Visit(expr->else_expression()));
@ -579,9 +554,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
} else if (scope_ == kExportScope) { } else if (scope_ == kExportScope) {
Variable* var = expr->var(); Variable* var = expr->var();
DCHECK(var->is_function()); DCHECK(var->is_function());
WasmFunctionBuilder* function = LookupOrInsertFunction(var); uint32_t index = LookupOrInsertFunction(var);
function->SetExported(); builder_->FunctionAt(index)->SetExported();
function->SetName( builder_->FunctionAt(index)->SetName(
AsmWasmBuilder::single_function_name, AsmWasmBuilder::single_function_name,
static_cast<int>(strlen(AsmWasmBuilder::single_function_name))); static_cast<int>(strlen(AsmWasmBuilder::single_function_name)));
} }
@ -634,10 +609,11 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
DCHECK(name->IsPropertyName()); DCHECK(name->IsPropertyName());
const AstRawString* raw_name = name->AsRawPropertyName(); const AstRawString* raw_name = name->AsRawPropertyName();
if (var->is_function()) { if (var->is_function()) {
WasmFunctionBuilder* function = LookupOrInsertFunction(var); uint32_t index = LookupOrInsertFunction(var);
function->SetExported(); builder_->FunctionAt(index)->SetExported();
function->SetName(reinterpret_cast<const char*>(raw_name->raw_data()), builder_->FunctionAt(index)->SetName(
raw_name->length()); reinterpret_cast<const char*>(raw_name->raw_data()),
raw_name->length());
} }
} }
} }
@ -645,7 +621,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
void VisitArrayLiteral(ArrayLiteral* expr) { UNREACHABLE(); } void VisitArrayLiteral(ArrayLiteral* expr) { UNREACHABLE(); }
void LoadInitFunction() { void LoadInitFunction() {
current_function_builder_ = init_function_; current_function_builder_ = builder_->FunctionAt(init_function_index_);
scope_ = kInitScope; scope_ = kInitScope;
} }
@ -674,8 +650,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
for (int i = 0; i < funcs->values()->length(); ++i) { for (int i = 0; i < funcs->values()->length(); ++i) {
VariableProxy* func = funcs->values()->at(i)->AsVariableProxy(); VariableProxy* func = funcs->values()->at(i)->AsVariableProxy();
DCHECK_NOT_NULL(func); DCHECK_NOT_NULL(func);
builder_->AddIndirectFunction( builder_->AddIndirectFunction(LookupOrInsertFunction(func->var()));
LookupOrInsertFunction(func->var())->func_index());
} }
} }
@ -705,11 +680,12 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
private: private:
class ImportedFunctionIndices : public ZoneObject { class ImportedFunctionIndices : public ZoneObject {
public: public:
bool has_name_; const char* name_;
int name_length_;
WasmModuleBuilder::SignatureMap signature_to_index_; WasmModuleBuilder::SignatureMap signature_to_index_;
explicit ImportedFunctionIndices(Zone* zone) ImportedFunctionIndices(const char* name, int name_length, Zone* zone)
: has_name_(false), signature_to_index_(zone) {} : name_(name), name_length_(name_length), signature_to_index_(zone) {}
}; };
ZoneHashMap table_; ZoneHashMap table_;
AsmWasmBuilderImpl* builder_; AsmWasmBuilderImpl* builder_;
@ -721,50 +697,29 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
ZoneAllocationPolicy(builder->zone())), ZoneAllocationPolicy(builder->zone())),
builder_(builder) {} builder_(builder) {}
// Set the imported name of a variable. Must happen after all signatures void AddImport(Variable* v, const char* name, int name_length) {
// (and thus import indices) are added for a given variable. ImportedFunctionIndices* indices = new (builder_->zone())
void SetImportName(Variable* v, const char* name, int name_length) { ImportedFunctionIndices(name, name_length, builder_->zone());
auto indices = GetEntry(v); ZoneHashMap::Entry* entry = table_.LookupOrInsert(
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) {
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())); v, ComputePointerHash(v), ZoneAllocationPolicy(builder_->zone()));
if (entry->value == nullptr) { entry->value = indices;
entry->value =
new (builder_->zone()) ImportedFunctionIndices(builder_->zone());
}
auto indices = reinterpret_cast<ImportedFunctionIndices*>(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) { uint32_t GetFunctionIndex(Variable* v, FunctionSig* sig) {
auto entry = table_.Lookup(v, ComputePointerHash(v)); ZoneHashMap::Entry* entry = table_.Lookup(v, ComputePointerHash(v));
if (entry == nullptr) return nullptr; DCHECK_NOT_NULL(entry);
return reinterpret_cast<ImportedFunctionIndices*>(entry->value); ImportedFunctionIndices* indices =
reinterpret_cast<ImportedFunctionIndices*>(entry->value);
WasmModuleBuilder::SignatureMap::iterator pos =
indices->signature_to_index_.find(sig);
if (pos != indices->signature_to_index_.end()) {
return pos->second;
} else {
uint32_t index = builder_->builder_->AddImport(
indices->name_, indices->name_length_, sig);
indices->signature_to_index_[sig] = index;
return index;
}
} }
}; };
@ -827,7 +782,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
RECURSE(Visit(value)); RECURSE(Visit(value));
} }
void EmitAssignment(Assignment* expr, MachineType type, ValueFate fate) { void EmitAssignment(Assignment* expr, MachineType type) {
// Match the left hand side of the assignment. // Match the left hand side of the assignment.
VariableProxy* target_var = expr->target()->AsVariableProxy(); VariableProxy* target_var = expr->target()->AsVariableProxy();
if (target_var != nullptr) { if (target_var != nullptr) {
@ -836,19 +791,11 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
LocalType var_type = TypeOf(expr); LocalType var_type = TypeOf(expr);
DCHECK_NE(kAstStmt, var_type); DCHECK_NE(kAstStmt, var_type);
if (var->IsContextSlot()) { if (var->IsContextSlot()) {
uint32_t index = LookupOrInsertGlobal(var, var_type); current_function_builder_->EmitWithVarInt(
current_function_builder_->EmitWithVarInt(kExprSetGlobal, index); kExprSetGlobal, LookupOrInsertGlobal(var, var_type));
if (fate == kLeaveOnStack) {
current_function_builder_->EmitWithVarInt(kExprGetGlobal, index);
}
} else { } else {
if (fate == kDrop) { current_function_builder_->EmitSetLocal(
current_function_builder_->EmitSetLocal( LookupOrInsertLocal(var, var_type));
LookupOrInsertLocal(var, var_type));
} else {
current_function_builder_->EmitTeeLocal(
LookupOrInsertLocal(var, var_type));
}
} }
} }
@ -860,7 +807,6 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
->IsA(AsmType::Float32Array())) { ->IsA(AsmType::Float32Array())) {
current_function_builder_->Emit(kExprF32ConvertF64); current_function_builder_->Emit(kExprF32ConvertF64);
} }
// Note that unlike StoreMem, AsmjsStoreMem ignores out-of-bounds writes.
WasmOpcode opcode; WasmOpcode opcode;
if (type == MachineType::Int8()) { if (type == MachineType::Int8()) {
opcode = kExprI32AsmjsStoreMem8; opcode = kExprI32AsmjsStoreMem8;
@ -882,10 +828,6 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
UNREACHABLE(); UNREACHABLE();
} }
current_function_builder_->Emit(opcode); 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) { if (target_var == nullptr && target_prop == nullptr) {
@ -894,10 +836,6 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
} }
void VisitAssignment(Assignment* expr) { void VisitAssignment(Assignment* expr) {
VisitAssignment(expr, kLeaveOnStack);
}
void VisitAssignment(Assignment* expr, ValueFate fate) {
bool as_init = false; bool as_init = false;
if (scope_ == kModuleScope) { if (scope_ == kModuleScope) {
// Skip extra assignment inserted by the parser when in this form: // Skip extra assignment inserted by the parser when in this form:
@ -915,7 +853,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
if (typer_->TypeOf(target)->AsFFIType() != nullptr) { if (typer_->TypeOf(target)->AsFFIType() != nullptr) {
const AstRawString* name = const AstRawString* name =
prop->key()->AsLiteral()->AsRawPropertyName(); prop->key()->AsLiteral()->AsRawPropertyName();
imported_function_table_.SetImportName( imported_function_table_.AddImport(
target->var(), reinterpret_cast<const char*>(name->raw_data()), target->var(), reinterpret_cast<const char*>(name->raw_data()),
name->length()); name->length());
} }
@ -943,12 +881,12 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
} }
if (as_init) LoadInitFunction(); if (as_init) LoadInitFunction();
MachineType mtype = MachineType::None(); MachineType mtype;
bool is_nop = false; bool is_nop = false;
EmitAssignmentLhs(expr->target(), &mtype); EmitAssignmentLhs(expr->target(), &mtype);
EmitAssignmentRhs(expr->target(), expr->value(), &is_nop); EmitAssignmentRhs(expr->target(), expr->value(), &is_nop);
if (!is_nop) { if (!is_nop) {
EmitAssignment(expr, mtype, fate); EmitAssignment(expr, mtype);
} }
if (as_init) UnLoadInitFunction(); if (as_init) UnLoadInitFunction();
} }
@ -1173,11 +1111,11 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
// if set_local(tmp, x) < 0 // if set_local(tmp, x) < 0
Visit(call->arguments()->at(0)); Visit(call->arguments()->at(0));
current_function_builder_->EmitTeeLocal(tmp.index()); current_function_builder_->EmitSetLocal(tmp.index());
byte code[] = {WASM_I8(0)}; byte code[] = {WASM_I8(0)};
current_function_builder_->EmitCode(code, sizeof(code)); current_function_builder_->EmitCode(code, sizeof(code));
current_function_builder_->Emit(kExprI32LtS); current_function_builder_->Emit(kExprI32LtS);
current_function_builder_->EmitWithU8(kExprIf, kLocalI32); current_function_builder_->Emit(kExprIf);
// then (0 - tmp) // then (0 - tmp)
current_function_builder_->EmitCode(code, sizeof(code)); current_function_builder_->EmitCode(code, sizeof(code));
@ -1209,13 +1147,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
// if set_local(tmp_x, x) < set_local(tmp_y, y) // if set_local(tmp_x, x) < set_local(tmp_y, y)
Visit(call->arguments()->at(0)); Visit(call->arguments()->at(0));
current_function_builder_->EmitTeeLocal(tmp_x.index()); current_function_builder_->EmitSetLocal(tmp_x.index());
Visit(call->arguments()->at(1)); Visit(call->arguments()->at(1));
current_function_builder_->EmitTeeLocal(tmp_y.index()); current_function_builder_->EmitSetLocal(tmp_y.index());
current_function_builder_->Emit(kExprI32LeS); current_function_builder_->Emit(kExprI32LeS);
current_function_builder_->EmitWithU8(kExprIf, kLocalI32); current_function_builder_->Emit(kExprIf);
// then tmp_x // then tmp_x
current_function_builder_->EmitGetLocal(tmp_x.index()); current_function_builder_->EmitGetLocal(tmp_x.index());
@ -1245,13 +1183,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
// if set_local(tmp_x, x) < set_local(tmp_y, y) // if set_local(tmp_x, x) < set_local(tmp_y, y)
Visit(call->arguments()->at(0)); Visit(call->arguments()->at(0));
current_function_builder_->EmitTeeLocal(tmp_x.index()); current_function_builder_->EmitSetLocal(tmp_x.index());
Visit(call->arguments()->at(1)); Visit(call->arguments()->at(1));
current_function_builder_->EmitTeeLocal(tmp_y.index()); current_function_builder_->EmitSetLocal(tmp_y.index());
current_function_builder_->Emit(kExprI32LeS); current_function_builder_->Emit(kExprI32LeS);
current_function_builder_->EmitWithU8(kExprIf, kLocalI32); current_function_builder_->Emit(kExprIf);
// then tmp_y // then tmp_y
current_function_builder_->EmitGetLocal(tmp_y.index()); current_function_builder_->EmitGetLocal(tmp_y.index());
@ -1337,20 +1275,18 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
} }
} }
void VisitCall(Call* expr) { VisitCallExpression(expr); } void VisitCall(Call* expr) {
bool VisitCallExpression(Call* expr) {
Call::CallType call_type = expr->GetCallType(); Call::CallType call_type = expr->GetCallType();
bool returns_value = true;
switch (call_type) { switch (call_type) {
case Call::OTHER_CALL: { case Call::OTHER_CALL: {
DCHECK_EQ(kFuncScope, scope_); DCHECK_EQ(kFuncScope, scope_);
VariableProxy* proxy = expr->expression()->AsVariableProxy(); VariableProxy* proxy = expr->expression()->AsVariableProxy();
if (proxy != nullptr) { if (proxy != nullptr) {
if (VisitStdlibFunction(expr, proxy)) { if (VisitStdlibFunction(expr, proxy)) {
return true; return;
} }
} }
uint32_t index;
VariableProxy* vp = expr->expression()->AsVariableProxy(); VariableProxy* vp = expr->expression()->AsVariableProxy();
DCHECK_NOT_NULL(vp); DCHECK_NOT_NULL(vp);
if (typer_->TypeOf(vp)->AsFFIType() != nullptr) { if (typer_->TypeOf(vp)->AsFFIType() != nullptr) {
@ -1360,23 +1296,22 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
args->length()); args->length());
if (return_type != kAstStmt) { if (return_type != kAstStmt) {
sig.AddReturn(return_type); sig.AddReturn(return_type);
} else {
returns_value = false;
} }
for (int i = 0; i < args->length(); ++i) { for (int i = 0; i < args->length(); ++i) {
sig.AddParam(TypeOf(args->at(i))); sig.AddParam(TypeOf(args->at(i)));
} }
uint32_t index = index =
imported_function_table_.GetFunctionIndex(vp->var(), sig.Build()); imported_function_table_.GetFunctionIndex(vp->var(), sig.Build());
VisitCallArgs(expr); VisitCallArgs(expr);
current_function_builder_->Emit(kExprCallFunction); current_function_builder_->Emit(kExprCallImport);
current_function_builder_->EmitVarInt(expr->arguments()->length());
current_function_builder_->EmitVarInt(index); current_function_builder_->EmitVarInt(index);
} else { } else {
WasmFunctionBuilder* function = LookupOrInsertFunction(vp->var()); index = LookupOrInsertFunction(vp->var());
VisitCallArgs(expr); VisitCallArgs(expr);
current_function_builder_->Emit(kExprCallFunction); current_function_builder_->Emit(kExprCallFunction);
current_function_builder_->EmitVarInt(function->func_index()); current_function_builder_->EmitVarInt(expr->arguments()->length());
returns_value = function->signature()->return_count() > 0; current_function_builder_->EmitVarInt(index);
} }
break; break;
} }
@ -1387,28 +1322,18 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
VariableProxy* var = p->obj()->AsVariableProxy(); VariableProxy* var = p->obj()->AsVariableProxy();
DCHECK_NOT_NULL(var); DCHECK_NOT_NULL(var);
FunctionTableIndices* indices = LookupFunctionTable(var->var()); FunctionTableIndices* indices = LookupFunctionTable(var->var());
Visit(p->key()); // TODO(titzer): should use RECURSE() RECURSE(Visit(p->key()));
// We have to use a temporary for the correct order of evaluation.
current_function_builder_->EmitI32Const(indices->start_index); current_function_builder_->EmitI32Const(indices->start_index);
current_function_builder_->Emit(kExprI32Add); current_function_builder_->Emit(kExprI32Add);
WasmTemporary tmp(current_function_builder_, kAstI32);
current_function_builder_->EmitSetLocal(tmp.index());
VisitCallArgs(expr); VisitCallArgs(expr);
current_function_builder_->EmitGetLocal(tmp.index());
current_function_builder_->Emit(kExprCallIndirect); current_function_builder_->Emit(kExprCallIndirect);
current_function_builder_->EmitVarInt(expr->arguments()->length());
current_function_builder_->EmitVarInt(indices->signature_index); current_function_builder_->EmitVarInt(indices->signature_index);
returns_value =
builder_->GetSignature(indices->signature_index)->return_count() >
0;
break; break;
} }
default: default:
UNREACHABLE(); UNREACHABLE();
} }
return returns_value;
} }
void VisitCallNew(CallNew* expr) { UNREACHABLE(); } void VisitCallNew(CallNew* expr) { UNREACHABLE(); }
@ -1594,13 +1519,16 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
RECURSE(Visit(GetLeft(expr))); RECURSE(Visit(GetLeft(expr)));
} else { } else {
if (expr->op() == Token::COMMA) { if (expr->op() == Token::COMMA) {
RECURSE(VisitForEffect(expr->left())); current_function_builder_->Emit(kExprBlock);
RECURSE(Visit(expr->right()));
return;
} }
RECURSE(Visit(expr->left())); RECURSE(Visit(expr->left()));
RECURSE(Visit(expr->right())); RECURSE(Visit(expr->right()));
if (expr->op() == Token::COMMA) {
current_function_builder_->Emit(kExprEnd);
}
switch (expr->op()) { switch (expr->op()) {
BINOP_CASE(Token::ADD, Add, NON_SIGNED_BINOP, true); BINOP_CASE(Token::ADD, Add, NON_SIGNED_BINOP, true);
BINOP_CASE(Token::SUB, Sub, NON_SIGNED_BINOP, true); BINOP_CASE(Token::SUB, Sub, NON_SIGNED_BINOP, true);
@ -1800,33 +1728,18 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
return (reinterpret_cast<IndexContainer*>(entry->value))->index; return (reinterpret_cast<IndexContainer*>(entry->value))->index;
} }
WasmFunctionBuilder* LookupOrInsertFunction(Variable* v) { uint32_t LookupOrInsertFunction(Variable* v) {
DCHECK_NOT_NULL(builder_); DCHECK_NOT_NULL(builder_);
ZoneHashMap::Entry* entry = functions_.Lookup(v, ComputePointerHash(v)); ZoneHashMap::Entry* entry = functions_.Lookup(v, ComputePointerHash(v));
if (entry == nullptr) { if (entry == nullptr) {
auto* func_type = typer_->TypeOf(v)->AsFunctionType(); uint32_t index = builder_->AddFunction();
DCHECK_NOT_NULL(func_type); IndexContainer* container = new (zone()) IndexContainer();
// Build the signature for the function. container->index = index;
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<int>(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), entry = functions_.LookupOrInsert(v, ComputePointerHash(v),
ZoneAllocationPolicy(zone())); ZoneAllocationPolicy(zone()));
function->SetName( entry->value = container;
reinterpret_cast<const char*>(v->raw_name()->raw_data()),
v->raw_name()->length());
entry->value = function;
} }
return (reinterpret_cast<WasmFunctionBuilder*>(entry->value)); return (reinterpret_cast<IndexContainer*>(entry->value))->index;
} }
LocalType TypeOf(Expression* expr) { return TypeFrom(typer_->TypeOf(expr)); } LocalType TypeOf(Expression* expr) { return TypeFrom(typer_->TypeOf(expr)); }
@ -1861,8 +1774,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
AsmTyper* typer_; AsmTyper* typer_;
ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_; ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
ZoneVector<ForeignVariable> foreign_variables_; ZoneVector<ForeignVariable> foreign_variables_;
WasmFunctionBuilder* init_function_; uint32_t init_function_index_;
WasmFunctionBuilder* foreign_init_function_; uint32_t foreign_init_function_index_;
uint32_t next_table_index_; uint32_t next_table_index_;
ZoneHashMap function_tables_; ZoneHashMap function_tables_;
ImportedFunctionTable imported_function_table_; ImportedFunctionTable imported_function_table_;

View File

@ -778,18 +778,6 @@ void Int64Lowering::LowerNode(Node* node) {
} }
break; break;
} }
case IrOpcode::kProjection: {
Node* call = node->InputAt(0);
DCHECK_EQ(IrOpcode::kCall, call->opcode());
CallDescriptor* descriptor =
const_cast<CallDescriptor*>(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: { case IrOpcode::kWord64ReverseBytes: {
Node* input = node->InputAt(0); Node* input = node->InputAt(0);
ReplaceNode(node, graph()->NewNode(machine()->Word32ReverseBytes().op(), ReplaceNode(node, graph()->NewNode(machine()->Word32ReverseBytes().op(),

View File

@ -189,29 +189,26 @@ class WasmTrapHelper : public ZoneObject {
Node* GetTrapValue(wasm::FunctionSig* sig) { Node* GetTrapValue(wasm::FunctionSig* sig) {
if (sig->return_count() > 0) { if (sig->return_count() > 0) {
return GetTrapValue(sig->GetReturn()); 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<float>(0xdeadbeef));
case wasm::kAstF64:
return jsgraph()->Float64Constant(
bit_cast<double>(0xdeadbeefdeadbeef));
break;
default:
UNREACHABLE();
return nullptr;
}
} else { } else {
return jsgraph()->Int32Constant(0xdeadbeef); 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<float>(0xdeadbeef));
case wasm::kAstF64:
return jsgraph()->Float64Constant(bit_cast<double>(0xdeadbeefdeadbeef));
break;
default:
UNREACHABLE();
return nullptr;
}
}
private: private:
WasmGraphBuilder* builder_; WasmGraphBuilder* builder_;
JSGraph* jsgraph_; JSGraph* jsgraph_;
@ -996,11 +993,16 @@ Node* WasmGraphBuilder::Return(unsigned count, Node** vals) {
DCHECK_NOT_NULL(*control_); DCHECK_NOT_NULL(*control_);
DCHECK_NOT_NULL(*effect_); 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); Node** buf = Realloc(vals, count, count + 2);
buf[count] = *effect_; buf[count] = *effect_;
buf[count + 1] = *control_; buf[count + 1] = *control_;
Node* ret = Node* ret = graph()->NewNode(jsgraph()->common()->Return(), count + 2, vals);
graph()->NewNode(jsgraph()->common()->Return(count), count + 2, vals);
MergeControlToEnd(jsgraph(), ret); MergeControlToEnd(jsgraph(), ret);
return ret; return ret;
@ -1992,8 +1994,8 @@ Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) {
return call; return call;
} }
Node** WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args, Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
const size_t params = sig->parameter_count(); const size_t params = sig->parameter_count();
const size_t extra = 2; // effect and control inputs. const size_t extra = 2; // effect and control inputs.
const size_t count = 1 + params + extra; const size_t count = 1 + params + extra;
@ -2012,38 +2014,33 @@ Node** WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
SetSourcePosition(call, position); SetSourcePosition(call, position);
*effect_ = call; *effect_ = call;
size_t ret_count = sig->return_count(); return call;
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, Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
DCHECK_NULL(args[0]); DCHECK_NULL(args[0]);
// Add code object as constant. // Add code object as constant.
Handle<Code> code = module_->GetFunctionCode(index); args[0] = HeapConstant(module_->GetCodeOrPlaceholder(index));
DCHECK(!code.is_null());
args[0] = HeapConstant(code);
wasm::FunctionSig* sig = module_->GetFunctionSignature(index); wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
return BuildWasmCall(sig, args, position); return BuildWasmCall(sig, args, position);
} }
Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node* WasmGraphBuilder::CallImport(uint32_t index, Node** args,
wasm::WasmCodePosition position) { 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) {
DCHECK_NOT_NULL(args[0]); DCHECK_NOT_NULL(args[0]);
DCHECK(module_ && module_->instance); DCHECK(module_ && module_->instance);
@ -2057,7 +2054,6 @@ Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
// Bounds check the index. // Bounds check the index.
uint32_t table_size = uint32_t table_size =
module_->IsValidTable(0) ? module_->GetTable(0)->max_size : 0; module_->IsValidTable(0) ? module_->GetTable(0)->max_size : 0;
wasm::FunctionSig* sig = module_->GetSignature(index);
if (table_size > 0) { if (table_size > 0) {
// Bounds check against the table size. // Bounds check against the table size.
Node* size = Uint32Constant(table_size); Node* size = Uint32Constant(table_size);
@ -2066,11 +2062,7 @@ Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
} else { } else {
// No function table. Generate a trap and return a constant. // No function table. Generate a trap and return a constant.
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position); trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position);
Node** rets = Buffer(sig->return_count()); return trap_->GetTrapValue(module_->GetSignature(index));
for (size_t i = 0; i < sig->return_count(); i++) {
rets[i] = trap_->GetTrapValue(sig->GetReturn(i));
}
return rets;
} }
Node* table = FunctionTable(0); Node* table = FunctionTable(0);
@ -2104,6 +2096,7 @@ Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
*effect_, *control_); *effect_, *control_);
args[0] = load_code; args[0] = load_code;
wasm::FunctionSig* sig = module_->GetSignature(index);
return BuildWasmCall(sig, args, position); return BuildWasmCall(sig, args, position);
} }
@ -2700,11 +2693,6 @@ 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) { Node* WasmGraphBuilder::MemSize(uint32_t offset) {
DCHECK(module_ && module_->instance); DCHECK(module_ && module_->instance);
uint32_t size = static_cast<uint32_t>(module_->instance->mem_size); uint32_t size = static_cast<uint32_t>(module_->instance->mem_size);

View File

@ -153,11 +153,12 @@ class WasmGraphBuilder {
Node* ReturnVoid(); Node* ReturnVoid();
Node* Unreachable(wasm::WasmCodePosition position); Node* Unreachable(wasm::WasmCodePosition position);
Node** CallDirect(uint32_t index, Node** args, Node* CallDirect(uint32_t index, Node** args,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
Node** CallIndirect(uint32_t index, Node** args, Node* CallImport(uint32_t index, Node** args,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
Node* CallIndirect(uint32_t index, Node** args,
wasm::WasmCodePosition position);
void BuildJSToWasmWrapper(Handle<Code> wasm_code, wasm::FunctionSig* sig); void BuildJSToWasmWrapper(Handle<Code> wasm_code, wasm::FunctionSig* sig);
void BuildWasmToJSWrapper(Handle<JSReceiver> target, wasm::FunctionSig* sig); void BuildWasmToJSWrapper(Handle<JSReceiver> target, wasm::FunctionSig* sig);
@ -169,7 +170,7 @@ class WasmGraphBuilder {
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Operations that concern the linear memory. // Operations that concern the linear memory.
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
Node* CurrentMemoryPages(); Node* MemSize(uint32_t offset);
Node* GetGlobal(uint32_t index); Node* GetGlobal(uint32_t index);
Node* SetGlobal(uint32_t index, Node* val); Node* SetGlobal(uint32_t index, Node* val);
Node* LoadMem(wasm::LocalType type, MachineType memtype, Node* index, Node* LoadMem(wasm::LocalType type, MachineType memtype, Node* index,
@ -228,7 +229,6 @@ class WasmGraphBuilder {
Graph* graph(); Graph* graph();
Node* String(const char* string); Node* String(const char* string);
Node* MemSize(uint32_t offset);
Node* MemBuffer(uint32_t offset); Node* MemBuffer(uint32_t offset);
void BoundsCheckMem(MachineType memtype, Node* index, uint32_t offset, void BoundsCheckMem(MachineType memtype, Node* index, uint32_t offset,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
@ -240,8 +240,8 @@ class WasmGraphBuilder {
Node* MaskShiftCount64(Node* node); Node* MaskShiftCount64(Node* node);
Node* BuildCCall(MachineSignature* sig, Node** args); Node* BuildCCall(MachineSignature* sig, Node** args);
Node** BuildWasmCall(wasm::FunctionSig* sig, Node** args, Node* BuildWasmCall(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
Node* BuildF32CopySign(Node* left, Node* right); Node* BuildF32CopySign(Node* left, Node* right);
Node* BuildF64CopySign(Node* left, Node* right); Node* BuildF64CopySign(Node* left, Node* right);

View File

@ -532,8 +532,6 @@ DEFINE_BOOL(wasm_simd_prototype, false,
"enable prototype simd opcodes for wasm") "enable prototype simd opcodes for wasm")
DEFINE_BOOL(wasm_eh_prototype, false, DEFINE_BOOL(wasm_eh_prototype, false,
"enable prototype exception handling opcodes for wasm") "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, DEFINE_BOOL(wasm_trap_handler, false,
"use signal handlers to catch out of bounds memory access in wasm" "use signal handlers to catch out of bounds memory access in wasm"

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,6 @@ class WasmGraphBuilder;
namespace wasm { namespace wasm {
const uint32_t kMaxNumWasmLocals = 8000000; const uint32_t kMaxNumWasmLocals = 8000000;
struct WasmGlobal;
// Helpers for decoding different kinds of operands which follow bytecodes. // Helpers for decoding different kinds of operands which follow bytecodes.
struct LocalIndexOperand { struct LocalIndexOperand {
@ -82,111 +81,39 @@ struct ImmF64Operand {
struct GlobalIndexOperand { struct GlobalIndexOperand {
uint32_t index; uint32_t index;
LocalType type; LocalType type;
const WasmGlobal* global;
unsigned length; unsigned length;
inline GlobalIndexOperand(Decoder* decoder, const byte* pc) { inline GlobalIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "global index"); index = decoder->checked_read_u32v(pc, 1, &length, "global index");
global = nullptr;
type = kAstStmt; type = kAstStmt;
} }
}; };
struct BlockTypeOperand {
uint32_t arity;
const byte* types; // pointer to encoded types for the block.
unsigned length;
inline BlockTypeOperand(Decoder* decoder, const byte* pc) {
uint8_t val = decoder->checked_read_u8(pc, 1, "block type");
LocalType type = kAstStmt;
length = 1;
arity = 0;
types = nullptr;
if (decode_local_type(val, &type)) {
arity = type == kAstStmt ? 0 : 1;
types = pc + 1;
} else {
// Handle multi-value blocks.
if (!FLAG_wasm_mv_prototype) {
decoder->error(pc, pc + 1, "invalid block arity > 1");
return;
}
if (val != kMultivalBlock) {
decoder->error(pc, pc + 1, "invalid block type");
return;
}
// Decode and check the types vector of the block.
unsigned len = 0;
uint32_t count = decoder->checked_read_u32v(pc, 2, &len, "block arity");
// {count} is encoded as {arity-2}, so that a {0} count here corresponds
// to a block with 2 values. This makes invalid/redundant encodings
// impossible.
arity = count + 2;
length = 1 + len + arity;
types = pc + 1 + 1 + len;
for (uint32_t i = 0; i < arity; i++) {
uint32_t offset = 1 + 1 + len + i;
val = decoder->checked_read_u8(pc, offset, "block type");
decode_local_type(val, &type);
if (type == kAstStmt) {
decoder->error(pc, pc + offset, "invalid block type");
return;
}
}
}
}
// Decode a byte representing a local type. Return {false} if the encoded
// byte was invalid or {kMultivalBlock}.
bool decode_local_type(uint8_t val, LocalType* result) {
switch (static_cast<LocalTypeCode>(val)) {
case kLocalVoid:
*result = kAstStmt;
return true;
case kLocalI32:
*result = kAstI32;
return true;
case kLocalI64:
*result = kAstI64;
return true;
case kLocalF32:
*result = kAstF32;
return true;
case kLocalF64:
*result = kAstF64;
return true;
default:
*result = kAstStmt;
return false;
}
}
LocalType read_entry(unsigned index) {
DCHECK_LT(index, arity);
LocalType result;
CHECK(decode_local_type(types[index], &result));
return result;
}
};
struct Control; struct Control;
struct BreakDepthOperand { struct BreakDepthOperand {
uint32_t arity;
uint32_t depth; uint32_t depth;
Control* target; Control* target;
unsigned length; unsigned length;
inline BreakDepthOperand(Decoder* decoder, const byte* pc) { inline BreakDepthOperand(Decoder* decoder, const byte* pc) {
depth = decoder->checked_read_u32v(pc, 1, &length, "break depth"); 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;
target = nullptr; target = nullptr;
} }
}; };
struct CallIndirectOperand { struct CallIndirectOperand {
uint32_t arity;
uint32_t index; uint32_t index;
FunctionSig* sig; FunctionSig* sig;
unsigned length; unsigned length;
inline CallIndirectOperand(Decoder* decoder, const byte* pc) { inline CallIndirectOperand(Decoder* decoder, const byte* pc) {
unsigned len1 = 0; unsigned len1 = 0;
unsigned len2 = 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"); index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "signature index");
length = len1 + len2; length = len1 + len2;
sig = nullptr; sig = nullptr;
@ -194,32 +121,59 @@ struct CallIndirectOperand {
}; };
struct CallFunctionOperand { struct CallFunctionOperand {
uint32_t arity;
uint32_t index; uint32_t index;
FunctionSig* sig; FunctionSig* sig;
unsigned length; unsigned length;
inline CallFunctionOperand(Decoder* decoder, const byte* pc) { inline CallFunctionOperand(Decoder* decoder, const byte* pc) {
unsigned len1 = 0; unsigned len1 = 0;
unsigned len2 = 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"); index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "function index");
length = len1 + len2; length = len1 + len2;
sig = nullptr; sig = nullptr;
} }
}; };
struct BranchTableOperand { struct CallImportOperand {
uint32_t table_count; uint32_t arity;
const byte* start; uint32_t index;
const byte* table; FunctionSig* sig;
inline BranchTableOperand(Decoder* decoder, const byte* pc) { unsigned length;
DCHECK_EQ(kExprBrTable, decoder->checked_read_u8(pc, 0, "opcode")); inline CallImportOperand(Decoder* decoder, const byte* pc) {
start = pc + 1;
unsigned len1 = 0; unsigned len1 = 0;
table_count = decoder->checked_read_u32v(pc, 1, &len1, "table count"); 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* table;
unsigned length;
inline BranchTableOperand(Decoder* decoder, const byte* pc) {
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");
if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 || if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 ||
len1 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) { len1 + len2 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) {
decoder->error(pc, "branch table size overflow"); decoder->error(pc, "branch table size overflow");
} }
table = pc + 1 + len1; length = len1 + len2 + (table_count + 1) * sizeof(uint32_t);
uint32_t table_start = 1 + len1 + len2;
if (decoder->check(pc, table_start, (table_count + 1) * sizeof(uint32_t),
"expected <table entries>")) {
table = pc + table_start;
} else {
table = nullptr;
}
} }
inline uint32_t read_entry(Decoder* decoder, unsigned i) { inline uint32_t read_entry(Decoder* decoder, unsigned i) {
DCHECK(i <= table_count); DCHECK(i <= table_count);
@ -227,43 +181,6 @@ struct BranchTableOperand {
} }
}; };
// A helper to iterate over a branch table.
class BranchTableIterator {
public:
unsigned cur_index() { return index_; }
bool has_next() { return index_ <= table_count_; }
uint32_t next() {
DCHECK(has_next());
index_++;
unsigned length = 0;
uint32_t result =
decoder_->checked_read_u32v(pc_, 0, &length, "branch table entry");
pc_ += length;
return result;
}
// length, including the length of the {BranchTableOperand}, but not the
// opcode.
unsigned length() {
while (has_next()) next();
return static_cast<unsigned>(pc_ - start_);
}
const byte* pc() { return pc_; }
BranchTableIterator(Decoder* decoder, BranchTableOperand& operand)
: decoder_(decoder),
start_(operand.start),
pc_(operand.table),
index_(0),
table_count_(operand.table_count) {}
private:
Decoder* decoder_;
const byte* start_;
const byte* pc_;
uint32_t index_; // the current index.
uint32_t table_count_; // the count of entries, not including default.
};
struct MemoryAccessOperand { struct MemoryAccessOperand {
uint32_t alignment; uint32_t alignment;
uint32_t offset; uint32_t offset;
@ -286,6 +203,15 @@ 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; typedef compiler::WasmGraphBuilder TFBuilder;
struct ModuleEnv; // forward declaration of module interface. struct ModuleEnv; // forward declaration of module interface.
@ -358,6 +284,9 @@ BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
// Computes the length of the opcode at the given address. // Computes the length of the opcode at the given address.
unsigned OpcodeLength(const byte* pc, const byte* end); 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. // A simple forward iterator for bytecodes.
class BytecodeIterator : public Decoder { class BytecodeIterator : public Decoder {
public: public:

View File

@ -208,19 +208,6 @@ class Decoder {
// Consume {size} bytes and send them to the bit bucket, advancing {pc_}. // Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
void consume_bytes(int size) { void consume_bytes(int size) {
TRACE(" +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), "skip",
size);
if (checkAvailable(size)) {
pc_ += size;
} else {
pc_ = limit_;
}
}
// Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
void consume_bytes(uint32_t size, const char* name = "skip") {
TRACE(" +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_), name,
size);
if (checkAvailable(size)) { if (checkAvailable(size)) {
pc_ += size; pc_ += size;
} else { } else {

View File

@ -30,11 +30,15 @@ namespace v8 {
namespace internal { namespace internal {
namespace wasm { namespace wasm {
// Emit a section code and the size as a padded varint that can be patched // Emit a section name and the size as a padded varint that can be patched
// later. // later.
size_t EmitSection(WasmSectionCode code, ZoneBuffer& buffer) { size_t EmitSection(WasmSection::Code code, ZoneBuffer& buffer) {
// Emit the section code. // Emit the section name.
buffer.write_u8(code); const char* name = WasmSection::getName(code);
TRACE("emit section: %s\n", name);
size_t length = WasmSection::getNameLength(code);
buffer.write_size(length); // Section name string size.
buffer.write(reinterpret_cast<const byte*>(name), length);
// Emit a placeholder for the length. // Emit a placeholder for the length.
return buffer.reserve_u32v(); return buffer.reserve_u32v();
@ -51,8 +55,6 @@ WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
locals_(builder->zone()), locals_(builder->zone()),
signature_index_(0), signature_index_(0),
exported_(0), exported_(0),
func_index_(static_cast<uint32_t>(builder->imports_.size() +
builder->functions_.size())),
body_(builder->zone()), body_(builder->zone()),
name_(builder->zone()), name_(builder->zone()),
i32_temps_(builder->zone()), i32_temps_(builder->zone()),
@ -88,10 +90,6 @@ void WasmFunctionBuilder::EmitSetLocal(uint32_t local_index) {
EmitWithVarInt(kExprSetLocal, 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) { void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size) {
for (size_t i = 0; i < code_size; ++i) { for (size_t i = 0; i < code_size; ++i) {
body_.push_back(code[i]); body_.push_back(code[i]);
@ -145,14 +143,14 @@ void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
buffer.write_u32v(signature_index_); buffer.write_u32v(signature_index_);
} }
void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer) const { void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer,
uint32_t func_index) const {
if (exported_) { if (exported_) {
buffer.write_u32v(func_index);
buffer.write_size(name_.size()); buffer.write_size(name_.size());
if (name_.size() > 0) { if (name_.size() > 0) {
buffer.write(reinterpret_cast<const byte*>(&name_[0]), name_.size()); buffer.write(reinterpret_cast<const byte*>(&name_[0]), name_.size());
} }
buffer.write_u8(kExternalFunction);
buffer.write_u32v(func_index_);
} }
} }
@ -177,10 +175,7 @@ WasmDataSegmentEncoder::WasmDataSegmentEncoder(Zone* zone, const byte* data,
} }
void WasmDataSegmentEncoder::Write(ZoneBuffer& buffer) const { void WasmDataSegmentEncoder::Write(ZoneBuffer& buffer) const {
buffer.write_u8(0); // linear memory zero
buffer.write_u8(kExprI32Const);
buffer.write_u32v(dest_); buffer.write_u32v(dest_);
buffer.write_u8(kExprEnd);
buffer.write_u32v(static_cast<uint32_t>(data_.size())); buffer.write_u32v(static_cast<uint32_t>(data_.size()));
buffer.write(&data_[0], data_.size()); buffer.write(&data_[0], data_.size());
} }
@ -196,11 +191,17 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
signature_map_(zone), signature_map_(zone),
start_function_index_(-1) {} start_function_index_(-1) {}
WasmFunctionBuilder* WasmModuleBuilder::AddFunction(FunctionSig* sig) { uint32_t WasmModuleBuilder::AddFunction() {
functions_.push_back(new (zone_) WasmFunctionBuilder(this)); functions_.push_back(new (zone_) WasmFunctionBuilder(this));
// Add the signature if one was provided here. return static_cast<uint32_t>(functions_.size() - 1);
if (sig) functions_.back()->SetSignature(sig); }
return functions_.back();
WasmFunctionBuilder* WasmModuleBuilder::FunctionAt(size_t index) {
if (functions_.size() > index) {
return functions_.at(index);
} else {
return nullptr;
}
} }
void WasmModuleBuilder::AddDataSegment(WasmDataSegmentEncoder* data) { void WasmModuleBuilder::AddDataSegment(WasmDataSegmentEncoder* data) {
@ -242,18 +243,16 @@ void WasmModuleBuilder::AddIndirectFunction(uint32_t index) {
uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length, uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length,
FunctionSig* sig) { FunctionSig* sig) {
DCHECK_EQ(0, functions_.size()); // imports must be added before functions!
imports_.push_back({AddSignature(sig), name, name_length}); imports_.push_back({AddSignature(sig), name, name_length});
return static_cast<uint32_t>(imports_.size() - 1); return static_cast<uint32_t>(imports_.size() - 1);
} }
void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) { void WasmModuleBuilder::MarkStartFunction(uint32_t index) {
start_function_index_ = function->func_index(); start_function_index_ = index;
} }
uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported, uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported) {
bool mutability) { globals_.push_back(std::make_pair(type, exported));
globals_.push_back(std::make_tuple(type, exported, mutability));
return static_cast<uint32_t>(globals_.size() - 1); return static_cast<uint32_t>(globals_.size() - 1);
} }
@ -267,7 +266,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
// == Emit signatures ======================================================== // == Emit signatures ========================================================
if (signatures_.size() > 0) { if (signatures_.size() > 0) {
size_t start = EmitSection(kTypeSectionCode, buffer); size_t start = EmitSection(WasmSection::Code::Signatures, buffer);
buffer.write_size(signatures_.size()); buffer.write_size(signatures_.size());
for (FunctionSig* sig : signatures_) { for (FunctionSig* sig : signatures_) {
@ -284,130 +283,86 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
FixupSection(buffer, start); 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 =========================================================== // == Emit imports ===========================================================
if (imports_.size() > 0) { if (imports_.size() > 0) {
size_t start = EmitSection(kImportSectionCode, buffer); size_t start = EmitSection(WasmSection::Code::ImportTable, buffer);
buffer.write_size(imports_.size()); buffer.write_size(imports_.size());
for (auto import : imports_) { for (auto import : imports_) {
buffer.write_u32v(import.name_length); // module name length
buffer.write(reinterpret_cast<const byte*>(import.name), // module name
import.name_length);
buffer.write_u32v(0); // field name length
buffer.write_u8(kExternalFunction);
buffer.write_u32v(import.sig_index); buffer.write_u32v(import.sig_index);
buffer.write_u32v(import.name_length);
buffer.write(reinterpret_cast<const byte*>(import.name),
import.name_length);
buffer.write_u32v(0);
} }
FixupSection(buffer, start); FixupSection(buffer, start);
} }
// == Emit function signatures =============================================== // == Emit function signatures ===============================================
bool has_names = false;
if (functions_.size() > 0) { if (functions_.size() > 0) {
size_t start = EmitSection(kFunctionSectionCode, buffer); size_t start = EmitSection(WasmSection::Code::FunctionSignatures, buffer);
buffer.write_size(functions_.size()); buffer.write_size(functions_.size());
for (auto function : functions_) { for (auto function : functions_) {
function->WriteSignature(buffer); function->WriteSignature(buffer);
if (function->exported()) exports++; if (function->exported()) exports++;
if (function->name_.size() > 0) has_names = true;
} }
FixupSection(buffer, start); FixupSection(buffer, start);
} }
// == emit function table ==================================================== // == emit function table ====================================================
if (indirect_functions_.size() > 0) { if (indirect_functions_.size() > 0) {
size_t start = EmitSection(kTableSectionCode, buffer); size_t start = EmitSection(WasmSection::Code::FunctionTable, 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()); buffer.write_size(indirect_functions_.size());
for (auto index : indirect_functions_) {
buffer.write_u32v(index);
}
FixupSection(buffer, start); FixupSection(buffer, start);
} }
// == emit memory declaration ================================================ // == emit memory declaration ================================================
{ {
size_t start = EmitSection(kMemorySectionCode, buffer); size_t start = EmitSection(WasmSection::Code::Memory, buffer);
buffer.write_u8(1); // memory count
buffer.write_u32v(kResizableMaximumFlag);
buffer.write_u32v(16); // min memory size buffer.write_u32v(16); // min memory size
buffer.write_u32v(16); // max memory size buffer.write_u32v(16); // max memory size
FixupSection(buffer, start); buffer.write_u8(0); // memory export
} static_assert(kDeclMemorySize == 3, "memory size must match emit above");
// == 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<kLocalTypeIndex>(global)));
buffer.write_u8(std::get<kMutabilityIndex>(global));
switch (std::get<kLocalTypeIndex>(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); FixupSection(buffer, start);
} }
// == emit exports =========================================================== // == emit exports ===========================================================
if (exports > 0) { if (exports > 0) {
size_t start = EmitSection(kExportSectionCode, buffer); size_t start = EmitSection(WasmSection::Code::ExportTable, buffer);
buffer.write_u32v(exports); buffer.write_u32v(exports);
for (auto function : functions_) function->WriteExport(buffer); uint32_t index = 0;
for (auto function : functions_) {
function->WriteExport(buffer, index++);
}
FixupSection(buffer, start); FixupSection(buffer, start);
} }
// == emit start function index ============================================== // == emit start function index ==============================================
if (start_function_index_ >= 0) { if (start_function_index_ >= 0) {
size_t start = EmitSection(kStartSectionCode, buffer); size_t start = EmitSection(WasmSection::Code::StartFunction, buffer);
buffer.write_u32v(start_function_index_); buffer.write_u32v(start_function_index_);
FixupSection(buffer, start); 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 ============================================================== // == emit code ==============================================================
if (functions_.size() > 0) { if (functions_.size() > 0) {
size_t start = EmitSection(kCodeSectionCode, buffer); size_t start = EmitSection(WasmSection::Code::FunctionBodies, buffer);
buffer.write_size(functions_.size()); buffer.write_size(functions_.size());
for (auto function : functions_) { for (auto function : functions_) {
function->WriteBody(buffer); function->WriteBody(buffer);
@ -417,7 +372,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
// == emit data segments ===================================================== // == emit data segments =====================================================
if (data_segments_.size() > 0) { if (data_segments_.size() > 0) {
size_t start = EmitSection(kDataSectionCode, buffer); size_t start = EmitSection(WasmSection::Code::DataSegments, buffer);
buffer.write_size(data_segments_.size()); buffer.write_size(data_segments_.size());
for (auto segment : data_segments_) { for (auto segment : data_segments_) {
@ -425,28 +380,6 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
} }
FixupSection(buffer, start); FixupSection(buffer, start);
} }
// == Emit names =============================================================
if (has_names) {
// Emit the section code.
buffer.write_u8(kUnknownSectionCode);
// Emit a placeholder for the length.
size_t start = buffer.reserve_u32v();
// Emit the section string.
buffer.write_size(4);
buffer.write(reinterpret_cast<const byte*>("name"), 4);
// Emit the names.
buffer.write_size(functions_.size());
for (auto function : functions_) {
buffer.write_size(function->name_.size());
if (function->name_.size() > 0) {
buffer.write(reinterpret_cast<const byte*>(&function->name_[0]),
function->name_.size());
}
buffer.write_u8(0);
}
FixupSection(buffer, start);
}
} }
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal

View File

@ -121,21 +121,18 @@ class WasmFunctionBuilder : public ZoneObject {
void Emit(WasmOpcode opcode); void Emit(WasmOpcode opcode);
void EmitGetLocal(uint32_t index); void EmitGetLocal(uint32_t index);
void EmitSetLocal(uint32_t index); void EmitSetLocal(uint32_t index);
void EmitTeeLocal(uint32_t index);
void EmitI32Const(int32_t val); void EmitI32Const(int32_t val);
void EmitWithU8(WasmOpcode opcode, const byte immediate); void EmitWithU8(WasmOpcode opcode, const byte immediate);
void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2); void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate); void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate);
void SetExported(); void SetExported();
void SetName(const char* name, int name_length); void SetName(const char* name, int name_length);
void WriteSignature(ZoneBuffer& buffer) const;
void WriteExport(ZoneBuffer& buffer) const;
void WriteBody(ZoneBuffer& buffer) const;
bool exported() { return exported_; } bool exported() { return exported_; }
uint32_t func_index() { return func_index_; }
FunctionSig* signature(); // Writing methods.
void WriteSignature(ZoneBuffer& buffer) const;
void WriteExport(ZoneBuffer& buffer, uint32_t func_index) const;
void WriteBody(ZoneBuffer& buffer) const;
private: private:
explicit WasmFunctionBuilder(WasmModuleBuilder* builder); explicit WasmFunctionBuilder(WasmModuleBuilder* builder);
@ -145,7 +142,6 @@ class WasmFunctionBuilder : public ZoneObject {
LocalDeclEncoder locals_; LocalDeclEncoder locals_;
uint32_t signature_index_; uint32_t signature_index_;
bool exported_; bool exported_;
uint32_t func_index_;
ZoneVector<uint8_t> body_; ZoneVector<uint8_t> body_;
ZoneVector<char> name_; ZoneVector<char> name_;
ZoneVector<uint32_t> i32_temps_; ZoneVector<uint32_t> i32_temps_;
@ -216,17 +212,14 @@ class WasmModuleBuilder : public ZoneObject {
explicit WasmModuleBuilder(Zone* zone); explicit WasmModuleBuilder(Zone* zone);
// Building methods. // Building methods.
uint32_t AddImport(const char* name, int name_length, FunctionSig* sig); uint32_t AddFunction();
void SetImportName(uint32_t index, const char* name, int name_length) { uint32_t AddGlobal(LocalType type, bool exported);
imports_[index].name = name; WasmFunctionBuilder* FunctionAt(size_t index);
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); void AddDataSegment(WasmDataSegmentEncoder* data);
uint32_t AddSignature(FunctionSig* sig); uint32_t AddSignature(FunctionSig* sig);
void AddIndirectFunction(uint32_t index); void AddIndirectFunction(uint32_t index);
void MarkStartFunction(WasmFunctionBuilder* builder); void MarkStartFunction(uint32_t index);
uint32_t AddImport(const char* name, int name_length, FunctionSig* sig);
// Writing methods. // Writing methods.
void WriteTo(ZoneBuffer& buffer) const; void WriteTo(ZoneBuffer& buffer) const;
@ -238,25 +231,18 @@ class WasmModuleBuilder : public ZoneObject {
Zone* zone() { return zone_; } Zone* zone() { return zone_; }
FunctionSig* GetSignature(uint32_t index) { return signatures_[index]; }
private: private:
friend class WasmFunctionBuilder;
Zone* zone_; Zone* zone_;
ZoneVector<FunctionSig*> signatures_; ZoneVector<FunctionSig*> signatures_;
ZoneVector<WasmFunctionImport> imports_; ZoneVector<WasmFunctionImport> imports_;
ZoneVector<WasmFunctionBuilder*> functions_; ZoneVector<WasmFunctionBuilder*> functions_;
ZoneVector<WasmDataSegmentEncoder*> data_segments_; ZoneVector<WasmDataSegmentEncoder*> data_segments_;
ZoneVector<uint32_t> indirect_functions_; ZoneVector<uint32_t> indirect_functions_;
ZoneVector<std::tuple<LocalType, bool, bool>> globals_; ZoneVector<std::pair<LocalType, bool>> globals_;
SignatureMap signature_map_; SignatureMap signature_map_;
int start_function_index_; int start_function_index_;
}; };
inline FunctionSig* WasmFunctionBuilder::signature() {
return builder_->signatures_[signature_index_];
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

File diff suppressed because it is too large Load Diff

View File

@ -30,11 +30,8 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, ModuleEnv* env,
// Extracts the function offset table from the wasm module bytes. // Extracts the function offset table from the wasm module bytes.
// Returns a vector with <offset, length> entries, or failure if the wasm bytes // Returns a vector with <offset, length> entries, or failure if the wasm bytes
// are detected as invalid. Note that this validation is not complete. // are detected as invalid. Note that this validation is not complete.
FunctionOffsetsResult DecodeWasmFunctionOffsets( FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
const byte* module_start, const byte* module_end, const byte* module_end);
uint32_t num_imported_functions);
WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end);
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal

View File

@ -32,15 +32,11 @@ ByteArray *GetOrCreateFunctionOffsetTable(Handle<WasmDebugInfo> debug_info) {
FunctionOffsetsResult function_offsets; FunctionOffsetsResult function_offsets;
{ {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
Handle<JSObject> wasm_object(debug_info->wasm_object(), isolate);
uint32_t num_imported_functions =
wasm::GetNumImportedFunctions(wasm_object);
SeqOneByteString *wasm_bytes = SeqOneByteString *wasm_bytes =
wasm::GetWasmBytes(debug_info->wasm_object()); wasm::GetWasmBytes(debug_info->wasm_object());
const byte *bytes_start = wasm_bytes->GetChars(); const byte *bytes_start = wasm_bytes->GetChars();
const byte *bytes_end = bytes_start + wasm_bytes->length(); 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()); DCHECK(function_offsets.ok());
size_t array_size = 2 * kIntSize * function_offsets.val.size(); size_t array_size = 2 * kIntSize * function_offsets.val.size();

View File

@ -722,38 +722,54 @@ class ControlTransfers : public ZoneObject {
public: public:
ControlTransferMap map_; ControlTransferMap map_;
ControlTransfers(Zone* zone, ModuleEnv* env, AstLocalDecls* locals, ControlTransfers(Zone* zone, size_t locals_encoded_size, const byte* start,
const byte* start, const byte* end) const byte* end)
: map_(zone) { : 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. // Represents a control flow label.
struct CLabel : public ZoneObject { struct CLabel : public ZoneObject {
const byte* target; const byte* target;
ZoneVector<const byte*> refs; size_t value_depth;
ZoneVector<CRef> refs;
explicit CLabel(Zone* zone) : target(nullptr), refs(zone) {} CLabel(Zone* zone, size_t v)
: target(nullptr), value_depth(v), refs(zone) {}
// Bind this label to the given PC. // Bind this label to the given PC.
void Bind(ControlTransferMap* map, const byte* start, const byte* pc) { void Bind(ControlTransferMap* map, const byte* start, const byte* pc,
bool expect_value) {
DCHECK_NULL(target); DCHECK_NULL(target);
target = pc; target = pc;
for (auto from_pc : refs) { for (auto from : refs) {
auto pcdiff = static_cast<pcdiff_t>(target - from_pc); auto pcdiff = static_cast<pcdiff_t>(target - from.pc);
size_t offset = static_cast<size_t>(from_pc - start); auto spdiff = static_cast<spdiff_t>(from.value_depth - value_depth);
(*map)[offset] = pcdiff; ControlTransfer::StackAction action = ControlTransfer::kNoAction;
if (expect_value && !from.explicit_value) {
action = spdiff == 0 ? ControlTransfer::kPushVoid
: ControlTransfer::kPopAndRepush;
}
pc_t offset = static_cast<size_t>(from.pc - start);
(*map)[offset] = {pcdiff, spdiff, action};
} }
} }
// Reference this label from the given location. // Reference this label from the given location.
void Ref(ControlTransferMap* map, const byte* start, void Ref(ControlTransferMap* map, const byte* start, CRef from) {
const byte* from_pc) { DCHECK_GE(from.value_depth, value_depth);
if (target) { if (target) {
// Target being bound before a reference means this is a loop. auto pcdiff = static_cast<pcdiff_t>(target - from.pc);
DCHECK_EQ(kExprLoop, *target); auto spdiff = static_cast<spdiff_t>(from.value_depth - value_depth);
auto pcdiff = static_cast<pcdiff_t>(target - from_pc); pc_t offset = static_cast<size_t>(from.pc - start);
size_t offset = static_cast<size_t>(from_pc - start); (*map)[offset] = {pcdiff, spdiff, ControlTransfer::kNoAction};
(*map)[offset] = pcdiff;
} else { } else {
refs.push_back(from_pc); refs.push_back(from);
} }
} }
}; };
@ -764,104 +780,122 @@ class ControlTransfers : public ZoneObject {
CLabel* end_label; CLabel* end_label;
CLabel* else_label; CLabel* else_label;
void Ref(ControlTransferMap* map, const byte* start, void Ref(ControlTransferMap* map, const byte* start, const byte* from_pc,
const byte* from_pc) { size_t from_value_depth, bool explicit_value) {
end_label->Ref(map, start, from_pc); end_label->Ref(map, start, {from_pc, from_value_depth, explicit_value});
} }
}; };
// Compute the ControlTransfer map. // Compute the ControlTransfer map.
// This algorithm maintains a stack of control constructs similar to the // This works by maintaining a stack of control constructs similar to the
// AST decoder. The {control_stack} allows matching {br,br_if,br_table} // AST decoder. The {control_stack} allows matching {br,br_if,br_table}
// bytecodes with their target, as well as determining whether the current // bytecodes with their target, as well as determining whether the current
// bytecodes are within the true or false block of an else. // bytecodes are within the true or false block of an else.
// The value stack depth is tracked as {value_depth} and is needed to
// determine how many values to pop off the stack for explicit and
// implicit control flow.
std::vector<Control> control_stack; std::vector<Control> control_stack;
CLabel* func_label = new (zone) CLabel(zone); size_t value_depth = 0;
control_stack.push_back({start, func_label, nullptr}); for (BytecodeIterator i(start + locals_encoded_size, end); i.has_next();
for (BytecodeIterator i(start, end, locals); i.has_next(); i.next()) { i.next()) {
WasmOpcode opcode = i.current(); WasmOpcode opcode = i.current();
TRACE("@%u: control %s\n", i.pc_offset(), TRACE("@%u: control %s (depth = %zu)\n", i.pc_offset(),
WasmOpcodes::OpcodeName(opcode)); WasmOpcodes::OpcodeName(opcode), value_depth);
switch (opcode) { switch (opcode) {
case kExprBlock: { case kExprBlock: {
TRACE("control @%u: Block\n", i.pc_offset()); TRACE("control @%u $%zu: Block\n", i.pc_offset(), value_depth);
CLabel* label = new (zone) CLabel(zone); CLabel* label = new (zone) CLabel(zone, value_depth);
control_stack.push_back({i.pc(), label, nullptr}); control_stack.push_back({i.pc(), label, nullptr});
break; break;
} }
case kExprLoop: { case kExprLoop: {
TRACE("control @%u: Loop\n", i.pc_offset()); TRACE("control @%u $%zu: Loop\n", i.pc_offset(), value_depth);
CLabel* label = new (zone) CLabel(zone); CLabel* label1 = new (zone) CLabel(zone, value_depth);
control_stack.push_back({i.pc(), label, nullptr}); CLabel* label2 = new (zone) CLabel(zone, value_depth);
label->Bind(&map_, start, i.pc()); control_stack.push_back({i.pc(), label1, nullptr});
control_stack.push_back({i.pc(), label2, nullptr});
label2->Bind(&map_, start, i.pc(), false);
break; break;
} }
case kExprIf: { case kExprIf: {
TRACE("control @%u: If\n", i.pc_offset()); TRACE("control @%u $%zu: If\n", i.pc_offset(), value_depth);
CLabel* end_label = new (zone) CLabel(zone); value_depth--;
CLabel* else_label = new (zone) CLabel(zone); CLabel* end_label = new (zone) CLabel(zone, value_depth);
CLabel* else_label = new (zone) CLabel(zone, value_depth);
control_stack.push_back({i.pc(), end_label, else_label}); control_stack.push_back({i.pc(), end_label, else_label});
else_label->Ref(&map_, start, i.pc()); else_label->Ref(&map_, start, {i.pc(), value_depth, false});
break; break;
} }
case kExprElse: { case kExprElse: {
Control* c = &control_stack.back(); Control* c = &control_stack.back();
TRACE("control @%u: Else\n", i.pc_offset()); TRACE("control @%u $%zu: Else\n", i.pc_offset(), value_depth);
c->end_label->Ref(&map_, start, i.pc()); c->end_label->Ref(&map_, start, {i.pc(), value_depth, false});
value_depth = c->end_label->value_depth;
DCHECK_NOT_NULL(c->else_label); DCHECK_NOT_NULL(c->else_label);
c->else_label->Bind(&map_, start, i.pc() + 1); c->else_label->Bind(&map_, start, i.pc() + 1, false);
c->else_label = nullptr; c->else_label = nullptr;
break; break;
} }
case kExprEnd: { case kExprEnd: {
Control* c = &control_stack.back(); Control* c = &control_stack.back();
TRACE("control @%u: End\n", i.pc_offset()); TRACE("control @%u $%zu: End\n", i.pc_offset(), value_depth);
if (c->end_label->target) { if (c->end_label->target) {
// only loops have bound labels. // only loops have bound labels.
DCHECK_EQ(kExprLoop, *c->pc); DCHECK_EQ(kExprLoop, *c->pc);
} else { control_stack.pop_back();
if (c->else_label) c->else_label->Bind(&map_, start, i.pc()); c = &control_stack.back();
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(); control_stack.pop_back();
break; break;
} }
case kExprBr: { case kExprBr: {
BreakDepthOperand operand(&i, i.pc()); BreakDepthOperand operand(&i, i.pc());
TRACE("control @%u: Br[depth=%u]\n", i.pc_offset(), operand.depth); TRACE("control @%u $%zu: Br[arity=%u, depth=%u]\n", i.pc_offset(),
Control* c = &control_stack[control_stack.size() - operand.depth - 1]; value_depth, operand.arity, operand.depth);
c->Ref(&map_, start, i.pc()); value_depth -= operand.arity;
control_stack[control_stack.size() - operand.depth - 1].Ref(
&map_, start, i.pc(), value_depth, operand.arity > 0);
value_depth++;
break; break;
} }
case kExprBrIf: { case kExprBrIf: {
BreakDepthOperand operand(&i, i.pc()); BreakDepthOperand operand(&i, i.pc());
TRACE("control @%u: BrIf[depth=%u]\n", i.pc_offset(), operand.depth); TRACE("control @%u $%zu: BrIf[arity=%u, depth=%u]\n", i.pc_offset(),
Control* c = &control_stack[control_stack.size() - operand.depth - 1]; value_depth, operand.arity, operand.depth);
c->Ref(&map_, start, i.pc()); 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++;
break; break;
} }
case kExprBrTable: { case kExprBrTable: {
BranchTableOperand operand(&i, i.pc()); BranchTableOperand operand(&i, i.pc());
BranchTableIterator iterator(&i, operand); TRACE("control @%u $%zu: BrTable[arity=%u count=%u]\n", i.pc_offset(),
TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(), value_depth, operand.arity, operand.table_count);
operand.table_count); value_depth -= (operand.arity + 1);
while (iterator.has_next()) { for (uint32_t j = 0; j < operand.table_count + 1; ++j) {
uint32_t j = iterator.cur_index(); uint32_t target = operand.read_entry(&i, j);
uint32_t target = iterator.next(); control_stack[control_stack.size() - target - 1].Ref(
Control* c = &control_stack[control_stack.size() - target - 1]; &map_, start, i.pc() + j, value_depth, operand.arity > 0);
c->Ref(&map_, start, i.pc() + j);
} }
value_depth++;
break; break;
} }
default: { default: {
value_depth = value_depth - OpcodeArity(i.pc(), end) + 1;
break; break;
} }
} }
} }
if (!func_label->target) func_label->Bind(&map_, start, end);
} }
pcdiff_t Lookup(pc_t from) { ControlTransfer Lookup(pc_t from) {
auto result = map_.find(from); auto result = map_.find(from);
if (result == map_.end()) { if (result == map_.end()) {
V8_Fatal(__FILE__, __LINE__, "no control target for pc %zu", from); V8_Fatal(__FILE__, __LINE__, "no control target for pc %zu", from);
@ -931,9 +965,9 @@ class CodeMap {
if (code->targets == nullptr && code->start) { if (code->targets == nullptr && code->start) {
// Compute the control targets map and the local declarations. // Compute the control targets map and the local declarations.
CHECK(DecodeLocalDecls(code->locals, code->start, code->end)); CHECK(DecodeLocalDecls(code->locals, code->start, code->end));
ModuleEnv env = {module_, nullptr, kWasmOrigin}; code->targets =
code->targets = new (zone_) ControlTransfers( new (zone_) ControlTransfers(zone_, code->locals.decls_encoded_size,
zone_, &env, &code->locals, code->orig_start, code->orig_end); code->orig_start, code->orig_end);
} }
return code; return code;
} }
@ -972,7 +1006,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
instance_(instance), instance_(instance),
stack_(zone), stack_(zone),
frames_(zone), frames_(zone),
blocks_(zone),
state_(WasmInterpreter::STOPPED), state_(WasmInterpreter::STOPPED),
break_pc_(kInvalidPc), break_pc_(kInvalidPc),
trap_reason_(kTrapCount) {} trap_reason_(kTrapCount) {}
@ -993,9 +1026,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
stack_.push_back(args[i]); stack_.push_back(args[i]);
} }
frames_.back().ret_pc = InitLocals(code); frames_.back().ret_pc = InitLocals(code);
blocks_.push_back(
{0, stack_.size(), frames_.size(),
static_cast<uint32_t>(code->function->sig->return_count())});
TRACE(" => PushFrame(#%u @%zu)\n", code->function->func_index, TRACE(" => PushFrame(#%u @%zu)\n", code->function->func_index,
frames_.back().ret_pc); frames_.back().ret_pc);
} }
@ -1044,11 +1074,11 @@ class ThreadImpl : public WasmInterpreter::Thread {
return nullptr; return nullptr;
} }
virtual WasmVal GetReturnValue(int index) { virtual WasmVal GetReturnValue() {
if (state_ == WasmInterpreter::TRAPPED) return WasmVal(0xdeadbeef); if (state_ == WasmInterpreter::TRAPPED) return WasmVal(0xdeadbeef);
CHECK_EQ(WasmInterpreter::FINISHED, state_); CHECK_EQ(WasmInterpreter::FINISHED, state_);
CHECK_LT(static_cast<size_t>(index), stack_.size()); CHECK_EQ(1, stack_.size());
return stack_[index]; return stack_[0];
} }
virtual pc_t GetBreakpointPc() { return break_pc_; } virtual pc_t GetBreakpointPc() { return break_pc_; }
@ -1072,18 +1102,10 @@ class ThreadImpl : public WasmInterpreter::Thread {
sp_t llimit() { return plimit() + code->locals.total_local_count; } 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_; CodeMap* codemap_;
WasmModuleInstance* instance_; WasmModuleInstance* instance_;
ZoneVector<WasmVal> stack_; ZoneVector<WasmVal> stack_;
ZoneVector<Frame> frames_; ZoneVector<Frame> frames_;
ZoneVector<Block> blocks_;
WasmInterpreter::State state_; WasmInterpreter::State state_;
pc_t break_pc_; pc_t break_pc_;
TrapReason trap_reason_; TrapReason trap_reason_;
@ -1108,9 +1130,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
DCHECK_GE(stack_.size(), arity); DCHECK_GE(stack_.size(), arity);
// The parameters will overlap the arguments already on the stack. // The parameters will overlap the arguments already on the stack.
frames_.push_back({code, 0, 0, stack_.size() - arity}); frames_.push_back({code, 0, 0, stack_.size() - arity});
blocks_.push_back(
{0, stack_.size(), frames_.size(),
static_cast<uint32_t>(code->function->sig->return_count())});
frames_.back().ret_pc = InitLocals(code); frames_.back().ret_pc = InitLocals(code);
TRACE(" => push func#%u @%zu\n", code->function->func_index, TRACE(" => push func#%u @%zu\n", code->function->func_index,
frames_.back().ret_pc); frames_.back().ret_pc);
@ -1149,38 +1168,21 @@ class ThreadImpl : public WasmInterpreter::Thread {
bool SkipBreakpoint(InterpreterCode* code, pc_t pc) { bool SkipBreakpoint(InterpreterCode* code, pc_t pc) {
if (pc == break_pc_) { if (pc == break_pc_) {
// Skip the previously hit breakpoint when resuming.
break_pc_ = kInvalidPc; break_pc_ = kInvalidPc;
return true; return true;
} }
return false; return false;
} }
int LookupTarget(InterpreterCode* code, pc_t pc) { bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, WasmVal val) {
return static_cast<int>(code->targets->Lookup(pc));
}
int DoBreak(InterpreterCode* code, pc_t pc, size_t depth) {
size_t bp = blocks_.size() - depth - 1;
Block* target = &blocks_[bp];
DoStackTransfer(target->sp, target->arity);
blocks_.resize(bp);
return LookupTarget(code, pc);
}
bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, size_t arity) {
DCHECK_GT(frames_.size(), 0u); DCHECK_GT(frames_.size(), 0u);
// Pop all blocks for this frame. stack_.resize(frames_.back().sp);
while (!blocks_.empty() && blocks_.back().fp == frames_.size()) {
blocks_.pop_back();
}
sp_t dest = frames_.back().sp;
frames_.pop_back(); frames_.pop_back();
if (frames_.size() == 0) { if (frames_.size() == 0) {
// A return from the last frame terminates the execution. // A return from the top frame terminates the execution.
state_ = WasmInterpreter::FINISHED; state_ = WasmInterpreter::FINISHED;
DoStackTransfer(0, arity); stack_.clear();
stack_.push_back(val);
TRACE(" => finish\n"); TRACE(" => finish\n");
return false; return false;
} else { } else {
@ -1189,8 +1191,16 @@ class ThreadImpl : public WasmInterpreter::Thread {
*code = top->code; *code = top->code;
*pc = top->ret_pc; *pc = top->ret_pc;
*limit = top->code->end - top->code->start; *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); TRACE(" => pop func#%u @%zu\n", (*code)->function->func_index, *pc);
DoStackTransfer(dest, arity);
stack_.push_back(val);
return true; return true;
} }
} }
@ -1201,21 +1211,31 @@ class ThreadImpl : public WasmInterpreter::Thread {
*limit = target->end - target->start; *limit = target->end - target->start;
} }
// Copies {arity} values on the top of the stack down the stack to {dest}, // Adjust the program counter {pc} and the stack contents according to the
// dropping the values in-between. // code's precomputed control transfer map. Returns the different between
void DoStackTransfer(sp_t dest, size_t arity) { // the new pc and the old pc.
// before: |---------------| pop_count | arity | int DoControlTransfer(InterpreterCode* code, pc_t pc) {
// ^ 0 ^ dest ^ stack_.size() auto target = code->targets->Lookup(pc);
// switch (target.action) {
// after: |---------------| arity | case ControlTransfer::kNoAction:
// ^ 0 ^ stack_.size() TRACE(" action [sp-%u]\n", target.spdiff);
DCHECK_LE(dest, stack_.size()); PopN(target.spdiff);
DCHECK_LE(dest + arity, stack_.size()); break;
size_t pop_count = stack_.size() - dest - arity; case ControlTransfer::kPopAndRepush: {
for (size_t i = 0; i < arity; i++) { WasmVal val = Pop();
stack_[dest + i] = stack_[dest + pop_count + i]; 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;
} }
stack_.resize(stack_.size() - pop_count); return target.pcdiff;
} }
void Execute(InterpreterCode* code, pc_t pc, int max) { void Execute(InterpreterCode* code, pc_t pc, int max) {
@ -1231,8 +1251,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
if (pc >= limit) { if (pc >= limit) {
// Fell off end of code; do an implicit return. // Fell off end of code; do an implicit return.
TRACE("@%-3zu: ImplicitReturn\n", pc); TRACE("@%-3zu: ImplicitReturn\n", pc);
if (!DoReturn(&code, &pc, &limit, code->function->sig->return_count())) WasmVal val = PopArity(code->function->sig->return_count());
return; if (!DoReturn(&code, &pc, &limit, val)) return;
decoder.Reset(code->start, code->end); decoder.Reset(code->start, code->end);
continue; continue;
} }
@ -1265,37 +1285,27 @@ class ThreadImpl : public WasmInterpreter::Thread {
switch (orig) { switch (orig) {
case kExprNop: case kExprNop:
Push(pc, WasmVal());
break; 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: { case kExprLoop: {
BlockTypeOperand operand(&decoder, code->at(pc)); // Do nothing.
blocks_.push_back({pc, stack_.size(), frames_.size(), 0});
len = 1 + operand.length;
break; break;
} }
case kExprIf: { case kExprIf: {
BlockTypeOperand operand(&decoder, code->at(pc));
WasmVal cond = Pop(); WasmVal cond = Pop();
bool is_true = cond.to<uint32_t>() != 0; bool is_true = cond.to<uint32_t>() != 0;
blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity});
if (is_true) { if (is_true) {
// fall through to the true block. // fall through to the true block.
len = 1 + operand.length;
TRACE(" true => fallthrough\n"); TRACE(" true => fallthrough\n");
} else { } else {
len = LookupTarget(code, pc); len = DoControlTransfer(code, pc);
TRACE(" false => @%zu\n", pc + len); TRACE(" false => @%zu\n", pc + len);
} }
break; break;
} }
case kExprElse: { case kExprElse: {
blocks_.pop_back(); len = DoControlTransfer(code, pc);
len = LookupTarget(code, pc);
TRACE(" end => @%zu\n", pc + len); TRACE(" end => @%zu\n", pc + len);
break; break;
} }
@ -1308,34 +1318,42 @@ class ThreadImpl : public WasmInterpreter::Thread {
} }
case kExprBr: { case kExprBr: {
BreakDepthOperand operand(&decoder, code->at(pc)); BreakDepthOperand operand(&decoder, code->at(pc));
len = DoBreak(code, pc, operand.depth); WasmVal val = PopArity(operand.arity);
len = DoControlTransfer(code, pc);
TRACE(" br => @%zu\n", pc + len); TRACE(" br => @%zu\n", pc + len);
if (operand.arity > 0) Push(pc, val);
break; break;
} }
case kExprBrIf: { case kExprBrIf: {
BreakDepthOperand operand(&decoder, code->at(pc)); BreakDepthOperand operand(&decoder, code->at(pc));
WasmVal cond = Pop(); WasmVal cond = Pop();
WasmVal val = PopArity(operand.arity);
bool is_true = cond.to<uint32_t>() != 0; bool is_true = cond.to<uint32_t>() != 0;
if (is_true) { if (is_true) {
len = DoBreak(code, pc, operand.depth); len = DoControlTransfer(code, pc);
TRACE(" br_if => @%zu\n", pc + len); TRACE(" br_if => @%zu\n", pc + len);
if (operand.arity > 0) Push(pc, val);
} else { } else {
TRACE(" false => fallthrough\n"); TRACE(" false => fallthrough\n");
len = 1 + operand.length; len = 1 + operand.length;
Push(pc, WasmVal());
} }
break; break;
} }
case kExprBrTable: { case kExprBrTable: {
BranchTableOperand operand(&decoder, code->at(pc)); BranchTableOperand operand(&decoder, code->at(pc));
uint32_t key = Pop().to<uint32_t>(); uint32_t key = Pop().to<uint32_t>();
WasmVal val = PopArity(operand.arity);
if (key >= operand.table_count) key = operand.table_count; if (key >= operand.table_count) key = operand.table_count;
len = key + DoBreak(code, pc + key, operand.table[key]); len = DoControlTransfer(code, pc + key) + key;
TRACE(" br[%u] => @%zu\n", key, pc + key + len); TRACE(" br[%u] => @%zu\n", key, pc + len);
if (operand.arity > 0) Push(pc, val);
break; break;
} }
case kExprReturn: { case kExprReturn: {
size_t arity = code->function->sig->return_count(); ReturnArityOperand operand(&decoder, code->at(pc));
if (!DoReturn(&code, &pc, &limit, arity)) return; WasmVal val = PopArity(operand.arity);
if (!DoReturn(&code, &pc, &limit, val)) return;
decoder.Reset(code->start, code->end); decoder.Reset(code->start, code->end);
continue; continue;
} }
@ -1344,7 +1362,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
return CommitPc(pc); return CommitPc(pc);
} }
case kExprEnd: { case kExprEnd: {
blocks_.pop_back(); len = DoControlTransfer(code, pc);
DCHECK_EQ(1, len);
break; break;
} }
case kExprI8Const: { case kExprI8Const: {
@ -1384,13 +1403,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
break; break;
} }
case kExprSetLocal: { 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)); LocalIndexOperand operand(&decoder, code->at(pc));
WasmVal val = Pop(); WasmVal val = Pop();
stack_[frames_.back().sp + operand.index] = val; stack_[frames_.back().sp + operand.index] = val;
@ -1398,10 +1410,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
len = 1 + operand.length; len = 1 + operand.length;
break; break;
} }
case kExprDrop: {
Pop();
break;
}
case kExprCallFunction: { case kExprCallFunction: {
CallFunctionOperand operand(&decoder, code->at(pc)); CallFunctionOperand operand(&decoder, code->at(pc));
InterpreterCode* target = codemap()->GetCode(operand.index); InterpreterCode* target = codemap()->GetCode(operand.index);
@ -1412,7 +1420,9 @@ class ThreadImpl : public WasmInterpreter::Thread {
} }
case kExprCallIndirect: { case kExprCallIndirect: {
CallIndirectOperand operand(&decoder, code->at(pc)); CallIndirectOperand operand(&decoder, code->at(pc));
uint32_t entry_index = Pop().to<uint32_t>(); size_t index = stack_.size() - operand.arity - 1;
DCHECK_LT(index, stack_.size());
uint32_t entry_index = stack_[index].to<uint32_t>();
// Assume only one table for now. // Assume only one table for now.
DCHECK_LE(module()->function_tables.size(), 1u); DCHECK_LE(module()->function_tables.size(), 1u);
InterpreterCode* target = codemap()->GetIndirectCode(0, entry_index); InterpreterCode* target = codemap()->GetIndirectCode(0, entry_index);
@ -1427,6 +1437,10 @@ class ThreadImpl : public WasmInterpreter::Thread {
decoder.Reset(code->start, code->end); decoder.Reset(code->start, code->end);
continue; continue;
} }
case kExprCallImport: {
UNIMPLEMENTED();
break;
}
case kExprGetGlobal: { case kExprGetGlobal: {
GlobalIndexOperand operand(&decoder, code->at(pc)); GlobalIndexOperand operand(&decoder, code->at(pc));
const WasmGlobal* global = &module()->globals[operand.index]; const WasmGlobal* global = &module()->globals[operand.index];
@ -1465,6 +1479,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
} else { } else {
UNREACHABLE(); UNREACHABLE();
} }
Push(pc, val);
len = 1 + operand.length; len = 1 + operand.length;
break; break;
} }
@ -1513,6 +1528,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
} \ } \
byte* addr = instance()->mem_start + operand.offset + index; \ byte* addr = instance()->mem_start + operand.offset + index; \
WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>())); \ WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>())); \
Push(pc, val); \
len = 1 + operand.length; \ len = 1 + operand.length; \
break; \ break; \
} }
@ -1578,8 +1594,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
break; break;
} }
case kExprMemorySize: { case kExprMemorySize: {
Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size / Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size)));
WasmModule::kPageSize)));
break; break;
} }
#define EXECUTE_SIMPLE_BINOP(name, ctype, op) \ #define EXECUTE_SIMPLE_BINOP(name, ctype, op) \
@ -1654,7 +1669,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
void Push(pc_t pc, WasmVal val) { void Push(pc_t pc, WasmVal val) {
// TODO(titzer): store PC as well? // TODO(titzer): store PC as well?
if (val.type != kAstStmt) stack_.push_back(val); stack_.push_back(val);
} }
void TraceStack(const char* phase, pc_t pc) { void TraceStack(const char* phase, pc_t pc) {
@ -1835,7 +1850,7 @@ bool WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function,
ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting( ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
Zone* zone, const byte* start, const byte* end) { Zone* zone, const byte* start, const byte* end) {
ControlTransfers targets(zone, nullptr, nullptr, start, end); ControlTransfers targets(zone, 0, start, end);
return targets.map_; return targets.map_;
} }

View File

@ -28,7 +28,15 @@ typedef uint32_t spdiff_t;
const pc_t kInvalidPc = 0x80000000; const pc_t kInvalidPc = 0x80000000;
typedef ZoneMap<pc_t, pcdiff_t> ControlTransferMap; // Visible for testing. A {ControlTransfer} helps the interpreter figure out
// the target program counter and stack manipulations for a branch.
struct ControlTransfer {
enum StackAction { kNoAction, kPopAndRepush, kPushVoid };
pcdiff_t pcdiff; // adjustment to the program counter (positive or negative).
spdiff_t spdiff; // number of elements to pop off the stack.
StackAction action; // action to perform on the stack.
};
typedef ZoneMap<pc_t, ControlTransfer> ControlTransferMap;
// Macro for defining union members. // Macro for defining union members.
#define FOREACH_UNION_MEMBER(V) \ #define FOREACH_UNION_MEMBER(V) \
@ -124,7 +132,7 @@ class WasmInterpreter {
virtual int GetFrameCount() = 0; virtual int GetFrameCount() = 0;
virtual const WasmFrame* GetFrame(int index) = 0; virtual const WasmFrame* GetFrame(int index) = 0;
virtual WasmFrame* GetMutableFrame(int index) = 0; virtual WasmFrame* GetMutableFrame(int index) = 0;
virtual WasmVal GetReturnValue(int index = 0) = 0; virtual WasmVal GetReturnValue() = 0;
// Thread-specific breakpoints. // Thread-specific breakpoints.
bool SetBreakpoint(const WasmFunction* function, int pc, bool enabled); bool SetBreakpoint(const WasmFunction* function, int pc, bool enabled);
@ -181,8 +189,9 @@ class WasmInterpreter {
bool SetFunctionCodeForTesting(const WasmFunction* function, bool SetFunctionCodeForTesting(const WasmFunction* function,
const byte* start, const byte* end); const byte* start, const byte* end);
// Computes the control transfers for the given bytecode. Used internally in // Computes the control targets for the given bytecode as {pc offset, sp
// the interpreter, but exposed for testing. // offset}
// pairs. Used internally in the interpreter, but exposed for testing.
static ControlTransferMap ComputeControlTransfersForTesting(Zone* zone, static ControlTransferMap ComputeControlTransfersForTesting(Zone* zone,
const byte* start, const byte* start,
const byte* end); const byte* end);

View File

@ -160,7 +160,7 @@ i::MaybeHandle<i::JSObject> InstantiateModule(
} }
object = i::wasm::WasmModule::Instantiate( object = i::wasm::WasmModule::Instantiate(
isolate, thrower, module_object.ToHandleChecked(), ffi, memory); isolate, module_object.ToHandleChecked(), ffi, memory);
if (!object.is_null()) { if (!object.is_null()) {
args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
} }
@ -296,10 +296,10 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj); i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj)); memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
} }
i::MaybeHandle<i::JSObject> instance = i::wasm::WasmModule::Instantiate( i::MaybeHandle<i::JSObject> instance =
i_isolate, &thrower, module_obj, ffi, memory); i::wasm::WasmModule::Instantiate(i_isolate, module_obj, ffi, memory);
if (instance.is_null()) { if (instance.is_null()) {
if (!thrower.error()) thrower.Error("Could not instantiate module"); thrower.Error("Could not instantiate module");
return; return;
} }
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
@ -388,10 +388,10 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::JSFunction> table_ctor( i::Handle<i::JSFunction> table_cons(
i_isolate->native_context()->wasm_table_constructor()); i_isolate->native_context()->wasm_table_constructor());
i::Handle<i::JSObject> table_obj = i::Handle<i::JSObject> table_obj =
i_isolate->factory()->NewJSObject(table_ctor); i_isolate->factory()->NewJSObject(table_cons);
i::Handle<i::FixedArray> fixed_array = i::Handle<i::FixedArray> fixed_array =
i_isolate->factory()->NewFixedArray(initial); i_isolate->factory()->NewFixedArray(initial);
i::Object* null = i_isolate->heap()->null_value(); i::Object* null = i_isolate->heap()->null_value();
@ -406,7 +406,6 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(table_obj)); return_value.Set(Utils::ToLocal(table_obj));
} }
void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) { void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate(); v8::Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate); HandleScope scope(isolate);
@ -437,14 +436,23 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
} }
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::JSFunction> memory_cons(
i_isolate->native_context()->wasm_memory_constructor());
i::Handle<i::JSObject> memory_obj =
i_isolate->factory()->NewJSObject(memory_cons);
i::Handle<i::JSArrayBuffer> buffer = i::Handle<i::JSArrayBuffer> buffer =
i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared); i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared);
size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) * size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) *
static_cast<size_t>(initial); static_cast<size_t>(initial);
i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size); i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size);
memory_obj->SetInternalField(0, *buffer);
i::Handle<i::JSObject> memory_obj = i::WasmJs::CreateWasmMemoryObject( memory_obj->SetInternalField(
i_isolate, buffer, has_maximum, maximum); 1, has_maximum
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
: static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
i::Handle<i::Symbol> memory_sym(
i_isolate->native_context()->wasm_memory_sym());
i::Object::SetProperty(memory_obj, memory_sym, memory_obj, i::STRICT).Check();
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(memory_obj)); return_value.Set(Utils::ToLocal(memory_obj));
} }
@ -484,24 +492,6 @@ void WebAssemblyMemoryGetBuffer(
} }
} // namespace } // namespace
i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject(
i::Isolate* i_isolate, i::Handle<i::JSArrayBuffer> buffer, bool has_maximum,
int maximum) {
i::Handle<i::JSFunction> memory_ctor(
i_isolate->native_context()->wasm_memory_constructor());
i::Handle<i::JSObject> memory_obj =
i_isolate->factory()->NewJSObject(memory_ctor);
memory_obj->SetInternalField(0, *buffer);
memory_obj->SetInternalField(
1, has_maximum
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
: static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
i::Handle<i::Symbol> memory_sym(
i_isolate->native_context()->wasm_memory_sym());
i::Object::SetProperty(memory_obj, memory_sym, memory_obj, i::STRICT).Check();
return memory_obj;
}
// TODO(titzer): we use the API to create the function template because the // TODO(titzer): we use the API to create the function template because the
// internal guts are too ugly to replicate here. // internal guts are too ugly to replicate here.
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,

View File

@ -24,10 +24,6 @@ class WasmJs {
static void InstallWasmConstructors(Isolate* isolate, static void InstallWasmConstructors(Isolate* isolate,
Handle<JSGlobalObject> global, Handle<JSGlobalObject> global,
Handle<Context> context); Handle<Context> context);
static Handle<JSObject> CreateWasmMemoryObject(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
bool has_maximum, int maximum);
}; };
} // namespace internal } // namespace internal

View File

@ -17,17 +17,17 @@
#define WASM_MODULE_HEADER U32_LE(kWasmMagic), U32_LE(kWasmVersion) #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 IMPORT_SIG_INDEX(v) U32V_1(v)
#define FUNC_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 NO_NAME U32V_1(0)
#define NAME_LENGTH(v) U32V_1(v) #define NAME_LENGTH(v) U32V_1(v)
#define ENTRY_COUNT(v) U32V_1(v)
#define ZERO_ALIGNMENT 0 #define ZERO_ALIGNMENT 0
#define ZERO_OFFSET 0 #define ZERO_OFFSET 0
#define BR_TARGET(v) U32V_1(v) #define BR_TARGET(v) U32_LE(v)
#define MASK_7 ((1 << 7) - 1) #define MASK_7 ((1 << 7) - 1)
#define MASK_14 ((1 << 14) - 1) #define MASK_14 ((1 << 14) - 1)
@ -62,76 +62,36 @@
#define ARITY_0 0 #define ARITY_0 0
#define ARITY_1 1 #define ARITY_1 1
#define ARITY_2 2
#define DEPTH_0 0 #define DEPTH_0 0
#define DEPTH_1 1 #define DEPTH_1 1
#define DEPTH_2 2
#define ARITY_2 2
#define WASM_BLOCK(...) kExprBlock, kLocalVoid, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_T(t, ...) \
kExprBlock, static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_BLOCK_TT(t1, t2, ...) \
kExprBlock, kMultivalBlock, 0, \
static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t1)), \
static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t2)), __VA_ARGS__, \
kExprEnd
#define WASM_BLOCK_I(...) kExprBlock, kLocalI32, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_L(...) kExprBlock, kLocalI64, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_F(...) kExprBlock, kLocalF32, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_D(...) kExprBlock, kLocalF64, __VA_ARGS__, kExprEnd
#define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd
#define WASM_LOOP(...) kExprLoop, kLocalVoid, __VA_ARGS__, kExprEnd
#define WASM_LOOP_I(...) kExprLoop, kLocalI32, __VA_ARGS__, kExprEnd
#define WASM_LOOP_L(...) kExprLoop, kLocalI64, __VA_ARGS__, kExprEnd
#define WASM_LOOP_F(...) kExprLoop, kLocalF32, __VA_ARGS__, kExprEnd
#define WASM_LOOP_D(...) kExprLoop, kLocalF64, __VA_ARGS__, kExprEnd
#define WASM_IF(cond, tstmt) cond, kExprIf, kLocalVoid, tstmt, kExprEnd
#define WASM_BLOCK(...) kExprBlock, __VA_ARGS__, kExprEnd
#define WASM_INFINITE_LOOP kExprLoop, kExprBr, ARITY_0, DEPTH_0, kExprEnd
#define WASM_LOOP(...) kExprLoop, __VA_ARGS__, kExprEnd
#define WASM_IF(cond, tstmt) cond, kExprIf, tstmt, kExprEnd
#define WASM_IF_ELSE(cond, tstmt, fstmt) \ #define WASM_IF_ELSE(cond, tstmt, fstmt) \
cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd cond, kExprIf, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t)), tstmt, \
kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_TT(t1, t2, cond, tstmt, fstmt) \
cond, kExprIf, kMultivalBlock, 0, \
static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t1)), \
static_cast<byte>(WasmOpcodes::LocalTypeCodeFor(t2)), tstmt, kExprElse, \
fstmt, kExprEnd
#define WASM_IF_ELSE_I(cond, tstmt, fstmt) \
cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_L(cond, tstmt, fstmt) \
cond, kExprIf, kLocalI64, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_F(cond, tstmt, fstmt) \
cond, kExprIf, kLocalF32, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_D(cond, tstmt, fstmt) \
cond, kExprIf, kLocalF64, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect #define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect
#define WASM_BR(depth) kExprBr, ARITY_0, static_cast<byte>(depth)
#define WASM_RETURN0 kExprReturn #define WASM_BR_IF(depth, cond) \
#define WASM_RETURN1(val) val, kExprReturn cond, kExprBrIf, ARITY_0, static_cast<byte>(depth)
#define WASM_RETURNN(count, ...) __VA_ARGS__, kExprReturn #define WASM_BRV(depth, val) val, kExprBr, ARITY_1, static_cast<byte>(depth)
#define WASM_BRV_IF(depth, val, cond) \
#define WASM_BR(depth) kExprBr, static_cast<byte>(depth) val, cond, kExprBrIf, ARITY_1, static_cast<byte>(depth)
#define WASM_BR_IF(depth, cond) cond, kExprBrIf, static_cast<byte>(depth) #define WASM_BREAK(depth) kExprBr, ARITY_0, static_cast<byte>(depth + 1)
#define WASM_BR_IFD(depth, val, cond) \ #define WASM_CONTINUE(depth) kExprBr, ARITY_0, static_cast<byte>(depth)
val, cond, kExprBrIf, static_cast<byte>(depth), kExprDrop #define WASM_BREAKV(depth, val) \
#define WASM_CONTINUE(depth) kExprBr, static_cast<byte>(depth) val, kExprBr, ARITY_1, static_cast<byte>(depth + 1)
#define WASM_RETURN0 kExprReturn, ARITY_0
#define WASM_RETURN1(val) val, kExprReturn, ARITY_1
#define WASM_RETURNN(count, ...) __VA_ARGS__, kExprReturn, count
#define WASM_UNREACHABLE kExprUnreachable #define WASM_UNREACHABLE kExprUnreachable
#define WASM_BR_TABLE(key, count, ...) \ #define WASM_BR_TABLE(key, count, ...) \
key, kExprBrTable, U32V_1(count), __VA_ARGS__ 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__
#define WASM_CASE(x) static_cast<byte>(x), static_cast<byte>(x >> 8) #define WASM_CASE(x) static_cast<byte>(x), static_cast<byte>(x >> 8)
#define WASM_CASE_BR(x) static_cast<byte>(x), static_cast<byte>(0x80 | (x) >> 8) #define WASM_CASE_BR(x) static_cast<byte>(x), static_cast<byte>(0x80 | (x) >> 8)
@ -383,8 +343,6 @@ class LocalDeclEncoder {
static_cast<byte>(bit_cast<uint64_t>(val) >> 56) static_cast<byte>(bit_cast<uint64_t>(val) >> 56)
#define WASM_GET_LOCAL(index) kExprGetLocal, static_cast<byte>(index) #define WASM_GET_LOCAL(index) kExprGetLocal, static_cast<byte>(index)
#define WASM_SET_LOCAL(index, val) val, kExprSetLocal, static_cast<byte>(index) #define WASM_SET_LOCAL(index, val) val, kExprSetLocal, static_cast<byte>(index)
#define WASM_TEE_LOCAL(index, val) val, kExprTeeLocal, static_cast<byte>(index)
#define WASM_DROP kExprDrop
#define WASM_GET_GLOBAL(index) kExprGetGlobal, static_cast<byte>(index) #define WASM_GET_GLOBAL(index) kExprGetGlobal, static_cast<byte>(index)
#define WASM_SET_GLOBAL(index, val) \ #define WASM_SET_GLOBAL(index, val) \
val, kExprSetGlobal, static_cast<byte>(index) val, kExprSetGlobal, static_cast<byte>(index)
@ -416,25 +374,49 @@ class LocalDeclEncoder {
v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \ v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \
alignment, ZERO_OFFSET alignment, ZERO_OFFSET
#define WASM_CALL_FUNCTION0(index) kExprCallFunction, static_cast<byte>(index) #define WASM_CALL_FUNCTION0(index) \
#define WASM_CALL_FUNCTION(index, ...) \ kExprCallFunction, 0, static_cast<byte>(index)
__VA_ARGS__, kExprCallFunction, static_cast<byte>(index) #define WASM_CALL_FUNCTION1(index, a) \
a, kExprCallFunction, 1, static_cast<byte>(index)
#define WASM_CALL_FUNCTION2(index, a, b) \
a, b, kExprCallFunction, 2, static_cast<byte>(index)
#define WASM_CALL_FUNCTION3(index, a, b, c) \
a, b, c, kExprCallFunction, 3, static_cast<byte>(index)
#define WASM_CALL_FUNCTION4(index, a, b, c, d) \
a, b, c, d, kExprCallFunction, 4, static_cast<byte>(index)
#define WASM_CALL_FUNCTION5(index, a, b, c, d, e) \
kExprCallFunction, 5, static_cast<byte>(index)
#define WASM_CALL_FUNCTIONN(arity, index, ...) \
__VA_ARGS__, kExprCallFunction, arity, static_cast<byte>(index)
#define WASM_CALL_IMPORT0(index) kExprCallImport, 0, static_cast<byte>(index)
#define WASM_CALL_IMPORT1(index, a) \
a, kExprCallImport, 1, static_cast<byte>(index)
#define WASM_CALL_IMPORT2(index, a, b) \
a, b, kExprCallImport, 2, static_cast<byte>(index)
#define WASM_CALL_IMPORT3(index, a, b, c) \
a, b, c, kExprCallImport, 3, static_cast<byte>(index)
#define WASM_CALL_IMPORT4(index, a, b, c, d) \
a, b, c, d, kExprCallImport, 4, static_cast<byte>(index)
#define WASM_CALL_IMPORT5(index, a, b, c, d, e) \
a, b, c, d, e, kExprCallImport, 5, static_cast<byte>(index)
#define WASM_CALL_IMPORTN(arity, index, ...) \
__VA_ARGS__, kExprCallImport, U32V_1(arity), static_cast<byte>(index),
// TODO(titzer): change usages of these macros to put func last.
#define WASM_CALL_INDIRECT0(index, func) \ #define WASM_CALL_INDIRECT0(index, func) \
func, kExprCallIndirect, static_cast<byte>(index) func, kExprCallIndirect, 0, static_cast<byte>(index)
#define WASM_CALL_INDIRECT1(index, func, a) \ #define WASM_CALL_INDIRECT1(index, func, a) \
a, func, kExprCallIndirect, static_cast<byte>(index) func, a, kExprCallIndirect, 1, static_cast<byte>(index)
#define WASM_CALL_INDIRECT2(index, func, a, b) \ #define WASM_CALL_INDIRECT2(index, func, a, b) \
a, b, func, kExprCallIndirect, static_cast<byte>(index) func, a, b, kExprCallIndirect, 2, static_cast<byte>(index)
#define WASM_CALL_INDIRECT3(index, func, a, b, c) \ #define WASM_CALL_INDIRECT3(index, func, a, b, c) \
a, b, c, func, kExprCallIndirect, static_cast<byte>(index) func, a, b, c, kExprCallIndirect, 3, static_cast<byte>(index)
#define WASM_CALL_INDIRECT4(index, func, a, b, c, d) \ #define WASM_CALL_INDIRECT4(index, func, a, b, c, d) \
a, b, c, d, func, kExprCallIndirect, static_cast<byte>(index) func, a, b, c, d, kExprCallIndirect, 4, static_cast<byte>(index)
#define WASM_CALL_INDIRECT5(index, func, a, b, c, d, e) \ #define WASM_CALL_INDIRECT5(index, func, a, b, c, d, e) \
a, b, c, d, e, func, kExprCallIndirect, static_cast<byte>(index) func, a, b, c, d, e, kExprCallIndirect, 5, static_cast<byte>(index)
#define WASM_CALL_INDIRECTN(arity, index, func, ...) \ #define WASM_CALL_INDIRECTN(arity, index, func, ...) \
__VA_ARGS__, func, kExprCallIndirect, static_cast<byte>(index) func, __VA_ARGS__, kExprCallIndirect, U32V_1(arity), static_cast<byte>(index)
#define WASM_NOT(x) x, kExprI32Eqz #define WASM_NOT(x) x, kExprI32Eqz
#define WASM_SEQ(...) __VA_ARGS__ #define WASM_SEQ(...) __VA_ARGS__
@ -442,16 +424,11 @@ class LocalDeclEncoder {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Constructs that are composed of multiple bytecodes. // Constructs that are composed of multiple bytecodes.
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#define WASM_WHILE(x, y) \ #define WASM_WHILE(x, y) \
kExprLoop, kLocalVoid, x, kExprIf, kLocalVoid, y, kExprBr, DEPTH_1, \ kExprLoop, x, kExprIf, y, kExprBr, ARITY_1, DEPTH_1, kExprEnd, kExprEnd
kExprEnd, kExprEnd
#define WASM_INC_LOCAL(index) \ #define WASM_INC_LOCAL(index) \
kExprGetLocal, static_cast<byte>(index), kExprI8Const, 1, kExprI32Add, \ kExprGetLocal, static_cast<byte>(index), kExprI8Const, 1, kExprI32Add, \
kExprTeeLocal, static_cast<byte>(index) kExprSetLocal, static_cast<byte>(index)
#define WASM_INC_LOCAL_BYV(index, count) \
kExprGetLocal, static_cast<byte>(index), kExprI8Const, \
static_cast<byte>(count), kExprI32Add, kExprTeeLocal, \
static_cast<byte>(index)
#define WASM_INC_LOCAL_BY(index, count) \ #define WASM_INC_LOCAL_BY(index, count) \
kExprGetLocal, static_cast<byte>(index), kExprI8Const, \ kExprGetLocal, static_cast<byte>(index), kExprI8Const, \
static_cast<byte>(count), kExprI32Add, kExprSetLocal, \ static_cast<byte>(count), kExprI32Add, kExprSetLocal, \
@ -634,13 +611,4 @@ class LocalDeclEncoder {
#define SIZEOF_SIG_ENTRY_x_xx 6 #define SIZEOF_SIG_ENTRY_x_xx 6
#define SIZEOF_SIG_ENTRY_x_xxx 7 #define SIZEOF_SIG_ENTRY_x_xxx 7
#define WASM_BRV(depth, val) val, kExprBr, static_cast<byte>(depth)
#define WASM_BRV_IF(depth, val, cond) \
val, cond, kExprBrIf, static_cast<byte>(depth)
#define WASM_BRV_IFD(depth, val, cond) \
val, cond, kExprBrIf, static_cast<byte>(depth), kExprDrop
#define WASM_IFB(cond, ...) cond, kExprIf, kLocalVoid, __VA_ARGS__, kExprEnd
#define WASM_BR_TABLEV(val, key, count, ...) \
val, key, kExprBrTable, U32V_1(count), __VA_ARGS__
#endif // V8_WASM_MACRO_GEN_H_ #endif // V8_WASM_MACRO_GEN_H_

File diff suppressed because it is too large Load Diff

View File

@ -27,73 +27,84 @@ const size_t kMaxModuleSize = 1024 * 1024 * 1024;
const size_t kMaxFunctionSize = 128 * 1024; const size_t kMaxFunctionSize = 128 * 1024;
const size_t kMaxStringSize = 256; const size_t kMaxStringSize = 256;
const uint32_t kWasmMagic = 0x6d736100; const uint32_t kWasmMagic = 0x6d736100;
const uint32_t kWasmVersion = 0x0c; const uint32_t kWasmVersion = 0x0b;
const uint8_t kWasmFunctionTypeForm = 0x40; const uint8_t kWasmFunctionTypeForm = 0x40;
const uint8_t kWasmAnyFunctionTypeForm = 0x20;
enum WasmSectionCode { // WebAssembly sections are named as strings in the binary format, but
kUnknownSectionCode = 0, // code for unknown sections // internally V8 uses an enum to handle them.
kTypeSectionCode = 1, // Function signature declarations //
kImportSectionCode = 2, // Import declarations // Entries have the form F(enumerator, string).
kFunctionSectionCode = 3, // Function declarations #define FOR_EACH_WASM_SECTION_TYPE(F) \
kTableSectionCode = 4, // Indirect function table and other tables F(Signatures, 1, "type") \
kMemorySectionCode = 5, // Memory attributes F(ImportTable, 2, "import") \
kGlobalSectionCode = 6, // Global declarations F(FunctionSignatures, 3, "function") \
kExportSectionCode = 7, // Exports F(FunctionTable, 4, "table") \
kStartSectionCode = 8, // Start function declaration F(Memory, 5, "memory") \
kElementSectionCode = 9, // Elements section F(ExportTable, 6, "export") \
kCodeSectionCode = 10, // Function code F(StartFunction, 7, "start") \
kDataSectionCode = 11, // Data segments F(FunctionBodies, 8, "code") \
kNameSectionCode = 12, // Name section (encoded as a string) F(DataSegments, 9, "data") \
}; F(Names, 10, "name") \
F(Globals, 0, "global") \
F(End, 0, "end")
inline bool IsValidSectionCode(uint8_t byte) { // Contants for the above section types: {LEB128 length, characters...}.
return kTypeSectionCode <= byte && byte <= kDataSectionCode; #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'
const char* SectionName(WasmSectionCode code); // Constants for the above section headers' size (LEB128 + characters).
#define WASM_SECTION_MEMORY_SIZE ((size_t)7)
static const char* kNameString = "name"; #define WASM_SECTION_SIGNATURES_SIZE ((size_t)5)
static const size_t kNameStringLength = 4; #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)
class WasmDebugInfo; 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. // 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 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. // Static representation of a WASM function.
struct WasmFunction { struct WasmFunction {
@ -104,59 +115,47 @@ struct WasmFunction {
uint32_t name_length; // length in bytes of the name. 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_start_offset; // offset in the module bytes of code start.
uint32_t code_end_offset; // offset in the module bytes of code end. uint32_t code_end_offset; // offset in the module bytes of code end.
bool imported; };
bool exported;
// 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.
}; };
// Static representation of a wasm global variable. // Static representation of a wasm global variable.
struct WasmGlobal { 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. LocalType type; // type of the global.
bool mutability; // {true} if mutable. uint32_t offset; // offset from beginning of globals area.
WasmInitExpr init; // the initialization expression of the global. bool exported; // true if this global is exported.
uint32_t offset; // offset into global memory.
bool imported; // true if imported.
bool exported; // true if exported.
}; };
// Static representation of a wasm data segment. // Static representation of a wasm data segment.
struct WasmDataSegment { struct WasmDataSegment {
WasmInitExpr dest_addr; // destination memory address of the data. uint32_t dest_addr; // destination memory address of the data.
uint32_t source_offset; // start offset in the module bytes. uint32_t source_offset; // start offset in the module bytes.
uint32_t source_size; // end 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. // Static representation of a wasm indirect call table.
struct WasmIndirectFunctionTable { struct WasmIndirectFunctionTable {
uint32_t size; // initial table size. uint32_t size; // initial table size.
uint32_t max_size; // maximum table size. uint32_t max_size; // maximum table size.
std::vector<int32_t> values; // function table, -1 indicating invalid. std::vector<uint16_t> values; // function table.
bool imported; // true if imported.
bool exported; // true if exported.
};
// Static representation of how to initialize a table.
struct WasmTableInit {
uint32_t table_index;
WasmInitExpr offset;
std::vector<uint32_t> entries;
};
// Static representation of a WASM import.
struct WasmImport {
uint32_t module_name_length; // length in bytes of the module name.
uint32_t module_name_offset; // offset in module bytes of the module name.
uint32_t field_name_length; // length in bytes of the import name.
uint32_t field_name_offset; // offset in module bytes of the import name.
WasmExternalKind kind; // kind of the import.
uint32_t index; // index into the respective space.
};
// Static representation of a WASM export.
struct WasmExport {
uint32_t name_length; // length in bytes of the exported name.
uint32_t name_offset; // offset in module bytes of the name to export.
WasmExternalKind kind; // kind of the export.
uint32_t index; // index into the respective space.
}; };
enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin }; enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
@ -164,7 +163,6 @@ enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
// Static representation of a module. // Static representation of a module.
struct WasmModule { struct WasmModule {
static const uint32_t kPageSize = 0x10000; // Page size, 64kb. 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 kMinMemPages = 1; // Minimum memory size = 64kb
static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb
@ -173,6 +171,7 @@ struct WasmModule {
uint32_t min_mem_pages; // minimum size of the memory in 64k pages. 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. uint32_t max_mem_pages; // maximum size of the memory in 64k pages.
bool mem_export; // true if the memory is exported. 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 // TODO(wasm): reconcile start function index being an int with
// the fact that we index on uint32_t, so we may technically not be // the fact that we index on uint32_t, so we may technically not be
// able to represent some start_function_index -es. // able to represent some start_function_index -es.
@ -181,16 +180,12 @@ struct WasmModule {
std::vector<WasmGlobal> globals; // globals in this module. std::vector<WasmGlobal> globals; // globals in this module.
uint32_t globals_size; // size of globals table. uint32_t globals_size; // size of globals table.
uint32_t num_imported_functions; // number of imported functions.
uint32_t num_declared_functions; // number of declared functions.
uint32_t num_exported_functions; // number of exported functions.
std::vector<FunctionSig*> signatures; // signatures in this module. std::vector<FunctionSig*> signatures; // signatures in this module.
std::vector<WasmFunction> functions; // functions in this module. std::vector<WasmFunction> functions; // functions in this module.
std::vector<WasmDataSegment> data_segments; // data segments in this module. std::vector<WasmDataSegment> data_segments; // data segments in this module.
std::vector<WasmIndirectFunctionTable> function_tables; // function tables. std::vector<WasmIndirectFunctionTable> function_tables; // function tables.
std::vector<WasmImport> import_table; // import table. std::vector<WasmImport> import_table; // import table.
std::vector<WasmExport> export_table; // export table. std::vector<WasmExport> export_table; // export table.
std::vector<WasmTableInit> table_inits; // initializations of tables
// We store the semaphore here to extend its lifetime. In <libc-2.21, which we // We store the semaphore here to extend its lifetime. In <libc-2.21, which we
// use on the try bots, semaphore::Wait() can return while some compilation // use on the try bots, semaphore::Wait() can return while some compilation
// tasks are still executing semaphore::Signal(). If the semaphore is cleaned // tasks are still executing semaphore::Signal(). If the semaphore is cleaned
@ -239,7 +234,6 @@ struct WasmModule {
// Creates a new instantiation of the module in the given isolate. // Creates a new instantiation of the module in the given isolate.
static MaybeHandle<JSObject> Instantiate(Isolate* isolate, static MaybeHandle<JSObject> Instantiate(Isolate* isolate,
ErrorThrower* thrower,
Handle<JSObject> module_object, Handle<JSObject> module_object,
Handle<JSReceiver> ffi, Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory); Handle<JSArrayBuffer> memory);
@ -261,6 +255,7 @@ struct WasmModuleInstance {
Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals. Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals.
std::vector<Handle<FixedArray>> function_tables; // indirect function tables. std::vector<Handle<FixedArray>> function_tables; // indirect function tables.
std::vector<Handle<Code>> function_code; // code objects for each function. std::vector<Handle<Code>> function_code; // code objects for each function.
std::vector<Handle<Code>> import_code; // code objects for each import.
// -- raw memory ------------------------------------------------------------ // -- raw memory ------------------------------------------------------------
byte* mem_start; // start of linear memory. byte* mem_start; // start of linear memory.
uint32_t mem_size; // size of the linear memory. uint32_t mem_size; // size of the linear memory.
@ -271,6 +266,7 @@ struct WasmModuleInstance {
: module(m), : module(m),
function_tables(m->function_tables.size()), function_tables(m->function_tables.size()),
function_code(m->functions.size()), function_code(m->functions.size()),
import_code(m->import_table.size()),
mem_start(nullptr), mem_start(nullptr),
mem_size(0), mem_size(0),
globals_start(nullptr) {} globals_start(nullptr) {}
@ -282,6 +278,9 @@ struct ModuleEnv {
const WasmModule* module; const WasmModule* module;
WasmModuleInstance* instance; WasmModuleInstance* instance;
ModuleOrigin origin; ModuleOrigin origin;
// TODO(mtrofin): remove this once we introduce WASM_DIRECT_CALL
// reloc infos.
std::vector<Handle<Code>> placeholders;
bool IsValidGlobal(uint32_t index) const { bool IsValidGlobal(uint32_t index) const {
return module && index < module->globals.size(); return module && index < module->globals.size();
@ -292,6 +291,9 @@ struct ModuleEnv {
bool IsValidSignature(uint32_t index) const { bool IsValidSignature(uint32_t index) const {
return module && index < module->signatures.size(); 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 { bool IsValidTable(uint32_t index) const {
return module && index < module->function_tables.size(); return module && index < module->function_tables.size();
} }
@ -303,6 +305,10 @@ struct ModuleEnv {
DCHECK(IsValidFunction(index)); DCHECK(IsValidFunction(index));
return module->functions[index].sig; return module->functions[index].sig;
} }
FunctionSig* GetImportSignature(uint32_t index) {
DCHECK(IsValidImport(index));
return module->import_table[index].sig;
}
FunctionSig* GetSignature(uint32_t index) { FunctionSig* GetSignature(uint32_t index) {
DCHECK(IsValidSignature(index)); DCHECK(IsValidSignature(index));
return module->signatures[index]; return module->signatures[index];
@ -314,10 +320,8 @@ struct ModuleEnv {
bool asm_js() { return origin == kAsmJsOrigin; } bool asm_js() { return origin == kAsmJsOrigin; }
Handle<Code> GetFunctionCode(uint32_t index) { Handle<Code> GetCodeOrPlaceholder(uint32_t index) const;
DCHECK_NOT_NULL(instance); Handle<Code> GetImportCode(uint32_t index);
return instance->function_code[index];
}
static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone, static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone,
FunctionSig* sig); FunctionSig* sig);
@ -404,9 +408,6 @@ MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
ErrorThrower* thrower, ErrorThrower* thrower,
ModuleOrigin origin); ModuleOrigin origin);
// Get the number of imported functions for a WASM instance.
uint32_t GetNumImportedFunctions(Handle<JSObject> wasm_object);
// Assumed to be called with a code object associated to a wasm module instance. // Assumed to be called with a code object associated to a wasm module instance.
// Intended to be called from runtime functions. // Intended to be called from runtime functions.
// Returns undefined if the runtime support was not setup, nullptr if the // Returns undefined if the runtime support was not setup, nullptr if the

View File

@ -22,9 +22,6 @@ enum LocalTypeCode {
kLocalS128 = 5 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. // We reuse the internal machine type to represent WebAssembly AST types.
// A typedef improves readability without adding a whole new type system. // A typedef improves readability without adding a whole new type system.
typedef MachineRepresentation LocalType; typedef MachineRepresentation LocalType;
@ -47,7 +44,7 @@ const WasmCodePosition kNoCodePosition = -1;
// Control expressions and blocks. // Control expressions and blocks.
#define FOREACH_CONTROL_OPCODE(V) \ #define FOREACH_CONTROL_OPCODE(V) \
V(Unreachable, 0x00, _) \ V(Nop, 0x00, _) \
V(Block, 0x01, _) \ V(Block, 0x01, _) \
V(Loop, 0x02, _) \ V(Loop, 0x02, _) \
V(If, 0x03, _) \ V(If, 0x03, _) \
@ -57,7 +54,7 @@ const WasmCodePosition kNoCodePosition = -1;
V(BrIf, 0x07, _) \ V(BrIf, 0x07, _) \
V(BrTable, 0x08, _) \ V(BrTable, 0x08, _) \
V(Return, 0x09, _) \ V(Return, 0x09, _) \
V(Nop, 0x0a, _) \ V(Unreachable, 0x0a, _) \
V(Throw, 0xfa, _) \ V(Throw, 0xfa, _) \
V(Try, 0xfb, _) \ V(Try, 0xfb, _) \
V(Catch, 0xfe, _) \ V(Catch, 0xfe, _) \
@ -71,10 +68,9 @@ const WasmCodePosition kNoCodePosition = -1;
V(F32Const, 0x13, _) \ V(F32Const, 0x13, _) \
V(GetLocal, 0x14, _) \ V(GetLocal, 0x14, _) \
V(SetLocal, 0x15, _) \ V(SetLocal, 0x15, _) \
V(TeeLocal, 0x19, _) \
V(Drop, 0x0b, _) \
V(CallFunction, 0x16, _) \ V(CallFunction, 0x16, _) \
V(CallIndirect, 0x17, _) \ V(CallIndirect, 0x17, _) \
V(CallImport, 0x18, _) \
V(I8Const, 0xcb, _) \ V(I8Const, 0xcb, _) \
V(GetGlobal, 0xbb, _) \ V(GetGlobal, 0xbb, _) \
V(SetGlobal, 0xbc, _) V(SetGlobal, 0xbc, _)
@ -501,8 +497,6 @@ class WasmOpcodes {
return 1 << ElementSizeLog2Of(type.representation()); return 1 << ElementSizeLog2Of(type.representation());
} }
static byte MemSize(LocalType type) { return 1 << ElementSizeLog2Of(type); }
static LocalTypeCode LocalTypeCodeFor(LocalType type) { static LocalTypeCode LocalTypeCodeFor(LocalType type) {
switch (type) { switch (type) {
case kAstI32: case kAstI32:

View File

@ -22,7 +22,19 @@ namespace wasm {
// Error codes for programmatic checking of the decoder's verification. // Error codes for programmatic checking of the decoder's verification.
enum ErrorCode { enum ErrorCode {
kSuccess, kSuccess,
kError, // TODO(titzer): introduce real error codes 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
}; };
// The overall result of decoding a function or a module. // The overall result of decoding a function or a module.

View File

@ -32,6 +32,7 @@
#define asu64(x) static_cast<uint64_t>(x) #define asu64(x) static_cast<uint64_t>(x)
#define B2(a, b) kExprBlock, a, b, kExprEnd #define B2(a, b) kExprBlock, a, b, kExprEnd
#define B1(a) kExprBlock, a, kExprEnd
// Can't bridge macro land with nested macros. // Can't bridge macro land with nested macros.
#if V8_TARGET_ARCH_MIPS #if V8_TARGET_ARCH_MIPS
@ -834,8 +835,8 @@ WASM_EXEC_TEST(CallI64Parameter) {
WasmRunner<int32_t> r(&module); WasmRunner<int32_t> r(&module);
BUILD( BUILD(
r, r,
WASM_I32_CONVERT_I64(WASM_CALL_FUNCTION( WASM_I32_CONVERT_I64(WASM_CALL_FUNCTIONN(
index, WASM_I64V_9(0xbcd12340000000b), 19, index, WASM_I64V_9(0xbcd12340000000b),
WASM_I64V_9(0xbcd12340000000c), WASM_I32V_1(0xd), WASM_I64V_9(0xbcd12340000000c), WASM_I32V_1(0xd),
WASM_I32_CONVERT_I64(WASM_I64V_9(0xbcd12340000000e)), WASM_I32_CONVERT_I64(WASM_I64V_9(0xbcd12340000000e)),
WASM_I64V_9(0xbcd12340000000f), WASM_I64V_10(0xbcd1234000000010), WASM_I64V_9(0xbcd12340000000f), WASM_I64V_10(0xbcd1234000000010),
@ -1118,7 +1119,7 @@ WASM_EXEC_TEST(Call_Int64Sub) {
// Build the caller function. // Build the caller function.
WasmRunner<int64_t> r(&module, MachineType::Int64(), MachineType::Int64()); WasmRunner<int64_t> r(&module, MachineType::Int64(), MachineType::Int64());
BUILD(r, WASM_CALL_FUNCTION(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); BUILD(r, WASM_CALL_FUNCTION2(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT32_INPUTS(i) { FOR_INT32_INPUTS(i) {
FOR_INT32_INPUTS(j) { FOR_INT32_INPUTS(j) {
@ -1153,11 +1154,7 @@ WASM_EXEC_TEST(LoadStoreI64_sx) {
ZERO_OFFSET, // -- ZERO_OFFSET, // --
kExprI64StoreMem, // -- kExprI64StoreMem, // --
ZERO_ALIGNMENT, // -- ZERO_ALIGNMENT, // --
ZERO_OFFSET, // -- ZERO_OFFSET // --
kExprI8Const, 0, // --
loads[m], // --
ZERO_ALIGNMENT, // --
ZERO_OFFSET, // --
}; };
r.Build(code, code + arraysize(code)); r.Build(code, code + arraysize(code));
@ -1259,9 +1256,10 @@ WASM_EXEC_TEST(F64ReinterpretI64) {
int64_t* memory = module.AddMemoryElems<int64_t>(8); int64_t* memory = module.AddMemoryElems<int64_t>(8);
WasmRunner<int64_t> r(&module, MachineType::Int64()); WasmRunner<int64_t> r(&module, MachineType::Int64());
BUILD(r, WASM_STORE_MEM(MachineType::Float64(), WASM_ZERO, BUILD(r,
WASM_F64_REINTERPRET_I64(WASM_GET_LOCAL(0))), WASM_BLOCK(WASM_STORE_MEM(MachineType::Float64(), WASM_ZERO,
WASM_GET_LOCAL(0)); WASM_F64_REINTERPRET_I64(WASM_GET_LOCAL(0))),
WASM_GET_LOCAL(0)));
FOR_INT32_INPUTS(i) { FOR_INT32_INPUTS(i) {
int64_t expected = static_cast<int64_t>(*i) * 0x300010001; int64_t expected = static_cast<int64_t>(*i) * 0x300010001;
@ -1322,17 +1320,18 @@ WASM_EXEC_TEST(MemI64_Sum) {
WasmRunner<uint64_t> r(&module, MachineType::Int32()); WasmRunner<uint64_t> r(&module, MachineType::Int32());
const byte kSum = r.AllocateLocal(kAstI64); const byte kSum = r.AllocateLocal(kAstI64);
BUILD( BUILD(r,
r, WASM_BLOCK(
WASM_WHILE( WASM_WHILE(
WASM_GET_LOCAL(0), WASM_GET_LOCAL(0),
WASM_BLOCK( WASM_BLOCK(
WASM_SET_LOCAL(kSum, WASM_SET_LOCAL(
WASM_I64_ADD(WASM_GET_LOCAL(kSum), kSum, WASM_I64_ADD(WASM_GET_LOCAL(kSum),
WASM_LOAD_MEM(MachineType::Int64(), WASM_LOAD_MEM(MachineType::Int64(),
WASM_GET_LOCAL(0)))), WASM_GET_LOCAL(0)))),
WASM_SET_LOCAL(0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(8))))), WASM_SET_LOCAL(
WASM_GET_LOCAL(1)); 0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I8(8))))),
WASM_GET_LOCAL(1)));
// Run 4 trials. // Run 4 trials.
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
@ -1354,8 +1353,7 @@ WASM_EXEC_TEST(StoreMemI64_alignment) {
for (byte i = 0; i <= 3; i++) { for (byte i = 0; i <= 3; i++) {
WasmRunner<int64_t> r(&module, MachineType::Int64()); WasmRunner<int64_t> r(&module, MachineType::Int64());
BUILD(r, WASM_STORE_MEM_ALIGNMENT(MachineType::Int64(), WASM_ZERO, i, 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.RandomizeMemory(1111);
module.WriteMemory<int64_t>(&memory[0], 0); module.WriteMemory<int64_t>(&memory[0], 0);
@ -1373,10 +1371,10 @@ WASM_EXEC_TEST(I64Global) {
int64_t* global = module.AddGlobal<int64_t>(kAstI64); int64_t* global = module.AddGlobal<int64_t>(kAstI64);
WasmRunner<int32_t> r(&module, MachineType::Int32()); WasmRunner<int32_t> r(&module, MachineType::Int32());
// global = global + p0 // global = global + p0
BUILD(r, WASM_SET_GLOBAL( BUILD(r, B2(WASM_SET_GLOBAL(
0, WASM_I64_AND(WASM_GET_GLOBAL(0), 0, WASM_I64_AND(WASM_GET_GLOBAL(0),
WASM_I64_SCONVERT_I32(WASM_GET_LOCAL(0)))), WASM_I64_SCONVERT_I32(WASM_GET_LOCAL(0)))),
WASM_ZERO); WASM_ZERO));
module.WriteMemory<int64_t>(global, 0xFFFFFFFFFFFFFFFFLL); module.WriteMemory<int64_t>(global, 0xFFFFFFFFFFFFFFFFLL);
for (int i = 9; i < 444444; i += 111111) { for (int i = 9; i < 444444; i += 111111) {
@ -1479,11 +1477,11 @@ static void CompileCallIndirectMany(LocalType param) {
WasmFunctionCompiler t(sig, &module); WasmFunctionCompiler t(sig, &module);
std::vector<byte> code; std::vector<byte> code;
ADD_CODE(code, kExprI8Const, 0);
for (byte p = 0; p < num_params; p++) { for (byte p = 0; p < num_params; p++) {
ADD_CODE(code, kExprGetLocal, p); ADD_CODE(code, kExprGetLocal, p);
} }
ADD_CODE(code, kExprI8Const, 0); ADD_CODE(code, kExprCallIndirect, static_cast<byte>(num_params), 1);
ADD_CODE(code, kExprCallIndirect, 1);
t.Build(&code[0], &code[0] + code.size()); t.Build(&code[0], &code[0] + code.size());
t.Compile(); t.Compile();
@ -1542,7 +1540,8 @@ static void Run_WasmMixedCall_N(WasmExecutionMode execution_mode, int start) {
} }
// Call the selector function. // Call the selector function.
ADD_CODE(code, kExprCallFunction, static_cast<byte>(index)); ADD_CODE(code, kExprCallFunction, static_cast<byte>(num_params),
static_cast<byte>(index));
// Store the result in memory. // Store the result in memory.
ADD_CODE(code, ADD_CODE(code,

View File

@ -36,7 +36,7 @@ TEST(Run_WasmInt8Const_i) {
TEST(Run_WasmIfElse) { TEST(Run_WasmIfElse) {
WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Int32()); WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Int32());
BUILD(r, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_I8(9), WASM_I8(10))); BUILD(r, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_I8(9), WASM_I8(10)));
CHECK_EQ(10, r.Call(0)); CHECK_EQ(10, r.Call(0));
CHECK_EQ(9, r.Call(1)); CHECK_EQ(9, r.Call(1));
} }
@ -65,39 +65,31 @@ TEST(Run_WasmNopsN) {
TEST(Run_WasmConstsN) { TEST(Run_WasmConstsN) {
const int kMaxConsts = 10; const int kMaxConsts = 10;
byte code[kMaxConsts * 3]; byte code[kMaxConsts * 2];
int32_t expected = 0;
for (int count = 1; count < kMaxConsts; count++) { for (int count = 1; count < kMaxConsts; count++) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
byte val = static_cast<byte>(count * 10 + i); code[i * 2] = kExprI8Const;
code[i * 3] = kExprI8Const; code[i * 2 + 1] = static_cast<byte>(count * 10 + i);
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<byte>(count * 11 - 1);
WasmRunner<int32_t> r(kExecuteInterpreted); WasmRunner<int32_t> r(kExecuteInterpreted);
r.Build(code, code + (count * 3)); r.Build(code, code + (count * 2));
CHECK_EQ(expected, r.Call()); CHECK_EQ(expected, r.Call());
} }
} }
TEST(Run_WasmBlocksN) { TEST(Run_WasmBlocksN) {
const int kMaxNops = 10; const int kMaxNops = 10;
const int kExtra = 5; const int kExtra = 4;
byte code[kMaxNops + kExtra]; byte code[kMaxNops + kExtra];
for (int nops = 0; nops < kMaxNops; nops++) { for (int nops = 0; nops < kMaxNops; nops++) {
byte expected = static_cast<byte>(30 + nops); byte expected = static_cast<byte>(30 + nops);
memset(code, kExprNop, sizeof(code)); memset(code, kExprNop, sizeof(code));
code[0] = kExprBlock; code[0] = kExprBlock;
code[1] = kLocalI32; code[1 + nops] = kExprI8Const;
code[2 + nops] = kExprI8Const; code[1 + nops + 1] = expected;
code[2 + nops + 1] = expected; code[1 + nops + 2] = kExprEnd;
code[2 + nops + 2] = kExprEnd;
WasmRunner<int32_t> r(kExecuteInterpreted); WasmRunner<int32_t> r(kExecuteInterpreted);
r.Build(code, code + nops + kExtra); r.Build(code, code + nops + kExtra);
@ -114,14 +106,14 @@ TEST(Run_WasmBlockBreakN) {
for (int index = 0; index < nops; index++) { for (int index = 0; index < nops; index++) {
memset(code, kExprNop, sizeof(code)); memset(code, kExprNop, sizeof(code));
code[0] = kExprBlock; code[0] = kExprBlock;
code[1] = kLocalI32;
code[sizeof(code) - 1] = kExprEnd; code[sizeof(code) - 1] = kExprEnd;
int expected = nops * 11 + index; int expected = nops * 11 + index;
code[2 + index + 0] = kExprI8Const; code[1 + index + 0] = kExprI8Const;
code[2 + index + 1] = static_cast<byte>(expected); code[1 + index + 1] = static_cast<byte>(expected);
code[2 + index + 2] = kExprBr; code[1 + index + 2] = kExprBr;
code[2 + index + 3] = 0; code[1 + index + 3] = ARITY_1;
code[1 + index + 4] = 0;
WasmRunner<int32_t> r(kExecuteInterpreted); WasmRunner<int32_t> r(kExecuteInterpreted);
r.Build(code, code + kMaxNops + kExtra); r.Build(code, code + kMaxNops + kExtra);
@ -134,10 +126,10 @@ TEST(Run_Wasm_nested_ifs_i) {
WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Int32(), WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Int32(),
MachineType::Int32()); MachineType::Int32());
BUILD(r, WASM_IF_ELSE_I( BUILD(r, WASM_IF_ELSE(
WASM_GET_LOCAL(0), WASM_GET_LOCAL(0),
WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_I8(11), WASM_I8(12)), WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_I8(11), WASM_I8(12)),
WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_I8(13), WASM_I8(14)))); WASM_IF_ELSE(WASM_GET_LOCAL(1), WASM_I8(13), WASM_I8(14))));
CHECK_EQ(11, r.Call(1, 1)); CHECK_EQ(11, r.Call(1, 1));
CHECK_EQ(12, r.Call(1, 0)); CHECK_EQ(12, r.Call(1, 0));
@ -308,10 +300,10 @@ TEST(GrowMemoryPreservesData) {
TestingModule module(kExecuteInterpreted); TestingModule module(kExecuteInterpreted);
WasmRunner<int32_t> r(&module, MachineType::Uint32()); WasmRunner<int32_t> r(&module, MachineType::Uint32());
module.AddMemory(WasmModule::kPageSize); module.AddMemory(WasmModule::kPageSize);
BUILD(r, WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index), BUILD(r, WASM_BLOCK(WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index),
WASM_I32V(value)), WASM_I32V(value)),
WASM_GROW_MEMORY(WASM_GET_LOCAL(0)), WASM_DROP, WASM_GROW_MEMORY(WASM_GET_LOCAL(0)),
WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V(index))); WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V(index))));
CHECK_EQ(value, r.Call(1)); CHECK_EQ(value, r.Call(1));
} }
@ -320,7 +312,7 @@ TEST(GrowMemoryInvalidSize) {
// Grow memory by an invalid amount without initial memory. // Grow memory by an invalid amount without initial memory.
TestingModule module(kExecuteInterpreted); TestingModule module(kExecuteInterpreted);
WasmRunner<int32_t> r(&module, MachineType::Uint32()); WasmRunner<int32_t> r(&module, MachineType::Uint32());
BUILD(r, WASM_GROW_MEMORY(WASM_GET_LOCAL(0))); BUILD(r, WASM_BLOCK(WASM_GROW_MEMORY(WASM_GET_LOCAL(0))));
CHECK_EQ(-1, r.Call(1048575)); CHECK_EQ(-1, r.Call(1048575));
} }
{ {
@ -328,7 +320,7 @@ TEST(GrowMemoryInvalidSize) {
TestingModule module(kExecuteInterpreted); TestingModule module(kExecuteInterpreted);
WasmRunner<int32_t> r(&module, MachineType::Uint32()); WasmRunner<int32_t> r(&module, MachineType::Uint32());
module.AddMemory(WasmModule::kPageSize); module.AddMemory(WasmModule::kPageSize);
BUILD(r, WASM_GROW_MEMORY(WASM_GET_LOCAL(0))); BUILD(r, WASM_BLOCK(WASM_GROW_MEMORY(WASM_GET_LOCAL(0))));
CHECK_EQ(-1, r.Call(1048575)); CHECK_EQ(-1, r.Call(1048575));
} }
} }

View File

@ -152,7 +152,7 @@ TEST(Run_CallJS_Add_jswrapped) {
WasmFunctionCompiler t(sigs.i_i(), &module); WasmFunctionCompiler t(sigs.i_i(), &module);
uint32_t js_index = uint32_t js_index =
module.AddJsFunction(sigs.i_i(), "(function(a) { return a + 99; })"); module.AddJsFunction(sigs.i_i(), "(function(a) { return a + 99; })");
BUILD(t, WASM_CALL_FUNCTION(js_index, WASM_GET_LOCAL(0))); BUILD(t, WASM_CALL_FUNCTION1(js_index, WASM_GET_LOCAL(0)));
Handle<JSFunction> jsfunc = module.WrapCode(t.CompileAndAdd()); Handle<JSFunction> jsfunc = module.WrapCode(t.CompileAndAdd());
@ -182,7 +182,8 @@ void RunJSSelectTest(int which) {
ADD_CODE(code, WASM_F64(inputs.arg_d(i))); ADD_CODE(code, WASM_F64(inputs.arg_d(i)));
} }
ADD_CODE(code, kExprCallFunction, static_cast<byte>(js_index)); ADD_CODE(code, kExprCallFunction, static_cast<byte>(num_params),
static_cast<byte>(js_index));
size_t end = code.size(); size_t end = code.size();
code.push_back(0); code.push_back(0);
@ -419,7 +420,7 @@ void RunJSSelectAlignTest(int num_args, int num_params) {
ADD_CODE(code, WASM_GET_LOCAL(i)); ADD_CODE(code, WASM_GET_LOCAL(i));
} }
ADD_CODE(code, kExprCallFunction, 0); ADD_CODE(code, kExprCallFunction, static_cast<byte>(num_params), 0);
size_t end = code.size(); size_t end = code.size();
code.push_back(0); code.push_back(0);

View File

@ -53,7 +53,9 @@ TEST(Run_WasmModule_Return114) {
Zone zone(&allocator); Zone zone(&allocator);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); uint16_t f_index = builder->AddFunction();
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
f->SetSignature(sigs.i_v());
ExportAsMain(f); ExportAsMain(f);
byte code[] = {WASM_I8(kReturnValue)}; byte code[] = {WASM_I8(kReturnValue)};
f->EmitCode(code, sizeof(code)); f->EmitCode(code, sizeof(code));
@ -67,18 +69,21 @@ TEST(Run_WasmModule_CallAdd) {
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_ii()); uint16_t f1_index = builder->AddFunction();
WasmFunctionBuilder* f = builder->FunctionAt(f1_index);
f->SetSignature(sigs.i_ii());
uint16_t param1 = 0; uint16_t param1 = 0;
uint16_t param2 = 1; uint16_t param2 = 1;
byte code1[] = {WASM_I32_ADD(WASM_GET_LOCAL(param1), WASM_GET_LOCAL(param2))}; byte code1[] = {WASM_I32_ADD(WASM_GET_LOCAL(param1), WASM_GET_LOCAL(param2))};
f1->EmitCode(code1, sizeof(code1)); f->EmitCode(code1, sizeof(code1));
WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v()); uint16_t f2_index = builder->AddFunction();
f = builder->FunctionAt(f2_index);
f->SetSignature(sigs.i_v());
ExportAsMain(f2); ExportAsMain(f);
byte code2[] = { byte code2[] = {WASM_CALL_FUNCTION2(f1_index, WASM_I8(77), WASM_I8(22))};
WASM_CALL_FUNCTION(f1->func_index(), WASM_I8(77), WASM_I8(22))}; f->EmitCode(code2, sizeof(code2));
f2->EmitCode(code2, sizeof(code2));
TestModule(&zone, builder, 99); TestModule(&zone, builder, 99);
} }
@ -89,7 +94,9 @@ TEST(Run_WasmModule_ReadLoadedDataSegment) {
TestSignatures sigs; TestSignatures sigs;
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); uint16_t f_index = builder->AddFunction();
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
f->SetSignature(sigs.i_v());
ExportAsMain(f); ExportAsMain(f);
byte code[] = { byte code[] = {
@ -108,16 +115,18 @@ TEST(Run_WasmModule_CheckMemoryIsZero) {
TestSignatures sigs; TestSignatures sigs;
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); uint16_t f_index = builder->AddFunction();
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
f->SetSignature(sigs.i_v());
uint16_t localIndex = f->AddLocal(kAstI32); uint16_t localIndex = f->AddLocal(kAstI32);
ExportAsMain(f); ExportAsMain(f);
byte code[] = {WASM_BLOCK_I( byte code[] = {WASM_BLOCK(
WASM_WHILE( WASM_WHILE(
WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I32V_3(kCheckSize)), WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I32V_3(kCheckSize)),
WASM_IF_ELSE( WASM_IF_ELSE(
WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(localIndex)), WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(localIndex)),
WASM_BRV(3, WASM_I8(-1)), WASM_INC_LOCAL_BY(localIndex, 4))), WASM_BRV(2, WASM_I8(-1)), WASM_INC_LOCAL_BY(localIndex, 4))),
WASM_I8(11))}; WASM_I8(11))};
f->EmitCode(code, sizeof(code)); f->EmitCode(code, sizeof(code));
TestModule(&zone, builder, 11); TestModule(&zone, builder, 11);
@ -129,18 +138,20 @@ TEST(Run_WasmModule_CallMain_recursive) {
TestSignatures sigs; TestSignatures sigs;
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); uint16_t f_index = builder->AddFunction();
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
f->SetSignature(sigs.i_v());
uint16_t localIndex = f->AddLocal(kAstI32); uint16_t localIndex = f->AddLocal(kAstI32);
ExportAsMain(f); ExportAsMain(f);
byte code[] = { byte code[] = {WASM_BLOCK(
WASM_SET_LOCAL(localIndex, WASM_SET_LOCAL(localIndex,
WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)), WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)),
WASM_IF_ELSE_I(WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I8(5)), WASM_IF_ELSE(WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I8(5)),
WASM_SEQ(WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, WASM_BLOCK(WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO,
WASM_INC_LOCAL(localIndex)), WASM_INC_LOCAL(localIndex)),
WASM_CALL_FUNCTION0(0)), WASM_BRV(1, WASM_CALL_FUNCTION0(0))),
WASM_I8(55))}; WASM_BRV(0, WASM_I8(55))))};
f->EmitCode(code, sizeof(code)); f->EmitCode(code, sizeof(code));
TestModule(&zone, builder, 55); TestModule(&zone, builder, 55);
} }
@ -153,16 +164,20 @@ TEST(Run_WasmModule_Global) {
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
uint32_t global1 = builder->AddGlobal(kAstI32, 0); uint32_t global1 = builder->AddGlobal(kAstI32, 0);
uint32_t global2 = builder->AddGlobal(kAstI32, 0); uint32_t global2 = builder->AddGlobal(kAstI32, 0);
WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v()); uint16_t f1_index = builder->AddFunction();
WasmFunctionBuilder* f = builder->FunctionAt(f1_index);
f->SetSignature(sigs.i_v());
byte code1[] = { byte code1[] = {
WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))}; WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))};
f1->EmitCode(code1, sizeof(code1)); f->EmitCode(code1, sizeof(code1));
WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v()); uint16_t f2_index = builder->AddFunction();
ExportAsMain(f2); f = builder->FunctionAt(f2_index);
f->SetSignature(sigs.i_v());
ExportAsMain(f);
byte code2[] = {WASM_SET_GLOBAL(global1, WASM_I32V_1(56)), byte code2[] = {WASM_SET_GLOBAL(global1, WASM_I32V_1(56)),
WASM_SET_GLOBAL(global2, WASM_I32V_1(41)), WASM_SET_GLOBAL(global2, WASM_I32V_1(41)),
WASM_RETURN1(WASM_CALL_FUNCTION0(f1->func_index()))}; WASM_RETURN1(WASM_CALL_FUNCTION0(f1_index))};
f2->EmitCode(code2, sizeof(code2)); f->EmitCode(code2, sizeof(code2));
TestModule(&zone, builder, 97); TestModule(&zone, builder, 97);
} }
@ -172,9 +187,11 @@ TEST(Run_WasmModule_Serialization) {
Zone zone(&allocator); Zone zone(&allocator);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
uint16_t f_index = builder->AddFunction();
TestSignatures sigs; TestSignatures sigs;
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i()); WasmFunctionBuilder* f = builder->FunctionAt(f_index);
f->SetSignature(sigs.i_i());
byte code[] = {WASM_GET_LOCAL(0), kExprI32Const, 1, kExprI32Add}; byte code[] = {WASM_GET_LOCAL(0), kExprI32Const, 1, kExprI32Add};
f->EmitCode(code, sizeof(code)); f->EmitCode(code, sizeof(code));
ExportAs(f, kFunctionName); ExportAs(f, kFunctionName);
@ -227,7 +244,7 @@ TEST(Run_WasmModule_Serialization) {
Handle<JSObject> module_object = Handle<JSObject> module_object =
Handle<JSObject>::cast(v8::Utils::OpenHandle(*compiled_module)); Handle<JSObject>::cast(v8::Utils::OpenHandle(*compiled_module));
Handle<JSObject> instance = Handle<JSObject> instance =
WasmModule::Instantiate(isolate, &thrower, module_object, WasmModule::Instantiate(isolate, module_object,
Handle<JSReceiver>::null(), Handle<JSReceiver>::null(),
Handle<JSArrayBuffer>::null()) Handle<JSArrayBuffer>::null())
.ToHandleChecked(); .ToHandleChecked();
@ -241,16 +258,19 @@ TEST(Run_WasmModule_Serialization) {
} }
TEST(Run_WasmModule_MemSize_GrowMem) { TEST(Run_WasmModule_MemSize_GrowMem) {
static const int kPageSize = 0x10000;
// Initial memory size = 16 + GrowMemory(10) // Initial memory size = 16 + GrowMemory(10)
static const int kExpectedValue = 26; static const int kExpectedValue = kPageSize * 26;
TestSignatures sigs; TestSignatures sigs;
v8::internal::AccountingAllocator allocator; v8::internal::AccountingAllocator allocator;
Zone zone(&allocator); Zone zone(&allocator);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); uint16_t f_index = builder->AddFunction();
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
f->SetSignature(sigs.i_v());
ExportAsMain(f); ExportAsMain(f);
byte code[] = {WASM_GROW_MEMORY(WASM_I8(10)), WASM_DROP, WASM_MEMORY_SIZE}; byte code[] = {WASM_GROW_MEMORY(WASM_I8(10)), WASM_MEMORY_SIZE};
f->EmitCode(code, sizeof(code)); f->EmitCode(code, sizeof(code));
TestModule(&zone, builder, kExpectedValue); TestModule(&zone, builder, kExpectedValue);
} }
@ -260,10 +280,12 @@ TEST(Run_WasmModule_GrowMemoryInIf) {
v8::internal::AccountingAllocator allocator; v8::internal::AccountingAllocator allocator;
Zone zone(&allocator); Zone zone(&allocator);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone); WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v()); uint16_t f_index = builder->AddFunction();
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
f->SetSignature(sigs.i_v());
ExportAsMain(f); ExportAsMain(f);
byte code[] = {WASM_IF_ELSE_I(WASM_I32V(0), WASM_GROW_MEMORY(WASM_I32V(1)), byte code[] = {WASM_IF_ELSE(WASM_I32V(0), WASM_GROW_MEMORY(WASM_I32V(1)),
WASM_I32V(12))}; WASM_I32V(12))};
f->EmitCode(code, sizeof(code)); f->EmitCode(code, sizeof(code));
TestModule(&zone, builder, 12); TestModule(&zone, builder, 12);
} }

View File

@ -19,43 +19,42 @@ using namespace v8::internal::compiler;
TEST_BODY(float, F32, WASM_F32_ADD) \ TEST_BODY(float, F32, WASM_F32_ADD) \
TEST_BODY(double, F64, WASM_F64_ADD) TEST_BODY(double, F64, WASM_F64_ADD)
#define LOAD_SET_GLOBAL_TEST_BODY(C_TYPE, MACHINE_TYPE, ADD) \ #define LOAD_SET_GLOBAL_TEST_BODY(C_TYPE, MACHINE_TYPE, ADD) \
TEST(WasmRelocateGlobal##MACHINE_TYPE) { \ TEST(WasmRelocateGlobal##MACHINE_TYPE) { \
TestingModule module(kExecuteCompiled); \ TestingModule module(kExecuteCompiled); \
module.AddGlobal<C_TYPE>(kAst##MACHINE_TYPE); \ module.AddGlobal<C_TYPE>(kAst##MACHINE_TYPE); \
module.AddGlobal<C_TYPE>(kAst##MACHINE_TYPE); \ module.AddGlobal<C_TYPE>(kAst##MACHINE_TYPE); \
\ \
WasmRunner<C_TYPE> r(&module, \ WasmRunner<C_TYPE> r(&module, \
WasmOpcodes::MachineTypeFor(kAst##MACHINE_TYPE)); \ WasmOpcodes::MachineTypeFor(kAst##MACHINE_TYPE)); \
\ \
/* global = global + p0 */ \ /* global = global + p0 */ \
BUILD(r, WASM_SET_GLOBAL(1, ADD(WASM_GET_GLOBAL(0), WASM_GET_LOCAL(0))), \ 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()); \
CHECK_EQ(1, module.instance->function_code.size()); \ \
\ int filter = 1 << RelocInfo::WASM_GLOBAL_REFERENCE; \
int filter = 1 << RelocInfo::WASM_GLOBAL_REFERENCE; \ \
\ Handle<Code> code = module.instance->function_code[0]; \
Handle<Code> code = module.instance->function_code[0]; \ \
\ Address old_start = module.instance->globals_start; \
Address old_start = module.instance->globals_start; \ Address new_start = old_start + 1; \
Address new_start = old_start + 1; \ \
\ Address old_addresses[2]; \
Address old_addresses[4]; \ uint32_t address_index = 0U; \
uint32_t address_index = 0U; \ for (RelocIterator it(*code, filter); !it.done(); it.next()) { \
for (RelocIterator it(*code, filter); !it.done(); it.next()) { \ old_addresses[address_index] = it.rinfo()->wasm_global_reference(); \
old_addresses[address_index] = it.rinfo()->wasm_global_reference(); \ it.rinfo()->update_wasm_global_reference(old_start, new_start); \
it.rinfo()->update_wasm_global_reference(old_start, new_start); \ ++address_index; \
++address_index; \ } \
} \ CHECK_EQ(2U, address_index); \
CHECK_LE(address_index, 4U); \ \
\ address_index = 0U; \
address_index = 0U; \ for (RelocIterator it(*code, filter); !it.done(); it.next()) { \
for (RelocIterator it(*code, filter); !it.done(); it.next()) { \ CHECK_EQ(old_addresses[address_index] + 1, \
CHECK_EQ(old_addresses[address_index] + 1, \ it.rinfo()->wasm_global_reference()); \
it.rinfo()->wasm_global_reference()); \ ++address_index; \
++address_index; \ } \
} \ CHECK_EQ(2U, address_index); \
CHECK_LE(address_index, 4U); \
} }
FOREACH_TYPE(LOAD_SET_GLOBAL_TEST_BODY) FOREACH_TYPE(LOAD_SET_GLOBAL_TEST_BODY)

File diff suppressed because it is too large Load Diff

View File

@ -41,9 +41,9 @@ void testFunctionNameTable(Vector<Vector<const char>> names) {
name.start() + name.length()); name.start() + name.length());
// Make every second function name null-terminated. // Make every second function name null-terminated.
if (func_index % 2) all_names.push_back('\0'); if (func_index % 2) all_names.push_back('\0');
module.functions.push_back( module.functions.push_back({nullptr, 0, 0,
{nullptr, 0, 0, static_cast<uint32_t>(name_offset), static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0, false, false}); static_cast<uint32_t>(name.length()), 0, 0});
++func_index; ++func_index;
} }

View File

@ -104,9 +104,8 @@ TEST(IllegalLoad) {
WasmFunctionCompiler comp1(sigs.v_v(), &module, ArrayVector("mem_oob")); WasmFunctionCompiler comp1(sigs.v_v(), &module, ArrayVector("mem_oob"));
// Set the execution context, such that a runtime error can be thrown. // Set the execution context, such that a runtime error can be thrown.
comp1.SetModuleContext(); comp1.SetModuleContext();
BUILD(comp1, WASM_IF(WASM_ONE, WASM_SEQ(WASM_LOAD_MEM(MachineType::Int32(), BUILD(comp1, WASM_IF(WASM_ONE,
WASM_I32V_1(-3)), WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(-3))));
WASM_DROP)));
uint32_t wasm_index = comp1.CompileAndAdd(); uint32_t wasm_index = comp1.CompileAndAdd();
WasmFunctionCompiler comp2(sigs.v_v(), &module, ArrayVector("call_mem_oob")); WasmFunctionCompiler comp2(sigs.v_v(), &module, ArrayVector("call_mem_oob"));
@ -132,7 +131,7 @@ TEST(IllegalLoad) {
// The column is 1-based, so add 1 to the actual byte offset. // The column is 1-based, so add 1 to the actual byte offset.
ExceptionInfo expected_exceptions[] = { ExceptionInfo expected_exceptions[] = {
{"<WASM UNNAMED>", static_cast<int>(wasm_index), 8}, // -- {"<WASM UNNAMED>", static_cast<int>(wasm_index), 7}, // --
{"<WASM UNNAMED>", static_cast<int>(wasm_index_2), 3}, // -- {"<WASM UNNAMED>", static_cast<int>(wasm_index_2), 3}, // --
{"callFn", 1, 24} // -- {"callFn", 1, 24} // --
}; };

View File

@ -181,7 +181,7 @@ class TestingModule : public ModuleEnv {
module_.functions.reserve(kMaxFunctions); module_.functions.reserve(kMaxFunctions);
} }
uint32_t index = static_cast<uint32_t>(module->functions.size()); uint32_t index = static_cast<uint32_t>(module->functions.size());
module_.functions.push_back({sig, index, 0, 0, 0, 0, 0, false, false}); module_.functions.push_back({sig, index, 0, 0, 0, 0, 0});
instance->function_code.push_back(code); instance->function_code.push_back(code);
if (interpreter_) { if (interpreter_) {
const WasmFunction* function = &module->functions.back(); const WasmFunction* function = &module->functions.back();
@ -230,7 +230,7 @@ class TestingModule : public ModuleEnv {
void AddIndirectFunctionTable(uint16_t* functions, uint32_t table_size) { void AddIndirectFunctionTable(uint16_t* functions, uint32_t table_size) {
module_.function_tables.push_back( module_.function_tables.push_back(
{table_size, table_size, std::vector<int32_t>(), false, false}); {table_size, table_size, std::vector<uint16_t>()});
for (uint32_t i = 0; i < table_size; ++i) { for (uint32_t i = 0; i < table_size; ++i) {
module_.function_tables.back().values.push_back(functions[i]); module_.function_tables.back().values.push_back(functions[i]);
} }
@ -267,8 +267,7 @@ class TestingModule : public ModuleEnv {
const WasmGlobal* AddGlobal(LocalType type) { const WasmGlobal* AddGlobal(LocalType type) {
byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type)); byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type));
global_offset = (global_offset + size - 1) & ~(size - 1); // align global_offset = (global_offset + size - 1) & ~(size - 1); // align
module_.globals.push_back( module_.globals.push_back({0, 0, type, global_offset, false});
{type, true, NO_INIT, global_offset, false, false});
global_offset += size; global_offset += size;
// limit number of globals. // limit number of globals.
CHECK_LT(global_offset, kMaxGlobalsSize); CHECK_LT(global_offset, kMaxGlobalsSize);
@ -284,13 +283,6 @@ inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, ModuleEnv* module,
DecodeResult result = DecodeResult result =
BuildTFGraph(zone->allocator(), &builder, module, sig, start, end); BuildTFGraph(zone->allocator(), &builder, module, sig, start, end);
if (result.failed()) { 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 pc = result.error_pc - result.start;
ptrdiff_t pt = result.error_pt - result.start; ptrdiff_t pt = result.error_pt - result.start;
std::ostringstream str; std::ostringstream str;

View File

@ -67,8 +67,8 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
ModuleOrigin::kWasmOrigin); ModuleOrigin::kWasmOrigin);
if (module_object.is_null()) return Handle<JSObject>::null(); if (module_object.is_null()) return Handle<JSObject>::null();
MaybeHandle<JSObject> maybe_instance = WasmModule::Instantiate( MaybeHandle<JSObject> maybe_instance = WasmModule::Instantiate(
isolate, thrower, module_object.ToHandleChecked(), isolate, module_object.ToHandleChecked(), Handle<JSReceiver>::null(),
Handle<JSReceiver>::null(), Handle<JSArrayBuffer>::null()); Handle<JSArrayBuffer>::null());
Handle<JSObject> instance; Handle<JSObject> instance;
if (!maybe_instance.ToHandle(&instance)) { if (!maybe_instance.ToHandle(&instance)) {
return Handle<JSObject>::null(); return Handle<JSObject>::null();

View File

@ -42,8 +42,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
WasmModuleBuilder builder(&zone); WasmModuleBuilder builder(&zone);
v8::internal::wasm::WasmFunctionBuilder* f = uint16_t f1_index = builder.AddFunction();
builder.AddFunction(sigs.i_iii()); WasmFunctionBuilder* f = builder.FunctionAt(f1_index);
f->SetSignature(sigs.i_iii());
f->EmitCode(data, static_cast<uint32_t>(size)); f->EmitCode(data, static_cast<uint32_t>(size));
f->SetExported(); f->SetExported();
f->SetName("main", 4); f->SetName("main", 4);

View File

@ -5,5 +5,6 @@
#include "test/fuzzer/wasm-section-fuzzers.h" #include "test/fuzzer/wasm-section-fuzzers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return fuzz_wasm_section(v8::internal::wasm::kDataSectionCode, data, size); return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::DataSegments,
data, size);
} }

View File

@ -5,6 +5,6 @@
#include "test/fuzzer/wasm-section-fuzzers.h" #include "test/fuzzer/wasm-section-fuzzers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return fuzz_wasm_section(v8::internal::wasm::kFunctionSectionCode, data, return fuzz_wasm_section(
size); v8::internal::wasm::WasmSection::Code::FunctionSignatures, data, size);
} }

View File

@ -5,5 +5,6 @@
#include "test/fuzzer/wasm-section-fuzzers.h" #include "test/fuzzer/wasm-section-fuzzers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return fuzz_wasm_section(v8::internal::wasm::kGlobalSectionCode, data, size); return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Globals, data,
size);
} }

View File

@ -5,5 +5,6 @@
#include "test/fuzzer/wasm-section-fuzzers.h" #include "test/fuzzer/wasm-section-fuzzers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return fuzz_wasm_section(v8::internal::wasm::kImportSectionCode, data, size); return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::ImportTable,
data, size);
} }

View File

@ -5,5 +5,6 @@
#include "test/fuzzer/wasm-section-fuzzers.h" #include "test/fuzzer/wasm-section-fuzzers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return fuzz_wasm_section(v8::internal::wasm::kMemorySectionCode, data, size); return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Memory, data,
size);
} }

View File

@ -5,6 +5,6 @@
#include "test/fuzzer/wasm-section-fuzzers.h" #include "test/fuzzer/wasm-section-fuzzers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// TODO(titzer): Names section requires a preceding function section. return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Names, data,
return fuzz_wasm_section(v8::internal::wasm::kNameSectionCode, data, size); size);
} }

View File

@ -15,7 +15,7 @@
using namespace v8::internal::wasm; using namespace v8::internal::wasm;
int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data, int fuzz_wasm_section(WasmSection::Code section, const uint8_t* data,
size_t size) { size_t size) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get(); v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate(); v8::Isolate* isolate = support->GetIsolate();
@ -38,18 +38,12 @@ int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data,
ZoneBuffer buffer(&zone); ZoneBuffer buffer(&zone);
buffer.write_u32(kWasmMagic); buffer.write_u32(kWasmMagic);
buffer.write_u32(kWasmVersion); buffer.write_u32(kWasmVersion);
if (section == kNameSectionCode) { const char* name = WasmSection::getName(section);
buffer.write_u8(kUnknownSectionCode); size_t length = WasmSection::getNameLength(section);
buffer.write_size(size + kNameStringLength + 1); buffer.write_size(length); // Section name string size.
buffer.write_u8(kNameStringLength); buffer.write(reinterpret_cast<const uint8_t*>(name), length);
buffer.write(reinterpret_cast<const uint8_t*>(kNameString), buffer.write_u32v(static_cast<uint32_t>(size));
kNameStringLength); buffer.write(data, size);
buffer.write(data, size);
} else {
buffer.write_u8(section);
buffer.write_size(size);
buffer.write(data, size);
}
ErrorThrower thrower(i_isolate, "decoder"); ErrorThrower thrower(i_isolate, "decoder");

View File

@ -10,7 +10,7 @@
#include "src/wasm/wasm-module.h" #include "src/wasm/wasm-module.h"
int fuzz_wasm_section(v8::internal::wasm::WasmSectionCode section, int fuzz_wasm_section(v8::internal::wasm::WasmSection::Code section,
const uint8_t* data, size_t size); const uint8_t* data, size_t size);
#endif // WASM_SECTION_FUZZERS_H_ #endif // WASM_SECTION_FUZZERS_H_

View File

@ -5,5 +5,6 @@
#include "test/fuzzer/wasm-section-fuzzers.h" #include "test/fuzzer/wasm-section-fuzzers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return fuzz_wasm_section(v8::internal::wasm::kTypeSectionCode, data, size); return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Signatures,
data, size);
} }

View File

@ -1,151 +0,0 @@
// 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()");

View File

@ -988,7 +988,6 @@ function TestFunctionTable(stdlib, foreign, buffer) {
return {caller:caller}; return {caller:caller};
} }
print("TestFunctionTable...");
var module = TestFunctionTable(stdlib); var module = TestFunctionTable(stdlib);
assertEquals(55, module.caller(0, 0, 33, 22)); assertEquals(55, module.caller(0, 0, 33, 22));
assertEquals(11, module.caller(0, 1, 33, 22)); assertEquals(11, module.caller(0, 1, 33, 22));
@ -1041,7 +1040,6 @@ function TestForeignFunctions() {
assertEquals(103, module.caller(23, 103)); assertEquals(103, module.caller(23, 103));
} }
print("TestForeignFunctions...");
TestForeignFunctions(); TestForeignFunctions();

View File

@ -20,16 +20,13 @@ function assertModule(module, memsize) {
assertFalse(mem === null); assertFalse(mem === null);
assertFalse(mem === 0); assertFalse(mem === 0);
assertEquals("object", typeof mem); assertEquals("object", typeof mem);
assertTrue(mem instanceof WebAssembly.Memory); assertTrue(mem instanceof ArrayBuffer);
var buf = mem.buffer;
assertTrue(buf instanceof ArrayBuffer);
assertEquals(memsize, buf.byteLength);
for (var i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
module.exports.memory = 0; // should be ignored module.exports.memory = 0; // should be ignored
mem.buffer = 0; // should be ignored assertEquals(mem, module.exports.memory);
assertSame(mem, module.exports.memory);
assertSame(buf, mem.buffer);
} }
assertEquals(memsize, module.exports.memory.byteLength);
} }
function assertFunction(module, func) { function assertFunction(module, func) {

View File

@ -17,7 +17,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
builder.addImport("getValue", kSig_i); builder.addImport("getValue", kSig_i);
builder.addFunction("f", kSig_i) builder.addFunction("f", kSig_i)
.addBody([ .addBody([
kExprCallFunction, 0 kExprCallImport, kArity0, 0
]).exportFunc(); ]).exportFunc();
var module = new WebAssembly.Module(builder.toBuffer()); var module = new WebAssembly.Module(builder.toBuffer());

View File

@ -17,13 +17,13 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("main", kSig_i_i) builder.addFunction("main", kSig_i_i)
.addBody([ .addBody([
kExprI32Const, 1,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32LoadMem, 0, 0, kExprI32LoadMem, 0, 0,
kExprI32Const, 1, kExprCallIndirect, kArity1, signature,
kExprCallIndirect, signature,
kExprGetLocal,0, kExprGetLocal,0,
kExprI32LoadMem,0, 0, kExprI32LoadMem,0, 0,
kExprCallFunction, 0, kExprCallImport, kArity0, 0,
kExprI32Add kExprI32Add
]).exportFunc(); ]).exportFunc();
@ -32,8 +32,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("_wrap_writer", signature) builder.addFunction("_wrap_writer", signature)
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallFunction, 1]); kExprCallImport, kArity1, 1]);
builder.appendToTable([2, 3]); builder.appendToTable([0, 1]);
var module = new WebAssembly.Module(builder.toBuffer()); var module = new WebAssembly.Module(builder.toBuffer());

View File

@ -45,10 +45,10 @@ function listener(event, exec_state, event_data, data) {
assertTrue(!!line, "line number must occur in disassembly"); assertTrue(!!line, "line number must occur in disassembly");
assertTrue(line.length > columnNr, "column number must be valid"); assertTrue(line.length > columnNr, "column number must be valid");
var expected_string; var expected_string;
if (name.endsWith("/1")) { if (name.endsWith("/0")) {
// Function 0 calls the imported function. // Function 0 calls the imported function.
expected_string = "kExprCallFunction,"; expected_string = "kExprCallImport,";
} else if (name.endsWith("/2")) { } else if (name.endsWith("/1")) {
// Function 1 calls function 0. // Function 1 calls function 0.
expected_string = "kExprCallFunction,"; expected_string = "kExprCallFunction,";
} else { } else {
@ -76,7 +76,7 @@ var builder = new WasmModuleBuilder();
builder.addImport("func", kSig_v_v); builder.addImport("func", kSig_v_v);
builder.addFunction("call_import", kSig_v_v) builder.addFunction("call_import", kSig_v_v)
.addBody([kExprCallFunction, 0]) .addBody([kExprCallImport, kArity0, 0])
.exportFunc(); .exportFunc();
// Add a bit of unneccessary code to increase the byte offset. // 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, kExprI32Const, (-7 & 0x7f), kExprSetLocal, 1,
kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add, kExprI64UConvertI32, kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add, kExprI64UConvertI32,
kExprI64Const, 0, kExprI64Const, 0,
kExprI64Ne, kExprIf, kAstStmt, kExprI64Ne, kExprIf,
kExprCallFunction, 1, kExprCallFunction, kArity0, 0,
kExprEnd kExprEnd
]) ])
.exportFunc(); .exportFunc();

View File

@ -7,27 +7,6 @@
// Ensure checked in wasm binaries used by integration tests from v8 hosts // Ensure checked in wasm binaries used by integration tests from v8 hosts
// (such as chromium) are up to date. // (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() { (function ensure_incrementer() {
var buff = readbuffer("test/mjsunit/wasm/incrementer.wasm"); var buff = readbuffer("test/mjsunit/wasm/incrementer.wasm");
var mod = new WebAssembly.Module(buff); var mod = new WebAssembly.Module(buff);

View File

@ -15,9 +15,9 @@ var module = (function () {
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Const, 0, kExprI32Const, 0,
kExprI32Ne, kExprI32Ne,
kExprIf, kAstStmt, kExprIf,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprThrow, kAstStmt, kExprThrow,
kExprEnd, kExprEnd,
kExprI32Const, 1 kExprI32Const, 1
]) ])
@ -26,7 +26,7 @@ var module = (function () {
builder.addFunction("throw_20", kSig_v_v) builder.addFunction("throw_20", kSig_v_v)
.addBody([ .addBody([
kExprI32Const, 20, kExprI32Const, 20,
kExprThrow, kAstStmt kExprThrow
]) ])
.exportFunc() .exportFunc()
@ -43,7 +43,7 @@ var module = (function () {
kExprI32Mul, kExprI32Mul,
kExprI32Const, 20, kExprI32Const, 20,
kExprI32Sub, kExprI32Sub,
kExprThrow, kAstStmt kExprThrow
]) ])
.exportFunc() .exportFunc()

View File

@ -15,7 +15,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([ .addBody([
kExprI8Const, kExprI8Const,
kReturnValue, kReturnValue,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
@ -36,7 +36,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([ .addBody([
kExprI8Const, kExprI8Const,
kReturnValue, kReturnValue,
kExprReturn kExprReturn, kArity1
]) ])
.exportAs("blah") .exportAs("blah")
.exportAs("foo"); .exportAs("foo");
@ -61,7 +61,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([ .addBody([
kExprI8Const, kExprI8Const,
kReturnValue, kReturnValue,
kExprReturn kExprReturn, kArity1
]) ])
.exportAs("0"); .exportAs("0");

View File

@ -16,7 +16,7 @@ function testCallFFI(ffi) {
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, 0, // -- kExprCallFunction, kArity2, 0, // --
]) // -- ]) // --
.exportFunc(); .exportFunc();
@ -106,7 +106,7 @@ assertThrows(function() {
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI64SConvertI32, kExprI64SConvertI32,
kExprCallFunction, index // -- kExprCallImport, kArity1, index // --
]) // -- ]) // --
.exportFunc(); .exportFunc();
var func = function() {return {};}; var func = function() {return {};};

View File

@ -16,7 +16,7 @@ function testCallFFI(func, check) {
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, 0 // -- kExprCallImport, kArity2, 0 // --
]) // -- ]) // --
.exportFunc(); .exportFunc();
@ -80,7 +80,7 @@ print("Constructor");
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, 0 // -- kExprCallImport, kArity2, 0 // --
]) // -- ]) // --
.exportFunc(); .exportFunc();
@ -98,7 +98,7 @@ print("Native function");
builder.addImport("func", sig_index); builder.addImport("func", sig_index);
builder.addFunction("main", sig_index) builder.addFunction("main", sig_index)
.addBody([ .addBody([
kExprCallFunction, 0 // -- kExprCallImport, kArity0, 0 // --
]) // -- ]) // --
.exportFunc(); .exportFunc();
@ -247,7 +247,7 @@ function testCallBinopVoid(type, func, check) {
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, 0, // -- kExprCallImport, kArity2, 0, // --
kExprI8Const, 99 // -- kExprI8Const, 99 // --
]) // -- ]) // --
.exportFunc() .exportFunc()
@ -302,11 +302,11 @@ function testCallPrint() {
builder.addImport("print", makeSig_v_x(kAstF64)); builder.addImport("print", makeSig_v_x(kAstF64));
builder.addFunction("main", makeSig_v_x(kAstF64)) builder.addFunction("main", makeSig_v_x(kAstF64))
.addBody([ .addBody([
kExprI8Const, 97, // -- kExprI8Const, 97, // --
kExprCallFunction, 0, // -- kExprCallImport, kArity1, 0, // --
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprCallFunction, 1 // -- kExprCallImport, kArity1, 1 // --
]) // -- ]) // --
.exportFunc() .exportFunc()
var main = builder.instantiate({print: print}).exports.main; var main = builder.instantiate({print: print}).exports.main;

View File

@ -52,11 +52,11 @@ var builder = new WasmModuleBuilder();
builder.addImport("func", kSig_v_v); builder.addImport("func", kSig_v_v);
builder.addFunction("wasm_1", kSig_v_v) builder.addFunction("wasm_1", kSig_v_v)
.addBody([kExprNop, kExprCallFunction, 2]) .addBody([kExprNop, kExprCallFunction, kArity0, 1])
.exportAs("main"); .exportAs("main");
builder.addFunction("wasm_2", kSig_v_v) builder.addFunction("wasm_2", kSig_v_v)
.addBody([kExprCallFunction, 0]); .addBody([kExprCallImport, kArity0, 0]);
function call_debugger() { function call_debugger() {
debugger; debugger;

View File

@ -19,11 +19,11 @@ var expected_names = ["exec_unreachable", "☠", null,
for (var func_name of func_names) { for (var func_name of func_names) {
last_func_index = builder.addFunction(func_name, kSig_v_v) last_func_index = builder.addFunction(func_name, kSig_v_v)
.addBody([kExprCallFunction, last_func_index]).index; .addBody([kExprCallFunction, kArity0, last_func_index]).index;
} }
builder.addFunction("main", kSig_v_v) builder.addFunction("main", kSig_v_v)
.addBody([kExprCallFunction, last_func_index]) .addBody([kExprCallFunction, kArity0, last_func_index])
.exportFunc(); .exportFunc();
var module = builder.instantiate(); var module = builder.instantiate();

View File

@ -27,8 +27,7 @@ function makeFFI(func, t) {
kExprGetLocal, 7, // -- kExprGetLocal, 7, // --
kExprGetLocal, 8, // -- kExprGetLocal, 8, // --
kExprGetLocal, 9, // -- kExprGetLocal, 9, // --
kExprCallFunction, 0, // -- kExprCallImport, 10, 0, // --
kExprDrop, // --
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprGetLocal, 2, // -- kExprGetLocal, 2, // --
@ -39,7 +38,7 @@ function makeFFI(func, t) {
kExprGetLocal, 7, // -- kExprGetLocal, 7, // --
kExprGetLocal, 8, // -- kExprGetLocal, 8, // --
kExprGetLocal, 9, // -- kExprGetLocal, 9, // --
kExprCallFunction, 0, // -- kExprCallImport, 10, 0 // --
]) // -- ]) // --
.exportFunc(); .exportFunc();

View File

@ -18,22 +18,19 @@ function genGrowMemoryBuilder() {
.addBody([kExprGetLocal, 0, kExprI32LoadMem, 0, 0]) .addBody([kExprGetLocal, 0, kExprI32LoadMem, 0, 0])
.exportFunc(); .exportFunc();
builder.addFunction("store", kSig_i_ii) 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(); .exportFunc();
builder.addFunction("load16", kSig_i_i) builder.addFunction("load16", kSig_i_i)
.addBody([kExprGetLocal, 0, kExprI32LoadMem16U, 0, 0]) .addBody([kExprGetLocal, 0, kExprI32LoadMem16U, 0, 0])
.exportFunc(); .exportFunc();
builder.addFunction("store16", kSig_i_ii) 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(); .exportFunc();
builder.addFunction("load8", kSig_i_i) builder.addFunction("load8", kSig_i_i)
.addBody([kExprGetLocal, 0, kExprI32LoadMem8U, 0, 0]) .addBody([kExprGetLocal, 0, kExprI32LoadMem8U, 0, 0])
.exportFunc(); .exportFunc();
builder.addFunction("store8", kSig_i_ii) 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(); .exportFunc();
return builder; return builder;
} }
@ -247,9 +244,9 @@ function testGrowMemoryCurrentMemory() {
var module = builder.instantiate(); var module = builder.instantiate();
function growMem(pages) { return module.exports.grow_memory(pages); } function growMem(pages) { return module.exports.grow_memory(pages); }
function MemSize() { return module.exports.memory_size(); } function MemSize() { return module.exports.memory_size(); }
assertEquals(1, MemSize()); assertEquals(65536, MemSize());
assertEquals(1, growMem(1)); assertEquals(1, growMem(1));
assertEquals(2, MemSize()); assertEquals(131072, MemSize());
} }
testGrowMemoryCurrentMemory(); testGrowMemoryCurrentMemory();

View File

@ -16,7 +16,7 @@ function testCallImport(func, check) {
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, 0]) // -- kExprCallImport, 2, 0]) // --
.exportAs("main"); .exportAs("main");
var main = builder.instantiate({func: func}).exports.main; var main = builder.instantiate({func: func}).exports.main;
@ -191,7 +191,7 @@ function testCallBinopVoid(type, func, check) {
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, 0, // -- kExprCallImport, 2, 0, // --
kExprI8Const, 99, // -- kExprI8Const, 99, // --
]) ])
.exportFunc("main"); .exportFunc("main");
@ -246,9 +246,9 @@ function testCallPrint() {
builder.addFunction("main", makeSig_r_x(kAstF64, kAstF64)) builder.addFunction("main", makeSig_r_x(kAstF64, kAstF64))
.addBody([ .addBody([
kExprI8Const, 97, // -- kExprI8Const, 97, // --
kExprCallFunction, 0, // -- kExprCallImport, kArity1, 0, // --
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprCallFunction, 1 // -- kExprCallImport, kArity1, 1 // --
]) ])
.exportFunc(); .exportFunc();
@ -270,8 +270,8 @@ function testCallImport2(foo, bar, expected) {
builder.addImport("bar", kSig_i); builder.addImport("bar", kSig_i);
builder.addFunction("main", kSig_i) builder.addFunction("main", kSig_i)
.addBody([ .addBody([
kExprCallFunction, 0, // -- kExprCallImport, kArity0, 0, // --
kExprCallFunction, 1, // -- kExprCallImport, kArity0, 1, // --
kExprI32Add, // -- kExprI32Add, // --
]) // -- ]) // --
.exportFunc(); .exportFunc();

Binary file not shown.

View File

@ -14,7 +14,7 @@ var module = (function () {
builder.addImport("add", sig_index); builder.addImport("add", sig_index);
builder.addFunction("add", sig_index) builder.addFunction("add", sig_index)
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0 kExprGetLocal, 0, kExprGetLocal, 1, kExprCallImport, kArity2, 0
]); ]);
builder.addFunction("sub", sig_index) builder.addFunction("sub", sig_index)
.addBody([ .addBody([
@ -24,13 +24,13 @@ var module = (function () {
]); ]);
builder.addFunction("main", kSig_i_iii) builder.addFunction("main", kSig_i_iii)
.addBody([ .addBody([
kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprGetLocal, 2, kExprGetLocal, 2,
kExprGetLocal, 0, kExprCallIndirect, kArity2, sig_index
kExprCallIndirect, sig_index
]) ])
.exportFunc() .exportFunc()
builder.appendToTable([1, 2, 3]); builder.appendToTable([0, 1, 2]);
return builder.instantiate({add: function(a, b) { return a + b | 0; }}); return builder.instantiate({add: function(a, b) { return a + b | 0; }});
})(); })();
@ -47,40 +47,3 @@ assertEquals(19, module.exports.main(0, 12, 7));
assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)"); assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)");
assertTraps(kTrapFuncInvalid, "module.exports.main(3, 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)");

View File

@ -1,122 +0,0 @@
// 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);

View File

@ -31,17 +31,14 @@ function CheckInstance(instance) {
assertFalse(mem === null); assertFalse(mem === null);
assertFalse(mem === 0); assertFalse(mem === 0);
assertEquals("object", typeof mem); assertEquals("object", typeof mem);
assertTrue(mem instanceof WebAssembly.Memory); assertTrue(mem instanceof ArrayBuffer);
var buf = mem.buffer; for (let i = 0; i < 4; i++) {
assertTrue(buf instanceof ArrayBuffer);
assertEquals(65536, buf.byteLength);
for (var i = 0; i < 4; i++) {
instance.exports.memory = 0; // should be ignored instance.exports.memory = 0; // should be ignored
mem.buffer = 0; // should be ignored
assertSame(mem, instance.exports.memory); assertSame(mem, instance.exports.memory);
assertSame(buf, mem.buffer);
} }
assertEquals(65536, instance.exports.memory.byteLength);
// Check the properties of the main function. // Check the properties of the main function.
let main = instance.exports.main; let main = instance.exports.main;
assertFalse(main === undefined); assertFalse(main === undefined);
@ -64,7 +61,6 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
// Negative tests. // Negative tests.
(function InvalidModules() { (function InvalidModules() {
print("InvalidModules...");
let invalid_cases = [undefined, 1, "", "a", {some:1, obj: "b"}]; let invalid_cases = [undefined, 1, "", "a", {some:1, obj: "b"}];
let len = invalid_cases.length; let len = invalid_cases.length;
for (var i = 0; i < len; ++i) { for (var i = 0; i < len; ++i) {
@ -79,10 +75,9 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
// Compile async an invalid blob. // Compile async an invalid blob.
(function InvalidBinaryAsyncCompilation() { (function InvalidBinaryAsyncCompilation() {
print("InvalidBinaryAsyncCompilation...");
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
builder.addFunction("f", kSig_i_i) builder.addFunction("f", kSig_i_i)
.addBody([kExprCallFunction, 0]); .addBody([kExprCallImport, kArity0, 0]);
let promise = WebAssembly.compile(builder.toBuffer()); let promise = WebAssembly.compile(builder.toBuffer());
promise promise
.then(compiled => .then(compiled =>
@ -92,7 +87,6 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
// Multiple instances tests. // Multiple instances tests.
(function ManyInstances() { (function ManyInstances() {
print("ManyInstances...");
let compiled_module = new WebAssembly.Module(buffer); let compiled_module = new WebAssembly.Module(buffer);
let instance_1 = new WebAssembly.Instance(compiled_module); let instance_1 = new WebAssembly.Instance(compiled_module);
let instance_2 = new WebAssembly.Instance(compiled_module); let instance_2 = new WebAssembly.Instance(compiled_module);
@ -100,7 +94,6 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
})(); })();
(function ManyInstancesAsync() { (function ManyInstancesAsync() {
print("ManyInstancesAsync...");
let promise = WebAssembly.compile(buffer); let promise = WebAssembly.compile(buffer);
promise.then(compiled_module => { promise.then(compiled_module => {
let instance_1 = new WebAssembly.Instance(compiled_module); let instance_1 = new WebAssembly.Instance(compiled_module);
@ -110,7 +103,6 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
})(); })();
(function InstancesAreIsolatedFromEachother() { (function InstancesAreIsolatedFromEachother() {
print("InstancesAreIsolatedFromEachother...");
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
builder.addMemory(1,1, true); builder.addMemory(1,1, true);
var kSig_v_i = makeSig([kAstI32], []); var kSig_v_i = makeSig([kAstI32], []);
@ -120,13 +112,13 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
builder.addFunction("main", kSig_i_i) builder.addFunction("main", kSig_i_i)
.addBody([ .addBody([
kExprI32Const, 1,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32LoadMem, 0, 0, kExprI32LoadMem, 0, 0,
kExprI32Const, 1, kExprCallIndirect, kArity1, signature,
kExprCallIndirect, signature,
kExprGetLocal,0, kExprGetLocal,0,
kExprI32LoadMem,0, 0, kExprI32LoadMem,0, 0,
kExprCallFunction, 0, kExprCallImport, kArity0, 0,
kExprI32Add kExprI32Add
]).exportFunc(); ]).exportFunc();
@ -135,8 +127,8 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
builder.addFunction("_wrap_writer", signature) builder.addFunction("_wrap_writer", signature)
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallFunction, 1]); kExprCallImport, kArity1, 1]);
builder.appendToTable([2, 3]); builder.appendToTable([0, 1]);
var module = new WebAssembly.Module(builder.toBuffer()); var module = new WebAssembly.Module(builder.toBuffer());
@ -163,7 +155,6 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
})(); })();
(function GlobalsArePrivateToTheInstance() { (function GlobalsArePrivateToTheInstance() {
print("GlobalsArePrivateToTheInstance...");
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
builder.addGlobal(kAstI32); builder.addGlobal(kAstI32);
builder.addFunction("read", kSig_i_v) builder.addFunction("read", kSig_i_v)
@ -188,7 +179,6 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
(function InstanceMemoryIsIsolated() { (function InstanceMemoryIsIsolated() {
print("InstanceMemoryIsIsolated...");
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
builder.addMemory(1,1, true); builder.addMemory(1,1, true);

View File

@ -15,36 +15,33 @@ function genModule(memory) {
builder.addMemory(1, 1, true); builder.addMemory(1, 1, true);
builder.addFunction("main", kSig_i_i) builder.addFunction("main", kSig_i_i)
.addBody([ .addBody([
// main body: while(i) { if(mem[i]) return -1; i -= 4; } return 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,
/**/ kExprLoop, kAstStmt, // -- kExprGetLocal,0,
/* */ kExprGetLocal, 0, // -- kExprIf,
/* */ kExprIf, kAstStmt, // -- kExprGetLocal,0,
/* */ kExprGetLocal, 0, // -- kExprI32LoadMem,0,0,
/* */ kExprI32LoadMem, 0, 0, // -- kExprIf,
/* */ kExprIf, kAstStmt, // -- kExprI8Const,255,
/* */ kExprI8Const, 255, // -- kExprReturn, kArity1,
/* */ kExprReturn, // -- kExprEnd,
/* */ kExprEnd, // -- kExprGetLocal,0,
/* */ kExprGetLocal, 0, // -- kExprI8Const,4,
/* */ kExprI8Const, 4, // -- kExprI32Sub,
/* */ kExprI32Sub, // -- kExprSetLocal,0,
/* */ kExprSetLocal, 0, // -- kExprBr, kArity1, 1,
/* */ kExprBr, 1, // -- kExprEnd,
/* */ kExprEnd, // -- kExprEnd,
/* */ kExprEnd, // -- kExprI8Const,0
/**/ kExprI8Const, 0 // --
]) ])
.exportFunc(); .exportFunc();
var module = builder.instantiate(null, memory);
assertTrue(module.exports.memory instanceof WebAssembly.Memory); return builder.instantiate(null, memory);
if (memory != null) assertEquals(memory, module.exports.memory.buffer);
return module;
} }
function testPokeMemory() { function testPokeMemory() {
var module = genModule(null); var module = genModule(null);
var buffer = module.exports.memory.buffer; var buffer = module.exports.memory;
var main = module.exports.main; var main = module.exports.main;
assertEquals(kMemSize, buffer.byteLength); assertEquals(kMemSize, buffer.byteLength);
@ -69,13 +66,9 @@ function testPokeMemory() {
testPokeMemory(); testPokeMemory();
function genAndGetMain(buffer) {
return genModule(buffer).exports.main; // to prevent intermediates living
}
function testSurvivalAcrossGc() { function testSurvivalAcrossGc() {
var checker = genAndGetMain(null); var checker = genModule(null).exports.main;
for (var i = 0; i < 3; i++) { for (var i = 0; i < 5; i++) {
print("gc run ", i); print("gc run ", i);
assertEquals(0, checker(kMemSize - 4)); assertEquals(0, checker(kMemSize - 4));
gc(); gc();
@ -117,8 +110,8 @@ testPokeOuterMemory();
function testOuterMemorySurvivalAcrossGc() { function testOuterMemorySurvivalAcrossGc() {
var buffer = new ArrayBuffer(kMemSize); var buffer = new ArrayBuffer(kMemSize);
var checker = genAndGetMain(buffer); var checker = genModule(buffer).exports.main;
for (var i = 0; i < 3; i++) { for (var i = 0; i < 5; i++) {
print("gc run ", i); print("gc run ", i);
assertEquals(0, checker(kMemSize - 4)); assertEquals(0, checker(kMemSize - 4));
gc(); gc();
@ -140,9 +133,7 @@ function testOOBThrows() {
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprI32LoadMem, 0, 0, kExprI32LoadMem, 0, 0,
kExprI32StoreMem, 0, 0, kExprI32StoreMem, 0, 0
kExprGetLocal, 1,
kExprI32LoadMem, 0, 0,
]) ])
.exportFunc(); .exportFunc();

View File

@ -20,16 +20,13 @@ function assertModule(module, memsize) {
assertFalse(mem === null); assertFalse(mem === null);
assertFalse(mem === 0); assertFalse(mem === 0);
assertEquals("object", typeof mem); assertEquals("object", typeof mem);
assertTrue(mem instanceof WebAssembly.Memory); assertTrue(mem instanceof ArrayBuffer);
var buf = mem.buffer;
assertTrue(buf instanceof ArrayBuffer);
assertEquals(memsize, buf.byteLength);
for (var i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
module.exports.memory = 0; // should be ignored module.exports.memory = 0; // should be ignored
mem.buffer = 0; // should be ignored assertEquals(mem, module.exports.memory);
assertSame(mem, module.exports.memory);
assertSame(buf, mem.buffer);
} }
assertEquals(memsize, module.exports.memory.byteLength);
} }
function assertFunction(module, func) { function assertFunction(module, func) {
@ -87,7 +84,7 @@ function assertFunction(module, func) {
.addBody([ // -- .addBody([ // --
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, f[i >>> 1].index]) // -- kExprCallFunction, kArity2, f[i >>> 1].index]) // --
.exportFunc() .exportFunc()
} }
var module = builder.instantiate(); var module = builder.instantiate();

View File

@ -16,7 +16,7 @@ function testCallImport(func, expected, a, b) {
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, 0]) // -- kExprCallImport, 2, 0]) // --
.exportAs("main"); .exportAs("main");
var main = builder.instantiate({func: func}).exports.main; var main = builder.instantiate({func: func}).exports.main;

View File

@ -45,7 +45,7 @@ var builder = new WasmModuleBuilder();
builder.addImport("func", kSig_v_v); builder.addImport("func", kSig_v_v);
builder.addFunction("main", kSig_v_v) builder.addFunction("main", kSig_v_v)
.addBody([kExprCallFunction, 0]) .addBody([kExprCallImport, kArity0, 0])
.exportAs("main"); .exportAs("main");
builder.addFunction("exec_unreachable", kSig_v_v) builder.addFunction("exec_unreachable", kSig_v_v)
@ -53,14 +53,14 @@ builder.addFunction("exec_unreachable", kSig_v_v)
.exportAs("exec_unreachable"); .exportAs("exec_unreachable");
// Make this function unnamed, just to test also this case. // Make this function unnamed, just to test also this case.
var mem_oob_func = builder.addFunction(undefined, kSig_i_v) var mem_oob_func = builder.addFunction(undefined, kSig_v_v)
// Access the memory at offset -1, to provoke a trap. // Access the memory at offset -1, to provoke a trap.
.addBody([kExprI32Const, 0x7f, kExprI32LoadMem8S, 0, 0]) .addBody([kExprI32Const, 0x7f, kExprI32LoadMem8S, 0, 0])
.exportAs("mem_out_of_bounds"); .exportAs("mem_out_of_bounds");
// Call the mem_out_of_bounds function, in order to have two WASM stack frames. // Call the mem_out_of_bounds function, in order to have two WASM stack frames.
builder.addFunction("call_mem_out_of_bounds", kSig_i_v) builder.addFunction("call_mem_out_of_bounds", kSig_v_v)
.addBody([kExprCallFunction, mem_oob_func.index]) .addBody([kExprCallFunction, kArity0, mem_oob_func.index])
.exportAs("call_mem_out_of_bounds"); .exportAs("call_mem_out_of_bounds");
var module = builder.instantiate({func: STACK}); var module = builder.instantiate({func: STACK});
@ -69,7 +69,7 @@ var module = builder.instantiate({func: STACK});
var expected_string = "Error\n" + var expected_string = "Error\n" +
// The line numbers below will change as this test gains / loses lines.. // The line numbers below will change as this test gains / loses lines..
" at STACK (stack.js:39:11)\n" + // -- " at STACK (stack.js:39:11)\n" + // --
" at main (<WASM>[1]+1)\n" + // -- " at main (<WASM>[0]+1)\n" + // --
" at testSimpleStack (stack.js:76:18)\n" + // -- " at testSimpleStack (stack.js:76:18)\n" + // --
" at stack.js:78:3"; // -- " at stack.js:78:3"; // --
@ -89,7 +89,7 @@ Error.prepareStackTrace = function(error, frames) {
verifyStack(stack, [ verifyStack(stack, [
// isWasm function line pos file // isWasm function line pos file
[ false, "STACK", 39, 0, "stack.js"], [ false, "STACK", 39, 0, "stack.js"],
[ true, "main", 1, 1, null], [ true, "main", 0, 1, null],
[ false, "testStackFrames", 87, 0, "stack.js"], [ false, "testStackFrames", 87, 0, "stack.js"],
[ false, null, 96, 0, "stack.js"] [ false, null, 96, 0, "stack.js"]
]); ]);
@ -103,7 +103,7 @@ Error.prepareStackTrace = function(error, frames) {
assertContains("unreachable", e.message); assertContains("unreachable", e.message);
verifyStack(e.stack, [ verifyStack(e.stack, [
// isWasm function line pos file // isWasm function line pos file
[ true, "exec_unreachable", 2, 1, null], [ true, "exec_unreachable", 1, 1, null],
[ false, "testWasmUnreachable", 100, 0, "stack.js"], [ false, "testWasmUnreachable", 100, 0, "stack.js"],
[ false, null, 111, 0, "stack.js"] [ false, null, 111, 0, "stack.js"]
]); ]);
@ -118,8 +118,8 @@ Error.prepareStackTrace = function(error, frames) {
assertContains("out of bounds", e.message); assertContains("out of bounds", e.message);
verifyStack(e.stack, [ verifyStack(e.stack, [
// isWasm function line pos file // isWasm function line pos file
[ true, "", 3, 3, null], [ true, "", 2, 3, null],
[ true, "call_mem_out_of_bounds", 4, 1, null], [ true, "call_mem_out_of_bounds", 3, 1, null],
[ false, "testWasmMemOutOfBounds", 115, 0, "stack.js"], [ false, "testWasmMemOutOfBounds", 115, 0, "stack.js"],
[ false, null, 127, 0, "stack.js"] [ false, null, 127, 0, "stack.js"]
]); ]);
@ -135,7 +135,7 @@ Error.prepareStackTrace = function(error, frames) {
builder.addFunction("recursion", sig_index) builder.addFunction("recursion", sig_index)
.addBody([ .addBody([
kExprI32Const, 0, kExprI32Const, 0,
kExprCallIndirect, sig_index kExprCallIndirect, kArity0, sig_index
]) ])
.exportFunc() .exportFunc()
builder.appendToTable([0]); builder.appendToTable([0]);

View File

@ -16,7 +16,7 @@ function makeFFI(func) {
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprCallFunction, 0, // -- kExprCallImport, kArity2, 0, // --
]) ])
.exportFunc() .exportFunc()

View File

@ -65,8 +65,8 @@ assertFails(kSig_i_dd, [kExprGetLocal, 0]);
var func = builder.addFunction("", kSig_v_v) var func = builder.addFunction("", kSig_v_v)
.addBody([kExprNop]); .addBody([kExprNop]);
builder.addExplicitSection([kStartSectionCode, 0]); builder.addExplicitSection([kDeclStart, 0]);
builder.addExplicitSection([kStartSectionCode, 0]); builder.addExplicitSection([kDeclStart, 0]);
assertThrows(builder.instantiate); assertThrows(builder.instantiate);
})(); })();
@ -84,7 +84,7 @@ assertFails(kSig_i_dd, [kExprGetLocal, 0]);
builder.addStart(func.index); builder.addStart(func.index);
var module = builder.instantiate(); var module = builder.instantiate();
var memory = module.exports.memory.buffer; var memory = module.exports.memory;
var view = new Int8Array(memory); var view = new Int8Array(memory);
assertEquals(77, view[0]); assertEquals(77, view[0]);
})(); })();
@ -102,7 +102,7 @@ assertFails(kSig_i_dd, [kExprGetLocal, 0]);
builder.addImport("foo", sig_index); builder.addImport("foo", sig_index);
var func = builder.addFunction("", sig_index) var func = builder.addFunction("", sig_index)
.addBody([kExprCallFunction, 0]); .addBody([kExprCallImport, kArity0, 0]);
builder.addStart(func.index); builder.addStart(func.index);

View File

@ -26,8 +26,8 @@ var expect_no_elison = 1;
.addFunction("second_export", sig_index) .addFunction("second_export", sig_index)
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallFunction, 0, kExprCallImport, kArity1, 0,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
@ -39,8 +39,8 @@ var expect_no_elison = 1;
.addFunction("first_export", sig_index) .addFunction("first_export", sig_index)
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallFunction, 2, kExprCallFunction, kArity1, 1,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
first_module first_module
@ -49,8 +49,8 @@ var expect_no_elison = 1;
kExprI32Const, 1, kExprI32Const, 1,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Add, kExprI32Add,
kExprCallFunction, 0, kExprCallImport, kArity1, 0,
kExprReturn kExprReturn, kArity1
]); ]);
var f = second_module var f = second_module
@ -83,8 +83,8 @@ var expect_no_elison = 1;
.addFunction("second_export", sig_index_1) .addFunction("second_export", sig_index_1)
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallFunction, 0, kExprCallImport, kArity1, 0,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
@ -97,8 +97,8 @@ var expect_no_elison = 1;
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprCallFunction, 2, kExprCallFunction, kArity2, 1,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
first_module first_module
@ -106,8 +106,8 @@ var expect_no_elison = 1;
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprCallFunction, 0, kExprCallImport, kArity2, 0,
kExprReturn kExprReturn, kArity1
]); ]);
var f = second_module var f = second_module
@ -142,8 +142,8 @@ var expect_no_elison = 1;
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprGetLocal, 2, kExprGetLocal, 2,
kExprCallFunction, 0, kExprCallImport, kArity3, 0,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
@ -156,8 +156,8 @@ var expect_no_elison = 1;
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprCallFunction, 2, kExprCallFunction, kArity2, 1,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
first_module first_module
@ -165,8 +165,8 @@ var expect_no_elison = 1;
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprCallFunction, 0, kExprCallImport, kArity2, 0,
kExprReturn kExprReturn, kArity1
]); ]);
var f = second_module var f = second_module
@ -200,8 +200,8 @@ var expect_no_elison = 1;
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprCallFunction, 0, kExprCallImport, kArity2, 0,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
@ -214,8 +214,8 @@ var expect_no_elison = 1;
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprCallFunction, 2, kExprCallFunction, kArity2, 1,
kExprReturn kExprReturn, kArity1
]) ])
.exportFunc(); .exportFunc();
first_module first_module
@ -223,8 +223,8 @@ var expect_no_elison = 1;
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprCallFunction, 0, kExprCallImport, kArity2, 0,
kExprReturn kExprReturn, kArity1
]); ]);
var f = second_module var f = second_module

View File

@ -7,7 +7,7 @@
load('test/mjsunit/wasm/wasm-constants.js'); load('test/mjsunit/wasm/wasm-constants.js');
load('test/mjsunit/wasm/wasm-module-builder.js'); load('test/mjsunit/wasm/wasm-module-builder.js');
var debug = true; var debug = false;
(function BasicTest() { (function BasicTest() {
var module = new WasmModuleBuilder(); var module = new WasmModuleBuilder();
@ -25,7 +25,7 @@ var debug = true;
var module = new WasmModuleBuilder(); var module = new WasmModuleBuilder();
var index = module.addImport("print", makeSig_v_x(kAstI32)); var index = module.addImport("print", makeSig_v_x(kAstI32));
module.addFunction("foo", kSig_v_v) module.addFunction("foo", kSig_v_v)
.addBody([kExprI8Const, 13, kExprCallFunction, index]) .addBody([kExprI8Const, 13, kExprCallImport, kArity1, index])
.exportAs("main"); .exportAs("main");
var buffer = module.toBuffer(debug); var buffer = module.toBuffer(debug);
@ -38,7 +38,7 @@ var debug = true;
var module = new WasmModuleBuilder(); var module = new WasmModuleBuilder();
module.addFunction(undefined, kSig_i_i) module.addFunction(undefined, kSig_i_i)
.addLocals({i32_count: 1}) .addLocals({i32_count: 1})
.addBody([kExprGetLocal, 0, kExprSetLocal, 1, kExprGetLocal, 1]) .addBody([kExprGetLocal, 0, kExprSetLocal, 1])
.exportAs("main"); .exportAs("main");
var buffer = module.toBuffer(debug); var buffer = module.toBuffer(debug);
@ -60,7 +60,7 @@ var debug = true;
var module = new WasmModuleBuilder(); var module = new WasmModuleBuilder();
module.addFunction(undefined, makeSig_r_x(p.type, p.type)) module.addFunction(undefined, makeSig_r_x(p.type, p.type))
.addLocals(p.locals) .addLocals(p.locals)
.addBody([kExprGetLocal, 0, kExprSetLocal, 1, kExprGetLocal, 1]) .addBody([kExprGetLocal, 0, kExprSetLocal, 1])
.exportAs("main"); .exportAs("main");
var buffer = module.toBuffer(debug); var buffer = module.toBuffer(debug);
@ -75,7 +75,7 @@ var debug = true;
module.addFunction("add", kSig_i_ii) module.addFunction("add", kSig_i_ii)
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add]); .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add]);
module.addFunction("main", kSig_i_ii) module.addFunction("main", kSig_i_ii)
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0]) .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, kArity2, 0])
.exportAs("main"); .exportAs("main");
var instance = module.instantiate(); var instance = module.instantiate();
@ -89,7 +89,7 @@ var debug = true;
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add]); .addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add]);
module.addFunction("main", kSig_i_iii) module.addFunction("main", kSig_i_iii)
.addBody([kExprGetLocal, .addBody([kExprGetLocal,
1, kExprGetLocal, 2, kExprGetLocal, 0, kExprCallIndirect, 0]) 0, kExprGetLocal, 1, kExprGetLocal, 2, kExprCallIndirect, kArity2, 0])
.exportAs("main"); .exportAs("main");
module.appendToTable([0]); module.appendToTable([0]);
@ -143,7 +143,7 @@ var debug = true;
var module = new WasmModuleBuilder(); var module = new WasmModuleBuilder();
var index = module.addImportWithModule("mod", "print", makeSig_v_x(kAstI32)); var index = module.addImportWithModule("mod", "print", makeSig_v_x(kAstI32));
module.addFunction("foo", kSig_v_v) module.addFunction("foo", kSig_v_v)
.addBody([kExprI8Const, 19, kExprCallFunction, index]) .addBody([kExprI8Const, 19, kExprCallImport, kArity1, index])
.exportAs("main"); .exportAs("main");
var buffer = module.toBuffer(debug); var buffer = module.toBuffer(debug);

View File

@ -30,29 +30,29 @@ var sig_index = builder.addType(kSig_i_v)
builder.addFunction("main", kSig_i_i) builder.addFunction("main", kSig_i_i)
.addBody([ .addBody([
// offset 1 // offset 1
kExprBlock, kAstI32, kExprBlock,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Const, 2, kExprI32Const, 2,
kExprI32LtU, kExprI32LtU,
kExprIf, kAstStmt, kExprIf,
// offset 9 // offset 8
kExprI32Const, 0x7e /* -2 */, kExprI32Const, 0x7e /* -2 */,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32DivU, kExprI32DivU,
// offset 15 // offset 13
kExprI32LoadMem, 0, 0, kExprI32LoadMem, 0, 0,
kExprBr, 1, kExprBr, 1, 1,
kExprEnd, kExprEnd,
// offset 21 // offset 20
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Const, 2, kExprI32Const, 2,
kExprI32Eq, kExprI32Eq,
kExprIf, kAstStmt, kExprIf,
kExprUnreachable, kExprUnreachable,
kExprEnd, kExprEnd,
// offset 30 // offset 28
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallIndirect, sig_index, kExprCallIndirect, kArity0, sig_index,
kExprEnd, kExprEnd,
]) ])
.exportAs("main"); .exportAs("main");
@ -72,7 +72,7 @@ function testWasmTrap(value, reason, position) {
} }
// The actual tests: // The actual tests:
testWasmTrap(0, kTrapDivByZero, 14); testWasmTrap(0, kTrapDivByZero, 12);
testWasmTrap(1, kTrapMemOutOfBounds, 15); testWasmTrap(1, kTrapMemOutOfBounds, 13);
testWasmTrap(2, kTrapUnreachable, 28); testWasmTrap(2, kTrapUnreachable, 26);
testWasmTrap(3, kTrapFuncInvalid, 32); testWasmTrap(3, kTrapFuncInvalid, 30);

View File

@ -49,7 +49,7 @@ function checkImportsAndExports(imported_module_name, imported_function_name,
kSig_v_v); kSig_v_v);
builder.addFunction(internal_function_name, kSig_v_v) builder.addFunction(internal_function_name, kSig_v_v)
.addBody([kExprCallFunction, 0]) .addBody([kExprCallImport, kArity0, 0])
.exportAs(exported_function_name); .exportAs(exported_function_name);
// sanity check: does javascript agree with out shouldThrow annotation? // sanity check: does javascript agree with out shouldThrow annotation?

View File

@ -25,7 +25,7 @@ try {
var data = bytes( var data = bytes(
kWasmFunctionTypeForm, 0, 1, kAstI32, // signature kWasmFunctionTypeForm, 0, 1, kAstI32, // signature
kDeclNoLocals, // -- kDeclNoLocals, // --
kExprBlock, kAstStmt, kExprNop, kExprNop, kExprEnd // body kExprBlock, kExprNop, kExprNop, kExprEnd // body
); );
Wasm.verifyFunction(data); Wasm.verifyFunction(data);

View File

@ -21,7 +21,7 @@ var kWasmH1 = 0x61;
var kWasmH2 = 0x73; var kWasmH2 = 0x73;
var kWasmH3 = 0x6d; var kWasmH3 = 0x6d;
var kWasmV0 = 0xC; var kWasmV0 = 11;
var kWasmV1 = 0; var kWasmV1 = 0;
var kWasmV2 = 0; var kWasmV2 = 0;
var kWasmV3 = 0; var kWasmV3 = 0;
@ -51,24 +51,30 @@ function bytesWithHeader() {
var kDeclNoLocals = 0; var kDeclNoLocals = 0;
// Section declaration constants // Section declaration constants
var kUnknownSectionCode = 0; var kDeclMemory = 0x00;
var kTypeSectionCode = 1; // Function signature declarations var kDeclTypes = 0x01;
var kImportSectionCode = 2; // Import declarations var kDeclFunctions = 0x02;
var kFunctionSectionCode = 3; // Function declarations var kDeclGlobals = 0x03;
var kTableSectionCode = 4; // Indirect function table and other tables var kDeclData = 0x04;
var kMemorySectionCode = 5; // Memory attributes var kDeclTable = 0x05;
var kGlobalSectionCode = 6; // Global declarations var kDeclEnd = 0x06;
var kExportSectionCode = 7; // Exports var kDeclStart = 0x07;
var kStartSectionCode = 8; // Start function declaration var kDeclImports = 0x08;
var kElementSectionCode = 9; // Elements section var kDeclExports = 0x09;
var kCodeSectionCode = 10; // Function code var kDeclFunctions = 0x0a;
var kDataSectionCode = 11; // Data segments var kDeclCode = 0x0b;
var kNameSectionCode = 12; // Name section (encoded as string) var kDeclNames = 0x0c;
var kArity0 = 0;
var kArity1 = 1;
var kArity2 = 2;
var kArity3 = 3;
var kWasmFunctionTypeForm = 0x40; var kWasmFunctionTypeForm = 0x40;
var kWasmAnyFunctionTypeForm = 0x20;
var kResizableMaximumFlag = 1; var section_names = [
"memory", "type", "old_function", "global", "data",
"table", "end", "start", "import", "export",
"function", "code", "name"];
// Function declaration flags // Function declaration flags
var kDeclFunctionName = 0x01; var kDeclFunctionName = 0x01;
@ -83,11 +89,6 @@ var kAstI64 = 2;
var kAstF32 = 3; var kAstF32 = 3;
var kAstF64 = 4; var kAstF64 = 4;
var kExternalFunction = 0;
var kExternalTable = 1;
var kExternalMemory = 2;
var kExternalGlobal = 3;
// Useful signatures // Useful signatures
var kSig_i = makeSig([], [kAstI32]); var kSig_i = makeSig([], [kAstI32]);
var kSig_d = makeSig([], [kAstF64]); var kSig_d = makeSig([], [kAstF64]);
@ -132,8 +133,7 @@ function makeSig_r_xx(r, x) {
} }
// Opcodes // Opcodes
var kExprUnreachable = 0x00; var kExprNop = 0x00;
var kExprNop = 0x0a;
var kExprBlock = 0x01; var kExprBlock = 0x01;
var kExprLoop = 0x02; var kExprLoop = 0x02;
var kExprIf = 0x03; var kExprIf = 0x03;
@ -143,10 +143,9 @@ var kExprBr = 0x06;
var kExprBrIf = 0x07; var kExprBrIf = 0x07;
var kExprBrTable = 0x08; var kExprBrTable = 0x08;
var kExprReturn = 0x09; var kExprReturn = 0x09;
var kExprUnreachable = 0x0a;
var kExprThrow = 0xfa; var kExprThrow = 0xfa;
var kExprEnd = 0x0f; var kExprEnd = 0x0f;
var kExprTeeLocal = 0x19;
var kExprDrop = 0x0b;
var kExprI32Const = 0x10; var kExprI32Const = 0x10;
var kExprI64Const = 0x11; var kExprI64Const = 0x11;
@ -156,6 +155,7 @@ var kExprGetLocal = 0x14;
var kExprSetLocal = 0x15; var kExprSetLocal = 0x15;
var kExprCallFunction = 0x16; var kExprCallFunction = 0x16;
var kExprCallIndirect = 0x17; var kExprCallIndirect = 0x17;
var kExprCallImport = 0x18;
var kExprI8Const = 0xcb; var kExprI8Const = 0xcb;
var kExprGetGlobal = 0xbb; var kExprGetGlobal = 0xbb;
var kExprSetGlobal = 0xbc; var kExprSetGlobal = 0xbc;

View File

@ -61,7 +61,7 @@ class Binary extends Array {
emit_section(section_code, content_generator) { emit_section(section_code, content_generator) {
// Emit section name. // Emit section name.
this.emit_u8(section_code); this.emit_string(section_names[section_code]);
// Emit the section to a temporary buffer: its full length isn't know yet. // Emit the section to a temporary buffer: its full length isn't know yet.
let section = new Binary; let section = new Binary;
content_generator(section); content_generator(section);
@ -147,7 +147,7 @@ class WasmModuleBuilder {
addFunction(name, type) { addFunction(name, type) {
let type_index = (typeof type) == "number" ? type : this.addType(type); let type_index = (typeof type) == "number" ? type : this.addType(type);
let func = new WasmFunctionBuilder(name, type_index); let func = new WasmFunctionBuilder(name, type_index);
func.index = this.functions.length + this.imports.length; func.index = this.functions.length;
this.functions.push(func); this.functions.push(func);
return func; return func;
} }
@ -182,7 +182,7 @@ class WasmModuleBuilder {
// Add type section // Add type section
if (wasm.types.length > 0) { if (wasm.types.length > 0) {
if (debug) print("emitting types @ " + binary.length); if (debug) print("emitting types @ " + binary.length);
binary.emit_section(kTypeSectionCode, section => { binary.emit_section(kDeclTypes, section => {
section.emit_varint(wasm.types.length); section.emit_varint(wasm.types.length);
for (let type of wasm.types) { for (let type of wasm.types) {
section.emit_u8(kWasmFunctionTypeForm); section.emit_u8(kWasmFunctionTypeForm);
@ -198,16 +198,27 @@ 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 // Add imports section
if (wasm.imports.length > 0) { if (wasm.imports.length > 0) {
if (debug) print("emitting imports @ " + binary.length); if (debug) print("emitting imports @ " + binary.length);
binary.emit_section(kImportSectionCode, section => { binary.emit_section(kDeclImports, section => {
section.emit_varint(wasm.imports.length); section.emit_varint(wasm.imports.length);
for (let imp of wasm.imports) { for (let imp of wasm.imports) {
section.emit_varint(imp.type);
section.emit_string(imp.module); section.emit_string(imp.module);
section.emit_string(imp.name || ''); section.emit_string(imp.name || '');
section.emit_u8(kExternalFunction);
section.emit_varint(imp.type);
} }
}); });
} }
@ -218,7 +229,7 @@ class WasmModuleBuilder {
let exports = 0; let exports = 0;
if (wasm.functions.length > 0) { if (wasm.functions.length > 0) {
if (debug) print("emitting function decls @ " + binary.length); if (debug) print("emitting function decls @ " + binary.length);
binary.emit_section(kFunctionSectionCode, section => { binary.emit_section(kDeclFunctions, section => {
section.emit_varint(wasm.functions.length); section.emit_varint(wasm.functions.length);
for (let func of wasm.functions) { for (let func of wasm.functions) {
has_names = has_names || (func.name != undefined && has_names = has_names || (func.name != undefined &&
@ -232,108 +243,56 @@ class WasmModuleBuilder {
// Add table. // Add table.
if (wasm.table.length > 0) { if (wasm.table.length > 0) {
if (debug) print("emitting table @ " + binary.length); if (debug) print("emitting table @ " + binary.length);
binary.emit_section(kTableSectionCode, section => { binary.emit_section(kDeclTable, 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); 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 // Add memory section
if (wasm.memory != undefined) { if (wasm.memory != undefined) {
if (debug) print("emitting memory @ " + binary.length); if (debug) print("emitting memory @ " + binary.length);
binary.emit_section(kMemorySectionCode, section => { binary.emit_section(kDeclMemory, section => {
section.emit_u8(1); // one memory entry
section.emit_varint(kResizableMaximumFlag);
section.emit_varint(wasm.memory.min); section.emit_varint(wasm.memory.min);
section.emit_varint(wasm.memory.max); 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. // Add export table.
var mem_export = (wasm.memory != undefined && wasm.memory.exp); if (exports > 0) {
if (exports > 0 || mem_export) {
if (debug) print("emitting exports @ " + binary.length); if (debug) print("emitting exports @ " + binary.length);
binary.emit_section(kExportSectionCode, section => { binary.emit_section(kDeclExports, section => {
section.emit_varint(exports + (mem_export ? 1 : 0)); section.emit_varint(exports);
for (let func of wasm.functions) { for (let func of wasm.functions) {
for (let exp of func.exports) { for (let exp of func.exports) {
section.emit_string(exp);
section.emit_u8(kExternalFunction);
section.emit_varint(func.index); section.emit_varint(func.index);
section.emit_string(exp);
} }
} }
if (mem_export) {
section.emit_string("memory");
section.emit_u8(kExternalMemory);
section.emit_u8(0);
}
}); });
} }
// Add start function section. // Add start function section.
if (wasm.start_index != undefined) { if (wasm.start_index != undefined) {
if (debug) print("emitting start function @ " + binary.length); if (debug) print("emitting start function @ " + binary.length);
binary.emit_section(kStartSectionCode, section => { binary.emit_section(kDeclStart, section => {
section.emit_varint(wasm.start_index); 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. // Add function bodies.
if (wasm.functions.length > 0) { if (wasm.functions.length > 0) {
// emit function bodies // emit function bodies
if (debug) print("emitting code @ " + binary.length); if (debug) print("emitting code @ " + binary.length);
binary.emit_section(kCodeSectionCode, section => { binary.emit_section(kDeclCode, section => {
section.emit_varint(wasm.functions.length); section.emit_varint(wasm.functions.length);
for (let func of wasm.functions) { for (let func of wasm.functions) {
// Function body length will be patched later. // Function body length will be patched later.
@ -372,13 +331,10 @@ class WasmModuleBuilder {
// Add data segments. // Add data segments.
if (wasm.segments.length > 0) { if (wasm.segments.length > 0) {
if (debug) print("emitting data segments @ " + binary.length); if (debug) print("emitting data segments @ " + binary.length);
binary.emit_section(kDataSectionCode, section => { binary.emit_section(kDeclData, section => {
section.emit_varint(wasm.segments.length); section.emit_varint(wasm.segments.length);
for (let seg of wasm.segments) { 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_varint(seg.addr);
section.emit_u8(kExprEnd);
section.emit_varint(seg.data.length); section.emit_varint(seg.data.length);
section.emit_bytes(seg.data); section.emit_bytes(seg.data);
} }
@ -394,8 +350,7 @@ class WasmModuleBuilder {
// Add function names. // Add function names.
if (has_names) { if (has_names) {
if (debug) print("emitting names @ " + binary.length); if (debug) print("emitting names @ " + binary.length);
binary.emit_section(kUnknownSectionCode, section => { binary.emit_section(kDeclNames, section => {
section.emit_string("name");
section.emit_varint(wasm.functions.length); section.emit_varint(wasm.functions.length);
for (let func of wasm.functions) { for (let func of wasm.functions) {
var name = func.name == undefined ? "" : func.name; var name = func.name == undefined ? "" : func.name;

File diff suppressed because it is too large Load Diff

View File

@ -24,28 +24,30 @@ namespace wasm {
#define B2(a, b) kExprBlock, a, b, kExprEnd #define B2(a, b) kExprBlock, a, b, kExprEnd
#define B3(a, b, c) kExprBlock, a, b, c, kExprEnd #define B3(a, b, c) kExprBlock, a, b, c, kExprEnd
#define TRANSFER_VOID 0 struct ExpectedTarget {
#define TRANSFER_ONE 1
struct ExpectedPcDelta {
pc_t pc; pc_t pc;
pcdiff_t expected; ControlTransfer expected;
}; };
// For nicer error messages. // For nicer error messages.
class ControlTransferMatcher : public MatcherInterface<const pcdiff_t&> { class ControlTransferMatcher : public MatcherInterface<const ControlTransfer&> {
public: public:
explicit ControlTransferMatcher(pc_t pc, const pcdiff_t& expected) explicit ControlTransferMatcher(pc_t pc, const ControlTransfer& expected)
: pc_(pc), expected_(expected) {} : pc_(pc), expected_(expected) {}
void DescribeTo(std::ostream* os) const override { void DescribeTo(std::ostream* os) const override {
*os << "@" << pc_ << " pcdiff = " << expected_; *os << "@" << pc_ << " {pcdiff = " << expected_.pcdiff
<< ", spdiff = " << expected_.spdiff
<< ", action = " << expected_.action << "}";
} }
bool MatchAndExplain(const pcdiff_t& input, bool MatchAndExplain(const ControlTransfer& input,
MatchResultListener* listener) const override { MatchResultListener* listener) const override {
if (input != expected_) { if (input.pcdiff != expected_.pcdiff || input.spdiff != expected_.spdiff ||
*listener << "@" << pc_ << " pcdiff = " << input; input.action != expected_.action) {
*listener << "@" << pc_ << " {pcdiff = " << input.pcdiff
<< ", spdiff = " << input.spdiff
<< ", action = " << input.action << "}";
return false; return false;
} }
return true; return true;
@ -53,43 +55,36 @@ class ControlTransferMatcher : public MatcherInterface<const pcdiff_t&> {
private: private:
pc_t pc_; pc_t pc_;
const pcdiff_t& expected_; const ControlTransfer& expected_;
}; };
class ControlTransferTest : public TestWithZone { class ControlTransferTest : public TestWithZone {
public: public:
void CheckPcDeltas(const byte* start, const byte* end, void CheckControlTransfers(const byte* start, const byte* end,
ExpectedPcDelta* expected_deltas, size_t num_targets) { ExpectedTarget* expected_targets,
size_t num_targets) {
ControlTransferMap map = ControlTransferMap map =
WasmInterpreter::ComputeControlTransfersForTesting(zone(), start, end); WasmInterpreter::ComputeControlTransfersForTesting(zone(), start, end);
// Check all control targets in the map. // Check all control targets in the map.
for (size_t i = 0; i < num_targets; i++) { for (size_t i = 0; i < num_targets; i++) {
pc_t pc = expected_deltas[i].pc; pc_t pc = expected_targets[i].pc;
auto it = map.find(pc); auto it = map.find(pc);
if (it == map.end()) { if (it == map.end()) {
EXPECT_TRUE(false) << "expected control target @ " << pc; printf("expected control target @ +%zu\n", pc);
EXPECT_TRUE(false);
} else { } else {
pcdiff_t expected = expected_deltas[i].expected; ControlTransfer& expected = expected_targets[i].expected;
pcdiff_t& target = it->second; ControlTransfer& target = it->second;
EXPECT_THAT(target, EXPECT_THAT(target,
MakeMatcher(new ControlTransferMatcher(pc, expected))); MakeMatcher(new ControlTransferMatcher(pc, expected)));
} }
} }
// Check there are no other control targets.
CheckNoOtherTargets<ExpectedPcDelta>(start, end, map, expected_deltas,
num_targets);
}
template <typename T>
void CheckNoOtherTargets(const byte* start, const byte* end,
ControlTransferMap& map, T* targets,
size_t num_targets) {
// Check there are no other control targets. // Check there are no other control targets.
for (pc_t pc = 0; start + pc < end; pc++) { for (pc_t pc = 0; start + pc < end; pc++) {
bool found = false; bool found = false;
for (size_t i = 0; i < num_targets; i++) { for (size_t i = 0; i < num_targets; i++) {
if (targets[i].pc == pc) { if (expected_targets[i].pc == pc) {
found = true; found = true;
break; break;
} }
@ -103,128 +98,125 @@ class ControlTransferTest : public TestWithZone {
} }
}; };
#define EXPECT_PC_DELTAS(...) \ // Macro for simplifying tests below.
do { \ #define EXPECT_TARGETS(...) \
ExpectedPcDelta pairs[] = {__VA_ARGS__}; \ do { \
CheckPcDeltas(code, code + sizeof(code), pairs, arraysize(pairs)); \ ExpectedTarget pairs[] = {__VA_ARGS__}; \
CheckControlTransfers(code, code + sizeof(code), pairs, arraysize(pairs)); \
} while (false) } while (false)
TEST_F(ControlTransferTest, SimpleIf) { TEST_F(ControlTransferTest, SimpleIf) {
byte code[] = { byte code[] = {
kExprI32Const, // @0 kExprI32Const, // @0
0, // @1 0, // +1
kExprIf, // @2 kExprIf, // @2
kLocalVoid, // @3 kExprEnd // @3
kExprEnd // @4
}; };
EXPECT_PC_DELTAS({2, 2}); EXPECT_TARGETS({2, {2, 0, ControlTransfer::kPushVoid}}, // --
{3, {1, 0, ControlTransfer::kPushVoid}});
} }
TEST_F(ControlTransferTest, SimpleIf1) { TEST_F(ControlTransferTest, SimpleIf1) {
byte code[] = { byte code[] = {
kExprI32Const, // @0 kExprI32Const, // @0
0, // @1 0, // +1
kExprIf, // @2 kExprIf, // @2
kLocalVoid, // @3 kExprNop, // @3
kExprNop, // @4 kExprEnd // @4
kExprEnd // @5
}; };
EXPECT_PC_DELTAS({2, 3}); EXPECT_TARGETS({2, {3, 0, ControlTransfer::kPushVoid}}, // --
{4, {1, 1, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, SimpleIf2) { TEST_F(ControlTransferTest, SimpleIf2) {
byte code[] = { byte code[] = {
kExprI32Const, // @0 kExprI32Const, // @0
0, // @1 0, // +1
kExprIf, // @2 kExprIf, // @2
kLocalVoid, // @3 kExprNop, // @3
kExprNop, // @4 kExprNop, // @4
kExprNop, // @5 kExprEnd // @5
kExprEnd // @6
}; };
EXPECT_PC_DELTAS({2, 4}); EXPECT_TARGETS({2, {4, 0, ControlTransfer::kPushVoid}}, // --
{5, {1, 2, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, SimpleIfElse) { TEST_F(ControlTransferTest, SimpleIfElse) {
byte code[] = { byte code[] = {
kExprI32Const, // @0 kExprI32Const, // @0
0, // @1 0, // +1
kExprIf, // @2 kExprIf, // @2
kLocalVoid, // @3 kExprElse, // @3
kExprElse, // @4 kExprEnd // @4
kExprEnd // @5
}; };
EXPECT_PC_DELTAS({2, 3}, {4, 2}); EXPECT_TARGETS({2, {2, 0, ControlTransfer::kNoAction}}, // --
} {3, {2, 0, ControlTransfer::kPushVoid}}, // --
{4, {1, 0, ControlTransfer::kPushVoid}});
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) { TEST_F(ControlTransferTest, SimpleIfElse1) {
byte code[] = { byte code[] = {
kExprI32Const, // @0 kExprI32Const, // @0
0, // @1 0, // +1
kExprIf, // @2 kExprIf, // @2
kLocalVoid, // @3 kExprNop, // @3
kExprElse, // @4 kExprElse, // @4
kExprNop, // @5 kExprNop, // @5
kExprEnd // @6 kExprEnd // @6
}; };
EXPECT_PC_DELTAS({2, 3}, {4, 3}); EXPECT_TARGETS({2, {3, 0, ControlTransfer::kNoAction}}, // --
{4, {3, 1, ControlTransfer::kPopAndRepush}}, // --
{6, {1, 1, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, IfBr) { TEST_F(ControlTransferTest, IfBr) {
byte code[] = { byte code[] = {
kExprI32Const, // @0 kExprI32Const, // @0
0, // @1 0, // +1
kExprIf, // @2 kExprIf, // @2
kLocalVoid, // @3 kExprBr, // @3
kExprBr, // @4 ARITY_0, // +1
0, // @5 0, // +1
kExprEnd // @6 kExprEnd // @6
}; };
EXPECT_PC_DELTAS({2, 4}, {4, 3}); EXPECT_TARGETS({2, {5, 0, ControlTransfer::kPushVoid}}, // --
{3, {4, 0, ControlTransfer::kPushVoid}}, // --
{6, {1, 1, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, IfBrElse) { TEST_F(ControlTransferTest, IfBrElse) {
byte code[] = { byte code[] = {
kExprI32Const, // @0 kExprI32Const, // @0
0, // @1 0, // +1
kExprIf, // @2 kExprIf, // @2
kLocalVoid, // @3 kExprBr, // @3
kExprBr, // @4 ARITY_0, // +1
0, // @5 0, // +1
kExprElse, // @6 kExprElse, // @6
kExprEnd // @7 kExprEnd // @7
}; };
EXPECT_PC_DELTAS({2, 5}, {4, 4}, {6, 2}); EXPECT_TARGETS({2, {5, 0, ControlTransfer::kNoAction}}, // --
{3, {5, 0, ControlTransfer::kPushVoid}}, // --
{6, {2, 1, ControlTransfer::kPopAndRepush}}, // --
{7, {1, 0, ControlTransfer::kPushVoid}});
} }
TEST_F(ControlTransferTest, IfElseBr) { TEST_F(ControlTransferTest, IfElseBr) {
byte code[] = { byte code[] = {
kExprI32Const, // @0 kExprI32Const, // @0
0, // @1 0, // +1
kExprIf, // @2 kExprIf, // @2
kLocalVoid, // @3 kExprNop, // @3
kExprElse, // @4 kExprElse, // @4
kExprBr, // @5 kExprBr, // @5
0, // @6 ARITY_0, // +1
kExprEnd // @7 0, // +1
kExprEnd // @8
}; };
EXPECT_PC_DELTAS({2, 3}, {4, 4}, {5, 3}); EXPECT_TARGETS({2, {3, 0, ControlTransfer::kNoAction}}, // --
{4, {5, 1, ControlTransfer::kPopAndRepush}}, // --
{5, {4, 0, ControlTransfer::kPushVoid}}, // --
{8, {1, 1, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, BlockEmpty) { TEST_F(ControlTransferTest, BlockEmpty) {
@ -232,233 +224,177 @@ TEST_F(ControlTransferTest, BlockEmpty) {
kExprBlock, // @0 kExprBlock, // @0
kExprEnd // @1 kExprEnd // @1
}; };
CheckPcDeltas(code, code + sizeof(code), nullptr, 0); EXPECT_TARGETS({1, {1, 0, ControlTransfer::kPushVoid}});
} }
TEST_F(ControlTransferTest, Br0) { TEST_F(ControlTransferTest, Br0) {
byte code[] = { byte code[] = {
kExprBlock, // @0 kExprBlock, // @0
kLocalVoid, // @1 kExprBr, // @1
kExprBr, // @2 ARITY_0, // +1
0, // @3 0, // +1
kExprEnd // @4 kExprEnd // @4
}; };
EXPECT_PC_DELTAS({2, 3}); EXPECT_TARGETS({1, {4, 0, ControlTransfer::kPushVoid}},
{4, {1, 1, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, Br1) { TEST_F(ControlTransferTest, Br1) {
byte code[] = { byte code[] = {
kExprBlock, // @0 kExprBlock, // @0
kLocalVoid, // @1 kExprNop, // @1
kExprNop, // @2 kExprBr, // @2
kExprBr, // @3 ARITY_0, // +1
0, // @4 0, // +1
kExprEnd // @5 kExprEnd // @5
}; };
EXPECT_PC_DELTAS({3, 3}); EXPECT_TARGETS({2, {4, 1, ControlTransfer::kPopAndRepush}}, // --
} {5, {1, 2, ControlTransfer::kPopAndRepush}});
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) { TEST_F(ControlTransferTest, Br2) {
byte code[] = { byte code[] = {
kExprBlock, // @0 kExprBlock, // @0
kLocalVoid, // @1 kExprNop, // @1
kExprNop, // @2 kExprNop, // @2
kExprNop, // @3 kExprBr, // @3
kExprBr, // @4 ARITY_0, // +1
0, // @5 0, // +1
kExprEnd // @6 kExprEnd // @6
}; };
EXPECT_PC_DELTAS({4, 3}); EXPECT_TARGETS({3, {4, 2, ControlTransfer::kPopAndRepush}}, // --
{6, {1, 3, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, Br0b) { TEST_F(ControlTransferTest, Br0b) {
byte code[] = { byte code[] = {
kExprBlock, // @0 kExprBlock, // @0
kLocalVoid, // @1 kExprBr, // @1
kExprBr, // @2 ARITY_0, // +1
0, // @3 0, // +1
kExprNop, // @4 kExprNop, // @4
kExprEnd // @5 kExprEnd // @5
}; };
EXPECT_PC_DELTAS({2, 4}); EXPECT_TARGETS({1, {5, 0, ControlTransfer::kPushVoid}}, // --
{5, {1, 2, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, Br0c) { TEST_F(ControlTransferTest, Br0c) {
byte code[] = { byte code[] = {
kExprBlock, // @0 kExprBlock, // @0
kLocalVoid, // @1 kExprBr, // @1
kExprBr, // @2 ARITY_0, // +1
0, // @3 0, // +1
kExprNop, // @4 kExprNop, // @4
kExprNop, // @5 kExprNop, // @5
kExprEnd // @6 kExprEnd // @6
}; };
EXPECT_PC_DELTAS({2, 5}); EXPECT_TARGETS({1, {6, 0, ControlTransfer::kPushVoid}}, // --
{6, {1, 3, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, SimpleLoop1) { TEST_F(ControlTransferTest, SimpleLoop1) {
byte code[] = { byte code[] = {
kExprLoop, // @0 kExprLoop, // @0
kLocalVoid, // @1 kExprBr, // @1
kExprBr, // @2 ARITY_0, // +1
0, // @3 0, // +1
kExprEnd // @4 kExprEnd // @4
}; };
EXPECT_PC_DELTAS({2, -2}); EXPECT_TARGETS({1, {-1, 0, ControlTransfer::kNoAction}}, // --
{4, {1, 1, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, SimpleLoop2) { TEST_F(ControlTransferTest, SimpleLoop2) {
byte code[] = { byte code[] = {
kExprLoop, // @0 kExprLoop, // @0
kLocalVoid, // @1 kExprNop, // @1
kExprNop, // @2 kExprBr, // @2
kExprBr, // @3 ARITY_0, // +1
0, // @4 0, // +1
kExprEnd // @5 kExprEnd // @5
}; };
EXPECT_PC_DELTAS({3, -3}); EXPECT_TARGETS({2, {-2, 1, ControlTransfer::kNoAction}}, // --
{5, {1, 2, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, SimpleLoopExit1) { TEST_F(ControlTransferTest, SimpleLoopExit1) {
byte code[] = { byte code[] = {
kExprLoop, // @0 kExprLoop, // @0
kLocalVoid, // @1 kExprBr, // @1
kExprBr, // @2 ARITY_0, // +1
1, // @3 1, // +1
kExprEnd // @4 kExprEnd // @4
}; };
EXPECT_PC_DELTAS({2, 3}); EXPECT_TARGETS({1, {4, 0, ControlTransfer::kPushVoid}}, // --
{4, {1, 1, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, SimpleLoopExit2) { TEST_F(ControlTransferTest, SimpleLoopExit2) {
byte code[] = { byte code[] = {
kExprLoop, // @0 kExprLoop, // @0
kLocalVoid, // @1 kExprNop, // @1
kExprNop, // @2 kExprBr, // @2
kExprBr, // @3 ARITY_0, // +1
1, // @4 1, // +1
kExprEnd // @5 kExprEnd // @5
}; };
EXPECT_PC_DELTAS({3, 3}); EXPECT_TARGETS({2, {4, 1, ControlTransfer::kPopAndRepush}}, // --
{5, {1, 2, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, BrTable0) { TEST_F(ControlTransferTest, BrTable0) {
byte code[] = { byte code[] = {
kExprBlock, // @0 kExprBlock, // @0
kLocalVoid, // @1 kExprI8Const, // @1
kExprI8Const, // @2 0, // +1
0, // @3 kExprBrTable, // @3
kExprBrTable, // @4 ARITY_0, // +1
0, // @5 0, // +1
U32V_1(0), // @6 U32_LE(0), // +4
kExprEnd // @7 kExprEnd // @10
}; };
EXPECT_PC_DELTAS({4, 4}); EXPECT_TARGETS({3, {8, 0, ControlTransfer::kPushVoid}}, // --
} {10, {1, 1, ControlTransfer::kPopAndRepush}});
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) { TEST_F(ControlTransferTest, BrTable1) {
byte code[] = { byte code[] = {
kExprBlock, // @0 kExprBlock, // @0
kLocalVoid, // @1 kExprI8Const, // @1
kExprI8Const, // @2 0, // +1
0, // @3 kExprBrTable, // @3
kExprBrTable, // @4 ARITY_0, // +1
1, // @5 1, // +1
U32V_1(0), // @6 U32_LE(0), // +4
U32V_1(0), // @7 U32_LE(0), // +4
kExprEnd // @8 kExprEnd // @14
}; };
EXPECT_PC_DELTAS({4, 5}, {5, 4}); EXPECT_TARGETS({3, {12, 0, ControlTransfer::kPushVoid}}, // --
{4, {11, 0, ControlTransfer::kPushVoid}}, // --
{14, {1, 1, ControlTransfer::kPopAndRepush}});
} }
TEST_F(ControlTransferTest, BrTable2) { TEST_F(ControlTransferTest, BrTable2) {
byte code[] = { byte code[] = {
kExprBlock, // @0 kExprBlock, // @0
kLocalVoid, // @1 kExprBlock, // @1
kExprBlock, // @2 kExprI8Const, // @2
kLocalVoid, // @3 0, // +1
kExprI8Const, // @4 kExprBrTable, // @4
0, // @5 ARITY_0, // +1
kExprBrTable, // @6 2, // +1
2, // @7 U32_LE(0), // +4
U32V_1(0), // @8 U32_LE(0), // +4
U32V_1(0), // @9 U32_LE(1), // +4
U32V_1(1), // @10 kExprEnd, // @19
kExprEnd, // @11 kExprEnd // @19
kExprEnd // @12
}; };
EXPECT_PC_DELTAS({6, 6}, {7, 5}, {8, 5}); 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}});
} }
} // namespace wasm } // namespace wasm

View File

@ -39,7 +39,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Empty0) {
} }
TEST_F(WasmLoopAssignmentAnalyzerTest, Empty1) { TEST_F(WasmLoopAssignmentAnalyzerTest, Empty1) {
byte code[] = {kExprLoop, kLocalVoid, 0}; byte code[] = {kExprLoop, 0};
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
BitVector* assigned = Analyze(code, code + arraysize(code)); BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) { for (int j = 0; j < assigned->length(); j++) {
@ -60,17 +60,6 @@ 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) { TEST_F(WasmLoopAssignmentAnalyzerTest, OneBeyond) {
num_locals = 5; num_locals = 5;
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
@ -109,10 +98,24 @@ 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) { TEST_F(WasmLoopAssignmentAnalyzerTest, BigLocal) {
num_locals = 65000; num_locals = 65000;
for (int i = 13; i < 65000; i = static_cast<int>(i * 1.5)) { for (int i = 13; i < 65000; i = static_cast<int>(i * 1.5)) {
byte code[] = {WASM_LOOP(WASM_I8(11), kExprSetLocal, U32V_3(i))}; byte code[] = {kExprLoop,
1,
kExprSetLocal,
LEBByte(i, 0),
LEBByte(i, 1),
LEBByte(i, 2),
11,
12,
13};
BitVector* assigned = Analyze(code, code + arraysize(code)); BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) { for (int j = 0; j < assigned->length(); j++) {
@ -169,7 +172,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) {
WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, WASM_GET_LOCAL(kSum)), WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, WASM_GET_LOCAL(kSum)),
WASM_GET_LOCAL(kIter))}; WASM_GET_LOCAL(kIter))};
BitVector* assigned = Analyze(code + 2, code + arraysize(code)); BitVector* assigned = Analyze(code + 1, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) { for (int j = 0; j < assigned->length(); j++) {
bool expected = j == kIter || j == kSum; bool expected = j == kIter || j == kSum;
CHECK_EQ(expected, assigned->Contains(j)); CHECK_EQ(expected, assigned->Contains(j));
@ -177,9 +180,9 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) {
} }
TEST_F(WasmLoopAssignmentAnalyzerTest, Malformed) { TEST_F(WasmLoopAssignmentAnalyzerTest, Malformed) {
byte code[] = {kExprLoop, kLocalVoid, kExprF32Neg, kExprBrTable, 0x0e, 'h', byte code[] = {kExprLoop, kExprF32Neg, kExprBrTable, 0x0e, 'h', 'e',
'e', 'l', 'l', 'o', ',', ' ', 'l', 'l', 'o', ',', ' ', 'w',
'w', 'o', 'r', 'l', 'd', '!'}; 'o', 'r', 'l', 'd', '!'};
BitVector* assigned = Analyze(code, code + arraysize(code)); BitVector* assigned = Analyze(code, code + arraysize(code));
CHECK_NULL(assigned); CHECK_NULL(assigned);
} }

File diff suppressed because it is too large Load Diff

View File

@ -56,44 +56,45 @@ TEST_F(WasmMacroGenTest, Statements) {
EXPECT_SIZE(7, WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, WASM_ZERO)); EXPECT_SIZE(7, WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(6, WASM_IF(WASM_ZERO, WASM_NOP)); EXPECT_SIZE(5, WASM_IF(WASM_ZERO, WASM_NOP));
EXPECT_SIZE(8, WASM_IF_ELSE(WASM_ZERO, WASM_NOP, WASM_NOP)); EXPECT_SIZE(7, WASM_IF_ELSE(WASM_ZERO, WASM_NOP, WASM_NOP));
EXPECT_SIZE(5, WASM_SELECT(WASM_ZERO, WASM_NOP, WASM_NOP)); EXPECT_SIZE(5, WASM_SELECT(WASM_ZERO, WASM_NOP, WASM_NOP));
EXPECT_SIZE(2, WASM_BR(0)); EXPECT_SIZE(3, WASM_BR(0));
EXPECT_SIZE(4, WASM_BR_IF(0, WASM_ZERO)); EXPECT_SIZE(5, WASM_BR_IF(0, WASM_ZERO));
EXPECT_SIZE(4, WASM_BLOCK(WASM_NOP)); EXPECT_SIZE(3, WASM_BLOCK(WASM_NOP));
EXPECT_SIZE(5, WASM_BLOCK(WASM_NOP, WASM_NOP)); EXPECT_SIZE(4, WASM_BLOCK(WASM_NOP, WASM_NOP));
EXPECT_SIZE(6, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_NOP)); EXPECT_SIZE(5, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_NOP));
EXPECT_SIZE(5, WASM_INFINITE_LOOP); EXPECT_SIZE(5, WASM_INFINITE_LOOP);
EXPECT_SIZE(4, WASM_LOOP(WASM_NOP)); EXPECT_SIZE(3, WASM_LOOP(WASM_NOP));
EXPECT_SIZE(5, WASM_LOOP(WASM_NOP, WASM_NOP)); EXPECT_SIZE(4, WASM_LOOP(WASM_NOP, WASM_NOP));
EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_NOP)); EXPECT_SIZE(5, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_NOP));
EXPECT_SIZE(5, WASM_LOOP(WASM_BR(0))); EXPECT_SIZE(5, WASM_LOOP(WASM_BR(0)));
EXPECT_SIZE(7, WASM_LOOP(WASM_BR_IF(0, WASM_ZERO))); EXPECT_SIZE(7, WASM_LOOP(WASM_BR_IF(0, WASM_ZERO)));
EXPECT_SIZE(1, WASM_RETURN0); EXPECT_SIZE(2, WASM_RETURN0);
EXPECT_SIZE(3, WASM_RETURN1(WASM_ZERO)); EXPECT_SIZE(4, WASM_RETURN1(WASM_ZERO));
EXPECT_SIZE(1, WASM_UNREACHABLE); EXPECT_SIZE(1, WASM_UNREACHABLE);
} }
TEST_F(WasmMacroGenTest, MacroStatements) { TEST_F(WasmMacroGenTest, MacroStatements) {
EXPECT_SIZE(11, WASM_WHILE(WASM_I8(0), WASM_NOP)); EXPECT_SIZE(10, WASM_WHILE(WASM_I8(0), WASM_NOP));
EXPECT_SIZE(7, WASM_INC_LOCAL(0)); EXPECT_SIZE(7, WASM_INC_LOCAL(0));
EXPECT_SIZE(7, WASM_INC_LOCAL_BY(0, 3)); EXPECT_SIZE(7, WASM_INC_LOCAL_BY(0, 3));
EXPECT_SIZE(2, WASM_CONTINUE(0)); EXPECT_SIZE(3, WASM_BREAK(0));
EXPECT_SIZE(3, WASM_CONTINUE(0));
} }
TEST_F(WasmMacroGenTest, BrTable) { TEST_F(WasmMacroGenTest, BrTable) {
EXPECT_SIZE(5, WASM_BR_TABLE(WASM_ZERO, 1, BR_TARGET(0))); EXPECT_SIZE(9, WASM_BR_TABLE(WASM_ZERO, 1, BR_TARGET(1)));
EXPECT_SIZE(6, WASM_BR_TABLE(WASM_ZERO, 2, BR_TARGET(0), BR_TARGET(0))); EXPECT_SIZE(11, WASM_BR_TABLEV(WASM_ZERO, WASM_ZERO, 1, BR_TARGET(1)));
} }
TEST_F(WasmMacroGenTest, Expressions) { TEST_F(WasmMacroGenTest, Expressions) {
@ -109,34 +110,43 @@ TEST_F(WasmMacroGenTest, Expressions) {
EXPECT_SIZE(3, WASM_NOT(WASM_ZERO)); EXPECT_SIZE(3, WASM_NOT(WASM_ZERO));
EXPECT_SIZE(4, WASM_BRV(1, WASM_ZERO)); EXPECT_SIZE(5, WASM_BRV(1, WASM_ZERO));
EXPECT_SIZE(6, WASM_BRV_IF(1, WASM_ZERO, WASM_ZERO)); EXPECT_SIZE(7, WASM_BRV_IF(1, WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_BLOCK(WASM_ZERO)); EXPECT_SIZE(4, WASM_BLOCK(WASM_ZERO));
EXPECT_SIZE(6, WASM_BLOCK(WASM_NOP, WASM_ZERO)); EXPECT_SIZE(5, WASM_BLOCK(WASM_NOP, WASM_ZERO));
EXPECT_SIZE(7, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_ZERO)); EXPECT_SIZE(6, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_ZERO));
EXPECT_SIZE(5, WASM_LOOP(WASM_ZERO)); EXPECT_SIZE(4, WASM_LOOP(WASM_ZERO));
EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_ZERO)); EXPECT_SIZE(5, WASM_LOOP(WASM_NOP, WASM_ZERO));
EXPECT_SIZE(7, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_ZERO)); EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_ZERO));
} }
TEST_F(WasmMacroGenTest, CallFunction) { TEST_F(WasmMacroGenTest, CallFunction) {
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(0)); EXPECT_SIZE(3, WASM_CALL_FUNCTION0(0));
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(1)); EXPECT_SIZE(3, WASM_CALL_FUNCTION0(1));
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(11)); EXPECT_SIZE(3, WASM_CALL_FUNCTION0(11));
EXPECT_SIZE(4, WASM_CALL_FUNCTION(0, WASM_ZERO)); EXPECT_SIZE(5, WASM_CALL_FUNCTION1(0, WASM_ZERO));
EXPECT_SIZE(6, WASM_CALL_FUNCTION(1, WASM_ZERO, 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));
} }
TEST_F(WasmMacroGenTest, CallIndirect) { TEST_F(WasmMacroGenTest, CallIndirect) {
EXPECT_SIZE(4, WASM_CALL_INDIRECT0(0, WASM_ZERO)); EXPECT_SIZE(5, WASM_CALL_INDIRECT0(0, WASM_ZERO));
EXPECT_SIZE(4, WASM_CALL_INDIRECT0(1, WASM_ZERO)); EXPECT_SIZE(5, WASM_CALL_INDIRECT0(1, WASM_ZERO));
EXPECT_SIZE(4, WASM_CALL_INDIRECT0(11, WASM_ZERO)); EXPECT_SIZE(5, WASM_CALL_INDIRECT0(11, WASM_ZERO));
EXPECT_SIZE(6, WASM_CALL_INDIRECT1(0, WASM_ZERO, WASM_ZERO)); EXPECT_SIZE(7, WASM_CALL_INDIRECT1(0, WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(8, WASM_CALL_INDIRECT2(1, WASM_ZERO, WASM_ZERO, WASM_ZERO)); EXPECT_SIZE(9, WASM_CALL_INDIRECT2(1, WASM_ZERO, WASM_ZERO, WASM_ZERO));
} }
TEST_F(WasmMacroGenTest, Int32Ops) { TEST_F(WasmMacroGenTest, Int32Ops) {