summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmBinaryIterator.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/wasm/WasmBinaryIterator.h')
-rw-r--r--js/src/wasm/WasmBinaryIterator.h2246
1 files changed, 2246 insertions, 0 deletions
diff --git a/js/src/wasm/WasmBinaryIterator.h b/js/src/wasm/WasmBinaryIterator.h
new file mode 100644
index 000000000..76e0c2875
--- /dev/null
+++ b/js/src/wasm/WasmBinaryIterator.h
@@ -0,0 +1,2246 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_binary_iterator_h
+#define wasm_binary_iterator_h
+
+#include "mozilla/Poison.h"
+
+#include "jsprf.h"
+
+#include "jit/AtomicOp.h"
+#include "wasm/WasmBinaryFormat.h"
+
+namespace js {
+namespace wasm {
+
+// The kind of a control-flow stack item.
+enum class LabelKind : uint8_t {
+ Block,
+ Loop,
+ Then,
+ UnreachableThen, // like Then, but not reachable
+ Else
+};
+
+#ifdef DEBUG
+// Families of opcodes that share a signature and validation logic.
+enum class OpKind {
+ Block,
+ Loop,
+ Unreachable,
+ Drop,
+ I32,
+ I64,
+ F32,
+ F64,
+ I8x16,
+ I16x8,
+ I32x4,
+ F32x4,
+ B8x16,
+ B16x8,
+ B32x4,
+ Br,
+ BrIf,
+ BrTable,
+ Nop,
+ Nullary,
+ Unary,
+ Binary,
+ Comparison,
+ Conversion,
+ Load,
+ Store,
+ TeeStore,
+ CurrentMemory,
+ GrowMemory,
+ Select,
+ GetLocal,
+ SetLocal,
+ TeeLocal,
+ GetGlobal,
+ SetGlobal,
+ TeeGlobal,
+ Call,
+ CallIndirect,
+ OldCallIndirect,
+ Return,
+ If,
+ Else,
+ End,
+ AtomicLoad,
+ AtomicStore,
+ AtomicBinOp,
+ AtomicCompareExchange,
+ AtomicExchange,
+ ExtractLane,
+ ReplaceLane,
+ Swizzle,
+ Shuffle,
+ Splat,
+ SimdSelect,
+ SimdCtor,
+ SimdBooleanReduction,
+ SimdShiftByScalar,
+ SimdComparison,
+};
+
+// Return the OpKind for a given Op. This is used for sanity-checking that
+// API users use the correct read function for a given Op.
+OpKind
+Classify(Op op);
+#endif
+
+// Common fields for linear memory access.
+template <typename Value>
+struct LinearMemoryAddress
+{
+ Value base;
+ uint32_t offset;
+ uint32_t align;
+
+ LinearMemoryAddress()
+ {}
+ LinearMemoryAddress(Value base, uint32_t offset, uint32_t align)
+ : base(base), offset(offset), align(align)
+ {}
+};
+
+template <typename ControlItem>
+class ControlStackEntry
+{
+ LabelKind kind_;
+ bool reachable_;
+ ExprType type_;
+ size_t valueStackStart_;
+ ControlItem controlItem_;
+
+ public:
+ ControlStackEntry(LabelKind kind, ExprType type, bool reachable, size_t valueStackStart)
+ : kind_(kind), reachable_(reachable), type_(type), valueStackStart_(valueStackStart),
+ controlItem_()
+ {
+ MOZ_ASSERT(type != ExprType::Limit);
+ }
+
+ LabelKind kind() const { return kind_; }
+ ExprType type() const { return type_; }
+ bool reachable() const { return reachable_; }
+ size_t valueStackStart() const { return valueStackStart_; }
+ ControlItem& controlItem() { return controlItem_; }
+
+ void setReachable() { reachable_ = true; }
+
+ void switchToElse(bool reachable) {
+ MOZ_ASSERT(kind_ == LabelKind::Then || kind_ == LabelKind::UnreachableThen);
+ reachable_ = reachable;
+ kind_ = LabelKind::Else;
+ controlItem_ = ControlItem();
+ }
+};
+
+// Specialization for when there is no additional data needed.
+template <>
+class ControlStackEntry<Nothing>
+{
+ LabelKind kind_;
+ bool reachable_;
+ ExprType type_;
+ size_t valueStackStart_;
+
+ public:
+ ControlStackEntry(LabelKind kind, ExprType type, bool reachable, size_t valueStackStart)
+ : kind_(kind), reachable_(reachable), type_(type), valueStackStart_(valueStackStart)
+ {
+ MOZ_ASSERT(type != ExprType::Limit);
+ }
+
+ LabelKind kind() const { return kind_; }
+ ExprType type() const { return type_; }
+ bool reachable() const { return reachable_; }
+ size_t valueStackStart() const { return valueStackStart_; }
+ Nothing controlItem() { return Nothing(); }
+
+ void setReachable() { reachable_ = true; }
+
+ void switchToElse(bool reachable) {
+ MOZ_ASSERT(kind_ == LabelKind::Then || kind_ == LabelKind::UnreachableThen);
+ reachable_ = reachable;
+ kind_ = LabelKind::Else;
+ }
+};
+
+template <typename Value>
+class TypeAndValue
+{
+ ValType type_;
+ Value value_;
+
+ public:
+ TypeAndValue() : type_(ValType(TypeCode::Limit)), value_() {}
+ explicit TypeAndValue(ValType type)
+ : type_(type), value_()
+ {}
+ TypeAndValue(ValType type, Value value)
+ : type_(type), value_(value)
+ {}
+ ValType type() const {
+ return type_;
+ }
+ Value value() const {
+ return value_;
+ }
+ void setValue(Value value) {
+ value_ = value;
+ }
+};
+
+// Specialization for when there is no additional data needed.
+template <>
+class TypeAndValue<Nothing>
+{
+ ValType type_;
+
+ public:
+ TypeAndValue() : type_(ValType(TypeCode::Limit)) {}
+ explicit TypeAndValue(ValType type) : type_(type) {}
+
+ TypeAndValue(ValType type, Nothing value)
+ : type_(type)
+ {}
+
+ ValType type() const { return type_; }
+ Nothing value() const { return Nothing(); }
+ void setValue(Nothing value) {}
+};
+
+// A policy class for configuring OpIter. Clients can use this as a
+// base class, and override the behavior as needed.
+struct OpIterPolicy
+{
+ // Should the iterator perform validation, such as type checking and
+ // validity checking?
+ static const bool Validate = false;
+
+ // Should the iterator produce output values?
+ static const bool Output = false;
+
+ // These members allow clients to add additional information to the value
+ // and control stacks, respectively. Using Nothing means that no additional
+ // field is added.
+ typedef Nothing Value;
+ typedef Nothing ControlItem;
+};
+
+// An iterator over the bytes of a function body. It performs validation
+// (if Policy::Validate is true) and unpacks the data into a usable form.
+//
+// The MOZ_STACK_CLASS attribute here is because of the use of DebugOnly.
+// There's otherwise nothing inherent in this class which would require
+// it to be used on the stack.
+template <typename Policy>
+class MOZ_STACK_CLASS OpIter : private Policy
+{
+ static const bool Validate = Policy::Validate;
+ static const bool Output = Policy::Output;
+ typedef typename Policy::Value Value;
+ typedef typename Policy::ControlItem ControlItem;
+
+ Decoder& d_;
+ const size_t offsetInModule_;
+
+ Vector<TypeAndValue<Value>, 8, SystemAllocPolicy> valueStack_;
+ Vector<ControlStackEntry<ControlItem>, 8, SystemAllocPolicy> controlStack_;
+ bool reachable_;
+
+ DebugOnly<Op> op_;
+ size_t offsetOfExpr_;
+
+ MOZ_MUST_USE bool readFixedU8(uint8_t* out) {
+ if (Validate)
+ return d_.readFixedU8(out);
+ *out = d_.uncheckedReadFixedU8();
+ return true;
+ }
+ MOZ_MUST_USE bool readFixedU32(uint32_t* out) {
+ if (Validate)
+ return d_.readFixedU32(out);
+ *out = d_.uncheckedReadFixedU32();
+ return true;
+ }
+ MOZ_MUST_USE bool readVarS32(int32_t* out) {
+ if (Validate)
+ return d_.readVarS32(out);
+ *out = d_.uncheckedReadVarS32();
+ return true;
+ }
+ MOZ_MUST_USE bool readVarU32(uint32_t* out) {
+ if (Validate)
+ return d_.readVarU32(out);
+ *out = d_.uncheckedReadVarU32();
+ return true;
+ }
+ MOZ_MUST_USE bool readVarS64(int64_t* out) {
+ if (Validate)
+ return d_.readVarS64(out);
+ *out = d_.uncheckedReadVarS64();
+ return true;
+ }
+ MOZ_MUST_USE bool readVarU64(uint64_t* out) {
+ if (Validate)
+ return d_.readVarU64(out);
+ *out = d_.uncheckedReadVarU64();
+ return true;
+ }
+ MOZ_MUST_USE bool readFixedF32(RawF32* out) {
+ if (Validate)
+ return d_.readFixedF32(out);
+ *out = d_.uncheckedReadFixedF32();
+ return true;
+ }
+ MOZ_MUST_USE bool readFixedF64(RawF64* out) {
+ if (Validate)
+ return d_.readFixedF64(out);
+ *out = d_.uncheckedReadFixedF64();
+ return true;
+ }
+ MOZ_MUST_USE bool readFixedI8x16(I8x16* out) {
+ if (Validate)
+ return d_.readFixedI8x16(out);
+ d_.uncheckedReadFixedI8x16(out);
+ return true;
+ }
+ MOZ_MUST_USE bool readFixedI16x8(I16x8* out) {
+ if (Validate)
+ return d_.readFixedI16x8(out);
+ d_.uncheckedReadFixedI16x8(out);
+ return true;
+ }
+ MOZ_MUST_USE bool readFixedI32x4(I32x4* out) {
+ if (Validate)
+ return d_.readFixedI32x4(out);
+ d_.uncheckedReadFixedI32x4(out);
+ return true;
+ }
+ MOZ_MUST_USE bool readFixedF32x4(F32x4* out) {
+ if (Validate)
+ return d_.readFixedF32x4(out);
+ d_.uncheckedReadFixedF32x4(out);
+ return true;
+ }
+
+ MOZ_MUST_USE bool readAtomicViewType(Scalar::Type* viewType) {
+ uint8_t x;
+ if (!readFixedU8(&x))
+ return fail("unable to read atomic view");
+ if (Validate && x >= Scalar::MaxTypedArrayViewType)
+ return fail("invalid atomic view type");
+ *viewType = Scalar::Type(x);
+ return true;
+ }
+
+ MOZ_MUST_USE bool readAtomicBinOpOp(jit::AtomicOp* op) {
+ uint8_t x;
+ if (!readFixedU8(&x))
+ return fail("unable to read atomic opcode");
+ if (Validate) {
+ switch (x) {
+ case jit::AtomicFetchAddOp:
+ case jit::AtomicFetchSubOp:
+ case jit::AtomicFetchAndOp:
+ case jit::AtomicFetchOrOp:
+ case jit::AtomicFetchXorOp:
+ break;
+ default:
+ return fail("unrecognized atomic binop");
+ }
+ }
+ *op = jit::AtomicOp(x);
+ return true;
+ }
+
+ MOZ_MUST_USE bool readLinearMemoryAddress(uint32_t byteSize, LinearMemoryAddress<Value>* addr);
+ MOZ_MUST_USE bool readBlockType(ExprType* expr);
+
+ MOZ_MUST_USE bool typeMismatch(ExprType actual, ExprType expected) MOZ_COLD;
+ MOZ_MUST_USE bool checkType(ValType actual, ValType expected);
+ MOZ_MUST_USE bool checkType(ExprType actual, ExprType expected);
+
+ MOZ_MUST_USE bool pushControl(LabelKind kind, ExprType type, bool reachable);
+ MOZ_MUST_USE bool mergeControl(LabelKind* kind, ExprType* type, Value* value);
+ MOZ_MUST_USE bool popControl(LabelKind* kind, ExprType* type, Value* value);
+
+ MOZ_MUST_USE bool push(ValType t) {
+ if (MOZ_UNLIKELY(!reachable_))
+ return true;
+ return valueStack_.emplaceBack(t);
+ }
+ MOZ_MUST_USE bool push(TypeAndValue<Value> tv) {
+ if (MOZ_UNLIKELY(!reachable_))
+ return true;
+ return valueStack_.append(tv);
+ }
+ void infalliblePush(ValType t) {
+ if (MOZ_UNLIKELY(!reachable_))
+ return;
+ valueStack_.infallibleEmplaceBack(t);
+ }
+ void infalliblePush(TypeAndValue<Value> tv) {
+ if (MOZ_UNLIKELY(!reachable_))
+ return;
+ valueStack_.infallibleAppend(tv);
+ }
+
+ // Test whether reading the top of the value stack is currently valid.
+ MOZ_MUST_USE bool checkTop() {
+ MOZ_ASSERT(reachable_);
+ if (Validate && valueStack_.length() <= controlStack_.back().valueStackStart()) {
+ if (valueStack_.empty())
+ return fail("popping value from empty stack");
+ return fail("popping value from outside block");
+ }
+ return true;
+ }
+
+ // Pop the top of the value stack.
+ MOZ_MUST_USE bool pop(TypeAndValue<Value>* tv) {
+ if (MOZ_UNLIKELY(!reachable_))
+ return true;
+ if (!checkTop())
+ return false;
+ *tv = valueStack_.popCopy();
+ return true;
+ }
+
+ // Pop the top of the value stack and check that it has the given type.
+ MOZ_MUST_USE bool popWithType(ValType expectedType, Value* value) {
+ if (MOZ_UNLIKELY(!reachable_))
+ return true;
+ if (!checkTop())
+ return false;
+ TypeAndValue<Value> tv = valueStack_.popCopy();
+ if (!checkType(tv.type(), expectedType))
+ return false;
+ if (Output)
+ *value = tv.value();
+ return true;
+ }
+
+ // Pop the top of the value stack and discard the result.
+ MOZ_MUST_USE bool pop() {
+ if (MOZ_UNLIKELY(!reachable_))
+ return true;
+ if (!checkTop())
+ return false;
+ valueStack_.popBack();
+ return true;
+ }
+
+ // Read the top of the value stack (without popping it).
+ MOZ_MUST_USE bool top(TypeAndValue<Value>* tv) {
+ if (MOZ_UNLIKELY(!reachable_))
+ return true;
+ if (!checkTop())
+ return false;
+ *tv = valueStack_.back();
+ return true;
+ }
+
+ // Read the top of the value stack (without popping it) and check that it
+ // has the given type.
+ MOZ_MUST_USE bool topWithType(ValType expectedType, Value* value) {
+ if (MOZ_UNLIKELY(!reachable_))
+ return true;
+ if (!checkTop())
+ return false;
+ TypeAndValue<Value>& tv = valueStack_.back();
+ if (!checkType(tv.type(), expectedType))
+ return false;
+ if (Output)
+ *value = tv.value();
+ return true;
+ }
+
+ // Read the value stack entry at depth |index|.
+ MOZ_MUST_USE bool peek(uint32_t index, TypeAndValue<Value>* tv) {
+ MOZ_ASSERT(reachable_);
+ if (Validate && valueStack_.length() - controlStack_.back().valueStackStart() < index)
+ return fail("peeking at value from outside block");
+ *tv = valueStack_[valueStack_.length() - index];
+ return true;
+ }
+
+ bool getControl(uint32_t relativeDepth, ControlStackEntry<ControlItem>** controlEntry) {
+ if (Validate && relativeDepth >= controlStack_.length())
+ return fail("branch depth exceeds current nesting level");
+
+ *controlEntry = &controlStack_[controlStack_.length() - 1 - relativeDepth];
+ return true;
+ }
+
+ void enterUnreachableCode() {
+ valueStack_.shrinkTo(controlStack_.back().valueStackStart());
+ reachable_ = false;
+ }
+
+ bool checkBrValue(uint32_t relativeDepth, ExprType* type, Value* value);
+ bool checkBrIfValues(uint32_t relativeDepth, Value* condition, ExprType* type, Value* value);
+
+ public:
+ explicit OpIter(Decoder& decoder, uint32_t offsetInModule = 0)
+ : d_(decoder), offsetInModule_(offsetInModule), reachable_(true),
+ op_(Op::Limit), offsetOfExpr_(0)
+ {}
+
+ // Return the decoding byte offset.
+ uint32_t currentOffset() const { return d_.currentOffset(); }
+
+ // Returning the offset within the entire module of the last-read Op.
+ TrapOffset trapOffset() const {
+ return TrapOffset(offsetInModule_ + offsetOfExpr_);
+ }
+
+ // Test whether the iterator has reached the end of the buffer.
+ bool done() const { return d_.done(); }
+
+ // Report a general failure.
+ MOZ_MUST_USE bool fail(const char* msg) MOZ_COLD;
+
+ // Report an unimplemented feature.
+ MOZ_MUST_USE bool notYetImplemented(const char* what) MOZ_COLD;
+
+ // Report an unrecognized opcode.
+ MOZ_MUST_USE bool unrecognizedOpcode(uint32_t expr) MOZ_COLD;
+
+ // Test whether the iterator is currently in "reachable" code.
+ bool inReachableCode() const { return reachable_; }
+
+ // ------------------------------------------------------------------------
+ // Decoding and validation interface.
+
+ MOZ_MUST_USE bool readOp(uint16_t* op);
+ MOZ_MUST_USE bool readFunctionStart(ExprType ret);
+ MOZ_MUST_USE bool readFunctionEnd();
+ MOZ_MUST_USE bool readReturn(Value* value);
+ MOZ_MUST_USE bool readBlock();
+ MOZ_MUST_USE bool readLoop();
+ MOZ_MUST_USE bool readIf(Value* condition);
+ MOZ_MUST_USE bool readElse(ExprType* thenType, Value* thenValue);
+ MOZ_MUST_USE bool readEnd(LabelKind* kind, ExprType* type, Value* value);
+ MOZ_MUST_USE bool readBr(uint32_t* relativeDepth, ExprType* type, Value* value);
+ MOZ_MUST_USE bool readBrIf(uint32_t* relativeDepth, ExprType* type,
+ Value* value, Value* condition);
+ MOZ_MUST_USE bool readBrTable(uint32_t* tableLength, ExprType* type,
+ Value* value, Value* index);
+ MOZ_MUST_USE bool readBrTableEntry(ExprType* type, Value* value, uint32_t* depth);
+ MOZ_MUST_USE bool readBrTableDefault(ExprType* type, Value* value, uint32_t* depth);
+ MOZ_MUST_USE bool readUnreachable();
+ MOZ_MUST_USE bool readDrop();
+ MOZ_MUST_USE bool readUnary(ValType operandType, Value* input);
+ MOZ_MUST_USE bool readConversion(ValType operandType, ValType resultType, Value* input);
+ MOZ_MUST_USE bool readBinary(ValType operandType, Value* lhs, Value* rhs);
+ MOZ_MUST_USE bool readComparison(ValType operandType, Value* lhs, Value* rhs);
+ MOZ_MUST_USE bool readLoad(ValType resultType, uint32_t byteSize,
+ LinearMemoryAddress<Value>* addr);
+ MOZ_MUST_USE bool readStore(ValType resultType, uint32_t byteSize,
+ LinearMemoryAddress<Value>* addr, Value* value);
+ MOZ_MUST_USE bool readTeeStore(ValType resultType, uint32_t byteSize,
+ LinearMemoryAddress<Value>* addr, Value* value);
+ MOZ_MUST_USE bool readNop();
+ MOZ_MUST_USE bool readCurrentMemory();
+ MOZ_MUST_USE bool readGrowMemory(Value* input);
+ MOZ_MUST_USE bool readSelect(ValType* type,
+ Value* trueValue, Value* falseValue, Value* condition);
+ MOZ_MUST_USE bool readGetLocal(const ValTypeVector& locals, uint32_t* id);
+ MOZ_MUST_USE bool readSetLocal(const ValTypeVector& locals, uint32_t* id, Value* value);
+ MOZ_MUST_USE bool readTeeLocal(const ValTypeVector& locals, uint32_t* id, Value* value);
+ MOZ_MUST_USE bool readGetGlobal(const GlobalDescVector& globals, uint32_t* id);
+ MOZ_MUST_USE bool readSetGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value);
+ MOZ_MUST_USE bool readTeeGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value);
+ MOZ_MUST_USE bool readI32Const(int32_t* i32);
+ MOZ_MUST_USE bool readI64Const(int64_t* i64);
+ MOZ_MUST_USE bool readF32Const(RawF32* f32);
+ MOZ_MUST_USE bool readF64Const(RawF64* f64);
+ MOZ_MUST_USE bool readI8x16Const(I8x16* i8x16);
+ MOZ_MUST_USE bool readI16x8Const(I16x8* i16x8);
+ MOZ_MUST_USE bool readI32x4Const(I32x4* i32x4);
+ MOZ_MUST_USE bool readF32x4Const(F32x4* f32x4);
+ MOZ_MUST_USE bool readB8x16Const(I8x16* i8x16);
+ MOZ_MUST_USE bool readB16x8Const(I16x8* i16x8);
+ MOZ_MUST_USE bool readB32x4Const(I32x4* i32x4);
+ MOZ_MUST_USE bool readCall(uint32_t* calleeIndex);
+ MOZ_MUST_USE bool readCallIndirect(uint32_t* sigIndex, Value* callee);
+ MOZ_MUST_USE bool readOldCallIndirect(uint32_t* sigIndex);
+ MOZ_MUST_USE bool readCallArg(ValType type, uint32_t numArgs, uint32_t argIndex, Value* arg);
+ MOZ_MUST_USE bool readCallArgsEnd(uint32_t numArgs);
+ MOZ_MUST_USE bool readOldCallIndirectCallee(Value* callee);
+ MOZ_MUST_USE bool readCallReturn(ExprType ret);
+ MOZ_MUST_USE bool readAtomicLoad(LinearMemoryAddress<Value>* addr,
+ Scalar::Type* viewType);
+ MOZ_MUST_USE bool readAtomicStore(LinearMemoryAddress<Value>* addr,
+ Scalar::Type* viewType,
+ Value* value);
+ MOZ_MUST_USE bool readAtomicBinOp(LinearMemoryAddress<Value>* addr,
+ Scalar::Type* viewType,
+ jit::AtomicOp* op,
+ Value* value);
+ MOZ_MUST_USE bool readAtomicCompareExchange(LinearMemoryAddress<Value>* addr,
+ Scalar::Type* viewType,
+ Value* oldValue,
+ Value* newValue);
+ MOZ_MUST_USE bool readAtomicExchange(LinearMemoryAddress<Value>* addr,
+ Scalar::Type* viewType,
+ Value* newValue);
+ MOZ_MUST_USE bool readSimdComparison(ValType simdType, Value* lhs,
+ Value* rhs);
+ MOZ_MUST_USE bool readSimdShiftByScalar(ValType simdType, Value* lhs,
+ Value* rhs);
+ MOZ_MUST_USE bool readSimdBooleanReduction(ValType simdType, Value* input);
+ MOZ_MUST_USE bool readExtractLane(ValType simdType, uint8_t* lane,
+ Value* vector);
+ MOZ_MUST_USE bool readReplaceLane(ValType simdType, uint8_t* lane,
+ Value* vector, Value* scalar);
+ MOZ_MUST_USE bool readSplat(ValType simdType, Value* scalar);
+ MOZ_MUST_USE bool readSwizzle(ValType simdType, uint8_t (* lanes)[16], Value* vector);
+ MOZ_MUST_USE bool readShuffle(ValType simdType, uint8_t (* lanes)[16],
+ Value* lhs, Value* rhs);
+ MOZ_MUST_USE bool readSimdSelect(ValType simdType, Value* trueValue,
+ Value* falseValue,
+ Value* condition);
+ MOZ_MUST_USE bool readSimdCtor();
+ MOZ_MUST_USE bool readSimdCtorArg(ValType elementType, uint32_t numElements, uint32_t argIndex,
+ Value* arg);
+ MOZ_MUST_USE bool readSimdCtorArgsEnd(uint32_t numElements);
+ MOZ_MUST_USE bool readSimdCtorReturn(ValType simdType);
+
+ // At a location where readOp is allowed, peek at the next opcode
+ // without consuming it or updating any internal state.
+ // Never fails: returns uint16_t(Op::Limit) if it can't read.
+ uint16_t peekOp();
+
+ // ------------------------------------------------------------------------
+ // Stack management.
+
+ // Set the result value of the current top-of-value-stack expression.
+ void setResult(Value value) {
+ if (MOZ_LIKELY(reachable_))
+ valueStack_.back().setValue(value);
+ }
+
+ // Return the result value of the current top-of-value-stack expression.
+ Value getResult() {
+ MOZ_ASSERT(reachable_);
+ return valueStack_.back().value();
+ }
+
+ // Return a reference to the top of the control stack.
+ ControlItem& controlItem() {
+ return controlStack_.back().controlItem();
+ }
+
+ // Return the signature of the top of the control stack.
+ ExprType controlType() {
+ return controlStack_.back().type();
+ }
+
+ // Test whether the control-stack is empty, meaning we've consumed the final
+ // end of the function body.
+ bool controlStackEmpty() const {
+ return controlStack_.empty();
+ }
+};
+
+template <typename Policy>
+bool
+OpIter<Policy>::typeMismatch(ExprType actual, ExprType expected)
+{
+ MOZ_ASSERT(Validate);
+ MOZ_ASSERT(reachable_);
+
+ UniqueChars error(JS_smprintf("type mismatch: expression has type %s but expected %s",
+ ToCString(actual), ToCString(expected)));
+ if (!error)
+ return false;
+
+ return fail(error.get());
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::checkType(ValType actual, ValType expected)
+{
+ return checkType(ToExprType(actual), ToExprType(expected));
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::checkType(ExprType actual, ExprType expected)
+{
+ MOZ_ASSERT(reachable_);
+
+ if (!Validate) {
+ MOZ_ASSERT(actual == expected, "type mismatch");
+ return true;
+ }
+
+ if (MOZ_LIKELY(actual == expected))
+ return true;
+
+ return typeMismatch(actual, expected);
+}
+
+template <typename Policy>
+bool
+OpIter<Policy>::notYetImplemented(const char* what)
+{
+ UniqueChars error(JS_smprintf("not yet implemented: %s", what));
+ if (!error)
+ return false;
+
+ return fail(error.get());
+}
+
+template <typename Policy>
+bool
+OpIter<Policy>::unrecognizedOpcode(uint32_t expr)
+{
+ UniqueChars error(JS_smprintf("unrecognized opcode: %x", expr));
+ if (!error)
+ return false;
+
+ return fail(error.get());
+}
+
+template <typename Policy>
+bool
+OpIter<Policy>::fail(const char* msg)
+{
+ return d_.fail("%s", msg);
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::pushControl(LabelKind kind, ExprType type, bool reachable)
+{
+ return controlStack_.emplaceBack(kind, type, reachable, valueStack_.length());
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::mergeControl(LabelKind* kind, ExprType* type, Value* value)
+{
+ MOZ_ASSERT(!controlStack_.empty());
+
+ ControlStackEntry<ControlItem>& controlItem = controlStack_.back();
+ *kind = controlItem.kind();
+
+ if (reachable_) {
+ // Unlike branching, exiting a scope via fallthrough does not implicitly
+ // pop excess items on the stack.
+ size_t valueStackStart = controlItem.valueStackStart();
+ size_t valueStackLength = valueStack_.length();
+ MOZ_ASSERT(valueStackLength >= valueStackStart);
+ if (valueStackLength == valueStackStart) {
+ *type = ExprType::Void;
+ if (!checkType(ExprType::Void, controlItem.type()))
+ return false;
+ } else {
+ *type = controlItem.type();
+ if (Validate && valueStackLength - valueStackStart > (IsVoid(*type) ? 0u : 1u))
+ return fail("unused values not explicitly dropped by end of block");
+ if (!topWithType(NonVoidToValType(*type), value))
+ return false;
+ }
+ } else {
+ if (*kind != LabelKind::Loop && controlItem.reachable()) {
+ // There was no fallthrough path, but there was some other reachable
+ // branch to the end.
+ reachable_ = true;
+ *type = controlItem.type();
+ if (!IsVoid(*type)) {
+ if (!push(NonVoidToValType(*type)))
+ return false;
+ }
+ } else {
+ // No fallthrough and no branch to the end either; we remain
+ // unreachable.
+ *type = ExprType::Void;
+ }
+ if (Output)
+ *value = Value();
+ }
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::popControl(LabelKind* kind, ExprType* type, Value* value)
+{
+ if (!mergeControl(kind, type, value))
+ return false;
+
+ if (*kind == LabelKind::Then) {
+ // A reachable If without an Else. Forbid a result value.
+ if (reachable_) {
+ if (Validate && !IsVoid(*type))
+ return fail("if without else with a result value");
+ }
+ reachable_ = true;
+ }
+
+ controlStack_.popBack();
+
+ if (!reachable_ && !controlStack_.empty())
+ valueStack_.shrinkTo(controlStack_.back().valueStackStart());
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readBlockType(ExprType* type)
+{
+ uint8_t unchecked;
+ if (!d_.readBlockType(&unchecked))
+ return fail("unable to read block signature");
+
+ if (Validate) {
+ switch (unchecked) {
+ case uint8_t(ExprType::Void):
+ case uint8_t(ExprType::I32):
+ case uint8_t(ExprType::I64):
+ case uint8_t(ExprType::F32):
+ case uint8_t(ExprType::F64):
+ case uint8_t(ExprType::I8x16):
+ case uint8_t(ExprType::I16x8):
+ case uint8_t(ExprType::I32x4):
+ case uint8_t(ExprType::F32x4):
+ case uint8_t(ExprType::B8x16):
+ case uint8_t(ExprType::B16x8):
+ case uint8_t(ExprType::B32x4):
+ break;
+ default:
+ return fail("invalid inline block type");
+ }
+ }
+
+ *type = ExprType(unchecked);
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readOp(uint16_t* op)
+{
+ offsetOfExpr_ = d_.currentOffset();
+
+ if (Validate) {
+ if (MOZ_UNLIKELY(!d_.readOp(op)))
+ return fail("unable to read opcode");
+ } else {
+ *op = uint16_t(d_.uncheckedReadOp());
+ }
+
+ op_ = Op(*op); // debug-only
+
+ return true;
+}
+
+template <typename Policy>
+inline uint16_t
+OpIter<Policy>::peekOp()
+{
+ const uint8_t* pos = d_.currentPosition();
+ uint16_t op;
+
+ if (Validate) {
+ if (MOZ_UNLIKELY(!d_.readOp(&op)))
+ op = uint16_t(Op::Limit);
+ } else {
+ op = uint16_t(d_.uncheckedReadOp());
+ }
+
+ d_.rollbackPosition(pos);
+
+ return op;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readFunctionStart(ExprType ret)
+{
+ MOZ_ASSERT(valueStack_.empty());
+ MOZ_ASSERT(controlStack_.empty());
+ MOZ_ASSERT(Op(op_) == Op::Limit);
+ MOZ_ASSERT(reachable_);
+
+ return pushControl(LabelKind::Block, ret, false);
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readFunctionEnd()
+{
+ if (Validate) {
+ if (!controlStack_.empty())
+ return fail("unbalanced function body control flow");
+ } else {
+ MOZ_ASSERT(controlStack_.empty());
+ }
+
+ op_ = Op::Limit;
+ valueStack_.clear();
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readReturn(Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Return);
+
+ if (MOZ_LIKELY(reachable_)) {
+ ControlStackEntry<ControlItem>& controlItem = controlStack_[0];
+ MOZ_ASSERT(controlItem.kind() == LabelKind::Block);
+
+ controlItem.setReachable();
+
+ if (!IsVoid(controlItem.type())) {
+ if (!popWithType(NonVoidToValType(controlItem.type()), value))
+ return false;
+ }
+ }
+
+ enterUnreachableCode();
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readBlock()
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Block);
+
+ ExprType type = ExprType::Limit;
+ if (!readBlockType(&type))
+ return false;
+
+ return pushControl(LabelKind::Block, type, false);
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readLoop()
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Loop);
+
+ ExprType type = ExprType::Limit;
+ if (!readBlockType(&type))
+ return false;
+
+ return pushControl(LabelKind::Loop, type, reachable_);
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readIf(Value* condition)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::If);
+
+ ExprType type = ExprType::Limit;
+ if (!readBlockType(&type))
+ return false;
+
+ if (MOZ_LIKELY(reachable_)) {
+ if (!popWithType(ValType::I32, condition))
+ return false;
+
+ return pushControl(LabelKind::Then, type, false);
+ }
+
+ return pushControl(LabelKind::UnreachableThen, type, false);
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readElse(ExprType* thenType, Value* thenValue)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Else);
+
+ // Finish up the then arm.
+ ExprType type = ExprType::Limit;
+ LabelKind kind;
+ if (!mergeControl(&kind, &type, thenValue))
+ return false;
+
+ if (Output)
+ *thenType = type;
+
+ // Pop the old then value from the stack.
+ if (!IsVoid(type))
+ valueStack_.popBack();
+
+ if (Validate && kind != LabelKind::Then && kind != LabelKind::UnreachableThen)
+ return fail("else can only be used within an if");
+
+ // Switch to the else arm.
+ controlStack_.back().switchToElse(reachable_);
+
+ reachable_ = kind != LabelKind::UnreachableThen;
+
+ MOZ_ASSERT(valueStack_.length() == controlStack_.back().valueStackStart());
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readEnd(LabelKind* kind, ExprType* type, Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::End);
+
+ LabelKind validateKind = static_cast<LabelKind>(-1);
+ ExprType validateType = ExprType::Limit;
+ if (!popControl(&validateKind, &validateType, value))
+ return false;
+
+ if (Output) {
+ *kind = validateKind;
+ *type = validateType;
+ }
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::checkBrValue(uint32_t relativeDepth, ExprType* type, Value* value)
+{
+ if (MOZ_LIKELY(reachable_)) {
+ ControlStackEntry<ControlItem>* controlItem = nullptr;
+ if (!getControl(relativeDepth, &controlItem))
+ return false;
+
+ if (controlItem->kind() != LabelKind::Loop) {
+ controlItem->setReachable();
+
+ ExprType expectedType = controlItem->type();
+ if (Output)
+ *type = expectedType;
+
+ if (!IsVoid(expectedType))
+ return topWithType(NonVoidToValType(expectedType), value);
+ }
+ }
+
+ if (Output) {
+ *type = ExprType::Void;
+ *value = Value();
+ }
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readBr(uint32_t* relativeDepth, ExprType* type, Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Br);
+
+ uint32_t validateRelativeDepth;
+ if (!readVarU32(&validateRelativeDepth))
+ return fail("unable to read br depth");
+
+ if (!checkBrValue(validateRelativeDepth, type, value))
+ return false;
+
+ if (Output)
+ *relativeDepth = validateRelativeDepth;
+
+ enterUnreachableCode();
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::checkBrIfValues(uint32_t relativeDepth, Value* condition,
+ ExprType* type, Value* value)
+{
+ if (MOZ_LIKELY(reachable_)) {
+ if (!popWithType(ValType::I32, condition))
+ return false;
+
+ ControlStackEntry<ControlItem>* controlItem = nullptr;
+ if (!getControl(relativeDepth, &controlItem))
+ return false;
+
+ if (controlItem->kind() != LabelKind::Loop) {
+ controlItem->setReachable();
+
+ ExprType expectedType = controlItem->type();
+ if (Output)
+ *type = expectedType;
+
+ if (!IsVoid(expectedType))
+ return topWithType(NonVoidToValType(expectedType), value);
+ }
+ }
+
+ if (Output) {
+ *type = ExprType::Void;
+ *value = Value();
+ }
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readBrIf(uint32_t* relativeDepth, ExprType* type, Value* value, Value* condition)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::BrIf);
+
+ uint32_t validateRelativeDepth;
+ if (!readVarU32(&validateRelativeDepth))
+ return fail("unable to read br_if depth");
+
+ if (!checkBrIfValues(validateRelativeDepth, condition, type, value))
+ return false;
+
+ if (Output)
+ *relativeDepth = validateRelativeDepth;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readBrTable(uint32_t* tableLength, ExprType* type,
+ Value* value, Value* index)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::BrTable);
+
+ if (!readVarU32(tableLength))
+ return fail("unable to read br_table table length");
+
+ if (MOZ_LIKELY(reachable_)) {
+ if (!popWithType(ValType::I32, index))
+ return false;
+ }
+
+ // Set *type to indicate that we don't know the type yet.
+ *type = ExprType::Limit;
+ if (Output)
+ *value = Value();
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readBrTableEntry(ExprType* type, Value* value, uint32_t* depth)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::BrTable);
+
+ if (!readVarU32(depth))
+ return false;
+
+ ExprType knownType = *type;
+
+ if (MOZ_LIKELY(reachable_)) {
+ ControlStackEntry<ControlItem>* controlItem = nullptr;
+ if (!getControl(*depth, &controlItem))
+ return false;
+
+ if (controlItem->kind() != LabelKind::Loop) {
+ controlItem->setReachable();
+
+ // If we've already seen one label, we know the type and can check
+ // that the type for the current label matches it.
+ if (knownType != ExprType::Limit)
+ return checkType(knownType, controlItem->type());
+
+ // This is the first label; record the type and the value now.
+ ExprType expectedType = controlItem->type();
+ if (!IsVoid(expectedType)) {
+ *type = expectedType;
+ return popWithType(NonVoidToValType(expectedType), value);
+ }
+ }
+
+ if (knownType != ExprType::Limit && knownType != ExprType::Void)
+ return typeMismatch(knownType, ExprType::Void);
+ }
+
+ *type = ExprType::Void;
+ if (Output)
+ *value = Value();
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readBrTableDefault(ExprType* type, Value* value, uint32_t* depth)
+{
+ if (!readBrTableEntry(type, value, depth))
+ return false;
+
+ MOZ_ASSERT(!reachable_ || *type != ExprType::Limit);
+
+ enterUnreachableCode();
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readUnreachable()
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Unreachable);
+
+ enterUnreachableCode();
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readDrop()
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Drop);
+
+ if (!pop())
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readUnary(ValType operandType, Value* input)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Unary);
+
+ if (!popWithType(operandType, input))
+ return false;
+
+ infalliblePush(operandType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readConversion(ValType operandType, ValType resultType, Value* input)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Conversion);
+
+ if (!popWithType(operandType, input))
+ return false;
+
+ infalliblePush(resultType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readBinary(ValType operandType, Value* lhs, Value* rhs)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Binary);
+
+ if (!popWithType(operandType, rhs))
+ return false;
+
+ if (!popWithType(operandType, lhs))
+ return false;
+
+ infalliblePush(operandType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readComparison(ValType operandType, Value* lhs, Value* rhs)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Comparison);
+
+ if (!popWithType(operandType, rhs))
+ return false;
+
+ if (!popWithType(operandType, lhs))
+ return false;
+
+ infalliblePush(ValType::I32);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readLinearMemoryAddress(uint32_t byteSize, LinearMemoryAddress<Value>* addr)
+{
+ uint8_t alignLog2;
+ if (!readFixedU8(&alignLog2))
+ return fail("unable to read load alignment");
+
+ uint32_t unusedOffset;
+ if (!readVarU32(Output ? &addr->offset : &unusedOffset))
+ return fail("unable to read load offset");
+
+ if (Validate && (alignLog2 >= 32 || (uint32_t(1) << alignLog2) > byteSize))
+ return fail("greater than natural alignment");
+
+ Value unused;
+ if (!popWithType(ValType::I32, Output ? &addr->base : &unused))
+ return false;
+
+ if (Output)
+ addr->align = uint32_t(1) << alignLog2;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readLoad(ValType resultType, uint32_t byteSize, LinearMemoryAddress<Value>* addr)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Load);
+
+ if (!readLinearMemoryAddress(byteSize, addr))
+ return false;
+
+ infalliblePush(resultType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readStore(ValType resultType, uint32_t byteSize, LinearMemoryAddress<Value>* addr,
+ Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Store);
+
+ if (!popWithType(resultType, value))
+ return false;
+
+ if (!readLinearMemoryAddress(byteSize, addr))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readTeeStore(ValType resultType, uint32_t byteSize, LinearMemoryAddress<Value>* addr,
+ Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::TeeStore);
+
+ if (!popWithType(resultType, value))
+ return false;
+
+ if (!readLinearMemoryAddress(byteSize, addr))
+ return false;
+
+ infalliblePush(TypeAndValue<Value>(resultType, Output ? *value : Value()));
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readNop()
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Nop);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readCurrentMemory()
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::CurrentMemory);
+
+ uint32_t flags;
+ if (!readVarU32(&flags))
+ return false;
+
+ if (Validate && flags != uint32_t(MemoryTableFlags::Default))
+ return fail("unexpected flags");
+
+ if (!push(ValType::I32))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readGrowMemory(Value* input)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::GrowMemory);
+
+ uint32_t flags;
+ if (!readVarU32(&flags))
+ return false;
+
+ if (Validate && flags != uint32_t(MemoryTableFlags::Default))
+ return fail("unexpected flags");
+
+ if (!popWithType(ValType::I32, input))
+ return false;
+
+ infalliblePush(ValType::I32);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSelect(ValType* type, Value* trueValue, Value* falseValue, Value* condition)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Select);
+
+ if (!popWithType(ValType::I32, condition))
+ return false;
+
+ TypeAndValue<Value> false_;
+ if (!pop(&false_))
+ return false;
+
+ TypeAndValue<Value> true_;
+ if (!pop(&true_))
+ return false;
+
+ ValType resultType = true_.type();
+ if (Validate && resultType != false_.type())
+ return fail("select operand types must match");
+
+ infalliblePush(resultType);
+
+ if (Output) {
+ *type = resultType;
+ *trueValue = true_.value();
+ *falseValue = false_.value();
+ }
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readGetLocal(const ValTypeVector& locals, uint32_t* id)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::GetLocal);
+
+ uint32_t validateId;
+ if (!readVarU32(&validateId))
+ return false;
+
+ if (Validate && validateId >= locals.length())
+ return fail("get_local index out of range");
+
+ if (!push(locals[validateId]))
+ return false;
+
+ if (Output)
+ *id = validateId;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSetLocal(const ValTypeVector& locals, uint32_t* id, Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SetLocal);
+
+ uint32_t validateId;
+ if (!readVarU32(&validateId))
+ return false;
+
+ if (Validate && validateId >= locals.length())
+ return fail("set_local index out of range");
+
+ if (!popWithType(locals[validateId], value))
+ return false;
+
+ if (Output)
+ *id = validateId;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readTeeLocal(const ValTypeVector& locals, uint32_t* id, Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::TeeLocal);
+
+ uint32_t validateId;
+ if (!readVarU32(&validateId))
+ return false;
+
+ if (Validate && validateId >= locals.length())
+ return fail("set_local index out of range");
+
+ if (!topWithType(locals[validateId], value))
+ return false;
+
+ if (Output)
+ *id = validateId;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readGetGlobal(const GlobalDescVector& globals, uint32_t* id)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::GetGlobal);
+
+ uint32_t validateId;
+ if (!readVarU32(&validateId))
+ return false;
+
+ if (Validate && validateId >= globals.length())
+ return fail("get_global index out of range");
+
+ if (!push(globals[validateId].type()))
+ return false;
+
+ if (Output)
+ *id = validateId;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSetGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SetGlobal);
+
+ uint32_t validateId;
+ if (!readVarU32(&validateId))
+ return false;
+
+ if (Validate && validateId >= globals.length())
+ return fail("set_global index out of range");
+
+ if (Validate && !globals[validateId].isMutable())
+ return fail("can't write an immutable global");
+
+ if (!popWithType(globals[validateId].type(), value))
+ return false;
+
+ if (Output)
+ *id = validateId;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readTeeGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::TeeGlobal);
+
+ uint32_t validateId;
+ if (!readVarU32(&validateId))
+ return false;
+
+ if (Validate && validateId >= globals.length())
+ return fail("set_global index out of range");
+
+ if (Validate && !globals[validateId].isMutable())
+ return fail("can't write an immutable global");
+
+ if (!topWithType(globals[validateId].type(), value))
+ return false;
+
+ if (Output)
+ *id = validateId;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readI32Const(int32_t* i32)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::I32);
+
+ int32_t unused;
+ if (!readVarS32(Output ? i32 : &unused))
+ return false;
+
+ if (!push(ValType::I32))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readI64Const(int64_t* i64)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::I64);
+
+ int64_t unused;
+ if (!readVarS64(Output ? i64 : &unused))
+ return false;
+
+ if (!push(ValType::I64))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readF32Const(RawF32* f32)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::F32);
+
+ RawF32 unused;
+ if (!readFixedF32(Output ? f32 : &unused))
+ return false;
+
+ if (!push(ValType::F32))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readF64Const(RawF64* f64)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::F64);
+
+ RawF64 unused;
+ if (!readFixedF64(Output ? f64 : &unused))
+ return false;
+
+ if (!push(ValType::F64))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readI8x16Const(I8x16* i8x16)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::I8x16);
+
+ I8x16 unused;
+ if (!readFixedI8x16(Output ? i8x16 : &unused))
+ return false;
+
+ if (!push(ValType::I8x16))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readI16x8Const(I16x8* i16x8)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::I16x8);
+
+ I16x8 unused;
+ if (!readFixedI16x8(Output ? i16x8 : &unused))
+ return false;
+
+ if (!push(ValType::I16x8))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readI32x4Const(I32x4* i32x4)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::I32x4);
+
+ I32x4 unused;
+ if (!readFixedI32x4(Output ? i32x4 : &unused))
+ return false;
+
+ if (!push(ValType::I32x4))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readF32x4Const(F32x4* f32x4)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::F32x4);
+
+ F32x4 unused;
+ if (!readFixedF32x4(Output ? f32x4 : &unused))
+ return false;
+
+ if (!push(ValType::F32x4))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readB8x16Const(I8x16* i8x16)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::B8x16);
+
+ I8x16 unused;
+ if (!readFixedI8x16(Output ? i8x16 : &unused))
+ return false;
+
+ if (!push(ValType::B8x16))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readB16x8Const(I16x8* i16x8)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::B16x8);
+
+ I16x8 unused;
+ if (!readFixedI16x8(Output ? i16x8 : &unused))
+ return false;
+
+ if (!push(ValType::B16x8))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readB32x4Const(I32x4* i32x4)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::B32x4);
+
+ I32x4 unused;
+ if (!readFixedI32x4(Output ? i32x4 : &unused))
+ return false;
+
+ if (!push(ValType::B32x4))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readCall(uint32_t* calleeIndex)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Call);
+
+ if (!readVarU32(calleeIndex))
+ return fail("unable to read call function index");
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readCallIndirect(uint32_t* sigIndex, Value* callee)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::CallIndirect);
+
+ if (!readVarU32(sigIndex))
+ return fail("unable to read call_indirect signature index");
+
+ uint32_t flags;
+ if (!readVarU32(&flags))
+ return false;
+
+ if (Validate && flags != uint32_t(MemoryTableFlags::Default))
+ return fail("unexpected flags");
+
+ if (reachable_) {
+ if (!popWithType(ValType::I32, callee))
+ return false;
+ }
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readOldCallIndirect(uint32_t* sigIndex)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::OldCallIndirect);
+
+ if (!readVarU32(sigIndex))
+ return fail("unable to read call_indirect signature index");
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readCallArg(ValType type, uint32_t numArgs, uint32_t argIndex, Value* arg)
+{
+ MOZ_ASSERT(reachable_);
+
+ TypeAndValue<Value> tv;
+
+ if (!peek(numArgs - argIndex, &tv))
+ return false;
+ if (!checkType(tv.type(), type))
+ return false;
+
+ if (Output)
+ *arg = tv.value();
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readCallArgsEnd(uint32_t numArgs)
+{
+ MOZ_ASSERT(reachable_);
+ MOZ_ASSERT(numArgs <= valueStack_.length());
+
+ valueStack_.shrinkBy(numArgs);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readOldCallIndirectCallee(Value* callee)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::OldCallIndirect);
+ MOZ_ASSERT(reachable_);
+
+ if (!popWithType(ValType::I32, callee))
+ return false;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readCallReturn(ExprType ret)
+{
+ MOZ_ASSERT(reachable_);
+
+ if (!IsVoid(ret)) {
+ if (!push(NonVoidToValType(ret)))
+ return false;
+ }
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readAtomicLoad(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::AtomicLoad);
+
+ Scalar::Type validateViewType;
+ if (!readAtomicViewType(&validateViewType))
+ return false;
+
+ uint32_t byteSize = Scalar::byteSize(validateViewType);
+ if (!readLinearMemoryAddress(byteSize, addr))
+ return false;
+
+ infalliblePush(ValType::I32);
+
+ if (Output)
+ *viewType = validateViewType;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readAtomicStore(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType,
+ Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::AtomicStore);
+
+ Scalar::Type validateViewType;
+ if (!readAtomicViewType(&validateViewType))
+ return false;
+
+ uint32_t byteSize = Scalar::byteSize(validateViewType);
+ if (!readLinearMemoryAddress(byteSize, addr))
+ return false;
+
+ if (!popWithType(ValType::I32, value))
+ return false;
+
+ infalliblePush(ValType::I32);
+
+ if (Output)
+ *viewType = validateViewType;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readAtomicBinOp(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType,
+ jit::AtomicOp* op, Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::AtomicBinOp);
+
+ Scalar::Type validateViewType;
+ if (!readAtomicViewType(&validateViewType))
+ return false;
+
+ if (!readAtomicBinOpOp(op))
+ return false;
+
+ uint32_t byteSize = Scalar::byteSize(validateViewType);
+ if (!readLinearMemoryAddress(byteSize, addr))
+ return false;
+
+ if (!popWithType(ValType::I32, value))
+ return false;
+
+ infalliblePush(ValType::I32);
+
+ if (Output)
+ *viewType = validateViewType;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readAtomicCompareExchange(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType,
+ Value* oldValue, Value* newValue)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::AtomicCompareExchange);
+
+ Scalar::Type validateViewType;
+ if (!readAtomicViewType(&validateViewType))
+ return false;
+
+ uint32_t byteSize = Scalar::byteSize(validateViewType);
+ if (!readLinearMemoryAddress(byteSize, addr))
+ return false;
+
+ if (!popWithType(ValType::I32, newValue))
+ return false;
+
+ if (!popWithType(ValType::I32, oldValue))
+ return false;
+
+ infalliblePush(ValType::I32);
+
+ if (Output)
+ *viewType = validateViewType;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readAtomicExchange(LinearMemoryAddress<Value>* addr, Scalar::Type* viewType,
+ Value* value)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::AtomicExchange);
+
+ Scalar::Type validateViewType;
+ if (!readAtomicViewType(&validateViewType))
+ return false;
+
+ uint32_t byteSize = Scalar::byteSize(validateViewType);
+ if (!readLinearMemoryAddress(byteSize, addr))
+ return false;
+
+ if (!popWithType(ValType::I32, value))
+ return false;
+
+ infalliblePush(ValType::I32);
+
+ if (Output)
+ *viewType = validateViewType;
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSimdComparison(ValType simdType, Value* lhs, Value* rhs)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SimdComparison);
+
+ if (!popWithType(simdType, rhs))
+ return false;
+
+ if (!popWithType(simdType, lhs))
+ return false;
+
+ infalliblePush(SimdBoolType(simdType));
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSimdShiftByScalar(ValType simdType, Value* lhs, Value* rhs)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SimdShiftByScalar);
+
+ if (!popWithType(ValType::I32, rhs))
+ return false;
+
+ if (!popWithType(simdType, lhs))
+ return false;
+
+ infalliblePush(simdType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSimdBooleanReduction(ValType simdType, Value* input)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SimdBooleanReduction);
+
+ if (!popWithType(simdType, input))
+ return false;
+
+ infalliblePush(ValType::I32);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readExtractLane(ValType simdType, uint8_t* lane, Value* vector)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::ExtractLane);
+
+ uint32_t laneBits;
+ if (!readVarU32(&laneBits))
+ return false;
+
+ if (Validate && laneBits >= NumSimdElements(simdType))
+ return fail("simd lane out of bounds for simd type");
+
+ if (!popWithType(simdType, vector))
+ return false;
+
+ infalliblePush(SimdElementType(simdType));
+
+ if (Output)
+ *lane = uint8_t(laneBits);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readReplaceLane(ValType simdType, uint8_t* lane, Value* vector, Value* scalar)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::ReplaceLane);
+
+ uint32_t laneBits;
+ if (!readVarU32(&laneBits))
+ return false;
+
+ if (Validate && laneBits >= NumSimdElements(simdType))
+ return fail("simd lane out of bounds for simd type");
+
+ if (!popWithType(SimdElementType(simdType), scalar))
+ return false;
+
+ if (!popWithType(simdType, vector))
+ return false;
+
+ infalliblePush(simdType);
+
+ if (Output)
+ *lane = uint8_t(laneBits);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSplat(ValType simdType, Value* scalar)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Splat);
+
+ if (!popWithType(SimdElementType(simdType), scalar))
+ return false;
+
+ infalliblePush(simdType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSwizzle(ValType simdType, uint8_t (* lanes)[16], Value* vector)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Swizzle);
+
+ uint32_t numSimdLanes = NumSimdElements(simdType);
+ MOZ_ASSERT(numSimdLanes <= mozilla::ArrayLength(*lanes));
+ for (uint32_t i = 0; i < numSimdLanes; ++i) {
+ uint8_t validateLane;
+ if (!readFixedU8(Output ? &(*lanes)[i] : &validateLane))
+ return fail("unable to read swizzle lane");
+ if (Validate && (Output ? (*lanes)[i] : validateLane) >= numSimdLanes)
+ return fail("swizzle index out of bounds");
+ }
+
+ if (!popWithType(simdType, vector))
+ return false;
+
+ infalliblePush(simdType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readShuffle(ValType simdType, uint8_t (* lanes)[16], Value* lhs, Value* rhs)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::Shuffle);
+
+ uint32_t numSimdLanes = NumSimdElements(simdType);
+ MOZ_ASSERT(numSimdLanes <= mozilla::ArrayLength(*lanes));
+ for (uint32_t i = 0; i < numSimdLanes; ++i) {
+ uint8_t validateLane;
+ if (!readFixedU8(Output ? &(*lanes)[i] : &validateLane))
+ return fail("unable to read shuffle lane");
+ if (Validate && (Output ? (*lanes)[i] : validateLane) >= numSimdLanes * 2)
+ return fail("shuffle index out of bounds");
+ }
+
+ if (!popWithType(simdType, rhs))
+ return false;
+
+ if (!popWithType(simdType, lhs))
+ return false;
+
+ infalliblePush(simdType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSimdSelect(ValType simdType, Value* trueValue, Value* falseValue,
+ Value* condition)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SimdSelect);
+
+ if (!popWithType(simdType, falseValue))
+ return false;
+ if (!popWithType(simdType, trueValue))
+ return false;
+ if (!popWithType(SimdBoolType(simdType), condition))
+ return false;
+
+ infalliblePush(simdType);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSimdCtor()
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SimdCtor);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSimdCtorArg(ValType elementType, uint32_t numElements, uint32_t index,
+ Value* arg)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SimdCtor);
+ MOZ_ASSERT(numElements > 0);
+
+ TypeAndValue<Value> tv;
+
+ if (!peek(numElements - index, &tv))
+ return false;
+ if (!checkType(tv.type(), elementType))
+ return false;
+
+ *arg = tv.value();
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSimdCtorArgsEnd(uint32_t numElements)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SimdCtor);
+ MOZ_ASSERT(numElements <= valueStack_.length());
+
+ valueStack_.shrinkBy(numElements);
+
+ return true;
+}
+
+template <typename Policy>
+inline bool
+OpIter<Policy>::readSimdCtorReturn(ValType simdType)
+{
+ MOZ_ASSERT(Classify(op_) == OpKind::SimdCtor);
+
+ infalliblePush(simdType);
+
+ return true;
+}
+
+} // namespace wasm
+} // namespace js
+
+namespace mozilla {
+
+// Specialize IsPod for the Nothing specializations.
+template<> struct IsPod<js::wasm::TypeAndValue<Nothing>> : TrueType {};
+template<> struct IsPod<js::wasm::ControlStackEntry<Nothing>> : TrueType {};
+
+} // namespace mozilla
+
+#endif // wasm_iterator_h