[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
Review-Url: https://codereview.chromium.org/2345593003
Cr-Original-Commit-Position: refs/heads/master@{#39678}
Cr-Commit-Position: refs/heads/master@{#39795}
This commit is contained in:
titzer 2016-09-27 13:46:10 -07:00 committed by Commit bot
parent fcf1bac99a
commit 28392ab196
83 changed files with 5648 additions and 4428 deletions

View File

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

View File

@ -128,6 +128,7 @@ AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
script_(script),
root_(root),
forward_definitions_(zone),
ffi_use_signatures_(zone),
stdlib_types_(zone),
stdlib_math_types_(zone),
module_info_(VariableInfo::ForSpecialSymbol(zone_, kModule)),
@ -329,8 +330,8 @@ AsmTyper::VariableInfo* AsmTyper::ImportLookup(Property* import) {
return i->second;
}
AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) {
ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) const {
const ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
ZoneHashMap::Entry* entry =
scope->Lookup(variable, ComputePointerHash(variable));
if (entry == nullptr && in_function_) {
@ -423,6 +424,8 @@ AsmType* AsmTyper::TypeOf(AstNode* node) const {
return AsmType::None();
}
AsmType* AsmTyper::TypeOf(Variable* v) const { return Lookup(v)->type(); }
AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) {
auto* var_info = Lookup(var);
if (var_info == nullptr) {
@ -2306,9 +2309,20 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
FAIL(call, "Calling something that's not a function.");
}
if (callee_type->AsFFIType() != nullptr &&
return_type == AsmType::Float()) {
FAIL(call, "Foreign functions can't return float.");
if (callee_type->AsFFIType() != nullptr) {
if (return_type == AsmType::Float()) {
FAIL(call, "Foreign functions can't return float.");
}
// Record FFI use signature, since the asm->wasm translator must know
// all uses up-front.
ffi_use_signatures_.emplace_back(
FFIUseSignature(call_var_proxy->var(), zone_));
FFIUseSignature* sig = &ffi_use_signatures_.back();
sig->return_type_ = return_type;
sig->arg_types_.reserve(args.size());
for (size_t i = 0; i < args.size(); ++i) {
sig->arg_types_.emplace_back(args[i]);
}
}
if (!callee_type->CanBeInvokedWith(return_type, args)) {

View File

@ -73,12 +73,26 @@ class AsmTyper final {
const char* error_message() const { return error_message_; }
AsmType* TypeOf(AstNode* node) const;
AsmType* TypeOf(Variable* v) const;
StandardMember VariableAsStandardMember(Variable* var);
typedef std::unordered_set<StandardMember, std::hash<int> > StdlibSet;
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:
friend class v8::internal::wasm::AsmTyperHarnessBuilder;
@ -192,7 +206,7 @@ class AsmTyper final {
// Lookup(Delta, Gamma, x)
//
// Delta is the global_scope_ member, and Gamma, local_scope_.
VariableInfo* Lookup(Variable* variable);
VariableInfo* Lookup(Variable* variable) const;
// All of the ValidateXXX methods below return AsmType::None() in case of
// validation failure.
@ -306,6 +320,7 @@ class AsmTyper final {
AsmType* return_type_ = nullptr;
ZoneVector<VariableInfo*> forward_definitions_;
ZoneVector<FFIUseSignature> ffi_use_signatures_;
ObjectTypeMap stdlib_types_;
ObjectTypeMap stdlib_math_types_;

View File

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

View File

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

View File

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

View File

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

View File

@ -532,6 +532,8 @@ DEFINE_BOOL(wasm_simd_prototype, false,
"enable prototype simd opcodes for wasm")
DEFINE_BOOL(wasm_eh_prototype, false,
"enable prototype exception handling opcodes for wasm")
DEFINE_BOOL(wasm_mv_prototype, false,
"enable prototype multi-value support for wasm")
DEFINE_BOOL(wasm_trap_handler, false,
"use signal handlers to catch out of bounds memory access in wasm"

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -208,6 +208,19 @@ class Decoder {
// Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
void consume_bytes(int size) {
TRACE(" +%d %-20s: %d bytes\n", static_cast<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)) {
pc_ += size;
} else {

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -32,8 +32,11 @@ FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone, ModuleEnv* env,
// Extracts the function offset table from the wasm module 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.
FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
const byte* module_end);
FunctionOffsetsResult DecodeWasmFunctionOffsets(
const byte* module_start, const byte* module_end,
uint32_t num_imported_functions);
WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end);
} // namespace wasm
} // namespace internal

View File

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

View File

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

View File

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

View File

@ -160,7 +160,7 @@ i::MaybeHandle<i::JSObject> InstantiateModule(
}
object = i::wasm::WasmModule::Instantiate(
isolate, module_object.ToHandleChecked(), ffi, memory);
isolate, thrower, module_object.ToHandleChecked(), ffi, memory);
if (!object.is_null()) {
args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
}
@ -296,10 +296,10 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
}
i::MaybeHandle<i::JSObject> instance =
i::wasm::WasmModule::Instantiate(i_isolate, module_obj, ffi, memory);
i::MaybeHandle<i::JSObject> instance = i::wasm::WasmModule::Instantiate(
i_isolate, &thrower, module_obj, ffi, memory);
if (instance.is_null()) {
thrower.Error("Could not instantiate module");
if (!thrower.error()) thrower.Error("Could not instantiate module");
return;
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
@ -384,10 +384,10 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::JSFunction> table_cons(
i::Handle<i::JSFunction> table_ctor(
i_isolate->native_context()->wasm_table_constructor());
i::Handle<i::JSObject> table_obj =
i_isolate->factory()->NewJSObject(table_cons);
i_isolate->factory()->NewJSObject(table_ctor);
i::Handle<i::FixedArray> fixed_array =
i_isolate->factory()->NewFixedArray(initial);
i::Object* null = i_isolate->heap()->null_value();
@ -402,6 +402,7 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(table_obj));
}
void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
@ -435,23 +436,14 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
}
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_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared);
size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) *
static_cast<size_t>(initial);
i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size);
memory_obj->SetInternalField(0, *buffer);
memory_obj->SetInternalField(
1, has_maximum.FromJust()
? 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();
i::Handle<i::JSObject> memory_obj = i::WasmJs::CreateWasmMemoryObject(
i_isolate, buffer, has_maximum.FromJust(), maximum);
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(memory_obj));
}
@ -491,6 +483,24 @@ void WebAssemblyMemoryGetBuffer(
}
} // 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
// internal guts are too ugly to replicate here.
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,

