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

Unified Diff: src/wasm/function-body-decoder.cc

Issue 2707203003: Merged: Squashed multiple commits. (Closed)
Patch Set: TypeCheckFallThru() on end. Created 3 years, 10 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/asmjs/asm-wasm-builder.cc ('k') | src/wasm/wasm-opcodes.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/wasm/function-body-decoder.cc
diff --git a/src/wasm/function-body-decoder.cc b/src/wasm/function-body-decoder.cc
index d67a1e3cc9335f0eb73de149b79e48fc9b68b220..04a28062373adc52baebd65c23eec949f71aed2c 100644
--- a/src/wasm/function-body-decoder.cc
+++ b/src/wasm/function-body-decoder.cc
@@ -85,9 +85,9 @@ struct MergeValues {
Value first;
} vals; // Either multiple values or a single value.
- Value& first() {
- DCHECK_GT(arity, 0);
- return arity == 1 ? vals.first : vals.array[0];
+ Value& operator[](size_t i) {
+ DCHECK_GT(arity, i);
+ return arity == 1 ? vals.first : vals.array[i];
}
};
@@ -99,11 +99,12 @@ enum ControlKind { kControlIf, kControlBlock, kControlLoop, kControlTry };
struct Control {
const byte* pc;
ControlKind kind;
- int stack_depth; // stack height at the beginning of the construct.
+ size_t 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).
TryInfo* try_info; // Information used for compiling try statements.
int32_t previous_catch; // The previous Control (on the stack) with a catch.
+ bool unreachable; // The current block has been ended.
// Values merged into the end of this control construct.
MergeValues merge;
@@ -114,30 +115,30 @@ struct Control {
inline bool is_try() const { return kind == kControlTry; }
// Named constructors.
- static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env,
+ static Control Block(const byte* pc, size_t stack_depth, SsaEnv* end_env,
int32_t previous_catch) {
- return {pc, kControlBlock, stack_depth, end_env,
- nullptr, nullptr, previous_catch, {0, {NO_VALUE}}};
+ return {pc, kControlBlock, stack_depth, end_env, nullptr,
+ nullptr, previous_catch, false, {0, {NO_VALUE}}};
}
- static Control If(const byte* pc, int stack_depth, SsaEnv* end_env,
+ static Control If(const byte* pc, size_t stack_depth, SsaEnv* end_env,
SsaEnv* false_env, int32_t previous_catch) {
- return {pc, kControlIf, stack_depth, end_env,
- false_env, nullptr, previous_catch, {0, {NO_VALUE}}};
+ return {pc, kControlIf, stack_depth, end_env, false_env,
+ nullptr, previous_catch, false, {0, {NO_VALUE}}};
}
- static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env,
+ static Control Loop(const byte* pc, size_t stack_depth, SsaEnv* end_env,
int32_t previous_catch) {
- return {pc, kControlLoop, stack_depth, end_env,
- nullptr, nullptr, previous_catch, {0, {NO_VALUE}}};
+ return {pc, kControlLoop, stack_depth, end_env, nullptr,
+ nullptr, previous_catch, false, {0, {NO_VALUE}}};
}
- static Control Try(const byte* pc, int stack_depth, SsaEnv* end_env,
+ static Control Try(const byte* pc, size_t stack_depth, SsaEnv* end_env,
Zone* zone, SsaEnv* catch_env, int32_t previous_catch) {
DCHECK_NOT_NULL(catch_env);
TryInfo* try_info = new (zone) TryInfo(catch_env);
- return {pc, kControlTry, stack_depth, end_env,
- nullptr, try_info, previous_catch, {0, {NO_VALUE}}};
+ return {pc, kControlTry, stack_depth, end_env, nullptr,
+ try_info, previous_catch, false, {0, {NO_VALUE}}};
}
};
@@ -654,9 +655,7 @@ class WasmFullDecoder : public WasmDecoder {
}
}
- if (pc_ >= end_) return; // Nothing to do.
-
- while (true) { // decoding loop.
+ while (pc_ < end_) { // decoding loop.
unsigned len = 1;
WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
@@ -686,6 +685,10 @@ class WasmFullDecoder : public WasmDecoder {
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
Value value = Pop(0, kWasmI32);
BUILD(Throw, value.node);
+ // TODO(titzer): Throw should end control, but currently we build a
+ // (reachable) runtime call instead of connecting it directly to
+ // end.
+ // EndControl();
break;
}
case kExprTry: {
@@ -721,9 +724,7 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
- if (ssa_env_->go()) {
- MergeValuesInto(c);
- }
+ FallThruTo(c);
stack_.resize(c->stack_depth);
DCHECK_NOT_NULL(c->try_info);
@@ -787,8 +788,8 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
FallThruTo(c);
- // Switch to environment for false branch.
stack_.resize(c->stack_depth);
+ // 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;
@@ -802,7 +803,8 @@ class WasmFullDecoder : public WasmDecoder {
Control* c = &control_.back();
if (c->is_loop()) {
// A loop just leaves the values on the stack.
- TypeCheckLoopFallThru(c);
+ TypeCheckFallThru(c);
+ if (c->unreachable) PushEndValues(c);
PopControl();
SetEnv("loop:end", ssa_env_);
break;
@@ -811,8 +813,7 @@ class WasmFullDecoder : public WasmDecoder {
if (c->false_env != nullptr) {
// End the true branch of a one-armed if.
Goto(c->false_env, c->end_env);
- if (ssa_env_->go() &&
- static_cast<int>(stack_.size()) != c->stack_depth) {
+ if (!c->unreachable && stack_.size() != c->stack_depth) {
error("end of if expected empty stack");
stack_.resize(c->stack_depth);
}
@@ -835,23 +836,13 @@ class WasmFullDecoder : public WasmDecoder {
}
FallThruTo(c);
SetEnv(name, c->end_env);
+ PushEndValues(c);
- // Push the end values onto the stack.
- stack_.resize(c->stack_depth);
- if (c->merge.arity == 1) {
- stack_.push_back(c->merge.vals.first);
- } else {
- for (unsigned i = 0; i < c->merge.arity; i++) {
- stack_.push_back(c->merge.vals.array[i]);
- }
- }
-
- PopControl();
-
- if (control_.empty()) {
- // If the last (implicit) control was popped, check we are at end.
+ if (control_.size() == 1) {
+ // If at the last (implicit) control, check we are at end.
if (pc_ + 1 != end_) {
error(pc_, pc_ + 1, "trailing code after function end");
+ break;
}
last_end_found_ = true;
if (ssa_env_->go()) {
@@ -859,25 +850,18 @@ class WasmFullDecoder : public WasmDecoder {
TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn");
DoReturn();
TRACE("\n");
+ } else {
+ TypeCheckFallThru(c);
}
- return;
}
+ PopControl();
break;
}
case kExprSelect: {
Value cond = Pop(2, kWasmI32);
Value fval = Pop();
- Value tval = Pop();
- if (tval.type == kWasmStmt || tval.type != fval.type) {
- if (tval.type != kWasmEnd && fval.type != kWasmEnd) {
- error("type mismatch in select");
- break;
- }
- }
+ Value tval = Pop(0, fval.type);
if (build()) {
- DCHECK(tval.type != kWasmEnd);
- DCHECK(fval.type != kWasmEnd);
- DCHECK(cond.type != kWasmEnd);
TFNode* controls[2];
builder_->BranchNoHint(cond.node, &controls[0], &controls[1]);
TFNode* merge = builder_->Merge(2, controls);
@@ -886,7 +870,7 @@ class WasmFullDecoder : public WasmDecoder {
Push(tval.type, phi);
ssa_env_->control = merge;
} else {
- Push(tval.type, nullptr);
+ Push(tval.type == kWasmVar ? fval.type : tval.type, nullptr);
}
break;
}
@@ -928,6 +912,7 @@ class WasmFullDecoder : public WasmDecoder {
SsaEnv* copy = Steal(break_env);
ssa_env_ = copy;
+ MergeValues* merge = nullptr;
while (ok() && iterator.has_next()) {
uint32_t i = iterator.cur_index();
const byte* pos = iterator.pc();
@@ -941,6 +926,27 @@ class WasmFullDecoder : public WasmDecoder {
? BUILD(IfDefault, sw)
: BUILD(IfValue, i, sw);
BreakTo(target);
+
+ // Check that label types match up.
+ Control* c = &control_[control_.size() - target - 1];
+ if (i == 0) {
+ merge = &c->merge;
+ } else if (merge->arity != c->merge.arity) {
+ error(pos, pos,
+ "inconsistent arity in br_table target %d"
+ " (previous was %u, this one %u)",
+ i, merge->arity, c->merge.arity);
+ } else if (control_.back().unreachable) {
+ for (uint32_t j = 0; ok() && j < merge->arity; ++j) {
+ if ((*merge)[j].type != c->merge[j].type) {
+ error(pos, pos,
+ "type error in br_table target %d operand %d"
+ " (previous expected %s, this one %s)",
+ i, j, WasmOpcodes::TypeName((*merge)[j].type),
+ WasmOpcodes::TypeName(c->merge[j].type));
+ }
+ }
+ }
}
if (failed()) break;
} else {
@@ -957,6 +963,7 @@ class WasmFullDecoder : public WasmDecoder {
ssa_env_ = break_env;
}
len = 1 + iterator.length();
+ EndControl();
break;
}
case kExprReturn: {
@@ -1207,6 +1214,35 @@ class WasmFullDecoder : public WasmDecoder {
#if DEBUG
if (FLAG_trace_wasm_decoder) {
+ PrintF(" ");
+ for (size_t i = 0; i < control_.size(); ++i) {
+ Control* c = &control_[i];
+ enum ControlKind {
+ kControlIf,
+ kControlBlock,
+ kControlLoop,
+ kControlTry
+ };
+ switch (c->kind) {
+ case kControlIf:
+ PrintF("I");
+ break;
+ case kControlBlock:
+ PrintF("B");
+ break;
+ case kControlLoop:
+ PrintF("L");
+ break;
+ case kControlTry:
+ PrintF("T");
+ break;
+ default:
+ break;
+ }
+ PrintF("%u", c->merge.arity);
+ if (c->unreachable) PrintF("*");
+ }
+ PrintF(" | ");
for (size_t i = 0; i < stack_.size(); ++i) {
Value& val = stack_[i];
WasmOpcode opcode = static_cast<WasmOpcode>(*val.pc);
@@ -1236,20 +1272,23 @@ class WasmFullDecoder : public WasmDecoder {
default:
break;
}
+ if (val.node == nullptr) PrintF("?");
}
PrintF("\n");
}
#endif
pc_ += len;
- if (pc_ >= end_) {
- // End of code reached or exceeded.
- if (pc_ > end_ && ok()) error("Beyond end of code");
- return;
- }
} // end decode loop
+ if (pc_ > end_ && ok()) error("Beyond end of code");
}
- void EndControl() { ssa_env_->Kill(SsaEnv::kControlEnd); }
+ void EndControl() {
+ ssa_env_->Kill(SsaEnv::kControlEnd);
+ if (!control_.empty()) {
+ stack_.resize(control_.back().stack_depth);
+ control_.back().unreachable = true;
+ }
+ }
void SetBlockType(Control* c, BlockTypeOperand& operand) {
c->merge.arity = operand.arity;
@@ -1286,26 +1325,22 @@ class WasmFullDecoder : public WasmDecoder {
}
void PushBlock(SsaEnv* end_env) {
- const int stack_depth = static_cast<int>(stack_.size());
control_.emplace_back(
- Control::Block(pc_, stack_depth, end_env, current_catch_));
+ Control::Block(pc_, stack_.size(), end_env, current_catch_));
}
void PushLoop(SsaEnv* end_env) {
- const int stack_depth = static_cast<int>(stack_.size());
control_.emplace_back(
- Control::Loop(pc_, stack_depth, end_env, current_catch_));
+ Control::Loop(pc_, stack_.size(), end_env, current_catch_));
}
void PushIf(SsaEnv* end_env, SsaEnv* false_env) {
- const int stack_depth = static_cast<int>(stack_.size());
control_.emplace_back(
- Control::If(pc_, stack_depth, end_env, false_env, current_catch_));
+ Control::If(pc_, stack_.size(), end_env, false_env, current_catch_));
}
void PushTry(SsaEnv* end_env, SsaEnv* catch_env) {
- const int stack_depth = static_cast<int>(stack_.size());
- control_.emplace_back(Control::Try(pc_, stack_depth, end_env, zone_,
+ control_.emplace_back(Control::Try(pc_, stack_.size(), end_env, zone_,
catch_env, current_catch_));
current_catch_ = static_cast<int32_t>(control_.size() - 1);
}
@@ -1413,11 +1448,24 @@ class WasmFullDecoder : public WasmDecoder {
}
void Push(ValueType type, TFNode* node) {
- if (type != kWasmStmt && type != kWasmEnd) {
+ if (type != kWasmStmt) {
stack_.push_back({pc_, node, type});
}
}
+ void PushEndValues(Control* c) {
+ DCHECK_EQ(c, &control_.back());
+ stack_.resize(c->stack_depth);
+ if (c->merge.arity == 1) {
+ stack_.push_back(c->merge.vals.first);
+ } else {
+ for (unsigned i = 0; i < c->merge.arity; i++) {
+ stack_.push_back(c->merge.vals.array[i]);
+ }
+ }
+ DCHECK_EQ(c->stack_depth + c->merge.arity, stack_.size());
+ }
+
void PushReturns(FunctionSig* sig, TFNode** rets) {
for (size_t i = 0; i < sig->return_count(); i++) {
// When verifying only, then {rets} will be null, so push null.
@@ -1431,30 +1479,23 @@ class WasmFullDecoder : public WasmDecoder {
}
Value Pop(int index, ValueType expected) {
- if (!ssa_env_->go()) {
- // Unreachable code is essentially not typechecked.
- return {pc_, nullptr, expected};
- }
Value val = Pop();
- if (val.type != expected) {
- if (val.type != kWasmEnd) {
- 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));
- }
+ if (val.type != expected && val.type != kWasmVar && expected != kWasmVar) {
+ 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 val;
}
Value Pop() {
- if (!ssa_env_->go()) {
- // Unreachable code is essentially not typechecked.
- return {pc_, nullptr, kWasmEnd};
- }
size_t limit = control_.empty() ? 0 : control_.back().stack_depth;
if (stack_.size() <= limit) {
- Value val = {pc_, nullptr, kWasmStmt};
- error(pc_, pc_, "%s found empty stack", SafeOpcodeNameAt(pc_));
+ // Popping past the current control start in reachable code.
+ Value val = {pc_, nullptr, kWasmVar};
+ if (!control_.back().unreachable) {
+ error(pc_, pc_, "%s found empty stack", SafeOpcodeNameAt(pc_));
+ }
return val;
}
Value val = stack_.back();
@@ -1462,22 +1503,6 @@ class WasmFullDecoder : public WasmDecoder {
return val;
}
- Value PopUpTo(int stack_depth) {
- if (!ssa_env_->go()) {
- // Unreachable code is essentially not typechecked.
- return {pc_, nullptr, kWasmEnd};
- }
- if (stack_depth == static_cast<int>(stack_.size())) {
- Value val = {pc_, nullptr, kWasmStmt};
- return val;
- } else {
- DCHECK_LE(stack_depth, stack_.size());
- Value val = Pop();
- stack_.resize(stack_depth);
- return val;
- }
- }
-
int baserel(const byte* ptr) {
return base_ ? static_cast<int>(ptr - base_) : 0;
}
@@ -1485,17 +1510,17 @@ class WasmFullDecoder : public WasmDecoder {
int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); }
void BreakTo(unsigned depth) {
- if (!ssa_env_->go()) return;
Control* c = &control_[control_.size() - depth - 1];
if (c->is_loop()) {
// This is the inner loop block, which does not have a value.
Goto(ssa_env_, c->end_env);
} else {
// Merge the value(s) into the end of the block.
- if (c->stack_depth + c->merge.arity > stack_.size()) {
+ size_t expected = control_.back().stack_depth + c->merge.arity;
+ if (stack_.size() < expected && !control_.back().unreachable) {
error(
pc_, pc_,
- "expected at least %d values on the stack for br to @%d, found %d",
+ "expected at least %u values on the stack for br to @%d, found %d",
c->merge.arity, startrel(c->pc),
static_cast<int>(stack_.size() - c->stack_depth));
return;
@@ -1505,37 +1530,41 @@ class WasmFullDecoder : public WasmDecoder {
}
void FallThruTo(Control* c) {
- if (!ssa_env_->go()) return;
+ DCHECK_EQ(c, &control_.back());
// Merge the value(s) into the end of the block.
- int arity = static_cast<int>(c->merge.arity);
- if (c->stack_depth + arity != static_cast<int>(stack_.size())) {
- error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d",
- arity, startrel(c->pc));
+ size_t expected = c->stack_depth + c->merge.arity;
+ if (stack_.size() == expected ||
+ (stack_.size() < expected && c->unreachable)) {
+ MergeValuesInto(c);
+ c->unreachable = false;
return;
}
- MergeValuesInto(c);
+ error(pc_, pc_, "expected %u elements on the stack for fallthru to @%d",
+ c->merge.arity, startrel(c->pc));
}
- inline Value& GetMergeValueFromStack(Control* c, int i) {
+ inline Value& GetMergeValueFromStack(Control* c, size_t i) {
return stack_[stack_.size() - c->merge.arity + i];
}
- void TypeCheckLoopFallThru(Control* c) {
- if (!ssa_env_->go()) return;
+ void TypeCheckFallThru(Control* c) {
+ DCHECK_EQ(c, &control_.back());
// Fallthru must match arity exactly.
int arity = static_cast<int>(c->merge.arity);
- if (c->stack_depth + arity != static_cast<int>(stack_.size())) {
+ if (c->stack_depth + arity < stack_.size() ||
+ (c->stack_depth + arity != stack_.size() && !c->unreachable)) {
error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d",
arity, startrel(c->pc));
return;
}
// Typecheck the values left on the stack.
- for (unsigned i = 0; i < c->merge.arity; i++) {
+ size_t avail = stack_.size() - c->stack_depth;
+ for (size_t i = avail >= c->merge.arity ? 0 : c->merge.arity - avail;
+ i < c->merge.arity; i++) {
Value& val = GetMergeValueFromStack(c, i);
- Value& old =
- c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i];
+ Value& old = c->merge[i];
if (val.type != old.type) {
- error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i,
+ error(pc_, pc_, "type error in merge[%zu] (expected %s, got %s)", i,
WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
return;
}
@@ -1545,23 +1574,24 @@ class WasmFullDecoder : public WasmDecoder {
void MergeValuesInto(Control* c) {
SsaEnv* target = c->end_env;
bool first = target->state == SsaEnv::kUnreachable;
+ bool reachable = ssa_env_->go();
Goto(ssa_env_, target);
- for (unsigned i = 0; i < c->merge.arity; i++) {
+ size_t avail = stack_.size() - control_.back().stack_depth;
+ for (size_t i = avail >= c->merge.arity ? 0 : c->merge.arity - avail;
+ i < c->merge.arity; i++) {
Value& val = GetMergeValueFromStack(c, i);
- Value& old =
- c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i];
- if (val.type != old.type) {
- error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i,
+ Value& old = c->merge[i];
+ if (val.type != old.type && val.type != kWasmVar) {
+ error(pc_, pc_, "type error in merge[%zu] (expected %s, got %s)", i,
WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type));
return;
}
- if (builder_) {
+ if (builder_ && reachable) {
+ DCHECK_NOT_NULL(val.node);
old.node =
first ? val.node : CreateOrMergeIntoPhi(old.type, target->control,
old.node, val.node);
- } else {
- old.node = nullptr;
}
}
}
@@ -1586,13 +1616,13 @@ class WasmFullDecoder : public WasmDecoder {
break;
}
}
- PrintF(" env = %p, state = %c, reason = %s", static_cast<void*>(env),
+ PrintF("{set_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");
+ PrintF("}");
}
#endif
ssa_env_ = env;
« no previous file with comments | « src/asmjs/asm-wasm-builder.cc ('k') | src/wasm/wasm-opcodes.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698