summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/function-body-decoder-impl.h
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/function-body-decoder-impl.h')
-rw-r--r--deps/v8/src/wasm/function-body-decoder-impl.h510
1 files changed, 345 insertions, 165 deletions
diff --git a/deps/v8/src/wasm/function-body-decoder-impl.h b/deps/v8/src/wasm/function-body-decoder-impl.h
index eb895a25b3..9f1ca23c62 100644
--- a/deps/v8/src/wasm/function-body-decoder-impl.h
+++ b/deps/v8/src/wasm/function-body-decoder-impl.h
@@ -13,6 +13,7 @@
#include "src/utils/bit-vector.h"
#include "src/wasm/decoder.h"
#include "src/wasm/function-body-decoder.h"
+#include "src/wasm/value-type.h"
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
@@ -64,7 +65,7 @@ struct WasmException;
#define ATOMIC_OP_LIST(V) \
V(AtomicNotify, Uint32) \
V(I32AtomicWait, Uint32) \
- V(I64AtomicWait, Uint32) \
+ V(I64AtomicWait, Uint64) \
V(I32AtomicLoad, Uint32) \
V(I64AtomicLoad, Uint64) \
V(I32AtomicLoad8U, Uint8) \
@@ -229,17 +230,17 @@ inline bool decode_local_type(uint8_t val, ValueType* result) {
case kLocalS128:
*result = kWasmS128;
return true;
- case kLocalAnyFunc:
- *result = kWasmAnyFunc;
+ case kLocalFuncRef:
+ *result = kWasmFuncRef;
return true;
case kLocalAnyRef:
*result = kWasmAnyRef;
return true;
- case kLocalExceptRef:
- *result = kWasmExceptRef;
+ case kLocalExnRef:
+ *result = kWasmExnRef;
return true;
default:
- *result = kWasmVar;
+ *result = kWasmBottom;
return false;
}
}
@@ -296,20 +297,20 @@ struct BlockTypeImmediate {
}
uint32_t in_arity() const {
- if (type != kWasmVar) return 0;
+ if (type != kWasmBottom) return 0;
return static_cast<uint32_t>(sig->parameter_count());
}
uint32_t out_arity() const {
if (type == kWasmStmt) return 0;
- if (type != kWasmVar) return 1;
+ if (type != kWasmBottom) return 1;
return static_cast<uint32_t>(sig->return_count());
}
ValueType in_type(uint32_t index) {
- DCHECK_EQ(kWasmVar, type);
+ DCHECK_EQ(kWasmBottom, type);
return sig->GetParam(index);
}
ValueType out_type(uint32_t index) {
- if (type == kWasmVar) return sig->GetReturn(index);
+ if (type == kWasmBottom) return sig->GetReturn(index);
DCHECK_NE(kWasmStmt, type);
DCHECK_EQ(0, index);
return type;
@@ -573,14 +574,14 @@ struct ElemDropImmediate {
template <Decoder::ValidateFlag validate>
struct TableCopyImmediate {
- TableIndexImmediate<validate> table_src;
TableIndexImmediate<validate> table_dst;
+ TableIndexImmediate<validate> table_src;
unsigned length = 0;
inline TableCopyImmediate(Decoder* decoder, const byte* pc) {
- table_src = TableIndexImmediate<validate>(decoder, pc + 1);
- table_dst =
- TableIndexImmediate<validate>(decoder, pc + 1 + table_src.length);
+ table_dst = TableIndexImmediate<validate>(decoder, pc + 1);
+ table_src =
+ TableIndexImmediate<validate>(decoder, pc + 1 + table_dst.length);
length = table_src.length + table_dst.length;
}
};
@@ -718,9 +719,9 @@ struct ControlBase {
const LocalIndexImmediate<validate>& imm) \
F(GetGlobal, Value* result, const GlobalIndexImmediate<validate>& imm) \
F(SetGlobal, const Value& value, const GlobalIndexImmediate<validate>& imm) \
- F(GetTable, const Value& index, Value* result, \
+ F(TableGet, const Value& index, Value* result, \
const TableIndexImmediate<validate>& imm) \
- F(SetTable, const Value& index, const Value& value, \
+ F(TableSet, const Value& index, const Value& value, \
const TableIndexImmediate<validate>& imm) \
F(Unreachable) \
F(Select, const Value& cond, const Value& fval, const Value& tval, \
@@ -759,6 +760,7 @@ struct ControlBase {
Vector<Value> values) \
F(AtomicOp, WasmOpcode opcode, Vector<Value> args, \
const MemoryAccessImmediate<validate>& imm, Value* result) \
+ F(AtomicFence) \
F(MemoryInit, const MemoryInitImmediate<validate>& imm, const Value& dst, \
const Value& src, const Value& size) \
F(DataDrop, const DataDropImmediate<validate>& imm) \
@@ -849,18 +851,18 @@ class WasmDecoder : public Decoder {
}
decoder->error(decoder->pc() - 1, "invalid local type");
return false;
- case kLocalAnyFunc:
+ case kLocalFuncRef:
if (enabled.anyref) {
- type = kWasmAnyFunc;
+ type = kWasmFuncRef;
break;
}
decoder->error(decoder->pc() - 1,
- "local type 'anyfunc' is not enabled with "
+ "local type 'funcref' is not enabled with "
"--experimental-wasm-anyref");
return false;
- case kLocalExceptRef:
+ case kLocalExnRef:
if (enabled.eh) {
- type = kWasmExceptRef;
+ type = kWasmExnRef;
break;
}
decoder->error(decoder->pc() - 1, "invalid local type");
@@ -1015,8 +1017,8 @@ class WasmDecoder : public Decoder {
return false;
}
if (!VALIDATE(module_ != nullptr &&
- module_->tables[imm.table_index].type == kWasmAnyFunc)) {
- error("table of call_indirect must be of type anyfunc");
+ module_->tables[imm.table_index].type == kWasmFuncRef)) {
+ error("table of call_indirect must be of type funcref");
return false;
}
if (!Complete(pc, imm)) {
@@ -1049,6 +1051,12 @@ class WasmDecoder : public Decoder {
SimdLaneImmediate<validate>& imm) {
uint8_t num_lanes = 0;
switch (opcode) {
+ case kExprF64x2ExtractLane:
+ case kExprF64x2ReplaceLane:
+ case kExprI64x2ExtractLane:
+ case kExprI64x2ReplaceLane:
+ num_lanes = 2;
+ break;
case kExprF32x4ExtractLane:
case kExprF32x4ReplaceLane:
case kExprI32x4ExtractLane:
@@ -1079,6 +1087,11 @@ class WasmDecoder : public Decoder {
SimdShiftImmediate<validate>& imm) {
uint8_t max_shift = 0;
switch (opcode) {
+ case kExprI64x2Shl:
+ case kExprI64x2ShrS:
+ case kExprI64x2ShrU:
+ max_shift = 64;
+ break;
case kExprI32x4Shl:
case kExprI32x4ShrS:
case kExprI32x4ShrU:
@@ -1121,7 +1134,7 @@ class WasmDecoder : public Decoder {
}
inline bool Complete(BlockTypeImmediate<validate>& imm) {
- if (imm.type != kWasmVar) return true;
+ if (imm.type != kWasmBottom) return true;
if (!VALIDATE(module_ && imm.sig_index < module_->signatures.size())) {
return false;
}
@@ -1238,8 +1251,8 @@ class WasmDecoder : public Decoder {
GlobalIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
- case kExprGetTable:
- case kExprSetTable: {
+ case kExprTableGet:
+ case kExprTableSet: {
TableIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
@@ -1405,6 +1418,12 @@ class WasmDecoder : public Decoder {
MemoryAccessImmediate<validate> imm(decoder, pc + 1, UINT32_MAX);
return 2 + imm.length;
}
+#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
+ FOREACH_ATOMIC_0_OPERAND_OPCODE(DECLARE_OPCODE_CASE)
+#undef DECLARE_OPCODE_CASE
+ {
+ return 2 + 1;
+ }
default:
decoder->error(pc, "invalid Atomics opcode");
return 2;
@@ -1428,11 +1447,11 @@ class WasmDecoder : public Decoder {
case kExprSelect:
case kExprSelectWithType:
return {3, 1};
- case kExprSetTable:
+ case kExprTableSet:
FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
return {2, 0};
FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
- case kExprGetTable:
+ case kExprTableGet:
case kExprTeeLocal:
case kExprMemoryGrow:
return {1, 1};
@@ -1536,7 +1555,6 @@ template <Decoder::ValidateFlag validate, typename Interface>
class WasmFullDecoder : public WasmDecoder<validate> {
using Value = typename Interface::Value;
using Control = typename Interface::Control;
- using MergeValues = Merge<Value>;
using ArgVector = base::SmallVector<Value, 8>;
// All Value types should be trivially copyable for performance. We push, pop,
@@ -1658,7 +1676,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ZoneVector<Control> control_; // stack of blocks, loops, and ifs.
static Value UnreachableValue(const uint8_t* pc) {
- return Value{pc, kWasmVar};
+ return Value{pc, kWasmBottom};
}
bool CheckHasMemory() {
@@ -1760,7 +1778,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
case kExprRethrow: {
CHECK_PROTOTYPE_OPCODE(eh);
- auto exception = Pop(0, kWasmExceptRef);
+ auto exception = Pop(0, kWasmExnRef);
CALL_INTERFACE_IF_REACHABLE(Rethrow, exception);
EndControl();
break;
@@ -1806,7 +1824,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
FallThruTo(c);
stack_.erase(stack_.begin() + c->stack_depth, stack_.end());
c->reachability = control_at(1)->innerReachability();
- auto* exception = Push(kWasmExceptRef);
+ auto* exception = Push(kWasmExnRef);
CALL_INTERFACE_IF_PARENT_REACHABLE(Catch, c, exception);
break;
}
@@ -1816,7 +1834,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
if (!this->Validate(this->pc_, imm.depth, control_.size())) break;
if (!this->Validate(this->pc_ + imm.depth.length, imm.index)) break;
Control* c = control_at(imm.depth.depth);
- auto exception = Pop(0, kWasmExceptRef);
+ auto exception = Pop(0, kWasmExnRef);
const WasmExceptionSig* sig = imm.index.exception->sig;
size_t value_count = sig->parameter_count();
// TODO(mstarzinger): This operand stack mutation is an ugly hack to
@@ -1825,15 +1843,17 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// special handling for both and do minimal/no stack mutation here.
for (size_t i = 0; i < value_count; ++i) Push(sig->GetParam(i));
Vector<Value> values(stack_.data() + c->stack_depth, value_count);
- if (!TypeCheckBranch(c)) break;
- if (control_.back().reachable()) {
+ TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
+ if (V8_LIKELY(check_result == kReachableBranch)) {
CALL_INTERFACE(BrOnException, exception, imm.index, imm.depth.depth,
values);
c->br_merge()->reached = true;
+ } else if (check_result == kInvalidStack) {
+ break;
}
len = 1 + imm.length;
for (size_t i = 0; i < value_count; ++i) Pop();
- auto* pexception = Push(kWasmExceptRef);
+ auto* pexception = Push(kWasmExnRef);
*pexception = exception;
break;
}
@@ -1875,7 +1895,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
this->error(this->pc_, "else already present for if");
break;
}
- if (!TypeCheckFallThru(c)) break;
+ if (!TypeCheckFallThru()) break;
c->kind = kControlIfElse;
CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c);
if (c->reachable()) c->end_merge.reached = true;
@@ -1902,7 +1922,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
}
- if (!TypeCheckFallThru(c)) break;
+ if (!TypeCheckFallThru()) break;
if (control_.size() == 1) {
// If at the last (implicit) control, check we are at end.
@@ -1917,7 +1937,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
control_.clear();
break;
}
-
PopControl(c);
break;
}
@@ -1925,8 +1944,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
auto cond = Pop(2, kWasmI32);
auto fval = Pop();
auto tval = Pop(0, fval.type);
- ValueType type = tval.type == kWasmVar ? fval.type : tval.type;
- if (ValueTypes::IsSubType(kWasmAnyRef, type)) {
+ ValueType type = tval.type == kWasmBottom ? fval.type : tval.type;
+ if (ValueTypes::IsSubType(type, kWasmAnyRef)) {
this->error(
"select without type is only valid for value type inputs");
break;
@@ -1951,12 +1970,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
BranchDepthImmediate<validate> imm(this, this->pc_);
if (!this->Validate(this->pc_, imm, control_.size())) break;
Control* c = control_at(imm.depth);
- if (!TypeCheckBranch(c)) break;
- if (imm.depth == control_.size() - 1) {
- DoReturn();
- } else if (control_.back().reachable()) {
- CALL_INTERFACE(Br, c);
- c->br_merge()->reached = true;
+ TypeCheckBranchResult check_result = TypeCheckBranch(c, false);
+ if (V8_LIKELY(check_result == kReachableBranch)) {
+ if (imm.depth == control_.size() - 1) {
+ DoReturn();
+ } else {
+ CALL_INTERFACE(Br, c);
+ c->br_merge()->reached = true;
+ }
+ } else if (check_result == kInvalidStack) {
+ break;
}
len = 1 + imm.length;
EndControl();
@@ -1968,10 +1991,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
if (this->failed()) break;
if (!this->Validate(this->pc_, imm, control_.size())) break;
Control* c = control_at(imm.depth);
- if (!TypeCheckBranch(c)) break;
- if (control_.back().reachable()) {
+ TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
+ if (V8_LIKELY(check_result == kReachableBranch)) {
CALL_INTERFACE(BrIf, cond, imm.depth);
c->br_merge()->reached = true;
+ } else if (check_result == kInvalidStack) {
+ break;
}
len = 1 + imm.length;
break;
@@ -1982,42 +2007,45 @@ class WasmFullDecoder : public WasmDecoder<validate> {
auto key = Pop(0, kWasmI32);
if (this->failed()) break;
if (!this->Validate(this->pc_, imm, control_.size())) break;
- uint32_t br_arity = 0;
+
+ // Cache the branch targets during the iteration, so that we can set
+ // all branch targets as reachable after the {CALL_INTERFACE} call.
std::vector<bool> br_targets(control_.size());
+
+ // The result types of the br_table instruction. We have to check the
+ // stack against these types. Only needed during validation.
+ std::vector<ValueType> result_types;
+
while (iterator.has_next()) {
- const uint32_t i = iterator.cur_index();
+ const uint32_t index = iterator.cur_index();
const byte* pos = iterator.pc();
uint32_t target = iterator.next();
- if (!VALIDATE(target < control_.size())) {
- this->errorf(pos,
- "improper branch in br_table target %u (depth %u)",
- i, target);
- break;
- }
+ if (!VALIDATE(ValidateBrTableTarget(target, pos, index))) break;
// Avoid redundant branch target checks.
if (br_targets[target]) continue;
br_targets[target] = true;
- // Check that label types match up.
- Control* c = control_at(target);
- uint32_t arity = c->br_merge()->arity;
- if (i == 0) {
- br_arity = arity;
- } else if (!VALIDATE(br_arity == arity)) {
- this->errorf(pos,
- "inconsistent arity in br_table target %u"
- " (previous was %u, this one %u)",
- i, br_arity, arity);
+
+ if (validate) {
+ if (index == 0) {
+ // With the first branch target, initialize the result types.
+ result_types = InitializeBrTableResultTypes(target);
+ } else if (!UpdateBrTableResultTypes(&result_types, target, pos,
+ index)) {
+ break;
+ }
}
- if (!TypeCheckBranch(c)) break;
}
- if (this->failed()) break;
+
+ if (!VALIDATE(TypeCheckBrTable(result_types))) break;
+
+ DCHECK(this->ok());
if (control_.back().reachable()) {
CALL_INTERFACE(BrTable, imm, key);
- for (uint32_t depth = control_depth(); depth-- > 0;) {
- if (!br_targets[depth]) continue;
- control_at(depth)->br_merge()->reached = true;
+ for (int i = 0, e = control_depth(); i < e; ++i) {
+ if (!br_targets[i]) continue;
+ control_at(i)->br_merge()->reached = true;
}
}
@@ -2026,8 +2054,19 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break;
}
case kExprReturn: {
- if (!TypeCheckReturn()) break;
- DoReturn();
+ if (V8_LIKELY(control_.back().reachable())) {
+ if (!VALIDATE(TypeCheckReturn())) break;
+ DoReturn();
+ } else {
+ // We pop 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 = 0; i < num_returns; ++i) {
+ Pop(i, this->sig_->GetReturn(i));
+ }
+ }
+
EndControl();
break;
}
@@ -2075,7 +2114,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CHECK_PROTOTYPE_OPCODE(anyref);
FunctionIndexImmediate<validate> imm(this, this->pc_);
if (!this->Validate(this->pc_, imm)) break;
- auto* value = Push(kWasmAnyFunc);
+ auto* value = Push(kWasmFuncRef);
CALL_INTERFACE_IF_REACHABLE(RefFunc, imm.index, value);
len = 1 + imm.length;
break;
@@ -2131,7 +2170,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE_IF_REACHABLE(SetGlobal, value, imm);
break;
}
- case kExprGetTable: {
+ case kExprTableGet: {
CHECK_PROTOTYPE_OPCODE(anyref);
TableIndexImmediate<validate> imm(this, this->pc_);
len = 1 + imm.length;
@@ -2139,17 +2178,17 @@ class WasmFullDecoder : public WasmDecoder<validate> {
DCHECK_NOT_NULL(this->module_);
auto index = Pop(0, kWasmI32);
auto* result = Push(this->module_->tables[imm.index].type);
- CALL_INTERFACE_IF_REACHABLE(GetTable, index, result, imm);
+ CALL_INTERFACE_IF_REACHABLE(TableGet, index, result, imm);
break;
}
- case kExprSetTable: {
+ case kExprTableSet: {
CHECK_PROTOTYPE_OPCODE(anyref);
TableIndexImmediate<validate> imm(this, this->pc_);
len = 1 + imm.length;
if (!this->Validate(this->pc_, imm)) break;
auto value = Pop(1, this->module_->tables[imm.index].type);
auto index = Pop(0, kWasmI32);
- CALL_INTERFACE_IF_REACHABLE(SetTable, index, value, imm);
+ CALL_INTERFACE_IF_REACHABLE(TableSet, index, value, imm);
break;
}
@@ -2328,7 +2367,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
case kAtomicPrefix: {
CHECK_PROTOTYPE_OPCODE(threads);
- if (!CheckHasSharedMemory()) break;
len++;
byte atomic_index =
this->template read_u8<validate>(this->pc_ + 1, "atomic index");
@@ -2348,8 +2386,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break;
default: {
// Deal with special asmjs opcodes.
- if (this->module_ != nullptr &&
- this->module_->origin == kAsmJsOrigin) {
+ if (this->module_ != nullptr && is_asmjs_module(this->module_)) {
FunctionSig* sig = WasmOpcodes::AsmjsSignature(opcode);
if (sig) {
BuildSimpleOperator(opcode, sig);
@@ -2520,6 +2557,90 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return imm.length;
}
+ bool ValidateBrTableTarget(uint32_t target, const byte* pos, int index) {
+ if (!VALIDATE(target < this->control_.size())) {
+ this->errorf(pos, "improper branch in br_table target %u (depth %u)",
+ index, target);
+ return false;
+ }
+ return true;
+ }
+
+ std::vector<ValueType> InitializeBrTableResultTypes(uint32_t target) {
+ auto* merge = control_at(target)->br_merge();
+ int br_arity = merge->arity;
+ std::vector<ValueType> result(br_arity);
+ for (int i = 0; i < br_arity; ++i) {
+ result[i] = (*merge)[i].type;
+ }
+ return result;
+ }
+
+ bool UpdateBrTableResultTypes(std::vector<ValueType>* result_types,
+ uint32_t target, const byte* pos, int index) {
+ auto* merge = control_at(target)->br_merge();
+ int br_arity = merge->arity;
+ // First we check if the arities match.
+ if (br_arity != static_cast<int>(result_types->size())) {
+ this->errorf(pos,
+ "inconsistent arity in br_table target %u (previous was "
+ "%zu, this one is %u)",
+ index, result_types->size(), br_arity);
+ return false;
+ }
+
+ for (int i = 0; i < br_arity; ++i) {
+ if (this->enabled_.anyref) {
+ // The expected type is the biggest common sub type of all targets.
+ (*result_types)[i] =
+ ValueTypes::CommonSubType((*result_types)[i], (*merge)[i].type);
+ } else {
+ // All target must have the same signature.
+ if ((*result_types)[i] != (*merge)[i].type) {
+ this->errorf(pos,
+ "inconsistent type in br_table target %u (previous "
+ "was %s, this one is %s)",
+ index, ValueTypes::TypeName((*result_types)[i]),
+ ValueTypes::TypeName((*merge)[i].type));
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ bool TypeCheckBrTable(const std::vector<ValueType>& result_types) {
+ int br_arity = static_cast<int>(result_types.size());
+ if (V8_LIKELY(control_.back().reachable())) {
+ int available =
+ static_cast<int>(stack_.size()) - control_.back().stack_depth;
+ // There have to be enough values on the stack.
+ if (available < br_arity) {
+ this->errorf(this->pc_,
+ "expected %u elements on the stack for branch to "
+ "@%d, found %u",
+ br_arity, startrel(control_.back().pc), available);
+ return false;
+ }
+ Value* stack_values = &*(stack_.end() - br_arity);
+ // Type-check the topmost br_arity values on the stack.
+ for (int i = 0; i < br_arity; ++i) {
+ Value& val = stack_values[i];
+ if (!ValueTypes::IsSubType(val.type, result_types[i])) {
+ this->errorf(this->pc_,
+ "type error in merge[%u] (expected %s, got %s)", i,
+ ValueTypes::TypeName(result_types[i]),
+ ValueTypes::TypeName(val.type));
+ return false;
+ }
+ }
+ } else { // !control_.back().reachable()
+ // Pop values from the stack, accoring to the expected signature.
+ for (int i = 0; i < br_arity; ++i) Pop(i + 1, result_types[i]);
+ }
+ return this->ok();
+ }
+
uint32_t SimdExtractLane(WasmOpcode opcode, ValueType type) {
SimdLaneImmediate<validate> imm(this, this->pc_);
if (this->Validate(this->pc_, opcode, imm)) {
@@ -2570,26 +2691,45 @@ class WasmFullDecoder : public WasmDecoder<validate> {
uint32_t DecodeSimdOpcode(WasmOpcode opcode) {
uint32_t len = 0;
switch (opcode) {
+ case kExprF64x2ExtractLane: {
+ len = SimdExtractLane(opcode, kWasmF64);
+ break;
+ }
case kExprF32x4ExtractLane: {
len = SimdExtractLane(opcode, kWasmF32);
break;
}
+ case kExprI64x2ExtractLane: {
+ len = SimdExtractLane(opcode, kWasmI64);
+ break;
+ }
case kExprI32x4ExtractLane:
case kExprI16x8ExtractLane:
case kExprI8x16ExtractLane: {
len = SimdExtractLane(opcode, kWasmI32);
break;
}
+ case kExprF64x2ReplaceLane: {
+ len = SimdReplaceLane(opcode, kWasmF64);
+ break;
+ }
case kExprF32x4ReplaceLane: {
len = SimdReplaceLane(opcode, kWasmF32);
break;
}
+ case kExprI64x2ReplaceLane: {
+ len = SimdReplaceLane(opcode, kWasmI64);
+ break;
+ }
case kExprI32x4ReplaceLane:
case kExprI16x8ReplaceLane:
case kExprI8x16ReplaceLane: {
len = SimdReplaceLane(opcode, kWasmI32);
break;
}
+ case kExprI64x2Shl:
+ case kExprI64x2ShrS:
+ case kExprI64x2ShrU:
case kExprI32x4Shl:
case kExprI32x4ShrS:
case kExprI32x4ShrU:
@@ -2631,16 +2771,19 @@ class WasmFullDecoder : public WasmDecoder<validate> {
uint32_t len = 0;
ValueType ret_type;
FunctionSig* sig = WasmOpcodes::Signature(opcode);
- if (sig != nullptr) {
- MachineType memtype;
- switch (opcode) {
+ if (!VALIDATE(sig != nullptr)) {
+ this->error("invalid atomic opcode");
+ return 0;
+ }
+ MachineType memtype;
+ switch (opcode) {
#define CASE_ATOMIC_STORE_OP(Name, Type) \
case kExpr##Name: { \
memtype = MachineType::Type(); \
ret_type = kWasmStmt; \
break; \
}
- ATOMIC_STORE_OP_LIST(CASE_ATOMIC_STORE_OP)
+ ATOMIC_STORE_OP_LIST(CASE_ATOMIC_STORE_OP)
#undef CASE_ATOMIC_OP
#define CASE_ATOMIC_OP(Name, Type) \
case kExpr##Name: { \
@@ -2648,22 +2791,28 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ret_type = GetReturnType(sig); \
break; \
}
- ATOMIC_OP_LIST(CASE_ATOMIC_OP)
+ ATOMIC_OP_LIST(CASE_ATOMIC_OP)
#undef CASE_ATOMIC_OP
- default:
- this->error("invalid atomic opcode");
+ case kExprAtomicFence: {
+ byte zero = this->template read_u8<validate>(this->pc_ + 2, "zero");
+ if (!VALIDATE(zero == 0)) {
+ this->error(this->pc_ + 2, "invalid atomic operand");
return 0;
+ }
+ CALL_INTERFACE_IF_REACHABLE(AtomicFence);
+ return 1;
}
- MemoryAccessImmediate<validate> imm(
- this, this->pc_ + 1, ElementSizeLog2Of(memtype.representation()));
- len += imm.length;
- auto args = PopArgs(sig);
- auto result = ret_type == kWasmStmt ? nullptr : Push(GetReturnType(sig));
- CALL_INTERFACE_IF_REACHABLE(AtomicOp, opcode, VectorOf(args), imm,
- result);
- } else {
- this->error("invalid atomic opcode");
- }
+ default:
+ this->error("invalid atomic opcode");
+ return 0;
+ }
+ if (!CheckHasSharedMemory()) return 0;
+ MemoryAccessImmediate<validate> imm(
+ this, this->pc_ + 1, ElementSizeLog2Of(memtype.representation()));
+ len += imm.length;
+ auto args = PopArgs(sig);
+ auto result = ret_type == kWasmStmt ? nullptr : Push(GetReturnType(sig));
+ CALL_INTERFACE_IF_REACHABLE(AtomicOp, opcode, VectorOf(args), imm, result);
return len;
}
@@ -2823,8 +2972,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
V8_INLINE Value Pop(int index, ValueType expected) {
auto val = Pop();
- if (!VALIDATE(ValueTypes::IsSubType(expected, val.type) ||
- val.type == kWasmVar || expected == kWasmVar)) {
+ if (!VALIDATE(ValueTypes::IsSubType(val.type, expected) ||
+ val.type == kWasmBottom || expected == kWasmBottom)) {
this->errorf(val.pc, "%s[%d] expected type %s, found %s of type %s",
SafeOpcodeNameAt(this->pc_), index,
ValueTypes::TypeName(expected), SafeOpcodeNameAt(val.pc),
@@ -2849,11 +2998,26 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return val;
}
+ // Pops values from the stack, as defined by {merge}. Thereby we type-check
+ // unreachable merges. Afterwards the values are pushed again on the stack
+ // according to the signature in {merge}. This is done so follow-up validation
+ // is possible.
+ bool TypeCheckUnreachableMerge(Merge<Value>& merge, bool conditional_branch) {
+ int arity = merge.arity;
+ // For conditional branches, stack value '0' is the condition of the branch,
+ // and the result values start at index '1'.
+ int index_offset = conditional_branch ? 1 : 0;
+ for (int i = 0; i < arity; ++i) Pop(index_offset + i, merge[i].type);
+ // Push values of the correct type back on the stack.
+ for (int i = arity - 1; i >= 0; --i) Push(merge[i].type);
+ return this->ok();
+ }
+
int startrel(const byte* ptr) { return static_cast<int>(ptr - this->start_); }
void FallThruTo(Control* c) {
DCHECK_EQ(c, &control_.back());
- if (!TypeCheckFallThru(c)) return;
+ if (!TypeCheckFallThru()) return;
if (!c->reachable()) return;
if (!c->is_loop()) CALL_INTERFACE(FallThruTo, c);
@@ -2861,6 +3025,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
bool TypeCheckMergeValues(Control* c, Merge<Value>* merge) {
+ // This is a CHECK instead of a DCHECK because {validate} is a constexpr,
+ // and a CHECK makes the whole function unreachable.
+ static_assert(validate, "Call this function only within VALIDATE");
DCHECK(merge == &c->start_merge || merge == &c->end_merge);
DCHECK_GE(stack_.size(), c->stack_depth + merge->arity);
// The computation of {stack_values} is only valid if {merge->arity} is >0.
@@ -2870,108 +3037,121 @@ class WasmFullDecoder : public WasmDecoder<validate> {
for (uint32_t i = 0; i < merge->arity; ++i) {
Value& val = stack_values[i];
Value& old = (*merge)[i];
- if (ValueTypes::IsSubType(old.type, val.type)) continue;
- // If {val.type} is polymorphic, which results from unreachable, make
- // it more specific by using the merge value's expected type.
- // If it is not polymorphic, this is a type error.
- if (!VALIDATE(val.type == kWasmVar)) {
+ if (!ValueTypes::IsSubType(val.type, old.type)) {
this->errorf(this->pc_, "type error in merge[%u] (expected %s, got %s)",
i, ValueTypes::TypeName(old.type),
ValueTypes::TypeName(val.type));
return false;
}
- val.type = old.type;
}
return true;
}
- bool TypeCheckFallThru(Control* c) {
- DCHECK_EQ(c, &control_.back());
- if (!validate) return true;
- uint32_t expected = c->end_merge.arity;
- DCHECK_GE(stack_.size(), c->stack_depth);
- uint32_t actual = static_cast<uint32_t>(stack_.size()) - c->stack_depth;
- // Fallthrus must match the arity of the control exactly.
- if (!InsertUnreachablesIfNecessary(expected, actual) || actual > expected) {
+ bool TypeCheckFallThru() {
+ Control& c = control_.back();
+ if (V8_LIKELY(c.reachable())) {
+ // We only do type-checking here. This is only needed during validation.
+ if (!validate) return true;
+
+ uint32_t expected = c.end_merge.arity;
+ DCHECK_GE(stack_.size(), c.stack_depth);
+ uint32_t actual = static_cast<uint32_t>(stack_.size()) - c.stack_depth;
+ // Fallthrus must match the arity of the control exactly.
+ if (actual != expected) {
+ this->errorf(
+ this->pc_,
+ "expected %u elements on the stack for fallthru to @%d, found %u",
+ expected, startrel(c.pc), actual);
+ return false;
+ }
+ if (expected == 0) return true; // Fast path.
+
+ return TypeCheckMergeValues(&c, &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 (available > arity) {
this->errorf(
this->pc_,
"expected %u elements on the stack for fallthru to @%d, found %u",
- expected, startrel(c->pc), actual);
+ arity, startrel(c.pc), available);
return false;
}
- if (expected == 0) return true; // Fast path.
-
- return TypeCheckMergeValues(c, &c->end_merge);
+ // Pop all values from the stack for type checking of existing stack
+ // values.
+ return TypeCheckUnreachableMerge(merge, false);
}
- bool TypeCheckBranch(Control* c) {
- // Branches must have at least the number of values expected; can have more.
- uint32_t expected = c->br_merge()->arity;
- if (expected == 0) return true; // Fast path.
- DCHECK_GE(stack_.size(), control_.back().stack_depth);
- uint32_t actual =
- static_cast<uint32_t>(stack_.size()) - control_.back().stack_depth;
- if (!InsertUnreachablesIfNecessary(expected, actual)) {
- this->errorf(this->pc_,
- "expected %u elements on the stack for br to @%d, found %u",
- expected, startrel(c->pc), actual);
- return false;
+ enum TypeCheckBranchResult {
+ kReachableBranch,
+ kUnreachableBranch,
+ kInvalidStack,
+ };
+
+ TypeCheckBranchResult TypeCheckBranch(Control* c, bool conditional_branch) {
+ 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.
+ DCHECK_GE(stack_.size(), control_.back().stack_depth);
+ uint32_t actual =
+ static_cast<uint32_t>(stack_.size()) - control_.back().stack_depth;
+ if (expected > actual) {
+ this->errorf(
+ this->pc_,
+ "expected %u elements on the stack for br to @%d, found %u",
+ expected, startrel(c->pc), actual);
+ return kInvalidStack;
+ }
+ return TypeCheckMergeValues(c, c->br_merge()) ? kReachableBranch
+ : kInvalidStack;
}
- return TypeCheckMergeValues(c, c->br_merge());
+
+ return TypeCheckUnreachableMerge(*c->br_merge(), conditional_branch)
+ ? 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.
- uint32_t num_returns = static_cast<uint32_t>(this->sig_->return_count());
- DCHECK_GE(stack_.size(), control_.back().stack_depth);
- uint32_t actual =
- static_cast<uint32_t>(stack_.size()) - control_.back().stack_depth;
- if (!InsertUnreachablesIfNecessary(num_returns, actual)) {
+ int num_available =
+ static_cast<int>(stack_.size()) - control_.back().stack_depth;
+ if (num_available < num_returns) {
this->errorf(this->pc_,
"expected %u elements on the stack for return, found %u",
- num_returns, actual);
+ num_returns, num_available);
return false;
}
// Typecheck the topmost {num_returns} values on the stack.
- if (num_returns == 0) return true;
// This line requires num_returns > 0.
Value* stack_values = &*(stack_.end() - num_returns);
- for (uint32_t i = 0; i < num_returns; ++i) {
+ for (int i = 0; i < num_returns; ++i) {
auto& val = stack_values[i];
ValueType expected_type = this->sig_->GetReturn(i);
- if (ValueTypes::IsSubType(expected_type, val.type)) continue;
- // If {val.type} is polymorphic, which results from unreachable,
- // make it more specific by using the return's expected type.
- // If it is not polymorphic, this is a type error.
- if (!VALIDATE(val.type == kWasmVar)) {
+ if (!ValueTypes::IsSubType(val.type, expected_type)) {
this->errorf(this->pc_,
"type error in return[%u] (expected %s, got %s)", i,
ValueTypes::TypeName(expected_type),
ValueTypes::TypeName(val.type));
return false;
}
- val.type = expected_type;
- }
- return true;
- }
-
- inline bool InsertUnreachablesIfNecessary(uint32_t expected,
- uint32_t actual) {
- if (V8_LIKELY(actual >= expected)) {
- return true; // enough actual values are there.
- }
- if (!VALIDATE(control_.back().unreachable())) {
- // There aren't enough values on the stack.
- return false;
}
- // A slow path. When the actual number of values on the stack is less
- // than the expected number of values and the current control is
- // unreachable, insert unreachable values below the actual values.
- // This simplifies {TypeCheckMergeValues}.
- auto pos = stack_.begin() + (stack_.size() - actual);
- stack_.insert(pos, expected - actual, UnreachableValue(this->pc_));
return true;
}