Revert of [wasm] Master CL for Binary 0xC changes. (patchset #26 id:490001 of https://codereview.chromium.org/2345593003/ )
Reason for revert: Main suspect for tsan: https://build.chromium.org/p/client.v8/builders/V8%20Linux64%20TSAN/builds/11893 Also changes layout tests: https://build.chromium.org/p/client.v8.fyi/builders/V8-Blink%20Linux%2064/builds/10036 +mips builder: https://build.chromium.org/p/client.v8.ports/builders/V8%20Mips%20-%20builder/builds/4032 Original issue's description: > [wasm] Master CL for Binary 0xC changes. > > [0xC] Convert to stack machine semantics. > [0xC] Use section codes instead of names. > [0xC] Add elements section decoding. > [0xC] Decoding of globals section. > [0xC] Decoding of memory section. > [0xC] Decoding of imports section. > [0xC] Decoding of exports section. > [0xC] Decoding of data section. > [0xC] Remove CallImport bytecode. > [0xC] Function bodies have an implicit block. > [0xC] Remove the bottom label from loops. > [0xC] Add signatures to blocks. > [0xC] Remove arities from branches. > Add tests for init expression decoding. > Rework compilation of import wrappers and how they are patched. > Rework function indices in debugging. > Fix ASM->WASM builder for stack machine. > Reorganize asm.js foreign functions due to import indices change. > > R=ahaas@chromium.org,rossberg@chromium.org,bradnelson@chromium.org > BUG=chromium:575167 > LOG=Y > > Committed: https://crrev.com/76eb976a67273b8c03c744f64ad850b0432554b9 > Cr-Commit-Position: refs/heads/master@{#39678} TBR=ahaas@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org,rossberg@chromium.org,bradnelson@google.com,titzer@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=chromium:575167 Review-Url: https://codereview.chromium.org/2361053004 Cr-Commit-Position: refs/heads/master@{#39685}
This commit is contained in:
parent
a767d8f956
commit
e1eee748dd
@ -207,8 +207,7 @@ 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, &thrower, module, foreign,
|
||||
memory);
|
||||
i::wasm::WasmModule::Instantiate(isolate, module, foreign, memory);
|
||||
if (maybe_module_object.is_null()) {
|
||||
return MaybeHandle<Object>();
|
||||
}
|
||||
|
@ -128,7 +128,6 @@ 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)),
|
||||
@ -330,8 +329,8 @@ AsmTyper::VariableInfo* AsmTyper::ImportLookup(Property* import) {
|
||||
return i->second;
|
||||
}
|
||||
|
||||
AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) const {
|
||||
const ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
|
||||
AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) {
|
||||
ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
|
||||
ZoneHashMap::Entry* entry =
|
||||
scope->Lookup(variable, ComputePointerHash(variable));
|
||||
if (entry == nullptr && in_function_) {
|
||||
@ -424,8 +423,6 @@ 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) {
|
||||
@ -2309,20 +2306,9 @@ AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
|
||||
FAIL(call, "Calling something that's not a function.");
|
||||
}
|
||||
|
||||
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->AsFFIType() != nullptr &&
|
||||
return_type == AsmType::Float()) {
|
||||
FAIL(call, "Foreign functions can't return float.");
|
||||
}
|
||||
|
||||
if (!callee_type->CanBeInvokedWith(return_type, args)) {
|
||||
|
@ -73,26 +73,12 @@ 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;
|
||||
|
||||
@ -206,7 +192,7 @@ class AsmTyper final {
|
||||
// Lookup(Delta, Gamma, x)
|
||||
//
|
||||
// Delta is the global_scope_ member, and Gamma, local_scope_.
|
||||
VariableInfo* Lookup(Variable* variable) const;
|
||||
VariableInfo* Lookup(Variable* variable);
|
||||
|
||||
// All of the ValidateXXX methods below return AsmType::None() in case of
|
||||
// validation failure.
|
||||
@ -320,7 +306,6 @@ class AsmTyper final {
|
||||
AsmType* return_type_ = nullptr;
|
||||
|
||||
ZoneVector<VariableInfo*> forward_definitions_;
|
||||
ZoneVector<FFIUseSignature> ffi_use_signatures_;
|
||||
ObjectTypeMap stdlib_types_;
|
||||
ObjectTypeMap stdlib_math_types_;
|
||||
|
||||
|
@ -32,7 +32,6 @@ namespace wasm {
|
||||
} while (false)
|
||||
|
||||
enum AsmScope { kModuleScope, kInitScope, kFuncScope, kExportScope };
|
||||
enum ValueFate { kDrop, kLeaveOnStack };
|
||||
|
||||
struct ForeignVariable {
|
||||
Handle<Name> name;
|
||||
@ -62,8 +61,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
typer_(typer),
|
||||
breakable_blocks_(zone),
|
||||
foreign_variables_(zone),
|
||||
init_function_(nullptr),
|
||||
foreign_init_function_(nullptr),
|
||||
init_function_index_(0),
|
||||
foreign_init_function_index_(0),
|
||||
next_table_index_(0),
|
||||
function_tables_(base::HashMap::PointersMatch,
|
||||
ZoneHashMap::kDefaultHashMapCapacity,
|
||||
@ -73,33 +72,35 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
}
|
||||
|
||||
void InitializeInitFunction() {
|
||||
init_function_index_ = builder_->AddFunction();
|
||||
FunctionSig::Builder b(zone(), 0, 0);
|
||||
init_function_ = builder_->AddFunction(b.Build());
|
||||
builder_->MarkStartFunction(init_function_);
|
||||
current_function_builder_ = builder_->FunctionAt(init_function_index_);
|
||||
current_function_builder_->SetSignature(b.Build());
|
||||
builder_->MarkStartFunction(init_function_index_);
|
||||
current_function_builder_ = nullptr;
|
||||
}
|
||||
|
||||
void BuildForeignInitFunction() {
|
||||
foreign_init_function_ = builder_->AddFunction();
|
||||
foreign_init_function_index_ = 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);
|
||||
}
|
||||
foreign_init_function_->SetExported();
|
||||
std::string raw_name = "__foreign_init__";
|
||||
foreign_init_function_->SetName(
|
||||
current_function_builder_ =
|
||||
builder_->FunctionAt(foreign_init_function_index_);
|
||||
current_function_builder_->SetExported();
|
||||
current_function_builder_->SetName(
|
||||
AsmWasmBuilder::foreign_init_name,
|
||||
static_cast<int>(strlen(AsmWasmBuilder::foreign_init_name)));
|
||||
|
||||
foreign_init_function_->SetName(raw_name.data(),
|
||||
static_cast<int>(raw_name.size()));
|
||||
foreign_init_function_->SetSignature(b.Build());
|
||||
current_function_builder_->SetSignature(b.Build());
|
||||
for (size_t pos = 0; pos < foreign_variables_.size(); ++pos) {
|
||||
foreign_init_function_->EmitGetLocal(static_cast<uint32_t>(pos));
|
||||
current_function_builder_->EmitGetLocal(static_cast<uint32_t>(pos));
|
||||
ForeignVariable* fv = &foreign_variables_[pos];
|
||||
uint32_t index = LookupOrInsertGlobal(fv->var, fv->type);
|
||||
foreign_init_function_->EmitWithVarInt(kExprSetGlobal, index);
|
||||
current_function_builder_->EmitWithVarInt(kExprSetGlobal, index);
|
||||
}
|
||||
current_function_builder_ = nullptr;
|
||||
}
|
||||
|
||||
i::Handle<i::FixedArray> GetForeignArgs() {
|
||||
@ -112,20 +113,7 @@ 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();
|
||||
@ -136,7 +124,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
void VisitFunctionDeclaration(FunctionDeclaration* decl) {
|
||||
DCHECK_EQ(kModuleScope, scope_);
|
||||
DCHECK_NULL(current_function_builder_);
|
||||
current_function_builder_ = LookupOrInsertFunction(decl->proxy()->var());
|
||||
uint32_t index = LookupOrInsertFunction(decl->proxy()->var());
|
||||
current_function_builder_ = builder_->FunctionAt(index);
|
||||
scope_ = kFuncScope;
|
||||
RECURSE(Visit(decl->fun()));
|
||||
scope_ = kModuleScope;
|
||||
@ -168,7 +157,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
}
|
||||
}
|
||||
if (scope_ == kFuncScope) {
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock,
|
||||
false);
|
||||
RECURSE(VisitStatements(stmt->statements()));
|
||||
} else {
|
||||
RECURSE(VisitStatements(stmt->statements()));
|
||||
@ -181,12 +171,10 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
|
||||
public:
|
||||
BlockVisitor(AsmWasmBuilderImpl* builder, BreakableStatement* stmt,
|
||||
WasmOpcode opcode)
|
||||
WasmOpcode opcode, bool is_loop)
|
||||
: builder_(builder) {
|
||||
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);
|
||||
builder_->breakable_blocks_.push_back(std::make_pair(stmt, is_loop));
|
||||
builder_->current_function_builder_->Emit(opcode);
|
||||
}
|
||||
~BlockVisitor() {
|
||||
builder_->current_function_builder_->Emit(kExprEnd);
|
||||
@ -195,32 +183,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
};
|
||||
|
||||
void VisitExpressionStatement(ExpressionStatement* stmt) {
|
||||
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);
|
||||
RECURSE(Visit(stmt->expression()));
|
||||
}
|
||||
|
||||
void VisitEmptyStatement(EmptyStatement* stmt) {}
|
||||
@ -230,7 +193,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
void VisitIfStatement(IfStatement* stmt) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
RECURSE(Visit(stmt->condition()));
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
// WASM ifs come with implement blocks for both arms.
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
if (stmt->HasThenStatement()) {
|
||||
@ -244,26 +207,48 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
breakable_blocks_.pop_back();
|
||||
}
|
||||
|
||||
void DoBreakOrContinue(BreakableStatement* target, bool is_continue) {
|
||||
void VisitContinueStatement(ContinueStatement* stmt) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
for (int i = static_cast<int>(breakable_blocks_.size()) - 1; i >= 0; --i) {
|
||||
DCHECK_NOT_NULL(stmt->target());
|
||||
int i = static_cast<int>(breakable_blocks_.size()) - 1;
|
||||
int block_distance = 0;
|
||||
for (; i >= 0; i--) {
|
||||
auto elem = breakable_blocks_.at(i);
|
||||
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;
|
||||
if (elem.first == stmt->target()) {
|
||||
DCHECK(elem.second);
|
||||
break;
|
||||
} else if (elem.second) {
|
||||
block_distance += 2;
|
||||
} else {
|
||||
block_distance += 1;
|
||||
}
|
||||
}
|
||||
UNREACHABLE(); // statement not found
|
||||
}
|
||||
|
||||
void VisitContinueStatement(ContinueStatement* stmt) {
|
||||
DoBreakOrContinue(stmt->target(), true);
|
||||
DCHECK(i >= 0);
|
||||
current_function_builder_->EmitWithU8(kExprBr, ARITY_0);
|
||||
current_function_builder_->EmitVarInt(block_distance);
|
||||
}
|
||||
|
||||
void VisitBreakStatement(BreakStatement* stmt) {
|
||||
DoBreakOrContinue(stmt->target(), false);
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
DCHECK_NOT_NULL(stmt->target());
|
||||
int i = static_cast<int>(breakable_blocks_.size()) - 1;
|
||||
int block_distance = 0;
|
||||
for (; i >= 0; i--) {
|
||||
auto elem = breakable_blocks_.at(i);
|
||||
if (elem.first == stmt->target()) {
|
||||
if (elem.second) {
|
||||
block_distance++;
|
||||
}
|
||||
break;
|
||||
} else if (elem.second) {
|
||||
block_distance += 2;
|
||||
} else {
|
||||
block_distance += 1;
|
||||
}
|
||||
}
|
||||
DCHECK(i >= 0);
|
||||
current_function_builder_->EmitWithU8(kExprBr, ARITY_0);
|
||||
current_function_builder_->EmitVarInt(block_distance);
|
||||
}
|
||||
|
||||
void VisitReturnStatement(ReturnStatement* stmt) {
|
||||
@ -273,7 +258,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
scope_ = kModuleScope;
|
||||
} else if (scope_ == kFuncScope) {
|
||||
RECURSE(Visit(stmt->expression()));
|
||||
current_function_builder_->Emit(kExprReturn);
|
||||
uint8_t arity =
|
||||
TypeOf(stmt->expression()) == kAstStmt ? ARITY_0 : ARITY_1;
|
||||
current_function_builder_->EmitWithU8(kExprReturn, arity);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -289,7 +276,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
VisitVariableProxy(tag);
|
||||
current_function_builder_->EmitI32Const(node->begin);
|
||||
current_function_builder_->Emit(kExprI32LtS);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
if_depth++;
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
HandleCase(node->left, case_to_block, tag, default_block, if_depth);
|
||||
@ -299,7 +286,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
VisitVariableProxy(tag);
|
||||
current_function_builder_->EmitI32Const(node->end);
|
||||
current_function_builder_->Emit(kExprI32GtS);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
if_depth++;
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
HandleCase(node->right, case_to_block, tag, default_block, if_depth);
|
||||
@ -309,9 +296,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
VisitVariableProxy(tag);
|
||||
current_function_builder_->EmitI32Const(node->begin);
|
||||
current_function_builder_->Emit(kExprI32Eq);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
DCHECK(case_to_block.find(node->begin) != case_to_block.end());
|
||||
current_function_builder_->Emit(kExprBr);
|
||||
current_function_builder_->EmitWithU8(kExprBr, ARITY_0);
|
||||
current_function_builder_->EmitVarInt(1 + if_depth +
|
||||
case_to_block[node->begin]);
|
||||
current_function_builder_->Emit(kExprEnd);
|
||||
@ -323,22 +310,22 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
} else {
|
||||
VisitVariableProxy(tag);
|
||||
}
|
||||
current_function_builder_->Emit(kExprBrTable);
|
||||
current_function_builder_->EmitWithU8(kExprBrTable, ARITY_0);
|
||||
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()) {
|
||||
uint32_t target = if_depth + case_to_block[v];
|
||||
current_function_builder_->EmitVarInt(target);
|
||||
byte break_code[] = {BR_TARGET(if_depth + case_to_block[v])};
|
||||
current_function_builder_->EmitCode(break_code, sizeof(break_code));
|
||||
} else {
|
||||
uint32_t target = if_depth + default_block;
|
||||
current_function_builder_->EmitVarInt(target);
|
||||
byte break_code[] = {BR_TARGET(if_depth + default_block)};
|
||||
current_function_builder_->EmitCode(break_code, sizeof(break_code));
|
||||
}
|
||||
if (v == kMaxInt) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint32_t target = if_depth + default_block;
|
||||
current_function_builder_->EmitVarInt(target);
|
||||
byte break_code[] = {BR_TARGET(if_depth + default_block)};
|
||||
current_function_builder_->EmitCode(break_code, sizeof(break_code));
|
||||
}
|
||||
|
||||
while (if_depth-- != prev_if_depth) {
|
||||
@ -355,14 +342,14 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
if (case_count == 0) {
|
||||
return;
|
||||
}
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock, false);
|
||||
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));
|
||||
blocks.push_back(new BlockVisitor(this, nullptr, kExprBlock, false));
|
||||
if (!clause->is_default()) {
|
||||
Literal* label = clause->label()->AsLiteral();
|
||||
Handle<Object> value = label->value();
|
||||
@ -379,12 +366,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);
|
||||
BlockVisitor switch_logic_block(this, nullptr, kExprBlock, false);
|
||||
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_->Emit(kExprBr);
|
||||
current_function_builder_->EmitWithU8(kExprBr, ARITY_0);
|
||||
current_function_builder_->EmitVarInt(default_block);
|
||||
}
|
||||
}
|
||||
@ -401,24 +388,22 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
|
||||
void VisitDoWhileStatement(DoWhileStatement* stmt) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true);
|
||||
RECURSE(Visit(stmt->body()));
|
||||
RECURSE(Visit(stmt->cond()));
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
current_function_builder_->EmitWithU8(kExprBr, 1);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 1);
|
||||
current_function_builder_->Emit(kExprEnd);
|
||||
}
|
||||
|
||||
void VisitWhileStatement(WhileStatement* stmt) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true);
|
||||
RECURSE(Visit(stmt->cond()));
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
RECURSE(Visit(stmt->body()));
|
||||
current_function_builder_->EmitWithU8(kExprBr, 1);
|
||||
current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 1);
|
||||
current_function_builder_->Emit(kExprEnd);
|
||||
breakable_blocks_.pop_back();
|
||||
}
|
||||
@ -428,13 +413,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
if (stmt->init() != nullptr) {
|
||||
RECURSE(Visit(stmt->init()));
|
||||
}
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprLoop, true);
|
||||
if (stmt->cond() != nullptr) {
|
||||
RECURSE(Visit(stmt->cond()));
|
||||
current_function_builder_->Emit(kExprI32Eqz);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
current_function_builder_->EmitWithU8(kExprBr, 2);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
current_function_builder_->Emit(kExprNop);
|
||||
current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 2);
|
||||
current_function_builder_->Emit(kExprEnd);
|
||||
}
|
||||
if (stmt->body() != nullptr) {
|
||||
@ -443,7 +428,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
if (stmt->next() != nullptr) {
|
||||
RECURSE(Visit(stmt->next()));
|
||||
}
|
||||
current_function_builder_->EmitWithU8(kExprBr, 0);
|
||||
current_function_builder_->Emit(kExprNop);
|
||||
current_function_builder_->EmitWithU8U8(kExprBr, ARITY_0, 0);
|
||||
}
|
||||
|
||||
void VisitForInStatement(ForInStatement* stmt) { UNREACHABLE(); }
|
||||
@ -460,13 +446,19 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
DeclarationScope* scope = expr->scope();
|
||||
if (scope_ == kFuncScope) {
|
||||
if (auto* func_type = typer_->TypeOf(expr)->AsFunctionType()) {
|
||||
// Add the parameters for the function.
|
||||
// Build the signature for the function.
|
||||
LocalType return_type = TypeFrom(func_type->ReturnType());
|
||||
const auto& arguments = func_type->Arguments();
|
||||
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();
|
||||
}
|
||||
@ -484,24 +476,7 @@ 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));
|
||||
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);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
RECURSE(Visit(expr->then_expression()));
|
||||
current_function_builder_->Emit(kExprElse);
|
||||
RECURSE(Visit(expr->else_expression()));
|
||||
@ -579,9 +554,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
} else if (scope_ == kExportScope) {
|
||||
Variable* var = expr->var();
|
||||
DCHECK(var->is_function());
|
||||
WasmFunctionBuilder* function = LookupOrInsertFunction(var);
|
||||
function->SetExported();
|
||||
function->SetName(
|
||||
uint32_t index = LookupOrInsertFunction(var);
|
||||
builder_->FunctionAt(index)->SetExported();
|
||||
builder_->FunctionAt(index)->SetName(
|
||||
AsmWasmBuilder::single_function_name,
|
||||
static_cast<int>(strlen(AsmWasmBuilder::single_function_name)));
|
||||
}
|
||||
@ -634,10 +609,11 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
DCHECK(name->IsPropertyName());
|
||||
const AstRawString* raw_name = name->AsRawPropertyName();
|
||||
if (var->is_function()) {
|
||||
WasmFunctionBuilder* function = LookupOrInsertFunction(var);
|
||||
function->SetExported();
|
||||
function->SetName(reinterpret_cast<const char*>(raw_name->raw_data()),
|
||||
raw_name->length());
|
||||
uint32_t index = LookupOrInsertFunction(var);
|
||||
builder_->FunctionAt(index)->SetExported();
|
||||
builder_->FunctionAt(index)->SetName(
|
||||
reinterpret_cast<const char*>(raw_name->raw_data()),
|
||||
raw_name->length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -645,7 +621,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
void VisitArrayLiteral(ArrayLiteral* expr) { UNREACHABLE(); }
|
||||
|
||||
void LoadInitFunction() {
|
||||
current_function_builder_ = init_function_;
|
||||
current_function_builder_ = builder_->FunctionAt(init_function_index_);
|
||||
scope_ = kInitScope;
|
||||
}
|
||||
|
||||
@ -674,8 +650,7 @@ 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())->func_index());
|
||||
builder_->AddIndirectFunction(LookupOrInsertFunction(func->var()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -705,11 +680,12 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
private:
|
||||
class ImportedFunctionIndices : public ZoneObject {
|
||||
public:
|
||||
bool has_name_;
|
||||
const char* name_;
|
||||
int name_length_;
|
||||
WasmModuleBuilder::SignatureMap signature_to_index_;
|
||||
|
||||
explicit ImportedFunctionIndices(Zone* zone)
|
||||
: has_name_(false), signature_to_index_(zone) {}
|
||||
ImportedFunctionIndices(const char* name, int name_length, Zone* zone)
|
||||
: name_(name), name_length_(name_length), signature_to_index_(zone) {}
|
||||
};
|
||||
ZoneHashMap table_;
|
||||
AsmWasmBuilderImpl* builder_;
|
||||
@ -721,50 +697,29 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
ZoneAllocationPolicy(builder->zone())),
|
||||
builder_(builder) {}
|
||||
|
||||
// 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) {
|
||||
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(
|
||||
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()));
|
||||
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;
|
||||
}
|
||||
entry->value = indices;
|
||||
}
|
||||
|
||||
ImportedFunctionIndices* GetEntry(Variable* v) {
|
||||
auto entry = table_.Lookup(v, ComputePointerHash(v));
|
||||
if (entry == nullptr) return nullptr;
|
||||
return reinterpret_cast<ImportedFunctionIndices*>(entry->value);
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -827,7 +782,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
RECURSE(Visit(value));
|
||||
}
|
||||
|
||||
void EmitAssignment(Assignment* expr, MachineType type, ValueFate fate) {
|
||||
void EmitAssignment(Assignment* expr, MachineType type) {
|
||||
// Match the left hand side of the assignment.
|
||||
VariableProxy* target_var = expr->target()->AsVariableProxy();
|
||||
if (target_var != nullptr) {
|
||||
@ -836,19 +791,11 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
LocalType var_type = TypeOf(expr);
|
||||
DCHECK_NE(kAstStmt, var_type);
|
||||
if (var->IsContextSlot()) {
|
||||
uint32_t index = LookupOrInsertGlobal(var, var_type);
|
||||
current_function_builder_->EmitWithVarInt(kExprSetGlobal, index);
|
||||
if (fate == kLeaveOnStack) {
|
||||
current_function_builder_->EmitWithVarInt(kExprGetGlobal, index);
|
||||
}
|
||||
current_function_builder_->EmitWithVarInt(
|
||||
kExprSetGlobal, LookupOrInsertGlobal(var, var_type));
|
||||
} else {
|
||||
if (fate == kDrop) {
|
||||
current_function_builder_->EmitSetLocal(
|
||||
LookupOrInsertLocal(var, var_type));
|
||||
} else {
|
||||
current_function_builder_->EmitTeeLocal(
|
||||
LookupOrInsertLocal(var, var_type));
|
||||
}
|
||||
current_function_builder_->EmitSetLocal(
|
||||
LookupOrInsertLocal(var, var_type));
|
||||
}
|
||||
}
|
||||
|
||||
@ -860,7 +807,6 @@ 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;
|
||||
@ -882,10 +828,6 @@ 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) {
|
||||
@ -894,10 +836,6 @@ 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:
|
||||
@ -915,7 +853,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
if (typer_->TypeOf(target)->AsFFIType() != nullptr) {
|
||||
const AstRawString* name =
|
||||
prop->key()->AsLiteral()->AsRawPropertyName();
|
||||
imported_function_table_.SetImportName(
|
||||
imported_function_table_.AddImport(
|
||||
target->var(), reinterpret_cast<const char*>(name->raw_data()),
|
||||
name->length());
|
||||
}
|
||||
@ -943,12 +881,12 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
}
|
||||
|
||||
if (as_init) LoadInitFunction();
|
||||
MachineType mtype = MachineType::None();
|
||||
MachineType mtype;
|
||||
bool is_nop = false;
|
||||
EmitAssignmentLhs(expr->target(), &mtype);
|
||||
EmitAssignmentRhs(expr->target(), expr->value(), &is_nop);
|
||||
if (!is_nop) {
|
||||
EmitAssignment(expr, mtype, fate);
|
||||
EmitAssignment(expr, mtype);
|
||||
}
|
||||
if (as_init) UnLoadInitFunction();
|
||||
}
|
||||
@ -1173,11 +1111,11 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
|
||||
// if set_local(tmp, x) < 0
|
||||
Visit(call->arguments()->at(0));
|
||||
current_function_builder_->EmitTeeLocal(tmp.index());
|
||||
current_function_builder_->EmitSetLocal(tmp.index());
|
||||
byte code[] = {WASM_I8(0)};
|
||||
current_function_builder_->EmitCode(code, sizeof(code));
|
||||
current_function_builder_->Emit(kExprI32LtS);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
|
||||
// then (0 - tmp)
|
||||
current_function_builder_->EmitCode(code, sizeof(code));
|
||||
@ -1209,13 +1147,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
|
||||
// if set_local(tmp_x, x) < set_local(tmp_y, y)
|
||||
Visit(call->arguments()->at(0));
|
||||
current_function_builder_->EmitTeeLocal(tmp_x.index());
|
||||
current_function_builder_->EmitSetLocal(tmp_x.index());
|
||||
|
||||
Visit(call->arguments()->at(1));
|
||||
current_function_builder_->EmitTeeLocal(tmp_y.index());
|
||||
current_function_builder_->EmitSetLocal(tmp_y.index());
|
||||
|
||||
current_function_builder_->Emit(kExprI32LeS);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
|
||||
// then tmp_x
|
||||
current_function_builder_->EmitGetLocal(tmp_x.index());
|
||||
@ -1245,13 +1183,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
// if set_local(tmp_x, x) < set_local(tmp_y, y)
|
||||
Visit(call->arguments()->at(0));
|
||||
|
||||
current_function_builder_->EmitTeeLocal(tmp_x.index());
|
||||
current_function_builder_->EmitSetLocal(tmp_x.index());
|
||||
|
||||
Visit(call->arguments()->at(1));
|
||||
current_function_builder_->EmitTeeLocal(tmp_y.index());
|
||||
current_function_builder_->EmitSetLocal(tmp_y.index());
|
||||
|
||||
current_function_builder_->Emit(kExprI32LeS);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
|
||||
current_function_builder_->Emit(kExprIf);
|
||||
|
||||
// then tmp_y
|
||||
current_function_builder_->EmitGetLocal(tmp_y.index());
|
||||
@ -1337,20 +1275,18 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
}
|
||||
}
|
||||
|
||||
void VisitCall(Call* expr) { VisitCallExpression(expr); }
|
||||
|
||||
bool VisitCallExpression(Call* expr) {
|
||||
void VisitCall(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 true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
uint32_t index;
|
||||
VariableProxy* vp = expr->expression()->AsVariableProxy();
|
||||
DCHECK_NOT_NULL(vp);
|
||||
if (typer_->TypeOf(vp)->AsFFIType() != nullptr) {
|
||||
@ -1360,23 +1296,22 @@ 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)));
|
||||
}
|
||||
uint32_t index =
|
||||
index =
|
||||
imported_function_table_.GetFunctionIndex(vp->var(), sig.Build());
|
||||
VisitCallArgs(expr);
|
||||
current_function_builder_->Emit(kExprCallFunction);
|
||||
current_function_builder_->Emit(kExprCallImport);
|
||||
current_function_builder_->EmitVarInt(expr->arguments()->length());
|
||||
current_function_builder_->EmitVarInt(index);
|
||||
} else {
|
||||
WasmFunctionBuilder* function = LookupOrInsertFunction(vp->var());
|
||||
index = LookupOrInsertFunction(vp->var());
|
||||
VisitCallArgs(expr);
|
||||
current_function_builder_->Emit(kExprCallFunction);
|
||||
current_function_builder_->EmitVarInt(function->func_index());
|
||||
returns_value = function->signature()->return_count() > 0;
|
||||
current_function_builder_->EmitVarInt(expr->arguments()->length());
|
||||
current_function_builder_->EmitVarInt(index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1387,28 +1322,18 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
VariableProxy* var = p->obj()->AsVariableProxy();
|
||||
DCHECK_NOT_NULL(var);
|
||||
FunctionTableIndices* indices = LookupFunctionTable(var->var());
|
||||
Visit(p->key()); // TODO(titzer): should use RECURSE()
|
||||
|
||||
// We have to use a temporary for the correct order of evaluation.
|
||||
RECURSE(Visit(p->key()));
|
||||
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(); }
|
||||
@ -1594,13 +1519,16 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
RECURSE(Visit(GetLeft(expr)));
|
||||
} else {
|
||||
if (expr->op() == Token::COMMA) {
|
||||
RECURSE(VisitForEffect(expr->left()));
|
||||
RECURSE(Visit(expr->right()));
|
||||
return;
|
||||
current_function_builder_->Emit(kExprBlock);
|
||||
}
|
||||
|
||||
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);
|
||||
@ -1800,33 +1728,18 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
return (reinterpret_cast<IndexContainer*>(entry->value))->index;
|
||||
}
|
||||
|
||||
WasmFunctionBuilder* LookupOrInsertFunction(Variable* v) {
|
||||
uint32_t LookupOrInsertFunction(Variable* v) {
|
||||
DCHECK_NOT_NULL(builder_);
|
||||
ZoneHashMap::Entry* entry = functions_.Lookup(v, ComputePointerHash(v));
|
||||
if (entry == nullptr) {
|
||||
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());
|
||||
uint32_t index = builder_->AddFunction();
|
||||
IndexContainer* container = new (zone()) IndexContainer();
|
||||
container->index = index;
|
||||
entry = functions_.LookupOrInsert(v, ComputePointerHash(v),
|
||||
ZoneAllocationPolicy(zone()));
|
||||
function->SetName(
|
||||
reinterpret_cast<const char*>(v->raw_name()->raw_data()),
|
||||
v->raw_name()->length());
|
||||
entry->value = function;
|
||||
entry->value = container;
|
||||
}
|
||||
return (reinterpret_cast<WasmFunctionBuilder*>(entry->value));
|
||||
return (reinterpret_cast<IndexContainer*>(entry->value))->index;
|
||||
}
|
||||
|
||||
LocalType TypeOf(Expression* expr) { return TypeFrom(typer_->TypeOf(expr)); }
|
||||
@ -1861,8 +1774,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
AsmTyper* typer_;
|
||||
ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
|
||||
ZoneVector<ForeignVariable> foreign_variables_;
|
||||
WasmFunctionBuilder* init_function_;
|
||||
WasmFunctionBuilder* foreign_init_function_;
|
||||
uint32_t init_function_index_;
|
||||
uint32_t foreign_init_function_index_;
|
||||
uint32_t next_table_index_;
|
||||
ZoneHashMap function_tables_;
|
||||
ImportedFunctionTable imported_function_table_;
|
||||
|
@ -778,18 +778,6 @@ 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(),
|
||||
|
@ -189,29 +189,26 @@ class WasmTrapHelper : public ZoneObject {
|
||||
|
||||
Node* GetTrapValue(wasm::FunctionSig* sig) {
|
||||
if (sig->return_count() > 0) {
|
||||
return GetTrapValue(sig->GetReturn());
|
||||
switch (sig->GetReturn()) {
|
||||
case wasm::kAstI32:
|
||||
return jsgraph()->Int32Constant(0xdeadbeef);
|
||||
case wasm::kAstI64:
|
||||
return jsgraph()->Int64Constant(0xdeadbeefdeadbeef);
|
||||
case wasm::kAstF32:
|
||||
return jsgraph()->Float32Constant(bit_cast<float>(0xdeadbeef));
|
||||
case wasm::kAstF64:
|
||||
return jsgraph()->Float64Constant(
|
||||
bit_cast<double>(0xdeadbeefdeadbeef));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
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_;
|
||||
@ -996,11 +993,16 @@ 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), count + 2, vals);
|
||||
Node* ret = graph()->NewNode(jsgraph()->common()->Return(), count + 2, vals);
|
||||
|
||||
MergeControlToEnd(jsgraph(), ret);
|
||||
return ret;
|
||||
@ -1992,8 +1994,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;
|
||||
@ -2012,38 +2014,33 @@ Node** WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
|
||||
SetSourcePosition(call, position);
|
||||
|
||||
*effect_ = 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;
|
||||
return call;
|
||||
}
|
||||
|
||||
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.
|
||||
Handle<Code> code = module_->GetFunctionCode(index);
|
||||
DCHECK(!code.is_null());
|
||||
args[0] = HeapConstant(code);
|
||||
args[0] = HeapConstant(module_->GetCodeOrPlaceholder(index));
|
||||
wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
|
||||
|
||||
return BuildWasmCall(sig, args, position);
|
||||
}
|
||||
|
||||
Node** WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
|
||||
wasm::WasmCodePosition 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) {
|
||||
DCHECK_NOT_NULL(args[0]);
|
||||
DCHECK(module_ && module_->instance);
|
||||
|
||||
@ -2057,7 +2054,6 @@ 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);
|
||||
@ -2066,11 +2062,7 @@ 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);
|
||||
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;
|
||||
return trap_->GetTrapValue(module_->GetSignature(index));
|
||||
}
|
||||
Node* table = FunctionTable(0);
|
||||
|
||||
@ -2104,6 +2096,7 @@ 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);
|
||||
}
|
||||
|
||||
@ -2700,11 +2693,6 @@ Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
|
||||
}
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::CurrentMemoryPages() {
|
||||
return graph()->NewNode(jsgraph()->machine()->Word32Shr(), MemSize(0),
|
||||
jsgraph()->Int32Constant(16));
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::MemSize(uint32_t offset) {
|
||||
DCHECK(module_ && module_->instance);
|
||||
uint32_t size = static_cast<uint32_t>(module_->instance->mem_size);
|
||||
|
@ -153,11 +153,12 @@ class WasmGraphBuilder {
|
||||
Node* ReturnVoid();
|
||||
Node* Unreachable(wasm::WasmCodePosition position);
|
||||
|
||||
Node** CallDirect(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* CallImport(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);
|
||||
|
||||
@ -169,7 +170,7 @@ class WasmGraphBuilder {
|
||||
//-----------------------------------------------------------------------
|
||||
// Operations that concern the linear memory.
|
||||
//-----------------------------------------------------------------------
|
||||
Node* CurrentMemoryPages();
|
||||
Node* MemSize(uint32_t offset);
|
||||
Node* GetGlobal(uint32_t index);
|
||||
Node* SetGlobal(uint32_t index, Node* val);
|
||||
Node* LoadMem(wasm::LocalType type, MachineType memtype, Node* index,
|
||||
@ -228,7 +229,6 @@ 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);
|
||||
|
@ -532,8 +532,6 @@ 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
@ -22,7 +22,6 @@ class WasmGraphBuilder;
|
||||
namespace wasm {
|
||||
|
||||
const uint32_t kMaxNumWasmLocals = 8000000;
|
||||
struct WasmGlobal;
|
||||
|
||||
// Helpers for decoding different kinds of operands which follow bytecodes.
|
||||
struct LocalIndexOperand {
|
||||
@ -82,111 +81,39 @@ 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) {
|
||||
depth = decoder->checked_read_u32v(pc, 1, &length, "break depth");
|
||||
unsigned len1 = 0;
|
||||
unsigned len2 = 0;
|
||||
arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
|
||||
depth = decoder->checked_read_u32v(pc, 1 + len1, &len2, "break depth");
|
||||
length = len1 + len2;
|
||||
target = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
@ -194,32 +121,59 @@ 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 BranchTableOperand {
|
||||
uint32_t table_count;
|
||||
const byte* start;
|
||||
const byte* table;
|
||||
inline BranchTableOperand(Decoder* decoder, const byte* pc) {
|
||||
DCHECK_EQ(kExprBrTable, decoder->checked_read_u8(pc, 0, "opcode"));
|
||||
start = pc + 1;
|
||||
struct CallImportOperand {
|
||||
uint32_t arity;
|
||||
uint32_t index;
|
||||
FunctionSig* sig;
|
||||
unsigned length;
|
||||
inline CallImportOperand(Decoder* decoder, const byte* pc) {
|
||||
unsigned len1 = 0;
|
||||
table_count = decoder->checked_read_u32v(pc, 1, &len1, "table count");
|
||||
unsigned len2 = 0;
|
||||
arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
|
||||
index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "import index");
|
||||
length = len1 + len2;
|
||||
sig = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct BranchTableOperand {
|
||||
uint32_t arity;
|
||||
uint32_t table_count;
|
||||
const byte* table;
|
||||
unsigned length;
|
||||
inline BranchTableOperand(Decoder* decoder, const byte* pc) {
|
||||
unsigned len1 = 0;
|
||||
unsigned len2 = 0;
|
||||
arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count");
|
||||
table_count =
|
||||
decoder->checked_read_u32v(pc, 1 + len1, &len2, "table count");
|
||||
if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 ||
|
||||
len1 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) {
|
||||
len1 + len2 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) {
|
||||
decoder->error(pc, "branch table size overflow");
|
||||
}
|
||||
table = pc + 1 + len1;
|
||||
length = len1 + len2 + (table_count + 1) * sizeof(uint32_t);
|
||||
|
||||
uint32_t table_start = 1 + len1 + len2;
|
||||
if (decoder->check(pc, table_start, (table_count + 1) * sizeof(uint32_t),
|
||||
"expected <table entries>")) {
|
||||
table = pc + table_start;
|
||||
} else {
|
||||
table = nullptr;
|
||||
}
|
||||
}
|
||||
inline uint32_t read_entry(Decoder* decoder, unsigned i) {
|
||||
DCHECK(i <= table_count);
|
||||
@ -227,43 +181,6 @@ struct BranchTableOperand {
|
||||
}
|
||||
};
|
||||
|
||||
// A helper to iterate over a branch table.
|
||||
class BranchTableIterator {
|
||||
public:
|
||||
unsigned cur_index() { return index_; }
|
||||
bool has_next() { return index_ <= table_count_; }
|
||||
uint32_t next() {
|
||||
DCHECK(has_next());
|
||||
index_++;
|
||||
unsigned length = 0;
|
||||
uint32_t result =
|
||||
decoder_->checked_read_u32v(pc_, 0, &length, "branch table entry");
|
||||
pc_ += length;
|
||||
return result;
|
||||
}
|
||||
// length, including the length of the {BranchTableOperand}, but not the
|
||||
// opcode.
|
||||
unsigned length() {
|
||||
while (has_next()) next();
|
||||
return static_cast<unsigned>(pc_ - start_);
|
||||
}
|
||||
const byte* pc() { return pc_; }
|
||||
|
||||
BranchTableIterator(Decoder* decoder, BranchTableOperand& operand)
|
||||
: decoder_(decoder),
|
||||
start_(operand.start),
|
||||
pc_(operand.table),
|
||||
index_(0),
|
||||
table_count_(operand.table_count) {}
|
||||
|
||||
private:
|
||||
Decoder* decoder_;
|
||||
const byte* start_;
|
||||
const byte* pc_;
|
||||
uint32_t index_; // the current index.
|
||||
uint32_t table_count_; // the count of entries, not including default.
|
||||
};
|
||||
|
||||
struct MemoryAccessOperand {
|
||||
uint32_t alignment;
|
||||
uint32_t offset;
|
||||
@ -286,6 +203,15 @@ struct MemoryAccessOperand {
|
||||
}
|
||||
};
|
||||
|
||||
struct ReturnArityOperand {
|
||||
uint32_t arity;
|
||||
unsigned length;
|
||||
|
||||
inline ReturnArityOperand(Decoder* decoder, const byte* pc) {
|
||||
arity = decoder->checked_read_u32v(pc, 1, &length, "return count");
|
||||
}
|
||||
};
|
||||
|
||||
typedef compiler::WasmGraphBuilder TFBuilder;
|
||||
struct ModuleEnv; // forward declaration of module interface.
|
||||
|
||||
@ -358,6 +284,9 @@ BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
|
||||
// Computes the length of the opcode at the given address.
|
||||
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:
|
||||
|
@ -208,19 +208,6 @@ 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 {
|
||||
|
@ -30,11 +30,15 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
// Emit a section code and the size as a padded varint that can be patched
|
||||
// Emit a section name and the size as a padded varint that can be patched
|
||||
// later.
|
||||
size_t EmitSection(WasmSectionCode code, ZoneBuffer& buffer) {
|
||||
// Emit the section code.
|
||||
buffer.write_u8(code);
|
||||
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);
|
||||
|
||||
// Emit a placeholder for the length.
|
||||
return buffer.reserve_u32v();
|
||||
@ -51,8 +55,6 @@ 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()),
|
||||
@ -88,10 +90,6 @@ 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]);
|
||||
@ -145,14 +143,14 @@ void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
|
||||
buffer.write_u32v(signature_index_);
|
||||
}
|
||||
|
||||
void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer) const {
|
||||
void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer,
|
||||
uint32_t func_index) 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_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,10 +175,7 @@ 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());
|
||||
}
|
||||
@ -196,11 +191,17 @@ WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
|
||||
signature_map_(zone),
|
||||
start_function_index_(-1) {}
|
||||
|
||||
WasmFunctionBuilder* WasmModuleBuilder::AddFunction(FunctionSig* sig) {
|
||||
uint32_t WasmModuleBuilder::AddFunction() {
|
||||
functions_.push_back(new (zone_) WasmFunctionBuilder(this));
|
||||
// Add the signature if one was provided here.
|
||||
if (sig) functions_.back()->SetSignature(sig);
|
||||
return functions_.back();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void WasmModuleBuilder::AddDataSegment(WasmDataSegmentEncoder* data) {
|
||||
@ -242,18 +243,16 @@ void WasmModuleBuilder::AddIndirectFunction(uint32_t index) {
|
||||
|
||||
uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length,
|
||||
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(WasmFunctionBuilder* function) {
|
||||
start_function_index_ = function->func_index();
|
||||
void WasmModuleBuilder::MarkStartFunction(uint32_t index) {
|
||||
start_function_index_ = index;
|
||||
}
|
||||
|
||||
uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported,
|
||||
bool mutability) {
|
||||
globals_.push_back(std::make_tuple(type, exported, mutability));
|
||||
uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported) {
|
||||
globals_.push_back(std::make_pair(type, exported));
|
||||
return static_cast<uint32_t>(globals_.size() - 1);
|
||||
}
|
||||
|
||||
@ -267,7 +266,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
|
||||
|
||||
// == Emit signatures ========================================================
|
||||
if (signatures_.size() > 0) {
|
||||
size_t start = EmitSection(kTypeSectionCode, buffer);
|
||||
size_t start = EmitSection(WasmSection::Code::Signatures, buffer);
|
||||
buffer.write_size(signatures_.size());
|
||||
|
||||
for (FunctionSig* sig : signatures_) {
|
||||
@ -284,130 +283,86 @@ 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(kImportSectionCode, buffer);
|
||||
size_t start = EmitSection(WasmSection::Code::ImportTable, buffer);
|
||||
buffer.write_size(imports_.size());
|
||||
for (auto import : imports_) {
|
||||
buffer.write_u32v(import.name_length); // module name length
|
||||
buffer.write(reinterpret_cast<const byte*>(import.name), // module name
|
||||
import.name_length);
|
||||
buffer.write_u32v(0); // field name length
|
||||
buffer.write_u8(kExternalFunction);
|
||||
buffer.write_u32v(import.sig_index);
|
||||
buffer.write_u32v(import.name_length);
|
||||
buffer.write(reinterpret_cast<const byte*>(import.name),
|
||||
import.name_length);
|
||||
buffer.write_u32v(0);
|
||||
}
|
||||
FixupSection(buffer, start);
|
||||
}
|
||||
|
||||
// == Emit function signatures ===============================================
|
||||
bool has_names = false;
|
||||
if (functions_.size() > 0) {
|
||||
size_t start = EmitSection(kFunctionSectionCode, buffer);
|
||||
size_t start = EmitSection(WasmSection::Code::FunctionSignatures, 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(kTableSectionCode, buffer);
|
||||
buffer.write_u8(1); // table count
|
||||
buffer.write_u8(kWasmAnyFunctionTypeForm);
|
||||
buffer.write_u8(kResizableMaximumFlag);
|
||||
buffer.write_size(indirect_functions_.size());
|
||||
size_t start = EmitSection(WasmSection::Code::FunctionTable, buffer);
|
||||
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(kMemorySectionCode, buffer);
|
||||
buffer.write_u8(1); // memory count
|
||||
buffer.write_u32v(kResizableMaximumFlag);
|
||||
size_t start = EmitSection(WasmSection::Code::Memory, buffer);
|
||||
buffer.write_u32v(16); // min memory size
|
||||
buffer.write_u32v(16); // max memory size
|
||||
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);
|
||||
}
|
||||
buffer.write_u8(0); // memory export
|
||||
static_assert(kDeclMemorySize == 3, "memory size must match emit above");
|
||||
FixupSection(buffer, start);
|
||||
}
|
||||
|
||||
// == emit exports ===========================================================
|
||||
if (exports > 0) {
|
||||
size_t start = EmitSection(kExportSectionCode, buffer);
|
||||
size_t start = EmitSection(WasmSection::Code::ExportTable, buffer);
|
||||
buffer.write_u32v(exports);
|
||||
for (auto function : functions_) function->WriteExport(buffer);
|
||||
uint32_t index = 0;
|
||||
for (auto function : functions_) {
|
||||
function->WriteExport(buffer, index++);
|
||||
}
|
||||
FixupSection(buffer, start);
|
||||
}
|
||||
|
||||
// == emit start function index ==============================================
|
||||
if (start_function_index_ >= 0) {
|
||||
size_t start = EmitSection(kStartSectionCode, buffer);
|
||||
size_t start = EmitSection(WasmSection::Code::StartFunction, buffer);
|
||||
buffer.write_u32v(start_function_index_);
|
||||
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(kCodeSectionCode, buffer);
|
||||
size_t start = EmitSection(WasmSection::Code::FunctionBodies, buffer);
|
||||
buffer.write_size(functions_.size());
|
||||
for (auto function : functions_) {
|
||||
function->WriteBody(buffer);
|
||||
@ -417,7 +372,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
|
||||
|
||||
// == emit data segments =====================================================
|
||||
if (data_segments_.size() > 0) {
|
||||
size_t start = EmitSection(kDataSectionCode, buffer);
|
||||
size_t start = EmitSection(WasmSection::Code::DataSegments, buffer);
|
||||
buffer.write_size(data_segments_.size());
|
||||
|
||||
for (auto segment : data_segments_) {
|
||||
@ -425,28 +380,6 @@ 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
|
||||
|
@ -121,21 +121,18 @@ class WasmFunctionBuilder : public ZoneObject {
|
||||
void Emit(WasmOpcode opcode);
|
||||
void EmitGetLocal(uint32_t index);
|
||||
void EmitSetLocal(uint32_t index);
|
||||
void EmitTeeLocal(uint32_t index);
|
||||
void EmitI32Const(int32_t val);
|
||||
void EmitWithU8(WasmOpcode opcode, const byte immediate);
|
||||
void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
|
||||
void EmitWithVarInt(WasmOpcode opcode, uint32_t immediate);
|
||||
void SetExported();
|
||||
void SetName(const char* name, int name_length);
|
||||
|
||||
void WriteSignature(ZoneBuffer& buffer) const;
|
||||
void WriteExport(ZoneBuffer& buffer) const;
|
||||
void WriteBody(ZoneBuffer& buffer) const;
|
||||
|
||||
bool exported() { return exported_; }
|
||||
uint32_t func_index() { return func_index_; }
|
||||
FunctionSig* signature();
|
||||
|
||||
// Writing methods.
|
||||
void WriteSignature(ZoneBuffer& buffer) const;
|
||||
void WriteExport(ZoneBuffer& buffer, uint32_t func_index) const;
|
||||
void WriteBody(ZoneBuffer& buffer) const;
|
||||
|
||||
private:
|
||||
explicit WasmFunctionBuilder(WasmModuleBuilder* builder);
|
||||
@ -145,7 +142,6 @@ class 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_;
|
||||
@ -216,17 +212,14 @@ class WasmModuleBuilder : public ZoneObject {
|
||||
explicit WasmModuleBuilder(Zone* zone);
|
||||
|
||||
// Building methods.
|
||||
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);
|
||||
uint32_t AddFunction();
|
||||
uint32_t AddGlobal(LocalType type, bool exported);
|
||||
WasmFunctionBuilder* FunctionAt(size_t index);
|
||||
void AddDataSegment(WasmDataSegmentEncoder* data);
|
||||
uint32_t AddSignature(FunctionSig* sig);
|
||||
void AddIndirectFunction(uint32_t index);
|
||||
void MarkStartFunction(WasmFunctionBuilder* builder);
|
||||
void MarkStartFunction(uint32_t index);
|
||||
uint32_t AddImport(const char* name, int name_length, FunctionSig* sig);
|
||||
|
||||
// Writing methods.
|
||||
void WriteTo(ZoneBuffer& buffer) const;
|
||||
@ -238,25 +231,18 @@ class 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::tuple<LocalType, bool, bool>> globals_;
|
||||
ZoneVector<std::pair<LocalType, 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
@ -30,11 +30,8 @@ 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,
|
||||
uint32_t num_imported_functions);
|
||||
|
||||
WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end);
|
||||
FunctionOffsetsResult DecodeWasmFunctionOffsets(const byte* module_start,
|
||||
const byte* module_end);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
|
@ -32,15 +32,11 @@ 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,
|
||||
num_imported_functions);
|
||||
function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end);
|
||||
}
|
||||
DCHECK(function_offsets.ok());
|
||||
size_t array_size = 2 * kIntSize * function_offsets.val.size();
|
||||
|
@ -722,38 +722,54 @@ class ControlTransfers : public ZoneObject {
|
||||
public:
|
||||
ControlTransferMap map_;
|
||||
|
||||
ControlTransfers(Zone* zone, ModuleEnv* env, AstLocalDecls* locals,
|
||||
const byte* start, const byte* end)
|
||||
ControlTransfers(Zone* zone, size_t locals_encoded_size, 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;
|
||||
ZoneVector<const byte*> refs;
|
||||
size_t value_depth;
|
||||
ZoneVector<CRef> refs;
|
||||
|
||||
explicit CLabel(Zone* zone) : target(nullptr), refs(zone) {}
|
||||
CLabel(Zone* zone, size_t v)
|
||||
: target(nullptr), value_depth(v), refs(zone) {}
|
||||
|
||||
// Bind this label to the given PC.
|
||||
void Bind(ControlTransferMap* map, const byte* start, const byte* pc) {
|
||||
void Bind(ControlTransferMap* map, const byte* start, const byte* pc,
|
||||
bool expect_value) {
|
||||
DCHECK_NULL(target);
|
||||
target = pc;
|
||||
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;
|
||||
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};
|
||||
}
|
||||
}
|
||||
|
||||
// Reference this label from the given location.
|
||||
void Ref(ControlTransferMap* map, const byte* start,
|
||||
const byte* from_pc) {
|
||||
void Ref(ControlTransferMap* map, const byte* start, CRef from) {
|
||||
DCHECK_GE(from.value_depth, value_depth);
|
||||
if (target) {
|
||||
// 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;
|
||||
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};
|
||||
} else {
|
||||
refs.push_back(from_pc);
|
||||
refs.push_back(from);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -764,104 +780,122 @@ class ControlTransfers : public ZoneObject {
|
||||
CLabel* end_label;
|
||||
CLabel* else_label;
|
||||
|
||||
void Ref(ControlTransferMap* map, const byte* start,
|
||||
const byte* from_pc) {
|
||||
end_label->Ref(map, start, from_pc);
|
||||
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});
|
||||
}
|
||||
};
|
||||
|
||||
// Compute the ControlTransfer map.
|
||||
// This algorithm maintains a stack of control constructs similar to the
|
||||
// This works by maintaining a stack of control constructs similar to the
|
||||
// AST decoder. The {control_stack} allows matching {br,br_if,br_table}
|
||||
// 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;
|
||||
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()) {
|
||||
size_t value_depth = 0;
|
||||
for (BytecodeIterator i(start + locals_encoded_size, end); i.has_next();
|
||||
i.next()) {
|
||||
WasmOpcode opcode = i.current();
|
||||
TRACE("@%u: control %s\n", i.pc_offset(),
|
||||
WasmOpcodes::OpcodeName(opcode));
|
||||
TRACE("@%u: control %s (depth = %zu)\n", i.pc_offset(),
|
||||
WasmOpcodes::OpcodeName(opcode), value_depth);
|
||||
switch (opcode) {
|
||||
case kExprBlock: {
|
||||
TRACE("control @%u: Block\n", i.pc_offset());
|
||||
CLabel* label = new (zone) CLabel(zone);
|
||||
TRACE("control @%u $%zu: Block\n", i.pc_offset(), value_depth);
|
||||
CLabel* label = new (zone) CLabel(zone, value_depth);
|
||||
control_stack.push_back({i.pc(), label, nullptr});
|
||||
break;
|
||||
}
|
||||
case kExprLoop: {
|
||||
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());
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case kExprIf: {
|
||||
TRACE("control @%u: If\n", i.pc_offset());
|
||||
CLabel* end_label = new (zone) CLabel(zone);
|
||||
CLabel* else_label = new (zone) CLabel(zone);
|
||||
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);
|
||||
control_stack.push_back({i.pc(), end_label, else_label});
|
||||
else_label->Ref(&map_, start, i.pc());
|
||||
else_label->Ref(&map_, start, {i.pc(), value_depth, false});
|
||||
break;
|
||||
}
|
||||
case kExprElse: {
|
||||
Control* c = &control_stack.back();
|
||||
TRACE("control @%u: Else\n", i.pc_offset());
|
||||
c->end_label->Ref(&map_, start, i.pc());
|
||||
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;
|
||||
DCHECK_NOT_NULL(c->else_label);
|
||||
c->else_label->Bind(&map_, start, i.pc() + 1);
|
||||
c->else_label->Bind(&map_, start, i.pc() + 1, false);
|
||||
c->else_label = nullptr;
|
||||
break;
|
||||
}
|
||||
case kExprEnd: {
|
||||
Control* c = &control_stack.back();
|
||||
TRACE("control @%u: End\n", i.pc_offset());
|
||||
TRACE("control @%u $%zu: End\n", i.pc_offset(), value_depth);
|
||||
if (c->end_label->target) {
|
||||
// only loops have bound labels.
|
||||
DCHECK_EQ(kExprLoop, *c->pc);
|
||||
} else {
|
||||
if (c->else_label) c->else_label->Bind(&map_, start, i.pc());
|
||||
c->end_label->Bind(&map_, start, i.pc() + 1);
|
||||
control_stack.pop_back();
|
||||
c = &control_stack.back();
|
||||
}
|
||||
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: 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());
|
||||
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++;
|
||||
break;
|
||||
}
|
||||
case kExprBrIf: {
|
||||
BreakDepthOperand operand(&i, i.pc());
|
||||
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());
|
||||
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++;
|
||||
break;
|
||||
}
|
||||
case kExprBrTable: {
|
||||
BranchTableOperand operand(&i, i.pc());
|
||||
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);
|
||||
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);
|
||||
}
|
||||
value_depth++;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
value_depth = value_depth - OpcodeArity(i.pc(), end) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!func_label->target) func_label->Bind(&map_, start, end);
|
||||
}
|
||||
|
||||
pcdiff_t Lookup(pc_t from) {
|
||||
ControlTransfer Lookup(pc_t from) {
|
||||
auto result = map_.find(from);
|
||||
if (result == map_.end()) {
|
||||
V8_Fatal(__FILE__, __LINE__, "no control target for pc %zu", from);
|
||||
@ -931,9 +965,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));
|
||||
ModuleEnv env = {module_, nullptr, kWasmOrigin};
|
||||
code->targets = new (zone_) ControlTransfers(
|
||||
zone_, &env, &code->locals, code->orig_start, code->orig_end);
|
||||
code->targets =
|
||||
new (zone_) ControlTransfers(zone_, code->locals.decls_encoded_size,
|
||||
code->orig_start, code->orig_end);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
@ -972,7 +1006,6 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
instance_(instance),
|
||||
stack_(zone),
|
||||
frames_(zone),
|
||||
blocks_(zone),
|
||||
state_(WasmInterpreter::STOPPED),
|
||||
break_pc_(kInvalidPc),
|
||||
trap_reason_(kTrapCount) {}
|
||||
@ -993,9 +1026,6 @@ 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);
|
||||
}
|
||||
@ -1044,11 +1074,11 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual WasmVal GetReturnValue(int index) {
|
||||
virtual WasmVal GetReturnValue() {
|
||||
if (state_ == WasmInterpreter::TRAPPED) return WasmVal(0xdeadbeef);
|
||||
CHECK_EQ(WasmInterpreter::FINISHED, state_);
|
||||
CHECK_LT(static_cast<size_t>(index), stack_.size());
|
||||
return stack_[index];
|
||||
CHECK_EQ(1, stack_.size());
|
||||
return stack_[0];
|
||||
}
|
||||
|
||||
virtual pc_t GetBreakpointPc() { return break_pc_; }
|
||||
@ -1072,18 +1102,10 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
sp_t llimit() { return plimit() + code->locals.total_local_count; }
|
||||
};
|
||||
|
||||
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_;
|
||||
@ -1108,9 +1130,6 @@ 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);
|
||||
@ -1149,38 +1168,21 @@ 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, WasmVal val) {
|
||||
DCHECK_GT(frames_.size(), 0u);
|
||||
// Pop all blocks for this frame.
|
||||
while (!blocks_.empty() && blocks_.back().fp == frames_.size()) {
|
||||
blocks_.pop_back();
|
||||
}
|
||||
|
||||
sp_t dest = frames_.back().sp;
|
||||
stack_.resize(frames_.back().sp);
|
||||
frames_.pop_back();
|
||||
if (frames_.size() == 0) {
|
||||
// A return from the last frame terminates the execution.
|
||||
// A return from the top frame terminates the execution.
|
||||
state_ = WasmInterpreter::FINISHED;
|
||||
DoStackTransfer(0, arity);
|
||||
stack_.clear();
|
||||
stack_.push_back(val);
|
||||
TRACE(" => finish\n");
|
||||
return false;
|
||||
} else {
|
||||
@ -1189,8 +1191,16 @@ 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);
|
||||
DoStackTransfer(dest, arity);
|
||||
|
||||
stack_.push_back(val);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1201,21 +1211,31 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
*limit = target->end - target->start;
|
||||
}
|
||||
|
||||
// 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];
|
||||
// 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;
|
||||
}
|
||||
stack_.resize(stack_.size() - pop_count);
|
||||
return target.pcdiff;
|
||||
}
|
||||
|
||||
void Execute(InterpreterCode* code, pc_t pc, int max) {
|
||||
@ -1231,8 +1251,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
if (pc >= limit) {
|
||||
// Fell off end of code; do an implicit return.
|
||||
TRACE("@%-3zu: ImplicitReturn\n", pc);
|
||||
if (!DoReturn(&code, &pc, &limit, code->function->sig->return_count()))
|
||||
return;
|
||||
WasmVal val = PopArity(code->function->sig->return_count());
|
||||
if (!DoReturn(&code, &pc, &limit, val)) return;
|
||||
decoder.Reset(code->start, code->end);
|
||||
continue;
|
||||
}
|
||||
@ -1265,37 +1285,27 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
|
||||
switch (orig) {
|
||||
case kExprNop:
|
||||
Push(pc, WasmVal());
|
||||
break;
|
||||
case kExprBlock: {
|
||||
BlockTypeOperand operand(&decoder, code->at(pc));
|
||||
blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity});
|
||||
len = 1 + operand.length;
|
||||
break;
|
||||
}
|
||||
case kExprBlock:
|
||||
case kExprLoop: {
|
||||
BlockTypeOperand operand(&decoder, code->at(pc));
|
||||
blocks_.push_back({pc, stack_.size(), frames_.size(), 0});
|
||||
len = 1 + operand.length;
|
||||
// Do nothing.
|
||||
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 = LookupTarget(code, pc);
|
||||
len = DoControlTransfer(code, pc);
|
||||
TRACE(" false => @%zu\n", pc + len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kExprElse: {
|
||||
blocks_.pop_back();
|
||||
len = LookupTarget(code, pc);
|
||||
len = DoControlTransfer(code, pc);
|
||||
TRACE(" end => @%zu\n", pc + len);
|
||||
break;
|
||||
}
|
||||
@ -1308,34 +1318,42 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
}
|
||||
case kExprBr: {
|
||||
BreakDepthOperand operand(&decoder, code->at(pc));
|
||||
len = DoBreak(code, pc, operand.depth);
|
||||
WasmVal val = PopArity(operand.arity);
|
||||
len = DoControlTransfer(code, pc);
|
||||
TRACE(" br => @%zu\n", pc + len);
|
||||
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 = DoBreak(code, pc, operand.depth);
|
||||
len = DoControlTransfer(code, pc);
|
||||
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 = key + DoBreak(code, pc + key, operand.table[key]);
|
||||
TRACE(" br[%u] => @%zu\n", key, pc + key + len);
|
||||
len = DoControlTransfer(code, pc + key) + key;
|
||||
TRACE(" br[%u] => @%zu\n", key, pc + len);
|
||||
if (operand.arity > 0) Push(pc, val);
|
||||
break;
|
||||
}
|
||||
case kExprReturn: {
|
||||
size_t arity = code->function->sig->return_count();
|
||||
if (!DoReturn(&code, &pc, &limit, arity)) return;
|
||||
ReturnArityOperand operand(&decoder, code->at(pc));
|
||||
WasmVal val = PopArity(operand.arity);
|
||||
if (!DoReturn(&code, &pc, &limit, val)) return;
|
||||
decoder.Reset(code->start, code->end);
|
||||
continue;
|
||||
}
|
||||
@ -1344,7 +1362,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
return CommitPc(pc);
|
||||
}
|
||||
case kExprEnd: {
|
||||
blocks_.pop_back();
|
||||
len = DoControlTransfer(code, pc);
|
||||
DCHECK_EQ(1, len);
|
||||
break;
|
||||
}
|
||||
case kExprI8Const: {
|
||||
@ -1384,13 +1403,6 @@ 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;
|
||||
@ -1398,10 +1410,6 @@ 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);
|
||||
@ -1412,7 +1420,9 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
}
|
||||
case kExprCallIndirect: {
|
||||
CallIndirectOperand operand(&decoder, code->at(pc));
|
||||
uint32_t entry_index = Pop().to<uint32_t>();
|
||||
size_t index = stack_.size() - operand.arity - 1;
|
||||
DCHECK_LT(index, stack_.size());
|
||||
uint32_t entry_index = stack_[index].to<uint32_t>();
|
||||
// Assume only one table for now.
|
||||
DCHECK_LE(module()->function_tables.size(), 1u);
|
||||
InterpreterCode* target = codemap()->GetIndirectCode(0, entry_index);
|
||||
@ -1427,6 +1437,10 @@ 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];
|
||||
@ -1465,6 +1479,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
Push(pc, val);
|
||||
len = 1 + operand.length;
|
||||
break;
|
||||
}
|
||||
@ -1513,6 +1528,7 @@ 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; \
|
||||
}
|
||||
@ -1578,8 +1594,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
break;
|
||||
}
|
||||
case kExprMemorySize: {
|
||||
Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size /
|
||||
WasmModule::kPageSize)));
|
||||
Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size)));
|
||||
break;
|
||||
}
|
||||
#define EXECUTE_SIMPLE_BINOP(name, ctype, op) \
|
||||
@ -1654,7 +1669,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
|
||||
|
||||
void Push(pc_t pc, WasmVal val) {
|
||||
// TODO(titzer): store PC as well?
|
||||
if (val.type != kAstStmt) stack_.push_back(val);
|
||||
stack_.push_back(val);
|
||||
}
|
||||
|
||||
void TraceStack(const char* phase, pc_t pc) {
|
||||
@ -1835,7 +1850,7 @@ bool WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function,
|
||||
|
||||
ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
|
||||
Zone* zone, const byte* start, const byte* end) {
|
||||
ControlTransfers targets(zone, nullptr, nullptr, start, end);
|
||||
ControlTransfers targets(zone, 0, start, end);
|
||||
return targets.map_;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,15 @@ typedef uint32_t spdiff_t;
|
||||
|
||||
const pc_t kInvalidPc = 0x80000000;
|
||||
|
||||
typedef ZoneMap<pc_t, pcdiff_t> ControlTransferMap;
|
||||
// Visible for testing. A {ControlTransfer} helps the interpreter figure out
|
||||
// the target program counter and stack manipulations for a branch.
|
||||
struct ControlTransfer {
|
||||
enum StackAction { kNoAction, kPopAndRepush, kPushVoid };
|
||||
pcdiff_t pcdiff; // adjustment to the program counter (positive or negative).
|
||||
spdiff_t spdiff; // number of elements to pop off the stack.
|
||||
StackAction action; // action to perform on the stack.
|
||||
};
|
||||
typedef ZoneMap<pc_t, ControlTransfer> ControlTransferMap;
|
||||
|
||||
// Macro for defining union members.
|
||||
#define FOREACH_UNION_MEMBER(V) \
|
||||
@ -124,7 +132,7 @@ class WasmInterpreter {
|
||||
virtual int GetFrameCount() = 0;
|
||||
virtual const WasmFrame* GetFrame(int index) = 0;
|
||||
virtual WasmFrame* GetMutableFrame(int index) = 0;
|
||||
virtual WasmVal GetReturnValue(int index = 0) = 0;
|
||||
virtual WasmVal GetReturnValue() = 0;
|
||||
|
||||
// Thread-specific breakpoints.
|
||||
bool SetBreakpoint(const WasmFunction* function, int pc, bool enabled);
|
||||
@ -181,8 +189,9 @@ class WasmInterpreter {
|
||||
bool SetFunctionCodeForTesting(const WasmFunction* function,
|
||||
const byte* start, const byte* end);
|
||||
|
||||
// Computes the control transfers for the given bytecode. Used internally in
|
||||
// the interpreter, but exposed for testing.
|
||||
// Computes the control targets for the given bytecode as {pc offset, sp
|
||||
// offset}
|
||||
// pairs. Used internally in the interpreter, but exposed for testing.
|
||||
static ControlTransferMap ComputeControlTransfersForTesting(Zone* zone,
|
||||
const byte* start,
|
||||
const byte* end);
|
||||
|
@ -160,7 +160,7 @@ i::MaybeHandle<i::JSObject> InstantiateModule(
|
||||
}
|
||||
|
||||
object = i::wasm::WasmModule::Instantiate(
|
||||
isolate, thrower, module_object.ToHandleChecked(), ffi, memory);
|
||||
isolate, 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, &thrower, module_obj, ffi, memory);
|
||||
i::MaybeHandle<i::JSObject> instance =
|
||||
i::wasm::WasmModule::Instantiate(i_isolate, module_obj, ffi, memory);
|
||||
if (instance.is_null()) {
|
||||
if (!thrower.error()) thrower.Error("Could not instantiate module");
|
||||
thrower.Error("Could not instantiate module");
|
||||
return;
|
||||
}
|
||||
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
|
||||
@ -388,10 +388,10 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
}
|
||||
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
i::Handle<i::JSFunction> table_ctor(
|
||||
i::Handle<i::JSFunction> table_cons(
|
||||
i_isolate->native_context()->wasm_table_constructor());
|
||||
i::Handle<i::JSObject> table_obj =
|
||||
i_isolate->factory()->NewJSObject(table_ctor);
|
||||
i_isolate->factory()->NewJSObject(table_cons);
|
||||
i::Handle<i::FixedArray> fixed_array =
|
||||
i_isolate->factory()->NewFixedArray(initial);
|
||||
i::Object* null = i_isolate->heap()->null_value();
|
||||
@ -406,7 +406,6 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
|
||||
return_value.Set(Utils::ToLocal(table_obj));
|
||||
}
|
||||
|
||||
void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
HandleScope scope(isolate);
|
||||
@ -437,14 +436,23 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
}
|
||||
}
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
i::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);
|
||||
|
||||
i::Handle<i::JSObject> memory_obj = i::WasmJs::CreateWasmMemoryObject(
|
||||
i_isolate, buffer, has_maximum, maximum);
|
||||
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();
|
||||
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
|
||||
return_value.Set(Utils::ToLocal(memory_obj));
|
||||
}
|
||||
@ -484,24 +492,6 @@ 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,
|
||||
|
@ -24,10 +24,6 @@ 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
|
||||
|
@ -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) U32V_1(v)
|
||||
#define BR_TARGET(v) U32_LE(v)
|
||||
|
||||
#define MASK_7 ((1 << 7) - 1)
|
||||
#define MASK_14 ((1 << 14) - 1)
|
||||
@ -62,76 +62,36 @@
|
||||
|
||||
#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, 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
|
||||
|
||||
cond, kExprIf, tstmt, kExprElse, fstmt, kExprEnd
|
||||
#define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect
|
||||
|
||||
#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_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_UNREACHABLE kExprUnreachable
|
||||
|
||||
#define WASM_BR_TABLE(key, count, ...) \
|
||||
key, kExprBrTable, U32V_1(count), __VA_ARGS__
|
||||
key, kExprBrTable, ARITY_0, U32V_1(count), __VA_ARGS__
|
||||
|
||||
#define WASM_BR_TABLEV(val, key, count, ...) \
|
||||
val, key, kExprBrTable, ARITY_1, U32V_1(count), __VA_ARGS__
|
||||
|
||||
#define WASM_CASE(x) static_cast<byte>(x), static_cast<byte>(x >> 8)
|
||||
#define WASM_CASE_BR(x) static_cast<byte>(x), static_cast<byte>(0x80 | (x) >> 8)
|
||||
@ -383,8 +343,6 @@ class LocalDeclEncoder {
|
||||
static_cast<byte>(bit_cast<uint64_t>(val) >> 56)
|
||||
#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)
|
||||
@ -416,25 +374,49 @@ class LocalDeclEncoder {
|
||||
v8::internal::wasm::WasmOpcodes::LoadStoreOpcodeOf(type, true)), \
|
||||
alignment, ZERO_OFFSET
|
||||
|
||||
#define WASM_CALL_FUNCTION0(index) kExprCallFunction, static_cast<byte>(index)
|
||||
#define WASM_CALL_FUNCTION(index, ...) \
|
||||
__VA_ARGS__, kExprCallFunction, static_cast<byte>(index)
|
||||
#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),
|
||||
|
||||
// TODO(titzer): change usages of these macros to put func last.
|
||||
#define WASM_CALL_INDIRECT0(index, func) \
|
||||
func, kExprCallIndirect, static_cast<byte>(index)
|
||||
func, kExprCallIndirect, 0, static_cast<byte>(index)
|
||||
#define WASM_CALL_INDIRECT1(index, func, a) \
|
||||
a, func, kExprCallIndirect, static_cast<byte>(index)
|
||||
func, a, kExprCallIndirect, 1, static_cast<byte>(index)
|
||||
#define WASM_CALL_INDIRECT2(index, func, a, b) \
|
||||
a, b, func, kExprCallIndirect, static_cast<byte>(index)
|
||||
func, a, b, kExprCallIndirect, 2, static_cast<byte>(index)
|
||||
#define WASM_CALL_INDIRECT3(index, func, a, b, c) \
|
||||
a, b, c, func, kExprCallIndirect, static_cast<byte>(index)
|
||||
func, a, b, c, kExprCallIndirect, 3, static_cast<byte>(index)
|
||||
#define WASM_CALL_INDIRECT4(index, func, a, b, c, d) \
|
||||
a, b, c, d, func, kExprCallIndirect, static_cast<byte>(index)
|
||||
func, a, b, c, d, kExprCallIndirect, 4, static_cast<byte>(index)
|
||||
#define WASM_CALL_INDIRECT5(index, func, a, b, c, d, e) \
|
||||
a, b, c, d, e, func, kExprCallIndirect, static_cast<byte>(index)
|
||||
func, a, b, c, d, e, kExprCallIndirect, 5, static_cast<byte>(index)
|
||||
#define WASM_CALL_INDIRECTN(arity, index, func, ...) \
|
||||
__VA_ARGS__, func, kExprCallIndirect, static_cast<byte>(index)
|
||||
func, __VA_ARGS__, kExprCallIndirect, U32V_1(arity), static_cast<byte>(index)
|
||||
|
||||
#define WASM_NOT(x) x, kExprI32Eqz
|
||||
#define WASM_SEQ(...) __VA_ARGS__
|
||||
@ -442,16 +424,11 @@ class LocalDeclEncoder {
|
||||
//------------------------------------------------------------------------------
|
||||
// Constructs that are composed of multiple bytecodes.
|
||||
//------------------------------------------------------------------------------
|
||||
#define WASM_WHILE(x, y) \
|
||||
kExprLoop, kLocalVoid, x, kExprIf, kLocalVoid, y, kExprBr, DEPTH_1, \
|
||||
kExprEnd, kExprEnd
|
||||
#define WASM_WHILE(x, y) \
|
||||
kExprLoop, x, kExprIf, y, kExprBr, ARITY_1, DEPTH_1, kExprEnd, kExprEnd
|
||||
#define WASM_INC_LOCAL(index) \
|
||||
kExprGetLocal, static_cast<byte>(index), kExprI8Const, 1, kExprI32Add, \
|
||||
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)
|
||||
kExprSetLocal, static_cast<byte>(index)
|
||||
#define WASM_INC_LOCAL_BY(index, count) \
|
||||
kExprGetLocal, static_cast<byte>(index), kExprI8Const, \
|
||||
static_cast<byte>(count), kExprI32Add, kExprSetLocal, \
|
||||
@ -634,13 +611,4 @@ 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
@ -27,73 +27,84 @@ 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 = 0x0c;
|
||||
const uint32_t kWasmVersion = 0x0b;
|
||||
const uint8_t kWasmFunctionTypeForm = 0x40;
|
||||
const uint8_t kWasmAnyFunctionTypeForm = 0x20;
|
||||
|
||||
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)
|
||||
};
|
||||
// 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")
|
||||
|
||||
inline bool IsValidSectionCode(uint8_t byte) {
|
||||
return kTypeSectionCode <= byte && byte <= kDataSectionCode;
|
||||
}
|
||||
// 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'
|
||||
|
||||
const char* SectionName(WasmSectionCode code);
|
||||
|
||||
static const char* kNameString = "name";
|
||||
static const size_t kNameStringLength = 4;
|
||||
// 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)
|
||||
|
||||
class WasmDebugInfo;
|
||||
|
||||
struct WasmSection {
|
||||
enum class Code : uint32_t {
|
||||
#define F(enumerator, order, string) enumerator,
|
||||
FOR_EACH_WASM_SECTION_TYPE(F)
|
||||
#undef F
|
||||
Max
|
||||
};
|
||||
static WasmSection::Code begin();
|
||||
static WasmSection::Code end();
|
||||
static WasmSection::Code next(WasmSection::Code code);
|
||||
static const char* getName(Code code);
|
||||
static int getOrder(Code code);
|
||||
static size_t getNameLength(Code code);
|
||||
static WasmSection::Code lookup(const byte* string, uint32_t length);
|
||||
};
|
||||
|
||||
enum WasmFunctionDeclBit {
|
||||
kDeclFunctionName = 0x01,
|
||||
kDeclFunctionExport = 0x08
|
||||
};
|
||||
|
||||
// Constants for fixed-size elements within a module.
|
||||
static const size_t kDeclMemorySize = 3;
|
||||
static const size_t kDeclDataSegmentSize = 13;
|
||||
|
||||
static const uint32_t kMaxReturnCount = 1;
|
||||
static const uint8_t kResizableMaximumFlag = 1;
|
||||
static const int32_t kInvalidFunctionIndex = -1;
|
||||
|
||||
enum WasmExternalKind {
|
||||
kExternalFunction = 0,
|
||||
kExternalTable = 1,
|
||||
kExternalMemory = 2,
|
||||
kExternalGlobal = 3
|
||||
};
|
||||
|
||||
// Representation of an initializer expression.
|
||||
struct WasmInitExpr {
|
||||
enum WasmInitKind {
|
||||
kNone,
|
||||
kGlobalIndex,
|
||||
kI32Const,
|
||||
kI64Const,
|
||||
kF32Const,
|
||||
kF64Const
|
||||
} kind;
|
||||
|
||||
union {
|
||||
int32_t i32_const;
|
||||
int64_t i64_const;
|
||||
float f32_const;
|
||||
double f64_const;
|
||||
uint32_t global_index;
|
||||
} val;
|
||||
};
|
||||
|
||||
#define NO_INIT \
|
||||
{ \
|
||||
WasmInitExpr::kNone, { 0u } \
|
||||
}
|
||||
|
||||
// Static representation of a WASM function.
|
||||
struct WasmFunction {
|
||||
@ -104,59 +115,47 @@ 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.
|
||||
bool imported;
|
||||
bool exported;
|
||||
};
|
||||
|
||||
// Static representation of an imported WASM function.
|
||||
struct WasmImport {
|
||||
FunctionSig* sig; // signature of the function.
|
||||
uint32_t sig_index; // index into the signature table.
|
||||
uint32_t module_name_offset; // offset in module bytes of the module name.
|
||||
uint32_t module_name_length; // length in bytes of the module name.
|
||||
uint32_t function_name_offset; // offset in module bytes of the import name.
|
||||
uint32_t function_name_length; // length in bytes of the import name.
|
||||
};
|
||||
|
||||
// Static representation of an exported WASM function.
|
||||
struct WasmExport {
|
||||
uint32_t func_index; // index into the function table.
|
||||
uint32_t name_offset; // offset in module bytes of the name to export.
|
||||
uint32_t name_length; // length in bytes of the exported name.
|
||||
};
|
||||
|
||||
// Static representation of a wasm global variable.
|
||||
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.
|
||||
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.
|
||||
uint32_t offset; // offset from beginning of globals area.
|
||||
bool exported; // true if this global is exported.
|
||||
};
|
||||
|
||||
// Static representation of a wasm data segment.
|
||||
struct WasmDataSegment {
|
||||
WasmInitExpr dest_addr; // destination memory address of the data.
|
||||
uint32_t dest_addr; // destination memory address of the data.
|
||||
uint32_t source_offset; // start offset in the module bytes.
|
||||
uint32_t source_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<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.
|
||||
uint32_t size; // initial table size.
|
||||
uint32_t max_size; // maximum table size.
|
||||
std::vector<uint16_t> values; // function table.
|
||||
};
|
||||
|
||||
enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
|
||||
@ -164,7 +163,6 @@ 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
|
||||
|
||||
@ -173,6 +171,7 @@ 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.
|
||||
@ -181,16 +180,12 @@ 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
|
||||
@ -239,7 +234,6 @@ struct WasmModule {
|
||||
|
||||
// Creates a new instantiation of the module in the given isolate.
|
||||
static MaybeHandle<JSObject> Instantiate(Isolate* isolate,
|
||||
ErrorThrower* thrower,
|
||||
Handle<JSObject> module_object,
|
||||
Handle<JSReceiver> ffi,
|
||||
Handle<JSArrayBuffer> memory);
|
||||
@ -261,6 +255,7 @@ 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.
|
||||
@ -271,6 +266,7 @@ 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) {}
|
||||
@ -282,6 +278,9 @@ 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();
|
||||
@ -292,6 +291,9 @@ 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();
|
||||
}
|
||||
@ -303,6 +305,10 @@ 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];
|
||||
@ -314,10 +320,8 @@ struct ModuleEnv {
|
||||
|
||||
bool asm_js() { return origin == kAsmJsOrigin; }
|
||||
|
||||
Handle<Code> GetFunctionCode(uint32_t index) {
|
||||
DCHECK_NOT_NULL(instance);
|
||||
return instance->function_code[index];
|
||||
}
|
||||
Handle<Code> GetCodeOrPlaceholder(uint32_t index) const;
|
||||
Handle<Code> GetImportCode(uint32_t index);
|
||||
|
||||
static compiler::CallDescriptor* GetWasmCallDescriptor(Zone* zone,
|
||||
FunctionSig* sig);
|
||||
@ -404,9 +408,6 @@ MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
|
||||
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
|
||||
|
@ -22,9 +22,6 @@ 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;
|
||||
@ -47,7 +44,7 @@ const WasmCodePosition kNoCodePosition = -1;
|
||||
|
||||
// Control expressions and blocks.
|
||||
#define FOREACH_CONTROL_OPCODE(V) \
|
||||
V(Unreachable, 0x00, _) \
|
||||
V(Nop, 0x00, _) \
|
||||
V(Block, 0x01, _) \
|
||||
V(Loop, 0x02, _) \
|
||||
V(If, 0x03, _) \
|
||||
@ -57,7 +54,7 @@ const WasmCodePosition kNoCodePosition = -1;
|
||||
V(BrIf, 0x07, _) \
|
||||
V(BrTable, 0x08, _) \
|
||||
V(Return, 0x09, _) \
|
||||
V(Nop, 0x0a, _) \
|
||||
V(Unreachable, 0x0a, _) \
|
||||
V(Throw, 0xfa, _) \
|
||||
V(Try, 0xfb, _) \
|
||||
V(Catch, 0xfe, _) \
|
||||
@ -71,10 +68,9 @@ 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, _)
|
||||
@ -501,8 +497,6 @@ 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:
|
||||
|
@ -22,7 +22,19 @@ namespace wasm {
|
||||
// Error codes for programmatic checking of the decoder's verification.
|
||||
enum ErrorCode {
|
||||
kSuccess,
|
||||
kError, // TODO(titzer): introduce real error codes
|
||||
kError, // TODO(titzer): remove me
|
||||
kOutOfMemory, // decoder ran out of memory
|
||||
kEndOfCode, // end of code reached prematurely
|
||||
kInvalidOpcode, // found invalid opcode
|
||||
kUnreachableCode, // found unreachable code
|
||||
kImproperContinue, // improperly nested continue
|
||||
kImproperBreak, // improperly nested break
|
||||
kReturnCount, // return count mismatch
|
||||
kTypeError, // type mismatch
|
||||
kInvalidLocalIndex, // invalid local
|
||||
kInvalidGlobalIndex, // invalid global
|
||||
kInvalidFunctionIndex, // invalid function
|
||||
kInvalidMemType // invalid memory type
|
||||
};
|
||||
|
||||
// The overall result of decoding a function or a module.
|
||||
|
@ -32,6 +32,7 @@
|
||||
#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
|
||||
@ -834,8 +835,8 @@ WASM_EXEC_TEST(CallI64Parameter) {
|
||||
WasmRunner<int32_t> r(&module);
|
||||
BUILD(
|
||||
r,
|
||||
WASM_I32_CONVERT_I64(WASM_CALL_FUNCTION(
|
||||
index, WASM_I64V_9(0xbcd12340000000b),
|
||||
WASM_I32_CONVERT_I64(WASM_CALL_FUNCTIONN(
|
||||
19, 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),
|
||||
@ -1118,7 +1119,7 @@ WASM_EXEC_TEST(Call_Int64Sub) {
|
||||
|
||||
// Build the caller function.
|
||||
WasmRunner<int64_t> r(&module, MachineType::Int64(), MachineType::Int64());
|
||||
BUILD(r, WASM_CALL_FUNCTION(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
||||
BUILD(r, WASM_CALL_FUNCTION2(index, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
||||
|
||||
FOR_INT32_INPUTS(i) {
|
||||
FOR_INT32_INPUTS(j) {
|
||||
@ -1153,11 +1154,7 @@ WASM_EXEC_TEST(LoadStoreI64_sx) {
|
||||
ZERO_OFFSET, // --
|
||||
kExprI64StoreMem, // --
|
||||
ZERO_ALIGNMENT, // --
|
||||
ZERO_OFFSET, // --
|
||||
kExprI8Const, 0, // --
|
||||
loads[m], // --
|
||||
ZERO_ALIGNMENT, // --
|
||||
ZERO_OFFSET, // --
|
||||
ZERO_OFFSET // --
|
||||
};
|
||||
|
||||
r.Build(code, code + arraysize(code));
|
||||
@ -1259,9 +1256,10 @@ WASM_EXEC_TEST(F64ReinterpretI64) {
|
||||
int64_t* memory = module.AddMemoryElems<int64_t>(8);
|
||||
WasmRunner<int64_t> r(&module, MachineType::Int64());
|
||||
|
||||
BUILD(r, WASM_STORE_MEM(MachineType::Float64(), WASM_ZERO,
|
||||
WASM_F64_REINTERPRET_I64(WASM_GET_LOCAL(0))),
|
||||
WASM_GET_LOCAL(0));
|
||||
BUILD(r,
|
||||
WASM_BLOCK(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;
|
||||
@ -1322,17 +1320,18 @@ WASM_EXEC_TEST(MemI64_Sum) {
|
||||
WasmRunner<uint64_t> r(&module, MachineType::Int32());
|
||||
const byte kSum = r.AllocateLocal(kAstI64);
|
||||
|
||||
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));
|
||||
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)));
|
||||
|
||||
// Run 4 trials.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
@ -1354,8 +1353,7 @@ 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);
|
||||
|
||||
@ -1373,10 +1371,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, WASM_SET_GLOBAL(
|
||||
0, WASM_I64_AND(WASM_GET_GLOBAL(0),
|
||||
WASM_I64_SCONVERT_I32(WASM_GET_LOCAL(0)))),
|
||||
WASM_ZERO);
|
||||
BUILD(r, B2(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) {
|
||||
@ -1479,11 +1477,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, kExprI8Const, 0);
|
||||
ADD_CODE(code, kExprCallIndirect, 1);
|
||||
ADD_CODE(code, kExprCallIndirect, static_cast<byte>(num_params), 1);
|
||||
|
||||
t.Build(&code[0], &code[0] + code.size());
|
||||
t.Compile();
|
||||
@ -1542,7 +1540,8 @@ static void Run_WasmMixedCall_N(WasmExecutionMode execution_mode, int start) {
|
||||
}
|
||||
|
||||
// Call the selector function.
|
||||
ADD_CODE(code, kExprCallFunction, static_cast<byte>(index));
|
||||
ADD_CODE(code, kExprCallFunction, static_cast<byte>(num_params),
|
||||
static_cast<byte>(index));
|
||||
|
||||
// Store the result in memory.
|
||||
ADD_CODE(code,
|
||||
|
@ -36,7 +36,7 @@ TEST(Run_WasmInt8Const_i) {
|
||||
|
||||
TEST(Run_WasmIfElse) {
|
||||
WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Int32());
|
||||
BUILD(r, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_I8(9), WASM_I8(10)));
|
||||
BUILD(r, WASM_IF_ELSE(WASM_GET_LOCAL(0), WASM_I8(9), WASM_I8(10)));
|
||||
CHECK_EQ(10, r.Call(0));
|
||||
CHECK_EQ(9, r.Call(1));
|
||||
}
|
||||
@ -65,39 +65,31 @@ TEST(Run_WasmNopsN) {
|
||||
|
||||
TEST(Run_WasmConstsN) {
|
||||
const int kMaxConsts = 10;
|
||||
byte code[kMaxConsts * 3];
|
||||
int32_t expected = 0;
|
||||
byte code[kMaxConsts * 2];
|
||||
for (int count = 1; count < kMaxConsts; count++) {
|
||||
for (int i = 0; i < count; 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;
|
||||
}
|
||||
code[i * 2] = kExprI8Const;
|
||||
code[i * 2 + 1] = static_cast<byte>(count * 10 + i);
|
||||
}
|
||||
byte expected = static_cast<byte>(count * 11 - 1);
|
||||
|
||||
WasmRunner<int32_t> r(kExecuteInterpreted);
|
||||
r.Build(code, code + (count * 3));
|
||||
r.Build(code, code + (count * 2));
|
||||
CHECK_EQ(expected, r.Call());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Run_WasmBlocksN) {
|
||||
const int kMaxNops = 10;
|
||||
const int kExtra = 5;
|
||||
const int kExtra = 4;
|
||||
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] = kLocalI32;
|
||||
code[2 + nops] = kExprI8Const;
|
||||
code[2 + nops + 1] = expected;
|
||||
code[2 + nops + 2] = kExprEnd;
|
||||
code[1 + nops] = kExprI8Const;
|
||||
code[1 + nops + 1] = expected;
|
||||
code[1 + nops + 2] = kExprEnd;
|
||||
|
||||
WasmRunner<int32_t> r(kExecuteInterpreted);
|
||||
r.Build(code, code + nops + kExtra);
|
||||
@ -114,14 +106,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[2 + index + 0] = kExprI8Const;
|
||||
code[2 + index + 1] = static_cast<byte>(expected);
|
||||
code[2 + index + 2] = kExprBr;
|
||||
code[2 + index + 3] = 0;
|
||||
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;
|
||||
|
||||
WasmRunner<int32_t> r(kExecuteInterpreted);
|
||||
r.Build(code, code + kMaxNops + kExtra);
|
||||
@ -134,10 +126,10 @@ TEST(Run_Wasm_nested_ifs_i) {
|
||||
WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Int32(),
|
||||
MachineType::Int32());
|
||||
|
||||
BUILD(r, WASM_IF_ELSE_I(
|
||||
BUILD(r, WASM_IF_ELSE(
|
||||
WASM_GET_LOCAL(0),
|
||||
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))));
|
||||
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))));
|
||||
|
||||
CHECK_EQ(11, r.Call(1, 1));
|
||||
CHECK_EQ(12, r.Call(1, 0));
|
||||
@ -308,10 +300,10 @@ TEST(GrowMemoryPreservesData) {
|
||||
TestingModule module(kExecuteInterpreted);
|
||||
WasmRunner<int32_t> r(&module, MachineType::Uint32());
|
||||
module.AddMemory(WasmModule::kPageSize);
|
||||
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)));
|
||||
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))));
|
||||
CHECK_EQ(value, r.Call(1));
|
||||
}
|
||||
|
||||
@ -320,7 +312,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_GROW_MEMORY(WASM_GET_LOCAL(0)));
|
||||
BUILD(r, WASM_BLOCK(WASM_GROW_MEMORY(WASM_GET_LOCAL(0))));
|
||||
CHECK_EQ(-1, r.Call(1048575));
|
||||
}
|
||||
{
|
||||
@ -328,7 +320,7 @@ TEST(GrowMemoryInvalidSize) {
|
||||
TestingModule module(kExecuteInterpreted);
|
||||
WasmRunner<int32_t> r(&module, MachineType::Uint32());
|
||||
module.AddMemory(WasmModule::kPageSize);
|
||||
BUILD(r, WASM_GROW_MEMORY(WASM_GET_LOCAL(0)));
|
||||
BUILD(r, WASM_BLOCK(WASM_GROW_MEMORY(WASM_GET_LOCAL(0))));
|
||||
CHECK_EQ(-1, r.Call(1048575));
|
||||
}
|
||||
}
|
||||
|
@ -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_FUNCTION(js_index, WASM_GET_LOCAL(0)));
|
||||
BUILD(t, WASM_CALL_FUNCTION1(js_index, WASM_GET_LOCAL(0)));
|
||||
|
||||
Handle<JSFunction> jsfunc = module.WrapCode(t.CompileAndAdd());
|
||||
|
||||
@ -182,7 +182,8 @@ void RunJSSelectTest(int which) {
|
||||
ADD_CODE(code, WASM_F64(inputs.arg_d(i)));
|
||||
}
|
||||
|
||||
ADD_CODE(code, kExprCallFunction, static_cast<byte>(js_index));
|
||||
ADD_CODE(code, kExprCallFunction, static_cast<byte>(num_params),
|
||||
static_cast<byte>(js_index));
|
||||
|
||||
size_t end = code.size();
|
||||
code.push_back(0);
|
||||
@ -419,7 +420,7 @@ void RunJSSelectAlignTest(int num_args, int num_params) {
|
||||
ADD_CODE(code, WASM_GET_LOCAL(i));
|
||||
}
|
||||
|
||||
ADD_CODE(code, kExprCallFunction, 0);
|
||||
ADD_CODE(code, kExprCallFunction, static_cast<byte>(num_params), 0);
|
||||
|
||||
size_t end = code.size();
|
||||
code.push_back(0);
|
||||
|
@ -53,7 +53,9 @@ TEST(Run_WasmModule_Return114) {
|
||||
Zone zone(&allocator);
|
||||
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
|
||||
uint16_t f_index = builder->AddFunction();
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
ExportAsMain(f);
|
||||
byte code[] = {WASM_I8(kReturnValue)};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
@ -67,18 +69,21 @@ TEST(Run_WasmModule_CallAdd) {
|
||||
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
|
||||
WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_ii());
|
||||
uint16_t f1_index = builder->AddFunction();
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f1_index);
|
||||
f->SetSignature(sigs.i_ii());
|
||||
uint16_t param1 = 0;
|
||||
uint16_t param2 = 1;
|
||||
byte code1[] = {WASM_I32_ADD(WASM_GET_LOCAL(param1), WASM_GET_LOCAL(param2))};
|
||||
f1->EmitCode(code1, sizeof(code1));
|
||||
f->EmitCode(code1, sizeof(code1));
|
||||
|
||||
WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v());
|
||||
uint16_t f2_index = builder->AddFunction();
|
||||
f = builder->FunctionAt(f2_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
|
||||
ExportAsMain(f2);
|
||||
byte code2[] = {
|
||||
WASM_CALL_FUNCTION(f1->func_index(), WASM_I8(77), WASM_I8(22))};
|
||||
f2->EmitCode(code2, sizeof(code2));
|
||||
ExportAsMain(f);
|
||||
byte code2[] = {WASM_CALL_FUNCTION2(f1_index, WASM_I8(77), WASM_I8(22))};
|
||||
f->EmitCode(code2, sizeof(code2));
|
||||
TestModule(&zone, builder, 99);
|
||||
}
|
||||
|
||||
@ -89,7 +94,9 @@ TEST(Run_WasmModule_ReadLoadedDataSegment) {
|
||||
TestSignatures sigs;
|
||||
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
|
||||
uint16_t f_index = builder->AddFunction();
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
|
||||
ExportAsMain(f);
|
||||
byte code[] = {
|
||||
@ -108,16 +115,18 @@ TEST(Run_WasmModule_CheckMemoryIsZero) {
|
||||
TestSignatures sigs;
|
||||
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
|
||||
uint16_t f_index = builder->AddFunction();
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
|
||||
uint16_t localIndex = f->AddLocal(kAstI32);
|
||||
ExportAsMain(f);
|
||||
byte code[] = {WASM_BLOCK_I(
|
||||
byte code[] = {WASM_BLOCK(
|
||||
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(3, WASM_I8(-1)), WASM_INC_LOCAL_BY(localIndex, 4))),
|
||||
WASM_BRV(2, WASM_I8(-1)), WASM_INC_LOCAL_BY(localIndex, 4))),
|
||||
WASM_I8(11))};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
TestModule(&zone, builder, 11);
|
||||
@ -129,18 +138,20 @@ TEST(Run_WasmModule_CallMain_recursive) {
|
||||
TestSignatures sigs;
|
||||
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
|
||||
uint16_t f_index = builder->AddFunction();
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
|
||||
uint16_t localIndex = f->AddLocal(kAstI32);
|
||||
ExportAsMain(f);
|
||||
byte code[] = {
|
||||
byte code[] = {WASM_BLOCK(
|
||||
WASM_SET_LOCAL(localIndex,
|
||||
WASM_LOAD_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_IF_ELSE(WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I8(5)),
|
||||
WASM_BLOCK(WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO,
|
||||
WASM_INC_LOCAL(localIndex)),
|
||||
WASM_CALL_FUNCTION0(0)),
|
||||
WASM_I8(55))};
|
||||
WASM_BRV(1, WASM_CALL_FUNCTION0(0))),
|
||||
WASM_BRV(0, WASM_I8(55))))};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
TestModule(&zone, builder, 55);
|
||||
}
|
||||
@ -153,16 +164,20 @@ TEST(Run_WasmModule_Global) {
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
uint32_t global1 = builder->AddGlobal(kAstI32, 0);
|
||||
uint32_t global2 = builder->AddGlobal(kAstI32, 0);
|
||||
WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v());
|
||||
uint16_t f1_index = builder->AddFunction();
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f1_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
byte code1[] = {
|
||||
WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))};
|
||||
f1->EmitCode(code1, sizeof(code1));
|
||||
WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v());
|
||||
ExportAsMain(f2);
|
||||
f->EmitCode(code1, sizeof(code1));
|
||||
uint16_t f2_index = builder->AddFunction();
|
||||
f = builder->FunctionAt(f2_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
ExportAsMain(f);
|
||||
byte code2[] = {WASM_SET_GLOBAL(global1, WASM_I32V_1(56)),
|
||||
WASM_SET_GLOBAL(global2, WASM_I32V_1(41)),
|
||||
WASM_RETURN1(WASM_CALL_FUNCTION0(f1->func_index()))};
|
||||
f2->EmitCode(code2, sizeof(code2));
|
||||
WASM_RETURN1(WASM_CALL_FUNCTION0(f1_index))};
|
||||
f->EmitCode(code2, sizeof(code2));
|
||||
TestModule(&zone, builder, 97);
|
||||
}
|
||||
|
||||
@ -172,9 +187,11 @@ TEST(Run_WasmModule_Serialization) {
|
||||
Zone zone(&allocator);
|
||||
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
uint16_t f_index = builder->AddFunction();
|
||||
TestSignatures sigs;
|
||||
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
|
||||
f->SetSignature(sigs.i_i());
|
||||
byte code[] = {WASM_GET_LOCAL(0), kExprI32Const, 1, kExprI32Add};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
ExportAs(f, kFunctionName);
|
||||
@ -227,7 +244,7 @@ TEST(Run_WasmModule_Serialization) {
|
||||
Handle<JSObject> module_object =
|
||||
Handle<JSObject>::cast(v8::Utils::OpenHandle(*compiled_module));
|
||||
Handle<JSObject> instance =
|
||||
WasmModule::Instantiate(isolate, &thrower, module_object,
|
||||
WasmModule::Instantiate(isolate, module_object,
|
||||
Handle<JSReceiver>::null(),
|
||||
Handle<JSArrayBuffer>::null())
|
||||
.ToHandleChecked();
|
||||
@ -241,16 +258,19 @@ TEST(Run_WasmModule_Serialization) {
|
||||
}
|
||||
|
||||
TEST(Run_WasmModule_MemSize_GrowMem) {
|
||||
static const int kPageSize = 0x10000;
|
||||
// Initial memory size = 16 + GrowMemory(10)
|
||||
static const int kExpectedValue = 26;
|
||||
static const int kExpectedValue = kPageSize * 26;
|
||||
TestSignatures sigs;
|
||||
v8::internal::AccountingAllocator allocator;
|
||||
Zone zone(&allocator);
|
||||
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
|
||||
uint16_t f_index = builder->AddFunction();
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
ExportAsMain(f);
|
||||
byte code[] = {WASM_GROW_MEMORY(WASM_I8(10)), WASM_DROP, WASM_MEMORY_SIZE};
|
||||
byte code[] = {WASM_GROW_MEMORY(WASM_I8(10)), WASM_MEMORY_SIZE};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
TestModule(&zone, builder, kExpectedValue);
|
||||
}
|
||||
@ -260,10 +280,12 @@ TEST(Run_WasmModule_GrowMemoryInIf) {
|
||||
v8::internal::AccountingAllocator allocator;
|
||||
Zone zone(&allocator);
|
||||
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
|
||||
uint16_t f_index = builder->AddFunction();
|
||||
WasmFunctionBuilder* f = builder->FunctionAt(f_index);
|
||||
f->SetSignature(sigs.i_v());
|
||||
ExportAsMain(f);
|
||||
byte code[] = {WASM_IF_ELSE_I(WASM_I32V(0), WASM_GROW_MEMORY(WASM_I32V(1)),
|
||||
WASM_I32V(12))};
|
||||
byte code[] = {WASM_IF_ELSE(WASM_I32V(0), WASM_GROW_MEMORY(WASM_I32V(1)),
|
||||
WASM_I32V(12))};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
TestModule(&zone, builder, 12);
|
||||
}
|
||||
|
@ -19,43 +19,42 @@ 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))), \
|
||||
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); \
|
||||
#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); \
|
||||
}
|
||||
|
||||
FOREACH_TYPE(LOAD_SET_GLOBAL_TEST_BODY)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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, false, false});
|
||||
module.functions.push_back({nullptr, 0, 0,
|
||||
static_cast<uint32_t>(name_offset),
|
||||
static_cast<uint32_t>(name.length()), 0, 0});
|
||||
++func_index;
|
||||
}
|
||||
|
||||
|
@ -104,9 +104,8 @@ 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_SEQ(WASM_LOAD_MEM(MachineType::Int32(),
|
||||
WASM_I32V_1(-3)),
|
||||
WASM_DROP)));
|
||||
BUILD(comp1, WASM_IF(WASM_ONE,
|
||||
WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(-3))));
|
||||
uint32_t wasm_index = comp1.CompileAndAdd();
|
||||
|
||||
WasmFunctionCompiler comp2(sigs.v_v(), &module, ArrayVector("call_mem_oob"));
|
||||
@ -132,7 +131,7 @@ TEST(IllegalLoad) {
|
||||
|
||||
// The column is 1-based, so add 1 to the actual byte offset.
|
||||
ExceptionInfo expected_exceptions[] = {
|
||||
{"<WASM UNNAMED>", static_cast<int>(wasm_index), 8}, // --
|
||||
{"<WASM UNNAMED>", static_cast<int>(wasm_index), 7}, // --
|
||||
{"<WASM UNNAMED>", static_cast<int>(wasm_index_2), 3}, // --
|
||||
{"callFn", 1, 24} // --
|
||||
};
|
||||
|
@ -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, false, false});
|
||||
module_.functions.push_back({sig, index, 0, 0, 0, 0, 0});
|
||||
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<int32_t>(), false, false});
|
||||
{table_size, table_size, std::vector<uint16_t>()});
|
||||
for (uint32_t i = 0; i < table_size; ++i) {
|
||||
module_.function_tables.back().values.push_back(functions[i]);
|
||||
}
|
||||
@ -267,8 +267,7 @@ 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(
|
||||
{type, true, NO_INIT, global_offset, false, false});
|
||||
module_.globals.push_back({0, 0, type, global_offset, false});
|
||||
global_offset += size;
|
||||
// limit number of globals.
|
||||
CHECK_LT(global_offset, kMaxGlobalsSize);
|
||||
@ -284,13 +283,6 @@ 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;
|
||||
|
@ -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, thrower, module_object.ToHandleChecked(),
|
||||
Handle<JSReceiver>::null(), Handle<JSArrayBuffer>::null());
|
||||
isolate, module_object.ToHandleChecked(), Handle<JSReceiver>::null(),
|
||||
Handle<JSArrayBuffer>::null());
|
||||
Handle<JSObject> instance;
|
||||
if (!maybe_instance.ToHandle(&instance)) {
|
||||
return Handle<JSObject>::null();
|
||||
|
@ -42,8 +42,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
|
||||
WasmModuleBuilder builder(&zone);
|
||||
|
||||
v8::internal::wasm::WasmFunctionBuilder* f =
|
||||
builder.AddFunction(sigs.i_iii());
|
||||
uint16_t f1_index = builder.AddFunction();
|
||||
WasmFunctionBuilder* f = builder.FunctionAt(f1_index);
|
||||
f->SetSignature(sigs.i_iii());
|
||||
f->EmitCode(data, static_cast<uint32_t>(size));
|
||||
f->SetExported();
|
||||
f->SetName("main", 4);
|
||||
|
@ -5,5 +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::kDataSectionCode, data, size);
|
||||
return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::DataSegments,
|
||||
data, size);
|
||||
}
|
||||
|
@ -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::kFunctionSectionCode, data,
|
||||
size);
|
||||
return fuzz_wasm_section(
|
||||
v8::internal::wasm::WasmSection::Code::FunctionSignatures, data, size);
|
||||
}
|
||||
|
@ -5,5 +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::kGlobalSectionCode, data, size);
|
||||
return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Globals, data,
|
||||
size);
|
||||
}
|
||||
|
@ -5,5 +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::kImportSectionCode, data, size);
|
||||
return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::ImportTable,
|
||||
data, size);
|
||||
}
|
||||
|
@ -5,5 +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::kMemorySectionCode, data, size);
|
||||
return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Memory, data,
|
||||
size);
|
||||
}
|
||||
|
@ -5,6 +5,6 @@
|
||||
#include "test/fuzzer/wasm-section-fuzzers.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
// TODO(titzer): Names section requires a preceding function section.
|
||||
return fuzz_wasm_section(v8::internal::wasm::kNameSectionCode, data, size);
|
||||
return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Names, data,
|
||||
size);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
using namespace v8::internal::wasm;
|
||||
|
||||
int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data,
|
||||
int fuzz_wasm_section(WasmSection::Code section, const uint8_t* data,
|
||||
size_t size) {
|
||||
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
|
||||
v8::Isolate* isolate = support->GetIsolate();
|
||||
@ -38,18 +38,12 @@ int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data,
|
||||
ZoneBuffer buffer(&zone);
|
||||
buffer.write_u32(kWasmMagic);
|
||||
buffer.write_u32(kWasmVersion);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
ErrorThrower thrower(i_isolate, "decoder");
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
int fuzz_wasm_section(v8::internal::wasm::WasmSectionCode section,
|
||||
int fuzz_wasm_section(v8::internal::wasm::WasmSection::Code section,
|
||||
const uint8_t* data, size_t size);
|
||||
|
||||
#endif // WASM_SECTION_FUZZERS_H_
|
||||
|
@ -5,5 +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::kTypeSectionCode, data, size);
|
||||
return fuzz_wasm_section(v8::internal::wasm::WasmSection::Code::Signatures,
|
||||
data, size);
|
||||
}
|
||||
|
@ -1,151 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --validate-asm --allow-natives-syntax
|
||||
|
||||
var selectedTest = undefined;
|
||||
//selectedTest = 16;
|
||||
|
||||
function skip(a) {
|
||||
return selectedTest != undefined ? a != selectedTest : false;
|
||||
}
|
||||
|
||||
const assign_in_stmt = [
|
||||
"if (E) =",
|
||||
"if (=) E",
|
||||
"if (E) E; else =",
|
||||
"for (=; E; S) S",
|
||||
"for (E; =; S) S",
|
||||
"for (E; E; =) E",
|
||||
"for (E; E; E) =",
|
||||
"do { = } while(E)",
|
||||
"do { S } while (=)",
|
||||
];
|
||||
const assign_in_expr = [
|
||||
"i32_func(=)",
|
||||
"(=) ? E : E",
|
||||
"E ? (=) : E",
|
||||
"E ? E : (=)",
|
||||
"(=) + E",
|
||||
"E + (=)",
|
||||
"imul(=, E)",
|
||||
"imul(E, =)",
|
||||
"~(=)",
|
||||
"(=) | 0",
|
||||
"(=), E",
|
||||
"E, (=)",
|
||||
"E, E, (=)",
|
||||
"E, (=), E",
|
||||
"(=), E, E",
|
||||
];
|
||||
|
||||
const stdlib = {
|
||||
Math: Math,
|
||||
Int8Array: Int8Array,
|
||||
Int16Array: Int16Array,
|
||||
Int32Array: Int32Array,
|
||||
Uint8Array: Uint8Array,
|
||||
Uint16Array: Uint16Array,
|
||||
Uint32Array: Uint32Array,
|
||||
Float32Array: Float32Array,
|
||||
Float64Array: Float64Array,
|
||||
};
|
||||
|
||||
const buffer = new ArrayBuffer(65536);
|
||||
|
||||
// Template for a module.
|
||||
function MODULE_TEMPLATE(stdlib, foreign, buffer) {
|
||||
"use asm";
|
||||
var imul = stdlib.Math.imul;
|
||||
var fround = stdlib.Math.fround;
|
||||
var M = new stdlib.Int32Array(buffer);
|
||||
var G = 0;
|
||||
|
||||
function void_func() {}
|
||||
function i32_func(a) {
|
||||
a = a | 0;
|
||||
return a | 0;
|
||||
}
|
||||
|
||||
FUNC_DECL
|
||||
return {main: main};
|
||||
}
|
||||
|
||||
// Template for main function.
|
||||
{
|
||||
function main(i32, f32, f64) {
|
||||
i32 = i32 | 0;
|
||||
f32 = fround(f32);
|
||||
f64 = +f64;
|
||||
FUNC_BODY
|
||||
}
|
||||
}
|
||||
|
||||
function RunAsmJsTest(asmfunc, expect) {
|
||||
var asm_source = asmfunc.toString();
|
||||
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
|
||||
|
||||
print("Testing " + asmfunc.name + " (js)...");
|
||||
var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer);
|
||||
expect(js_module);
|
||||
|
||||
print("Testing " + asmfunc.name + " (asm.js)...");
|
||||
var asm_module = asmfunc(stdlib, {}, buffer);
|
||||
assertTrue(%IsAsmWasmCode(asmfunc));
|
||||
expect(asm_module);
|
||||
}
|
||||
|
||||
var test = 0;
|
||||
|
||||
function DoTheTests(expr, assign, stmt) {
|
||||
// ==== Expression assignment tests ========================================
|
||||
for (let e of assign_in_expr) {
|
||||
if (skip(++test)) continue;
|
||||
var orig = e;
|
||||
e = e.replace(/=/g, assign);
|
||||
e = e.replace(/E/g, expr);
|
||||
e = e.replace(/S/g, stmt);
|
||||
var str = main.toString().replace("FUNC_BODY", "return (" + e + ") | 0;");
|
||||
var asm_source = MODULE_TEMPLATE.toString().replace("FUNC_DECL", str);
|
||||
// TODO(titzer): a verbosity API for these kinds of tests?
|
||||
// print(asm_source);
|
||||
|
||||
doTest(asm_source, "(" + test + ") " + e);
|
||||
}
|
||||
|
||||
// ==== Statement assignment tests =========================================
|
||||
for (let e of assign_in_stmt) {
|
||||
if (skip(++test)) continue;
|
||||
var orig = e;
|
||||
e = e.replace(/=/g, assign);
|
||||
e = e.replace(/E/g, expr);
|
||||
e = e.replace(/S/g, stmt);
|
||||
var str = main.toString().replace("FUNC_BODY", e + "; return 0;");
|
||||
var asm_source = MODULE_TEMPLATE.toString().replace("FUNC_DECL", str);
|
||||
// print(asm_source);
|
||||
|
||||
doTest(asm_source, "(" + test + ") " + e);
|
||||
}
|
||||
|
||||
function doTest(asm_source, orig) {
|
||||
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
|
||||
print("Testing JS: " + orig);
|
||||
var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer);
|
||||
expect(js_module);
|
||||
|
||||
var asmfunc = eval("(" + asm_source + ")");
|
||||
|
||||
print("Testing ASMJS: " + orig);
|
||||
var asm_module = asmfunc(stdlib, {}, buffer);
|
||||
assertTrue(%IsAsmWasmCode(asmfunc));
|
||||
expect(asm_module);
|
||||
}
|
||||
|
||||
function expect(module) { module.main(0, 0, 0); print(" ok"); return true; }
|
||||
}
|
||||
|
||||
DoTheTests("(i32 | 0)", "i32 = 0", "void_func()");
|
||||
DoTheTests("G", "G = 0", "void_func()");
|
||||
DoTheTests("G", "G = 0", "G");
|
||||
DoTheTests("(M[0] | 0)", "M[0] = 0", "void_func()");
|
@ -988,7 +988,6 @@ 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));
|
||||
@ -1041,7 +1040,6 @@ function TestForeignFunctions() {
|
||||
assertEquals(103, module.caller(23, 103));
|
||||
}
|
||||
|
||||
print("TestForeignFunctions...");
|
||||
TestForeignFunctions();
|
||||
|
||||
|
||||
|
@ -20,16 +20,13 @@ function assertModule(module, memsize) {
|
||||
assertFalse(mem === null);
|
||||
assertFalse(mem === 0);
|
||||
assertEquals("object", typeof mem);
|
||||
assertTrue(mem instanceof WebAssembly.Memory);
|
||||
var buf = mem.buffer;
|
||||
assertTrue(buf instanceof ArrayBuffer);
|
||||
assertEquals(memsize, buf.byteLength);
|
||||
assertTrue(mem instanceof ArrayBuffer);
|
||||
for (var i = 0; i < 4; i++) {
|
||||
module.exports.memory = 0; // should be ignored
|
||||
mem.buffer = 0; // should be ignored
|
||||
assertSame(mem, module.exports.memory);
|
||||
assertSame(buf, mem.buffer);
|
||||
assertEquals(mem, module.exports.memory);
|
||||
}
|
||||
|
||||
assertEquals(memsize, module.exports.memory.byteLength);
|
||||
}
|
||||
|
||||
function assertFunction(module, func) {
|
||||
|
@ -17,7 +17,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
builder.addImport("getValue", kSig_i);
|
||||
builder.addFunction("f", kSig_i)
|
||||
.addBody([
|
||||
kExprCallFunction, 0
|
||||
kExprCallImport, kArity0, 0
|
||||
]).exportFunc();
|
||||
|
||||
var module = new WebAssembly.Module(builder.toBuffer());
|
||||
|
@ -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,
|
||||
kExprI32Const, 1,
|
||||
kExprCallIndirect, signature,
|
||||
kExprCallIndirect, kArity1, signature,
|
||||
kExprGetLocal,0,
|
||||
kExprI32LoadMem,0, 0,
|
||||
kExprCallFunction, 0,
|
||||
kExprCallImport, kArity0, 0,
|
||||
kExprI32Add
|
||||
]).exportFunc();
|
||||
|
||||
@ -32,8 +32,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
builder.addFunction("_wrap_writer", signature)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprCallFunction, 1]);
|
||||
builder.appendToTable([2, 3]);
|
||||
kExprCallImport, kArity1, 1]);
|
||||
builder.appendToTable([0, 1]);
|
||||
|
||||
|
||||
var module = new WebAssembly.Module(builder.toBuffer());
|
||||
|
@ -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("/1")) {
|
||||
if (name.endsWith("/0")) {
|
||||
// Function 0 calls the imported function.
|
||||
expected_string = "kExprCallFunction,";
|
||||
} else if (name.endsWith("/2")) {
|
||||
expected_string = "kExprCallImport,";
|
||||
} else if (name.endsWith("/1")) {
|
||||
// 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([kExprCallFunction, 0])
|
||||
.addBody([kExprCallImport, kArity0, 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, kAstStmt,
|
||||
kExprCallFunction, 1,
|
||||
kExprI64Ne, kExprIf,
|
||||
kExprCallFunction, kArity0, 0,
|
||||
kExprEnd
|
||||
])
|
||||
.exportFunc();
|
||||
|
@ -7,27 +7,6 @@
|
||||
// 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);
|
||||
|
@ -15,9 +15,9 @@ var module = (function () {
|
||||
kExprGetLocal, 0,
|
||||
kExprI32Const, 0,
|
||||
kExprI32Ne,
|
||||
kExprIf, kAstStmt,
|
||||
kExprIf,
|
||||
kExprGetLocal, 0,
|
||||
kExprThrow, kAstStmt,
|
||||
kExprThrow,
|
||||
kExprEnd,
|
||||
kExprI32Const, 1
|
||||
])
|
||||
@ -26,7 +26,7 @@ var module = (function () {
|
||||
builder.addFunction("throw_20", kSig_v_v)
|
||||
.addBody([
|
||||
kExprI32Const, 20,
|
||||
kExprThrow, kAstStmt
|
||||
kExprThrow
|
||||
])
|
||||
.exportFunc()
|
||||
|
||||
@ -43,7 +43,7 @@ var module = (function () {
|
||||
kExprI32Mul,
|
||||
kExprI32Const, 20,
|
||||
kExprI32Sub,
|
||||
kExprThrow, kAstStmt
|
||||
kExprThrow
|
||||
])
|
||||
.exportFunc()
|
||||
|
||||
|
@ -15,7 +15,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
.addBody([
|
||||
kExprI8Const,
|
||||
kReturnValue,
|
||||
kExprReturn
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
@ -36,7 +36,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
.addBody([
|
||||
kExprI8Const,
|
||||
kReturnValue,
|
||||
kExprReturn
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportAs("blah")
|
||||
.exportAs("foo");
|
||||
@ -61,7 +61,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
.addBody([
|
||||
kExprI8Const,
|
||||
kReturnValue,
|
||||
kExprReturn
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportAs("0");
|
||||
|
||||
|
@ -16,7 +16,7 @@ function testCallFFI(ffi) {
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, 0, // --
|
||||
kExprCallFunction, kArity2, 0, // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
@ -106,7 +106,7 @@ assertThrows(function() {
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprI64SConvertI32,
|
||||
kExprCallFunction, index // --
|
||||
kExprCallImport, kArity1, index // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
var func = function() {return {};};
|
||||
|
@ -16,7 +16,7 @@ function testCallFFI(func, check) {
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, 0 // --
|
||||
kExprCallImport, kArity2, 0 // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
@ -80,7 +80,7 @@ print("Constructor");
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, 0 // --
|
||||
kExprCallImport, kArity2, 0 // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
@ -98,7 +98,7 @@ print("Native function");
|
||||
builder.addImport("func", sig_index);
|
||||
builder.addFunction("main", sig_index)
|
||||
.addBody([
|
||||
kExprCallFunction, 0 // --
|
||||
kExprCallImport, kArity0, 0 // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
@ -247,7 +247,7 @@ function testCallBinopVoid(type, func, check) {
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, 0, // --
|
||||
kExprCallImport, kArity2, 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, // --
|
||||
kExprCallFunction, 0, // --
|
||||
kExprGetLocal, 0, // --
|
||||
kExprCallFunction, 1 // --
|
||||
]) // --
|
||||
kExprI8Const, 97, // --
|
||||
kExprCallImport, kArity1, 0, // --
|
||||
kExprGetLocal, 0, // --
|
||||
kExprCallImport, kArity1, 1 // --
|
||||
]) // --
|
||||
.exportFunc()
|
||||
|
||||
var main = builder.instantiate({print: print}).exports.main;
|
||||
|
@ -52,11 +52,11 @@ var builder = new WasmModuleBuilder();
|
||||
builder.addImport("func", kSig_v_v);
|
||||
|
||||
builder.addFunction("wasm_1", kSig_v_v)
|
||||
.addBody([kExprNop, kExprCallFunction, 2])
|
||||
.addBody([kExprNop, kExprCallFunction, kArity0, 1])
|
||||
.exportAs("main");
|
||||
|
||||
builder.addFunction("wasm_2", kSig_v_v)
|
||||
.addBody([kExprCallFunction, 0]);
|
||||
.addBody([kExprCallImport, kArity0, 0]);
|
||||
|
||||
function call_debugger() {
|
||||
debugger;
|
||||
|
@ -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, last_func_index]).index;
|
||||
.addBody([kExprCallFunction, kArity0, last_func_index]).index;
|
||||
}
|
||||
|
||||
builder.addFunction("main", kSig_v_v)
|
||||
.addBody([kExprCallFunction, last_func_index])
|
||||
.addBody([kExprCallFunction, kArity0, last_func_index])
|
||||
.exportFunc();
|
||||
|
||||
var module = builder.instantiate();
|
||||
|
@ -27,8 +27,7 @@ function makeFFI(func, t) {
|
||||
kExprGetLocal, 7, // --
|
||||
kExprGetLocal, 8, // --
|
||||
kExprGetLocal, 9, // --
|
||||
kExprCallFunction, 0, // --
|
||||
kExprDrop, // --
|
||||
kExprCallImport, 10, 0, // --
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprGetLocal, 2, // --
|
||||
@ -39,7 +38,7 @@ function makeFFI(func, t) {
|
||||
kExprGetLocal, 7, // --
|
||||
kExprGetLocal, 8, // --
|
||||
kExprGetLocal, 9, // --
|
||||
kExprCallFunction, 0, // --
|
||||
kExprCallImport, 10, 0 // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
||||
|
@ -18,22 +18,19 @@ function genGrowMemoryBuilder() {
|
||||
.addBody([kExprGetLocal, 0, kExprI32LoadMem, 0, 0])
|
||||
.exportFunc();
|
||||
builder.addFunction("store", kSig_i_ii)
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem, 0, 0,
|
||||
kExprGetLocal, 1])
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem, 0, 0])
|
||||
.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,
|
||||
kExprGetLocal, 1])
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem16, 0, 0])
|
||||
.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,
|
||||
kExprGetLocal, 1])
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem8, 0, 0])
|
||||
.exportFunc();
|
||||
return builder;
|
||||
}
|
||||
@ -247,9 +244,9 @@ function testGrowMemoryCurrentMemory() {
|
||||
var module = builder.instantiate();
|
||||
function growMem(pages) { return module.exports.grow_memory(pages); }
|
||||
function MemSize() { return module.exports.memory_size(); }
|
||||
assertEquals(1, MemSize());
|
||||
assertEquals(65536, MemSize());
|
||||
assertEquals(1, growMem(1));
|
||||
assertEquals(2, MemSize());
|
||||
assertEquals(131072, MemSize());
|
||||
}
|
||||
|
||||
testGrowMemoryCurrentMemory();
|
||||
|
@ -16,7 +16,7 @@ function testCallImport(func, check) {
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, 0]) // --
|
||||
kExprCallImport, 2, 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, // --
|
||||
kExprCallFunction, 0, // --
|
||||
kExprCallImport, 2, 0, // --
|
||||
kExprI8Const, 99, // --
|
||||
])
|
||||
.exportFunc("main");
|
||||
@ -246,9 +246,9 @@ function testCallPrint() {
|
||||
builder.addFunction("main", makeSig_r_x(kAstF64, kAstF64))
|
||||
.addBody([
|
||||
kExprI8Const, 97, // --
|
||||
kExprCallFunction, 0, // --
|
||||
kExprCallImport, kArity1, 0, // --
|
||||
kExprGetLocal, 0, // --
|
||||
kExprCallFunction, 1 // --
|
||||
kExprCallImport, kArity1, 1 // --
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
@ -270,8 +270,8 @@ function testCallImport2(foo, bar, expected) {
|
||||
builder.addImport("bar", kSig_i);
|
||||
builder.addFunction("main", kSig_i)
|
||||
.addBody([
|
||||
kExprCallFunction, 0, // --
|
||||
kExprCallFunction, 1, // --
|
||||
kExprCallImport, kArity0, 0, // --
|
||||
kExprCallImport, kArity0, 1, // --
|
||||
kExprI32Add, // --
|
||||
]) // --
|
||||
.exportFunc();
|
||||
|
Binary file not shown.
@ -14,7 +14,7 @@ var module = (function () {
|
||||
builder.addImport("add", sig_index);
|
||||
builder.addFunction("add", sig_index)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0
|
||||
kExprGetLocal, 0, kExprGetLocal, 1, kExprCallImport, kArity2, 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,
|
||||
kExprGetLocal, 0,
|
||||
kExprCallIndirect, sig_index
|
||||
kExprCallIndirect, kArity2, sig_index
|
||||
])
|
||||
.exportFunc()
|
||||
builder.appendToTable([1, 2, 3]);
|
||||
builder.appendToTable([0, 1, 2]);
|
||||
|
||||
return builder.instantiate({add: function(a, b) { return a + b | 0; }});
|
||||
})();
|
||||
@ -47,40 +47,3 @@ assertEquals(19, module.exports.main(0, 12, 7));
|
||||
|
||||
assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)");
|
||||
assertTraps(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)");
|
||||
|
@ -1,122 +0,0 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --expose-wasm --expose-gc
|
||||
|
||||
load("test/mjsunit/wasm/wasm-constants.js");
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
let nogc = () => {};
|
||||
|
||||
function newModule() {
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addMemory(1, 1, true);
|
||||
builder.addFunction("main", kSig_i)
|
||||
.addBody([kExprI32Const, 0, kExprI32LoadMem, 0, 0])
|
||||
.exportFunc();
|
||||
|
||||
return new WebAssembly.Module(builder.toBuffer());
|
||||
}
|
||||
|
||||
function newInstance(module, val) {
|
||||
var instance = new WebAssembly.Instance(module);
|
||||
var view = new Int32Array(instance.exports.memory.buffer);
|
||||
view[0] = val;
|
||||
return instance;
|
||||
}
|
||||
|
||||
function TestSingleLiveInstance(gc) {
|
||||
let module = newModule();
|
||||
|
||||
print("TestSingleLiveInstance...");
|
||||
for (var i = 0; i < 5; i++) {
|
||||
(() => { // don't leak references between iterations.
|
||||
print(" [" + i + "]...");
|
||||
gc();
|
||||
var instance = newInstance(module, i + 99);
|
||||
assertEquals(i + 99, instance.exports.main());
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
TestSingleLiveInstance(nogc);
|
||||
TestSingleLiveInstance(gc);
|
||||
|
||||
function TestMultiInstance(gc) {
|
||||
let module = newModule();
|
||||
|
||||
print("TestMultiInstance...");
|
||||
// Note: compute the root instances in another function to be
|
||||
// sure that {roots} really is the only set of roots to the instances.
|
||||
let roots = (() => { return [
|
||||
newInstance(module, 33),
|
||||
newInstance(module, 4444),
|
||||
newInstance(module, 555555)
|
||||
];})();
|
||||
|
||||
(() => { // don't leak references!
|
||||
print(" [0]...");
|
||||
gc();
|
||||
assertEquals(33, roots[0].exports.main());
|
||||
roots[0] = null;
|
||||
})();
|
||||
|
||||
(() => { // don't leak references!
|
||||
print(" [1]...");
|
||||
gc();
|
||||
assertEquals(4444, roots[1].exports.main());
|
||||
roots[1] = null;
|
||||
})();
|
||||
|
||||
(() => { // don't leak references!
|
||||
print(" [2]...");
|
||||
gc();
|
||||
assertEquals(555555, roots[2].exports.main());
|
||||
roots[2] = null;
|
||||
})();
|
||||
}
|
||||
|
||||
TestMultiInstance(nogc);
|
||||
TestMultiInstance(gc);
|
||||
|
||||
function TestReclaimingCompiledModule() {
|
||||
let module = newModule();
|
||||
|
||||
print("TestReclaimingCompiledModule...");
|
||||
let roots = (() => { return [
|
||||
newInstance(module, 7777),
|
||||
newInstance(module, 8888),
|
||||
];})();
|
||||
|
||||
(() => { // don't leak references!
|
||||
print(" [0]...");
|
||||
assertEquals(7777, roots[0].exports.main());
|
||||
assertEquals(8888, roots[1].exports.main());
|
||||
roots[1] = null;
|
||||
})();
|
||||
|
||||
(() => { // don't leak references!
|
||||
print(" [1]...");
|
||||
gc();
|
||||
roots[1] = newInstance(module, 9999);
|
||||
assertEquals(7777, roots[0].exports.main());
|
||||
assertEquals(9999, roots[1].exports.main());
|
||||
roots[0] = null;
|
||||
roots[1] = null;
|
||||
})();
|
||||
|
||||
(() => { // don't leak references!
|
||||
print(" [2]...");
|
||||
gc();
|
||||
roots[0] = newInstance(module, 11111);
|
||||
roots[1] = newInstance(module, 22222);
|
||||
assertEquals(11111, roots[0].exports.main());
|
||||
assertEquals(22222, roots[1].exports.main());
|
||||
roots[0] = null;
|
||||
roots[1] = null;
|
||||
})();
|
||||
}
|
||||
|
||||
TestReclaimingCompiledModule(nogc);
|
||||
TestReclaimingCompiledModule(gc);
|
@ -31,17 +31,14 @@ function CheckInstance(instance) {
|
||||
assertFalse(mem === null);
|
||||
assertFalse(mem === 0);
|
||||
assertEquals("object", typeof mem);
|
||||
assertTrue(mem instanceof WebAssembly.Memory);
|
||||
var buf = mem.buffer;
|
||||
assertTrue(buf instanceof ArrayBuffer);
|
||||
assertEquals(65536, buf.byteLength);
|
||||
for (var i = 0; i < 4; i++) {
|
||||
assertTrue(mem instanceof ArrayBuffer);
|
||||
for (let 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);
|
||||
@ -64,7 +61,6 @@ 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) {
|
||||
@ -79,10 +75,9 @@ 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([kExprCallFunction, 0]);
|
||||
.addBody([kExprCallImport, kArity0, 0]);
|
||||
let promise = WebAssembly.compile(builder.toBuffer());
|
||||
promise
|
||||
.then(compiled =>
|
||||
@ -92,7 +87,6 @@ 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);
|
||||
@ -100,7 +94,6 @@ 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);
|
||||
@ -110,7 +103,6 @@ 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], []);
|
||||
@ -120,13 +112,13 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
|
||||
|
||||
builder.addFunction("main", kSig_i_i)
|
||||
.addBody([
|
||||
kExprI32Const, 1,
|
||||
kExprGetLocal, 0,
|
||||
kExprI32LoadMem, 0, 0,
|
||||
kExprI32Const, 1,
|
||||
kExprCallIndirect, signature,
|
||||
kExprCallIndirect, kArity1, signature,
|
||||
kExprGetLocal,0,
|
||||
kExprI32LoadMem,0, 0,
|
||||
kExprCallFunction, 0,
|
||||
kExprCallImport, kArity0, 0,
|
||||
kExprI32Add
|
||||
]).exportFunc();
|
||||
|
||||
@ -135,8 +127,8 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
|
||||
builder.addFunction("_wrap_writer", signature)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprCallFunction, 1]);
|
||||
builder.appendToTable([2, 3]);
|
||||
kExprCallImport, kArity1, 1]);
|
||||
builder.appendToTable([0, 1]);
|
||||
|
||||
|
||||
var module = new WebAssembly.Module(builder.toBuffer());
|
||||
@ -163,7 +155,6 @@ 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)
|
||||
@ -188,7 +179,6 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
|
||||
|
||||
|
||||
(function InstanceMemoryIsIsolated() {
|
||||
print("InstanceMemoryIsIsolated...");
|
||||
var builder = new WasmModuleBuilder();
|
||||
builder.addMemory(1,1, true);
|
||||
|
||||
|
@ -15,36 +15,33 @@ 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;
|
||||
// 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 // --
|
||||
// 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
|
||||
])
|
||||
.exportFunc();
|
||||
var module = builder.instantiate(null, memory);
|
||||
assertTrue(module.exports.memory instanceof WebAssembly.Memory);
|
||||
if (memory != null) assertEquals(memory, module.exports.memory.buffer);
|
||||
return module;
|
||||
|
||||
return builder.instantiate(null, memory);
|
||||
}
|
||||
|
||||
function testPokeMemory() {
|
||||
var module = genModule(null);
|
||||
var buffer = module.exports.memory.buffer;
|
||||
var buffer = module.exports.memory;
|
||||
var main = module.exports.main;
|
||||
assertEquals(kMemSize, buffer.byteLength);
|
||||
|
||||
@ -69,13 +66,9 @@ function testPokeMemory() {
|
||||
|
||||
testPokeMemory();
|
||||
|
||||
function genAndGetMain(buffer) {
|
||||
return genModule(buffer).exports.main; // to prevent intermediates living
|
||||
}
|
||||
|
||||
function testSurvivalAcrossGc() {
|
||||
var checker = genAndGetMain(null);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var checker = genModule(null).exports.main;
|
||||
for (var i = 0; i < 5; i++) {
|
||||
print("gc run ", i);
|
||||
assertEquals(0, checker(kMemSize - 4));
|
||||
gc();
|
||||
@ -117,8 +110,8 @@ testPokeOuterMemory();
|
||||
|
||||
function testOuterMemorySurvivalAcrossGc() {
|
||||
var buffer = new ArrayBuffer(kMemSize);
|
||||
var checker = genAndGetMain(buffer);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var checker = genModule(buffer).exports.main;
|
||||
for (var i = 0; i < 5; i++) {
|
||||
print("gc run ", i);
|
||||
assertEquals(0, checker(kMemSize - 4));
|
||||
gc();
|
||||
@ -140,9 +133,7 @@ function testOOBThrows() {
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprI32LoadMem, 0, 0,
|
||||
kExprI32StoreMem, 0, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprI32LoadMem, 0, 0,
|
||||
kExprI32StoreMem, 0, 0
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
|
@ -20,16 +20,13 @@ function assertModule(module, memsize) {
|
||||
assertFalse(mem === null);
|
||||
assertFalse(mem === 0);
|
||||
assertEquals("object", typeof mem);
|
||||
assertTrue(mem instanceof WebAssembly.Memory);
|
||||
var buf = mem.buffer;
|
||||
assertTrue(buf instanceof ArrayBuffer);
|
||||
assertEquals(memsize, buf.byteLength);
|
||||
assertTrue(mem instanceof ArrayBuffer);
|
||||
for (var i = 0; i < 4; i++) {
|
||||
module.exports.memory = 0; // should be ignored
|
||||
mem.buffer = 0; // should be ignored
|
||||
assertSame(mem, module.exports.memory);
|
||||
assertSame(buf, mem.buffer);
|
||||
assertEquals(mem, module.exports.memory);
|
||||
}
|
||||
|
||||
assertEquals(memsize, module.exports.memory.byteLength);
|
||||
}
|
||||
|
||||
function assertFunction(module, func) {
|
||||
@ -87,7 +84,7 @@ function assertFunction(module, func) {
|
||||
.addBody([ // --
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, f[i >>> 1].index]) // --
|
||||
kExprCallFunction, kArity2, f[i >>> 1].index]) // --
|
||||
.exportFunc()
|
||||
}
|
||||
var module = builder.instantiate();
|
||||
|
@ -16,7 +16,7 @@ function testCallImport(func, expected, a, b) {
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, 0]) // --
|
||||
kExprCallImport, 2, 0]) // --
|
||||
.exportAs("main");
|
||||
|
||||
var main = builder.instantiate({func: func}).exports.main;
|
||||
|
@ -45,7 +45,7 @@ var builder = new WasmModuleBuilder();
|
||||
builder.addImport("func", kSig_v_v);
|
||||
|
||||
builder.addFunction("main", kSig_v_v)
|
||||
.addBody([kExprCallFunction, 0])
|
||||
.addBody([kExprCallImport, kArity0, 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_i_v)
|
||||
var mem_oob_func = builder.addFunction(undefined, kSig_v_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_i_v)
|
||||
.addBody([kExprCallFunction, mem_oob_func.index])
|
||||
builder.addFunction("call_mem_out_of_bounds", kSig_v_v)
|
||||
.addBody([kExprCallFunction, kArity0, 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>[1]+1)\n" + // --
|
||||
" at main (<WASM>[0]+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", 1, 1, null],
|
||||
[ true, "main", 0, 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", 2, 1, null],
|
||||
[ true, "exec_unreachable", 1, 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, "", 3, 3, null],
|
||||
[ true, "call_mem_out_of_bounds", 4, 1, null],
|
||||
[ true, "", 2, 3, null],
|
||||
[ true, "call_mem_out_of_bounds", 3, 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, sig_index
|
||||
kExprCallIndirect, kArity0, sig_index
|
||||
])
|
||||
.exportFunc()
|
||||
builder.appendToTable([0]);
|
||||
|
@ -16,7 +16,7 @@ function makeFFI(func) {
|
||||
.addBody([
|
||||
kExprGetLocal, 0, // --
|
||||
kExprGetLocal, 1, // --
|
||||
kExprCallFunction, 0, // --
|
||||
kExprCallImport, kArity2, 0, // --
|
||||
])
|
||||
.exportFunc()
|
||||
|
||||
|
@ -65,8 +65,8 @@ assertFails(kSig_i_dd, [kExprGetLocal, 0]);
|
||||
var func = builder.addFunction("", kSig_v_v)
|
||||
.addBody([kExprNop]);
|
||||
|
||||
builder.addExplicitSection([kStartSectionCode, 0]);
|
||||
builder.addExplicitSection([kStartSectionCode, 0]);
|
||||
builder.addExplicitSection([kDeclStart, 0]);
|
||||
builder.addExplicitSection([kDeclStart, 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.buffer;
|
||||
var memory = module.exports.memory;
|
||||
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([kExprCallFunction, 0]);
|
||||
.addBody([kExprCallImport, kArity0, 0]);
|
||||
|
||||
builder.addStart(func.index);
|
||||
|
||||
|
@ -26,8 +26,8 @@ var expect_no_elison = 1;
|
||||
.addFunction("second_export", sig_index)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprCallFunction, 0,
|
||||
kExprReturn
|
||||
kExprCallImport, kArity1, 0,
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
@ -39,8 +39,8 @@ var expect_no_elison = 1;
|
||||
.addFunction("first_export", sig_index)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprCallFunction, 2,
|
||||
kExprReturn
|
||||
kExprCallFunction, kArity1, 1,
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
first_module
|
||||
@ -49,8 +49,8 @@ var expect_no_elison = 1;
|
||||
kExprI32Const, 1,
|
||||
kExprGetLocal, 0,
|
||||
kExprI32Add,
|
||||
kExprCallFunction, 0,
|
||||
kExprReturn
|
||||
kExprCallImport, kArity1, 0,
|
||||
kExprReturn, kArity1
|
||||
]);
|
||||
|
||||
var f = second_module
|
||||
@ -83,8 +83,8 @@ var expect_no_elison = 1;
|
||||
.addFunction("second_export", sig_index_1)
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprCallFunction, 0,
|
||||
kExprReturn
|
||||
kExprCallImport, kArity1, 0,
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
@ -97,8 +97,8 @@ var expect_no_elison = 1;
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprCallFunction, 2,
|
||||
kExprReturn
|
||||
kExprCallFunction, kArity2, 1,
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
first_module
|
||||
@ -106,8 +106,8 @@ var expect_no_elison = 1;
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprCallFunction, 0,
|
||||
kExprReturn
|
||||
kExprCallImport, kArity2, 0,
|
||||
kExprReturn, kArity1
|
||||
]);
|
||||
|
||||
var f = second_module
|
||||
@ -142,8 +142,8 @@ var expect_no_elison = 1;
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprGetLocal, 2,
|
||||
kExprCallFunction, 0,
|
||||
kExprReturn
|
||||
kExprCallImport, kArity3, 0,
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
@ -156,8 +156,8 @@ var expect_no_elison = 1;
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprCallFunction, 2,
|
||||
kExprReturn
|
||||
kExprCallFunction, kArity2, 1,
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
first_module
|
||||
@ -165,8 +165,8 @@ var expect_no_elison = 1;
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprCallFunction, 0,
|
||||
kExprReturn
|
||||
kExprCallImport, kArity2, 0,
|
||||
kExprReturn, kArity1
|
||||
]);
|
||||
|
||||
var f = second_module
|
||||
@ -200,8 +200,8 @@ var expect_no_elison = 1;
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprCallFunction, 0,
|
||||
kExprReturn
|
||||
kExprCallImport, kArity2, 0,
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
@ -214,8 +214,8 @@ var expect_no_elison = 1;
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprCallFunction, 2,
|
||||
kExprReturn
|
||||
kExprCallFunction, kArity2, 1,
|
||||
kExprReturn, kArity1
|
||||
])
|
||||
.exportFunc();
|
||||
first_module
|
||||
@ -223,8 +223,8 @@ var expect_no_elison = 1;
|
||||
.addBody([
|
||||
kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kExprCallFunction, 0,
|
||||
kExprReturn
|
||||
kExprCallImport, kArity2, 0,
|
||||
kExprReturn, kArity1
|
||||
]);
|
||||
|
||||
var f = second_module
|
||||
|
@ -7,7 +7,7 @@
|
||||
load('test/mjsunit/wasm/wasm-constants.js');
|
||||
load('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
var debug = true;
|
||||
var debug = false;
|
||||
|
||||
(function BasicTest() {
|
||||
var module = new WasmModuleBuilder();
|
||||
@ -25,7 +25,7 @@ var debug = true;
|
||||
var module = new WasmModuleBuilder();
|
||||
var index = module.addImport("print", makeSig_v_x(kAstI32));
|
||||
module.addFunction("foo", kSig_v_v)
|
||||
.addBody([kExprI8Const, 13, kExprCallFunction, index])
|
||||
.addBody([kExprI8Const, 13, kExprCallImport, kArity1, index])
|
||||
.exportAs("main");
|
||||
|
||||
var buffer = module.toBuffer(debug);
|
||||
@ -38,7 +38,7 @@ var debug = true;
|
||||
var module = new WasmModuleBuilder();
|
||||
module.addFunction(undefined, kSig_i_i)
|
||||
.addLocals({i32_count: 1})
|
||||
.addBody([kExprGetLocal, 0, kExprSetLocal, 1, kExprGetLocal, 1])
|
||||
.addBody([kExprGetLocal, 0, kExprSetLocal, 1])
|
||||
.exportAs("main");
|
||||
|
||||
var buffer = module.toBuffer(debug);
|
||||
@ -60,7 +60,7 @@ var debug = true;
|
||||
var module = new WasmModuleBuilder();
|
||||
module.addFunction(undefined, makeSig_r_x(p.type, p.type))
|
||||
.addLocals(p.locals)
|
||||
.addBody([kExprGetLocal, 0, kExprSetLocal, 1, kExprGetLocal, 1])
|
||||
.addBody([kExprGetLocal, 0, kExprSetLocal, 1])
|
||||
.exportAs("main");
|
||||
|
||||
var buffer = module.toBuffer(debug);
|
||||
@ -75,7 +75,7 @@ var debug = true;
|
||||
module.addFunction("add", kSig_i_ii)
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add]);
|
||||
module.addFunction("main", kSig_i_ii)
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, 0])
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprCallFunction, kArity2, 0])
|
||||
.exportAs("main");
|
||||
|
||||
var instance = module.instantiate();
|
||||
@ -89,7 +89,7 @@ var debug = true;
|
||||
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32Add]);
|
||||
module.addFunction("main", kSig_i_iii)
|
||||
.addBody([kExprGetLocal,
|
||||
1, kExprGetLocal, 2, kExprGetLocal, 0, kExprCallIndirect, 0])
|
||||
0, kExprGetLocal, 1, kExprGetLocal, 2, kExprCallIndirect, kArity2, 0])
|
||||
.exportAs("main");
|
||||
module.appendToTable([0]);
|
||||
|
||||
@ -143,7 +143,7 @@ var debug = true;
|
||||
var module = new WasmModuleBuilder();
|
||||
var index = module.addImportWithModule("mod", "print", makeSig_v_x(kAstI32));
|
||||
module.addFunction("foo", kSig_v_v)
|
||||
.addBody([kExprI8Const, 19, kExprCallFunction, index])
|
||||
.addBody([kExprI8Const, 19, kExprCallImport, kArity1, index])
|
||||
.exportAs("main");
|
||||
|
||||
var buffer = module.toBuffer(debug);
|
||||
|
@ -30,29 +30,29 @@ var sig_index = builder.addType(kSig_i_v)
|
||||
builder.addFunction("main", kSig_i_i)
|
||||
.addBody([
|
||||
// offset 1
|
||||
kExprBlock, kAstI32,
|
||||
kExprBlock,
|
||||
kExprGetLocal, 0,
|
||||
kExprI32Const, 2,
|
||||
kExprI32LtU,
|
||||
kExprIf, kAstStmt,
|
||||
// offset 9
|
||||
kExprIf,
|
||||
// offset 8
|
||||
kExprI32Const, 0x7e /* -2 */,
|
||||
kExprGetLocal, 0,
|
||||
kExprI32DivU,
|
||||
// offset 15
|
||||
// offset 13
|
||||
kExprI32LoadMem, 0, 0,
|
||||
kExprBr, 1,
|
||||
kExprBr, 1, 1,
|
||||
kExprEnd,
|
||||
// offset 21
|
||||
// offset 20
|
||||
kExprGetLocal, 0,
|
||||
kExprI32Const, 2,
|
||||
kExprI32Eq,
|
||||
kExprIf, kAstStmt,
|
||||
kExprIf,
|
||||
kExprUnreachable,
|
||||
kExprEnd,
|
||||
// offset 30
|
||||
kExprGetLocal, 0,
|
||||
kExprCallIndirect, sig_index,
|
||||
// offset 28
|
||||
kExprGetLocal, 0,
|
||||
kExprCallIndirect, kArity0, sig_index,
|
||||
kExprEnd,
|
||||
])
|
||||
.exportAs("main");
|
||||
@ -72,7 +72,7 @@ function testWasmTrap(value, reason, position) {
|
||||
}
|
||||
|
||||
// The actual tests:
|
||||
testWasmTrap(0, kTrapDivByZero, 14);
|
||||
testWasmTrap(1, kTrapMemOutOfBounds, 15);
|
||||
testWasmTrap(2, kTrapUnreachable, 28);
|
||||
testWasmTrap(3, kTrapFuncInvalid, 32);
|
||||
testWasmTrap(0, kTrapDivByZero, 12);
|
||||
testWasmTrap(1, kTrapMemOutOfBounds, 13);
|
||||
testWasmTrap(2, kTrapUnreachable, 26);
|
||||
testWasmTrap(3, kTrapFuncInvalid, 30);
|
||||
|
@ -49,7 +49,7 @@ function checkImportsAndExports(imported_module_name, imported_function_name,
|
||||
kSig_v_v);
|
||||
|
||||
builder.addFunction(internal_function_name, kSig_v_v)
|
||||
.addBody([kExprCallFunction, 0])
|
||||
.addBody([kExprCallImport, kArity0, 0])
|
||||
.exportAs(exported_function_name);
|
||||
|
||||
// sanity check: does javascript agree with out shouldThrow annotation?
|
||||
|
@ -25,7 +25,7 @@ try {
|
||||
var data = bytes(
|
||||
kWasmFunctionTypeForm, 0, 1, kAstI32, // signature
|
||||
kDeclNoLocals, // --
|
||||
kExprBlock, kAstStmt, kExprNop, kExprNop, kExprEnd // body
|
||||
kExprBlock, kExprNop, kExprNop, kExprEnd // body
|
||||
);
|
||||
|
||||
Wasm.verifyFunction(data);
|
||||
|
@ -21,7 +21,7 @@ var kWasmH1 = 0x61;
|
||||
var kWasmH2 = 0x73;
|
||||
var kWasmH3 = 0x6d;
|
||||
|
||||
var kWasmV0 = 0xC;
|
||||
var kWasmV0 = 11;
|
||||
var kWasmV1 = 0;
|
||||
var kWasmV2 = 0;
|
||||
var kWasmV3 = 0;
|
||||
@ -51,24 +51,30 @@ function bytesWithHeader() {
|
||||
var kDeclNoLocals = 0;
|
||||
|
||||
// Section declaration constants
|
||||
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 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 kArity0 = 0;
|
||||
var kArity1 = 1;
|
||||
var kArity2 = 2;
|
||||
var kArity3 = 3;
|
||||
var kWasmFunctionTypeForm = 0x40;
|
||||
var kWasmAnyFunctionTypeForm = 0x20;
|
||||
|
||||
var kResizableMaximumFlag = 1;
|
||||
var section_names = [
|
||||
"memory", "type", "old_function", "global", "data",
|
||||
"table", "end", "start", "import", "export",
|
||||
"function", "code", "name"];
|
||||
|
||||
// Function declaration flags
|
||||
var kDeclFunctionName = 0x01;
|
||||
@ -83,11 +89,6 @@ 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]);
|
||||
@ -132,8 +133,7 @@ function makeSig_r_xx(r, x) {
|
||||
}
|
||||
|
||||
// Opcodes
|
||||
var kExprUnreachable = 0x00;
|
||||
var kExprNop = 0x0a;
|
||||
var kExprNop = 0x00;
|
||||
var kExprBlock = 0x01;
|
||||
var kExprLoop = 0x02;
|
||||
var kExprIf = 0x03;
|
||||
@ -143,10 +143,9 @@ 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;
|
||||
@ -156,6 +155,7 @@ var kExprGetLocal = 0x14;
|
||||
var kExprSetLocal = 0x15;
|
||||
var kExprCallFunction = 0x16;
|
||||
var kExprCallIndirect = 0x17;
|
||||
var kExprCallImport = 0x18;
|
||||
var kExprI8Const = 0xcb;
|
||||
var kExprGetGlobal = 0xbb;
|
||||
var kExprSetGlobal = 0xbc;
|
||||
|
@ -61,7 +61,7 @@ class Binary extends Array {
|
||||
|
||||
emit_section(section_code, content_generator) {
|
||||
// Emit section name.
|
||||
this.emit_u8(section_code);
|
||||
this.emit_string(section_names[section_code]);
|
||||
// Emit the section to a temporary buffer: its full length isn't know yet.
|
||||
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 + this.imports.length;
|
||||
func.index = this.functions.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(kTypeSectionCode, section => {
|
||||
binary.emit_section(kDeclTypes, section => {
|
||||
section.emit_varint(wasm.types.length);
|
||||
for (let type of wasm.types) {
|
||||
section.emit_u8(kWasmFunctionTypeForm);
|
||||
@ -198,16 +198,27 @@ class WasmModuleBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
if (wasm.globals.length > 0) {
|
||||
if (debug) print ("emitting globals @ " + binary.length);
|
||||
binary.emit_section(kDeclGlobals, section => {
|
||||
section.emit_varint(wasm.globals.length);
|
||||
for (let global_type of wasm.globals) {
|
||||
section.emit_varint(0); // length of global name
|
||||
section.emit_u8(global_type);
|
||||
section.emit_u8(false); // we do not support exported globals
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add imports section
|
||||
if (wasm.imports.length > 0) {
|
||||
if (debug) print("emitting imports @ " + binary.length);
|
||||
binary.emit_section(kImportSectionCode, section => {
|
||||
binary.emit_section(kDeclImports, 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -218,7 +229,7 @@ class WasmModuleBuilder {
|
||||
let exports = 0;
|
||||
if (wasm.functions.length > 0) {
|
||||
if (debug) print("emitting function decls @ " + binary.length);
|
||||
binary.emit_section(kFunctionSectionCode, section => {
|
||||
binary.emit_section(kDeclFunctions, section => {
|
||||
section.emit_varint(wasm.functions.length);
|
||||
for (let func of wasm.functions) {
|
||||
has_names = has_names || (func.name != undefined &&
|
||||
@ -232,108 +243,56 @@ class WasmModuleBuilder {
|
||||
// Add table.
|
||||
if (wasm.table.length > 0) {
|
||||
if (debug) print("emitting table @ " + binary.length);
|
||||
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);
|
||||
binary.emit_section(kDeclTable, section => {
|
||||
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(kMemorySectionCode, section => {
|
||||
section.emit_u8(1); // one memory entry
|
||||
section.emit_varint(kResizableMaximumFlag);
|
||||
binary.emit_section(kDeclMemory, section => {
|
||||
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.
|
||||
var mem_export = (wasm.memory != undefined && wasm.memory.exp);
|
||||
if (exports > 0 || mem_export) {
|
||||
if (exports > 0) {
|
||||
if (debug) print("emitting exports @ " + binary.length);
|
||||
binary.emit_section(kExportSectionCode, section => {
|
||||
section.emit_varint(exports + (mem_export ? 1 : 0));
|
||||
binary.emit_section(kDeclExports, section => {
|
||||
section.emit_varint(exports);
|
||||
for (let func of wasm.functions) {
|
||||
for (let exp of func.exports) {
|
||||
section.emit_string(exp);
|
||||
section.emit_u8(kExternalFunction);
|
||||
section.emit_varint(func.index);
|
||||
section.emit_string(exp);
|
||||
}
|
||||
}
|
||||
if (mem_export) {
|
||||
section.emit_string("memory");
|
||||
section.emit_u8(kExternalMemory);
|
||||
section.emit_u8(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add start function section.
|
||||
if (wasm.start_index != undefined) {
|
||||
if (debug) print("emitting start function @ " + binary.length);
|
||||
binary.emit_section(kStartSectionCode, section => {
|
||||
binary.emit_section(kDeclStart, 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(kCodeSectionCode, section => {
|
||||
binary.emit_section(kDeclCode, section => {
|
||||
section.emit_varint(wasm.functions.length);
|
||||
for (let func of wasm.functions) {
|
||||
// Function body length will be patched later.
|
||||
@ -372,13 +331,10 @@ class WasmModuleBuilder {
|
||||
// Add data segments.
|
||||
if (wasm.segments.length > 0) {
|
||||
if (debug) print("emitting data segments @ " + binary.length);
|
||||
binary.emit_section(kDataSectionCode, section => {
|
||||
binary.emit_section(kDeclData, 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);
|
||||
}
|
||||
@ -394,8 +350,7 @@ class WasmModuleBuilder {
|
||||
// Add function names.
|
||||
if (has_names) {
|
||||
if (debug) print("emitting names @ " + binary.length);
|
||||
binary.emit_section(kUnknownSectionCode, section => {
|
||||
section.emit_string("name");
|
||||
binary.emit_section(kDeclNames, section => {
|
||||
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
@ -24,28 +24,30 @@ namespace wasm {
|
||||
#define B2(a, b) kExprBlock, a, b, kExprEnd
|
||||
#define B3(a, b, c) kExprBlock, a, b, c, kExprEnd
|
||||
|
||||
#define TRANSFER_VOID 0
|
||||
#define TRANSFER_ONE 1
|
||||
|
||||
struct ExpectedPcDelta {
|
||||
struct ExpectedTarget {
|
||||
pc_t pc;
|
||||
pcdiff_t expected;
|
||||
ControlTransfer expected;
|
||||
};
|
||||
|
||||
// For nicer error messages.
|
||||
class ControlTransferMatcher : public MatcherInterface<const pcdiff_t&> {
|
||||
class ControlTransferMatcher : public MatcherInterface<const ControlTransfer&> {
|
||||
public:
|
||||
explicit ControlTransferMatcher(pc_t pc, const pcdiff_t& expected)
|
||||
explicit ControlTransferMatcher(pc_t pc, const ControlTransfer& expected)
|
||||
: pc_(pc), expected_(expected) {}
|
||||
|
||||
void DescribeTo(std::ostream* os) const override {
|
||||
*os << "@" << pc_ << " pcdiff = " << expected_;
|
||||
*os << "@" << pc_ << " {pcdiff = " << expected_.pcdiff
|
||||
<< ", spdiff = " << expected_.spdiff
|
||||
<< ", action = " << expected_.action << "}";
|
||||
}
|
||||
|
||||
bool MatchAndExplain(const pcdiff_t& input,
|
||||
bool MatchAndExplain(const ControlTransfer& input,
|
||||
MatchResultListener* listener) const override {
|
||||
if (input != expected_) {
|
||||
*listener << "@" << pc_ << " pcdiff = " << input;
|
||||
if (input.pcdiff != expected_.pcdiff || input.spdiff != expected_.spdiff ||
|
||||
input.action != expected_.action) {
|
||||
*listener << "@" << pc_ << " {pcdiff = " << input.pcdiff
|
||||
<< ", spdiff = " << input.spdiff
|
||||
<< ", action = " << input.action << "}";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -53,43 +55,36 @@ class ControlTransferMatcher : public MatcherInterface<const pcdiff_t&> {
|
||||
|
||||
private:
|
||||
pc_t pc_;
|
||||
const pcdiff_t& expected_;
|
||||
const ControlTransfer& expected_;
|
||||
};
|
||||
|
||||
class ControlTransferTest : public TestWithZone {
|
||||
public:
|
||||
void CheckPcDeltas(const byte* start, const byte* end,
|
||||
ExpectedPcDelta* expected_deltas, size_t num_targets) {
|
||||
void CheckControlTransfers(const byte* start, const byte* end,
|
||||
ExpectedTarget* expected_targets,
|
||||
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_deltas[i].pc;
|
||||
pc_t pc = expected_targets[i].pc;
|
||||
auto it = map.find(pc);
|
||||
if (it == map.end()) {
|
||||
EXPECT_TRUE(false) << "expected control target @ " << pc;
|
||||
printf("expected control target @ +%zu\n", pc);
|
||||
EXPECT_TRUE(false);
|
||||
} else {
|
||||
pcdiff_t expected = expected_deltas[i].expected;
|
||||
pcdiff_t& target = it->second;
|
||||
ControlTransfer& expected = expected_targets[i].expected;
|
||||
ControlTransfer& 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 (targets[i].pc == pc) {
|
||||
if (expected_targets[i].pc == pc) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -103,128 +98,125 @@ class ControlTransferTest : public TestWithZone {
|
||||
}
|
||||
};
|
||||
|
||||
#define EXPECT_PC_DELTAS(...) \
|
||||
do { \
|
||||
ExpectedPcDelta pairs[] = {__VA_ARGS__}; \
|
||||
CheckPcDeltas(code, code + sizeof(code), pairs, arraysize(pairs)); \
|
||||
// Macro for simplifying tests below.
|
||||
#define EXPECT_TARGETS(...) \
|
||||
do { \
|
||||
ExpectedTarget pairs[] = {__VA_ARGS__}; \
|
||||
CheckControlTransfers(code, code + sizeof(code), pairs, arraysize(pairs)); \
|
||||
} while (false)
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleIf) {
|
||||
byte code[] = {
|
||||
kExprI32Const, // @0
|
||||
0, // @1
|
||||
0, // +1
|
||||
kExprIf, // @2
|
||||
kLocalVoid, // @3
|
||||
kExprEnd // @4
|
||||
kExprEnd // @3
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 2});
|
||||
EXPECT_TARGETS({2, {2, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{3, {1, 0, ControlTransfer::kPushVoid}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleIf1) {
|
||||
byte code[] = {
|
||||
kExprI32Const, // @0
|
||||
0, // @1
|
||||
0, // +1
|
||||
kExprIf, // @2
|
||||
kLocalVoid, // @3
|
||||
kExprNop, // @4
|
||||
kExprEnd // @5
|
||||
kExprNop, // @3
|
||||
kExprEnd // @4
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 3});
|
||||
EXPECT_TARGETS({2, {3, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{4, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleIf2) {
|
||||
byte code[] = {
|
||||
kExprI32Const, // @0
|
||||
0, // @1
|
||||
0, // +1
|
||||
kExprIf, // @2
|
||||
kLocalVoid, // @3
|
||||
kExprNop, // @3
|
||||
kExprNop, // @4
|
||||
kExprNop, // @5
|
||||
kExprEnd // @6
|
||||
kExprEnd // @5
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 4});
|
||||
EXPECT_TARGETS({2, {4, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{5, {1, 2, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleIfElse) {
|
||||
byte code[] = {
|
||||
kExprI32Const, // @0
|
||||
0, // @1
|
||||
0, // +1
|
||||
kExprIf, // @2
|
||||
kLocalVoid, // @3
|
||||
kExprElse, // @4
|
||||
kExprEnd // @5
|
||||
kExprElse, // @3
|
||||
kExprEnd // @4
|
||||
};
|
||||
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});
|
||||
EXPECT_TARGETS({2, {2, 0, ControlTransfer::kNoAction}}, // --
|
||||
{3, {2, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{4, {1, 0, ControlTransfer::kPushVoid}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleIfElse1) {
|
||||
byte code[] = {
|
||||
kExprI32Const, // @0
|
||||
0, // @1
|
||||
0, // +1
|
||||
kExprIf, // @2
|
||||
kLocalVoid, // @3
|
||||
kExprNop, // @3
|
||||
kExprElse, // @4
|
||||
kExprNop, // @5
|
||||
kExprEnd // @6
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 3}, {4, 3});
|
||||
EXPECT_TARGETS({2, {3, 0, ControlTransfer::kNoAction}}, // --
|
||||
{4, {3, 1, ControlTransfer::kPopAndRepush}}, // --
|
||||
{6, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, IfBr) {
|
||||
byte code[] = {
|
||||
kExprI32Const, // @0
|
||||
0, // @1
|
||||
0, // +1
|
||||
kExprIf, // @2
|
||||
kLocalVoid, // @3
|
||||
kExprBr, // @4
|
||||
0, // @5
|
||||
kExprBr, // @3
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprEnd // @6
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 4}, {4, 3});
|
||||
EXPECT_TARGETS({2, {5, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{3, {4, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{6, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, IfBrElse) {
|
||||
byte code[] = {
|
||||
kExprI32Const, // @0
|
||||
0, // @1
|
||||
0, // +1
|
||||
kExprIf, // @2
|
||||
kLocalVoid, // @3
|
||||
kExprBr, // @4
|
||||
0, // @5
|
||||
kExprBr, // @3
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprElse, // @6
|
||||
kExprEnd // @7
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 5}, {4, 4}, {6, 2});
|
||||
EXPECT_TARGETS({2, {5, 0, ControlTransfer::kNoAction}}, // --
|
||||
{3, {5, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{6, {2, 1, ControlTransfer::kPopAndRepush}}, // --
|
||||
{7, {1, 0, ControlTransfer::kPushVoid}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, IfElseBr) {
|
||||
byte code[] = {
|
||||
kExprI32Const, // @0
|
||||
0, // @1
|
||||
0, // +1
|
||||
kExprIf, // @2
|
||||
kLocalVoid, // @3
|
||||
kExprNop, // @3
|
||||
kExprElse, // @4
|
||||
kExprBr, // @5
|
||||
0, // @6
|
||||
kExprEnd // @7
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprEnd // @8
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 3}, {4, 4}, {5, 3});
|
||||
EXPECT_TARGETS({2, {3, 0, ControlTransfer::kNoAction}}, // --
|
||||
{4, {5, 1, ControlTransfer::kPopAndRepush}}, // --
|
||||
{5, {4, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{8, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, BlockEmpty) {
|
||||
@ -232,233 +224,177 @@ TEST_F(ControlTransferTest, BlockEmpty) {
|
||||
kExprBlock, // @0
|
||||
kExprEnd // @1
|
||||
};
|
||||
CheckPcDeltas(code, code + sizeof(code), nullptr, 0);
|
||||
EXPECT_TARGETS({1, {1, 0, ControlTransfer::kPushVoid}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, Br0) {
|
||||
byte code[] = {
|
||||
kExprBlock, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprBr, // @2
|
||||
0, // @3
|
||||
kExprBr, // @1
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprEnd // @4
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 3});
|
||||
EXPECT_TARGETS({1, {4, 0, ControlTransfer::kPushVoid}},
|
||||
{4, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, Br1) {
|
||||
byte code[] = {
|
||||
kExprBlock, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprNop, // @2
|
||||
kExprBr, // @3
|
||||
0, // @4
|
||||
kExprNop, // @1
|
||||
kExprBr, // @2
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprEnd // @5
|
||||
};
|
||||
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});
|
||||
EXPECT_TARGETS({2, {4, 1, ControlTransfer::kPopAndRepush}}, // --
|
||||
{5, {1, 2, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, Br2) {
|
||||
byte code[] = {
|
||||
kExprBlock, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprNop, // @1
|
||||
kExprNop, // @2
|
||||
kExprNop, // @3
|
||||
kExprBr, // @4
|
||||
0, // @5
|
||||
kExprBr, // @3
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprEnd // @6
|
||||
};
|
||||
EXPECT_PC_DELTAS({4, 3});
|
||||
EXPECT_TARGETS({3, {4, 2, ControlTransfer::kPopAndRepush}}, // --
|
||||
{6, {1, 3, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, Br0b) {
|
||||
byte code[] = {
|
||||
kExprBlock, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprBr, // @2
|
||||
0, // @3
|
||||
kExprBr, // @1
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprNop, // @4
|
||||
kExprEnd // @5
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 4});
|
||||
EXPECT_TARGETS({1, {5, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{5, {1, 2, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, Br0c) {
|
||||
byte code[] = {
|
||||
kExprBlock, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprBr, // @2
|
||||
0, // @3
|
||||
kExprBr, // @1
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprNop, // @4
|
||||
kExprNop, // @5
|
||||
kExprEnd // @6
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 5});
|
||||
EXPECT_TARGETS({1, {6, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{6, {1, 3, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleLoop1) {
|
||||
byte code[] = {
|
||||
kExprLoop, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprBr, // @2
|
||||
0, // @3
|
||||
kExprEnd // @4
|
||||
kExprLoop, // @0
|
||||
kExprBr, // @1
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprEnd // @4
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, -2});
|
||||
EXPECT_TARGETS({1, {-1, 0, ControlTransfer::kNoAction}}, // --
|
||||
{4, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleLoop2) {
|
||||
byte code[] = {
|
||||
kExprLoop, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprNop, // @2
|
||||
kExprBr, // @3
|
||||
0, // @4
|
||||
kExprEnd // @5
|
||||
kExprLoop, // @0
|
||||
kExprNop, // @1
|
||||
kExprBr, // @2
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
kExprEnd // @5
|
||||
};
|
||||
EXPECT_PC_DELTAS({3, -3});
|
||||
EXPECT_TARGETS({2, {-2, 1, ControlTransfer::kNoAction}}, // --
|
||||
{5, {1, 2, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleLoopExit1) {
|
||||
byte code[] = {
|
||||
kExprLoop, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprBr, // @2
|
||||
1, // @3
|
||||
kExprEnd // @4
|
||||
kExprLoop, // @0
|
||||
kExprBr, // @1
|
||||
ARITY_0, // +1
|
||||
1, // +1
|
||||
kExprEnd // @4
|
||||
};
|
||||
EXPECT_PC_DELTAS({2, 3});
|
||||
EXPECT_TARGETS({1, {4, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{4, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, SimpleLoopExit2) {
|
||||
byte code[] = {
|
||||
kExprLoop, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprNop, // @2
|
||||
kExprBr, // @3
|
||||
1, // @4
|
||||
kExprEnd // @5
|
||||
kExprLoop, // @0
|
||||
kExprNop, // @1
|
||||
kExprBr, // @2
|
||||
ARITY_0, // +1
|
||||
1, // +1
|
||||
kExprEnd // @5
|
||||
};
|
||||
EXPECT_PC_DELTAS({3, 3});
|
||||
EXPECT_TARGETS({2, {4, 1, ControlTransfer::kPopAndRepush}}, // --
|
||||
{5, {1, 2, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, BrTable0) {
|
||||
byte code[] = {
|
||||
kExprBlock, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprI8Const, // @2
|
||||
0, // @3
|
||||
kExprBrTable, // @4
|
||||
0, // @5
|
||||
U32V_1(0), // @6
|
||||
kExprEnd // @7
|
||||
kExprI8Const, // @1
|
||||
0, // +1
|
||||
kExprBrTable, // @3
|
||||
ARITY_0, // +1
|
||||
0, // +1
|
||||
U32_LE(0), // +4
|
||||
kExprEnd // @10
|
||||
};
|
||||
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});
|
||||
EXPECT_TARGETS({3, {8, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{10, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, BrTable1) {
|
||||
byte code[] = {
|
||||
kExprBlock, // @0
|
||||
kLocalVoid, // @1
|
||||
kExprI8Const, // @2
|
||||
0, // @3
|
||||
kExprBrTable, // @4
|
||||
1, // @5
|
||||
U32V_1(0), // @6
|
||||
U32V_1(0), // @7
|
||||
kExprEnd // @8
|
||||
kExprI8Const, // @1
|
||||
0, // +1
|
||||
kExprBrTable, // @3
|
||||
ARITY_0, // +1
|
||||
1, // +1
|
||||
U32_LE(0), // +4
|
||||
U32_LE(0), // +4
|
||||
kExprEnd // @14
|
||||
};
|
||||
EXPECT_PC_DELTAS({4, 5}, {5, 4});
|
||||
EXPECT_TARGETS({3, {12, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{4, {11, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{14, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
TEST_F(ControlTransferTest, BrTable2) {
|
||||
byte code[] = {
|
||||
kExprBlock, // @0
|
||||
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
|
||||
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
|
||||
};
|
||||
EXPECT_PC_DELTAS({6, 6}, {7, 5}, {8, 5});
|
||||
EXPECT_TARGETS({4, {16, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{5, {15, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{6, {15, 0, ControlTransfer::kPushVoid}}, // --
|
||||
{19, {1, 1, ControlTransfer::kPopAndRepush}}, // --
|
||||
{20, {1, 1, ControlTransfer::kPopAndRepush}});
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
|
@ -39,7 +39,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Empty0) {
|
||||
}
|
||||
|
||||
TEST_F(WasmLoopAssignmentAnalyzerTest, Empty1) {
|
||||
byte code[] = {kExprLoop, kLocalVoid, 0};
|
||||
byte code[] = {kExprLoop, 0};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
BitVector* assigned = Analyze(code, code + arraysize(code));
|
||||
for (int j = 0; j < assigned->length(); j++) {
|
||||
@ -60,17 +60,6 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, One) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WasmLoopAssignmentAnalyzerTest, TeeOne) {
|
||||
num_locals = 5;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
byte code[] = {WASM_LOOP(WASM_TEE_LOCAL(i, WASM_ZERO))};
|
||||
BitVector* assigned = Analyze(code, code + arraysize(code));
|
||||
for (int j = 0; j < assigned->length(); j++) {
|
||||
CHECK_EQ(j == i, assigned->Contains(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WasmLoopAssignmentAnalyzerTest, OneBeyond) {
|
||||
num_locals = 5;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
@ -109,10 +98,24 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, NestedIf) {
|
||||
}
|
||||
}
|
||||
|
||||
static byte LEBByte(uint32_t val, byte which) {
|
||||
byte b = (val >> (which * 7)) & 0x7F;
|
||||
if (val >> ((which + 1) * 7)) b |= 0x80;
|
||||
return b;
|
||||
}
|
||||
|
||||
TEST_F(WasmLoopAssignmentAnalyzerTest, BigLocal) {
|
||||
num_locals = 65000;
|
||||
for (int i = 13; i < 65000; i = static_cast<int>(i * 1.5)) {
|
||||
byte code[] = {WASM_LOOP(WASM_I8(11), kExprSetLocal, U32V_3(i))};
|
||||
byte code[] = {kExprLoop,
|
||||
1,
|
||||
kExprSetLocal,
|
||||
LEBByte(i, 0),
|
||||
LEBByte(i, 1),
|
||||
LEBByte(i, 2),
|
||||
11,
|
||||
12,
|
||||
13};
|
||||
|
||||
BitVector* assigned = Analyze(code, code + arraysize(code));
|
||||
for (int j = 0; j < assigned->length(); j++) {
|
||||
@ -169,7 +172,7 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) {
|
||||
WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, WASM_GET_LOCAL(kSum)),
|
||||
WASM_GET_LOCAL(kIter))};
|
||||
|
||||
BitVector* assigned = Analyze(code + 2, code + arraysize(code));
|
||||
BitVector* assigned = Analyze(code + 1, code + arraysize(code));
|
||||
for (int j = 0; j < assigned->length(); j++) {
|
||||
bool expected = j == kIter || j == kSum;
|
||||
CHECK_EQ(expected, assigned->Contains(j));
|
||||
@ -177,9 +180,9 @@ TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) {
|
||||
}
|
||||
|
||||
TEST_F(WasmLoopAssignmentAnalyzerTest, Malformed) {
|
||||
byte code[] = {kExprLoop, kLocalVoid, kExprF32Neg, kExprBrTable, 0x0e, 'h',
|
||||
'e', 'l', 'l', 'o', ',', ' ',
|
||||
'w', 'o', 'r', 'l', 'd', '!'};
|
||||
byte code[] = {kExprLoop, 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
@ -56,44 +56,45 @@ TEST_F(WasmMacroGenTest, Statements) {
|
||||
|
||||
EXPECT_SIZE(7, WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, WASM_ZERO));
|
||||
|
||||
EXPECT_SIZE(6, WASM_IF(WASM_ZERO, WASM_NOP));
|
||||
EXPECT_SIZE(5, WASM_IF(WASM_ZERO, WASM_NOP));
|
||||
|
||||
EXPECT_SIZE(8, WASM_IF_ELSE(WASM_ZERO, WASM_NOP, WASM_NOP));
|
||||
EXPECT_SIZE(7, WASM_IF_ELSE(WASM_ZERO, WASM_NOP, WASM_NOP));
|
||||
|
||||
EXPECT_SIZE(5, WASM_SELECT(WASM_ZERO, WASM_NOP, WASM_NOP));
|
||||
|
||||
EXPECT_SIZE(2, WASM_BR(0));
|
||||
EXPECT_SIZE(4, WASM_BR_IF(0, WASM_ZERO));
|
||||
EXPECT_SIZE(3, WASM_BR(0));
|
||||
EXPECT_SIZE(5, WASM_BR_IF(0, WASM_ZERO));
|
||||
|
||||
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(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(5, WASM_INFINITE_LOOP);
|
||||
|
||||
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(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(5, WASM_LOOP(WASM_BR(0)));
|
||||
EXPECT_SIZE(7, WASM_LOOP(WASM_BR_IF(0, WASM_ZERO)));
|
||||
|
||||
EXPECT_SIZE(1, WASM_RETURN0);
|
||||
EXPECT_SIZE(3, WASM_RETURN1(WASM_ZERO));
|
||||
EXPECT_SIZE(2, WASM_RETURN0);
|
||||
EXPECT_SIZE(4, WASM_RETURN1(WASM_ZERO));
|
||||
|
||||
EXPECT_SIZE(1, WASM_UNREACHABLE);
|
||||
}
|
||||
|
||||
TEST_F(WasmMacroGenTest, MacroStatements) {
|
||||
EXPECT_SIZE(11, WASM_WHILE(WASM_I8(0), WASM_NOP));
|
||||
EXPECT_SIZE(10, WASM_WHILE(WASM_I8(0), WASM_NOP));
|
||||
EXPECT_SIZE(7, WASM_INC_LOCAL(0));
|
||||
EXPECT_SIZE(7, WASM_INC_LOCAL_BY(0, 3));
|
||||
|
||||
EXPECT_SIZE(2, WASM_CONTINUE(0));
|
||||
EXPECT_SIZE(3, WASM_BREAK(0));
|
||||
EXPECT_SIZE(3, WASM_CONTINUE(0));
|
||||
}
|
||||
|
||||
TEST_F(WasmMacroGenTest, BrTable) {
|
||||
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)));
|
||||
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)));
|
||||
}
|
||||
|
||||
TEST_F(WasmMacroGenTest, Expressions) {
|
||||
@ -109,34 +110,43 @@ TEST_F(WasmMacroGenTest, Expressions) {
|
||||
|
||||
EXPECT_SIZE(3, WASM_NOT(WASM_ZERO));
|
||||
|
||||
EXPECT_SIZE(4, WASM_BRV(1, WASM_ZERO));
|
||||
EXPECT_SIZE(6, WASM_BRV_IF(1, WASM_ZERO, WASM_ZERO));
|
||||
EXPECT_SIZE(5, WASM_BRV(1, WASM_ZERO));
|
||||
EXPECT_SIZE(7, WASM_BRV_IF(1, WASM_ZERO, 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_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_LOOP(WASM_ZERO));
|
||||
EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_ZERO));
|
||||
EXPECT_SIZE(7, WASM_LOOP(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));
|
||||
}
|
||||
|
||||
TEST_F(WasmMacroGenTest, CallFunction) {
|
||||
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(0));
|
||||
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(1));
|
||||
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(11));
|
||||
EXPECT_SIZE(3, WASM_CALL_FUNCTION0(0));
|
||||
EXPECT_SIZE(3, WASM_CALL_FUNCTION0(1));
|
||||
EXPECT_SIZE(3, WASM_CALL_FUNCTION0(11));
|
||||
|
||||
EXPECT_SIZE(4, WASM_CALL_FUNCTION(0, WASM_ZERO));
|
||||
EXPECT_SIZE(6, WASM_CALL_FUNCTION(1, WASM_ZERO, WASM_ZERO));
|
||||
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));
|
||||
}
|
||||
|
||||
TEST_F(WasmMacroGenTest, CallIndirect) {
|
||||
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(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(6, WASM_CALL_INDIRECT1(0, WASM_ZERO, WASM_ZERO));
|
||||
EXPECT_SIZE(8, WASM_CALL_INDIRECT2(1, WASM_ZERO, WASM_ZERO, 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));
|
||||
}
|
||||
|
||||
TEST_F(WasmMacroGenTest, Int32Ops) {
|
||||
|
Loading…
Reference in New Issue
Block a user