View File

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

View File

@ -17,17 +17,17 @@
#define WASM_MODULE_HEADER U32_LE(kWasmMagic), U32_LE(kWasmVersion)
#define SIG_INDEX(v) U16_LE(v)
// TODO(binji): make SIG_INDEX match this.
#define IMPORT_SIG_INDEX(v) U32V_1(v)
#define FUNC_INDEX(v) U32V_1(v)
#define TABLE_INDEX(v) U32V_1(v)
#define NO_NAME U32V_1(0)
#define NAME_LENGTH(v) U32V_1(v)
#define ENTRY_COUNT(v) U32V_1(v)
#define ZERO_ALIGNMENT 0
#define ZERO_OFFSET 0
#define BR_TARGET(v) U32_LE(v)
#define BR_TARGET(v) U32V_1(v)
#define MASK_7 ((1 << 7) - 1)
#define MASK_14 ((1 << 14) - 1)
@ -62,36 +62,76 @@
#define ARITY_0 0
#define ARITY_1 1
#define ARITY_2 2
#define DEPTH_0 0
#define DEPTH_1 1
#define DEPTH_2 2
#define ARITY_2 2
#define WASM_BLOCK(...) kExprBlock, kLocalVoid, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_T(t, ...) \
kExprBlock, static_cast<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) \
cond, kExprIf, tstmt, kExprElse, fstmt, kExprEnd
cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<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_BR(depth) kExprBr, ARITY_0, static_cast<byte>(depth)
#define WASM_BR_IF(depth, cond) \
cond, kExprBrIf, ARITY_0, static_cast<byte>(depth)
#define WASM_BRV(depth, val) val, kExprBr, ARITY_1, static_cast<byte>(depth)
#define WASM_BRV_IF(depth, val, cond) \
val, cond, kExprBrIf, ARITY_1, static_cast<byte>(depth)
#define WASM_BREAK(depth) kExprBr, ARITY_0, static_cast<byte>(depth + 1)
#define WASM_CONTINUE(depth) kExprBr, ARITY_0, static_cast<byte>(depth)
#define WASM_BREAKV(depth, val) \
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_RETURN0 kExprReturn
#define WASM_RETURN1(val) val, kExprReturn
#define WASM_RETURNN(count, ...) __VA_ARGS__, kExprReturn
#define WASM_BR(depth) kExprBr, static_cast<byte>(depth)
#define WASM_BR_IF(depth, cond) cond, kExprBrIf, static_cast<byte>(depth)
#define WASM_BR_IFD(depth, val, cond) \
val, cond, kExprBrIf, static_cast<byte>(depth), kExprDrop
#define WASM_CONTINUE(depth) kExprBr, static_cast<byte>(depth)
#define WASM_UNREACHABLE kExprUnreachable
#define WASM_BR_TABLE(key, count, ...) \
key, kExprBrTable, ARITY_0, U32V_1(count), __VA_ARGS__
#define WASM_BR_TABLEV(val, key, count, ...) \
val, key, kExprBrTable, ARITY_1, U32V_1(count), __VA_ARGS__
key, kExprBrTable, U32V_1(count), __VA_ARGS__
#define WASM_CASE(x) static_cast<byte>(x), static_cast<byte>(x >> 8)
#define WASM_CASE_BR(x) static_cast<byte>(x), static_cast<byte>(0x80 | (x) >> 8)
@ -343,6 +383,8 @@ class LocalDeclEncoder {
static_cast<byte>(bit_cast<uint64_t>(val) >> 56)
#define WASM_GET_LOCAL(index) kExprGetLocal, 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_SET_GLOBAL(index, val) \
val, kExprSetGlobal, static_cast<byte>(index)
@ -374,49 +416,25 @@ class LocalDeclEncoder {
v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \
alignment, ZERO_OFFSET
#define WASM_CALL_FUNCTION0(index) \
kExprCallFunction, 0, static_cast<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),
#define WASM_CALL_FUNCTION0(index) kExprCallFunction, static_cast<byte>(index)
#define WASM_CALL_FUNCTION(index, ...) \
__VA_ARGS__, kExprCallFunction, static_cast<byte>(index)
// TODO(titzer): change usages of these macros to put func last.
#define WASM_CALL_INDIRECT0(index, func) \
func, kExprCallIndirect, 0, static_cast<byte>(index)
func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT1(index, func, a) \
func, a, kExprCallIndirect, 1, static_cast<byte>(index)
a, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT2(index, func, a, b) \
func, a, b, kExprCallIndirect, 2, static_cast<byte>(index)
a, b, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT3(index, func, a, b, c) \
func, a, b, c, kExprCallIndirect, 3, static_cast<byte>(index)
a, b, c, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT4(index, func, a, b, c, d) \
func, a, b, c, d, kExprCallIndirect, 4, static_cast<byte>(index)
a, b, c, d, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECT5(index, func, a, b, c, d, e) \
func, a, b, c, d, e, kExprCallIndirect, 5, static_cast<byte>(index)
a, b, c, d, e, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_CALL_INDIRECTN(arity, index, func, ...) \
func, __VA_ARGS__, kExprCallIndirect, U32V_1(arity), static_cast<byte>(index)
__VA_ARGS__, func, kExprCallIndirect, static_cast<byte>(index)
#define WASM_NOT(x) x, kExprI32Eqz
#define WASM_SEQ(...) __VA_ARGS__
@ -424,11 +442,16 @@ class LocalDeclEncoder {
//------------------------------------------------------------------------------
// Constructs that are composed of multiple bytecodes.
//------------------------------------------------------------------------------
#define WASM_WHILE(x, y) \
kExprLoop, x, kExprIf, y, kExprBr, ARITY_1, DEPTH_1, kExprEnd, kExprEnd
#define WASM_WHILE(x, y) \
kExprLoop, kLocalVoid, x, kExprIf, kLocalVoid, y, kExprBr, DEPTH_1, \
kExprEnd, kExprEnd
#define WASM_INC_LOCAL(index) \
kExprGetLocal, static_cast<byte>(index), kExprI8Const, 1, kExprI32Add, \
kExprSetLocal, static_cast<byte>(index)
kExprTeeLocal, 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) \
kExprGetLocal, static_cast<byte>(index), kExprI8Const, \
static_cast<byte>(count), kExprI32Add, kExprSetLocal, \
@ -611,4 +634,13 @@ class LocalDeclEncoder {
#define SIZEOF_SIG_ENTRY_x_xx 6
#define SIZEOF_SIG_ENTRY_x_xxx 7
#define WASM_BRV(depth, val) val, kExprBr, static_cast<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_

File diff suppressed because it is too large Load Diff

View File

@ -27,84 +27,71 @@ const size_t kMaxModuleSize = 1024 * 1024 * 1024;
const size_t kMaxFunctionSize = 128 * 1024;
const size_t kMaxStringSize = 256;
const uint32_t kWasmMagic = 0x6d736100;
const uint32_t kWasmVersion = 0x0b;
const uint32_t kWasmVersion = 0x0c;
const uint8_t kWasmFunctionTypeForm = 0x40;
const uint8_t kWasmAnyFunctionTypeForm = 0x20;
// WebAssembly sections are named as strings in the binary format, but
// internally V8 uses an enum to handle them.
//
// Entries have the form F(enumerator, string).
#define FOR_EACH_WASM_SECTION_TYPE(F) \
F(Signatures, 1, "type") \
F(ImportTable, 2, "import") \
F(FunctionSignatures, 3, "function") \
F(FunctionTable, 4, "table") \
F(Memory, 5, "memory") \
F(ExportTable, 6, "export") \
F(StartFunction, 7, "start") \
F(FunctionBodies, 8, "code") \
F(DataSegments, 9, "data") \
F(Names, 10, "name") \
F(Globals, 0, "global") \
F(End, 0, "end")
enum WasmSectionCode {
kUnknownSectionCode = 0, // code for unknown sections
kTypeSectionCode = 1, // Function signature declarations
kImportSectionCode = 2, // Import declarations
kFunctionSectionCode = 3, // Function declarations
kTableSectionCode = 4, // Indirect function table and other tables
kMemorySectionCode = 5, // Memory attributes
kGlobalSectionCode = 6, // Global declarations
kExportSectionCode = 7, // Exports
kStartSectionCode = 8, // Start function declaration
kElementSectionCode = 9, // Elements section
kCodeSectionCode = 10, // Function code
kDataSectionCode = 11, // Data segments
kNameSectionCode = 12, // Name section (encoded as a string)
};
// Contants for the above section types: {LEB128 length, characters...}.
#define WASM_SECTION_MEMORY 6, 'm', 'e', 'm', 'o', 'r', 'y'
#define WASM_SECTION_SIGNATURES 4, 't', 'y', 'p', 'e'
#define WASM_SECTION_GLOBALS 6, 'g', 'l', 'o', 'b', 'a', 'l'
#define WASM_SECTION_DATA_SEGMENTS 4, 'd', 'a', 't', 'a'
#define WASM_SECTION_FUNCTION_TABLE 5, 't', 'a', 'b', 'l', 'e'
#define WASM_SECTION_END 3, 'e', 'n', 'd'
#define WASM_SECTION_START_FUNCTION 5, 's', 't', 'a', 'r', 't'
#define WASM_SECTION_IMPORT_TABLE 6, 'i', 'm', 'p', 'o', 'r', 't'
#define WASM_SECTION_EXPORT_TABLE 6, 'e', 'x', 'p', 'o', 'r', 't'
#define WASM_SECTION_FUNCTION_SIGNATURES \
8, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n'
#define WASM_SECTION_FUNCTION_BODIES 4, 'c', 'o', 'd', 'e'
#define WASM_SECTION_NAMES 4, 'n', 'a', 'm', 'e'
inline bool IsValidSectionCode(uint8_t byte) {
return kTypeSectionCode <= byte && byte <= kDataSectionCode;
}
// Constants for the above section headers' size (LEB128 + characters).
#define WASM_SECTION_MEMORY_SIZE ((size_t)7)
#define WASM_SECTION_SIGNATURES_SIZE ((size_t)5)
#define WASM_SECTION_GLOBALS_SIZE ((size_t)7)
#define WASM_SECTION_DATA_SEGMENTS_SIZE ((size_t)5)
#define WASM_SECTION_FUNCTION_TABLE_SIZE ((size_t)6)
#define WASM_SECTION_END_SIZE ((size_t)4)
#define WASM_SECTION_START_FUNCTION_SIZE ((size_t)6)
#define WASM_SECTION_IMPORT_TABLE_SIZE ((size_t)7)
#define WASM_SECTION_EXPORT_TABLE_SIZE ((size_t)7)
#define WASM_SECTION_FUNCTION_SIGNATURES_SIZE ((size_t)9)
#define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)5)
#define WASM_SECTION_NAMES_SIZE ((size_t)5)
const char* SectionName(WasmSectionCode code);
class WasmDebugInfo;
struct V8_EXPORT_PRIVATE WasmSection {
enum class Code : uint32_t {
#define F(enumerator, order, string) enumerator,
FOR_EACH_WASM_SECTION_TYPE(F)
#undef F
Max
};
static WasmSection::Code begin();
static WasmSection::Code end();
static WasmSection::Code next(WasmSection::Code code);
static const char* getName(Code code);
static int getOrder(Code code);
static size_t getNameLength(Code code);
static WasmSection::Code lookup(const byte* string, uint32_t length);
};
enum WasmFunctionDeclBit {
kDeclFunctionName = 0x01,
kDeclFunctionExport = 0x08
};
// Constants for fixed-size elements within a module.
static const size_t kDeclMemorySize = 3;
static const size_t kDeclDataSegmentSize = 13;
static const uint32_t kMaxReturnCount = 1;
static const uint8_t kResizableMaximumFlag = 1;
static const int32_t kInvalidFunctionIndex = -1;
enum WasmExternalKind {
kExternalFunction = 0,
kExternalTable = 1,
kExternalMemory = 2,
kExternalGlobal = 3
};
// Representation of an initializer expression.
struct WasmInitExpr {
enum WasmInitKind {
kNone,
kGlobalIndex,
kI32Const,
kI64Const,
kF32Const,
kF64Const
} kind;
union {
int32_t i32_const;
int64_t i64_const;
float f32_const;
double f64_const;
uint32_t global_index;
} val;
};
#define NO_INIT \
{ \
WasmInitExpr::kNone, { 0u } \
}
// Static representation of a WASM function.
struct WasmFunction {
@ -115,47 +102,59 @@ struct WasmFunction {
uint32_t name_length; // length in bytes of the name.
uint32_t code_start_offset; // offset in the module bytes of code start.
uint32_t code_end_offset; // offset in the module bytes of code end.
};
// Static representation of an imported WASM function.
struct WasmImport {
FunctionSig* sig; // signature of the function.
uint32_t sig_index; // index into the signature table.
uint32_t module_name_offset; // offset in module bytes of the module name.
uint32_t module_name_length; // length in bytes of the module name.
uint32_t function_name_offset; // offset in module bytes of the import name.
uint32_t function_name_length; // length in bytes of the import name.
};
// Static representation of an exported WASM function.
struct WasmExport {
uint32_t func_index; // index into the function table.
uint32_t name_offset; // offset in module bytes of the name to export.
uint32_t name_length; // length in bytes of the exported name.
bool imported;
bool exported;
};
// Static representation of a wasm global variable.
struct WasmGlobal {
uint32_t name_offset; // offset in the module bytes of the name, if any.
uint32_t name_length; // length in bytes of the global name.
LocalType type; // type of the global.
uint32_t offset; // offset from beginning of globals area.
bool exported; // true if this global is exported.
bool mutability; // {true} if mutable.
WasmInitExpr init; // the initialization expression of the global.
uint32_t offset; // offset into global memory.
bool imported; // true if imported.
bool exported; // true if exported.
};
// Static representation of a wasm data segment.
struct WasmDataSegment {
uint32_t dest_addr; // destination memory address of the data.
WasmInitExpr dest_addr; // destination memory address of the data.
uint32_t source_offset; // start offset in the module bytes.
uint32_t source_size; // end offset in the module bytes.
bool init; // true if loaded upon instantiation.
};
// Static representation of a wasm indirect call table.
struct WasmIndirectFunctionTable {
uint32_t size; // initial table size.
uint32_t max_size; // maximum table size.
std::vector<uint16_t> values; // function table.
uint32_t size; // initial table size.
uint32_t max_size; // maximum table size.
std::vector<int32_t> values; // function table, -1 indicating invalid.
bool imported; // true if imported.
bool exported; // true if exported.
};
// Static representation of how to initialize a table.
struct WasmTableInit {
uint32_t table_index;
WasmInitExpr offset;
std::vector<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 };
@ -163,6 +162,7 @@ enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
// Static representation of a module.
struct WasmModule {
static const uint32_t kPageSize = 0x10000; // Page size, 64kb.
static const uint32_t kMaxLegalPages = 65536; // Maximum legal pages
static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb
static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb
@ -171,7 +171,6 @@ struct WasmModule {
uint32_t min_mem_pages; // minimum size of the memory in 64k pages.
uint32_t max_mem_pages; // maximum size of the memory in 64k pages.
bool mem_export; // true if the memory is exported.
bool mem_external; // true if the memory is external.
// TODO(wasm): reconcile start function index being an int with
// the fact that we index on uint32_t, so we may technically not be
// able to represent some start_function_index -es.
@ -180,12 +179,16 @@ struct WasmModule {
std::vector<WasmGlobal> globals; // globals in this module.
uint32_t globals_size; // size of globals table.
uint32_t num_imported_functions; // number of imported functions.
uint32_t num_declared_functions; // number of declared functions.
uint32_t num_exported_functions; // number of exported functions.
std::vector<FunctionSig*> signatures; // signatures in this module.
std::vector<WasmFunction> functions; // functions in this module.
std::vector<WasmDataSegment> data_segments; // data segments in this module.
std::vector<WasmIndirectFunctionTable> function_tables; // function tables.
std::vector<WasmImport> import_table; // import 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
// use on the try bots, semaphore::Wait() can return while some compilation
// tasks are still executing semaphore::Signal(). If the semaphore is cleaned
@ -234,8 +237,8 @@ struct WasmModule {
// Creates a new instantiation of the module in the given isolate.
V8_EXPORT_PRIVATE static MaybeHandle<JSObject> Instantiate(
Isolate* isolate, Handle<JSObject> module_object, Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory);
Isolate* isolate, ErrorThrower* thrower, Handle<JSObject> module_object,
Handle<JSReceiver> ffi, Handle<JSArrayBuffer> memory);
MaybeHandle<FixedArray> CompileFunctions(Isolate* isolate,
ErrorThrower* thrower) const;
@ -254,7 +257,6 @@ struct WasmModuleInstance {
Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals.
std::vector<Handle<FixedArray>> function_tables; // indirect function tables.
std::vector<Handle<Code>> function_code; // code objects for each function.
std::vector<Handle<Code>> import_code; // code objects for each import.
// -- raw memory ------------------------------------------------------------
byte* mem_start; // start of linear memory.
uint32_t mem_size; // size of the linear memory.
@ -265,7 +267,6 @@ struct WasmModuleInstance {
: module(m),
function_tables(m->function_tables.size()),
function_code(m->functions.size()),
import_code(m->import_table.size()),
mem_start(nullptr),
mem_size(0),
globals_start(nullptr) {}
@ -277,9 +278,6 @@ struct ModuleEnv {
const WasmModule* module;
WasmModuleInstance* instance;
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 {
return module && index < module->globals.size();
@ -290,9 +288,6 @@ struct ModuleEnv {
bool IsValidSignature(uint32_t index) const {
return module && index < module->signatures.size();
}
bool IsValidImport(uint32_t index) const {
return module && index < module->import_table.size();
}
bool IsValidTable(uint32_t index) const {
return module && index < module->function_tables.size();
}
@ -304,10 +299,6 @@ struct ModuleEnv {
DCHECK(IsValidFunction(index));
return module->functions[index].sig;
}
FunctionSig* GetImportSignature(uint32_t index) {
DCHECK(IsValidImport(index));
return module->import_table[index].sig;
}
FunctionSig* GetSignature(uint32_t index) {
DCHECK(IsValidSignature(index));
return module->signatures[index];
@ -319,8 +310,10 @@ struct ModuleEnv {
bool asm_js() { return origin == kAsmJsOrigin; }
Handle<Code> GetCodeOrPlaceholder(uint32_t index) const;
Handle<Code> GetImportCode(uint32_t index);
Handle<Code> GetFunctionCode(uint32_t index) {
DCHECK_NOT_NULL(instance);
return instance->function_code[index];
}
static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone,
FunctionSig* sig);
@ -405,6 +398,9 @@ V8_EXPORT_PRIVATE MaybeHandle<JSObject> CreateModuleObjectFromBytes(
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
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.
// Intended to be called from runtime functions.
// Returns undefined if the runtime support was not setup, nullptr if the

View File

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

View File

@ -22,19 +22,7 @@ namespace wasm {
// Error codes for programmatic checking of the decoder's verification.
enum ErrorCode {
kSuccess,
kError, // TODO(titzer): remove me
kOutOfMemory, // decoder ran out of memory
kEndOfCode, // end of code reached prematurely
kInvalidOpcode, // found invalid opcode
kUnreachableCode, // found unreachable code
kImproperContinue, // improperly nested continue
kImproperBreak, // improperly nested break
kReturnCount, // return count mismatch
kTypeError, // type mismatch
kInvalidLocalIndex, // invalid local
kInvalidGlobalIndex, // invalid global
kInvalidFunctionIndex, // invalid function
kInvalidMemType // invalid memory type
kError, // TODO(titzer): introduce real error codes
};
// The overall result of decoding a function or a module.

View File

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

View File

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

View File

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

View File

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

View File

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

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());
// Make every second function name null-terminated.
if (func_index % 2) all_names.push_back('\0');
module.functions.push_back({nullptr, 0, 0,
static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0});
module.functions.push_back(
{nullptr, 0, 0, static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0, false, false});
++func_index;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,151 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --validate-asm --allow-natives-syntax
var selectedTest = undefined;
//selectedTest = 16;
function skip(a) {
return selectedTest != undefined ? a != selectedTest : false;
}
const assign_in_stmt = [
"if (E) =",
"if (=) E",
"if (E) E; else =",
"for (=; E; S) S",
"for (E; =; S) S",
"for (E; E; =) E",
"for (E; E; E) =",
"do { = } while(E)",
"do { S } while (=)",
];
const assign_in_expr = [
"i32_func(=)",
"(=) ? E : E",
"E ? (=) : E",
"E ? E : (=)",
"(=) + E",
"E + (=)",
"imul(=, E)",
"imul(E, =)",
"~(=)",
"(=) | 0",
"(=), E",
"E, (=)",
"E, E, (=)",
"E, (=), E",
"(=), E, E",
];
const stdlib = {
Math: Math,
Int8Array: Int8Array,
Int16Array: Int16Array,
Int32Array: Int32Array,
Uint8Array: Uint8Array,
Uint16Array: Uint16Array,
Uint32Array: Uint32Array,
Float32Array: Float32Array,
Float64Array: Float64Array,
};
const buffer = new ArrayBuffer(65536);
// Template for a module.
function MODULE_TEMPLATE(stdlib, foreign, buffer) {
"use asm";
var imul = stdlib.Math.imul;
var fround = stdlib.Math.fround;
var M = new stdlib.Int32Array(buffer);
var G = 0;
function void_func() {}
function i32_func(a) {
a = a | 0;
return a | 0;
}
FUNC_DECL
return {main: main};
}
// Template for main function.
{
function main(i32, f32, f64) {
i32 = i32 | 0;
f32 = fround(f32);
f64 = +f64;
FUNC_BODY
}
}
function RunAsmJsTest(asmfunc, expect) {
var asm_source = asmfunc.toString();
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
print("Testing " + asmfunc.name + " (js)...");
var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer);
expect(js_module);
print("Testing " + asmfunc.name + " (asm.js)...");
var asm_module = asmfunc(stdlib, {}, buffer);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
var test = 0;
function DoTheTests(expr, assign, stmt) {
// ==== Expression assignment tests ========================================
for (let e of assign_in_expr) {
if (skip(++test)) continue;
var orig = e;
e = e.replace(/=/g, assign);
e = e.replace(/E/g, expr);
e = e.replace(/S/g, stmt);
var str = main.toString().replace("FUNC_BODY", "return (" + e + ") | 0;");
var asm_source = MODULE_TEMPLATE.toString().replace("FUNC_DECL", str);
// TODO(titzer): a verbosity API for these kinds of tests?
// print(asm_source);
doTest(asm_source, "(" + test + ") " + e);
}
// ==== Statement assignment tests =========================================
for (let e of assign_in_stmt) {
if (skip(++test)) continue;
var orig = e;
e = e.replace(/=/g, assign);
e = e.replace(/E/g, expr);
e = e.replace(/S/g, stmt);
var str = main.toString().replace("FUNC_BODY", e + "; return 0;");
var asm_source = MODULE_TEMPLATE.toString().replace("FUNC_DECL", str);
// print(asm_source);
doTest(asm_source, "(" + test + ") " + e);
}
function doTest(asm_source, orig) {
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
print("Testing JS: " + orig);
var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer);
expect(js_module);
var asmfunc = eval("(" + asm_source + ")");
print("Testing ASMJS: " + orig);
var asm_module = asmfunc(stdlib, {}, buffer);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
function expect(module) { module.main(0, 0, 0); print(" ok"); return true; }
}
DoTheTests("(i32 | 0)", "i32 = 0", "void_func()");
DoTheTests("G", "G = 0", "void_func()");
DoTheTests("G", "G = 0", "G");
DoTheTests("(M[0] | 0)", "M[0] = 0", "void_func()");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,27 @@
// Ensure checked in wasm binaries used by integration tests from v8 hosts
// (such as chromium) are up to date.
(function print_incrementer() {
if (true) return; // remove to regenerate the module
load('test/mjsunit/wasm/wasm-constants.js');
load('test/mjsunit/wasm/wasm-module-builder.js');
var module = new WasmModuleBuilder();
module.addFunction(undefined, kSig_i_i)
.addBody([kExprGetLocal, 0, kExprI32Const, 1, kExprI32Add])
.exportAs("increment");
var buffer = module.toBuffer(true);
var view = new Uint8Array(buffer);
print("const unsigned char module[] = {");
for (var i = 0; i < buffer.byteLength; i++) {
print(" " + view[i] + ",");
}
print("};");
})();
(function ensure_incrementer() {
var buff = readbuffer("test/mjsunit/wasm/incrementer.wasm");
var mod = new WebAssembly.Module(buff);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

@ -14,7 +14,7 @@ var module = (function () {
builder.addImport("add", sig_index);
builder.addFunction("add", sig_index)
.addBody([
kExprGetLocal, 0, kExprGetLocal, 1, kExprCallImport, kArity2, 0
kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0
]);
builder.addFunction("sub", sig_index)
.addBody([
@ -24,13 +24,13 @@ var module = (function () {
]);
builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprCallIndirect, kArity2, sig_index
kExprGetLocal, 0,
kExprCallIndirect, sig_index
])
.exportFunc()
builder.appendToTable([0, 1, 2]);
builder.appendToTable([1, 2, 3]);
return builder.instantiate({add: function(a, b) { return a + b | 0; }});
})();
@ -47,3 +47,40 @@ assertEquals(19, module.exports.main(0, 12, 7));
assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)");
assertTraps(kTrapFuncInvalid, "module.exports.main(3, 12, 33)");
module = (function () {
var builder = new WasmModuleBuilder();
var sig_i_ii = builder.addType(kSig_i_ii);
var sig_i_i = builder.addType(kSig_i_i);
builder.addImport("mul", sig_i_ii);
builder.addFunction("add", sig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
builder.addFunction("popcnt", sig_i_i)
.addBody([
kExprGetLocal, 0, // --
kExprI32Popcnt // --
]);
builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 2,
kExprGetLocal, 0,
kExprCallIndirect, sig_i_ii
])
.exportFunc()
builder.appendToTable([0, 1, 2, 3]);
return builder.instantiate({mul: function(a, b) { return a * b | 0; }});
})();
assertEquals(-6, module.exports.main(0, -2, 3));
assertEquals(99, module.exports.main(1, 22, 77));
assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)");
assertTraps(kTrapFuncSigMismatch, "module.exports.main(3, 12, 33)");
assertTraps(kTrapFuncInvalid, "module.exports.main(4, 12, 33)");

View File

@ -0,0 +1,122 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --expose-gc
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
let nogc = () => {};
function newModule() {
let builder = new WasmModuleBuilder();
builder.addMemory(1, 1, true);
builder.addFunction("main", kSig_i)
.addBody([kExprI32Const, 0, kExprI32LoadMem, 0, 0])
.exportFunc();
return new WebAssembly.Module(builder.toBuffer());
}
function newInstance(module, val) {
var instance = new WebAssembly.Instance(module);
var view = new Int32Array(instance.exports.memory.buffer);
view[0] = val;
return instance;
}
function TestSingleLiveInstance(gc) {
let module = newModule();
print("TestSingleLiveInstance...");
for (var i = 0; i < 5; i++) {
(() => { // don't leak references between iterations.
print(" [" + i + "]...");
gc();
var instance = newInstance(module, i + 99);
assertEquals(i + 99, instance.exports.main());
})();
}
}
TestSingleLiveInstance(nogc);
TestSingleLiveInstance(gc);
function TestMultiInstance(gc) {
let module = newModule();
print("TestMultiInstance...");
// Note: compute the root instances in another function to be
// sure that {roots} really is the only set of roots to the instances.
let roots = (() => { return [
newInstance(module, 33),
newInstance(module, 4444),
newInstance(module, 555555)
];})();
(() => { // don't leak references!
print(" [0]...");
gc();
assertEquals(33, roots[0].exports.main());
roots[0] = null;
})();
(() => { // don't leak references!
print(" [1]...");
gc();
assertEquals(4444, roots[1].exports.main());
roots[1] = null;
})();
(() => { // don't leak references!
print(" [2]...");
gc();
assertEquals(555555, roots[2].exports.main());
roots[2] = null;
})();
}
TestMultiInstance(nogc);
TestMultiInstance(gc);
function TestReclaimingCompiledModule() {
let module = newModule();
print("TestReclaimingCompiledModule...");
let roots = (() => { return [
newInstance(module, 7777),
newInstance(module, 8888),
];})();
(() => { // don't leak references!
print(" [0]...");
assertEquals(7777, roots[0].exports.main());
assertEquals(8888, roots[1].exports.main());
roots[1] = null;
})();
(() => { // don't leak references!
print(" [1]...");
gc();
roots[1] = newInstance(module, 9999);
assertEquals(7777, roots[0].exports.main());
assertEquals(9999, roots[1].exports.main());
roots[0] = null;
roots[1] = null;
})();
(() => { // don't leak references!
print(" [2]...");
gc();
roots[0] = newInstance(module, 11111);
roots[1] = newInstance(module, 22222);
assertEquals(11111, roots[0].exports.main());
assertEquals(22222, roots[1].exports.main());
roots[0] = null;
roots[1] = null;
})();
}
TestReclaimingCompiledModule(nogc);
TestReclaimingCompiledModule(gc);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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