[wasm][refactor] Simplify/unify parts of the function decoder

Changes:
- Remove TypeCheckBranchResult. Change TypeCheckBranch() to return bool.
  Refactor call sites to reflect this (decouple current code
  reachability check from type check).
- Unify TypeCheckBranch(), TypeCheckFallthrough(), and the type-checking
  part of Return() into TypeCheckStackAgainstMerge().
- Make sure all TypeCheck* functions are only called within VALIDATE.
- In graph-builder-interface, rename end_env -> merge_env to reflect
  its function for loops.
- Change expected error messages in some tests.

Change-Id: I857edc18db9c2454ad12d539ffe7a10e96367710
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2839560
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74100}
This commit is contained in:
Manos Koukoutos 2021-04-21 05:05:22 +00:00 committed by Commit Bot
parent 32281d6247
commit c4113c4705
12 changed files with 168 additions and 249 deletions

View File

@ -2647,34 +2647,28 @@ class WasmFullDecoder : public WasmDecoder<validate> {
if (!this->Validate(this->pc_ + 1, imm, control_.size())) return 0;
Value ref_object = Peek(0, 0);
Control* c = control_at(imm.depth);
TypeCheckBranchResult check_result = TypeCheckBranch(c, true, 1);
if (!VALIDATE(TypeCheckBranch<true>(c, 1))) return 0;
switch (ref_object.type.kind()) {
case kBottom:
// We are in a polymorphic stack. Leave the stack as it is.
DCHECK(check_result != kReachableBranch);
DCHECK(!current_code_reachable_and_ok_);
break;
case kRef:
// For a non-nullable value, we won't take the branch, and can leave
// the stack as it is.
break;
case kOptRef: {
if (V8_LIKELY(check_result == kReachableBranch)) {
CALL_INTERFACE_IF_OK_AND_REACHABLE(BrOnNull, ref_object, imm.depth);
Value result = CreateValue(
ValueType::Ref(ref_object.type.heap_type(), kNonNullable));
// The result of br_on_null has the same value as the argument (but a
// non-nullable type).
CALL_INTERFACE_IF_OK_AND_REACHABLE(Forward, ref_object, &result);
if (V8_LIKELY(current_code_reachable_and_ok_)) {
c->br_merge()->reached = true;
}
Drop(ref_object);
Push(result);
} else {
// Even in non-reachable code, we need to push a value of the correct
// type to the stack.
Drop(ref_object);
Push(CreateValue(
ValueType::Ref(ref_object.type.heap_type(), kNonNullable)));
}
break;
}
default:
@ -2753,7 +2747,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
this->DecodeError("else already present for if");
return 0;
}
if (!TypeCheckFallThru()) return 0;
if (!VALIDATE(TypeCheckFallThru())) return 0;
c->kind = kControlIfElse;
CALL_INTERFACE_IF_OK_AND_PARENT_REACHABLE(Else, c);
if (c->reachable()) c->end_merge.reached = true;
@ -2797,7 +2791,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
this->local_types_.begin() + c->locals_count);
this->num_locals_ -= c->locals_count;
}
if (!TypeCheckFallThru()) return 0;
if (control_.size() == 1) {
// If at the last (implicit) control, check we are at end.
@ -2808,10 +2801,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// The result of the block is the return value.
trace_msg->Append("\n" TRACE_INST_FORMAT, startrel(this->pc_),
"(implicit) return");
DoReturn();
DoReturn<kStrictCounting, kFallthroughMerge>();
control_.clear();
return 1;
}
if (!VALIDATE(TypeCheckFallThru())) return 0;
PopControl();
return 1;
}
@ -2852,9 +2847,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
BranchDepthImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_ + 1, imm, control_.size())) return 0;
Control* c = control_at(imm.depth);
TypeCheckBranchResult check_result = TypeCheckBranch(c, false, 0);
if (V8_LIKELY(check_result == kReachableBranch)) {
if (!VALIDATE(TypeCheckBranch<false>(c, 0))) return 0;
CALL_INTERFACE_IF_OK_AND_REACHABLE(BrOrRet, imm.depth, 0);
if (V8_LIKELY(current_code_reachable_and_ok_)) {
c->br_merge()->reached = true;
}
EndControl();
@ -2866,9 +2861,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
if (!this->Validate(this->pc_ + 1, imm, control_.size())) return 0;
Value cond = Peek(0, 0, kWasmI32);
Control* c = control_at(imm.depth);
TypeCheckBranchResult check_result = TypeCheckBranch(c, true, 1);
if (V8_LIKELY(check_result == kReachableBranch)) {
if (!VALIDATE(TypeCheckBranch<true>(c, 1))) return 0;
CALL_INTERFACE_IF_OK_AND_REACHABLE(BrIf, cond, imm.depth);
if (V8_LIKELY(current_code_reachable_and_ok_)) {
c->br_merge()->reached = true;
}
Drop(cond);
@ -2909,9 +2904,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
arity);
return 0;
}
TypeCheckBranchResult check_result =
TypeCheckBranch(control_at(target), false, 1);
if (V8_UNLIKELY(check_result == kInvalidStack)) return 0;
if (!VALIDATE(TypeCheckBranch<false>(control_at(target), 1))) return 0;
}
}
@ -2928,22 +2921,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
DECODE(Return) {
if (V8_LIKELY(current_code_reachable_and_ok_)) {
if (!VALIDATE(TypeCheckReturn())) return 0;
DoReturn();
} else {
// We inspect all return values from the stack to check their type.
// Since we deal with unreachable code, we do not have to keep the
// values.
int num_returns = static_cast<int>(this->sig_->return_count());
for (int i = num_returns - 1, depth = 0; i >= 0; --i, ++depth) {
Peek(depth, i, this->sig_->GetReturn(i));
}
Drop(num_returns);
}
EndControl();
return 1;
return DoReturn<kNonStrictCounting, kReturnMerge>() ? 1 : 0;
}
DECODE(Unreachable) {
@ -3657,7 +3635,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// If the parent block was reachable before, but the popped control does not
// return to here, this block becomes "spec only reachable".
if (!parent_reached) SetSucceedingCodeDynamicallyUnreachable();
current_code_reachable_and_ok_ = control_.back().reachable();
current_code_reachable_and_ok_ = this->ok() && control_.back().reachable();
}
int DecodeLoadMem(LoadType type, int prefix_len = 1) {
@ -4338,8 +4316,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
? kWasmBottom
: ValueType::Ref(rtt.type.ref_index(), kNonNullable));
Push(result_on_branch);
TypeCheckBranchResult check_result = TypeCheckBranch(c, true, 0);
if (V8_LIKELY(check_result == kReachableBranch)) {
if (!VALIDATE(TypeCheckBranch<true>(c, 0))) return 0;
// This logic ensures that code generation can assume that functions
// can only be cast to function types, and data objects to data types.
if (V8_LIKELY(ObjectRelatedWithRtt(obj, rtt))) {
@ -4349,12 +4326,11 @@ class WasmFullDecoder : public WasmDecoder<validate> {
Value* value_on_branch = stack_value(1);
CALL_INTERFACE_IF_OK_AND_REACHABLE(
BrOnCast, obj, rtt, value_on_branch, branch_depth.depth);
if (V8_LIKELY(current_code_reachable_and_ok_)) {
c->br_merge()->reached = true;
}
// Otherwise the types are unrelated. Do not branch.
} else if (check_result == kInvalidStack) {
return 0;
}
// Otherwise the types are unrelated. Do not branch.
Drop(result_on_branch);
Push(obj); // Restore stack state on fallthrough.
return opcode_length + branch_depth.length;
@ -4422,8 +4398,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
Value result_on_branch =
CreateValue(ValueType::Ref(heap_type, kNonNullable));
Push(result_on_branch);
TypeCheckBranchResult check_result = TypeCheckBranch(c, true, 0);
if (V8_LIKELY(check_result == kReachableBranch)) {
if (!VALIDATE(TypeCheckBranch<true>(c, 0))) return 0;
// The {value_on_branch} parameter we pass to the interface must be
// pointer-identical to the object on the stack, so we can't reuse
// {result_on_branch} which was passed-by-value to {Push}.
@ -4438,9 +4413,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE_IF_OK_AND_REACHABLE(BrOnI31, obj, value_on_branch,
branch_depth.depth);
}
if (V8_LIKELY(current_code_reachable_and_ok_)) {
c->br_merge()->reached = true;
} else if (check_result == kInvalidStack) {
return 0;
}
Drop(result_on_branch);
Push(obj); // Restore stack state on fallthrough.
@ -4626,12 +4600,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
}
void DoReturn() {
DCHECK_IMPLIES(current_code_reachable_and_ok_,
stack_size() >= this->sig_->return_count());
CALL_INTERFACE_IF_OK_AND_REACHABLE(DoReturn, 0);
}
V8_INLINE void EnsureStackSpace(int slots_needed) {
if (V8_LIKELY(stack_capacity_end_ - stack_end_ >= slots_needed)) return;
GrowStackSpace(slots_needed);
@ -4755,7 +4723,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// TODO(wasm): This check is often redundant.
if (V8_UNLIKELY(stack_size() < limit + count)) {
// Popping past the current control start in reachable code.
if (!VALIDATE(!control_.back().reachable())) {
if (!VALIDATE(!current_code_reachable_and_ok_)) {
NotEnoughArgumentsError(0);
}
// Pop what we can.
@ -4767,21 +4735,68 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// For more descriptive call sites:
V8_INLINE void Drop(const Value& /* unused */) { Drop(1); }
// Check if any values that may exist on top of the stack are compatible with
// {merge}. If {push_branch_values}, push back to the stack values based on
// the type of {merge} (this is needed for conditional branches due to their
// typing rules, and fallthroughs so that the outer control finds enough
// values on the stack).
// TODO(manoskouk): We expect this behavior to change, either due to
// relaxation of dead code verification, or the introduction of subtyping.
// {drop_values} is the number of stack values that will be dropped before the
// branch is taken. This is currently 1 for br (condition), br_table (index)
// and br_on_null (reference), and 0 for all other branches.
bool TypeCheckUnreachableMerge(Merge<Value>& merge, bool push_branch_values,
uint32_t drop_values) {
int arity = merge.arity;
enum StackElementsCountMode : bool {
kNonStrictCounting = false,
kStrictCounting = true
};
enum MergeType { kBranchMerge, kReturnMerge, kFallthroughMerge };
// - If the current code is reachable check if the current stack values are
// compatible with {merge} based on their number and types. Disregard the
// first {drop_values} on the stack. If {strict_count}, check that
// #(stack elements) == {merge->arity}, otherwise
// #(stack elements) >= {merge->arity}.
// - If the current code is unreachable, check if any values that may exist on
// top of the stack are compatible with {merge}. If {push_branch_values},
// push back to the stack values based on the type of {merge} (this is
// needed for conditional branches due to their typing rules, and
// fallthroughs so that the outer control finds the expected values on the
// stack). TODO(manoskouk): We expect the unreachable-code behavior to
// change, either due to relaxation of dead code verification, or the
// introduction of subtyping.
template <StackElementsCountMode strict_count, bool push_branch_values,
MergeType merge_type>
bool TypeCheckStackAgainstMerge(uint32_t drop_values, Merge<Value>* merge) {
static_assert(validate, "Call this function only within VALIDATE");
constexpr const char* merge_description =
merge_type == kBranchMerge
? "branch"
: merge_type == kReturnMerge ? "return" : "fallthru";
uint32_t arity = merge->arity;
uint32_t actual = stack_size() - control_.back().stack_depth;
if (V8_LIKELY(current_code_reachable_and_ok_)) {
if (V8_UNLIKELY(strict_count ? actual != drop_values + arity
: actual < drop_values + arity)) {
this->DecodeError("expected %u elements on the stack for %s, found %u",
arity, merge_description,
actual >= drop_values ? actual - drop_values : 0);
return false;
}
// Typecheck the topmost {merge->arity} values on the stack.
Value* stack_values = stack_end_ - (arity + drop_values);
for (uint32_t i = 0; i < arity; ++i) {
Value& val = stack_values[i];
Value& old = (*merge)[i];
if (!IsSubtypeOf(val.type, old.type, this->module_)) {
this->DecodeError("type error in %s[%u] (expected %s, got %s)",
merge_description, i, old.type.name().c_str(),
val.type.name().c_str());
return false;
}
}
return true;
}
// Unreachable code validation starts here.
if (V8_UNLIKELY(strict_count && actual > drop_values + arity)) {
this->DecodeError("expected %u elements on the stack for %s, found %u",
arity, merge_description,
actual >= drop_values ? actual - drop_values : 0);
return false;
}
// TODO(manoskouk): Use similar code as above if we keep unreachable checks.
for (int i = arity - 1, depth = drop_values; i >= 0; --i, ++depth) {
Peek(depth, i, merge[i].type);
Peek(depth, i, (*merge)[i].type);
}
if (push_branch_values) {
Drop(drop_values);
@ -4790,7 +4805,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// than requested. So ensuring stack space here is not redundant.
EnsureStackSpace(drop_values + arity);
// Push values of the correct type onto the stack.
for (int i = 0; i < arity; i++) Push(CreateValue(merge[i].type));
for (int i = 0; i < static_cast<int>(arity); i++) {
Push(CreateValue((*merge)[i].type));
}
// {drop_values} are about to be dropped anyway, so we can forget their
// previous types, but we do have to maintain the correct stack height.
for (uint32_t i = 0; i < drop_values; i++) {
@ -4800,36 +4817,29 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return this->ok();
}
template <StackElementsCountMode strict_count, MergeType merge_type>
bool DoReturn() {
if (!VALIDATE((TypeCheckStackAgainstMerge<strict_count, false, merge_type>(
0, &control_.front().end_merge)))) {
return false;
}
DCHECK_IMPLIES(current_code_reachable_and_ok_,
stack_size() >= this->sig_->return_count());
CALL_INTERFACE_IF_OK_AND_REACHABLE(DoReturn, 0);
EndControl();
return true;
}
int startrel(const byte* ptr) { return static_cast<int>(ptr - this->start_); }
void FallThrough() {
Control* c = &control_.back();
DCHECK_NE(c->kind, kControlLoop);
if (!TypeCheckFallThru()) return;
if (!VALIDATE(TypeCheckFallThru())) return;
CALL_INTERFACE_IF_OK_AND_REACHABLE(FallThruTo, c);
if (c->reachable()) c->end_merge.reached = true;
}
bool TypeCheckMergeValues(Control* c, uint32_t drop_values,
Merge<Value>* merge) {
static_assert(validate, "Call this function only within VALIDATE");
DCHECK(merge == &c->start_merge || merge == &c->end_merge);
DCHECK_GE(stack_size() - drop_values, c->stack_depth + merge->arity);
Value* stack_values = stack_value(merge->arity + drop_values);
// Typecheck the topmost {merge->arity} values on the stack.
for (uint32_t i = 0; i < merge->arity; ++i) {
Value& val = stack_values[i];
Value& old = (*merge)[i];
if (!VALIDATE(IsSubtypeOf(val.type, old.type, this->module_))) {
this->DecodeError("type error in merge[%u] (expected %s, got %s)", i,
old.type.name().c_str(), val.type.name().c_str());
return false;
}
}
return true;
}
bool TypeCheckOneArmedIf(Control* c) {
static_assert(validate, "Call this function only within VALIDATE");
DCHECK(c->is_onearmed_if());
@ -4852,46 +4862,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
bool TypeCheckFallThru() {
static_assert(validate, "Call this function only within VALIDATE");
Control& c = control_.back();
if (V8_LIKELY(c.reachable())) {
uint32_t expected = c.end_merge.arity;
DCHECK_GE(stack_size(), c.stack_depth);
uint32_t actual = stack_size() - c.stack_depth;
// Fallthrus must match the arity of the control exactly.
if (!VALIDATE(actual == expected)) {
this->DecodeError(
"expected %u elements on the stack for fallthru to @%d, found %u",
expected, startrel(c.pc()), actual);
return false;
return TypeCheckStackAgainstMerge<kStrictCounting, true, kFallthroughMerge>(
0, &control_.back().end_merge);
}
if (expected == 0) return true; // Fast path.
return TypeCheckMergeValues(&c, 0, &c.end_merge);
}
// Type-check an unreachable fallthru. First we do an arity check, then a
// type check. Note that type-checking may require an adjustment of the
// stack, if some stack values are missing to match the block signature.
Merge<Value>& merge = c.end_merge;
int arity = static_cast<int>(merge.arity);
int available = static_cast<int>(stack_size()) - c.stack_depth;
// For fallthrus, not more than the needed values should be available.
if (!VALIDATE(available <= arity)) {
this->DecodeError(
"expected %u elements on the stack for fallthru to @%d, found %u",
arity, startrel(c.pc()), available);
return false;
}
// Pop all values from the stack for type checking of existing stack
// values.
return TypeCheckUnreachableMerge(merge, true, 0);
}
enum TypeCheckBranchResult {
kReachableBranch,
kUnreachableBranch,
kInvalidStack,
};
// If the current code is reachable, check if the current stack values are
// compatible with a jump to {c}, based on their number and types.
@ -4903,65 +4876,11 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// {drop_values} is the number of stack values that will be dropped before the
// branch is taken. This is currently 1 for for br (condition), br_table
// (index) and br_on_null (reference), and 0 for all other branches.
TypeCheckBranchResult TypeCheckBranch(Control* c, bool push_branch_values,
uint32_t drop_values) {
if (V8_LIKELY(control_.back().reachable())) {
// We only do type-checking here. This is only needed during validation.
if (!validate) return kReachableBranch;
// Branches must have at least the number of values expected; can have
// more.
uint32_t expected = c->br_merge()->arity;
if (expected == 0) return kReachableBranch; // Fast path.
uint32_t limit = control_.back().stack_depth;
if (!VALIDATE(stack_size() >= limit + drop_values + expected)) {
uint32_t actual = stack_size() - limit;
actual -= std::min(actual, drop_values);
this->DecodeError(
"expected %u elements on the stack for br to @%d, found %u",
expected, startrel(c->pc()), actual);
return kInvalidStack;
}
return TypeCheckMergeValues(c, drop_values, c->br_merge())
? kReachableBranch
: kInvalidStack;
}
return TypeCheckUnreachableMerge(*c->br_merge(), push_branch_values,
drop_values)
? kUnreachableBranch
: kInvalidStack;
}
bool TypeCheckReturn() {
int num_returns = static_cast<int>(this->sig_->return_count());
// No type checking is needed if there are no returns.
if (num_returns == 0) return true;
// Returns must have at least the number of values expected; can have more.
int num_available =
static_cast<int>(stack_size()) - control_.back().stack_depth;
if (!VALIDATE(num_available >= num_returns)) {
this->DecodeError(
"expected %u elements on the stack for return, found %u", num_returns,
num_available);
return false;
}
// Typecheck the topmost {num_returns} values on the stack.
// This line requires num_returns > 0.
Value* stack_values = stack_end_ - num_returns;
for (int i = 0; i < num_returns; ++i) {
Value& val = stack_values[i];
ValueType expected_type = this->sig_->GetReturn(i);
if (!VALIDATE(IsSubtypeOf(val.type, expected_type, this->module_))) {
this->DecodeError("type error in return[%u] (expected %s, got %s)", i,
expected_type.name().c_str(),
val.type.name().c_str());
return false;
}
}
return true;
template <bool push_branch_values>
bool TypeCheckBranch(Control* c, uint32_t drop_values) {
static_assert(validate, "Call this function only within VALIDATE");
return TypeCheckStackAgainstMerge<kNonStrictCounting, push_branch_values,
kBranchMerge>(drop_values, c->br_merge());
}
void onFirstError() override {

View File

@ -95,7 +95,7 @@ class WasmGraphBuildingInterface {
};
struct Control : public ControlBase<Value, validate> {
SsaEnv* end_env = nullptr; // end environment for the construct.
SsaEnv* merge_env = nullptr; // merge environment for the construct.
SsaEnv* false_env = nullptr; // false environment (only for if).
TryInfo* try_info = nullptr; // information about try statements.
int32_t previous_catch = -1; // previous Control with a catch.
@ -154,15 +154,15 @@ class WasmGraphBuildingInterface {
void Block(FullDecoder* decoder, Control* block) {
// The branch environment is the outer environment.
block->end_env = ssa_env_;
block->merge_env = ssa_env_;
SetEnv(Steal(decoder->zone(), ssa_env_));
}
void Loop(FullDecoder* decoder, Control* block) {
SsaEnv* finish_try_env = Steal(decoder->zone(), ssa_env_);
block->end_env = finish_try_env;
SetEnv(finish_try_env);
// The continue environment is the inner environment.
// This is the merge environment at the beginning of the loop.
SsaEnv* merge_env = Steal(decoder->zone(), ssa_env_);
block->merge_env = merge_env;
SetEnv(merge_env);
ssa_env_->state = SsaEnv::kMerged;
@ -214,15 +214,15 @@ class WasmGraphBuildingInterface {
control());
}
// Now we setup a new environment for the inside of the loop.
SetEnv(Split(decoder->zone(), ssa_env_));
builder_->StackCheck(decoder->position());
ssa_env_->SetNotMerged();
if (!decoder->ok()) return;
// Wrap input merge into phis.
for (uint32_t i = 0; i < block->start_merge.arity; ++i) {
Value& val = block->start_merge[i];
TFNode* inputs[] = {val.node, block->end_env->control};
TFNode* inputs[] = {val.node, block->merge_env->control};
val.node = builder_->Phi(val.type, 1, inputs);
}
}
@ -236,7 +236,7 @@ class WasmGraphBuildingInterface {
SsaEnv* try_env = Steal(decoder->zone(), outer_env);
SetEnv(try_env);
TryInfo* try_info = decoder->zone()->New<TryInfo>(catch_env);
block->end_env = outer_env;
block->merge_env = outer_env;
block->try_info = try_info;
}
@ -244,12 +244,12 @@ class WasmGraphBuildingInterface {
TFNode* if_true = nullptr;
TFNode* if_false = nullptr;
builder_->BranchNoHint(cond.node, &if_true, &if_false);
SsaEnv* end_env = ssa_env_;
SsaEnv* merge_env = ssa_env_;
SsaEnv* false_env = Split(decoder->zone(), ssa_env_);
false_env->control = if_false;
SsaEnv* true_env = Steal(decoder->zone(), ssa_env_);
true_env->control = if_true;
if_block->end_env = end_env;
if_block->merge_env = merge_env;
if_block->false_env = false_env;
SetEnv(true_env);
}
@ -290,7 +290,7 @@ class WasmGraphBuildingInterface {
MergeValuesInto(decoder, block, &block->end_merge, values);
}
// Now continue with the merged environment.
SetEnv(block->end_env);
SetEnv(block->merge_env);
}
void UnOp(FullDecoder* decoder, WasmOpcode opcode, const Value& value,
@ -1197,7 +1197,7 @@ class WasmGraphBuildingInterface {
Value* values) {
DCHECK(merge == &c->start_merge || merge == &c->end_merge);
SsaEnv* target = c->end_env;
SsaEnv* target = c->merge_env;
// This has to be computed before calling Goto().
const bool first = target->state == SsaEnv::kUnreachable;

View File

@ -1,5 +1,5 @@
*%(basename)s:9: CompileError: WebAssembly.compile(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
*%(basename)s:9: CompileError: WebAssembly.compile(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24
let rethrow = e => setTimeout(_ => {throw e}, 0);
^
CompileError: WebAssembly.compile(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
CompileError: WebAssembly.compile(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24

View File

@ -1,5 +1,5 @@
*%(basename)s:9: CompileError: WebAssembly.instantiate(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
*%(basename)s:9: CompileError: WebAssembly.instantiate(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24
let rethrow = e => setTimeout(_ => {throw e}, 0);
^
CompileError: WebAssembly.instantiate(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
CompileError: WebAssembly.instantiate(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24

View File

@ -1,5 +1,5 @@
*%(basename)s:11: CompileError: WebAssembly.compileStreaming(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
*%(basename)s:11: CompileError: WebAssembly.compileStreaming(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24
let rethrow = e => setTimeout(_ => {throw e}, 0);
^
CompileError: WebAssembly.compileStreaming(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
CompileError: WebAssembly.compileStreaming(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24

View File

@ -1,5 +1,5 @@
*%(basename)s:11: CompileError: WebAssembly.instantiateStreaming(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
*%(basename)s:11: CompileError: WebAssembly.instantiateStreaming(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24
let rethrow = e => setTimeout(_ => {throw e}, 0);
^
CompileError: WebAssembly.instantiateStreaming(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
CompileError: WebAssembly.instantiateStreaming(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24

View File

@ -1,6 +1,6 @@
*%(basename)s:9: CompileError: WebAssembly.Module(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
*%(basename)s:9: CompileError: WebAssembly.Module(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24
new WebAssembly.Module(builder.toBuffer());
^
CompileError: WebAssembly.Module(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru to @1, found 0 @+24
CompileError: WebAssembly.Module(): Compiling function #0:"f" failed: expected 1 elements on the stack for fallthru, found 0 @+24
at *%(basename)s:9:1

View File

@ -29,5 +29,5 @@ kExprEnd, // @21
assertThrows(
() => {builder.toModule()}, WebAssembly.CompileError,
'WebAssembly.Module(): Compiling function #0:\"main\" failed: ' +
'type error in merge[0] (expected f32, got i32) @+57');
'type error in branch[0] (expected f32, got i32) @+57');
})();

View File

@ -67,7 +67,7 @@ assertPromiseResult(async function badFunctionInTheMiddle() {
await assertCompileError(
buffer,
'Compiling function #10:\"bad\" failed: ' +
'expected 1 elements on the stack for fallthru to @1, found 0 @+94');
'expected 1 elements on the stack for fallthru, found 0 @+94');
}());
assertPromiseResult(async function importWithoutCode() {

View File

@ -35,7 +35,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
assertPromiseResult(WebAssembly.compile(bytes)
.then(assertUnreachable,
error => assertEquals("WebAssembly.compile(): type error in " +
"merge[0] (expected i32, got i64) @+56", error.message)));
"fallthru[0] (expected i32, got i64) @+56", error.message)));
})();
(function testCompileEmptyModule() {

View File

@ -33,7 +33,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
assertThrows(() => builder.toModule(),
WebAssembly.CompileError,
"WebAssembly.Module(): Compiling function #0:\"id\" failed: type error " +
"in merge[0] (expected i32, got i64) @+56");
"in fallthru[0] (expected i32, got i64) @+56");
})();
(function testCompileEmptyModule() {

View File

@ -3841,7 +3841,7 @@ TEST_F(FunctionBodyDecoderTest, BrOnNull) {
WASM_I32V(0), kExprSelectWithType, 1,
WASM_REF_TYPE(reps[0]))},
kAppendEnd,
"expected 1 elements on the stack for br to @1, found 0");
"expected 1 elements on the stack for branch, found 0");
}
TEST_F(FunctionBodyDecoderTest, GCStruct) {
@ -3880,8 +3880,7 @@ TEST_F(FunctionBodyDecoderTest, GCStruct) {
&sig_r_v,
{WASM_STRUCT_NEW_WITH_RTT(struct_type_index, WASM_I32V(0), WASM_I32V(1),
WASM_RTT_CANON(struct_type_index))},
kAppendEnd,
"expected 1 elements on the stack for fallthru to @1, found 2");
kAppendEnd, "expected 1 elements on the stack for fallthru, found 2");
// Mistyped arguments.
ExpectFailure(&sig_v_r,
{WASM_STRUCT_NEW_WITH_RTT(struct_type_index, WASM_LOCAL_GET(0),
@ -3927,7 +3926,7 @@ TEST_F(FunctionBodyDecoderTest, GCStruct) {
ExpectFailure(
&sig_f_r,
{WASM_STRUCT_GET(struct_type_index, field_index, WASM_LOCAL_GET(0))},
kAppendEnd, "type error in merge[0] (expected f32, got i32)");
kAppendEnd, "type error in fallthru[0] (expected f32, got i32)");
/** struct.set **/
ExpectValidates(&sig_v_r, {WASM_STRUCT_SET(struct_type_index, field_index,
@ -3953,7 +3952,7 @@ TEST_F(FunctionBodyDecoderTest, GCStruct) {
{WASM_STRUCT_SET(struct_type_index, field_index,
WASM_LOCAL_GET(0), WASM_I32V(0))},
kAppendEnd,
"expected 1 elements on the stack for fallthru to @1, found 0");
"expected 1 elements on the stack for fallthru, found 0");
// Setting immutable field.
ExpectFailure(
sigs.v_v(),
@ -4063,7 +4062,7 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
ExpectFailure(
&sig_f_r,
{WASM_ARRAY_GET(array_type_index, WASM_LOCAL_GET(0), WASM_I32V(5))},
kAppendEnd, "type error in merge[0] (expected f32, got funcref)");
kAppendEnd, "type error in fallthru[0] (expected f32, got funcref)");
// array.get_s/u fail.
ExpectFailure(
@ -4112,7 +4111,8 @@ TEST_F(FunctionBodyDecoderTest, GCArray) {
{WASM_ARRAY_LEN(array_type_index, WASM_LOCAL_GET(0))});
// Wrong return type.
ExpectFailure(&sig_f_r, {WASM_ARRAY_LEN(array_type_index, WASM_LOCAL_GET(0))},
kAppendEnd, "type error in merge[0] (expected f32, got i32)");
kAppendEnd,
"type error in fallthru[0] (expected f32, got i32)");
// Non-array type index.
ExpectFailure(&sig_i_r,
{WASM_ARRAY_LEN(struct_type_index, WASM_LOCAL_GET(0))},
@ -4234,7 +4234,7 @@ TEST_F(FunctionBodyDecoderTest, RttCanon) {
ValueType rtt2 = ValueType::Rtt(type_index, 1);
FunctionSig sig2(1, 0, &rtt2);
ExpectFailure(&sig2, {WASM_RTT_CANON(type_index)}, kAppendEnd,
"type error in merge[0]");
"type error in fallthru[0]");
}
}