mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
Bug 1577508 - Allow WebAssembly blocks to return multiple values r=luke
Differential Revision: https://phabricator.services.mozilla.com/D43977 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
72b2e89d13
commit
7ba23d10fa
@ -230,10 +230,19 @@ assertErrorMessage(() => wasmEval(moduleWithSections([
|
||||
nameSection([moduleNameSubsection('hi')])])
|
||||
).f(), RuntimeError, /unreachable/);
|
||||
|
||||
// Diagnose nonstandard block signature types.
|
||||
for (var bad of [0xff, 0, 1, 0x3f])
|
||||
// Diagnose invalid block signature types.
|
||||
for (var bad of [0xff, 1, 0x3f])
|
||||
assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, bad, EndCode]})])])), CompileError, /invalid .*block type/);
|
||||
|
||||
if (wasmMultiValueEnabled()) {
|
||||
// In this test module, 0 denotes a void-to-void block type.
|
||||
let binary = moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, 0, EndCode]})])]);
|
||||
assertEq(WebAssembly.validate(binary), true);
|
||||
} else {
|
||||
const bad = 0;
|
||||
assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, bad, EndCode]})])])), CompileError, /invalid .*block type/);
|
||||
}
|
||||
|
||||
// Ensure all invalid opcodes rejected
|
||||
for (let op of undefinedOpcodes) {
|
||||
let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[op]})])]);
|
||||
|
@ -18,7 +18,7 @@ const invalidRefBlockType = funcBody({locals:[], body:[
|
||||
0x42,
|
||||
EndCode,
|
||||
]});
|
||||
checkInvalid(invalidRefBlockType, /invalid inline block type/);
|
||||
checkInvalid(invalidRefBlockType, /ref/);
|
||||
|
||||
const invalidTooBigRefType = funcBody({locals:[], body:[
|
||||
BlockCode,
|
||||
@ -26,4 +26,4 @@ const invalidTooBigRefType = funcBody({locals:[], body:[
|
||||
varU32(1000000),
|
||||
EndCode,
|
||||
]});
|
||||
checkInvalid(invalidTooBigRefType, /invalid inline block type/);
|
||||
checkInvalid(invalidTooBigRefType, /ref/);
|
||||
|
@ -202,7 +202,7 @@ wasmFailValidateText(`
|
||||
(br_table 1 0 (i32.const 15))
|
||||
)
|
||||
)
|
||||
)`, /br_table operand must be subtype of all target types/);
|
||||
)`, /br_table targets must all have the same arity/);
|
||||
|
||||
wasmFailValidateText(`
|
||||
(module
|
||||
@ -212,7 +212,7 @@ wasmFailValidateText(`
|
||||
(br_table 1 0 (i32.const 15))
|
||||
)
|
||||
)
|
||||
)`, /br_table operand must be subtype of all target types/);
|
||||
)`, /br_table targets must all have the same arity/);
|
||||
|
||||
wasmValidateText(`
|
||||
(module
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -42,7 +42,13 @@ enum class SectionId {
|
||||
GcFeatureOptIn = 42 // Arbitrary, but fits in 7 bits
|
||||
};
|
||||
|
||||
// WebAssembly type encodings are all single-byte negative SLEB128s, hence:
|
||||
// forall tc:TypeCode. ((tc & SLEB128SignMask) == SLEB128SignBit
|
||||
static const uint8_t SLEB128SignMask = 0xc0;
|
||||
static const uint8_t SLEB128SignBit = 0x40;
|
||||
|
||||
enum class TypeCode {
|
||||
|
||||
I32 = 0x7f, // SLEB128(-0x01)
|
||||
I64 = 0x7e, // SLEB128(-0x02)
|
||||
F32 = 0x7d, // SLEB128(-0x03)
|
||||
@ -63,7 +69,7 @@ enum class TypeCode {
|
||||
// Type constructor for structure types - unofficial
|
||||
Struct = 0x50, // SLEB128(-0x30)
|
||||
|
||||
// Special code representing the block signature ()->()
|
||||
// The 'empty' case of blocktype.
|
||||
BlockVoid = 0x40, // SLEB128(-0x40)
|
||||
|
||||
// Type designator for null - unofficial, will not appear in the binary format
|
||||
|
@ -43,10 +43,12 @@ using mozilla::Some;
|
||||
namespace {
|
||||
|
||||
typedef Vector<MBasicBlock*, 8, SystemAllocPolicy> BlockVector;
|
||||
typedef Vector<MDefinition*, 8, SystemAllocPolicy> DefVector;
|
||||
|
||||
struct IonCompilePolicy {
|
||||
// We store SSA definitions in the value stack.
|
||||
typedef MDefinition* Value;
|
||||
typedef DefVector ValueVector;
|
||||
|
||||
// We store loop headers and then/else blocks in the control flow stack.
|
||||
typedef MBasicBlock* ControlItem;
|
||||
@ -1151,23 +1153,18 @@ class FunctionCompiler {
|
||||
|
||||
inline bool inDeadCode() const { return curBlock_ == nullptr; }
|
||||
|
||||
void returnExpr(MDefinition* operand) {
|
||||
void returnValues(const DefVector& values) {
|
||||
if (inDeadCode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MWasmReturn* ins = MWasmReturn::New(alloc(), operand);
|
||||
curBlock_->end(ins);
|
||||
curBlock_ = nullptr;
|
||||
}
|
||||
MOZ_ASSERT(values.length() <= 1, "until multi-return");
|
||||
|
||||
void returnVoid() {
|
||||
if (inDeadCode()) {
|
||||
return;
|
||||
if (values.empty()) {
|
||||
curBlock_->end(MWasmReturnVoid::New(alloc()));
|
||||
} else {
|
||||
curBlock_->end(MWasmReturn::New(alloc(), values[0]));
|
||||
}
|
||||
|
||||
MWasmReturnVoid* ins = MWasmReturnVoid::New(alloc());
|
||||
curBlock_->end(ins);
|
||||
curBlock_ = nullptr;
|
||||
}
|
||||
|
||||
@ -1183,39 +1180,42 @@ class FunctionCompiler {
|
||||
}
|
||||
|
||||
private:
|
||||
static bool hasPushed(MBasicBlock* block) {
|
||||
uint32_t numPushed = block->stackDepth() - block->info().firstStackSlot();
|
||||
MOZ_ASSERT(numPushed == 0 || numPushed == 1);
|
||||
return numPushed;
|
||||
static uint32_t numPushed(MBasicBlock* block) {
|
||||
return block->stackDepth() - block->info().firstStackSlot();
|
||||
}
|
||||
|
||||
public:
|
||||
void pushDef(MDefinition* def) {
|
||||
void pushDefs(const DefVector& defs) {
|
||||
if (inDeadCode()) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(!hasPushed(curBlock_));
|
||||
if (def && def->type() != MIRType::None) {
|
||||
MOZ_ASSERT(numPushed(curBlock_) == 0);
|
||||
for (MDefinition* def : defs) {
|
||||
MOZ_ASSERT(def->type() != MIRType::None);
|
||||
curBlock_->push(def);
|
||||
}
|
||||
}
|
||||
|
||||
MDefinition* popDefIfPushed() {
|
||||
if (!hasPushed(curBlock_)) {
|
||||
return nullptr;
|
||||
bool popPushedDefs(DefVector* defs) {
|
||||
size_t n = numPushed(curBlock_);
|
||||
if (!defs->resizeUninitialized(n)) {
|
||||
return false;
|
||||
}
|
||||
MDefinition* def = curBlock_->pop();
|
||||
MOZ_ASSERT(def->type() != MIRType::Value);
|
||||
return def;
|
||||
for (; n > 0; n--) {
|
||||
MDefinition* def = curBlock_->pop();
|
||||
MOZ_ASSERT(def->type() != MIRType::Value);
|
||||
(*defs)[n - 1] = def;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void addJoinPredecessor(MDefinition* def, MBasicBlock** joinPred) {
|
||||
void addJoinPredecessor(const DefVector& defs, MBasicBlock** joinPred) {
|
||||
*joinPred = curBlock_;
|
||||
if (inDeadCode()) {
|
||||
return;
|
||||
}
|
||||
pushDef(def);
|
||||
pushDefs(defs);
|
||||
}
|
||||
|
||||
public:
|
||||
@ -1241,15 +1241,15 @@ class FunctionCompiler {
|
||||
}
|
||||
|
||||
bool switchToElse(MBasicBlock* elseBlock, MBasicBlock** thenJoinPred) {
|
||||
MDefinition* ifDef;
|
||||
if (!finishBlock(&ifDef)) {
|
||||
DefVector values;
|
||||
if (!finishBlock(&values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!elseBlock) {
|
||||
*thenJoinPred = nullptr;
|
||||
} else {
|
||||
addJoinPredecessor(ifDef, thenJoinPred);
|
||||
addJoinPredecessor(values, thenJoinPred);
|
||||
|
||||
curBlock_ = elseBlock;
|
||||
mirGraph().moveBlockToEnd(curBlock_);
|
||||
@ -1258,47 +1258,44 @@ class FunctionCompiler {
|
||||
return startBlock();
|
||||
}
|
||||
|
||||
bool joinIfElse(MBasicBlock* thenJoinPred, MDefinition** def) {
|
||||
MDefinition* elseDef;
|
||||
if (!finishBlock(&elseDef)) {
|
||||
bool joinIfElse(MBasicBlock* thenJoinPred, DefVector* defs) {
|
||||
DefVector values;
|
||||
if (!finishBlock(&values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!thenJoinPred && inDeadCode()) {
|
||||
*def = nullptr;
|
||||
} else {
|
||||
MBasicBlock* elseJoinPred;
|
||||
addJoinPredecessor(elseDef, &elseJoinPred);
|
||||
|
||||
mozilla::Array<MBasicBlock*, 2> blocks;
|
||||
size_t numJoinPreds = 0;
|
||||
if (thenJoinPred) {
|
||||
blocks[numJoinPreds++] = thenJoinPred;
|
||||
}
|
||||
if (elseJoinPred) {
|
||||
blocks[numJoinPreds++] = elseJoinPred;
|
||||
}
|
||||
|
||||
if (numJoinPreds == 0) {
|
||||
*def = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
MBasicBlock* join;
|
||||
if (!goToNewBlock(blocks[0], &join)) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 1; i < numJoinPreds; ++i) {
|
||||
if (!goToExistingBlock(blocks[i], join)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
curBlock_ = join;
|
||||
*def = popDefIfPushed();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
MBasicBlock* elseJoinPred;
|
||||
addJoinPredecessor(values, &elseJoinPred);
|
||||
|
||||
mozilla::Array<MBasicBlock*, 2> blocks;
|
||||
size_t numJoinPreds = 0;
|
||||
if (thenJoinPred) {
|
||||
blocks[numJoinPreds++] = thenJoinPred;
|
||||
}
|
||||
if (elseJoinPred) {
|
||||
blocks[numJoinPreds++] = elseJoinPred;
|
||||
}
|
||||
|
||||
if (numJoinPreds == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MBasicBlock* join;
|
||||
if (!goToNewBlock(blocks[0], &join)) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 1; i < numJoinPreds; ++i) {
|
||||
if (!goToExistingBlock(blocks[i], join)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
curBlock_ = join;
|
||||
return popPushedDefs(defs);
|
||||
}
|
||||
|
||||
bool startBlock() {
|
||||
@ -1308,10 +1305,10 @@ class FunctionCompiler {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool finishBlock(MDefinition** def) {
|
||||
bool finishBlock(DefVector* defs) {
|
||||
MOZ_ASSERT(blockDepth_);
|
||||
uint32_t topLabel = --blockDepth_;
|
||||
return bindBranches(topLabel, def);
|
||||
return bindBranches(topLabel, defs);
|
||||
}
|
||||
|
||||
bool startLoop(MBasicBlock** loopHeader) {
|
||||
@ -1401,7 +1398,7 @@ class FunctionCompiler {
|
||||
}
|
||||
|
||||
public:
|
||||
bool closeLoop(MBasicBlock* loopHeader, MDefinition** loopResult) {
|
||||
bool closeLoop(MBasicBlock* loopHeader, DefVector* loopResults) {
|
||||
MOZ_ASSERT(blockDepth_ >= 1);
|
||||
MOZ_ASSERT(loopDepth_);
|
||||
|
||||
@ -1413,7 +1410,6 @@ class FunctionCompiler {
|
||||
blockPatches_[headerLabel].empty());
|
||||
blockDepth_--;
|
||||
loopDepth_--;
|
||||
*loopResult = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1428,7 +1424,7 @@ class FunctionCompiler {
|
||||
// branches as forward jumps to a single backward jump. This is
|
||||
// unfortunate but the optimizer is able to fold these into single jumps
|
||||
// to backedges.
|
||||
MDefinition* _;
|
||||
DefVector _;
|
||||
if (!bindBranches(headerLabel, &_)) {
|
||||
return false;
|
||||
}
|
||||
@ -1437,7 +1433,7 @@ class FunctionCompiler {
|
||||
|
||||
if (curBlock_) {
|
||||
// We're on the loop backedge block, created by bindBranches.
|
||||
if (hasPushed(curBlock_)) {
|
||||
for (size_t i = 0, n = numPushed(curBlock_); i != n; i++) {
|
||||
curBlock_->pop();
|
||||
}
|
||||
|
||||
@ -1462,8 +1458,7 @@ class FunctionCompiler {
|
||||
}
|
||||
|
||||
blockDepth_ -= 1;
|
||||
*loopResult = inDeadCode() ? nullptr : popDefIfPushed();
|
||||
return true;
|
||||
return inDeadCode() || popPushedDefs(loopResults);
|
||||
}
|
||||
|
||||
bool addControlFlowPatch(MControlInstruction* ins, uint32_t relative,
|
||||
@ -1479,7 +1474,7 @@ class FunctionCompiler {
|
||||
return blockPatches_[absolute].append(ControlFlowPatch(ins, index));
|
||||
}
|
||||
|
||||
bool br(uint32_t relativeDepth, MDefinition* maybeValue) {
|
||||
bool br(uint32_t relativeDepth, const DefVector& values) {
|
||||
if (inDeadCode()) {
|
||||
return true;
|
||||
}
|
||||
@ -1489,14 +1484,14 @@ class FunctionCompiler {
|
||||
return false;
|
||||
}
|
||||
|
||||
pushDef(maybeValue);
|
||||
pushDefs(values);
|
||||
|
||||
curBlock_->end(jump);
|
||||
curBlock_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool brIf(uint32_t relativeDepth, MDefinition* maybeValue,
|
||||
bool brIf(uint32_t relativeDepth, const DefVector& values,
|
||||
MDefinition* condition) {
|
||||
if (inDeadCode()) {
|
||||
return true;
|
||||
@ -1512,7 +1507,7 @@ class FunctionCompiler {
|
||||
return false;
|
||||
}
|
||||
|
||||
pushDef(maybeValue);
|
||||
pushDefs(values);
|
||||
|
||||
curBlock_->end(test);
|
||||
curBlock_ = joinBlock;
|
||||
@ -1520,7 +1515,7 @@ class FunctionCompiler {
|
||||
}
|
||||
|
||||
bool brTable(MDefinition* operand, uint32_t defaultDepth,
|
||||
const Uint32Vector& depths, MDefinition* maybeValue) {
|
||||
const Uint32Vector& depths, const DefVector& values) {
|
||||
if (inDeadCode()) {
|
||||
return true;
|
||||
}
|
||||
@ -1573,7 +1568,7 @@ class FunctionCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
pushDef(maybeValue);
|
||||
pushDefs(values);
|
||||
|
||||
curBlock_->end(table);
|
||||
curBlock_ = nullptr;
|
||||
@ -1621,10 +1616,9 @@ class FunctionCompiler {
|
||||
return next->addPredecessor(alloc(), prev);
|
||||
}
|
||||
|
||||
bool bindBranches(uint32_t absolute, MDefinition** def) {
|
||||
bool bindBranches(uint32_t absolute, DefVector* defs) {
|
||||
if (absolute >= blockPatches_.length() || blockPatches_[absolute].empty()) {
|
||||
*def = inDeadCode() ? nullptr : popDefIfPushed();
|
||||
return true;
|
||||
return inDeadCode() || popPushedDefs(defs);
|
||||
}
|
||||
|
||||
ControlFlowPatchVector& patches = blockPatches_[absolute];
|
||||
@ -1664,7 +1658,9 @@ class FunctionCompiler {
|
||||
|
||||
curBlock_ = join;
|
||||
|
||||
*def = popDefIfPushed();
|
||||
if (!popPushedDefs(defs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
patches.clear();
|
||||
return true;
|
||||
@ -1754,11 +1750,13 @@ static bool EmitF64Const(FunctionCompiler& f) {
|
||||
}
|
||||
|
||||
static bool EmitBlock(FunctionCompiler& f) {
|
||||
return f.iter().readBlock() && f.startBlock();
|
||||
ResultType params;
|
||||
return f.iter().readBlock(¶ms) && f.startBlock();
|
||||
}
|
||||
|
||||
static bool EmitLoop(FunctionCompiler& f) {
|
||||
if (!f.iter().readLoop()) {
|
||||
ResultType params;
|
||||
if (!f.iter().readLoop(¶ms)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1774,8 +1772,9 @@ static bool EmitLoop(FunctionCompiler& f) {
|
||||
}
|
||||
|
||||
static bool EmitIf(FunctionCompiler& f) {
|
||||
ResultType params;
|
||||
MDefinition* condition = nullptr;
|
||||
if (!f.iter().readIf(&condition)) {
|
||||
if (!f.iter().readIf(¶ms, &condition)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1789,15 +1788,14 @@ static bool EmitIf(FunctionCompiler& f) {
|
||||
}
|
||||
|
||||
static bool EmitElse(FunctionCompiler& f) {
|
||||
ExprType thenType;
|
||||
MDefinition* thenValue;
|
||||
if (!f.iter().readElse(&thenType, &thenValue)) {
|
||||
ResultType paramType;
|
||||
ResultType resultType;
|
||||
DefVector thenValues;
|
||||
if (!f.iter().readElse(¶mType, &resultType, &thenValues)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsVoid(thenType)) {
|
||||
f.pushDef(thenValue);
|
||||
}
|
||||
f.pushDefs(thenValues);
|
||||
|
||||
if (!f.switchToElse(f.iter().controlItem(), &f.iter().controlItem())) {
|
||||
return false;
|
||||
@ -1808,40 +1806,33 @@ static bool EmitElse(FunctionCompiler& f) {
|
||||
|
||||
static bool EmitEnd(FunctionCompiler& f) {
|
||||
LabelKind kind;
|
||||
ExprType type;
|
||||
MDefinition* value;
|
||||
if (!f.iter().readEnd(&kind, &type, &value)) {
|
||||
ResultType type;
|
||||
DefVector preJoinDefs;
|
||||
if (!f.iter().readEnd(&kind, &type, &preJoinDefs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MBasicBlock* block = f.iter().controlItem();
|
||||
|
||||
f.iter().popEnd();
|
||||
|
||||
if (!IsVoid(type)) {
|
||||
f.pushDef(value);
|
||||
}
|
||||
f.pushDefs(preJoinDefs);
|
||||
|
||||
MDefinition* def = nullptr;
|
||||
DefVector postJoinDefs;
|
||||
switch (kind) {
|
||||
case LabelKind::Body:
|
||||
MOZ_ASSERT(f.iter().controlStackEmpty());
|
||||
if (!f.finishBlock(&def)) {
|
||||
if (!f.finishBlock(&postJoinDefs)) {
|
||||
return false;
|
||||
}
|
||||
if (f.inDeadCode() || IsVoid(type)) {
|
||||
f.returnVoid();
|
||||
} else {
|
||||
f.returnExpr(def);
|
||||
}
|
||||
f.returnValues(postJoinDefs);
|
||||
return f.iter().readFunctionEnd(f.iter().end());
|
||||
case LabelKind::Block:
|
||||
if (!f.finishBlock(&def)) {
|
||||
if (!f.finishBlock(&postJoinDefs)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case LabelKind::Loop:
|
||||
if (!f.closeLoop(block, &def)) {
|
||||
if (!f.closeLoop(block, &postJoinDefs)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@ -1852,76 +1843,54 @@ static bool EmitEnd(FunctionCompiler& f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.joinIfElse(block, &def)) {
|
||||
if (!f.joinIfElse(block, &postJoinDefs)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case LabelKind::Else:
|
||||
if (!f.joinIfElse(block, &def)) {
|
||||
if (!f.joinIfElse(block, &postJoinDefs)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!IsVoid(type)) {
|
||||
MOZ_ASSERT_IF(!f.inDeadCode(), def);
|
||||
f.iter().setResult(def);
|
||||
}
|
||||
MOZ_ASSERT_IF(!f.inDeadCode(), postJoinDefs.length() == type.length());
|
||||
f.iter().setResults(postJoinDefs.length(), postJoinDefs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool EmitBr(FunctionCompiler& f) {
|
||||
uint32_t relativeDepth;
|
||||
ExprType type;
|
||||
MDefinition* value;
|
||||
if (!f.iter().readBr(&relativeDepth, &type, &value)) {
|
||||
ResultType type;
|
||||
DefVector values;
|
||||
if (!f.iter().readBr(&relativeDepth, &type, &values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsVoid(type)) {
|
||||
if (!f.br(relativeDepth, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!f.br(relativeDepth, value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return f.br(relativeDepth, values);
|
||||
}
|
||||
|
||||
static bool EmitBrIf(FunctionCompiler& f) {
|
||||
uint32_t relativeDepth;
|
||||
ExprType type;
|
||||
MDefinition* value;
|
||||
ResultType type;
|
||||
DefVector values;
|
||||
MDefinition* condition;
|
||||
if (!f.iter().readBrIf(&relativeDepth, &type, &value, &condition)) {
|
||||
if (!f.iter().readBrIf(&relativeDepth, &type, &values, &condition)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsVoid(type)) {
|
||||
if (!f.brIf(relativeDepth, nullptr, condition)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!f.brIf(relativeDepth, value, condition)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return f.brIf(relativeDepth, values, condition);
|
||||
}
|
||||
|
||||
static bool EmitBrTable(FunctionCompiler& f) {
|
||||
Uint32Vector depths;
|
||||
uint32_t defaultDepth;
|
||||
ExprType branchValueType;
|
||||
MDefinition* branchValue;
|
||||
ResultType branchValueType;
|
||||
DefVector branchValues;
|
||||
MDefinition* index;
|
||||
if (!f.iter().readBrTable(&depths, &defaultDepth, &branchValueType,
|
||||
&branchValue, &index)) {
|
||||
&branchValues, &index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1937,24 +1906,19 @@ static bool EmitBrTable(FunctionCompiler& f) {
|
||||
}
|
||||
|
||||
if (allSameDepth) {
|
||||
return f.br(defaultDepth, branchValue);
|
||||
return f.br(defaultDepth, branchValues);
|
||||
}
|
||||
|
||||
return f.brTable(index, defaultDepth, depths, branchValue);
|
||||
return f.brTable(index, defaultDepth, depths, branchValues);
|
||||
}
|
||||
|
||||
static bool EmitReturn(FunctionCompiler& f) {
|
||||
MDefinition* value;
|
||||
if (!f.iter().readReturn(&value)) {
|
||||
DefVector values;
|
||||
if (!f.iter().readReturn(&values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f.funcType().results().length() == 0) {
|
||||
f.returnVoid();
|
||||
return true;
|
||||
}
|
||||
|
||||
f.returnExpr(value);
|
||||
f.returnValues(values);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1967,8 +1931,6 @@ static bool EmitUnreachable(FunctionCompiler& f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef IonOpIter::ValueVector DefVector;
|
||||
|
||||
static bool EmitCallArgs(FunctionCompiler& f, const FuncType& funcType,
|
||||
const DefVector& args, CallCompileState* call) {
|
||||
for (size_t i = 0, n = funcType.args().length(); i < n; ++i) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -39,6 +39,93 @@ typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
|
||||
typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
|
||||
typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
|
||||
|
||||
/*****************************************************************************/
|
||||
// ABIResultIter implementation
|
||||
|
||||
static uint32_t ResultStackSize(ValType type) {
|
||||
switch (type.code()) {
|
||||
case ValType::I32:
|
||||
return ABIResult::StackSizeOfInt32;
|
||||
case ValType::I64:
|
||||
return ABIResult::StackSizeOfInt64;
|
||||
case ValType::F32:
|
||||
return ABIResult::StackSizeOfFloat;
|
||||
case ValType::F64:
|
||||
return ABIResult::StackSizeOfDouble;
|
||||
case ValType::Ref:
|
||||
case ValType::FuncRef:
|
||||
case ValType::AnyRef:
|
||||
return ABIResult::StackSizeOfPtr;
|
||||
case ValType::NullRef:
|
||||
default:
|
||||
MOZ_CRASH("Unexpected result type");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ABIResult::size() const { return ResultStackSize(type()); }
|
||||
|
||||
void ABIResultIter::settleRegister(ValType type) {
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(index() < RegisterResultCount);
|
||||
static_assert(RegisterResultCount == 1, "expected a single register result");
|
||||
|
||||
switch (type.code()) {
|
||||
case ValType::I32:
|
||||
cur_ = ABIResult(type, ReturnReg);
|
||||
break;
|
||||
case ValType::I64:
|
||||
cur_ = ABIResult(type, ReturnReg64);
|
||||
break;
|
||||
case ValType::F32:
|
||||
cur_ = ABIResult(type, ReturnFloat32Reg);
|
||||
break;
|
||||
case ValType::F64:
|
||||
cur_ = ABIResult(type, ReturnDoubleReg);
|
||||
break;
|
||||
case ValType::Ref:
|
||||
case ValType::FuncRef:
|
||||
case ValType::AnyRef:
|
||||
cur_ = ABIResult(type, ReturnReg);
|
||||
break;
|
||||
case ValType::NullRef:
|
||||
default:
|
||||
MOZ_CRASH("Unexpected result type");
|
||||
}
|
||||
}
|
||||
|
||||
void ABIResultIter::settleNext() {
|
||||
MOZ_ASSERT(direction_ == Next);
|
||||
MOZ_ASSERT(!done());
|
||||
|
||||
uint32_t typeIndex = count_ - index_ - 1;
|
||||
ValType type = type_[typeIndex];
|
||||
|
||||
if (index_ < RegisterResultCount) {
|
||||
settleRegister(type);
|
||||
return;
|
||||
}
|
||||
|
||||
cur_ = ABIResult(type, nextStackOffset_);
|
||||
nextStackOffset_ += ResultStackSize(type);
|
||||
}
|
||||
|
||||
void ABIResultIter::settlePrev() {
|
||||
MOZ_ASSERT(direction_ == Prev);
|
||||
MOZ_ASSERT(!done());
|
||||
uint32_t typeIndex = index_;
|
||||
ValType type = type_[typeIndex];
|
||||
|
||||
if (count_ - index_ - 1 < RegisterResultCount) {
|
||||
settleRegister(type);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t size = ResultStackSize(type);
|
||||
MOZ_ASSERT(nextStackOffset_ >= size);
|
||||
nextStackOffset_ -= size;
|
||||
cur_ = ABIResult(type, nextStackOffset_);
|
||||
}
|
||||
|
||||
#ifdef WASM_CODEGEN_DEBUG
|
||||
template <class Closure>
|
||||
static void GenPrint(DebugChannel channel, MacroAssembler& masm,
|
||||
|
@ -20,10 +20,227 @@
|
||||
#define wasm_stubs_h
|
||||
|
||||
#include "wasm/WasmGenerator.h"
|
||||
#include "wasm/WasmOpIter.h"
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
// ValType and location for a single result: either in a register or on the
|
||||
// stack.
|
||||
|
||||
class ABIResult {
|
||||
ValType type_;
|
||||
enum class Location { Gpr, Gpr64, Fpr, Stack } loc_;
|
||||
union {
|
||||
Register gpr_;
|
||||
Register64 gpr64_;
|
||||
FloatRegister fpr_;
|
||||
uint32_t stackOffset_;
|
||||
};
|
||||
|
||||
void validate() {
|
||||
#ifdef DEBUG
|
||||
if (onStack()) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(inRegister());
|
||||
switch (type_.code()) {
|
||||
case ValType::I32:
|
||||
MOZ_ASSERT(loc_ == Location::Gpr);
|
||||
break;
|
||||
case ValType::I64:
|
||||
MOZ_ASSERT(loc_ == Location::Gpr64);
|
||||
break;
|
||||
case ValType::F32:
|
||||
case ValType::F64:
|
||||
MOZ_ASSERT(loc_ == Location::Fpr);
|
||||
break;
|
||||
case ValType::AnyRef:
|
||||
case ValType::FuncRef:
|
||||
case ValType::Ref:
|
||||
MOZ_ASSERT(loc_ == Location::Gpr);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("bad value type");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
friend class ABIResultIter;
|
||||
ABIResult(){};
|
||||
|
||||
public:
|
||||
// Sizes of items in the stack area.
|
||||
//
|
||||
// The size values come from the implementations of Push() in
|
||||
// MacroAssembler-x86-shared.cpp and MacroAssembler-arm-shared.cpp, and from
|
||||
// VFPRegister::size() in Architecture-arm.h.
|
||||
//
|
||||
// On ARM unlike on x86 we push a single for float.
|
||||
|
||||
static constexpr size_t StackSizeOfPtr = sizeof(intptr_t);
|
||||
static constexpr size_t StackSizeOfInt32 = StackSizeOfPtr;
|
||||
static constexpr size_t StackSizeOfInt64 = sizeof(int64_t);
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
static constexpr size_t StackSizeOfFloat = sizeof(float);
|
||||
#else
|
||||
static constexpr size_t StackSizeOfFloat = sizeof(double);
|
||||
#endif
|
||||
static constexpr size_t StackSizeOfDouble = sizeof(double);
|
||||
|
||||
ABIResult(ValType type, Register gpr)
|
||||
: type_(type), loc_(Location::Gpr), gpr_(gpr) {
|
||||
validate();
|
||||
}
|
||||
ABIResult(ValType type, Register64 gpr64)
|
||||
: type_(type), loc_(Location::Gpr64), gpr64_(gpr64) {
|
||||
validate();
|
||||
}
|
||||
ABIResult(ValType type, FloatRegister fpr)
|
||||
: type_(type), loc_(Location::Fpr), fpr_(fpr) {
|
||||
validate();
|
||||
}
|
||||
ABIResult(ValType type, uint32_t stackOffset)
|
||||
: type_(type), loc_(Location::Stack), stackOffset_(stackOffset) {
|
||||
validate();
|
||||
}
|
||||
|
||||
ValType type() const { return type_; }
|
||||
bool onStack() const { return loc_ == Location::Stack; }
|
||||
bool inRegister() const { return !onStack(); }
|
||||
Register gpr() const {
|
||||
MOZ_ASSERT(loc_ == Location::Gpr);
|
||||
return gpr_;
|
||||
}
|
||||
Register64 gpr64() const {
|
||||
MOZ_ASSERT(loc_ == Location::Gpr64);
|
||||
return gpr64_;
|
||||
}
|
||||
FloatRegister fpr() const {
|
||||
MOZ_ASSERT(loc_ == Location::Fpr);
|
||||
return fpr_;
|
||||
}
|
||||
// Offset from SP.
|
||||
uint32_t stackOffset() const {
|
||||
MOZ_ASSERT(loc_ == Location::Stack);
|
||||
return stackOffset_;
|
||||
}
|
||||
uint32_t size() const;
|
||||
};
|
||||
|
||||
// Just as WebAssembly functions can take multiple arguments, they can also
|
||||
// return multiple results. As with a call, a limited number of results will be
|
||||
// located in registers, and the rest will be stored in a stack area. The
|
||||
// |ABIResultIter| computes result locations, given a |ResultType|.
|
||||
//
|
||||
// Recall that a |ResultType| represents a sequence of value types t1..tN,
|
||||
// indexed from 1 to N. In principle it doesn't matter how we decide which
|
||||
// results get to be in registers and which go to the stack. To better
|
||||
// harmonize with WebAssembly's abstract stack machine, whose properties are
|
||||
// taken advantage of by the baseline compiler, our strategy is to start
|
||||
// allocating result locations in "reverse" order: from result N down to 1.
|
||||
//
|
||||
// If a result with index I is in a register, then all results with index J > I
|
||||
// are also in registers. If a result I is on the stack, then all results with
|
||||
// index K < I are also on the stack, farther away from the stack pointer than
|
||||
// result I.
|
||||
//
|
||||
// Currently only a single result is ever stored in a register, though this may
|
||||
// change in the future on register-rich platforms.
|
||||
//
|
||||
// NB: The baseline compiler also uses thie ABI for locations of block
|
||||
// parameters and return values, within individual WebAssembly functions.
|
||||
|
||||
class ABIResultIter {
|
||||
ResultType type_;
|
||||
uint32_t count_;
|
||||
uint32_t index_;
|
||||
uint32_t nextStackOffset_;
|
||||
enum { Next, Prev } direction_;
|
||||
ABIResult cur_;
|
||||
|
||||
void settleRegister(ValType type);
|
||||
void settleNext();
|
||||
void settlePrev();
|
||||
|
||||
static constexpr size_t RegisterResultCount = 1;
|
||||
|
||||
public:
|
||||
explicit ABIResultIter(const ResultType& type)
|
||||
: type_(type), count_(type.length()) {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
index_ = nextStackOffset_ = 0;
|
||||
direction_ = Next;
|
||||
if (!done()) {
|
||||
settleNext();
|
||||
}
|
||||
}
|
||||
bool done() const { return index_ == count_; }
|
||||
uint32_t index() const { return index_; }
|
||||
uint32_t count() const { return count_; }
|
||||
uint32_t remaining() const { return count_ - index_; }
|
||||
void switchToNext() {
|
||||
MOZ_ASSERT(direction_ == Prev);
|
||||
if (!done() && cur().onStack()) {
|
||||
nextStackOffset_ += cur().size();
|
||||
}
|
||||
index_ = count_ - index_;
|
||||
direction_ = Next;
|
||||
if (!done()) {
|
||||
settleNext();
|
||||
}
|
||||
}
|
||||
void switchToPrev() {
|
||||
MOZ_ASSERT(direction_ == Next);
|
||||
if (!done() && cur().onStack()) {
|
||||
nextStackOffset_ -= cur().size();
|
||||
}
|
||||
index_ = count_ - index_;
|
||||
direction_ = Prev;
|
||||
if (!done()) settlePrev();
|
||||
}
|
||||
void next() {
|
||||
MOZ_ASSERT(direction_ == Next);
|
||||
MOZ_ASSERT(!done());
|
||||
index_++;
|
||||
if (!done()) {
|
||||
settleNext();
|
||||
}
|
||||
}
|
||||
void prev() {
|
||||
MOZ_ASSERT(direction_ == Prev);
|
||||
MOZ_ASSERT(!done());
|
||||
index_++;
|
||||
if (!done()) {
|
||||
settlePrev();
|
||||
}
|
||||
}
|
||||
const ABIResult& cur() const {
|
||||
MOZ_ASSERT(!done());
|
||||
return cur_;
|
||||
}
|
||||
|
||||
uint32_t stackBytesConsumedSoFar() const { return nextStackOffset_; }
|
||||
|
||||
static inline bool HasStackResults(const ResultType& type) {
|
||||
return type.length() > RegisterResultCount;
|
||||
}
|
||||
|
||||
static uint32_t MeasureStackBytes(const ResultType& type) {
|
||||
if (!HasStackResults(type)) {
|
||||
return 0;
|
||||
}
|
||||
ABIResultIter iter(type);
|
||||
while (!iter.done()) {
|
||||
iter.next();
|
||||
}
|
||||
return iter.stackBytesConsumedSoFar();
|
||||
}
|
||||
};
|
||||
|
||||
extern bool GenerateBuiltinThunk(jit::MacroAssembler& masm,
|
||||
jit::ABIFunctionType abiType,
|
||||
ExitReason exitReason, void* funcPtr,
|
||||
|
@ -6178,12 +6178,23 @@ static bool EncodeExprList(Encoder& e, const AstExprVector& v) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool EncodeBlockType(Encoder& e, AstExprType& t) {
|
||||
ExprType type = t.type();
|
||||
static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
|
||||
MOZ_ASSERT(size_t(type.code()) < size_t(TypeCode::Limit));
|
||||
if (type.isRef()) {
|
||||
return e.writeFixedU8(uint8_t(ExprType::Ref)) &&
|
||||
e.writeVarU32(type.refTypeIndex());
|
||||
}
|
||||
return e.writeFixedU8(uint8_t(type.code()));
|
||||
}
|
||||
|
||||
static bool EncodeBlock(Encoder& e, AstBlock& b) {
|
||||
if (!e.writeOp(b.op())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!e.writeBlockType(b.type().type())) {
|
||||
if (!EncodeBlockType(e, b.type())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -6368,7 +6379,7 @@ static bool EncodeIf(Encoder& e, AstIf& i) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!e.writeBlockType(i.type().type())) {
|
||||
if (!EncodeBlockType(e, i.type())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -227,21 +227,6 @@ uint8_t* FuncType::serialize(uint8_t* cursor) const {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
// ExprType is not POD while ReadScalar requires POD, so specialize.
|
||||
template <>
|
||||
inline const uint8_t* ReadScalar<ExprType>(const uint8_t* src, ExprType* dst) {
|
||||
static_assert(sizeof(PackedTypeCode) == sizeof(ExprType),
|
||||
"ExprType must carry only a PackedTypeCode");
|
||||
memcpy(dst->packedPtr(), src, sizeof(PackedTypeCode));
|
||||
return src + sizeof(*dst);
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
const uint8_t* FuncType::deserialize(const uint8_t* cursor) {
|
||||
cursor = DeserializePodVector(cursor, &results_);
|
||||
if (!cursor) {
|
||||
|
@ -213,26 +213,28 @@ static_assert(std::is_pod<PackedTypeCode>::value,
|
||||
"must be POD to be simply serialized/deserialized");
|
||||
|
||||
const uint32_t NoTypeCode = 0xFF; // Only use these
|
||||
const uint32_t NoRefTypeIndex = 0xFFFFFF; // with PackedTypeCode
|
||||
|
||||
static inline PackedTypeCode InvalidPackedTypeCode() {
|
||||
return PackedTypeCode((NoRefTypeIndex << 8) | NoTypeCode);
|
||||
}
|
||||
|
||||
static inline PackedTypeCode PackTypeCode(TypeCode tc) {
|
||||
MOZ_ASSERT(uint32_t(tc) <= 0xFF);
|
||||
MOZ_ASSERT(tc != TypeCode::Ref);
|
||||
return PackedTypeCode((NoRefTypeIndex << 8) | uint32_t(tc));
|
||||
}
|
||||
const uint32_t NoRefTypeIndex = 0x3FFFFF; // with PackedTypeCode
|
||||
|
||||
static inline PackedTypeCode PackTypeCode(TypeCode tc, uint32_t refTypeIndex) {
|
||||
MOZ_ASSERT(uint32_t(tc) <= 0xFF);
|
||||
MOZ_ASSERT_IF(tc != TypeCode::Ref, refTypeIndex == NoRefTypeIndex);
|
||||
MOZ_ASSERT_IF(tc == TypeCode::Ref, refTypeIndex <= MaxTypes);
|
||||
static_assert(MaxTypes < (1 << (32 - 8)), "enough bits");
|
||||
// A PackedTypeCode should be representable in a single word, so in the
|
||||
// smallest case, 32 bits. However sometimes 2 bits of the word may be taken
|
||||
// by a pointer tag; for that reason, limit to 30 bits; and then there's the
|
||||
// 8-bit typecode, so 22 bits left for the type index.
|
||||
static_assert(MaxTypes < (1 << (30 - 8)), "enough bits");
|
||||
return PackedTypeCode((refTypeIndex << 8) | uint32_t(tc));
|
||||
}
|
||||
|
||||
static inline PackedTypeCode PackTypeCode(TypeCode tc) {
|
||||
return PackTypeCode(tc, NoRefTypeIndex);
|
||||
}
|
||||
|
||||
static inline PackedTypeCode InvalidPackedTypeCode() {
|
||||
return PackedTypeCode(NoTypeCode);
|
||||
}
|
||||
|
||||
static inline PackedTypeCode PackedTypeCodeFromBits(uint32_t bits) {
|
||||
return PackTypeCode(TypeCode(bits & 255), bits >> 8);
|
||||
}
|
||||
|
@ -444,15 +444,24 @@ bool wasm::DecodeValidatedLocalEntries(Decoder& d, ValTypeVector* locals) {
|
||||
|
||||
// Function body validation.
|
||||
|
||||
class NothingVector {
|
||||
Nothing unused_;
|
||||
|
||||
public:
|
||||
bool resize(size_t length) { return true; }
|
||||
Nothing& operator[](size_t) { return unused_; }
|
||||
Nothing& back() { return unused_; }
|
||||
};
|
||||
|
||||
struct ValidatingPolicy {
|
||||
typedef Nothing Value;
|
||||
typedef NothingVector ValueVector;
|
||||
typedef Nothing ControlItem;
|
||||
};
|
||||
|
||||
typedef OpIter<ValidatingPolicy> ValidatingOpIter;
|
||||
|
||||
static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
||||
// FIXME(1401675): Replace with BlockType.
|
||||
uint32_t funcIndex,
|
||||
const ValTypeVector& locals,
|
||||
const uint8_t* bodyEnd, Decoder* d) {
|
||||
@ -473,12 +482,13 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
||||
}
|
||||
|
||||
Nothing nothing;
|
||||
NothingVector nothings;
|
||||
ResultType unusedType;
|
||||
|
||||
switch (op.b0) {
|
||||
case uint16_t(Op::End): {
|
||||
LabelKind unusedKind;
|
||||
ExprType unusedType;
|
||||
if (!iter.readEnd(&unusedKind, &unusedType, ¬hing)) {
|
||||
if (!iter.readEnd(&unusedKind, &unusedType, ¬hings)) {
|
||||
return false;
|
||||
}
|
||||
iter.popEnd();
|
||||
@ -493,12 +503,12 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
||||
CHECK(iter.readDrop());
|
||||
case uint16_t(Op::Call): {
|
||||
uint32_t unusedIndex;
|
||||
ValidatingOpIter::ValueVector unusedArgs;
|
||||
NothingVector unusedArgs;
|
||||
CHECK(iter.readCall(&unusedIndex, &unusedArgs));
|
||||
}
|
||||
case uint16_t(Op::CallIndirect): {
|
||||
uint32_t unusedIndex, unusedIndex2;
|
||||
ValidatingOpIter::ValueVector unusedArgs;
|
||||
NothingVector unusedArgs;
|
||||
CHECK(iter.readCallIndirect(&unusedIndex, &unusedIndex2, ¬hing,
|
||||
&unusedArgs));
|
||||
}
|
||||
@ -568,15 +578,13 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
||||
¬hing));
|
||||
}
|
||||
case uint16_t(Op::Block):
|
||||
CHECK(iter.readBlock());
|
||||
CHECK(iter.readBlock(&unusedType));
|
||||
case uint16_t(Op::Loop):
|
||||
CHECK(iter.readLoop());
|
||||
CHECK(iter.readLoop(&unusedType));
|
||||
case uint16_t(Op::If):
|
||||
CHECK(iter.readIf(¬hing));
|
||||
case uint16_t(Op::Else): {
|
||||
ExprType type;
|
||||
CHECK(iter.readElse(&type, ¬hing));
|
||||
}
|
||||
CHECK(iter.readIf(&unusedType, ¬hing));
|
||||
case uint16_t(Op::Else):
|
||||
CHECK(iter.readElse(&unusedType, &unusedType, ¬hings));
|
||||
case uint16_t(Op::I32Clz):
|
||||
case uint16_t(Op::I32Ctz):
|
||||
case uint16_t(Op::I32Popcnt):
|
||||
@ -815,23 +823,20 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
||||
CHECK(iter.readMemorySize());
|
||||
case uint16_t(Op::Br): {
|
||||
uint32_t unusedDepth;
|
||||
ExprType unusedType;
|
||||
CHECK(iter.readBr(&unusedDepth, &unusedType, ¬hing));
|
||||
CHECK(iter.readBr(&unusedDepth, &unusedType, ¬hings));
|
||||
}
|
||||
case uint16_t(Op::BrIf): {
|
||||
uint32_t unusedDepth;
|
||||
ExprType unusedType;
|
||||
CHECK(iter.readBrIf(&unusedDepth, &unusedType, ¬hing, ¬hing));
|
||||
CHECK(iter.readBrIf(&unusedDepth, &unusedType, ¬hings, ¬hing));
|
||||
}
|
||||
case uint16_t(Op::BrTable): {
|
||||
Uint32Vector unusedDepths;
|
||||
uint32_t unusedDefault;
|
||||
ExprType unusedType;
|
||||
CHECK(iter.readBrTable(&unusedDepths, &unusedDefault, &unusedType,
|
||||
¬hing, ¬hing));
|
||||
¬hings, ¬hing));
|
||||
}
|
||||
case uint16_t(Op::Return):
|
||||
CHECK(iter.readReturn(¬hing));
|
||||
CHECK(iter.readReturn(¬hings));
|
||||
case uint16_t(Op::Unreachable):
|
||||
CHECK(iter.readUnreachable());
|
||||
case uint16_t(Op::MiscPrefix): {
|
||||
@ -958,7 +963,7 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
||||
return iter.unrecognizedOpcode(&op);
|
||||
}
|
||||
uint32_t unusedUint;
|
||||
ValidatingOpIter::ValueVector unusedArgs;
|
||||
NothingVector unusedArgs;
|
||||
CHECK(iter.readStructNew(&unusedUint, &unusedArgs));
|
||||
}
|
||||
case uint32_t(MiscOp::StructGet): {
|
||||
@ -1210,10 +1215,8 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
||||
bool wasm::ValidateFunctionBody(const ModuleEnvironment& env,
|
||||
uint32_t funcIndex, uint32_t bodySize,
|
||||
Decoder& d) {
|
||||
const FuncType& funcType = *env.funcTypes[funcIndex];
|
||||
|
||||
ValTypeVector locals;
|
||||
if (!locals.appendAll(funcType.args())) {
|
||||
if (!locals.appendAll(env.funcTypes[funcIndex]->args())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -396,15 +396,6 @@ class Encoder {
|
||||
}
|
||||
return writeFixedU8(uint8_t(type.code()));
|
||||
}
|
||||
MOZ_MUST_USE bool writeBlockType(ExprType type) {
|
||||
static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
|
||||
MOZ_ASSERT(size_t(type.code()) < size_t(TypeCode::Limit));
|
||||
if (type.isRef()) {
|
||||
return writeFixedU8(uint8_t(ExprType::Ref)) &&
|
||||
writeVarU32(type.refTypeIndex());
|
||||
}
|
||||
return writeFixedU8(uint8_t(type.code()));
|
||||
}
|
||||
MOZ_MUST_USE bool writeOp(Op op) {
|
||||
static_assert(size_t(Op::Limit) == 256, "fits");
|
||||
MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
|
||||
@ -626,6 +617,16 @@ class Decoder {
|
||||
const uint8_t* begin() const { return beg_; }
|
||||
const uint8_t* end() const { return end_; }
|
||||
|
||||
// Peek at the next byte, if it exists, without advancing the position.
|
||||
|
||||
bool peekByte(uint8_t* byte) {
|
||||
if (done()) {
|
||||
return false;
|
||||
}
|
||||
*byte = *cur_;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fixed-size encoding operations simply copy the literal bytes (without
|
||||
// attempting to align).
|
||||
|
||||
@ -708,20 +709,6 @@ class Decoder {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
MOZ_MUST_USE bool readBlockType(uint8_t* code, uint32_t* refTypeIndex) {
|
||||
static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
|
||||
if (!readFixedU8(code)) {
|
||||
return false;
|
||||
}
|
||||
if (*code == uint8_t(TypeCode::Ref)) {
|
||||
if (!readVarU32(refTypeIndex)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*refTypeIndex = NoRefTypeIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
MOZ_MUST_USE bool readOp(OpBytes* op) {
|
||||
static_assert(size_t(Op::Limit) == 256, "fits");
|
||||
uint8_t u8;
|
||||
|
Loading…
Reference in New Issue
Block a user