Mypal/js/src/wasm/WasmBinaryIterator.h
2019-03-11 13:26:37 +03:00

2247 lines
59 KiB
C++

/* -*- 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