Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1167)

Unified Diff: src/wasm/ast-decoder.cc

Issue 1830663002: [wasm] Binary 11: AST changes. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/wasm/ast-decoder.h ('k') | src/wasm/encoder.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/wasm/ast-decoder.cc
diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc
index 62026517a74605bc38bded99dcbdf6c7afb20d89..17c84c6ac24121e7d7e43c451c64f80fd3472970 100644
--- a/src/wasm/ast-decoder.cc
+++ b/src/wasm/ast-decoder.cc
@@ -42,17 +42,6 @@ struct Tree {
WasmOpcode opcode() const { return static_cast<WasmOpcode>(*pc); }
};
-// A production represents an incomplete decoded tree in the LR decoder.
-struct Production {
- Tree* tree; // the root of the syntax tree.
- int index; // the current index into the children of the tree.
-
- WasmOpcode opcode() const { return static_cast<WasmOpcode>(*pc()); }
- const byte* pc() const { return tree->pc; }
- bool done() const { return index >= static_cast<int>(tree->count); }
- Tree* last() const { return index > 0 ? tree->children[index - 1] : nullptr; }
-};
-
// An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph.
// It maintains a control state that tracks whether the environment
@@ -72,19 +61,30 @@ struct SsaEnv {
control = nullptr;
effect = nullptr;
}
+ void SetNotMerged() {
+ if (state == kMerged) state = kReached;
+ }
};
-// An entry in the stack of blocks during decoding.
-struct Block {
- SsaEnv* ssa_env; // SSA renaming environment.
- int stack_depth; // production stack depth.
+// An entry on the value stack.
+struct Value {
+ const byte* pc;
+ TFNode* node;
+ LocalType type;
};
-// An entry in the stack of ifs during decoding.
-struct IfEnv {
- SsaEnv* false_env;
- SsaEnv* merge_env;
- SsaEnv** case_envs;
+// An entry on the control stack (i.e. if, block, loop).
+struct Control {
+ const byte* pc;
+ int stack_depth; // stack height at the beginning of the construct.
+ SsaEnv* end_env; // end environment for the construct.
+ SsaEnv* false_env; // false environment (only for if).
+ TFNode* node; // result node for the construct.
+ LocalType type; // result type for the construct.
+ bool is_loop; // true if this is the inner label of a loop.
+
+ bool is_if() { return *pc == kExprIf; }
+ bool is_block() { return *pc == kExprBlock; }
};
// Macros that build nodes only if there is a graph and the current SSA
@@ -157,30 +157,50 @@ class WasmDecoder : public Decoder {
return false;
}
- inline bool Validate(const byte* pc, FunctionIndexOperand& operand) {
+ inline bool Validate(const byte* pc, CallFunctionOperand& operand) {
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->functions.size()) {
operand.sig = m->module->functions[operand.index].sig;
+ uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
+ if (operand.arity != expected) {
+ error(pc, pc + 1,
+ "arity mismatch in direct function call (expected %u, got %u)",
+ expected, operand.arity);
+ return false;
+ }
return true;
}
error(pc, pc + 1, "invalid function index");
return false;
}
- inline bool Validate(const byte* pc, SignatureIndexOperand& operand) {
+ inline bool Validate(const byte* pc, CallIndirectOperand& operand) {
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->signatures.size()) {
operand.sig = m->module->signatures[operand.index];
+ uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
+ if (operand.arity != expected) {
+ error(pc, pc + 1,
+ "arity mismatch in indirect function call (expected %u, got %u)",
+ expected, operand.arity);
+ return false;
+ }
return true;
}
error(pc, pc + 1, "invalid signature index");
return false;
}
- inline bool Validate(const byte* pc, ImportIndexOperand& operand) {
+ inline bool Validate(const byte* pc, CallImportOperand& operand) {
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->import_table.size()) {
operand.sig = m->module->import_table[operand.index].sig;
+ uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
+ if (operand.arity != expected) {
+ error(pc, pc + 1, "arity mismatch in import call (expected %u, got %u)",
+ expected, operand.arity);
+ return false;
+ }
return true;
}
error(pc, pc + 1, "invalid signature index");
@@ -188,9 +208,13 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, BreakDepthOperand& operand,
- ZoneVector<Block>& blocks) {
- if (operand.depth < blocks.size()) {
- operand.target = &blocks[blocks.size() - operand.depth - 1];
+ ZoneVector<Control>& control) {
+ if (operand.arity > 1) {
+ error(pc, pc + 1, "invalid arity for br or br_if");
+ return false;
+ }
+ if (operand.depth < control.size()) {
+ operand.target = &control[control.size() - operand.depth - 1];
return true;
}
error(pc, pc + 1, "invalid break depth");
@@ -199,6 +223,10 @@ class WasmDecoder : public Decoder {
bool Validate(const byte* pc, BranchTableOperand& operand,
size_t block_depth) {
+ if (operand.arity > 1) {
+ error(pc, pc + 1, "invalid arity for break");
+ return false;
+ }
// Verify table.
for (uint32_t i = 0; i < operand.table_count + 1; i++) {
uint32_t target = operand.read_entry(this, i);
@@ -229,47 +257,52 @@ class WasmDecoder : public Decoder {
case kExprLoadGlobal:
case kExprNop:
case kExprUnreachable:
+ case kExprEnd:
+ case kExprBlock:
+ case kExprLoop:
return 0;
- case kExprBr:
case kExprStoreGlobal:
case kExprSetLocal:
+ case kExprElse:
return 1;
+ case kExprBr: {
+ BreakDepthOperand operand(this, pc);
+ return operand.arity;
+ }
+ case kExprBrIf: {
+ BreakDepthOperand operand(this, pc);
+ return 1 + operand.arity;
+ }
+ case kExprBrTable: {
+ BranchTableOperand operand(this, pc);
+ return 1 + operand.arity;
+ }
+
case kExprIf:
- case kExprBrIf:
- return 2;
- case kExprIfElse:
+ return 1;
case kExprSelect:
return 3;
- case kExprBlock:
- case kExprLoop: {
- BlockCountOperand operand(this, pc);
- return operand.count;
- }
-
case kExprCallFunction: {
- FunctionIndexOperand operand(this, pc);
+ CallFunctionOperand operand(this, pc);
return static_cast<int>(
module_->GetFunctionSignature(operand.index)->parameter_count());
}
case kExprCallIndirect: {
- SignatureIndexOperand operand(this, pc);
+ CallIndirectOperand operand(this, pc);
return 1 + static_cast<int>(
module_->GetSignature(operand.index)->parameter_count());
}
case kExprCallImport: {
- ImportIndexOperand operand(this, pc);
+ CallImportOperand operand(this, pc);
return static_cast<int>(
module_->GetImportSignature(operand.index)->parameter_count());
}
case kExprReturn: {
return static_cast<int>(sig_->return_count());
}
- case kExprBrTable: {
- return 1;
- }
#define DECLARE_OPCODE_CASE(name, opcode, sig) \
case kExpr##name: \
@@ -281,7 +314,6 @@ class WasmDecoder : public Decoder {
FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE)
FOREACH_ASMJS_COMPAT_OPCODE(DECLARE_OPCODE_CASE)
#undef DECLARE_OPCODE_CASE
- case kExprDeclLocals:
default:
UNREACHABLE();
return 0;
@@ -298,11 +330,6 @@ class WasmDecoder : public Decoder {
MemoryAccessOperand operand(this, pc);
return 1 + operand.length;
}
- case kExprBlock:
- case kExprLoop: {
- BlockCountOperand operand(this, pc);
- return 1 + operand.length;
- }
case kExprBr:
case kExprBrIf: {
BreakDepthOperand operand(this, pc);
@@ -315,15 +342,15 @@ class WasmDecoder : public Decoder {
}
case kExprCallFunction: {
- FunctionIndexOperand operand(this, pc);
+ CallFunctionOperand operand(this, pc);
return 1 + operand.length;
}
case kExprCallIndirect: {
- SignatureIndexOperand operand(this, pc);
+ CallIndirectOperand operand(this, pc);
return 1 + operand.length;
}
case kExprCallImport: {
- ImportIndexOperand operand(this, pc);
+ CallImportOperand operand(this, pc);
return 1 + operand.length;
}
@@ -350,6 +377,10 @@ class WasmDecoder : public Decoder {
return 5;
case kExprF64Const:
return 9;
+ case kExprReturn: {
+ ReturnArityOperand operand(this, pc);
+ return 1 + operand.length;
+ }
default:
return 1;
@@ -368,48 +399,56 @@ class SR_WasmDecoder : public WasmDecoder {
builder_(builder),
base_(body.base),
local_type_vec_(zone),
- trees_(zone),
stack_(zone),
- blocks_(zone),
- ifs_(zone) {
+ control_(zone) {
local_types_ = &local_type_vec_;
}
- TreeResult Decode() {
+ bool Decode() {
+ base::ElapsedTimer decode_timer;
+ if (FLAG_trace_wasm_decode_time) {
+ decode_timer.Start();
+ }
+ stack_.clear();
+ control_.clear();
+
if (end_ < pc_) {
error(pc_, "function body end < start");
- return result_;
+ return false;
}
DecodeLocalDecls();
InitSsaEnv();
DecodeFunctionBody();
- Tree* tree = nullptr;
- if (ok()) {
- if (ssa_env_->go()) {
- if (stack_.size() > 0) {
- error(stack_.back().pc(), end_, "fell off end of code");
- }
- AddImplicitReturnAtEnd();
- }
- if (trees_.size() == 0) {
- if (sig_->return_count() > 0) {
- error(start_, "no trees created");
- }
- } else {
- tree = trees_[0];
- }
+ if (failed()) return TraceFailed();
+
+ if (!control_.empty()) {
+ error(pc_, control_.back().pc, "unterminated control structure");
+ return TraceFailed();
+ }
+
+ if (ssa_env_->go()) {
+ TRACE(" @%-6d #xx:%-20s|", startrel(pc_), "ImplicitReturn");
+ DoReturn();
+ if (failed()) return TraceFailed();
+ TRACE("\n");
}
- if (ok()) {
- TRACE("wasm-decode ok\n");
+ if (FLAG_trace_wasm_decode_time) {
+ double ms = decode_timer.Elapsed().InMillisecondsF();
+ PrintF("wasm-decode ok (%0.3f ms)\n\n", ms);
} else {
- TRACE("wasm-error module+%-6d func+%d: %s\n\n", baserel(error_pc_),
- startrel(error_pc_), error_msg_.get());
+ TRACE("wasm-decode ok\n\n");
}
- return toResult(tree);
+ return true;
+ }
+
+ bool TraceFailed() {
+ TRACE("wasm-error module+%-6d func+%d: %s\n\n", baserel(error_pc_),
+ startrel(error_pc_), error_msg_.get());
+ return false;
}
bool DecodeLocalDecls(AstLocalDecls& decls) {
@@ -447,15 +486,12 @@ class SR_WasmDecoder : public WasmDecoder {
Zone* zone_;
TFBuilder* builder_;
const byte* base_;
- TreeResult result_;
SsaEnv* ssa_env_;
ZoneVector<LocalType> local_type_vec_;
- ZoneVector<Tree*> trees_;
- ZoneVector<Production> stack_;
- ZoneVector<Block> blocks_;
- ZoneVector<IfEnv> ifs_;
+ ZoneVector<Value> stack_;
+ ZoneVector<Control> control_;
inline bool build() { return builder_ && ssa_env_->go(); }
@@ -507,53 +543,6 @@ class SR_WasmDecoder : public WasmDecoder {
}
}
- void Leaf(LocalType type, TFNode* node = nullptr) {
- size_t size = sizeof(Tree);
- Tree* tree = reinterpret_cast<Tree*>(zone_->New(size));
- tree->type = type;
- tree->count = 0;
- tree->pc = pc_;
- tree->node = node;
- tree->children[0] = nullptr;
- Reduce(tree);
- }
-
- void Shift(LocalType type, uint32_t count) {
- size_t size =
- sizeof(Tree) + (count == 0 ? 0 : ((count - 1) * sizeof(Tree*)));
- Tree* tree = reinterpret_cast<Tree*>(zone_->New(size));
- tree->type = type;
- tree->count = count;
- tree->pc = pc_;
- tree->node = nullptr;
- for (uint32_t i = 0; i < count; i++) tree->children[i] = nullptr;
- if (count == 0) {
- Production p = {tree, 0};
- Reduce(&p);
- Reduce(tree);
- } else {
- stack_.push_back({tree, 0});
- }
- }
-
- void Reduce(Tree* tree) {
- while (true) {
- if (stack_.size() == 0) {
- trees_.push_back(tree);
- break;
- }
- Production* p = &stack_.back();
- p->tree->children[p->index++] = tree;
- Reduce(p);
- if (p->done()) {
- tree = p->tree;
- stack_.pop_back();
- } else {
- break;
- }
- }
- }
-
char* indentation() {
static const int kMaxIndent = 64;
static char bytes[kMaxIndent + 1];
@@ -604,11 +593,11 @@ class SR_WasmDecoder : public WasmDecoder {
total_locals_ = local_type_vec_.size();
}
- // Decodes the body of a function, producing reduced trees into {result}.
+ // Decodes the body of a function.
void DecodeFunctionBody() {
- TRACE("wasm-decode %p...%p (%d bytes) %s\n",
+ TRACE("wasm-decode %p...%p (module+%d, %d bytes) %s\n",
reinterpret_cast<const void*>(start_),
- reinterpret_cast<const void*>(limit_),
+ reinterpret_cast<const void*>(limit_), baserel(pc_),
static_cast<int>(limit_ - start_), builder_ ? "graph building" : "");
if (pc_ >= limit_) return; // Nothing to do.
@@ -616,49 +605,45 @@ class SR_WasmDecoder : public WasmDecoder {
while (true) { // decoding loop.
int len = 1;
WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
- TRACE("wasm-decode module+%-6d %s func+%d: 0x%02x %s\n", baserel(pc_),
- indentation(), startrel(pc_), opcode,
- WasmOpcodes::OpcodeName(opcode));
+ TRACE(" @%-6d #%02x:%-20s|", startrel(pc_), opcode,
+ WasmOpcodes::ShortOpcodeName(opcode));
FunctionSig* sig = WasmOpcodes::Signature(opcode);
if (sig) {
- // A simple expression with a fixed signature.
- Shift(sig->GetReturn(), static_cast<uint32_t>(sig->parameter_count()));
- pc_ += len;
- if (pc_ >= limit_) {
- // End of code reached or exceeded.
- if (pc_ > limit_ && ok()) {
- error("Beyond end of code");
+ // Fast case of a simple operator.
+ TFNode* node;
+ switch (sig->parameter_count()) {
+ case 1: {
+ Value val = Pop(0, sig->GetParam(0));
+ node = BUILD(Unop, opcode, val.node);
+ break;
}
- return;
+ case 2: {
+ Value rval = Pop(1, sig->GetParam(1));
+ Value lval = Pop(0, sig->GetParam(0));
+ node = BUILD(Binop, opcode, lval.node, rval.node);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ node = nullptr;
+ break;
}
- continue; // back to decoding loop.
- }
-
- switch (opcode) {
- case kExprNop:
- Leaf(kAstStmt);
- break;
- case kExprBlock: {
- BlockCountOperand operand(this, pc_);
- if (operand.count < 1) {
- Leaf(kAstStmt);
- } else {
- Shift(kAstEnd, operand.count);
+ Push(GetReturnType(sig), node);
+ } else {
+ // Complex bytecode.
+ switch (opcode) {
+ case kExprNop:
+ Push(kAstStmt, nullptr);
+ break;
+ case kExprBlock: {
// The break environment is the outer environment.
SsaEnv* break_env = ssa_env_;
PushBlock(break_env);
SetEnv("block:start", Steal(break_env));
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprLoop: {
- BlockCountOperand operand(this, pc_);
- if (operand.count < 1) {
- Leaf(kAstStmt);
- } else {
- Shift(kAstEnd, operand.count);
+ case kExprLoop: {
// The break environment is the outer environment.
SsaEnv* break_env = ssa_env_;
PushBlock(break_env);
@@ -666,711 +651,601 @@ class SR_WasmDecoder : public WasmDecoder {
// The continue environment is the inner environment.
PrepareForLoop(pc_, cont_env);
SetEnv("loop:start", Split(cont_env));
- if (ssa_env_->go()) ssa_env_->state = SsaEnv::kReached;
- PushBlock(cont_env);
- blocks_.back().stack_depth = -1; // no production for inner block.
+ ssa_env_->SetNotMerged();
+ PushLoop(cont_env);
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprIf:
- Shift(kAstStmt, 2);
- break;
- case kExprIfElse:
- Shift(kAstEnd, 3); // Result type is typeof(x) in {c ? x : y}.
- break;
- case kExprSelect:
- Shift(kAstStmt, 3); // Result type is typeof(x) in {c ? x : y}.
- break;
- case kExprBr: {
- BreakDepthOperand operand(this, pc_);
- if (Validate(pc_, operand, blocks_)) {
- Shift(kAstEnd, 1);
+ case kExprIf: {
+ // Condition on top of stack. Split environments for branches.
+ Value cond = Pop(0, kAstI32);
+ TFNode* if_true = nullptr;
+ TFNode* if_false = nullptr;
+ BUILD(Branch, cond.node, &if_true, &if_false);
+ SsaEnv* end_env = ssa_env_;
+ SsaEnv* false_env = Split(ssa_env_);
+ false_env->control = if_false;
+ SsaEnv* true_env = Steal(ssa_env_);
+ true_env->control = if_true;
+ PushIf(end_env, false_env);
+ SetEnv("if:true", true_env);
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprBrIf: {
- BreakDepthOperand operand(this, pc_);
- if (Validate(pc_, operand, blocks_)) {
- Shift(kAstStmt, 2);
+ case kExprElse: {
+ if (control_.empty()) {
+ error(pc_, "else does not match any if");
+ break;
+ }
+ Control* c = &control_.back();
+ if (!c->is_if()) {
+ error(pc_, c->pc, "else does not match an if");
+ break;
+ }
+ if (c->false_env == nullptr) {
+ error(pc_, c->pc, "else already present for if");
+ break;
+ }
+ Value val = PopUpTo(c->stack_depth);
+ MergeInto(c->end_env, &c->node, &c->type, val);
+ // Switch to environment for false branch.
+ SetEnv("if_else:false", c->false_env);
+ c->false_env = nullptr; // record that an else is already seen
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprBrTable: {
- BranchTableOperand operand(this, pc_);
- if (Validate(pc_, operand, blocks_.size())) {
- Shift(kAstEnd, 1);
+ case kExprEnd: {
+ if (control_.empty()) {
+ error(pc_, "end does not match any if or block");
+ break;
+ }
+ const char* name = "block:end";
+ Control* c = &control_.back();
+ if (c->is_loop) {
+ // Loops always push control in pairs.
+ control_.pop_back();
+ c = &control_.back();
+ name = "loop:end";
+ }
+ Value val = PopUpTo(c->stack_depth);
+ if (c->is_if()) {
+ if (c->false_env != nullptr) {
+ // End the true branch of a one-armed if.
+ Goto(c->false_env, c->end_env);
+ val = {val.pc, nullptr, kAstStmt};
+ name = "if:merge";
+ } else {
+ // End the false branch of a two-armed if.
+ name = "if_else:merge";
+ }
+ }
+ if (ssa_env_->go()) {
+ MergeInto(c->end_env, &c->node, &c->type, val);
+ }
+ SetEnv(name, c->end_env);
+ stack_.resize(c->stack_depth);
+ Push(c->type, c->node);
+ control_.pop_back();
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprReturn: {
- int count = static_cast<int>(sig_->return_count());
- if (count == 0) {
- BUILD(Return, 0, builder_->Buffer(0));
- ssa_env_->Kill();
- Leaf(kAstEnd);
- } else {
- Shift(kAstEnd, count);
+ case kExprSelect: {
+ Value cond = Pop(2, kAstI32);
+ Value fval = Pop();
+ Value tval = Pop();
+ if (tval.type == kAstStmt || tval.type != fval.type) {
+ if (tval.type != kAstEnd && fval.type != kAstEnd) {
+ error(pc_, "type mismatch in select");
+ break;
+ }
+ }
+ if (build()) {
+ DCHECK(tval.type != kAstEnd);
+ DCHECK(fval.type != kAstEnd);
+ DCHECK(cond.type != kAstEnd);
+ TFNode* controls[2];
+ builder_->Branch(cond.node, &controls[0], &controls[1]);
+ TFNode* merge = builder_->Merge(2, controls);
+ TFNode* vals[2] = {tval.node, fval.node};
+ TFNode* phi = builder_->Phi(tval.type, 2, vals, merge);
+ Push(tval.type, phi);
+ ssa_env_->control = merge;
+ } else {
+ Push(tval.type, nullptr);
+ }
+ break;
}
- break;
- }
- case kExprUnreachable: {
- // TODO(clemensh): add source position for unreachable
- BUILD0(Unreachable);
- ssa_env_->Kill(SsaEnv::kControlEnd);
- Leaf(kAstEnd, nullptr);
- break;
- }
- case kExprI8Const: {
- ImmI8Operand operand(this, pc_);
- Leaf(kAstI32, BUILD(Int32Constant, operand.value));
- len = 1 + operand.length;
- break;
- }
- case kExprI32Const: {
- ImmI32Operand operand(this, pc_);
- Leaf(kAstI32, BUILD(Int32Constant, operand.value));
- len = 1 + operand.length;
- break;
- }
- case kExprI64Const: {
- ImmI64Operand operand(this, pc_);
- Leaf(kAstI64, BUILD(Int64Constant, operand.value));
- len = 1 + operand.length;
- break;
- }
- case kExprF32Const: {
- ImmF32Operand operand(this, pc_);
- Leaf(kAstF32, BUILD(Float32Constant, operand.value));
- len = 1 + operand.length;
- break;
- }
- case kExprF64Const: {
- ImmF64Operand operand(this, pc_);
- Leaf(kAstF64, BUILD(Float64Constant, operand.value));
- len = 1 + operand.length;
- break;
- }
- case kExprGetLocal: {
- LocalIndexOperand operand(this, pc_);
- if (Validate(pc_, operand)) {
- TFNode* val = build() ? ssa_env_->locals[operand.index] : nullptr;
- Leaf(operand.type, val);
+ case kExprBr: {
+ BreakDepthOperand operand(this, pc_);
+ Value val = {pc_, nullptr, kAstStmt};
+ if (operand.arity) val = Pop();
+ if (Validate(pc_, operand, control_)) {
+ BreakTo(operand.target, val);
+ }
+ len = 1 + operand.length;
+ Push(kAstEnd, nullptr);
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprSetLocal: {
- LocalIndexOperand operand(this, pc_);
- if (Validate(pc_, operand)) {
- Shift(operand.type, 1);
+ case kExprBrIf: {
+ BreakDepthOperand operand(this, pc_);
+ Value cond = Pop(operand.arity, kAstI32);
+ Value val = {pc_, nullptr, kAstStmt};
+ if (operand.arity == 1) val = Pop();
+ if (Validate(pc_, operand, control_)) {
+ SsaEnv* fenv = ssa_env_;
+ SsaEnv* tenv = Split(fenv);
+ fenv->SetNotMerged();
+ BUILD(Branch, cond.node, &tenv->control, &fenv->control);
+ ssa_env_ = tenv;
+ BreakTo(operand.target, val);
+ ssa_env_ = fenv;
+ }
+ len = 1 + operand.length;
+ Push(kAstStmt, nullptr);
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprLoadGlobal: {
- GlobalIndexOperand operand(this, pc_);
- if (Validate(pc_, operand)) {
- Leaf(operand.type, BUILD(LoadGlobal, operand.index));
+ case kExprBrTable: {
+ BranchTableOperand operand(this, pc_);
+ if (Validate(pc_, operand, control_.size())) {
+ Value key = Pop(operand.arity, kAstI32);
+ Value val = {pc_, nullptr, kAstStmt};
+ if (operand.arity == 1) val = Pop();
+ if (failed()) break;
+
+ SsaEnv* break_env = ssa_env_;
+ if (operand.table_count > 0) {
+ // Build branches to the various blocks based on the table.
+ TFNode* sw = BUILD(Switch, operand.table_count + 1, key.node);
+
+ SsaEnv* copy = Steal(break_env);
+ ssa_env_ = copy;
+ for (uint32_t i = 0; i < operand.table_count + 1; i++) {
+ uint16_t target = operand.read_entry(this, i);
+ ssa_env_ = Split(copy);
+ ssa_env_->control = (i == operand.table_count)
+ ? BUILD(IfDefault, sw)
+ : BUILD(IfValue, i, sw);
+ int depth = target;
+ Control* c = &control_[control_.size() - depth - 1];
+ MergeInto(c->end_env, &c->node, &c->type, val);
+ }
+ } else {
+ // Only a default target. Do the equivalent of br.
+ uint16_t target = operand.read_entry(this, 0);
+ int depth = target;
+ Control* c = &control_[control_.size() - depth - 1];
+ MergeInto(c->end_env, &c->node, &c->type, val);
+ }
+ // br_table ends the control flow like br.
+ ssa_env_ = break_env;
+ Push(kAstStmt, nullptr);
+ }
+ len = 1 + operand.length;
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprStoreGlobal: {
- GlobalIndexOperand operand(this, pc_);
- if (Validate(pc_, operand)) {
- Shift(operand.type, 1);
+ case kExprReturn: {
+ ReturnArityOperand operand(this, pc_);
+ if (operand.arity != sig_->return_count()) {
+ error(pc_, pc_ + 1, "arity mismatch in return");
+ }
+ DoReturn();
+ len = 1 + operand.length;
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprI32LoadMem8S:
- case kExprI32LoadMem8U:
- case kExprI32LoadMem16S:
- case kExprI32LoadMem16U:
- case kExprI32LoadMem:
- len = DecodeLoadMem(pc_, kAstI32);
- break;
- case kExprI64LoadMem8S:
- case kExprI64LoadMem8U:
- case kExprI64LoadMem16S:
- case kExprI64LoadMem16U:
- case kExprI64LoadMem32S:
- case kExprI64LoadMem32U:
- case kExprI64LoadMem:
- len = DecodeLoadMem(pc_, kAstI64);
- break;
- case kExprF32LoadMem:
- len = DecodeLoadMem(pc_, kAstF32);
- break;
- case kExprF64LoadMem:
- len = DecodeLoadMem(pc_, kAstF64);
- break;
- case kExprI32StoreMem8:
- case kExprI32StoreMem16:
- case kExprI32StoreMem:
- len = DecodeStoreMem(pc_, kAstI32);
- break;
- case kExprI64StoreMem8:
- case kExprI64StoreMem16:
- case kExprI64StoreMem32:
- case kExprI64StoreMem:
- len = DecodeStoreMem(pc_, kAstI64);
- break;
- case kExprF32StoreMem:
- len = DecodeStoreMem(pc_, kAstF32);
- break;
- case kExprF64StoreMem:
- len = DecodeStoreMem(pc_, kAstF64);
- break;
- case kExprMemorySize:
- Leaf(kAstI32, BUILD(MemSize, 0));
- break;
- case kExprGrowMemory:
- Shift(kAstI32, 1);
- break;
- case kExprCallFunction: {
- FunctionIndexOperand operand(this, pc_);
- if (Validate(pc_, operand)) {
- LocalType type = operand.sig->return_count() == 0
- ? kAstStmt
- : operand.sig->GetReturn();
- Shift(type, static_cast<int>(operand.sig->parameter_count()));
+ case kExprUnreachable: {
+ // TODO(clemensh): add source position for unreachable
+ Push(kAstEnd, BUILD0(Unreachable));
+ ssa_env_->Kill(SsaEnv::kControlEnd);
+ break;
}
- len = 1 + operand.length;
- break;
- }
- case kExprCallIndirect: {
- SignatureIndexOperand operand(this, pc_);
- if (Validate(pc_, operand)) {
- LocalType type = operand.sig->return_count() == 0
- ? kAstStmt
- : operand.sig->GetReturn();
- Shift(type, static_cast<int>(1 + operand.sig->parameter_count()));
+ case kExprI8Const: {
+ ImmI8Operand operand(this, pc_);
+ Push(kAstI32, BUILD(Int32Constant, operand.value));
+ len = 1 + operand.length;
+ break;
}
- len = 1 + operand.length;
- break;
+ case kExprI32Const: {
+ ImmI32Operand operand(this, pc_);
+ Push(kAstI32, BUILD(Int32Constant, operand.value));
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprI64Const: {
+ ImmI64Operand operand(this, pc_);
+ Push(kAstI64, BUILD(Int64Constant, operand.value));
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprF32Const: {
+ ImmF32Operand operand(this, pc_);
+ Push(kAstF32, BUILD(Float32Constant, operand.value));
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprF64Const: {
+ ImmF64Operand operand(this, pc_);
+ Push(kAstF64, BUILD(Float64Constant, operand.value));
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprGetLocal: {
+ LocalIndexOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ if (build()) {
+ Push(operand.type, ssa_env_->locals[operand.index]);
+ } else {
+ Push(operand.type, nullptr);
+ }
+ }
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprSetLocal: {
+ LocalIndexOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ Value val = Pop(0, local_type_vec_[operand.index]);
+ if (ssa_env_->locals) ssa_env_->locals[operand.index] = val.node;
+ Push(val.type, val.node);
+ }
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprLoadGlobal: {
+ GlobalIndexOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ Push(operand.type, BUILD(LoadGlobal, operand.index));
+ }
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprStoreGlobal: {
+ GlobalIndexOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ Value val = Pop(0, operand.type);
+ BUILD(StoreGlobal, operand.index, val.node);
+ Push(val.type, val.node);
+ }
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprI32LoadMem8S:
+ len = DecodeLoadMem(kAstI32, MachineType::Int8());
+ break;
+ case kExprI32LoadMem8U:
+ len = DecodeLoadMem(kAstI32, MachineType::Uint8());
+ break;
+ case kExprI32LoadMem16S:
+ len = DecodeLoadMem(kAstI32, MachineType::Int16());
+ break;
+ case kExprI32LoadMem16U:
+ len = DecodeLoadMem(kAstI32, MachineType::Uint16());
+ break;
+ case kExprI32LoadMem:
+ len = DecodeLoadMem(kAstI32, MachineType::Int32());
+ break;
+
+ case kExprI64LoadMem8S:
+ len = DecodeLoadMem(kAstI64, MachineType::Int8());
+ break;
+ case kExprI64LoadMem8U:
+ len = DecodeLoadMem(kAstI64, MachineType::Uint8());
+ break;
+ case kExprI64LoadMem16S:
+ len = DecodeLoadMem(kAstI64, MachineType::Int16());
+ break;
+ case kExprI64LoadMem16U:
+ len = DecodeLoadMem(kAstI64, MachineType::Uint16());
+ break;
+ case kExprI64LoadMem32S:
+ len = DecodeLoadMem(kAstI64, MachineType::Int32());
+ break;
+ case kExprI64LoadMem32U:
+ len = DecodeLoadMem(kAstI64, MachineType::Uint32());
+ break;
+ case kExprI64LoadMem:
+ len = DecodeLoadMem(kAstI64, MachineType::Int64());
+ break;
+ case kExprF32LoadMem:
+ len = DecodeLoadMem(kAstF32, MachineType::Float32());
+ break;
+ case kExprF64LoadMem:
+ len = DecodeLoadMem(kAstF64, MachineType::Float64());
+ break;
+ case kExprI32StoreMem8:
+ len = DecodeStoreMem(kAstI32, MachineType::Int8());
+ break;
+ case kExprI32StoreMem16:
+ len = DecodeStoreMem(kAstI32, MachineType::Int16());
+ break;
+ case kExprI32StoreMem:
+ len = DecodeStoreMem(kAstI32, MachineType::Int32());
+ break;
+ case kExprI64StoreMem8:
+ len = DecodeStoreMem(kAstI64, MachineType::Int8());
+ break;
+ case kExprI64StoreMem16:
+ len = DecodeStoreMem(kAstI64, MachineType::Int16());
+ break;
+ case kExprI64StoreMem32:
+ len = DecodeStoreMem(kAstI64, MachineType::Int32());
+ break;
+ case kExprI64StoreMem:
+ len = DecodeStoreMem(kAstI64, MachineType::Int64());
+ break;
+ case kExprF32StoreMem:
+ len = DecodeStoreMem(kAstF32, MachineType::Float32());
+ break;
+ case kExprF64StoreMem:
+ len = DecodeStoreMem(kAstF64, MachineType::Float64());
+ break;
+
+ case kExprMemorySize:
+ Push(kAstI32, BUILD(MemSize, 0));
+ break;
+ case kExprGrowMemory: {
+ Value val = Pop(0, kAstI32);
+ USE(val); // TODO(titzer): build node for grow memory
+ Push(kAstI32, BUILD(Int32Constant, 0));
+ break;
+ }
+ case kExprCallFunction: {
+ CallFunctionOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ TFNode** buffer = PopArgs(operand.sig);
+ TFNode* call = BUILD(CallDirect, operand.index, buffer);
+ Push(GetReturnType(operand.sig), call);
+ AddSourcePosition(call, pc_);
+ }
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprCallIndirect: {
+ CallIndirectOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ TFNode** buffer = PopArgs(operand.sig);
+ Value index = Pop(0, kAstI32);
+ if (buffer) buffer[0] = index.node;
+ TFNode* call = BUILD(CallIndirect, operand.index, buffer);
+ Push(GetReturnType(operand.sig), call);
+ AddSourcePosition(call, pc_);
+ }
+ len = 1 + operand.length;
+ break;
+ }
+ case kExprCallImport: {
+ CallImportOperand operand(this, pc_);
+ if (Validate(pc_, operand)) {
+ TFNode** buffer = PopArgs(operand.sig);
+ TFNode* call = BUILD(CallImport, operand.index, buffer);
+ Push(GetReturnType(operand.sig), call);
+ AddSourcePosition(call, pc_);
+ }
+ len = 1 + operand.length;
+ break;
+ }
+ default:
+ error("Invalid opcode");
+ return;
}
- case kExprCallImport: {
- ImportIndexOperand operand(this, pc_);
- if (Validate(pc_, operand)) {
- LocalType type = operand.sig->return_count() == 0
- ? kAstStmt
- : operand.sig->GetReturn();
- Shift(type, static_cast<int>(operand.sig->parameter_count()));
+ } // end complex bytecode
+
+#if DEBUG
+ if (FLAG_trace_wasm_decoder) {
+ for (size_t i = 0; i < stack_.size(); i++) {
+ Value& val = stack_[i];
+ WasmOpcode opcode = static_cast<WasmOpcode>(*val.pc);
+ PrintF(" %c@%d:%s", WasmOpcodes::ShortNameOf(val.type),
+ static_cast<int>(val.pc - start_),
+ WasmOpcodes::ShortOpcodeName(opcode));
+ switch (opcode) {
+ case kExprI32Const: {
+ ImmI32Operand operand(this, val.pc);
+ PrintF("[%d]", operand.value);
+ break;
+ }
+ case kExprGetLocal: {
+ LocalIndexOperand operand(this, val.pc);
+ PrintF("[%u]", operand.index);
+ break;
+ }
+ case kExprSetLocal: {
+ LocalIndexOperand operand(this, val.pc);
+ PrintF("[%u]", operand.index);
+ break;
+ }
+ default:
+ break;
}
- len = 1 + operand.length;
- break;
}
- case kExprDeclLocals:
- default:
- error("Invalid opcode");
- return;
+ PrintF("\n");
}
+#endif
pc_ += len;
if (pc_ >= limit_) {
// End of code reached or exceeded.
- if (pc_ > limit_ && ok()) {
- error("Beyond end of code");
- }
+ if (pc_ > limit_ && ok()) error("Beyond end of code");
return;
}
+ } // end decode loop
+ } // end DecodeFunctionBody()
+
+ TFNode** PopArgs(FunctionSig* sig) {
+ if (build()) {
+ int count = static_cast<int>(sig->parameter_count());
+ TFNode** buffer = builder_->Buffer(count + 1);
+ buffer[0] = nullptr; // reserved for code object or function index.
+ for (int i = count - 1; i >= 0; i--) {
+ buffer[i + 1] = Pop(i, sig->GetParam(i)).node;
+ }
+ return buffer;
+ } else {
+ int count = static_cast<int>(sig->parameter_count());
+ for (int i = count - 1; i >= 0; i--) {
+ Pop(i, sig->GetParam(i));
+ }
+ return nullptr;
}
}
- void PushBlock(SsaEnv* ssa_env) {
- blocks_.push_back({ssa_env, static_cast<int>(stack_.size() - 1)});
+ LocalType GetReturnType(FunctionSig* sig) {
+ return sig->return_count() == 0 ? kAstStmt : sig->GetReturn();
}
- int DecodeLoadMem(const byte* pc, LocalType type) {
- MemoryAccessOperand operand(this, pc);
- Shift(type, 1);
- return 1 + operand.length;
+ void PushBlock(SsaEnv* end_env) {
+ int stack_depth = static_cast<int>(stack_.size());
+ control_.push_back(
+ {pc_, stack_depth, end_env, nullptr, nullptr, kAstEnd, false});
}
- int DecodeStoreMem(const byte* pc, LocalType type) {
- MemoryAccessOperand operand(this, pc);
- Shift(type, 2);
+ void PushLoop(SsaEnv* end_env) {
+ int stack_depth = static_cast<int>(stack_.size());
+ control_.push_back(
+ {pc_, stack_depth, end_env, nullptr, nullptr, kAstEnd, true});
+ }
+
+ void PushIf(SsaEnv* end_env, SsaEnv* false_env) {
+ int stack_depth = static_cast<int>(stack_.size());
+ control_.push_back(
+ {pc_, stack_depth, end_env, false_env, nullptr, kAstStmt, false});
+ }
+
+ int DecodeLoadMem(LocalType type, MachineType mem_type) {
+ MemoryAccessOperand operand(this, pc_);
+ Value index = Pop(0, kAstI32);
+ TFNode* node = BUILD(LoadMem, type, mem_type, index.node, operand.offset);
+ Push(type, node);
return 1 + operand.length;
}
- void AddImplicitReturnAtEnd() {
- int retcount = static_cast<int>(sig_->return_count());
- if (retcount == 0) {
- BUILD0(ReturnVoid);
- return;
- }
+ int DecodeStoreMem(LocalType type, MachineType mem_type) {
+ MemoryAccessOperand operand(this, pc_);
+ Value val = Pop(1, type);
+ Value index = Pop(0, kAstI32);
+ BUILD(StoreMem, mem_type, index.node, operand.offset, val.node);
+ Push(type, val.node);
+ return 1 + operand.length;
+ }
- if (static_cast<int>(trees_.size()) < retcount) {
- error(limit_, nullptr,
- "ImplicitReturn expects %d arguments, only %d remain", retcount,
- static_cast<int>(trees_.size()));
- return;
- }
+ void DoReturn() {
+ int count = static_cast<int>(sig_->return_count());
+ TFNode** buffer = nullptr;
+ if (build()) buffer = builder_->Buffer(count);
- TRACE("wasm-decode implicit return of %d args\n", retcount);
-
- TFNode** buffer = BUILD(Buffer, retcount);
- for (int index = 0; index < retcount; index++) {
- Tree* tree = trees_[trees_.size() - 1 - index];
- if (buffer) buffer[index] = tree->node;
- LocalType expected = sig_->GetReturn(index);
- if (tree->type != expected) {
- error(limit_, tree->pc,
- "ImplicitReturn[%d] expected type %s, found %s of type %s", index,
- WasmOpcodes::TypeName(expected),
- WasmOpcodes::OpcodeName(tree->opcode()),
- WasmOpcodes::TypeName(tree->type));
- return;
- }
+ // Pop return values off the stack in reverse order.
+ for (int i = count - 1; i >= 0; i--) {
+ Value val = Pop(i, sig_->GetReturn(i));
+ if (buffer) buffer[i] = val.node;
}
- BUILD(Return, retcount, buffer);
+ Push(kAstEnd, BUILD(Return, count, buffer));
+ ssa_env_->Kill(SsaEnv::kControlEnd);
}
- int baserel(const byte* ptr) {
- return base_ ? static_cast<int>(ptr - base_) : 0;
+ void Push(LocalType type, TFNode* node) {
+ stack_.push_back({pc_, node, type});
}
- int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); }
+ const char* SafeOpcodeNameAt(const byte* pc) {
+ if (pc >= end_) return "<end>";
+ return WasmOpcodes::ShortOpcodeName(static_cast<WasmOpcode>(*pc));
+ }
- void Reduce(Production* p) {
- WasmOpcode opcode = p->opcode();
- TRACE("-----reduce module+%-6d %s func+%d: 0x%02x %s\n", baserel(p->pc()),
- indentation(), startrel(p->pc()), opcode,
- WasmOpcodes::OpcodeName(opcode));
- FunctionSig* sig = WasmOpcodes::Signature(opcode);
- if (sig) {
- // A simple expression with a fixed signature.
- TypeCheckLast(p, sig->GetParam(p->index - 1));
- if (p->done() && build()) {
- if (sig->parameter_count() == 2) {
- p->tree->node = builder_->Binop(opcode, p->tree->children[0]->node,
- p->tree->children[1]->node);
- } else if (sig->parameter_count() == 1) {
- p->tree->node = builder_->Unop(opcode, p->tree->children[0]->node);
- } else {
- UNREACHABLE();
- }
+ Value Pop(int index, LocalType expected) {
+ Value val = Pop();
+ if (val.type != expected) {
+ if (val.type != kAstEnd) {
+ error(pc_, val.pc, "%s[%d] expected type %s, found %s of type %s",
+ SafeOpcodeNameAt(pc_), index, WasmOpcodes::TypeName(expected),
+ SafeOpcodeNameAt(val.pc), WasmOpcodes::TypeName(val.type));
}
- return;
}
+ return val;
+ }
- switch (opcode) {
- case kExprBlock: {
- if (p->done()) {
- Block* last = &blocks_.back();
- DCHECK_EQ(stack_.size() - 1, last->stack_depth);
- // fallthrough with the last expression.
- ReduceBreakToExprBlock(p, last);
- SetEnv("block:end", last->ssa_env);
- blocks_.pop_back();
- }
- break;
- }
- case kExprLoop: {
- if (p->done()) {
- // Pop the continue environment.
- blocks_.pop_back();
- // Get the break environment.
- Block* last = &blocks_.back();
- DCHECK_EQ(stack_.size() - 1, last->stack_depth);
- // fallthrough with the last expression.
- ReduceBreakToExprBlock(p, last);
- SetEnv("loop:end", last->ssa_env);
- blocks_.pop_back();
- }
- break;
- }
- case kExprIf: {
- if (p->index == 1) {
- // Condition done. Split environment for true branch.
- TypeCheckLast(p, kAstI32);
- SsaEnv* false_env = ssa_env_;
- SsaEnv* true_env = Split(ssa_env_);
- ifs_.push_back({nullptr, false_env, nullptr});
- BUILD(Branch, p->last()->node, &true_env->control,
- &false_env->control);
- SetEnv("if:true", true_env);
- } else if (p->index == 2) {
- // True block done. Merge true and false environments.
- IfEnv* env = &ifs_.back();
- SsaEnv* merge = env->merge_env;
- if (merge->go()) {
- merge->state = SsaEnv::kReached;
- Goto(ssa_env_, merge);
- }
- SetEnv("if:merge", merge);
- ifs_.pop_back();
- }
- break;
- }
- case kExprIfElse: {
- if (p->index == 1) {
- // Condition done. Split environment for true and false branches.
- TypeCheckLast(p, kAstI32);
- SsaEnv* merge_env = ssa_env_;
- TFNode* if_true = nullptr;
- TFNode* if_false = nullptr;
- BUILD(Branch, p->last()->node, &if_true, &if_false);
- SsaEnv* false_env = Split(ssa_env_);
- SsaEnv* true_env = Steal(ssa_env_);
- false_env->control = if_false;
- true_env->control = if_true;
- ifs_.push_back({false_env, merge_env, nullptr});
- SetEnv("if_else:true", true_env);
- } else if (p->index == 2) {
- // True expr done.
- IfEnv* env = &ifs_.back();
- MergeIntoProduction(p, env->merge_env, p->last());
- // Switch to environment for false branch.
- SsaEnv* false_env = ifs_.back().false_env;
- SetEnv("if_else:false", false_env);
- } else if (p->index == 3) {
- // False expr done.
- IfEnv* env = &ifs_.back();
- MergeIntoProduction(p, env->merge_env, p->last());
- SetEnv("if_else:merge", env->merge_env);
- ifs_.pop_back();
- }
- break;
- }
- case kExprSelect: {
- if (p->index == 1) {
- // True expression done.
- p->tree->type = p->last()->type;
- if (p->tree->type == kAstStmt) {
- error(p->pc(), p->tree->children[1]->pc,
- "select operand should be expression");
- }
- } else if (p->index == 2) {
- // False expression done.
- TypeCheckLast(p, p->tree->type);
- } else {
- // Condition done.
- DCHECK(p->done());
- TypeCheckLast(p, kAstI32);
- if (build()) {
- TFNode* controls[2];
- builder_->Branch(p->tree->children[2]->node, &controls[0],
- &controls[1]);
- TFNode* merge = builder_->Merge(2, controls);
- TFNode* vals[2] = {p->tree->children[0]->node,
- p->tree->children[1]->node};
- TFNode* phi = builder_->Phi(p->tree->type, 2, vals, merge);
- p->tree->node = phi;
- ssa_env_->control = merge;
- }
- }
- break;
- }
- case kExprBr: {
- BreakDepthOperand operand(this, p->pc());
- CHECK(Validate(p->pc(), operand, blocks_));
- ReduceBreakToExprBlock(p, operand.target);
- break;
- }
- case kExprBrIf: {
- if (p->done()) {
- TypeCheckLast(p, kAstI32);
- BreakDepthOperand operand(this, p->pc());
- CHECK(Validate(p->pc(), operand, blocks_));
- SsaEnv* fenv = ssa_env_;
- SsaEnv* tenv = Split(fenv);
- BUILD(Branch, p->tree->children[1]->node, &tenv->control,
- &fenv->control);
- ssa_env_ = tenv;
- ReduceBreakToExprBlock(p, operand.target, p->tree->children[0]);
- ssa_env_ = fenv;
- }
- break;
- }
- case kExprBrTable: {
- if (p->index == 1) {
- // Switch key finished.
- TypeCheckLast(p, kAstI32);
- if (failed()) break;
-
- BranchTableOperand operand(this, p->pc());
- DCHECK(Validate(p->pc(), operand, blocks_.size()));
-
- // Build a switch only if it has more than just a default target.
- bool build_switch = operand.table_count > 0;
- TFNode* sw = nullptr;
- if (build_switch) {
- sw = BUILD(Switch, operand.table_count + 1, p->last()->node);
- }
-
- // Process the targets of the break table.
- SsaEnv* prev = ssa_env_;
- SsaEnv* copy = Steal(prev);
- for (uint32_t i = 0; i < operand.table_count + 1; i++) {
- uint32_t target = operand.read_entry(this, i);
- SsaEnv* env = copy;
- if (build_switch) {
- ssa_env_ = env = Split(env);
- env->control = i == operand.table_count ? BUILD(IfDefault, sw)
- : BUILD(IfValue, i, sw);
- }
- SsaEnv* tenv = blocks_[blocks_.size() - target - 1].ssa_env;
- Goto(env, tenv);
- }
- ssa_env_ = prev;
- }
- break;
- }
- case kExprReturn: {
- TypeCheckLast(p, sig_->GetReturn(p->index - 1));
- if (p->done()) {
- if (build()) {
- int count = p->tree->count;
- TFNode** buffer = builder_->Buffer(count);
- for (int i = 0; i < count; i++) {
- buffer[i] = p->tree->children[i]->node;
- }
- BUILD(Return, count, buffer);
- }
- ssa_env_->Kill(SsaEnv::kControlEnd);
- }
- break;
- }
- case kExprSetLocal: {
- LocalIndexOperand operand(this, p->pc());
- CHECK(Validate(p->pc(), operand));
- Tree* val = p->last();
- if (operand.type == val->type) {
- if (build()) ssa_env_->locals[operand.index] = val->node;
- p->tree->node = val->node;
- } else {
- error(p->pc(), val->pc, "Typecheck failed in SetLocal");
- }
- break;
- }
- case kExprStoreGlobal: {
- GlobalIndexOperand operand(this, p->pc());
- CHECK(Validate(p->pc(), operand));
- Tree* val = p->last();
- if (operand.type == val->type) {
- BUILD(StoreGlobal, operand.index, val->node);
- p->tree->node = val->node;
- } else {
- error(p->pc(), val->pc, "Typecheck failed in StoreGlobal");
- }
- break;
- }
-
- case kExprI32LoadMem8S:
- return ReduceLoadMem(p, kAstI32, MachineType::Int8());
- case kExprI32LoadMem8U:
- return ReduceLoadMem(p, kAstI32, MachineType::Uint8());
- case kExprI32LoadMem16S:
- return ReduceLoadMem(p, kAstI32, MachineType::Int16());
- case kExprI32LoadMem16U:
- return ReduceLoadMem(p, kAstI32, MachineType::Uint16());
- case kExprI32LoadMem:
- return ReduceLoadMem(p, kAstI32, MachineType::Int32());
-
- case kExprI64LoadMem8S:
- return ReduceLoadMem(p, kAstI64, MachineType::Int8());
- case kExprI64LoadMem8U:
- return ReduceLoadMem(p, kAstI64, MachineType::Uint8());
- case kExprI64LoadMem16S:
- return ReduceLoadMem(p, kAstI64, MachineType::Int16());
- case kExprI64LoadMem16U:
- return ReduceLoadMem(p, kAstI64, MachineType::Uint16());
- case kExprI64LoadMem32S:
- return ReduceLoadMem(p, kAstI64, MachineType::Int32());
- case kExprI64LoadMem32U:
- return ReduceLoadMem(p, kAstI64, MachineType::Uint32());
- case kExprI64LoadMem:
- return ReduceLoadMem(p, kAstI64, MachineType::Int64());
-
- case kExprF32LoadMem:
- return ReduceLoadMem(p, kAstF32, MachineType::Float32());
-
- case kExprF64LoadMem:
- return ReduceLoadMem(p, kAstF64, MachineType::Float64());
-
- case kExprI32StoreMem8:
- return ReduceStoreMem(p, kAstI32, MachineType::Int8());
- case kExprI32StoreMem16:
- return ReduceStoreMem(p, kAstI32, MachineType::Int16());
- case kExprI32StoreMem:
- return ReduceStoreMem(p, kAstI32, MachineType::Int32());
-
- case kExprI64StoreMem8:
- return ReduceStoreMem(p, kAstI64, MachineType::Int8());
- case kExprI64StoreMem16:
- return ReduceStoreMem(p, kAstI64, MachineType::Int16());
- case kExprI64StoreMem32:
- return ReduceStoreMem(p, kAstI64, MachineType::Int32());
- case kExprI64StoreMem:
- return ReduceStoreMem(p, kAstI64, MachineType::Int64());
-
- case kExprF32StoreMem:
- return ReduceStoreMem(p, kAstF32, MachineType::Float32());
-
- case kExprF64StoreMem:
- return ReduceStoreMem(p, kAstF64, MachineType::Float64());
-
- case kExprGrowMemory:
- TypeCheckLast(p, kAstI32);
- // TODO(titzer): build node for GrowMemory
- p->tree->node = BUILD(Int32Constant, 0);
- return;
+ Value Pop() {
+ if (stack_.empty()) {
+ Value val = {pc_, nullptr, kAstStmt};
+ error(pc_, pc_, "%s found empty stack", SafeOpcodeNameAt(pc_));
+ return val;
+ }
+ Value val = stack_.back();
+ stack_.pop_back();
+ return val;
+ }
- case kExprCallFunction: {
- FunctionIndexOperand operand(this, p->pc());
- CHECK(Validate(p->pc(), operand));
- if (p->index > 0) {
- TypeCheckLast(p, operand.sig->GetParam(p->index - 1));
- }
- if (p->done() && build()) {
- uint32_t count = p->tree->count + 1;
- TFNode** buffer = builder_->Buffer(count);
- buffer[0] = nullptr; // reserved for code object.
- for (uint32_t i = 1; i < count; i++) {
- buffer[i] = p->tree->children[i - 1]->node;
- }
- p->tree->node = builder_->CallDirect(operand.index, buffer);
- AddSourcePosition(p);
- }
- break;
- }
- case kExprCallIndirect: {
- SignatureIndexOperand operand(this, p->pc());
- CHECK(Validate(p->pc(), operand));
- if (p->index == 1) {
- TypeCheckLast(p, kAstI32);
- } else {
- TypeCheckLast(p, operand.sig->GetParam(p->index - 2));
- }
- if (p->done() && build()) {
- uint32_t count = p->tree->count;
- TFNode** buffer = builder_->Buffer(count);
- for (uint32_t i = 0; i < count; i++) {
- buffer[i] = p->tree->children[i]->node;
- }
- p->tree->node = builder_->CallIndirect(operand.index, buffer);
- AddSourcePosition(p);
- }
- break;
- }
- case kExprCallImport: {
- ImportIndexOperand operand(this, p->pc());
- CHECK(Validate(p->pc(), operand));
- if (p->index > 0) {
- TypeCheckLast(p, operand.sig->GetParam(p->index - 1));
- }
- if (p->done() && build()) {
- uint32_t count = p->tree->count + 1;
- TFNode** buffer = builder_->Buffer(count);
- buffer[0] = nullptr; // reserved for code object.
- for (uint32_t i = 1; i < count; i++) {
- buffer[i] = p->tree->children[i - 1]->node;
- }
- p->tree->node = builder_->CallImport(operand.index, buffer);
- AddSourcePosition(p);
- }
- break;
- }
- default:
- break;
+ Value PopUpTo(int stack_depth) {
+ if (stack_depth == stack_.size()) {
+ Value val = {pc_, nullptr, kAstStmt};
+ return val;
+ } else {
+ DCHECK_LE(stack_depth, static_cast<int>(stack_.size()));
+ Value val = Pop();
+ stack_.resize(stack_depth);
+ return val;
}
}
- void ReduceBreakToExprBlock(Production* p, Block* block) {
- ReduceBreakToExprBlock(p, block, p->tree->count > 0 ? p->last() : nullptr);
+ int baserel(const byte* ptr) {
+ return base_ ? static_cast<int>(ptr - base_) : 0;
}
- void ReduceBreakToExprBlock(Production* p, Block* block, Tree* val) {
- if (block->stack_depth < 0) {
+ int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); }
+
+ void BreakTo(Control* block, Value& val) {
+ if (block->is_loop) {
// This is the inner loop block, which does not have a value.
- Goto(ssa_env_, block->ssa_env);
+ Goto(ssa_env_, block->end_env);
} else {
// Merge the value into the production for the block.
- Production* bp = &stack_[block->stack_depth];
- MergeIntoProduction(bp, block->ssa_env, val);
+ MergeInto(block->end_env, &block->node, &block->type, val);
}
}
- void MergeIntoProduction(Production* p, SsaEnv* target, Tree* expr) {
+ void MergeInto(SsaEnv* target, TFNode** node, LocalType* type, Value& val) {
if (!ssa_env_->go()) return;
+ DCHECK_NE(kAstEnd, val.type);
bool first = target->state == SsaEnv::kUnreachable;
Goto(ssa_env_, target);
- if (expr == nullptr || expr->type == kAstEnd) return;
if (first) {
// first merge to this environment; set the type and the node.
- p->tree->type = expr->type;
- p->tree->node = expr->node;
- } else {
+ *type = val.type;
+ *node = val.node;
+ } else if (val.type == *type && val.type != kAstStmt) {
// merge with the existing value for this block.
- LocalType type = p->tree->type;
- if (expr->type != type) {
- type = kAstStmt;
- p->tree->type = kAstStmt;
- p->tree->node = nullptr;
- } else if (type != kAstStmt) {
- p->tree->node = CreateOrMergeIntoPhi(type, target->control,
- p->tree->node, expr->node);
- }
- }
- }
-
- void ReduceLoadMem(Production* p, LocalType type, MachineType mem_type) {
- DCHECK_EQ(1, p->index);
- TypeCheckLast(p, kAstI32); // index
- if (build()) {
- MemoryAccessOperand operand(this, p->pc());
- p->tree->node =
- builder_->LoadMem(type, mem_type, p->last()->node, operand.offset);
- }
- }
-
- void ReduceStoreMem(Production* p, LocalType type, MachineType mem_type) {
- if (p->index == 1) {
- TypeCheckLast(p, kAstI32); // index
+ *node = CreateOrMergeIntoPhi(*type, target->control, *node, val.node);
} else {
- DCHECK_EQ(2, p->index);
- TypeCheckLast(p, type);
- if (build()) {
- MemoryAccessOperand operand(this, p->pc());
- TFNode* val = p->tree->children[1]->node;
- builder_->StoreMem(mem_type, p->tree->children[0]->node, operand.offset,
- val);
- p->tree->node = val;
- }
- }
- }
-
- void TypeCheckLast(Production* p, LocalType expected) {
- LocalType result = p->last()->type;
- if (result == expected) return;
- if (result == kAstEnd) return;
- if (expected != kAstStmt) {
- error(p->pc(), p->last()->pc,
- "%s[%d] expected type %s, found %s of type %s",
- WasmOpcodes::OpcodeName(p->opcode()), p->index - 1,
- WasmOpcodes::TypeName(expected),
- WasmOpcodes::OpcodeName(p->last()->opcode()),
- WasmOpcodes::TypeName(p->last()->type));
+ // types don't match, or block is already a stmt.
+ *type = kAstStmt;
+ *node = nullptr;
}
}
void SetEnv(const char* reason, SsaEnv* env) {
#if DEBUG
- TRACE(" env = %p, block depth = %d, reason = %s", static_cast<void*>(env),
- static_cast<int>(blocks_.size()), reason);
- if (FLAG_trace_wasm_decoder && env && env->control) {
- TRACE(", control = ");
- compiler::WasmGraphBuilder::PrintDebugName(env->control);
+ if (FLAG_trace_wasm_decoder) {
+ char state = 'X';
+ if (env) {
+ switch (env->state) {
+ case SsaEnv::kReached:
+ state = 'R';
+ break;
+ case SsaEnv::kUnreachable:
+ state = 'U';
+ break;
+ case SsaEnv::kMerged:
+ state = 'M';
+ break;
+ case SsaEnv::kControlEnd:
+ state = 'E';
+ break;
+ }
+ }
+ PrintF(" env = %p, state = %c, reason = %s", static_cast<void*>(env),
+ state, reason);
+ if (env && env->control) {
+ PrintF(", control = ");
+ compiler::WasmGraphBuilder::PrintDebugName(env->control);
+ }
+ PrintF("\n");
}
- TRACE("\n");
#endif
ssa_env_ = env;
if (builder_) {
@@ -1504,8 +1379,6 @@ class SR_WasmDecoder : public WasmDecoder {
size_t size = sizeof(TFNode*) * EnvironmentCount();
result->control = from->control;
result->effect = from->effect;
- result->state = from->state == SsaEnv::kUnreachable ? SsaEnv::kUnreachable
- : SsaEnv::kReached;
if (from->go()) {
result->state = SsaEnv::kReached;
@@ -1552,99 +1425,55 @@ class SR_WasmDecoder : public WasmDecoder {
virtual void onFirstError() {
limit_ = start_; // Terminate decoding loop.
builder_ = nullptr; // Don't build any more nodes.
-#if DEBUG
- PrintStackForDebugging();
-#endif
+ TRACE(" !%s\n", error_msg_.get());
}
-
-#if DEBUG
- void PrintStackForDebugging() { PrintProduction(0); }
-
- void PrintProduction(size_t depth) {
- if (depth >= stack_.size()) return;
- Production* p = &stack_[depth];
- for (size_t d = 0; d < depth; d++) PrintF(" ");
-
- PrintF("@%d %s [%d]\n", static_cast<int>(p->tree->pc - start_),
- WasmOpcodes::OpcodeName(p->opcode()), p->tree->count);
- for (int i = 0; i < p->index; i++) {
- Tree* child = p->tree->children[i];
- for (size_t d = 0; d <= depth; d++) PrintF(" ");
- PrintF("@%d %s [%d]", static_cast<int>(child->pc - start_),
- WasmOpcodes::OpcodeName(child->opcode()), child->count);
- if (child->node) {
- PrintF(" => TF");
- compiler::WasmGraphBuilder::PrintDebugName(child->node);
- }
- PrintF("\n");
- }
- PrintProduction(depth + 1);
- }
-#endif
-
BitVector* AnalyzeLoopAssignment(const byte* pc) {
if (pc >= limit_) return nullptr;
if (*pc != kExprLoop) return nullptr;
BitVector* assigned =
- new (zone_) BitVector(static_cast<int>(total_locals_), zone_);
- // Keep a stack to model the nesting of expressions.
- std::vector<int> arity_stack;
- arity_stack.push_back(OpcodeArity(pc));
- pc += OpcodeLength(pc);
-
+ new (zone_) BitVector(static_cast<int>(local_type_vec_.size()), zone_);
+ int depth = 0;
// Iteratively process all AST nodes nested inside the loop.
while (pc < limit_) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
- int arity = 0;
int length = 1;
- int assigned_index = -1;
- if (opcode == kExprSetLocal) {
- LocalIndexOperand operand(this, pc);
- if (assigned->length() > 0 &&
- static_cast<int>(operand.index) < assigned->length()) {
- // Unverified code might have an out-of-bounds index.
- // Ignore out-of-bounds indices, as the main verification will fail.
- assigned->Add(operand.index);
- assigned_index = operand.index;
+ switch (opcode) {
+ case kExprLoop:
+ case kExprIf:
+ case kExprBlock:
+ depth++;
+ DCHECK_EQ(1, OpcodeLength(pc));
+ break;
+ case kExprSetLocal: {
+ LocalIndexOperand operand(this, pc);
+ if (assigned->length() > 0 &&
+ static_cast<int>(operand.index) < assigned->length()) {
+ // Unverified code might have an out-of-bounds index.
+ assigned->Add(operand.index);
+ }
+ length = 1 + operand.length;
+ break;
}
- arity = 1;
- length = 1 + operand.length;
- } else {
- arity = OpcodeArity(pc);
- length = OpcodeLength(pc);
- }
-
- TRACE("loop-assign module+%-6d %s func+%d: 0x%02x %s", baserel(pc),
- indentation(), startrel(pc), opcode,
- WasmOpcodes::OpcodeName(opcode));
-
- if (assigned_index >= 0) {
- TRACE(" (assigned local #%d)\n", assigned_index);
- } else {
- TRACE("\n");
+ case kExprEnd:
+ depth--;
+ break;
+ default:
+ length = OpcodeLength(pc);
+ break;
}
-
+ if (depth <= 0) break;
pc += length;
- arity_stack.push_back(arity);
- while (arity_stack.back() == 0) {
- arity_stack.pop_back();
- if (arity_stack.empty()) return assigned; // reached end of loop
- arity_stack.back()--;
- }
}
return assigned;
}
- void AddSourcePosition(Production* p) {
- DCHECK_NOT_NULL(p->tree->node);
- AddSourcePosition(p->tree->node, p->pc());
- }
-
void AddSourcePosition(TFNode* node, const byte* pc) {
- int offset = static_cast<int>(pc - start_);
- DCHECK_EQ(pc - start_, offset); // overflows cannot happen
- builder_->SetSourcePosition(node, offset);
+ if (node) {
+ int offset = static_cast<int>(pc - start_);
+ DCHECK_EQ(pc - start_, offset); // overflows cannot happen
+ builder_->SetSourcePosition(node, offset);
+ }
}
};
@@ -1661,16 +1490,16 @@ TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
FunctionBody& body) {
Zone zone(allocator);
SR_WasmDecoder decoder(&zone, nullptr, body);
- TreeResult result = decoder.Decode();
- return result;
+ decoder.Decode();
+ return decoder.toResult<Tree*>(nullptr);
}
TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
TFBuilder* builder, FunctionBody& body) {
Zone zone(allocator);
SR_WasmDecoder decoder(&zone, builder, body);
- TreeResult result = decoder.Decode();
- return result;
+ decoder.Decode();
+ return decoder.toResult<Tree*>(nullptr);
}
@@ -1751,21 +1580,21 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
if (body.module) {
switch (opcode) {
case kExprCallIndirect: {
- SignatureIndexOperand operand(&decoder, pc);
+ CallIndirectOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) {
os << " // sig #" << operand.index << ": " << *operand.sig;
}
break;
}
case kExprCallImport: {
- ImportIndexOperand operand(&decoder, pc);
+ CallImportOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) {
os << " // import #" << operand.index << ": " << *operand.sig;
}
break;
}
case kExprCallFunction: {
- FunctionIndexOperand operand(&decoder, pc);
+ CallFunctionOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) {
os << " // function #" << operand.index << ": " << *operand.sig;
}
« no previous file with comments | « src/wasm/ast-decoder.h ('k') | src/wasm/encoder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698