summaryrefslogtreecommitdiff
path: root/chromium/v8/src/wasm/function-body-decoder-impl.h
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/v8/src/wasm/function-body-decoder-impl.h
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/v8/src/wasm/function-body-decoder-impl.h')
-rw-r--r--chromium/v8/src/wasm/function-body-decoder-impl.h2667
1 files changed, 1481 insertions, 1186 deletions
diff --git a/chromium/v8/src/wasm/function-body-decoder-impl.h b/chromium/v8/src/wasm/function-body-decoder-impl.h
index 48b804a3a92..d038a7c8d52 100644
--- a/chromium/v8/src/wasm/function-body-decoder-impl.h
+++ b/chromium/v8/src/wasm/function-body-decoder-impl.h
@@ -18,6 +18,7 @@
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
+#include "src/wasm/wasm-subtyping.h"
namespace v8 {
namespace internal {
@@ -42,7 +43,7 @@ struct WasmException;
}())
#define CHECK_PROTOTYPE_OPCODE_GEN(feat, opt_break) \
- DCHECK(!this->module_ || this->module_->origin == kWasmOrigin); \
+ DCHECK(this->module_->origin == kWasmOrigin); \
if (!this->enabled_.has_##feat()) { \
this->error("Invalid opcode (enable with --experimental-wasm-" #feat ")"); \
opt_break \
@@ -128,6 +129,138 @@ struct WasmException;
V(I64AtomicStore16U, Uint16) \
V(I64AtomicStore32U, Uint32)
+namespace value_type_reader {
+
+// Read a value type starting at address 'pc' in 'decoder'.
+// No bytes are consumed. The result is written into the 'result' parameter.
+// Returns the amount of bytes read, or 0 if decoding failed.
+// Registers an error if the type opcode is invalid iff validate is set.
+template <Decoder::ValidateFlag validate>
+ValueType read_value_type(Decoder* decoder, const byte* pc,
+ uint32_t* const length, const WasmFeatures& enabled) {
+ *length = 1;
+ byte val = decoder->read_u8<validate>(pc, "value type opcode");
+ if (decoder->failed()) {
+ return kWasmBottom;
+ }
+
+ ValueTypeCode code = static_cast<ValueTypeCode>(val);
+
+#define REF_TYPE_CASE(heap_type, nullable, feature) \
+ case kLocal##heap_type##Ref: { \
+ ValueType result = ValueType::Ref(kHeap##heap_type, nullable); \
+ if (enabled.has_##feature()) { \
+ return result; \
+ } \
+ decoder->errorf( \
+ pc, "invalid value type '%s', enable with --experimental-wasm-%s", \
+ result.type_name().c_str(), #feature); \
+ return kWasmBottom; \
+ }
+
+ switch (code) {
+ REF_TYPE_CASE(Func, kNullable, reftypes)
+ REF_TYPE_CASE(Extern, kNullable, reftypes)
+ REF_TYPE_CASE(Eq, kNullable, gc)
+ REF_TYPE_CASE(Exn, kNullable, eh)
+ case kLocalI32:
+ return kWasmI32;
+ case kLocalI64:
+ return kWasmI64;
+ case kLocalF32:
+ return kWasmF32;
+ case kLocalF64:
+ return kWasmF64;
+ case kLocalRef:
+ case kLocalOptRef: {
+ // Set length for the macro-defined cases:
+ *length += 1;
+ Nullability nullability = code == kLocalOptRef ? kNullable : kNonNullable;
+ uint8_t heap_index = decoder->read_u8<validate>(pc + 1, "heap type");
+ switch (static_cast<ValueTypeCode>(heap_index)) {
+ REF_TYPE_CASE(Func, nullability, typed_funcref)
+ REF_TYPE_CASE(Extern, nullability, typed_funcref)
+ REF_TYPE_CASE(Eq, nullability, gc)
+ REF_TYPE_CASE(Exn, nullability, eh)
+ default:
+ uint32_t type_index =
+ decoder->read_u32v<validate>(pc + 1, length, "type index");
+ *length += 1;
+ if (!enabled.has_gc()) {
+ decoder->error(
+ pc,
+ "invalid value type '(ref [null] (type $t))', enable with "
+ "--experimental-wasm-typed-gc");
+ return kWasmBottom;
+ }
+
+ if (!VALIDATE(type_index < kV8MaxWasmTypes)) {
+ decoder->errorf(pc + 1,
+ "Type index %u is greater than the maximum "
+ "number %zu of type definitions supported by V8",
+ type_index, kV8MaxWasmTypes);
+ return kWasmBottom;
+ }
+ return ValueType::Ref(static_cast<HeapType>(type_index), nullability);
+ }
+ decoder->errorf(
+ pc,
+ "invalid value type '(ref%s $t)', enable with --experimental-wasm-gc",
+ nullability ? " null" : "");
+ return kWasmBottom;
+ }
+#undef REF_TYPE_CASE
+ case kLocalRtt:
+ if (enabled.has_gc()) {
+ uint32_t depth_length;
+ uint32_t depth =
+ decoder->read_u32v<validate>(pc + 1, &depth_length, "depth");
+ // TODO(7748): Introduce a proper limit.
+ const uint32_t kMaxRttSubtypingDepth = 7;
+ if (!VALIDATE(depth <= kMaxRttSubtypingDepth)) {
+ decoder->errorf(pc,
+ "subtyping depth %u is greater than the maximum "
+ "depth %u supported by V8",
+ depth, kMaxRttSubtypingDepth);
+ return kWasmBottom;
+ }
+ uint32_t type_index = decoder->read_u32v<validate>(
+ pc + 1 + depth_length, length, "type index");
+ if (!VALIDATE(type_index < kV8MaxWasmTypes)) {
+ decoder->errorf(pc,
+ "Type index %u is greater than the maximum "
+ "number %zu of type definitions supported by V8",
+ type_index, kV8MaxWasmTypes);
+ return kWasmBottom;
+ }
+ *length += 1 + depth_length;
+ return ValueType::Rtt(static_cast<HeapType>(type_index),
+ static_cast<uint8_t>(depth));
+ }
+ decoder->error(
+ pc, "invalid value type 'rtt', enable with --experimental-wasm-gc");
+ return kWasmBottom;
+ case kLocalS128:
+ if (enabled.has_simd()) {
+ return kWasmS128;
+ }
+ decoder->error(
+ pc,
+ "invalid value type 'Simd128', enable with --experimental-wasm-simd");
+ return kWasmBottom;
+ case kLocalVoid:
+ case kLocalI8:
+ case kLocalI16:
+ // Although these types are included in ValueType, they are technically
+ // not value types and are only used in specific contexts. The caller of
+ // this function is responsible to check for them separately.
+ break;
+ }
+ // Malformed modules specifying invalid types can get here.
+ return kWasmBottom;
+}
+} // namespace value_type_reader
+
// Helpers for decoding different kinds of immediates which follow bytecodes.
template <Decoder::ValidateFlag validate>
struct LocalIndexImmediate {
@@ -174,7 +307,9 @@ struct ImmF32Immediate {
float value;
uint32_t length = 4;
inline ImmF32Immediate(Decoder* decoder, const byte* pc) {
- // Avoid bit_cast because it might not preserve the signalling bit of a NaN.
+ // We can't use bit_cast here because calling any helper function that
+ // returns a float would potentially flip NaN bits per C++ semantics, so we
+ // have to inline the memcpy call directly.
uint32_t tmp = decoder->read_u32<validate>(pc + 1, "immf32");
memcpy(&value, &tmp, sizeof(value));
}
@@ -192,6 +327,17 @@ struct ImmF64Immediate {
};
template <Decoder::ValidateFlag validate>
+struct RefNullImmediate {
+ ValueType type;
+ uint32_t length = 1;
+ inline RefNullImmediate(const WasmFeatures& enabled, Decoder* decoder,
+ const byte* pc) {
+ type = value_type_reader::read_value_type<validate>(decoder, pc + 1,
+ &length, enabled);
+ }
+};
+
+template <Decoder::ValidateFlag validate>
struct GlobalIndexImmediate {
uint32_t index;
ValueType type = kWasmStmt;
@@ -203,135 +349,6 @@ struct GlobalIndexImmediate {
}
};
-namespace value_type_reader {
-
-// Read a value type starting at address 'pc' in 'decoder'.
-// No bytes are consumed. The result is written into the 'result' parameter.
-// Returns the amount of bytes read, or 0 if decoding failed.
-// Registers an error if the type opcode is invalid iff validate is set.
-template <Decoder::ValidateFlag validate>
-uint32_t read_value_type(Decoder* decoder, const byte* pc, ValueType* result,
- const WasmFeatures& enabled) {
- byte val = decoder->read_u8<validate>(pc, "value type opcode");
- if (decoder->failed()) return 0;
-
- ValueTypeCode code = static_cast<ValueTypeCode>(val);
- switch (code) {
- case kLocalI32:
- *result = kWasmI32;
- return 1;
- case kLocalI64:
- *result = kWasmI64;
- return 1;
- case kLocalF32:
- *result = kWasmF32;
- return 1;
- case kLocalF64:
- *result = kWasmF64;
- return 1;
- case kLocalAnyRef:
- if (enabled.has_anyref()) {
- *result = kWasmAnyRef;
- return 1;
- }
- decoder->error(pc,
- "invalid value type 'anyref', enable with "
- "--experimental-wasm-anyref");
- return 0;
- case kLocalFuncRef:
- if (enabled.has_anyref()) {
- *result = kWasmFuncRef;
- return 1;
- }
- decoder->error(pc,
- "invalid value type 'funcref', enable with "
- "--experimental-wasm-anyref");
- return 0;
- case kLocalNullRef:
- if (enabled.has_anyref()) {
- *result = kWasmNullRef;
- return 1;
- }
- decoder->error(pc,
- "invalid value type 'nullref', enable with "
- "--experimental-wasm-anyref");
- return 0;
- case kLocalExnRef:
- if (enabled.has_eh()) {
- *result = kWasmExnRef;
- return 1;
- }
- decoder->error(pc,
- "invalid value type 'exception ref', enable with "
- "--experimental-wasm-eh");
- return 0;
- case kLocalRef:
- if (enabled.has_gc()) {
- uint32_t length;
- uint32_t type_index =
- decoder->read_u32v<validate>(pc + 1, &length, "type index");
- *result = ValueType(ValueType::kRef, type_index);
- return length + 1;
- }
- decoder->error(pc,
- "invalid value type 'ref', enable with "
- "--experimental-wasm-gc");
- return 0;
- case kLocalOptRef:
- if (enabled.has_gc()) {
- uint32_t length;
- uint32_t type_index =
- decoder->read_u32v<validate>(pc + 1, &length, "type index");
- *result = ValueType(ValueType::kOptRef, type_index);
- return length + 1;
- }
- decoder->error(pc,
- "invalid value type 'optref', enable with "
- "--experimental-wasm-gc");
- return 0;
- case kLocalEqRef:
- if (enabled.has_gc()) {
- *result = kWasmEqRef;
- return 1;
- }
- decoder->error(pc,
- "invalid value type 'eqref', enable with "
- "--experimental-wasm-simd");
- return 0;
- case kLocalI31Ref:
- if (enabled.has_gc()) {
- // TODO(7748): Implement
- decoder->error(pc, "'i31ref' is unimplemented");
- }
- decoder->error(pc,
- "invalid value type 'i31ref', enable with "
- "--experimental-wasm-simd");
- return 0;
- case kLocalRttRef:
- if (enabled.has_gc()) {
- // TODO(7748): Implement
- decoder->error(pc, "'rttref' is unimplemented");
- }
- decoder->error(pc,
- "invalid value type 'rttref', enable with "
- "--experimental-wasm-simd");
- return 0;
- case kLocalS128:
- if (enabled.has_simd()) {
- *result = kWasmS128;
- return 1;
- }
- decoder->error(pc,
- "invalid value type 'Simd128', enable with "
- "--experimental-wasm-simd");
- return 0;
- default:
- *result = kWasmBottom;
- return 0;
- }
-}
-} // namespace value_type_reader
-
template <Decoder::ValidateFlag validate>
struct SelectTypeImmediate {
uint32_t length;
@@ -346,10 +363,11 @@ struct SelectTypeImmediate {
pc + 1, "Invalid number of types. Select accepts exactly one type");
return;
}
- uint32_t type_length = value_type_reader::read_value_type<validate>(
- decoder, pc + length + 1, &type, enabled);
+ uint32_t type_length;
+ type = value_type_reader::read_value_type<validate>(
+ decoder, pc + length + 1, &type_length, enabled);
length += type_length;
- if (type_length == 0) {
+ if (type == kWasmBottom) {
decoder->error(pc + 1, "invalid select type");
}
}
@@ -368,9 +386,9 @@ struct BlockTypeImmediate {
// 1st case: void block. Struct fields stay at default values.
return;
}
- length = value_type_reader::read_value_type<validate>(decoder, pc + 1,
- &type, enabled);
- if (length > 0) {
+ type = value_type_reader::read_value_type<validate>(decoder, pc + 1,
+ &length, enabled);
+ if (type != kWasmBottom) {
// 2nd case: block with val type immediate.
return;
}
@@ -497,6 +515,17 @@ struct ArrayIndexImmediate {
}
};
+// TODO(jkummerow): Make this a superclass of StructIndexImmediate and
+// ArrayIndexImmediate? Maybe even FunctionIndexImmediate too?
+template <Decoder::ValidateFlag validate>
+struct TypeIndexImmediate {
+ uint32_t index = 0;
+ uint32_t length = 0;
+ inline TypeIndexImmediate(Decoder* decoder, const byte* pc) {
+ index = decoder->read_u32v<validate>(pc, &length, "type index");
+ }
+};
+
template <Decoder::ValidateFlag validate>
struct CallIndirectImmediate {
uint32_t table_index;
@@ -509,7 +538,7 @@ struct CallIndirectImmediate {
sig_index = decoder->read_u32v<validate>(pc + 1, &len, "signature index");
TableIndexImmediate<validate> table(decoder, pc + len);
if (!VALIDATE((table.index == 0 && table.length == 1) ||
- enabled.has_anyref())) {
+ enabled.has_reftypes())) {
decoder->errorf(pc + 1 + len, "expected table index 0, found %u",
table.index);
}
@@ -733,7 +762,7 @@ struct Merge {
// Reachability::kReachable.
bool reached;
- Merge(bool reached = false) : reached(reached) {}
+ explicit Merge(bool reached = false) : reached(reached) {}
Value& operator[](uint32_t i) {
DCHECK_GT(arity, i);
@@ -746,6 +775,7 @@ enum ControlKind : uint8_t {
kControlIfElse,
kControlBlock,
kControlLoop,
+ kControlLet,
kControlTry,
kControlTryCatch
};
@@ -763,6 +793,7 @@ enum Reachability : uint8_t {
template <typename Value>
struct ControlBase {
ControlKind kind = kControlBlock;
+ uint32_t locals_count = 0;
uint32_t stack_depth = 0; // stack height at the beginning of the construct.
const uint8_t* pc = nullptr;
Reachability reachability = kReachable;
@@ -773,13 +804,16 @@ struct ControlBase {
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(ControlBase);
- ControlBase(ControlKind kind, uint32_t stack_depth, const uint8_t* pc,
- Reachability reachability)
+ ControlBase(ControlKind kind, uint32_t locals_count, uint32_t stack_depth,
+ const uint8_t* pc, Reachability reachability)
: kind(kind),
+ locals_count(locals_count),
stack_depth(stack_depth),
pc(pc),
reachability(reachability),
- start_merge(reachability == kReachable) {}
+ start_merge(reachability == kReachable) {
+ DCHECK(kind == kControlLet || locals_count == 0);
+ }
// Check whether the current block is reachable.
bool reachable() const { return reachability == kReachable; }
@@ -799,6 +833,7 @@ struct ControlBase {
bool is_onearmed_if() const { return kind == kControlIf; }
bool is_if_else() const { return kind == kControlIfElse; }
bool is_block() const { return kind == kControlBlock; }
+ bool is_let() const { return kind == kControlLet; }
bool is_loop() const { return kind == kControlLoop; }
bool is_incomplete_try() const { return kind == kControlTry; }
bool is_try_catch() const { return kind == kControlTryCatch; }
@@ -809,122 +844,120 @@ struct ControlBase {
}
};
-enum class LoadTransformationKind : uint8_t {
- kSplat,
- kExtend,
-};
-
// This is the list of callback functions that an interface for the
// WasmFullDecoder should implement.
// F(Name, args...)
-#define INTERFACE_FUNCTIONS(F) \
- /* General: */ \
- F(StartFunction) \
- F(StartFunctionBody, Control* block) \
- F(FinishFunction) \
- F(OnFirstError) \
- F(NextInstruction, WasmOpcode) \
- /* Control: */ \
- F(Block, Control* block) \
- F(Loop, Control* block) \
- F(Try, Control* block) \
- F(Catch, Control* block, Value* exception) \
- F(If, const Value& cond, Control* if_block) \
- F(FallThruTo, Control* c) \
- F(PopControl, Control* block) \
- F(EndControl, Control* block) \
- /* Instructions: */ \
- F(UnOp, WasmOpcode opcode, const Value& value, Value* result) \
- F(BinOp, WasmOpcode opcode, const Value& lhs, const Value& rhs, \
- Value* result) \
- F(I32Const, Value* result, int32_t value) \
- F(I64Const, Value* result, int64_t value) \
- F(F32Const, Value* result, float value) \
- F(F64Const, Value* result, double value) \
- F(RefNull, Value* result) \
- F(RefFunc, uint32_t function_index, Value* result) \
- F(RefAsNonNull, const Value& arg, Value* result) \
- F(Drop, const Value& value) \
- F(DoReturn, Vector<Value> values) \
- F(LocalGet, Value* result, const LocalIndexImmediate<validate>& imm) \
- F(LocalSet, const Value& value, const LocalIndexImmediate<validate>& imm) \
- F(LocalTee, const Value& value, Value* result, \
- const LocalIndexImmediate<validate>& imm) \
- F(GlobalGet, Value* result, const GlobalIndexImmediate<validate>& imm) \
- F(GlobalSet, const Value& value, const GlobalIndexImmediate<validate>& imm) \
- F(TableGet, const Value& index, Value* result, \
- const TableIndexImmediate<validate>& imm) \
- 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, \
- Value* result) \
- F(Br, Control* target) \
- F(BrIf, const Value& cond, uint32_t depth) \
- F(BrTable, const BranchTableImmediate<validate>& imm, const Value& key) \
- F(Else, Control* if_block) \
- F(LoadMem, LoadType type, const MemoryAccessImmediate<validate>& imm, \
- const Value& index, Value* result) \
- F(LoadTransform, LoadType type, LoadTransformationKind transform, \
- MemoryAccessImmediate<validate>& imm, const Value& index, Value* result) \
- F(StoreMem, StoreType type, const MemoryAccessImmediate<validate>& imm, \
- const Value& index, const Value& value) \
- F(CurrentMemoryPages, Value* result) \
- F(MemoryGrow, const Value& value, Value* result) \
- F(CallDirect, const CallFunctionImmediate<validate>& imm, \
- const Value args[], Value returns[]) \
- F(CallIndirect, const Value& index, \
- const CallIndirectImmediate<validate>& imm, const Value args[], \
- Value returns[]) \
- F(ReturnCall, const CallFunctionImmediate<validate>& imm, \
- const Value args[]) \
- F(ReturnCallIndirect, const Value& index, \
- const CallIndirectImmediate<validate>& imm, const Value args[]) \
- F(BrOnNull, const Value& ref_object, uint32_t depth) \
- F(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \
- F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
- const Vector<Value> inputs, Value* result) \
- F(Simd8x16ShuffleOp, const Simd8x16ShuffleImmediate<validate>& imm, \
- const Value& input0, const Value& input1, Value* result) \
- F(Throw, const ExceptionIndexImmediate<validate>& imm, \
- const Vector<Value>& args) \
- F(Rethrow, const Value& exception) \
- F(BrOnException, const Value& exception, \
- const ExceptionIndexImmediate<validate>& imm, uint32_t depth, \
- 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) \
- F(MemoryCopy, const MemoryCopyImmediate<validate>& imm, const Value& dst, \
- const Value& src, const Value& size) \
- F(MemoryFill, const MemoryIndexImmediate<validate>& imm, const Value& dst, \
- const Value& value, const Value& size) \
- F(TableInit, const TableInitImmediate<validate>& imm, Vector<Value> args) \
- F(ElemDrop, const ElemDropImmediate<validate>& imm) \
- F(TableCopy, const TableCopyImmediate<validate>& imm, Vector<Value> args) \
- F(TableGrow, const TableIndexImmediate<validate>& imm, const Value& value, \
- const Value& delta, Value* result) \
- F(TableSize, const TableIndexImmediate<validate>& imm, Value* result) \
- F(TableFill, const TableIndexImmediate<validate>& imm, const Value& start, \
- const Value& value, const Value& count) \
- F(StructNew, const StructIndexImmediate<validate>& imm, const Value args[], \
- Value* result) \
- F(StructGet, const Value& struct_object, \
- const FieldIndexImmediate<validate>& field, Value* result) \
- F(StructSet, const Value& struct_object, \
- const FieldIndexImmediate<validate>& field, const Value& field_value) \
- F(ArrayNew, const ArrayIndexImmediate<validate>& imm, const Value& length, \
- const Value& initial_value, Value* result) \
- F(ArrayGet, const Value& array_obj, \
- const ArrayIndexImmediate<validate>& imm, const Value& index, \
- Value* result) \
- F(ArraySet, const Value& array_obj, \
- const ArrayIndexImmediate<validate>& imm, const Value& index, \
- const Value& value) \
- F(ArrayLen, const Value& array_obj, Value* result) \
+#define INTERFACE_FUNCTIONS(F) \
+ /* General: */ \
+ F(StartFunction) \
+ F(StartFunctionBody, Control* block) \
+ F(FinishFunction) \
+ F(OnFirstError) \
+ F(NextInstruction, WasmOpcode) \
+ /* Control: */ \
+ F(Block, Control* block) \
+ F(Loop, Control* block) \
+ F(Try, Control* block) \
+ F(Catch, Control* block, Value* exception) \
+ F(If, const Value& cond, Control* if_block) \
+ F(FallThruTo, Control* c) \
+ F(PopControl, Control* block) \
+ F(EndControl, Control* block) \
+ /* Instructions: */ \
+ F(UnOp, WasmOpcode opcode, const Value& value, Value* result) \
+ F(BinOp, WasmOpcode opcode, const Value& lhs, const Value& rhs, \
+ Value* result) \
+ F(I32Const, Value* result, int32_t value) \
+ F(I64Const, Value* result, int64_t value) \
+ F(F32Const, Value* result, float value) \
+ F(F64Const, Value* result, double value) \
+ F(RefNull, Value* result) \
+ F(RefFunc, uint32_t function_index, Value* result) \
+ F(RefAsNonNull, const Value& arg, Value* result) \
+ F(Drop, const Value& value) \
+ F(DoReturn, Vector<Value> values) \
+ F(LocalGet, Value* result, const LocalIndexImmediate<validate>& imm) \
+ F(LocalSet, const Value& value, const LocalIndexImmediate<validate>& imm) \
+ F(LocalTee, const Value& value, Value* result, \
+ const LocalIndexImmediate<validate>& imm) \
+ F(AllocateLocals, Vector<Value> local_values) \
+ F(DeallocateLocals, uint32_t count) \
+ F(GlobalGet, Value* result, const GlobalIndexImmediate<validate>& imm) \
+ F(GlobalSet, const Value& value, const GlobalIndexImmediate<validate>& imm) \
+ F(TableGet, const Value& index, Value* result, \
+ const TableIndexImmediate<validate>& imm) \
+ 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, \
+ Value* result) \
+ F(Br, Control* target) \
+ F(BrIf, const Value& cond, uint32_t depth) \
+ F(BrTable, const BranchTableImmediate<validate>& imm, const Value& key) \
+ F(Else, Control* if_block) \
+ F(LoadMem, LoadType type, const MemoryAccessImmediate<validate>& imm, \
+ const Value& index, Value* result) \
+ F(LoadTransform, LoadType type, LoadTransformationKind transform, \
+ MemoryAccessImmediate<validate>& imm, const Value& index, Value* result) \
+ F(StoreMem, StoreType type, const MemoryAccessImmediate<validate>& imm, \
+ const Value& index, const Value& value) \
+ F(CurrentMemoryPages, Value* result) \
+ F(MemoryGrow, const Value& value, Value* result) \
+ F(CallDirect, const CallFunctionImmediate<validate>& imm, \
+ const Value args[], Value returns[]) \
+ F(CallIndirect, const Value& index, \
+ const CallIndirectImmediate<validate>& imm, const Value args[], \
+ Value returns[]) \
+ F(ReturnCall, const CallFunctionImmediate<validate>& imm, \
+ const Value args[]) \
+ F(ReturnCallIndirect, const Value& index, \
+ const CallIndirectImmediate<validate>& imm, const Value args[]) \
+ F(BrOnNull, const Value& ref_object, uint32_t depth) \
+ F(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \
+ F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
+ const Vector<Value> inputs, Value* result) \
+ F(Simd8x16ShuffleOp, const Simd8x16ShuffleImmediate<validate>& imm, \
+ const Value& input0, const Value& input1, Value* result) \
+ F(Throw, const ExceptionIndexImmediate<validate>& imm, \
+ const Vector<Value>& args) \
+ F(Rethrow, const Value& exception) \
+ F(BrOnException, const Value& exception, \
+ const ExceptionIndexImmediate<validate>& imm, uint32_t depth, \
+ 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) \
+ F(MemoryCopy, const MemoryCopyImmediate<validate>& imm, const Value& dst, \
+ const Value& src, const Value& size) \
+ F(MemoryFill, const MemoryIndexImmediate<validate>& imm, const Value& dst, \
+ const Value& value, const Value& size) \
+ F(TableInit, const TableInitImmediate<validate>& imm, Vector<Value> args) \
+ F(ElemDrop, const ElemDropImmediate<validate>& imm) \
+ F(TableCopy, const TableCopyImmediate<validate>& imm, Vector<Value> args) \
+ F(TableGrow, const TableIndexImmediate<validate>& imm, const Value& value, \
+ const Value& delta, Value* result) \
+ F(TableSize, const TableIndexImmediate<validate>& imm, Value* result) \
+ F(TableFill, const TableIndexImmediate<validate>& imm, const Value& start, \
+ const Value& value, const Value& count) \
+ F(StructNew, const StructIndexImmediate<validate>& imm, const Value args[], \
+ Value* result) \
+ F(StructGet, const Value& struct_object, \
+ const FieldIndexImmediate<validate>& field, bool is_signed, Value* result) \
+ F(StructSet, const Value& struct_object, \
+ const FieldIndexImmediate<validate>& field, const Value& field_value) \
+ F(ArrayNew, const ArrayIndexImmediate<validate>& imm, const Value& length, \
+ const Value& initial_value, Value* result) \
+ F(ArrayGet, const Value& array_obj, \
+ const ArrayIndexImmediate<validate>& imm, const Value& index, \
+ bool is_signed, Value* result) \
+ F(ArraySet, const Value& array_obj, \
+ const ArrayIndexImmediate<validate>& imm, const Value& index, \
+ const Value& value) \
+ F(ArrayLen, const Value& array_obj, Value* result) \
+ F(RttCanon, const TypeIndexImmediate<validate>& imm, Value* result) \
F(PassThrough, const Value& from, Value* to)
// Generic Wasm bytecode decoder with utilities for decoding immediates,
@@ -954,44 +987,81 @@ class WasmDecoder : public Decoder {
: static_cast<uint32_t>(local_types_->size());
}
- static bool DecodeLocals(const WasmFeatures& enabled, Decoder* decoder,
- const FunctionSig* sig,
- ZoneVector<ValueType>* type_list) {
- DCHECK_NOT_NULL(type_list);
- DCHECK_EQ(0, type_list->size());
- // Initialize from signature.
- if (sig != nullptr) {
- type_list->assign(sig->parameters().begin(), sig->parameters().end());
+ void InitializeLocalsFromSig() {
+ if (sig_ != nullptr) {
+ local_types_->assign(sig_->parameters().begin(),
+ sig_->parameters().end());
}
+ }
+
+ // Decodes local definitions in the current decoder.
+ // Returns true iff locals are found.
+ // Writes the total length of decoded locals in 'total_length'.
+ // If insert_postion is present, the decoded locals will be inserted into the
+ // 'local_types_' of this decoder. Otherwise, this function is used just to
+ // check validity and determine the encoding length of the locals in bytes.
+ // The decoder's pc is not advanced. If no locals are found (i.e., no
+ // compressed uint32 is found at pc), this will exit as 'false' and without an
+ // error.
+ bool DecodeLocals(const byte* pc, uint32_t* total_length,
+ const base::Optional<uint32_t> insert_position) {
+ DCHECK_NOT_NULL(local_types_);
+
+ uint32_t length;
+ *total_length = 0;
+
+ // The 'else' value is useless, we pass it for convenience.
+ ZoneVector<ValueType>::iterator insert_iterator =
+ insert_position.has_value()
+ ? local_types_->begin() + insert_position.value()
+ : local_types_->begin();
+
// Decode local declarations, if any.
- uint32_t entries = decoder->consume_u32v("local decls count");
- if (decoder->failed()) return false;
+ uint32_t entries = read_u32v<kValidate>(pc, &length, "local decls count");
+ if (failed()) {
+ error(pc + *total_length, "invalid local decls count");
+ return false;
+ }
+ *total_length += length;
TRACE("local decls count: %u\n", entries);
- while (entries-- > 0 && decoder->more()) {
- uint32_t count = decoder->consume_u32v("local count");
- if (decoder->failed()) return false;
- DCHECK_LE(type_list->size(), kV8MaxWasmFunctionLocals);
- if (count > kV8MaxWasmFunctionLocals - type_list->size()) {
- decoder->error(decoder->pc() - 1, "local count too large");
+ while (entries-- > 0) {
+ if (!more()) {
+ error(end(), "expected more local decls but reached end of input");
+ return false;
+ }
+ uint32_t count =
+ read_u32v<kValidate>(pc + *total_length, &length, "local count");
+ if (failed()) {
+ error(pc + *total_length, "invalid local count");
return false;
}
- ValueType type;
- uint32_t type_length = value_type_reader::read_value_type<validate>(
- decoder, decoder->pc(), &type, enabled);
- if (type_length == 0) {
- decoder->error(decoder->pc(), "invalid local type");
+ DCHECK_LE(local_types_->size(), kV8MaxWasmFunctionLocals);
+ if (count > kV8MaxWasmFunctionLocals - local_types_->size()) {
+ error(pc + *total_length, "local count too large");
return false;
}
- type_list->insert(type_list->end(), count, type);
- decoder->consume_bytes(type_length);
+ *total_length += length;
+
+ ValueType type = value_type_reader::read_value_type<kValidate>(
+ this, pc + *total_length, &length, enabled_);
+ if (type == kWasmBottom) {
+ error(pc + *total_length, "invalid local type");
+ return false;
+ }
+ *total_length += length;
+ if (insert_position.has_value()) {
+ // Move the insertion iterator to the end of the newly inserted locals.
+ insert_iterator =
+ local_types_->insert(insert_iterator, count, type) + count;
+ }
}
- DCHECK(decoder->ok());
+ DCHECK(ok());
return true;
}
- static BitVector* AnalyzeLoopAssignment(Decoder* decoder, const byte* pc,
+ static BitVector* AnalyzeLoopAssignment(WasmDecoder* decoder, const byte* pc,
uint32_t locals_count, Zone* zone) {
if (pc >= decoder->end()) return nullptr;
if (*pc != kExprLoop) return nullptr;
@@ -1055,11 +1125,17 @@ class WasmDecoder : public Decoder {
return true;
}
- inline bool Complete(const byte* pc, ExceptionIndexImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr &&
- imm.index < module_->exceptions.size())) {
+ inline bool Validate(const byte* pc, RefNullImmediate<validate>& imm) {
+ if (!VALIDATE(imm.type.is_nullable())) {
+ errorf(pc + 1, "ref.null does not exist for %s",
+ imm.type.type_name().c_str());
return false;
}
+ return true;
+ }
+
+ inline bool Complete(const byte* pc, ExceptionIndexImmediate<validate>& imm) {
+ if (!VALIDATE(imm.index < module_->exceptions.size())) return false;
imm.exception = &module_->exceptions[imm.index];
return true;
}
@@ -1073,7 +1149,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, GlobalIndexImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr && imm.index < module_->globals.size())) {
+ if (!VALIDATE(imm.index < module_->globals.size())) {
errorf(pc + 1, "invalid global index: %u", imm.index);
return false;
}
@@ -1083,9 +1159,7 @@ class WasmDecoder : public Decoder {
}
inline bool Complete(const byte* pc, StructIndexImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr && module_->has_struct(imm.index))) {
- return false;
- }
+ if (!VALIDATE(module_->has_struct(imm.index))) return false;
imm.struct_type = module_->struct_type(imm.index);
return true;
}
@@ -1104,9 +1178,7 @@ class WasmDecoder : public Decoder {
}
inline bool Complete(const byte* pc, ArrayIndexImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr && module_->has_array(imm.index))) {
- return false;
- }
+ if (!VALIDATE(module_->has_array(imm.index))) return false;
imm.array_type = module_->array_type(imm.index);
return true;
}
@@ -1117,6 +1189,15 @@ class WasmDecoder : public Decoder {
return false;
}
+ inline bool Validate(const byte* pc, TypeIndexImmediate<validate>& imm) {
+ if (!VALIDATE(module_ != nullptr && (module_->has_struct(imm.index) ||
+ module_->has_array(imm.index)))) {
+ errorf(pc, "invalid type index: %u", imm.index);
+ return false;
+ }
+ return true;
+ }
+
inline bool CanReturnCall(const FunctionSig* target_sig) {
if (target_sig == nullptr) return false;
size_t num_returns = sig_->return_count();
@@ -1128,11 +1209,11 @@ class WasmDecoder : public Decoder {
}
inline bool Complete(const byte* pc, CallFunctionImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr &&
- imm.index < module_->functions.size())) {
- return false;
- }
+ if (!VALIDATE(imm.index < module_->functions.size())) return false;
imm.sig = module_->functions[imm.index].sig;
+ if (imm.sig->return_count() > 1) {
+ this->detected_->Add(kFeature_mv);
+ }
return true;
}
@@ -1145,22 +1226,20 @@ class WasmDecoder : public Decoder {
}
inline bool Complete(const byte* pc, CallIndirectImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr &&
- module_->has_signature(imm.sig_index))) {
- return false;
- }
+ if (!VALIDATE(module_->has_signature(imm.sig_index))) return false;
imm.sig = module_->signature(imm.sig_index);
+ if (imm.sig->return_count() > 1) {
+ this->detected_->Add(kFeature_mv);
+ }
return true;
}
inline bool Validate(const byte* pc, CallIndirectImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr &&
- imm.table_index < module_->tables.size())) {
+ if (!VALIDATE(imm.table_index < module_->tables.size())) {
error("function table has to exist to execute call_indirect");
return false;
}
- if (!VALIDATE(module_ != nullptr &&
- module_->tables[imm.table_index].type == kWasmFuncRef)) {
+ if (!VALIDATE(module_->tables[imm.table_index].type == kWasmFuncRef)) {
error("table of call_indirect must be of type funcref");
return false;
}
@@ -1235,7 +1314,7 @@ class WasmDecoder : public Decoder {
max_lane = std::max(max_lane, imm.shuffle[i]);
}
// Shuffle indices must be in [0..31] for a 16 lane shuffle.
- if (!VALIDATE(max_lane <= 2 * kSimd128Size)) {
+ if (!VALIDATE(max_lane < 2 * kSimd128Size)) {
error(pc_ + 2, "invalid shuffle mask");
return false;
}
@@ -1244,24 +1323,24 @@ class WasmDecoder : public Decoder {
inline bool Complete(BlockTypeImmediate<validate>& imm) {
if (imm.type != kWasmBottom) return true;
- if (!VALIDATE(module_ && module_->has_signature(imm.sig_index))) {
- return false;
- }
+ if (!VALIDATE(module_->has_signature(imm.sig_index))) return false;
imm.sig = module_->signature(imm.sig_index);
+ if (imm.sig->return_count() > 1) {
+ this->detected_->Add(kFeature_mv);
+ }
return true;
}
inline bool Validate(BlockTypeImmediate<validate>& imm) {
if (!Complete(imm)) {
errorf(pc_, "block type index %u out of bounds (%zu types)",
- imm.sig_index, module_ ? module_->types.size() : 0);
+ imm.sig_index, module_->types.size());
return false;
}
return true;
}
inline bool Validate(const byte* pc, FunctionIndexImmediate<validate>& imm) {
- if (!module_) return true;
if (!VALIDATE(imm.index < module_->functions.size())) {
errorf(pc, "invalid function index: %u", imm.index);
return false;
@@ -1274,7 +1353,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, MemoryIndexImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr && module_->has_memory)) {
+ if (!VALIDATE(module_->has_memory)) {
errorf(pc + 1, "memory instruction with no memory");
return false;
}
@@ -1282,9 +1361,8 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(MemoryInitImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr &&
- imm.data_segment_index <
- module_->num_declared_data_segments)) {
+ if (!VALIDATE(imm.data_segment_index <
+ module_->num_declared_data_segments)) {
errorf(pc_ + 2, "invalid data segment index: %u", imm.data_segment_index);
return false;
}
@@ -1294,8 +1372,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(DataDropImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr &&
- imm.index < module_->num_declared_data_segments)) {
+ if (!VALIDATE(imm.index < module_->num_declared_data_segments)) {
errorf(pc_ + 2, "invalid data segment index: %u", imm.index);
return false;
}
@@ -1309,7 +1386,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, TableIndexImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr && imm.index < module_->tables.size())) {
+ if (!VALIDATE(imm.index < module_->tables.size())) {
errorf(pc, "invalid table index: %u", imm.index);
return false;
}
@@ -1317,8 +1394,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(TableInitImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr &&
- imm.elem_segment_index < module_->elem_segments.size())) {
+ if (!VALIDATE(imm.elem_segment_index < module_->elem_segments.size())) {
errorf(pc_ + 2, "invalid element segment index: %u",
imm.elem_segment_index);
return false;
@@ -1327,18 +1403,17 @@ class WasmDecoder : public Decoder {
return false;
}
ValueType elem_type = module_->elem_segments[imm.elem_segment_index].type;
- if (!VALIDATE(
- elem_type.IsSubTypeOf(module_->tables[imm.table.index].type))) {
+ if (!VALIDATE(IsSubtypeOf(elem_type, module_->tables[imm.table.index].type,
+ module_))) {
errorf(pc_ + 2, "table %u is not a super-type of %s", imm.table.index,
- elem_type.type_name());
+ elem_type.type_name().c_str());
return false;
}
return true;
}
inline bool Validate(ElemDropImmediate<validate>& imm) {
- if (!VALIDATE(module_ != nullptr &&
- imm.index < module_->elem_segments.size())) {
+ if (!VALIDATE(imm.index < module_->elem_segments.size())) {
errorf(pc_ + 2, "invalid element segment index: %u", imm.index);
return false;
}
@@ -1349,16 +1424,16 @@ class WasmDecoder : public Decoder {
if (!Validate(pc_ + 1, imm.table_src)) return false;
if (!Validate(pc_ + 2, imm.table_dst)) return false;
ValueType src_type = module_->tables[imm.table_src.index].type;
- if (!VALIDATE(
- src_type.IsSubTypeOf(module_->tables[imm.table_dst.index].type))) {
+ if (!VALIDATE(IsSubtypeOf(
+ src_type, module_->tables[imm.table_dst.index].type, module_))) {
errorf(pc_ + 2, "table %u is not a super-type of %s", imm.table_dst.index,
- src_type.type_name());
+ src_type.type_name().c_str());
return false;
}
return true;
}
- static uint32_t OpcodeLength(Decoder* decoder, const byte* pc) {
+ static uint32_t OpcodeLength(WasmDecoder* decoder, const byte* pc) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
switch (opcode) {
#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
@@ -1403,6 +1478,15 @@ class WasmDecoder : public Decoder {
return 1 + imm.length;
}
+ case kExprLet: {
+ BlockTypeImmediate<validate> imm(WasmFeatures::All(), decoder, pc);
+ uint32_t locals_length;
+ bool locals_result =
+ decoder->DecodeLocals(decoder->pc() + 1 + imm.length,
+ &locals_length, base::Optional<uint32_t>());
+ return 1 + imm.length + (locals_result ? locals_length : 0);
+ }
+
case kExprThrow: {
ExceptionIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
@@ -1442,6 +1526,10 @@ class WasmDecoder : public Decoder {
return 1 + imm.length;
}
case kExprRefNull: {
+ RefNullImmediate<validate> imm(WasmFeatures::All(), decoder, pc);
+ return 1 + imm.length;
+ }
+ case kExprRefIsNull: {
return 1;
}
case kExprRefFunc: {
@@ -1594,13 +1682,27 @@ class WasmDecoder : public Decoder {
BranchDepthImmediate<validate> imm(decoder, pc + 2);
return 2 + imm.length;
}
- case kExprRttGet:
+ case kExprRttCanon: {
+ // TODO(7748): Introduce "HeapTypeImmediate" and use it here.
+ TypeIndexImmediate<validate> heaptype(decoder, pc + 2);
+ return 2 + heaptype.length;
+ }
case kExprRttSub: {
- // TODO(7748): Impelement.
- UNIMPLEMENTED();
+ // TODO(7748): Implement.
+ decoder->error(pc, "rtt.sub not implemented yet");
+ return 2;
}
+ case kExprI31New:
+ case kExprI31GetS:
+ case kExprI31GetU:
+ case kExprRefTest:
+ case kExprRefCast:
+ return 2;
+
default:
+ // This is unreachable except for malformed modules.
+ decoder->error(pc, "invalid gc opcode");
return 2;
}
}
@@ -1609,7 +1711,8 @@ class WasmDecoder : public Decoder {
}
}
- std::pair<uint32_t, uint32_t> StackEffect(const byte* pc) {
+ // TODO(clemensb): This is only used by the interpreter; move there.
+ V8_EXPORT_PRIVATE std::pair<uint32_t, uint32_t> StackEffect(const byte* pc) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
// Handle "simple" opcodes with a fixed signature first.
const FunctionSig* sig = WasmOpcodes::Signature(opcode);
@@ -1631,6 +1734,7 @@ class WasmDecoder : public Decoder {
case kExprMemoryGrow:
case kExprRefAsNonNull:
case kExprBrOnNull:
+ case kExprRefIsNull:
return {1, 1};
case kExprLocalSet:
case kExprGlobalSet:
@@ -1682,6 +1786,9 @@ class WasmDecoder : public Decoder {
case kExprReturnCallIndirect:
case kExprUnreachable:
return {0, 0};
+ case kExprLet:
+ // TODO(7748): Implement
+ return {0, 0};
case kNumericPrefix:
case kAtomicPrefix:
case kSimdPrefix: {
@@ -1712,12 +1819,14 @@ class WasmDecoder : public Decoder {
};
#define CALL_INTERFACE(name, ...) interface_.name(this, ##__VA_ARGS__)
-#define CALL_INTERFACE_IF_REACHABLE(name, ...) \
- do { \
- DCHECK(!control_.empty()); \
- if (VALIDATE(this->ok()) && control_.back().reachable()) { \
- interface_.name(this, ##__VA_ARGS__); \
- } \
+#define CALL_INTERFACE_IF_REACHABLE(name, ...) \
+ do { \
+ DCHECK(!control_.empty()); \
+ DCHECK_EQ(current_code_reachable_, \
+ this->ok() && control_.back().reachable()); \
+ if (current_code_reachable_) { \
+ interface_.name(this, ##__VA_ARGS__); \
+ } \
} while (false)
#define CALL_INTERFACE_IF_PARENT_REACHABLE(name, ...) \
do { \
@@ -1765,8 +1874,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
DCHECK_EQ(0, this->local_types_->size());
- WasmDecoder<validate>::DecodeLocals(this->enabled_, this, this->sig_,
- this->local_types_);
+ this->InitializeLocalsFromSig();
+ uint32_t locals_length;
+ this->DecodeLocals(this->pc(), &locals_length,
+ static_cast<uint32_t>(this->local_types_->size()));
+ this->consume_bytes(locals_length);
+
CALL_INTERFACE(StartFunction);
DecodeFunctionBody();
if (!this->failed()) CALL_INTERFACE(FinishFunction);
@@ -1839,6 +1952,14 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return &*(stack_.end() - depth);
}
+ void SetSucceedingCodeDynamicallyUnreachable() {
+ Control* current = &control_.back();
+ if (current->reachable()) {
+ current->reachability = kSpecOnlyReachable;
+ current_code_reachable_ = false;
+ }
+ }
+
private:
Zone* zone_;
@@ -1847,6 +1968,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ZoneVector<ValueType> local_type_vec_; // types of local variables.
ZoneVector<Value> stack_; // stack of values.
ZoneVector<Control> control_; // stack of blocks, loops, and ifs.
+ // Controls whether code should be generated for the current block (basically
+ // a cache for {ok() && control_.back().reachable()}).
+ bool current_code_reachable_ = true;
static Value UnreachableValue(const uint8_t* pc) {
return Value{pc, kWasmBottom};
@@ -1895,832 +2019,905 @@ class WasmFullDecoder : public WasmDecoder<validate> {
int len_ = 0;
};
- // Decodes the body of a function.
- void DecodeFunctionBody() {
- TRACE("wasm-decode %p...%p (module+%u, %d bytes)\n", this->start(),
- this->end(), this->pc_offset(),
- static_cast<int>(this->end() - this->start()));
-
- // Set up initial function block.
- {
- auto* c = PushControl(kControlBlock);
- InitMerge(&c->start_merge, 0, [](uint32_t) -> Value { UNREACHABLE(); });
- InitMerge(&c->end_merge,
- static_cast<uint32_t>(this->sig_->return_count()),
- [&](uint32_t i) {
- return Value{this->pc_, this->sig_->GetReturn(i)};
- });
- CALL_INTERFACE(StartFunctionBody, c);
- }
-
- while (this->pc_ < this->end_) { // decoding loop.
- uint32_t len = 1;
- WasmOpcode opcode = static_cast<WasmOpcode>(*this->pc_);
-
- CALL_INTERFACE_IF_REACHABLE(NextInstruction, opcode);
+ // Helper to avoid calling member methods (which are more expensive to call
+ // indirectly).
+ template <WasmOpcode opcode>
+ static int DecodeOp(WasmFullDecoder* decoder) {
+ return decoder->DecodeOp<opcode>();
+ }
+ template <WasmOpcode opcode>
+ int DecodeOp() {
#if DEBUG
- TraceLine trace_msg;
+ TraceLine trace_msg;
#define TRACE_PART(...) trace_msg.Append(__VA_ARGS__)
- if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
- TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
- WasmOpcodes::OpcodeName(opcode));
- }
+ if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
+ TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
+ WasmOpcodes::OpcodeName(opcode));
+ }
#else
#define TRACE_PART(...)
#endif
- switch (opcode) {
+ int len = 1;
+
+ // TODO(clemensb): Break this up into individual functions.
+ switch (opcode) {
#define BUILD_SIMPLE_OPCODE(op, _, sig) \
case kExpr##op: \
BuildSimpleOperator_##sig(opcode); \
break;
- FOREACH_SIMPLE_OPCODE(BUILD_SIMPLE_OPCODE)
+ FOREACH_SIMPLE_OPCODE(BUILD_SIMPLE_OPCODE)
#undef BUILD_SIMPLE_OPCODE
- case kExprNop:
- break;
- case kExprBlock: {
- BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
- if (!this->Validate(imm)) break;
- auto args = PopArgs(imm.sig);
- auto* block = PushControl(kControlBlock);
- SetBlockType(block, imm, args.begin());
- CALL_INTERFACE_IF_REACHABLE(Block, block);
- PushMergeValues(block, &block->start_merge);
- len = 1 + imm.length;
- break;
- }
- case kExprRethrow: {
- CHECK_PROTOTYPE_OPCODE(eh);
- auto exception = Pop(0, kWasmExnRef);
- CALL_INTERFACE_IF_REACHABLE(Rethrow, exception);
- EndControl();
+ case kExprNop:
+ break;
+ case kExprBlock: {
+ BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
+ if (!this->Validate(imm)) break;
+ ArgVector args = PopArgs(imm.sig);
+ Control* block = PushControl(kControlBlock);
+ SetBlockType(block, imm, args.begin());
+ CALL_INTERFACE_IF_REACHABLE(Block, block);
+ PushMergeValues(block, &block->start_merge);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprRethrow: {
+ CHECK_PROTOTYPE_OPCODE(eh);
+ Value exception = Pop(0, kWasmExnRef);
+ CALL_INTERFACE_IF_REACHABLE(Rethrow, exception);
+ EndControl();
+ break;
+ }
+ case kExprThrow: {
+ CHECK_PROTOTYPE_OPCODE(eh);
+ ExceptionIndexImmediate<validate> imm(this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ ArgVector args = PopArgs(imm.exception->ToFunctionSig());
+ CALL_INTERFACE_IF_REACHABLE(Throw, imm, VectorOf(args));
+ EndControl();
+ break;
+ }
+ case kExprTry: {
+ CHECK_PROTOTYPE_OPCODE(eh);
+ BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
+ if (!this->Validate(imm)) break;
+ ArgVector args = PopArgs(imm.sig);
+ Control* try_block = PushControl(kControlTry);
+ SetBlockType(try_block, imm, args.begin());
+ len = 1 + imm.length;
+ CALL_INTERFACE_IF_REACHABLE(Try, try_block);
+ PushMergeValues(try_block, &try_block->start_merge);
+ break;
+ }
+ case kExprCatch: {
+ CHECK_PROTOTYPE_OPCODE(eh);
+ if (!VALIDATE(!control_.empty())) {
+ this->error("catch does not match any try");
break;
}
- case kExprThrow: {
- CHECK_PROTOTYPE_OPCODE(eh);
- ExceptionIndexImmediate<validate> imm(this, this->pc_);
- len = 1 + imm.length;
- if (!this->Validate(this->pc_, imm)) break;
- auto args = PopArgs(imm.exception->ToFunctionSig());
- CALL_INTERFACE_IF_REACHABLE(Throw, imm, VectorOf(args));
- EndControl();
+ Control* c = &control_.back();
+ if (!VALIDATE(c->is_try())) {
+ this->error("catch does not match any try");
break;
}
- case kExprTry: {
- CHECK_PROTOTYPE_OPCODE(eh);
- BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
- if (!this->Validate(imm)) break;
- auto args = PopArgs(imm.sig);
- auto* try_block = PushControl(kControlTry);
- SetBlockType(try_block, imm, args.begin());
- len = 1 + imm.length;
- CALL_INTERFACE_IF_REACHABLE(Try, try_block);
- PushMergeValues(try_block, &try_block->start_merge);
+ if (!VALIDATE(c->is_incomplete_try())) {
+ this->error("catch already present for try");
break;
}
- case kExprCatch: {
- CHECK_PROTOTYPE_OPCODE(eh);
- if (!VALIDATE(!control_.empty())) {
- this->error("catch does not match any try");
- break;
- }
- Control* c = &control_.back();
- if (!VALIDATE(c->is_try())) {
- this->error("catch does not match any try");
- break;
- }
- if (!VALIDATE(c->is_incomplete_try())) {
- this->error("catch already present for try");
- break;
+ c->kind = kControlTryCatch;
+ FallThruTo(c);
+ stack_.erase(stack_.begin() + c->stack_depth, stack_.end());
+ c->reachability = control_at(1)->innerReachability();
+ current_code_reachable_ = this->ok() && c->reachable();
+ Value* exception = Push(kWasmExnRef);
+ CALL_INTERFACE_IF_PARENT_REACHABLE(Catch, c, exception);
+ break;
+ }
+ case kExprBrOnExn: {
+ CHECK_PROTOTYPE_OPCODE(eh);
+ BranchOnExceptionImmediate<validate> imm(this, this->pc_);
+ 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);
+ Value exception = Pop(0, kWasmExnRef);
+ const WasmExceptionSig* sig = imm.index.exception->sig;
+ size_t value_count = sig->parameter_count();
+ // TODO(wasm): This operand stack mutation is an ugly hack to make
+ // both type checking here as well as environment merging in the
+ // graph builder interface work out of the box. We should introduce
+ // 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);
+ TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
+ if (this->failed()) break;
+ 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();
+ Value* pexception = Push(kWasmExnRef);
+ *pexception = exception;
+ break;
+ }
+ case kExprBrOnNull: {
+ CHECK_PROTOTYPE_OPCODE(typed_funcref);
+ BranchDepthImmediate<validate> imm(this, this->pc_);
+ if (!this->Validate(this->pc_, imm, control_.size())) break;
+ len = 1 + imm.length;
+ Value ref_object = Pop();
+ if (this->failed()) break;
+ Control* c = control_at(imm.depth);
+ TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
+ if (V8_LIKELY(check_result == kReachableBranch)) {
+ switch (ref_object.type.kind()) {
+ case ValueType::kRef: {
+ Value* result = Push(ref_object.type);
+ CALL_INTERFACE(PassThrough, ref_object, result);
+ break;
+ }
+ case ValueType::kOptRef: {
+ // We need to Push the result value after calling BrOnNull on
+ // the interface. Therefore we must sync the ref_object and
+ // result nodes afterwards (in PassThrough).
+ CALL_INTERFACE(BrOnNull, ref_object, imm.depth);
+ Value* result = Push(
+ ValueType::Ref(ref_object.type.heap_type(), kNonNullable));
+ CALL_INTERFACE(PassThrough, ref_object, result);
+ c->br_merge()->reached = true;
+ break;
+ }
+ default:
+ this->error(this->pc_,
+ "invalid agrument type to ref.as_non_null");
+ break;
}
- c->kind = kControlTryCatch;
- FallThruTo(c);
- stack_.erase(stack_.begin() + c->stack_depth, stack_.end());
- c->reachability = control_at(1)->innerReachability();
- auto* exception = Push(kWasmExnRef);
- CALL_INTERFACE_IF_PARENT_REACHABLE(Catch, c, exception);
- break;
}
- case kExprBrOnExn: {
- CHECK_PROTOTYPE_OPCODE(eh);
- BranchOnExceptionImmediate<validate> imm(this, this->pc_);
- 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, kWasmExnRef);
- const WasmExceptionSig* sig = imm.index.exception->sig;
- size_t value_count = sig->parameter_count();
- // TODO(wasm): This operand stack mutation is an ugly hack to make
- // both type checking here as well as environment merging in the
- // graph builder interface work out of the box. We should introduce
- // 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);
- 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(kWasmExnRef);
- *pexception = exception;
+ break;
+ }
+ case kExprLet: {
+ CHECK_PROTOTYPE_OPCODE(typed_funcref);
+ BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
+ if (!this->Validate(imm)) break;
+ uint32_t current_local_count =
+ static_cast<uint32_t>(local_type_vec_.size());
+ // Temporarily add the let-defined values
+ // to the beginning of the function locals.
+ uint32_t locals_length;
+ if (!this->DecodeLocals(this->pc() + 1 + imm.length, &locals_length,
+ 0)) {
+ break;
+ }
+ len = 1 + imm.length + locals_length;
+ uint32_t locals_count =
+ static_cast<uint32_t>(local_type_vec_.size() - current_local_count);
+ ArgVector let_local_values =
+ PopArgs(static_cast<uint32_t>(imm.in_arity()),
+ VectorOf(local_type_vec_.data(), locals_count));
+ ArgVector args = PopArgs(imm.sig);
+ Control* let_block = PushControl(kControlLet, locals_count);
+ SetBlockType(let_block, imm, args.begin());
+ CALL_INTERFACE_IF_REACHABLE(Block, let_block);
+ PushMergeValues(let_block, &let_block->start_merge);
+ CALL_INTERFACE_IF_REACHABLE(AllocateLocals, VectorOf(let_local_values));
+ break;
+ }
+ case kExprLoop: {
+ BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
+ if (!this->Validate(imm)) break;
+ ArgVector args = PopArgs(imm.sig);
+ Control* block = PushControl(kControlLoop);
+ SetBlockType(&control_.back(), imm, args.begin());
+ len = 1 + imm.length;
+ CALL_INTERFACE_IF_REACHABLE(Loop, block);
+ PushMergeValues(block, &block->start_merge);
+ break;
+ }
+ case kExprIf: {
+ BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
+ if (!this->Validate(imm)) break;
+ Value cond = Pop(0, kWasmI32);
+ ArgVector args = PopArgs(imm.sig);
+ if (!VALIDATE(this->ok())) break;
+ Control* if_block = PushControl(kControlIf);
+ SetBlockType(if_block, imm, args.begin());
+ CALL_INTERFACE_IF_REACHABLE(If, cond, if_block);
+ len = 1 + imm.length;
+ PushMergeValues(if_block, &if_block->start_merge);
+ break;
+ }
+ case kExprElse: {
+ if (!VALIDATE(!control_.empty())) {
+ this->error("else does not match any if");
break;
}
- case kExprBrOnNull: {
- CHECK_PROTOTYPE_OPCODE(gc);
- BranchDepthImmediate<validate> imm(this, this->pc_);
- if (!this->Validate(this->pc_, imm, control_.size())) break;
- len = 1 + imm.length;
- Value ref_object = Pop();
- if (this->failed()) break;
- Control* c = control_at(imm.depth);
- TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
- if (V8_LIKELY(check_result == kReachableBranch)) {
- switch (ref_object.type.kind()) {
- case ValueType::kRef: {
- auto* result = Push(
- ValueType(ValueType::kRef, ref_object.type.ref_index()));
- CALL_INTERFACE(PassThrough, ref_object, result);
- break;
- }
- case ValueType::kOptRef: {
- // We need to Push the result value after calling BrOnNull on
- // the interface. Therefore we must sync the ref_object and
- // result nodes afterwards (in PassThrough).
- CALL_INTERFACE(BrOnNull, ref_object, imm.depth);
- auto* result = Push(
- ValueType(ValueType::kRef, ref_object.type.ref_index()));
- CALL_INTERFACE(PassThrough, ref_object, result);
- c->br_merge()->reached = true;
- break;
- }
- case ValueType::kNullRef:
- if (imm.depth == control_.size() - 1) {
- DoReturn();
- } else {
- CALL_INTERFACE(Br, c);
- c->br_merge()->reached = true;
- }
- EndControl();
- break;
- default:
- this->error(this->pc_,
- "invalid agrument type to ref.as_non_null");
- break;
- }
- }
+ Control* c = &control_.back();
+ if (!VALIDATE(c->is_if())) {
+ this->error(this->pc_, "else does not match an if");
break;
}
- case kExprLoop: {
- BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
- if (!this->Validate(imm)) break;
- auto args = PopArgs(imm.sig);
- auto* block = PushControl(kControlLoop);
- SetBlockType(&control_.back(), imm, args.begin());
- len = 1 + imm.length;
- CALL_INTERFACE_IF_REACHABLE(Loop, block);
- PushMergeValues(block, &block->start_merge);
+ if (c->is_if_else()) {
+ this->error(this->pc_, "else already present for if");
break;
}
- case kExprIf: {
- BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
- if (!this->Validate(imm)) break;
- auto cond = Pop(0, kWasmI32);
- auto args = PopArgs(imm.sig);
- if (!VALIDATE(this->ok())) break;
- auto* if_block = PushControl(kControlIf);
- SetBlockType(if_block, imm, args.begin());
- CALL_INTERFACE_IF_REACHABLE(If, cond, if_block);
- len = 1 + imm.length;
- PushMergeValues(if_block, &if_block->start_merge);
+ if (!TypeCheckFallThru()) break;
+ c->kind = kControlIfElse;
+ CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c);
+ if (c->reachable()) c->end_merge.reached = true;
+ PushMergeValues(c, &c->start_merge);
+ c->reachability = control_at(1)->innerReachability();
+ current_code_reachable_ = this->ok() && c->reachable();
+ break;
+ }
+ case kExprEnd: {
+ if (!VALIDATE(!control_.empty())) {
+ this->error("end does not match any if, try, or block");
break;
}
- case kExprElse: {
- if (!VALIDATE(!control_.empty())) {
- this->error("else does not match any if");
- break;
- }
- Control* c = &control_.back();
- if (!VALIDATE(c->is_if())) {
- this->error(this->pc_, "else does not match an if");
- break;
- }
- if (c->is_if_else()) {
- this->error(this->pc_, "else already present for if");
- break;
- }
- if (!TypeCheckFallThru()) break;
- c->kind = kControlIfElse;
- CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c);
- if (c->reachable()) c->end_merge.reached = true;
- PushMergeValues(c, &c->start_merge);
- c->reachability = control_at(1)->innerReachability();
+ Control* c = &control_.back();
+ if (!VALIDATE(!c->is_incomplete_try())) {
+ this->error(this->pc_, "missing catch or catch-all in try");
break;
}
- case kExprEnd: {
- if (!VALIDATE(!control_.empty())) {
- this->error("end does not match any if, try, or block");
- break;
- }
- Control* c = &control_.back();
- if (!VALIDATE(!c->is_incomplete_try())) {
- this->error(this->pc_, "missing catch or catch-all in try");
+ if (c->is_onearmed_if()) {
+ if (!VALIDATE(c->end_merge.arity == c->start_merge.arity)) {
+ this->error(c->pc,
+ "start-arity and end-arity of one-armed if must match");
break;
}
- if (c->is_onearmed_if()) {
- if (!VALIDATE(c->end_merge.arity == c->start_merge.arity)) {
- this->error(
- c->pc,
- "start-arity and end-arity of one-armed if must match");
- break;
- }
- if (!TypeCheckOneArmedIf(c)) break;
- }
- if (!TypeCheckFallThru()) break;
-
- if (control_.size() == 1) {
- // If at the last (implicit) control, check we are at end.
- if (!VALIDATE(this->pc_ + 1 == this->end_)) {
- this->error(this->pc_ + 1, "trailing code after function end");
- break;
- }
- // The result of the block is the return value.
- TRACE_PART("\n" TRACE_INST_FORMAT, startrel(this->pc_),
- "(implicit) return");
- DoReturn();
- control_.clear();
- break;
- }
- PopControl(c);
- break;
+ if (!TypeCheckOneArmedIf(c)) break;
+ }
+ if (c->is_let()) {
+ this->local_types_->erase(
+ this->local_types_->begin(),
+ this->local_types_->begin() + c->locals_count);
+ CALL_INTERFACE_IF_REACHABLE(DeallocateLocals, c->locals_count);
}
- case kExprSelect: {
- auto cond = Pop(2, kWasmI32);
- auto fval = Pop();
- auto tval = Pop(0, fval.type);
- ValueType type = tval.type == kWasmBottom ? fval.type : tval.type;
- if (type.IsSubTypeOf(kWasmAnyRef)) {
- this->error(
- "select without type is only valid for value type inputs");
+ if (!TypeCheckFallThru()) break;
+
+ if (control_.size() == 1) {
+ // If at the last (implicit) control, check we are at end.
+ if (!VALIDATE(this->pc_ + 1 == this->end_)) {
+ this->error(this->pc_ + 1, "trailing code after function end");
break;
}
- auto* result = Push(type);
- CALL_INTERFACE_IF_REACHABLE(Select, cond, fval, tval, result);
- break;
- }
- case kExprSelectWithType: {
- CHECK_PROTOTYPE_OPCODE(anyref);
- SelectTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
- if (this->failed()) break;
- auto cond = Pop(2, kWasmI32);
- auto fval = Pop(1, imm.type);
- auto tval = Pop(0, imm.type);
- auto* result = Push(imm.type);
- CALL_INTERFACE_IF_REACHABLE(Select, cond, fval, tval, result);
- len = 1 + imm.length;
+ // The result of the block is the return value.
+ TRACE_PART("\n" TRACE_INST_FORMAT, startrel(this->pc_),
+ "(implicit) return");
+ DoReturn();
+ control_.clear();
break;
}
- case kExprBr: {
- BranchDepthImmediate<validate> imm(this, this->pc_);
- if (!this->Validate(this->pc_, imm, control_.size())) break;
- Control* c = control_at(imm.depth);
- 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();
+ PopControl(c);
+ break;
+ }
+ case kExprSelect: {
+ Value cond = Pop(2, kWasmI32);
+ Value fval = Pop();
+ Value tval = Pop(0, fval.type);
+ ValueType type = tval.type == kWasmBottom ? fval.type : tval.type;
+ if (type.is_reference_type()) {
+ this->error(
+ "select without type is only valid for value type inputs");
break;
}
- case kExprBrIf: {
- BranchDepthImmediate<validate> imm(this, this->pc_);
- auto cond = Pop(0, kWasmI32);
- if (this->failed()) break;
- if (!this->Validate(this->pc_, imm, control_.size())) break;
- Control* c = control_at(imm.depth);
- TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
- if (V8_LIKELY(check_result == kReachableBranch)) {
- CALL_INTERFACE(BrIf, cond, imm.depth);
+ Value* result = Push(type);
+ CALL_INTERFACE_IF_REACHABLE(Select, cond, fval, tval, result);
+ break;
+ }
+ case kExprSelectWithType: {
+ CHECK_PROTOTYPE_OPCODE(reftypes);
+ SelectTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
+ if (this->failed()) break;
+ Value cond = Pop(2, kWasmI32);
+ Value fval = Pop(1, imm.type);
+ Value tval = Pop(0, imm.type);
+ Value* result = Push(imm.type);
+ CALL_INTERFACE_IF_REACHABLE(Select, cond, fval, tval, result);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprBr: {
+ BranchDepthImmediate<validate> imm(this, this->pc_);
+ if (!this->Validate(this->pc_, imm, control_.size())) break;
+ Control* c = control_at(imm.depth);
+ 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;
+ } else if (check_result == kInvalidStack) {
break;
}
- case kExprBrTable: {
- BranchTableImmediate<validate> imm(this, this->pc_);
- BranchTableIterator<validate> iterator(this, imm);
- auto key = Pop(0, kWasmI32);
- if (this->failed()) break;
- if (!this->Validate(this->pc_, imm, control_.size())) break;
-
- // 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 index = iterator.cur_index();
- const byte* pos = iterator.pc();
- uint32_t target = iterator.next();
- if (!VALIDATE(ValidateBrTableTarget(target, pos, index))) break;
- // Avoid redundant branch target checks.
- if (br_targets[target]) continue;
- br_targets[target] = true;
-
- 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;
- }
+ len = 1 + imm.length;
+ EndControl();
+ break;
+ }
+ case kExprBrIf: {
+ BranchDepthImmediate<validate> imm(this, this->pc_);
+ Value cond = Pop(0, kWasmI32);
+ if (this->failed()) break;
+ if (!this->Validate(this->pc_, imm, control_.size())) break;
+ Control* c = control_at(imm.depth);
+ 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;
+ }
+ case kExprBrTable: {
+ BranchTableImmediate<validate> imm(this, this->pc_);
+ BranchTableIterator<validate> iterator(this, imm);
+ Value key = Pop(0, kWasmI32);
+ if (this->failed()) break;
+ if (!this->Validate(this->pc_, imm, control_.size())) break;
+
+ // 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 index = iterator.cur_index();
+ const byte* pos = iterator.pc();
+ uint32_t target = iterator.next();
+ if (!VALIDATE(ValidateBrTableTarget(target, pos, index))) break;
+ // Avoid redundant branch target checks.
+ if (br_targets[target]) continue;
+ br_targets[target] = true;
+
+ 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 (!VALIDATE(TypeCheckBrTable(result_types))) break;
+ if (!VALIDATE(TypeCheckBrTable(result_types))) break;
- DCHECK(this->ok());
+ DCHECK(this->ok());
- if (control_.back().reachable()) {
- CALL_INTERFACE(BrTable, imm, key);
+ if (current_code_reachable_) {
+ CALL_INTERFACE(BrTable, imm, key);
- for (int i = 0, e = control_depth(); i < e; ++i) {
- if (!br_targets[i]) continue;
- control_at(i)->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;
}
-
- len = 1 + iterator.length();
- EndControl();
- break;
}
- case kExprReturn: {
- 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 = num_returns - 1; i >= 0; --i) {
- Pop(i, this->sig_->GetReturn(i));
- }
- }
- EndControl();
- break;
- }
- case kExprUnreachable: {
- CALL_INTERFACE_IF_REACHABLE(Unreachable);
- EndControl();
- break;
- }
- case kExprI32Const: {
- ImmI32Immediate<validate> imm(this, this->pc_);
- auto* value = Push(kWasmI32);
- CALL_INTERFACE_IF_REACHABLE(I32Const, value, imm.value);
- len = 1 + imm.length;
- break;
- }
- case kExprI64Const: {
- ImmI64Immediate<validate> imm(this, this->pc_);
- auto* value = Push(kWasmI64);
- CALL_INTERFACE_IF_REACHABLE(I64Const, value, imm.value);
- len = 1 + imm.length;
- break;
- }
- case kExprF32Const: {
- ImmF32Immediate<validate> imm(this, this->pc_);
- auto* value = Push(kWasmF32);
- CALL_INTERFACE_IF_REACHABLE(F32Const, value, imm.value);
- len = 1 + imm.length;
- break;
- }
- case kExprF64Const: {
- ImmF64Immediate<validate> imm(this, this->pc_);
- auto* value = Push(kWasmF64);
- CALL_INTERFACE_IF_REACHABLE(F64Const, value, imm.value);
- len = 1 + imm.length;
- break;
- }
- case kExprRefNull: {
- CHECK_PROTOTYPE_OPCODE(anyref);
- auto* value = Push(kWasmNullRef);
- CALL_INTERFACE_IF_REACHABLE(RefNull, value);
- len = 1;
- break;
- }
- case kExprRefFunc: {
- CHECK_PROTOTYPE_OPCODE(anyref);
- FunctionIndexImmediate<validate> imm(this, this->pc_);
- if (!this->Validate(this->pc_, imm)) break;
- auto* value = Push(kWasmFuncRef);
- CALL_INTERFACE_IF_REACHABLE(RefFunc, imm.index, value);
- len = 1 + imm.length;
- break;
- }
- case kExprRefAsNonNull: {
- CHECK_PROTOTYPE_OPCODE(gc);
- auto value = Pop();
- switch (value.type.kind()) {
- case ValueType::kRef: {
- auto* result =
- Push(ValueType(ValueType::kRef, value.type.ref_index()));
- CALL_INTERFACE_IF_REACHABLE(PassThrough, value, result);
- break;
- }
- case ValueType::kOptRef: {
- auto* result =
- Push(ValueType(ValueType::kRef, value.type.ref_index()));
- CALL_INTERFACE_IF_REACHABLE(RefAsNonNull, value, result);
- break;
- }
- case ValueType::kNullRef:
- // TODO(7748): Fix this once the standard clears up (see
- // https://github.com/WebAssembly/function-references/issues/21).
- CALL_INTERFACE_IF_REACHABLE(Unreachable);
- EndControl();
- break;
- default:
- this->error(this->pc_ + 1,
- "invalid agrument type to ref.as_non_null");
- break;
+ len = 1 + iterator.length();
+ EndControl();
+ break;
+ }
+ case kExprReturn: {
+ if (V8_LIKELY(current_code_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 = num_returns - 1; i >= 0; --i) {
+ Pop(i, this->sig_->GetReturn(i));
}
- break;
- }
- case kExprLocalGet: {
- LocalIndexImmediate<validate> imm(this, this->pc_);
- if (!this->Validate(this->pc_, imm)) break;
- auto* value = Push(imm.type);
- CALL_INTERFACE_IF_REACHABLE(LocalGet, value, imm);
- len = 1 + imm.length;
- break;
}
- case kExprLocalSet: {
- LocalIndexImmediate<validate> imm(this, this->pc_);
- if (!this->Validate(this->pc_, imm)) break;
- auto value = Pop(0, local_type_vec_[imm.index]);
- CALL_INTERFACE_IF_REACHABLE(LocalSet, value, imm);
- len = 1 + imm.length;
- break;
- }
- case kExprLocalTee: {
- LocalIndexImmediate<validate> imm(this, this->pc_);
- if (!this->Validate(this->pc_, imm)) break;
- auto value = Pop(0, local_type_vec_[imm.index]);
- auto* result = Push(value.type);
- CALL_INTERFACE_IF_REACHABLE(LocalTee, value, result, imm);
- len = 1 + imm.length;
- break;
- }
- case kExprDrop: {
- auto value = Pop();
- CALL_INTERFACE_IF_REACHABLE(Drop, value);
+
+ EndControl();
+ break;
+ }
+ case kExprUnreachable: {
+ CALL_INTERFACE_IF_REACHABLE(Unreachable);
+ EndControl();
+ break;
+ }
+ case kExprI32Const: {
+ ImmI32Immediate<validate> imm(this, this->pc_);
+ Value* value = Push(kWasmI32);
+ CALL_INTERFACE_IF_REACHABLE(I32Const, value, imm.value);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprI64Const: {
+ ImmI64Immediate<validate> imm(this, this->pc_);
+ Value* value = Push(kWasmI64);
+ CALL_INTERFACE_IF_REACHABLE(I64Const, value, imm.value);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprF32Const: {
+ ImmF32Immediate<validate> imm(this, this->pc_);
+ Value* value = Push(kWasmF32);
+ CALL_INTERFACE_IF_REACHABLE(F32Const, value, imm.value);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprF64Const: {
+ ImmF64Immediate<validate> imm(this, this->pc_);
+ Value* value = Push(kWasmF64);
+ CALL_INTERFACE_IF_REACHABLE(F64Const, value, imm.value);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprRefNull: {
+ CHECK_PROTOTYPE_OPCODE(reftypes);
+ RefNullImmediate<validate> imm(this->enabled_, this, this->pc_);
+ if (!this->Validate(this->pc_, imm)) break;
+ Value* value = Push(imm.type);
+ CALL_INTERFACE_IF_REACHABLE(RefNull, value);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprRefIsNull: {
+ CHECK_PROTOTYPE_OPCODE(reftypes);
+ Value value = Pop();
+ Value* result = Push(kWasmI32);
+ len = 1;
+ if (value.type.is_nullable()) {
+ CALL_INTERFACE_IF_REACHABLE(UnOp, opcode, value, result);
break;
}
- case kExprGlobalGet: {
- GlobalIndexImmediate<validate> imm(this, this->pc_);
- len = 1 + imm.length;
- if (!this->Validate(this->pc_, imm)) break;
- auto* result = Push(imm.type);
- CALL_INTERFACE_IF_REACHABLE(GlobalGet, result, imm);
+ if (value.type.is_reference_type()) {
+ // Due to the check above, we know that the value is not null.
+ CALL_INTERFACE_IF_REACHABLE(I32Const, result, 0);
break;
}
- case kExprGlobalSet: {
- GlobalIndexImmediate<validate> imm(this, this->pc_);
- len = 1 + imm.length;
- if (!this->Validate(this->pc_, imm)) break;
- if (!VALIDATE(imm.global->mutability)) {
- this->errorf(this->pc_, "immutable global #%u cannot be assigned",
- imm.index);
+ this->errorf(this->pc_,
+ "invalid argument type to ref.is_null. Expected "
+ "reference type, got %s",
+ value.type.type_name().c_str());
+ break;
+ }
+ case kExprRefFunc: {
+ CHECK_PROTOTYPE_OPCODE(reftypes);
+ FunctionIndexImmediate<validate> imm(this, this->pc_);
+ if (!this->Validate(this->pc_, imm)) break;
+ Value* value = Push(ValueType::Ref(kHeapFunc, kNonNullable));
+ CALL_INTERFACE_IF_REACHABLE(RefFunc, imm.index, value);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprRefAsNonNull: {
+ CHECK_PROTOTYPE_OPCODE(typed_funcref);
+ Value value = Pop();
+ switch (value.type.kind()) {
+ case ValueType::kRef: {
+ Value* result = Push(value.type);
+ CALL_INTERFACE_IF_REACHABLE(PassThrough, value, result);
break;
}
- auto value = Pop(0, imm.type);
- CALL_INTERFACE_IF_REACHABLE(GlobalSet, value, imm);
- break;
- }
- case kExprTableGet: {
- CHECK_PROTOTYPE_OPCODE(anyref);
- TableIndexImmediate<validate> imm(this, this->pc_);
- len = 1 + imm.length;
- if (!this->Validate(this->pc_, imm)) break;
- DCHECK_NOT_NULL(this->module_);
- auto index = Pop(0, kWasmI32);
- auto* result = Push(this->module_->tables[imm.index].type);
- CALL_INTERFACE_IF_REACHABLE(TableGet, index, result, imm);
- break;
- }
- 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(TableSet, index, value, imm);
- break;
- }
-
- case kExprI32LoadMem8S:
- len = 1 + DecodeLoadMem(LoadType::kI32Load8S);
- break;
- case kExprI32LoadMem8U:
- len = 1 + DecodeLoadMem(LoadType::kI32Load8U);
- break;
- case kExprI32LoadMem16S:
- len = 1 + DecodeLoadMem(LoadType::kI32Load16S);
- break;
- case kExprI32LoadMem16U:
- len = 1 + DecodeLoadMem(LoadType::kI32Load16U);
- break;
- case kExprI32LoadMem:
- len = 1 + DecodeLoadMem(LoadType::kI32Load);
- break;
- case kExprI64LoadMem8S:
- len = 1 + DecodeLoadMem(LoadType::kI64Load8S);
- break;
- case kExprI64LoadMem8U:
- len = 1 + DecodeLoadMem(LoadType::kI64Load8U);
- break;
- case kExprI64LoadMem16S:
- len = 1 + DecodeLoadMem(LoadType::kI64Load16S);
- break;
- case kExprI64LoadMem16U:
- len = 1 + DecodeLoadMem(LoadType::kI64Load16U);
- break;
- case kExprI64LoadMem32S:
- len = 1 + DecodeLoadMem(LoadType::kI64Load32S);
- break;
- case kExprI64LoadMem32U:
- len = 1 + DecodeLoadMem(LoadType::kI64Load32U);
- break;
- case kExprI64LoadMem:
- len = 1 + DecodeLoadMem(LoadType::kI64Load);
- break;
- case kExprF32LoadMem:
- len = 1 + DecodeLoadMem(LoadType::kF32Load);
- break;
- case kExprF64LoadMem:
- len = 1 + DecodeLoadMem(LoadType::kF64Load);
- break;
- case kExprI32StoreMem8:
- len = 1 + DecodeStoreMem(StoreType::kI32Store8);
- break;
- case kExprI32StoreMem16:
- len = 1 + DecodeStoreMem(StoreType::kI32Store16);
- break;
- case kExprI32StoreMem:
- len = 1 + DecodeStoreMem(StoreType::kI32Store);
- break;
- case kExprI64StoreMem8:
- len = 1 + DecodeStoreMem(StoreType::kI64Store8);
- break;
- case kExprI64StoreMem16:
- len = 1 + DecodeStoreMem(StoreType::kI64Store16);
- break;
- case kExprI64StoreMem32:
- len = 1 + DecodeStoreMem(StoreType::kI64Store32);
- break;
- case kExprI64StoreMem:
- len = 1 + DecodeStoreMem(StoreType::kI64Store);
- break;
- case kExprF32StoreMem:
- len = 1 + DecodeStoreMem(StoreType::kF32Store);
- break;
- case kExprF64StoreMem:
- len = 1 + DecodeStoreMem(StoreType::kF64Store);
- break;
- case kExprMemoryGrow: {
- if (!CheckHasMemory()) break;
- MemoryIndexImmediate<validate> imm(this, this->pc_);
- len = 1 + imm.length;
- DCHECK_NOT_NULL(this->module_);
- if (!VALIDATE(this->module_->origin == kWasmOrigin)) {
- this->error("grow_memory is not supported for asmjs modules");
+ case ValueType::kOptRef: {
+ Value* result =
+ Push(ValueType::Ref(value.type.heap_type(), kNonNullable));
+ CALL_INTERFACE_IF_REACHABLE(RefAsNonNull, value, result);
break;
}
- auto value = Pop(0, kWasmI32);
- auto* result = Push(kWasmI32);
- CALL_INTERFACE_IF_REACHABLE(MemoryGrow, value, result);
- break;
+ default:
+ this->error(this->pc_ + 1,
+ "invalid agrument type to ref.as_non_null");
+ break;
}
- case kExprMemorySize: {
- if (!CheckHasMemory()) break;
- MemoryIndexImmediate<validate> imm(this, this->pc_);
- auto* result = Push(kWasmI32);
- len = 1 + imm.length;
- CALL_INTERFACE_IF_REACHABLE(CurrentMemoryPages, result);
+ break;
+ }
+ case kExprLocalGet: {
+ LocalIndexImmediate<validate> imm(this, this->pc_);
+ if (!this->Validate(this->pc_, imm)) break;
+ Value* value = Push(imm.type);
+ CALL_INTERFACE_IF_REACHABLE(LocalGet, value, imm);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprLocalSet: {
+ LocalIndexImmediate<validate> imm(this, this->pc_);
+ if (!this->Validate(this->pc_, imm)) break;
+ Value value = Pop(0, local_type_vec_[imm.index]);
+ CALL_INTERFACE_IF_REACHABLE(LocalSet, value, imm);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprLocalTee: {
+ LocalIndexImmediate<validate> imm(this, this->pc_);
+ if (!this->Validate(this->pc_, imm)) break;
+ Value value = Pop(0, local_type_vec_[imm.index]);
+ Value* result = Push(value.type);
+ CALL_INTERFACE_IF_REACHABLE(LocalTee, value, result, imm);
+ len = 1 + imm.length;
+ break;
+ }
+ case kExprDrop: {
+ Value value = Pop();
+ CALL_INTERFACE_IF_REACHABLE(Drop, value);
+ break;
+ }
+ case kExprGlobalGet: {
+ GlobalIndexImmediate<validate> imm(this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ Value* result = Push(imm.type);
+ CALL_INTERFACE_IF_REACHABLE(GlobalGet, result, imm);
+ break;
+ }
+ case kExprGlobalSet: {
+ GlobalIndexImmediate<validate> imm(this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ if (!VALIDATE(imm.global->mutability)) {
+ this->errorf(this->pc_, "immutable global #%u cannot be assigned",
+ imm.index);
break;
}
- case kExprCallFunction: {
- CallFunctionImmediate<validate> imm(this, this->pc_);
- len = 1 + imm.length;
- if (!this->Validate(this->pc_, imm)) break;
- auto args = PopArgs(imm.sig);
- auto* returns = PushReturns(imm.sig);
- CALL_INTERFACE_IF_REACHABLE(CallDirect, imm, args.begin(), returns);
+ Value value = Pop(0, imm.type);
+ CALL_INTERFACE_IF_REACHABLE(GlobalSet, value, imm);
+ break;
+ }
+ case kExprTableGet: {
+ CHECK_PROTOTYPE_OPCODE(reftypes);
+ TableIndexImmediate<validate> imm(this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ Value index = Pop(0, kWasmI32);
+ Value* result = Push(this->module_->tables[imm.index].type);
+ CALL_INTERFACE_IF_REACHABLE(TableGet, index, result, imm);
+ break;
+ }
+ case kExprTableSet: {
+ CHECK_PROTOTYPE_OPCODE(reftypes);
+ TableIndexImmediate<validate> imm(this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ Value value = Pop(1, this->module_->tables[imm.index].type);
+ Value index = Pop(0, kWasmI32);
+ CALL_INTERFACE_IF_REACHABLE(TableSet, index, value, imm);
+ break;
+ }
+
+ case kExprI32LoadMem8S:
+ len = 1 + DecodeLoadMem(LoadType::kI32Load8S);
+ break;
+ case kExprI32LoadMem8U:
+ len = 1 + DecodeLoadMem(LoadType::kI32Load8U);
+ break;
+ case kExprI32LoadMem16S:
+ len = 1 + DecodeLoadMem(LoadType::kI32Load16S);
+ break;
+ case kExprI32LoadMem16U:
+ len = 1 + DecodeLoadMem(LoadType::kI32Load16U);
+ break;
+ case kExprI32LoadMem:
+ len = 1 + DecodeLoadMem(LoadType::kI32Load);
+ break;
+ case kExprI64LoadMem8S:
+ len = 1 + DecodeLoadMem(LoadType::kI64Load8S);
+ break;
+ case kExprI64LoadMem8U:
+ len = 1 + DecodeLoadMem(LoadType::kI64Load8U);
+ break;
+ case kExprI64LoadMem16S:
+ len = 1 + DecodeLoadMem(LoadType::kI64Load16S);
+ break;
+ case kExprI64LoadMem16U:
+ len = 1 + DecodeLoadMem(LoadType::kI64Load16U);
+ break;
+ case kExprI64LoadMem32S:
+ len = 1 + DecodeLoadMem(LoadType::kI64Load32S);
+ break;
+ case kExprI64LoadMem32U:
+ len = 1 + DecodeLoadMem(LoadType::kI64Load32U);
+ break;
+ case kExprI64LoadMem:
+ len = 1 + DecodeLoadMem(LoadType::kI64Load);
+ break;
+ case kExprF32LoadMem:
+ len = 1 + DecodeLoadMem(LoadType::kF32Load);
+ break;
+ case kExprF64LoadMem:
+ len = 1 + DecodeLoadMem(LoadType::kF64Load);
+ break;
+ case kExprI32StoreMem8:
+ len = 1 + DecodeStoreMem(StoreType::kI32Store8);
+ break;
+ case kExprI32StoreMem16:
+ len = 1 + DecodeStoreMem(StoreType::kI32Store16);
+ break;
+ case kExprI32StoreMem:
+ len = 1 + DecodeStoreMem(StoreType::kI32Store);
+ break;
+ case kExprI64StoreMem8:
+ len = 1 + DecodeStoreMem(StoreType::kI64Store8);
+ break;
+ case kExprI64StoreMem16:
+ len = 1 + DecodeStoreMem(StoreType::kI64Store16);
+ break;
+ case kExprI64StoreMem32:
+ len = 1 + DecodeStoreMem(StoreType::kI64Store32);
+ break;
+ case kExprI64StoreMem:
+ len = 1 + DecodeStoreMem(StoreType::kI64Store);
+ break;
+ case kExprF32StoreMem:
+ len = 1 + DecodeStoreMem(StoreType::kF32Store);
+ break;
+ case kExprF64StoreMem:
+ len = 1 + DecodeStoreMem(StoreType::kF64Store);
+ break;
+ case kExprMemoryGrow: {
+ if (!CheckHasMemory()) break;
+ MemoryIndexImmediate<validate> imm(this, this->pc_);
+ len = 1 + imm.length;
+ if (!VALIDATE(this->module_->origin == kWasmOrigin)) {
+ this->error("grow_memory is not supported for asmjs modules");
break;
}
- case kExprCallIndirect: {
- CallIndirectImmediate<validate> imm(this->enabled_, this, this->pc_);
- len = 1 + imm.length;
- if (!this->Validate(this->pc_, imm)) break;
- auto index = Pop(0, kWasmI32);
- auto args = PopArgs(imm.sig);
- auto* returns = PushReturns(imm.sig);
- CALL_INTERFACE_IF_REACHABLE(CallIndirect, index, imm, args.begin(),
- returns);
+ Value value = Pop(0, kWasmI32);
+ Value* result = Push(kWasmI32);
+ CALL_INTERFACE_IF_REACHABLE(MemoryGrow, value, result);
+ break;
+ }
+ case kExprMemorySize: {
+ if (!CheckHasMemory()) break;
+ MemoryIndexImmediate<validate> imm(this, this->pc_);
+ Value* result = Push(kWasmI32);
+ len = 1 + imm.length;
+ CALL_INTERFACE_IF_REACHABLE(CurrentMemoryPages, result);
+ break;
+ }
+ case kExprCallFunction: {
+ CallFunctionImmediate<validate> imm(this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ ArgVector args = PopArgs(imm.sig);
+ Value* returns = PushReturns(imm.sig);
+ CALL_INTERFACE_IF_REACHABLE(CallDirect, imm, args.begin(), returns);
+ break;
+ }
+ case kExprCallIndirect: {
+ CallIndirectImmediate<validate> imm(this->enabled_, this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ Value index = Pop(0, kWasmI32);
+ ArgVector args = PopArgs(imm.sig);
+ Value* returns = PushReturns(imm.sig);
+ CALL_INTERFACE_IF_REACHABLE(CallIndirect, index, imm, args.begin(),
+ returns);
+ break;
+ }
+ case kExprReturnCall: {
+ CHECK_PROTOTYPE_OPCODE(return_call);
+
+ CallFunctionImmediate<validate> imm(this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ if (!this->CanReturnCall(imm.sig)) {
+ OPCODE_ERROR(opcode, "tail call return types mismatch");
break;
}
- case kExprReturnCall: {
- CHECK_PROTOTYPE_OPCODE(return_call);
- CallFunctionImmediate<validate> imm(this, this->pc_);
- len = 1 + imm.length;
- if (!this->Validate(this->pc_, imm)) break;
- if (!this->CanReturnCall(imm.sig)) {
- OPCODE_ERROR(opcode, "tail call return types mismatch");
- break;
- }
+ ArgVector args = PopArgs(imm.sig);
- auto args = PopArgs(imm.sig);
-
- CALL_INTERFACE_IF_REACHABLE(ReturnCall, imm, args.begin());
- EndControl();
- break;
- }
- case kExprReturnCallIndirect: {
- CHECK_PROTOTYPE_OPCODE(return_call);
- CallIndirectImmediate<validate> imm(this->enabled_, this, this->pc_);
- len = 1 + imm.length;
- if (!this->Validate(this->pc_, imm)) break;
- if (!this->CanReturnCall(imm.sig)) {
- OPCODE_ERROR(opcode, "tail call return types mismatch");
- break;
- }
- auto index = Pop(0, kWasmI32);
- auto args = PopArgs(imm.sig);
- CALL_INTERFACE_IF_REACHABLE(ReturnCallIndirect, index, imm,
- args.begin());
- EndControl();
- break;
- }
- case kNumericPrefix: {
- ++len;
- byte numeric_index =
- this->template read_u8<validate>(this->pc_ + 1, "numeric index");
- opcode = static_cast<WasmOpcode>(opcode << 8 | numeric_index);
- if (opcode == kExprTableGrow || opcode == kExprTableSize ||
- opcode == kExprTableFill) {
- CHECK_PROTOTYPE_OPCODE(anyref);
- } else if (opcode >= kExprMemoryInit) {
- CHECK_PROTOTYPE_OPCODE(bulk_memory);
- }
- TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
- WasmOpcodes::OpcodeName(opcode));
- len += DecodeNumericOpcode(opcode);
- break;
- }
- case kSimdPrefix: {
- CHECK_PROTOTYPE_OPCODE(simd);
- uint32_t length = 0;
- opcode =
- this->template read_prefixed_opcode<validate>(this->pc_, &length);
- if (!VALIDATE(this->ok())) break;
- len += length;
-
- TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
- WasmOpcodes::OpcodeName(opcode));
- len += DecodeSimdOpcode(opcode, length);
- break;
- }
- case kAtomicPrefix: {
- CHECK_PROTOTYPE_OPCODE(threads);
- len++;
- byte atomic_index =
- this->template read_u8<validate>(this->pc_ + 1, "atomic index");
- opcode = static_cast<WasmOpcode>(opcode << 8 | atomic_index);
- TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
- WasmOpcodes::OpcodeName(opcode));
- len += DecodeAtomicOpcode(opcode);
+ CALL_INTERFACE_IF_REACHABLE(ReturnCall, imm, args.begin());
+ EndControl();
+ break;
+ }
+ case kExprReturnCallIndirect: {
+ CHECK_PROTOTYPE_OPCODE(return_call);
+ CallIndirectImmediate<validate> imm(this->enabled_, this, this->pc_);
+ len = 1 + imm.length;
+ if (!this->Validate(this->pc_, imm)) break;
+ if (!this->CanReturnCall(imm.sig)) {
+ OPCODE_ERROR(opcode, "tail call return types mismatch");
break;
}
- case kGCPrefix: {
- CHECK_PROTOTYPE_OPCODE(gc);
- byte gc_index =
- this->template read_u8<validate>(this->pc_ + 1, "gc index");
- opcode = static_cast<WasmOpcode>(opcode << 8 | gc_index);
- TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
- WasmOpcodes::OpcodeName(opcode));
- len = DecodeGCOpcode(opcode);
- break;
+ Value index = Pop(0, kWasmI32);
+ ArgVector args = PopArgs(imm.sig);
+ CALL_INTERFACE_IF_REACHABLE(ReturnCallIndirect, index, imm,
+ args.begin());
+ EndControl();
+ break;
+ }
+ case kNumericPrefix: {
+ ++len;
+ byte numeric_index =
+ this->template read_u8<validate>(this->pc_ + 1, "numeric index");
+ WasmOpcode full_opcode =
+ static_cast<WasmOpcode>(opcode << 8 | numeric_index);
+ if (full_opcode == kExprTableGrow || full_opcode == kExprTableSize ||
+ full_opcode == kExprTableFill) {
+ CHECK_PROTOTYPE_OPCODE(reftypes);
+ } else if (full_opcode >= kExprMemoryInit) {
+ CHECK_PROTOTYPE_OPCODE(bulk_memory);
}
+ TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
+ WasmOpcodes::OpcodeName(full_opcode));
+ len += DecodeNumericOpcode(full_opcode);
+ break;
+ }
+ case kSimdPrefix: {
+ CHECK_PROTOTYPE_OPCODE(simd);
+ uint32_t length = 0;
+ WasmOpcode full_opcode =
+ this->template read_prefixed_opcode<validate>(this->pc_, &length);
+ if (!VALIDATE(this->ok())) break;
+ len += length;
+
+ TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
+ WasmOpcodes::OpcodeName(full_opcode));
+ len += DecodeSimdOpcode(full_opcode, length);
+ break;
+ }
+ case kAtomicPrefix: {
+ CHECK_PROTOTYPE_OPCODE(threads);
+ len++;
+ byte atomic_index =
+ this->template read_u8<validate>(this->pc_ + 1, "atomic index");
+ WasmOpcode full_opcode =
+ static_cast<WasmOpcode>(opcode << 8 | atomic_index);
+ TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
+ WasmOpcodes::OpcodeName(full_opcode));
+ len += DecodeAtomicOpcode(full_opcode);
+ break;
+ }
+ case kGCPrefix: {
+ CHECK_PROTOTYPE_OPCODE(gc);
+ byte gc_index =
+ this->template read_u8<validate>(this->pc_ + 1, "gc index");
+ WasmOpcode full_opcode =
+ static_cast<WasmOpcode>(opcode << 8 | gc_index);
+ TRACE_PART(TRACE_INST_FORMAT, startrel(this->pc_),
+ WasmOpcodes::OpcodeName(full_opcode));
+ len = DecodeGCOpcode(full_opcode);
+ break;
+ }
// Note that prototype opcodes are not handled in the fastpath
// above this switch, to avoid checking a feature flag.
#define SIMPLE_PROTOTYPE_CASE(name, opc, sig) \
case kExpr##name: /* fallthrough */
- FOREACH_SIMPLE_PROTOTYPE_OPCODE(SIMPLE_PROTOTYPE_CASE)
+ FOREACH_SIMPLE_PROTOTYPE_OPCODE(SIMPLE_PROTOTYPE_CASE)
#undef SIMPLE_PROTOTYPE_CASE
- BuildSimplePrototypeOperator(opcode);
- break;
- default: {
- // Deal with special asmjs opcodes.
- if (this->module_ != nullptr && is_asmjs_module(this->module_)) {
- const FunctionSig* sig = WasmOpcodes::AsmjsSignature(opcode);
- if (sig) {
- BuildSimpleOperator(opcode, sig);
- }
- } else {
- this->error("Invalid opcode");
- return;
+ BuildSimplePrototypeOperator(opcode);
+ break;
+ default: {
+ // Deal with special asmjs opcodes.
+ if (is_asmjs_module(this->module_)) {
+ const FunctionSig* sig = WasmOpcodes::AsmjsSignature(opcode);
+ if (sig) {
+ BuildSimpleOperator(opcode, sig);
}
+ } else {
+ this->error("Invalid opcode");
+ return 0;
}
}
+ }
#if DEBUG
- if (FLAG_trace_wasm_decoder) {
- TRACE_PART(" ");
- for (Control& c : control_) {
- switch (c.kind) {
- case kControlIf:
- TRACE_PART("I");
- break;
- case kControlBlock:
- TRACE_PART("B");
- break;
- case kControlLoop:
- TRACE_PART("L");
- break;
- case kControlTry:
- TRACE_PART("T");
- break;
- default:
- break;
- }
- if (c.start_merge.arity) TRACE_PART("%u-", c.start_merge.arity);
- TRACE_PART("%u", c.end_merge.arity);
- if (!c.reachable()) TRACE_PART("%c", c.unreachable() ? '*' : '#');
+ if (FLAG_trace_wasm_decoder) {
+ TRACE_PART(" ");
+ for (Control& c : control_) {
+ switch (c.kind) {
+ case kControlIf:
+ TRACE_PART("I");
+ break;
+ case kControlBlock:
+ TRACE_PART("B");
+ break;
+ case kControlLoop:
+ TRACE_PART("L");
+ break;
+ case kControlTry:
+ TRACE_PART("T");
+ break;
+ case kControlIfElse:
+ case kControlTryCatch:
+ case kControlLet: // TODO(7748): Implement
+ break;
}
- TRACE_PART(" | ");
- for (size_t i = 0; i < stack_.size(); ++i) {
- auto& val = stack_[i];
- WasmOpcode opcode = static_cast<WasmOpcode>(*val.pc);
- if (WasmOpcodes::IsPrefixOpcode(opcode)) {
- opcode = this->template read_prefixed_opcode<Decoder::kNoValidate>(
- val.pc);
+ if (c.start_merge.arity) TRACE_PART("%u-", c.start_merge.arity);
+ TRACE_PART("%u", c.end_merge.arity);
+ if (!c.reachable()) TRACE_PART("%c", c.unreachable() ? '*' : '#');
+ }
+ TRACE_PART(" | ");
+ for (size_t i = 0; i < stack_.size(); ++i) {
+ Value& val = stack_[i];
+ WasmOpcode val_opcode = static_cast<WasmOpcode>(*val.pc);
+ if (WasmOpcodes::IsPrefixOpcode(val_opcode)) {
+ val_opcode =
+ this->template read_prefixed_opcode<Decoder::kNoValidate>(val.pc);
+ }
+ TRACE_PART(" %c@%d:%s", val.type.short_name(),
+ static_cast<int>(val.pc - this->start_),
+ WasmOpcodes::OpcodeName(val_opcode));
+ // If the decoder failed, don't try to decode the immediates, as this
+ // can trigger a DCHECK failure.
+ if (this->failed()) continue;
+ switch (val_opcode) {
+ case kExprI32Const: {
+ ImmI32Immediate<Decoder::kNoValidate> imm(this, val.pc);
+ TRACE_PART("[%d]", imm.value);
+ break;
}
- TRACE_PART(" %c@%d:%s", val.type.short_name(),
- static_cast<int>(val.pc - this->start_),
- WasmOpcodes::OpcodeName(opcode));
- // If the decoder failed, don't try to decode the immediates, as this
- // can trigger a DCHECK failure.
- if (this->failed()) continue;
- switch (opcode) {
- case kExprI32Const: {
- ImmI32Immediate<Decoder::kNoValidate> imm(this, val.pc);
- TRACE_PART("[%d]", imm.value);
- break;
- }
- case kExprLocalGet:
- case kExprLocalSet:
- case kExprLocalTee: {
- LocalIndexImmediate<Decoder::kNoValidate> imm(this, val.pc);
- TRACE_PART("[%u]", imm.index);
- break;
- }
- case kExprGlobalGet:
- case kExprGlobalSet: {
- GlobalIndexImmediate<Decoder::kNoValidate> imm(this, val.pc);
- TRACE_PART("[%u]", imm.index);
- break;
- }
- default:
- break;
+ case kExprLocalGet:
+ case kExprLocalSet:
+ case kExprLocalTee: {
+ LocalIndexImmediate<Decoder::kNoValidate> imm(this, val.pc);
+ TRACE_PART("[%u]", imm.index);
+ break;
+ }
+ case kExprGlobalGet:
+ case kExprGlobalSet: {
+ GlobalIndexImmediate<Decoder::kNoValidate> imm(this, val.pc);
+ TRACE_PART("[%u]", imm.index);
+ break;
}
+ default:
+ break;
}
}
+ }
#endif
+ return len;
+ }
+
+ using OpcodeHandler = int (*)(WasmFullDecoder*);
+
+ template <size_t idx>
+ struct GetOpcodeHandlerTableEntry
+ : public std::integral_constant<
+ OpcodeHandler,
+ &WasmFullDecoder::DecodeOp<static_cast<WasmOpcode>(idx)>> {};
+
+ OpcodeHandler GetOpcodeHandler(uint8_t opcode) {
+ static constexpr std::array<OpcodeHandler, 256> kOpcodeHandlers =
+ base::make_array<256, GetOpcodeHandlerTableEntry>();
+ return kOpcodeHandlers[opcode];
+ }
+
+ void DecodeFunctionBody() {
+ TRACE("wasm-decode %p...%p (module+%u, %d bytes)\n", this->start(),
+ this->end(), this->pc_offset(),
+ static_cast<int>(this->end() - this->start()));
+
+ // Set up initial function block.
+ {
+ Control* c = PushControl(kControlBlock);
+ InitMerge(&c->start_merge, 0, [](uint32_t) -> Value { UNREACHABLE(); });
+ InitMerge(&c->end_merge,
+ static_cast<uint32_t>(this->sig_->return_count()),
+ [&](uint32_t i) {
+ return Value{this->pc_, this->sig_->GetReturn(i)};
+ });
+ CALL_INTERFACE(StartFunctionBody, c);
+ }
+
+ // Decode the function body.
+ while (this->pc_ < this->end_) {
+ uint8_t first_byte = *this->pc_;
+ CALL_INTERFACE_IF_REACHABLE(NextInstruction,
+ static_cast<WasmOpcode>(first_byte));
+ OpcodeHandler handler = GetOpcodeHandler(first_byte);
+ int len = (*handler)(this);
this->pc_ += len;
- } // end decode loop
+ }
+
if (!VALIDATE(this->pc_ == this->end_) && this->ok()) {
this->error("Beyond end of code");
}
@@ -2728,13 +2925,14 @@ class WasmFullDecoder : public WasmDecoder<validate> {
void EndControl() {
DCHECK(!control_.empty());
- auto* current = &control_.back();
+ Control* current = &control_.back();
stack_.erase(stack_.begin() + current->stack_depth, stack_.end());
CALL_INTERFACE_IF_REACHABLE(EndControl, current);
current->reachability = kUnreachable;
+ current_code_reachable_ = false;
}
- template<typename func>
+ template <typename func>
void InitMerge(Merge<Value>* merge, uint32_t arity, func get_val) {
merge->arity = arity;
if (arity == 1) {
@@ -2771,7 +2969,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
int count = static_cast<int>(type->field_count());
ArgVector args(count);
for (int i = count - 1; i >= 0; i--) {
- args[i] = Pop(i, type->field(i));
+ args[i] = Pop(i, type->field(i).Unpacked());
+ }
+ return args;
+ }
+
+ V8_INLINE ArgVector PopArgs(uint32_t base_index,
+ Vector<ValueType> arg_types) {
+ ArgVector args(arg_types.size());
+ for (int i = static_cast<int>(arg_types.size()) - 1; i >= 0; i--) {
+ args[i] = Pop(base_index + i, arg_types[i]);
}
return args;
}
@@ -2781,10 +2988,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return sig->return_count() == 0 ? kWasmStmt : sig->GetReturn();
}
- Control* PushControl(ControlKind kind) {
+ Control* PushControl(ControlKind kind, uint32_t locals_count = 0) {
Reachability reachability =
control_.empty() ? kReachable : control_.back().innerReachability();
- control_.emplace_back(kind, stack_size(), this->pc_, reachability);
+ control_.emplace_back(kind, locals_count, stack_size(), this->pc_,
+ reachability);
+ current_code_reachable_ = this->ok() && reachability == kReachable;
return &control_.back();
}
@@ -2800,17 +3009,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
control_.pop_back();
// 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 && control_.back().reachable()) {
- control_.back().reachability = kSpecOnlyReachable;
- }
+ if (!parent_reached) SetSucceedingCodeDynamicallyUnreachable();
+ current_code_reachable_ = control_.back().reachable();
}
int DecodeLoadMem(LoadType type, int prefix_len = 0) {
if (!CheckHasMemory()) return 0;
MemoryAccessImmediate<validate> imm(this, this->pc_ + prefix_len,
type.size_log_2());
- auto index = Pop(0, kWasmI32);
- auto* result = Push(type.value_type());
+ Value index = Pop(0, kWasmI32);
+ Value* result = Push(type.value_type());
CALL_INTERFACE_IF_REACHABLE(LoadMem, type, imm, index, result);
return imm.length;
}
@@ -2823,8 +3031,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
transform == LoadTransformationKind::kExtend ? 3 : type.size_log_2();
MemoryAccessImmediate<validate> imm(this, this->pc_ + opcode_length,
max_alignment);
- auto index = Pop(0, kWasmI32);
- auto* result = Push(kWasmS128);
+ Value index = Pop(0, kWasmI32);
+ Value* result = Push(kWasmS128);
CALL_INTERFACE_IF_REACHABLE(LoadTransform, type, transform, imm, index,
result);
return imm.length;
@@ -2834,8 +3042,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
if (!CheckHasMemory()) return 0;
MemoryAccessImmediate<validate> imm(this, this->pc_ + prefix_len,
store.size_log_2());
- auto value = Pop(1, store.value_type());
- auto index = Pop(0, kWasmI32);
+ Value value = Pop(1, store.value_type());
+ Value index = Pop(0, kWasmI32);
CALL_INTERFACE_IF_REACHABLE(StoreMem, store, imm, index, value);
return imm.length;
}
@@ -2850,7 +3058,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
std::vector<ValueType> InitializeBrTableResultTypes(uint32_t target) {
- auto* merge = control_at(target)->br_merge();
+ Merge<Value>* 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) {
@@ -2861,7 +3069,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
bool UpdateBrTableResultTypes(std::vector<ValueType>* result_types,
uint32_t target, const byte* pos, int index) {
- auto* merge = control_at(target)->br_merge();
+ Merge<Value>* 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())) {
@@ -2873,18 +3081,27 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
for (int i = 0; i < br_arity; ++i) {
- if (this->enabled_.has_anyref()) {
+ if (this->enabled_.has_reftypes()) {
// The expected type is the biggest common sub type of all targets.
+ ValueType type = (*result_types)[i];
(*result_types)[i] =
- ValueType::CommonSubType((*result_types)[i], (*merge)[i].type);
+ CommonSubtype((*result_types)[i], (*merge)[i].type, this->module_);
+ if ((*result_types)[i] == kWasmBottom) {
+ this->errorf(pos,
+ "inconsistent type in br_table target %u (previous "
+ "was %s, this one is %s)",
+ index, type.type_name().c_str(),
+ (*merge)[i].type.type_name().c_str());
+ return false;
+ }
} 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, (*result_types)[i].type_name(),
- (*merge)[i].type.type_name());
+ index, (*result_types)[i].type_name().c_str(),
+ (*merge)[i].type.type_name().c_str());
return false;
}
}
@@ -2909,10 +3126,11 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// Type-check the topmost br_arity values on the stack.
for (int i = 0; i < br_arity; ++i) {
Value& val = stack_values[i];
- if (!val.type.IsSubTypeOf(result_types[i])) {
+ if (!IsSubtypeOf(val.type, result_types[i], this->module_)) {
this->errorf(this->pc_,
"type error in merge[%u] (expected %s, got %s)", i,
- result_types[i].type_name(), val.type.type_name());
+ result_types[i].type_name().c_str(),
+ val.type.type_name().c_str());
return false;
}
}
@@ -2928,7 +3146,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
SimdLaneImmediate<validate> imm(this, this->pc_, opcode_length);
if (this->Validate(this->pc_, opcode, imm)) {
Value inputs[] = {Pop(0, kWasmS128)};
- auto* result = Push(type);
+ Value* result = Push(type);
CALL_INTERFACE_IF_REACHABLE(SimdLaneOp, opcode, imm, ArrayVector(inputs),
result);
}
@@ -2943,7 +3161,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
UnreachableValue(this->pc_)};
inputs[1] = Pop(1, type);
inputs[0] = Pop(0, kWasmS128);
- auto* result = Push(kWasmS128);
+ Value* result = Push(kWasmS128);
CALL_INTERFACE_IF_REACHABLE(SimdLaneOp, opcode, imm, ArrayVector(inputs),
result);
}
@@ -2953,9 +3171,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
uint32_t Simd8x16ShuffleOp(uint32_t opcode_length) {
Simd8x16ShuffleImmediate<validate> imm(this, this->pc_, opcode_length);
if (this->Validate(this->pc_, imm)) {
- auto input1 = Pop(1, kWasmS128);
- auto input0 = Pop(0, kWasmS128);
- auto* result = Push(kWasmS128);
+ Value input1 = Pop(1, kWasmS128);
+ Value input0 = Pop(0, kWasmS128);
+ Value* result = Push(kWasmS128);
CALL_INTERFACE_IF_REACHABLE(Simd8x16ShuffleOp, imm, input0, input1,
result);
}
@@ -3075,8 +3293,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
this->error("invalid simd opcode");
break;
}
- auto args = PopArgs(sig);
- auto* results =
+ ArgVector args = PopArgs(sig);
+ Value* results =
sig->return_count() == 0 ? nullptr : Push(GetReturnType(sig));
CALL_INTERFACE_IF_REACHABLE(SimdOp, opcode, VectorOf(args), results);
}
@@ -3091,29 +3309,66 @@ class WasmFullDecoder : public WasmDecoder<validate> {
StructIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length;
if (!this->Validate(this->pc_, imm)) break;
- auto args = PopArgs(imm.struct_type);
- auto* value = Push(ValueType(ValueType::kRef, imm.index));
+ ArgVector args = PopArgs(imm.struct_type);
+ Value* value = Push(
+ ValueType::Ref(static_cast<HeapType>(imm.index), kNonNullable));
CALL_INTERFACE_IF_REACHABLE(StructNew, imm, args.begin(), value);
break;
}
case kExprStructGet: {
FieldIndexImmediate<validate> field(this, this->pc_ + len);
if (!this->Validate(this->pc_ + len, field)) break;
+ ValueType field_type =
+ field.struct_index.struct_type->field(field.index);
+ if (field_type.is_packed()) {
+ this->error(this->pc_,
+ "struct.get used with a field of packed type. "
+ "Use struct.get_s or struct.get_u instead.");
+ break;
+ }
len += field.length;
- auto struct_obj =
- Pop(0, ValueType(ValueType::kOptRef, field.struct_index.index));
- auto* value = Push(field.struct_index.struct_type->field(field.index));
- CALL_INTERFACE_IF_REACHABLE(StructGet, struct_obj, field, value);
+ Value struct_obj = Pop(
+ 0, ValueType::Ref(static_cast<HeapType>(field.struct_index.index),
+ kNullable));
+ Value* value = Push(field_type);
+ CALL_INTERFACE_IF_REACHABLE(StructGet, struct_obj, field, true, value);
+ break;
+ }
+ case kExprStructGetU:
+ case kExprStructGetS: {
+ FieldIndexImmediate<validate> field(this, this->pc_ + len);
+ if (!this->Validate(this->pc_ + len, field)) break;
+ len += field.length;
+ ValueType field_type =
+ field.struct_index.struct_type->field(field.index);
+ if (!field_type.is_packed()) {
+ this->errorf(this->pc_,
+ "%s is only valid for packed struct fields. "
+ "Use struct.get instead.",
+ WasmOpcodes::OpcodeName(opcode));
+ break;
+ }
+ Value struct_obj = Pop(
+ 0, ValueType::Ref(static_cast<HeapType>(field.struct_index.index),
+ kNullable));
+ Value* value = Push(field_type.Unpacked());
+ CALL_INTERFACE_IF_REACHABLE(StructGet, struct_obj, field,
+ opcode == kExprStructGetS, value);
break;
}
case kExprStructSet: {
FieldIndexImmediate<validate> field(this, this->pc_ + len);
if (!this->Validate(this->pc_ + len, field)) break;
len += field.length;
- auto field_value = Pop(
- 0, ValueType(field.struct_index.struct_type->field(field.index)));
- auto struct_obj =
- Pop(0, ValueType(ValueType::kOptRef, field.struct_index.index));
+ const StructType* struct_type = field.struct_index.struct_type;
+ if (!struct_type->mutability(field.index)) {
+ this->error(this->pc_, "setting immutable struct field");
+ break;
+ }
+ Value field_value = Pop(1, struct_type->field(field.index).Unpacked());
+ Value struct_obj = Pop(
+ 0, ValueType::Ref(static_cast<HeapType>(field.struct_index.index),
+ kNullable));
CALL_INTERFACE_IF_REACHABLE(StructSet, struct_obj, field, field_value);
break;
}
@@ -3121,31 +3376,66 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ArrayIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length;
if (!this->Validate(this->pc_, imm)) break;
- auto length = Pop(0, kWasmI32);
- auto initial_value = Pop(0, imm.array_type->element_type());
- auto* value = Push(ValueType(ValueType::kRef, imm.index));
+ Value length = Pop(1, kWasmI32);
+ Value initial_value = Pop(0, imm.array_type->element_type().Unpacked());
+ Value* value = Push(
+ ValueType::Ref(static_cast<HeapType>(imm.index), kNonNullable));
CALL_INTERFACE_IF_REACHABLE(ArrayNew, imm, length, initial_value,
value);
break;
}
+ case kExprArrayGetS:
+ case kExprArrayGetU: {
+ ArrayIndexImmediate<validate> imm(this, this->pc_ + len);
+ len += imm.length;
+ if (!this->Validate(this->pc_ + len, imm)) break;
+ if (!imm.array_type->element_type().is_packed()) {
+ this->errorf(this->pc_,
+ "%s is only valid for packed arrays. "
+ "Use or array.get instead.",
+ WasmOpcodes::OpcodeName(opcode));
+ break;
+ }
+ Value index = Pop(1, kWasmI32);
+ Value array_obj =
+ Pop(0, ValueType::Ref(static_cast<HeapType>(imm.index), kNullable));
+ Value* value = Push(imm.array_type->element_type().Unpacked());
+ // TODO(7748): Optimize this when array_obj is non-nullable ref.
+ CALL_INTERFACE_IF_REACHABLE(ArrayGet, array_obj, imm, index,
+ opcode == kExprArrayGetS, value);
+ break;
+ }
case kExprArrayGet: {
ArrayIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length;
if (!this->Validate(this->pc_ + len, imm)) break;
- auto index = Pop(0, kWasmI32);
- auto array_obj = Pop(0, ValueType(ValueType::kOptRef, imm.index));
- auto* value = Push(imm.array_type->element_type());
+ if (imm.array_type->element_type().is_packed()) {
+ this->error(this->pc_,
+ "array.get used with a field of packed type. "
+ "Use array.get_s or array.get_u instead.");
+ break;
+ }
+ Value index = Pop(1, kWasmI32);
+ Value array_obj =
+ Pop(0, ValueType::Ref(static_cast<HeapType>(imm.index), kNullable));
+ Value* value = Push(imm.array_type->element_type());
// TODO(7748): Optimize this when array_obj is non-nullable ref.
- CALL_INTERFACE_IF_REACHABLE(ArrayGet, array_obj, imm, index, value);
+ CALL_INTERFACE_IF_REACHABLE(ArrayGet, array_obj, imm, index, true,
+ value);
break;
}
case kExprArraySet: {
ArrayIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length;
if (!this->Validate(this->pc_ + len, imm)) break;
- auto value = Pop(0, imm.array_type->element_type());
- auto index = Pop(0, kWasmI32);
- auto array_obj = Pop(0, ValueType(ValueType::kOptRef, imm.index));
+ if (!imm.array_type->mutability()) {
+ this->error(this->pc_, "setting element of immutable array");
+ break;
+ }
+ Value value = Pop(2, imm.array_type->element_type().Unpacked());
+ Value index = Pop(1, kWasmI32);
+ Value array_obj =
+ Pop(0, ValueType::Ref(static_cast<HeapType>(imm.index), kNullable));
// TODO(7748): Optimize this when array_obj is non-nullable ref.
CALL_INTERFACE_IF_REACHABLE(ArraySet, array_obj, imm, index, value);
break;
@@ -3154,11 +3444,22 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ArrayIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length;
if (!this->Validate(this->pc_ + len, imm)) break;
- auto array_obj = Pop(0, ValueType(ValueType::kOptRef, imm.index));
- auto* value = Push(kWasmI32);
+ Value array_obj =
+ Pop(0, ValueType::Ref(static_cast<HeapType>(imm.index), kNullable));
+ Value* value = Push(kWasmI32);
CALL_INTERFACE_IF_REACHABLE(ArrayLen, array_obj, value);
break;
}
+ case kExprRttCanon: {
+ // TODO(7748): Introduce HeapTypeImmediate and use that here.
+ TypeIndexImmediate<validate> imm(this, this->pc_ + len);
+ len += imm.length;
+ if (!this->Validate(this->pc_ + len, imm)) break;
+ Value* value =
+ Push(ValueType::Rtt(static_cast<HeapType>(imm.index), 1));
+ CALL_INTERFACE_IF_REACHABLE(RttCanon, imm, value);
+ break;
+ }
default:
this->error("invalid gc opcode");
return 0;
@@ -3209,8 +3510,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
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));
+ ArgVector args = PopArgs(sig);
+ Value* result = ret_type == kWasmStmt ? nullptr : Push(GetReturnType(sig));
CALL_INTERFACE_IF_REACHABLE(AtomicOp, opcode, VectorOf(args), imm, result);
return len;
}
@@ -3234,9 +3535,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
MemoryInitImmediate<validate> imm(this, this->pc_);
if (!this->Validate(imm)) break;
len += imm.length;
- auto size = Pop(2, sig->GetParam(2));
- auto src = Pop(1, sig->GetParam(1));
- auto dst = Pop(0, sig->GetParam(0));
+ Value size = Pop(2, sig->GetParam(2));
+ Value src = Pop(1, sig->GetParam(1));
+ Value dst = Pop(0, sig->GetParam(0));
CALL_INTERFACE_IF_REACHABLE(MemoryInit, imm, dst, src, size);
break;
}
@@ -3251,9 +3552,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
MemoryCopyImmediate<validate> imm(this, this->pc_);
if (!this->Validate(imm)) break;
len += imm.length;
- auto size = Pop(2, sig->GetParam(2));
- auto src = Pop(1, sig->GetParam(1));
- auto dst = Pop(0, sig->GetParam(0));
+ Value size = Pop(2, sig->GetParam(2));
+ Value src = Pop(1, sig->GetParam(1));
+ Value dst = Pop(0, sig->GetParam(0));
CALL_INTERFACE_IF_REACHABLE(MemoryCopy, imm, dst, src, size);
break;
}
@@ -3261,9 +3562,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
MemoryIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_ + 1, imm)) break;
len += imm.length;
- auto size = Pop(2, sig->GetParam(2));
- auto value = Pop(1, sig->GetParam(1));
- auto dst = Pop(0, sig->GetParam(0));
+ Value size = Pop(2, sig->GetParam(2));
+ Value value = Pop(1, sig->GetParam(1));
+ Value dst = Pop(0, sig->GetParam(0));
CALL_INTERFACE_IF_REACHABLE(MemoryFill, imm, dst, value, size);
break;
}
@@ -3271,7 +3572,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
TableInitImmediate<validate> imm(this, this->pc_);
if (!this->Validate(imm)) break;
len += imm.length;
- auto args = PopArgs(sig);
+ ArgVector args = PopArgs(sig);
CALL_INTERFACE_IF_REACHABLE(TableInit, imm, VectorOf(args));
break;
}
@@ -3286,7 +3587,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
TableCopyImmediate<validate> imm(this, this->pc_);
if (!this->Validate(imm)) break;
len += imm.length;
- auto args = PopArgs(sig);
+ ArgVector args = PopArgs(sig);
CALL_INTERFACE_IF_REACHABLE(TableCopy, imm, VectorOf(args));
break;
}
@@ -3294,9 +3595,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
TableIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_, imm)) break;
len += imm.length;
- auto delta = Pop(1, sig->GetParam(1));
- auto value = Pop(0, this->module_->tables[imm.index].type);
- auto* result = Push(kWasmI32);
+ Value delta = Pop(1, sig->GetParam(1));
+ Value value = Pop(0, this->module_->tables[imm.index].type);
+ Value* result = Push(kWasmI32);
CALL_INTERFACE_IF_REACHABLE(TableGrow, imm, value, delta, result);
break;
}
@@ -3304,7 +3605,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
TableIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_, imm)) break;
len += imm.length;
- auto* result = Push(kWasmI32);
+ Value* result = Push(kWasmI32);
CALL_INTERFACE_IF_REACHABLE(TableSize, imm, result);
break;
}
@@ -3312,9 +3613,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
TableIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_, imm)) break;
len += imm.length;
- auto count = Pop(2, sig->GetParam(2));
- auto value = Pop(1, this->module_->tables[imm.index].type);
- auto start = Pop(0, sig->GetParam(0));
+ Value count = Pop(2, sig->GetParam(2));
+ Value value = Pop(1, this->module_->tables[imm.index].type);
+ Value start = Pop(0, sig->GetParam(0));
CALL_INTERFACE_IF_REACHABLE(TableFill, imm, start, value, count);
break;
}
@@ -3330,6 +3631,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
void DoReturn() {
size_t return_count = this->sig_->return_count();
+ if (return_count > 1) {
+ this->detected_->Add(kFeature_mv);
+ }
DCHECK_GE(stack_.size(), return_count);
Vector<Value> return_values =
return_count == 0
@@ -3370,12 +3674,13 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
V8_INLINE Value Pop(int index, ValueType expected) {
- auto val = Pop();
- if (!VALIDATE(val.type.IsSubTypeOf(expected) || val.type == kWasmBottom ||
- expected == kWasmBottom)) {
+ Value val = Pop();
+ if (!VALIDATE(IsSubtypeOf(val.type, expected, this->module_) ||
+ val.type == kWasmBottom || expected == kWasmBottom)) {
this->errorf(val.pc, "%s[%d] expected type %s, found %s of type %s",
- SafeOpcodeNameAt(this->pc_), index, expected.type_name(),
- SafeOpcodeNameAt(val.pc), val.type.type_name());
+ SafeOpcodeNameAt(this->pc_), index,
+ expected.type_name().c_str(), SafeOpcodeNameAt(val.pc),
+ val.type.type_name().c_str());
}
return val;
}
@@ -3391,7 +3696,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
return UnreachableValue(this->pc_);
}
- auto val = stack_.back();
+ Value val = stack_.back();
stack_.pop_back();
return val;
}
@@ -3435,9 +3740,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
for (uint32_t i = 0; i < merge->arity; ++i) {
Value& val = stack_values[i];
Value& old = (*merge)[i];
- if (!val.type.IsSubTypeOf(old.type)) {
+ if (!IsSubtypeOf(val.type, old.type, this->module_)) {
this->errorf(this->pc_, "type error in merge[%u] (expected %s, got %s)",
- i, old.type.type_name(), val.type.type_name());
+ i, old.type.type_name().c_str(),
+ val.type.type_name().c_str());
return false;
}
}
@@ -3452,9 +3758,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
for (uint32_t i = 0; i < c->start_merge.arity; ++i) {
Value& start = c->start_merge[i];
Value& end = c->end_merge[i];
- if (!start.type.IsSubTypeOf(end.type)) {
+ if (!IsSubtypeOf(start.type, end.type, this->module_)) {
this->errorf(this->pc_, "type error in merge[%u] (expected %s, got %s)",
- i, end.type.type_name(), start.type.type_name());
+ i, end.type.type_name().c_str(),
+ start.type.type_name().c_str());
return false;
}
}
@@ -3463,7 +3770,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
bool TypeCheckFallThru() {
- static_assert(validate, "Call this function only whithin VALIDATE");
+ 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;
@@ -3554,12 +3861,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// This line requires num_returns > 0.
Value* stack_values = &*(stack_.end() - num_returns);
for (int i = 0; i < num_returns; ++i) {
- auto& val = stack_values[i];
+ Value& val = stack_values[i];
ValueType expected_type = this->sig_->GetReturn(i);
- if (!val.type.IsSubTypeOf(expected_type)) {
- this->errorf(this->pc_,
- "type error in return[%u] (expected %s, got %s)", i,
- expected_type.type_name(), val.type.type_name());
+ if (!IsSubtypeOf(val.type, expected_type, this->module_)) {
+ this->errorf(
+ this->pc_, "type error in return[%u] (expected %s, got %s)", i,
+ expected_type.type_name().c_str(), val.type.type_name().c_str());
return false;
}
}
@@ -3568,14 +3875,13 @@ class WasmFullDecoder : public WasmDecoder<validate> {
void onFirstError() override {
this->end_ = this->pc_; // Terminate decoding loop.
+ this->current_code_reachable_ = false;
TRACE(" !%s\n", this->error_.message().c_str());
CALL_INTERFACE(OnFirstError);
}
void BuildSimplePrototypeOperator(WasmOpcode opcode) {
- if (opcode == kExprRefIsNull) {
- RET_ON_PROTOTYPE_OPCODE(anyref);
- } else if (opcode == kExprRefEq) {
+ if (opcode == kExprRefEq) {
RET_ON_PROTOTYPE_OPCODE(gc);
}
const FunctionSig* sig = WasmOpcodes::Signature(opcode);
@@ -3583,39 +3889,28 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
void BuildSimpleOperator(WasmOpcode opcode, const FunctionSig* sig) {
- switch (sig->parameter_count()) {
- case 1: {
- auto val = Pop(0, sig->GetParam(0));
- auto* ret =
- sig->return_count() == 0 ? nullptr : Push(sig->GetReturn(0));
- CALL_INTERFACE_IF_REACHABLE(UnOp, opcode, val, ret);
- break;
- }
- case 2: {
- auto rval = Pop(1, sig->GetParam(1));
- auto lval = Pop(0, sig->GetParam(0));
- auto* ret =
- sig->return_count() == 0 ? nullptr : Push(sig->GetReturn(0));
- CALL_INTERFACE_IF_REACHABLE(BinOp, opcode, lval, rval, ret);
- break;
- }
- default:
- UNREACHABLE();
+ DCHECK_GE(1, sig->return_count());
+ ValueType ret = sig->return_count() == 0 ? kWasmStmt : sig->GetReturn(0);
+ if (sig->parameter_count() == 1) {
+ BuildSimpleOperator(opcode, ret, sig->GetParam(0));
+ } else {
+ DCHECK_EQ(2, sig->parameter_count());
+ BuildSimpleOperator(opcode, ret, sig->GetParam(0), sig->GetParam(1));
}
}
void BuildSimpleOperator(WasmOpcode opcode, ValueType return_type,
ValueType arg_type) {
- auto val = Pop(0, arg_type);
- auto* ret = return_type == kWasmStmt ? nullptr : Push(return_type);
+ Value val = Pop(0, arg_type);
+ Value* ret = return_type == kWasmStmt ? nullptr : Push(return_type);
CALL_INTERFACE_IF_REACHABLE(UnOp, opcode, val, ret);
}
void BuildSimpleOperator(WasmOpcode opcode, ValueType return_type,
ValueType lhs_type, ValueType rhs_type) {
- auto rval = Pop(1, rhs_type);
- auto lval = Pop(0, lhs_type);
- auto* ret = return_type == kWasmStmt ? nullptr : Push(return_type);
+ Value rval = Pop(1, rhs_type);
+ Value lval = Pop(0, lhs_type);
+ Value* ret = return_type == kWasmStmt ? nullptr : Push(return_type);
CALL_INTERFACE_IF_REACHABLE(BinOp, opcode, lval, rval, ret);
}