Bug 1247846 - Baldr: add indirect function table and call_indirect (r=sunfish)

MozReview-Commit-ID: HFq8Nh0XCkB
This commit is contained in:
Luke Wagner 2016-02-16 16:48:23 -06:00
parent d4618c01b3
commit 1aab84c5af
30 changed files with 623 additions and 123 deletions

View File

@ -1783,7 +1783,7 @@ class MOZ_STACK_CLASS ModuleValidator
}
#undef ADDSTDLIBSIMDOPNAME
UniqueModuleGeneratorData genData = MakeUnique<ModuleGeneratorData>();
UniqueModuleGeneratorData genData = MakeUnique<ModuleGeneratorData>(ModuleKind::AsmJS);
if (!genData ||
!genData->sigs.resize(MaxSigs) ||
!genData->funcSigs.resize(MaxFuncs) ||
@ -1800,7 +1800,7 @@ class MOZ_STACK_CLASS ModuleValidator
return false;
}
return mg_.init(Move(genData), Move(filename), ModuleKind::AsmJS);
return mg_.init(Move(genData), Move(filename));
}
ExclusiveContext* cx() const { return cx_; }

View File

@ -164,6 +164,17 @@ DecodeExprType(JSContext* cx, Decoder& d, ExprType *type)
return true;
}
static bool
DecodeCallWithSig(FunctionDecoder& f, const Sig& sig, ExprType expected)
{
for (ValType argType : sig.args()) {
if (!DecodeExpr(f, ToExprType(argType)))
return false;
}
return CheckType(f, sig.ret(), expected);
}
static bool
DecodeCall(FunctionDecoder& f, ExprType expected)
{
@ -174,14 +185,7 @@ DecodeCall(FunctionDecoder& f, ExprType expected)
if (funcIndex >= f.mg().numFuncSigs())
return f.fail("callee index out of range");
const DeclaredSig& sig = f.mg().funcSig(funcIndex);
for (ValType argType : sig.args()) {
if (!DecodeExpr(f, ToExprType(argType)))
return false;
}
return CheckType(f, sig.ret(), expected);
return DecodeCallWithSig(f, f.mg().funcSig(funcIndex), expected);
}
static bool
@ -194,14 +198,23 @@ DecodeCallImport(FunctionDecoder& f, ExprType expected)
if (importIndex >= f.mg().numImports())
return f.fail("import index out of range");
const DeclaredSig& sig = *f.mg().import(importIndex).sig;
return DecodeCallWithSig(f, *f.mg().import(importIndex).sig, expected);
}
for (ValType argType : sig.args()) {
if (!DecodeExpr(f, ToExprType(argType)))
return false;
}
static bool
DecodeCallIndirect(FunctionDecoder& f, ExprType expected)
{
uint32_t sigIndex;
if (!f.d().readU32(&sigIndex))
return f.fail("unable to read indirect call signature index");
return CheckType(f, sig.ret(), expected);
if (sigIndex >= f.mg().numSigs())
return f.fail("signature index out of range");
if (!DecodeExpr(f, ExprType::I32))
return false;
return DecodeCallWithSig(f, f.mg().sig(sigIndex), expected);
}
static bool
@ -387,6 +400,8 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
return DecodeCall(f, expected);
case Expr::CallImport:
return DecodeCallImport(f, expected);
case Expr::CallIndirect:
return DecodeCallIndirect(f, expected);
case Expr::I32Const:
return DecodeConstI32(f, expected);
case Expr::I64Const:
@ -756,6 +771,66 @@ DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
return true;
}
static bool
DecodeTableSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
{
if (!d.readCStringIf(TableSection))
return true;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected table section byte size");
if (!d.readVarU32(&init->numTableElems))
return Fail(cx, d, "expected number of table elems");
if (init->numTableElems > MaxTableElems)
return Fail(cx, d, "too many table elements");
Uint32Vector elems;
if (!elems.resize(init->numTableElems))
return false;
for (uint32_t i = 0; i < init->numTableElems; i++) {
uint32_t funcIndex;
if (!d.readVarU32(&funcIndex))
return Fail(cx, d, "expected table element");
if (funcIndex >= init->funcSigs.length())
return Fail(cx, d, "table element out of range");
elems[i] = funcIndex;
}
if (!d.finishSection(sectionStart))
return Fail(cx, d, "table section byte size mismatch");
// Convert the single (heterogeneous) indirect function table into an
// internal set of asm.js-like homogeneous tables indexed by signature.
// Every element in the heterogeneous table is present in only one
// homogeneous table (as determined by its signature). An element's index in
// the heterogeneous table is the same as its index in its homogeneous table
// and all other homogeneous tables are given an entry that will fault if
// called for at that element's index.
for (uint32_t elemIndex = 0; elemIndex < elems.length(); elemIndex++) {
uint32_t funcIndex = elems[elemIndex];
TableModuleGeneratorData& table = init->sigToTable[init->funcSigIndex(funcIndex)];
if (table.numElems == 0) {
table.numElems = elems.length();
if (!table.elemFuncIndices.appendN(ModuleGenerator::BadIndirectCall, elems.length()))
return false;
}
}
for (uint32_t elemIndex = 0; elemIndex < elems.length(); elemIndex++) {
uint32_t funcIndex = elems[elemIndex];
init->sigToTable[init->funcSigIndex(funcIndex)].elemFuncIndices[elemIndex] = funcIndex;
}
return true;
}
static bool
DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames)
{
@ -1014,7 +1089,7 @@ DecodeCodeSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
}
if (funcIndex != mg.numFuncSigs())
return Fail(cx, d, "fewer function definitions than declarations");
return Fail(cx, d, "different number of definitions than declarations");
if (!mg.finishFuncDefs())
return false;
@ -1087,7 +1162,8 @@ DecodeUnknownSection(JSContext* cx, Decoder& d)
!strcmp(sectionName.get(), DeclSection) ||
!strcmp(sectionName.get(), ExportSection) ||
!strcmp(sectionName.get(), CodeSection) ||
!strcmp(sectionName.get(), DataSection))
!strcmp(sectionName.get(), DataSection) ||
!strcmp(sectionName.get(), TableSection))
{
return Fail(cx, d, "known section out of order");
}
@ -1125,6 +1201,9 @@ DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t len
if (!DecodeDeclarationSection(cx, d, init.get()))
return false;
if (!DecodeTableSection(cx, d, init.get()))
return false;
ModuleGenerator mg(cx);
if (!mg.init(Move(init), Move(file)))
return false;

View File

@ -46,6 +46,7 @@ static const uint32_t EncodingVersion = -1; // experimental
static const char SigSection[] = "sig";
static const char ImportSection[] = "import";
static const char DeclSection[] = "decl";
static const char TableSection[] = "table";
static const char MemorySection[] = "memory";
static const char ExportSection[] = "export";
static const char CodeSection[] = "code";
@ -770,7 +771,6 @@ class Decoder
class FuncBytecode
{
// Function metadata
ModuleKind kind_;
const DeclaredSig& sig_;
ValTypeVector locals_;
uint32_t lineOrBytecode_;
@ -789,10 +789,8 @@ class FuncBytecode
ValTypeVector&& locals,
uint32_t lineOrBytecode,
Uint32Vector&& callSiteLineNums,
unsigned generateTime,
ModuleKind kind)
: kind_(kind),
sig_(sig),
unsigned generateTime)
: sig_(sig),
locals_(Move(locals)),
lineOrBytecode_(lineOrBytecode),
callSiteLineNums_(Move(callSiteLineNums)),
@ -813,7 +811,6 @@ class FuncBytecode
ValType localType(size_t i) const { return locals_[i]; }
unsigned generateTime() const { return generateTime_; }
bool isAsmJS() const { return kind_ == ModuleKind::AsmJS; }
};
typedef UniquePtr<FuncBytecode> UniqueFuncBytecode;

View File

@ -95,6 +95,7 @@ FrameIterator::settle()
break;
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::ErrorExit:
case CodeRange::Inline:
case CodeRange::CallThunk:
MOZ_CRASH("Should not encounter an exit during iteration");
@ -491,6 +492,7 @@ ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
break;
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::ErrorExit:
case CodeRange::Inline:
case CodeRange::CallThunk:
MOZ_CRASH("Unexpected CodeRange kind");
@ -545,7 +547,8 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
case CodeRange::Function:
case CodeRange::CallThunk:
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit: {
case CodeRange::ImportInterpExit:
case CodeRange::ErrorExit: {
// When the pc is inside the prologue/epilogue, the innermost
// call's AsmJSFrame is not complete and thus fp points to the the
// second-to-innermost call's AsmJSFrame. Since fp can only tell you
@ -659,6 +662,7 @@ ProfilingFrameIterator::operator++()
case CodeRange::Function:
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::ErrorExit:
case CodeRange::Inline:
case CodeRange::CallThunk:
stackAddress_ = callerFP_;
@ -683,6 +687,7 @@ ProfilingFrameIterator::label() const
// devtools/client/performance/modules/logic/frame-utils.js
const char* importJitDescription = "fast FFI trampoline (in asm.js)";
const char* importInterpDescription = "slow FFI trampoline (in asm.js)";
const char* errorDescription = "error generation (in asm.js)";
const char* nativeDescription = "native call (in asm.js)";
switch (exitReason_) {
@ -692,6 +697,8 @@ ProfilingFrameIterator::label() const
return importJitDescription;
case ExitReason::ImportInterp:
return importInterpDescription;
case ExitReason::Error:
return errorDescription;
case ExitReason::Native:
return nativeDescription;
}
@ -701,6 +708,7 @@ ProfilingFrameIterator::label() const
case CodeRange::Entry: return "entry trampoline (in asm.js)";
case CodeRange::ImportJitExit: return importJitDescription;
case CodeRange::ImportInterpExit: return importInterpDescription;
case CodeRange::ErrorExit: return errorDescription;
case CodeRange::Inline: return "inline stub (in asm.js)";
case CodeRange::CallThunk: return "call thunk (in asm.js)";
}

View File

@ -67,6 +67,7 @@ enum class ExitReason : uint32_t
None, // default state, the pc is in wasm code
ImportJit, // fast-path call directly into JIT code
ImportInterp, // slow-path call into C++ Invoke()
Error, // call to error generation
Native // call to native C++ code (e.g., Math.sin, ToInt32(), interrupt)
};

View File

@ -36,6 +36,8 @@ using mozilla::MakeEnumeratedRange;
static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
const unsigned ModuleGenerator::BadIndirectCall;
ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
: cx_(cx),
jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())),
@ -108,7 +110,7 @@ ParallelCompilationEnabled(ExclusiveContext* cx)
}
bool
ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename, ModuleKind kind)
ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename)
{
if (!funcIndexToExport_.init())
return false;
@ -119,7 +121,7 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename, Mo
module_->globalBytes = InitialGlobalDataBytes;
module_->compileArgs = CompileArgs(cx_);
module_->kind = kind;
module_->kind = shared->kind;
module_->heapUsage = HeapUsage::None;
module_->filename = Move(filename);
@ -127,15 +129,18 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename, Mo
if (!exportMap_)
return false;
shared_ = Move(shared);
// For asm.js, the Vectors in ModuleGeneratorData are max-sized reservations
// and will be initialized in a linear order via init* functions as the
// module is generated. For wasm, the Vectors are correctly-sized and
// already initialized.
shared_ = Move(shared);
if (kind == ModuleKind::Wasm) {
if (module_->kind == ModuleKind::Wasm) {
numSigs_ = shared_->sigs.length();
module_->numFuncs = shared_->funcSigs.length();
module_->globalBytes = AlignBytes(module_->globalBytes, sizeof(void*));
for (ImportModuleGeneratorData& import : shared_->imports) {
MOZ_ASSERT(!import.globalDataOffset);
import.globalDataOffset = module_->globalBytes;
@ -143,6 +148,17 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename, Mo
if (!addImport(*import.sig, import.globalDataOffset))
return false;
}
MOZ_ASSERT(module_->globalBytes % sizeof(void*) == 0);
for (TableModuleGeneratorData& table : shared_->sigToTable) {
MOZ_ASSERT(table.numElems == table.elemFuncIndices.length());
if (!table.numElems)
continue;
MOZ_ASSERT(!table.globalDataOffset);
table.globalDataOffset = module_->globalBytes;
module_->globalBytes += table.numElems * sizeof(void*);
}
}
return true;
@ -346,6 +362,7 @@ ModuleGenerator::finishCodegen(StaticLinkData* link)
Vector<ProfilingOffsets> interpExits(cx_);
Vector<ProfilingOffsets> jitExits(cx_);
EnumeratedArray<JumpTarget, JumpTarget::Limit, Offsets> jumpTargets;
ProfilingOffsets badIndirectCallExit;
Offsets interruptExit;
{
@ -372,6 +389,7 @@ ModuleGenerator::finishCodegen(StaticLinkData* link)
for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit))
jumpTargets[target] = GenerateJumpTarget(masm, target);
badIndirectCallExit = GenerateBadIndirectCallExit(masm);
interruptExit = GenerateInterruptStub(masm);
if (masm.oom() || !masm_.asmMergeWith(masm))
@ -406,15 +424,40 @@ ModuleGenerator::finishCodegen(StaticLinkData* link)
return false;
}
badIndirectCallExit.offsetBy(offsetInWhole);
if (!module_->codeRanges.emplaceBack(CodeRange::ErrorExit, badIndirectCallExit))
return false;
interruptExit.offsetBy(offsetInWhole);
if (!module_->codeRanges.emplaceBack(CodeRange::Inline, interruptExit))
return false;
// The signal handler redirects PC to the out-of-bounds and interrupt stubs.
// Fill in StaticLinkData with the offsets of these stubs.
link->pod.outOfBoundsOffset = jumpTargets[JumpTarget::OutOfBounds].begin;
link->pod.interruptOffset = interruptExit.begin;
for (uint32_t sigIndex = 0; sigIndex < numSigs_; sigIndex++) {
const TableModuleGeneratorData& table = shared_->sigToTable[sigIndex];
if (table.elemFuncIndices.empty())
continue;
Uint32Vector elemOffsets;
if (!elemOffsets.resize(table.elemFuncIndices.length()))
return false;
for (size_t i = 0; i < table.elemFuncIndices.length(); i++) {
uint32_t funcIndex = table.elemFuncIndices[i];
if (funcIndex == BadIndirectCall)
elemOffsets[i] = badIndirectCallExit.begin;
else
elemOffsets[i] = funcEntry(funcIndex);
}
if (!link->funcPtrTables.emplaceBack(table.globalDataOffset, Move(elemOffsets)))
return false;
}
// Only call convertOutOfRangeBranchesToThunks after all other codegen that may
// emit new jumps to JumpTargets has finished.
@ -503,24 +546,6 @@ ModuleGenerator::finishStaticLinkData(uint8_t* code, uint32_t codeBytes, StaticL
}
#endif
// Convert the function pointer table elements from function-indices to code
// offsets that static linking will convert to absolute addresses.
for (uint32_t sigIndex = 0; sigIndex < numSigs_; sigIndex++) {
const TableModuleGeneratorData& table = shared_->sigToTable[sigIndex];
if (table.elemFuncIndices.empty())
continue;
Uint32Vector elemOffsets;
if (!elemOffsets.resize(table.elemFuncIndices.length()))
return false;
for (size_t i = 0; i < table.elemFuncIndices.length(); i++)
elemOffsets[i] = funcEntry(table.elemFuncIndices[i]);
if (!link->funcPtrTables.emplaceBack(table.globalDataOffset, Move(elemOffsets)))
return false;
}
return true;
}
@ -790,8 +815,7 @@ ModuleGenerator::finishFuncDef(uint32_t funcIndex, unsigned generateTime, Functi
Move(fg->locals_),
fg->lineOrBytecode_,
Move(fg->callSiteLineNums_),
generateTime,
module_->kind);
generateTime);
if (!func)
return false;

View File

@ -96,11 +96,22 @@ typedef Vector<AsmJSGlobalVariable, 0, SystemAllocPolicy> AsmJSGlobalVariableVec
struct ModuleGeneratorData
{
ModuleKind kind;
uint32_t numTableElems;
DeclaredSigVector sigs;
TableModuleGeneratorDataVector sigToTable;
DeclaredSigPtrVector funcSigs;
ImportModuleGeneratorDataVector imports;
AsmJSGlobalVariableVector globals;
uint32_t funcSigIndex(uint32_t funcIndex) const {
return funcSigs[funcIndex] - sigs.begin();
}
explicit ModuleGeneratorData(ModuleKind kind = ModuleKind::Wasm)
: kind(kind), numTableElems(0)
{}
};
typedef UniquePtr<ModuleGeneratorData> UniqueModuleGeneratorData;
@ -118,11 +129,17 @@ class ModuleGeneratorThreadView
explicit ModuleGeneratorThreadView(const ModuleGeneratorData& shared)
: shared_(shared)
{}
bool isAsmJS() const {
return shared_.kind == ModuleKind::AsmJS;
}
uint32_t numTableElems() const {
MOZ_ASSERT(!isAsmJS());
return shared_.numTableElems;
}
const DeclaredSig& sig(uint32_t sigIndex) const {
return shared_.sigs[sigIndex];
}
const TableModuleGeneratorData& sigToTable(uint32_t sigIndex) const {
MOZ_ASSERT(shared_.sigToTable[sigIndex].numElems != 0);
return shared_.sigToTable[sigIndex];
}
const DeclaredSig& funcSig(uint32_t funcIndex) const {
@ -195,32 +212,25 @@ class MOZ_STACK_CLASS ModuleGenerator
explicit ModuleGenerator(ExclusiveContext* cx);
~ModuleGenerator();
bool init(UniqueModuleGeneratorData shared, UniqueChars filename, ModuleKind = ModuleKind::Wasm);
bool init(UniqueModuleGeneratorData shared, UniqueChars filename);
bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; }
CompileArgs args() const { return module_->compileArgs; }
jit::MacroAssembler& masm() { return masm_; }
// asm.js global variables:
bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index);
const AsmJSGlobalVariable& globalVar(unsigned index) const { return shared_->globals[index]; }
// Heap usage:
void initHeapUsage(HeapUsage heapUsage);
bool usesHeap() const;
// Signatures:
void initSig(uint32_t sigIndex, Sig&& sig);
uint32_t numSigs() const { return numSigs_; }
const DeclaredSig& sig(uint32_t sigIndex) const;
// Function declarations:
bool initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
uint32_t numFuncSigs() const { return module_->numFuncs; }
const DeclaredSig& funcSig(uint32_t funcIndex) const;
// Imports:
bool initImport(uint32_t importIndex, uint32_t sigIndex);
uint32_t numImports() const;
const ImportModuleGeneratorData& import(uint32_t index) const;
@ -236,9 +246,19 @@ class MOZ_STACK_CLASS ModuleGenerator
bool finishFuncDefs();
// Function-pointer tables:
static const uint32_t BadIndirectCall = UINT32_MAX;
// asm.js lazy initialization:
void initSig(uint32_t sigIndex, Sig&& sig);
bool initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
bool initImport(uint32_t importIndex, uint32_t sigIndex);
bool initSigTableLength(uint32_t sigIndex, uint32_t numElems);
void initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices);
// asm.js global variables:
bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index);
const AsmJSGlobalVariable& globalVar(unsigned index) const { return shared_->globals[index]; }
// Return a ModuleData object which may be used to construct a Module, the
// StaticLinkData required to call Module::staticallyLink, and the list of
// functions that took a long time to compile.

View File

@ -752,7 +752,7 @@ class FunctionCompiler
return callPrivate(MAsmJSCall::Callee(AsmJSInternalCallee(funcIndex)), call, sig.ret(), def);
}
bool funcPtrCall(const Sig& sig, uint32_t maskLit, uint32_t globalDataOffset, MDefinition* index,
bool funcPtrCall(const Sig& sig, uint32_t length, uint32_t globalDataOffset, MDefinition* index,
const Call& call, MDefinition** def)
{
if (inDeadCode()) {
@ -760,12 +760,28 @@ class FunctionCompiler
return true;
}
MConstant* mask = MConstant::New(alloc(), Int32Value(maskLit));
curBlock_->add(mask);
MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask);
curBlock_->add(maskedIndex);
MAsmJSLoadFuncPtr* ptrFun = MAsmJSLoadFuncPtr::New(alloc(), globalDataOffset, maskedIndex);
curBlock_->add(ptrFun);
MAsmJSLoadFuncPtr* ptrFun;
if (mg().isAsmJS()) {
MOZ_ASSERT(IsPowerOfTwo(length));
MConstant* mask = MConstant::New(alloc(), Int32Value(length - 1));
curBlock_->add(mask);
MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask);
curBlock_->add(maskedIndex);
ptrFun = MAsmJSLoadFuncPtr::New(alloc(), maskedIndex, globalDataOffset);
curBlock_->add(ptrFun);
} else {
// For wasm code, as a space optimization, the ModuleGenerator does not allocate a
// table for signatures which do not contain any indirectly-callable functions.
// However, these signatures may still be called (it is not a validation error)
// so we instead have a flag alwaysThrow which throws an exception instead of loading
// the function pointer from the (non-existant) array.
MOZ_ASSERT(!length || length == mg_.numTableElems());
bool alwaysThrow = !length;
ptrFun = MAsmJSLoadFuncPtr::New(alloc(), index, mg_.numTableElems(), alwaysThrow,
globalDataOffset);
curBlock_->add(ptrFun);
}
return callPrivate(MAsmJSCall::Callee(ptrFun), call, sig.ret(), def);
}
@ -1613,11 +1629,7 @@ EmitCallIndirect(FunctionCompiler& f, ExprType ret, MDefinition** def)
return false;
const TableModuleGeneratorData& table = f.mg().sigToTable(sigIndex);
uint32_t length = table.numElems;
MOZ_ASSERT(IsPowerOfTwo(length));
uint32_t mask = length - 1;
return f.funcPtrCall(sig, mask, table.globalDataOffset, index, call, def);
return f.funcPtrCall(sig, table.numElems, table.globalDataOffset, index, call, def);
}
static bool
@ -3006,7 +3018,7 @@ wasm::IonCompileFunction(IonCompileTask* task)
return false;
MDefinition* last = nullptr;
if (func.isAsmJS()) {
if (f.mg().isAsmJS()) {
while (!f.done()) {
if (!EmitExprStmt(f, &last))
return false;

View File

@ -375,7 +375,7 @@ CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
MOZ_ASSERT(begin_ < profilingReturn_);
MOZ_ASSERT(profilingReturn_ < end_);
MOZ_ASSERT(u.kind_ == ImportJitExit || u.kind_ == ImportInterpExit);
MOZ_ASSERT(u.kind_ == ImportJitExit || u.kind_ == ImportInterpExit || u.kind_ == ErrorExit);
}
CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
@ -875,6 +875,14 @@ Module::Module(UniqueModuleData module)
{
*(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN();
*(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN();
#ifdef DEBUG
uint32_t lastEnd = 0;
for (const CodeRange& cr : module_->codeRanges) {
MOZ_ASSERT(cr.begin() >= lastEnd);
lastEnd = cr.end();
}
#endif
}
Module::~Module()

View File

@ -212,7 +212,7 @@ class CodeRange
void assertValid();
public:
enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline, CallThunk };
enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, ErrorExit, Inline, CallThunk };
CodeRange() = default;
CodeRange(Kind kind, Offsets offsets);

View File

@ -830,6 +830,8 @@ wasm::GenerateJumpTarget(MacroAssembler& masm, JumpTarget target)
return GenerateErrorStub(masm, SymbolicAddress::OnImpreciseConversion);
case JumpTarget::OutOfBounds:
return GenerateErrorStub(masm, SymbolicAddress::OnOutOfBounds);
case JumpTarget::BadIndirectCall:
return GenerateErrorStub(masm, SymbolicAddress::BadIndirectCall);
case JumpTarget::Throw:
return GenerateThrow(masm);
case JumpTarget::Limit:
@ -838,6 +840,25 @@ wasm::GenerateJumpTarget(MacroAssembler& masm, JumpTarget target)
MOZ_CRASH("bad JumpTarget");
}
ProfilingOffsets
wasm::GenerateBadIndirectCallExit(MacroAssembler& masm)
{
MIRTypeVector args;
unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, args);
ProfilingOffsets offsets;
GenerateExitPrologue(masm, framePushed, ExitReason::Error, &offsets);
AssertStackAlignment(masm, ABIStackAlignment);
masm.call(SymbolicAddress::BadIndirectCall);
masm.jump(JumpTarget::Throw);
GenerateExitEpilogue(masm, framePushed, ExitReason::Error, &offsets);
offsets.end = masm.currentOffset();
return offsets;
}
static const LiveRegisterSet AllRegsExceptSP(
GeneralRegisterSet(Registers::AllMask&
~(uint32_t(1) << Registers::StackPointer)),

View File

@ -36,6 +36,9 @@ GenerateJitExit(jit::MacroAssembler& masm, const Import& import, bool usesHeap);
extern Offsets
GenerateJumpTarget(jit::MacroAssembler& masm, JumpTarget target);
extern ProfilingOffsets
GenerateBadIndirectCallExit(jit::MacroAssembler& masm);
extern Offsets
GenerateInterruptStub(jit::MacroAssembler& masm);

View File

@ -95,13 +95,16 @@ class WasmAstSig : public WasmAstBase
ExprType ret() const {
return ret_;
}
bool operator==(const WasmAstSig& rhs) const {
return ret() == rhs.ret() && EqualContainers(args(), rhs.args());
}
typedef const WasmAstSig& Lookup;
static HashNumber hash(Lookup sig) {
return AddContainerToHash(sig.args(), HashNumber(sig.ret()));
}
static bool match(const WasmAstSig* lhs, Lookup rhs) {
return lhs->ret() == rhs.ret() && EqualContainers(lhs->args(), rhs.args());
return *lhs == rhs;
}
};
@ -113,6 +116,7 @@ enum class WasmAstExprKind
BinaryOperator,
Block,
Call,
CallIndirect,
ComparisonOperator,
Const,
ConversionOperator,
@ -230,6 +234,23 @@ class WasmAstCall : public WasmAstExpr
const WasmAstExprVector& args() const { return args_; }
};
class WasmAstCallIndirect : public WasmAstExpr
{
uint32_t sigIndex_;
WasmAstExpr* index_;
WasmAstExprVector args_;
public:
static const WasmAstExprKind Kind = WasmAstExprKind::CallIndirect;
WasmAstCallIndirect(uint32_t sigIndex, WasmAstExpr* index, WasmAstExprVector&& args)
: WasmAstExpr(Kind), sigIndex_(sigIndex), index_(index), args_(Move(args))
{}
uint32_t sigIndex() const { return sigIndex_; }
WasmAstExpr* index() const { return index_; }
const WasmAstExprVector& args() const { return args_; }
};
class WasmAstIfElse : public WasmAstExpr
{
Expr expr_;
@ -368,6 +389,17 @@ class WasmAstExport : public WasmAstNode
size_t funcIndex() const { MOZ_ASSERT(kind_ == WasmAstExportKind::Func); return u.funcIndex_; }
};
typedef WasmAstVector<uint32_t> WasmAstTableElemVector;
class WasmAstTable : public WasmAstNode
{
WasmAstTableElemVector elems_;
public:
explicit WasmAstTable(WasmAstTableElemVector&& elems) : elems_(Move(elems)) {}
const WasmAstTableElemVector& elems() const { return elems_; }
};
class WasmAstSegment : public WasmAstNode
{
uint32_t offset_;
@ -411,6 +443,7 @@ class WasmAstModule : public WasmAstNode
SigMap sigMap_;
ImportVector imports_;
ExportVector exports_;
WasmAstTable* table_;
FuncVector funcs_;
public:
@ -421,6 +454,7 @@ class WasmAstModule : public WasmAstNode
sigMap_(lifo),
imports_(lifo),
exports_(lifo),
table_(nullptr),
funcs_(lifo)
{}
bool init() {
@ -473,6 +507,15 @@ class WasmAstModule : public WasmAstNode
const ExportVector& exports() const {
return exports_;
}
bool initTable(WasmAstTable* table) {
if (table_)
return false;
table_ = table;
return true;
}
const WasmAstTable* maybeTable() const {
return table_;
}
};
class WasmAstUnaryOperator final : public WasmAstExpr
@ -568,6 +611,7 @@ class WasmToken
Block,
Call,
CallImport,
CallIndirect,
CloseParen,
ComparisonOpcode,
Const,
@ -598,6 +642,7 @@ class WasmToken
Segment,
SetLocal,
Store,
Table,
Text,
Type,
UnaryOpcode,
@ -965,13 +1010,17 @@ class WasmTokenStream
}
// Helpers:
bool getIf(WasmToken::Kind kind) {
bool getIf(WasmToken::Kind kind, WasmToken* token) {
if (peek().kind() == kind) {
get();
*token = get();
return true;
}
return false;
}
bool getIf(WasmToken::Kind kind) {
WasmToken token;
return getIf(kind, &token);
}
bool match(WasmToken::Kind expect, WasmToken* token, UniqueChars* error) {
*token = get();
if (token->kind() == expect)
@ -1098,6 +1147,8 @@ WasmToken WasmTokenStream::next()
case 'c':
if (consume(MOZ_UTF16("call"))) {
if (consume(MOZ_UTF16("_indirect")))
return WasmToken(WasmToken::CallIndirect, begin, cur_);
if (consume(MOZ_UTF16("_import")))
return WasmToken(WasmToken::CallImport, begin, cur_);
return WasmToken(WasmToken::Call, begin, cur_);
@ -1633,6 +1684,8 @@ WasmToken WasmTokenStream::next()
break;
case 't':
if (consume(MOZ_UTF16("table")))
return WasmToken(WasmToken::Table, begin, cur_);
if (consume(MOZ_UTF16("type")))
return WasmToken(WasmToken::Type, begin, cur_);
break;
@ -1705,25 +1758,54 @@ ParseBlock(WasmParseContext& c)
return new(c.lifo) WasmAstBlock(Move(exprs));
}
static bool
ParseArgs(WasmParseContext& c, WasmAstExprVector* args)
{
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmAstExpr* arg = ParseExprInsideParens(c);
if (!arg || !args->append(arg))
return false;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return false;
}
return true;
}
static WasmAstCall*
ParseCall(WasmParseContext& c, Expr expr)
{
MOZ_ASSERT(expr == Expr::Call || expr == Expr::CallImport);
WasmToken index;
if (!c.ts.match(WasmToken::Index, &index, c.error))
return nullptr;
WasmAstExprVector args(c.lifo);
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmAstExpr* arg = ParseExprInsideParens(c);
if (!arg || !args.append(arg))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
}
if (!ParseArgs(c, &args))
return nullptr;
return new(c.lifo) WasmAstCall(expr, index.index(), Move(args));
}
static WasmAstCallIndirect*
ParseCallIndirect(WasmParseContext& c)
{
WasmToken sigIndex;
if (!c.ts.match(WasmToken::Index, &sigIndex, c.error))
return nullptr;
WasmAstExpr* index = ParseExpr(c);
if (!index)
return nullptr;
WasmAstExprVector args(c.lifo);
if (!ParseArgs(c, &args))
return nullptr;
return new(c.lifo) WasmAstCallIndirect(sigIndex.index(), index, Move(args));
}
static uint_fast8_t
CountLeadingZeroes4(uint8_t x)
{
@ -2262,6 +2344,8 @@ ParseExprInsideParens(WasmParseContext& c)
return ParseCall(c, Expr::Call);
case WasmToken::CallImport:
return ParseCall(c, Expr::CallImport);
case WasmToken::CallIndirect:
return ParseCallIndirect(c);
case WasmToken::ComparisonOpcode:
return ParseComparisonOperator(c, token.expr());
case WasmToken::Const:
@ -2312,6 +2396,8 @@ ParseResult(WasmParseContext& c, ExprType* result)
return true;
}
static const uint32_t BadSigIndex = UINT32_MAX;
static WasmAstFunc*
ParseFunc(WasmParseContext& c, WasmAstModule* module)
{
@ -2319,7 +2405,24 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module)
WasmAstValTypeVector args(c.lifo);
ExprType result = ExprType::Void;
uint32_t sigIndex = BadSigIndex;
WasmToken openParen;
if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
if (c.ts.getIf(WasmToken::Type)) {
WasmToken sigToken;
if (!c.ts.match(WasmToken::Index, &sigToken, c.error))
return nullptr;
sigIndex = sigToken.index();
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
} else {
c.ts.unget(openParen);
}
}
WasmAstExpr* maybeBody = nullptr;
while (c.ts.getIf(WasmToken::OpenParen) && !maybeBody) {
WasmToken token = c.ts.get();
switch (token.kind()) {
@ -2346,9 +2449,10 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module)
return nullptr;
}
uint32_t sigIndex;
if (!module->declare(WasmAstSig(Move(args), result), &sigIndex))
return nullptr;
if (sigIndex == BadSigIndex) {
if (!module->declare(WasmAstSig(Move(args), result), &sigIndex))
return nullptr;
}
return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), maybeBody);
}
@ -2385,10 +2489,10 @@ ParseFuncType(WasmParseContext& c, WasmAstSig* sig)
static WasmAstSig*
ParseTypeDef(WasmParseContext& c)
{
if (!c.ts.match(WasmToken::Func, c.error))
return nullptr;
if (!c.ts.match(WasmToken::OpenParen, c.error))
return nullptr;
if (!c.ts.match(WasmToken::Func, c.error))
return nullptr;
WasmAstSig sig(c.lifo);
if (!ParseFuncType(c, &sig))
@ -2480,6 +2584,20 @@ ParseExport(WasmParseContext& c)
}
static WasmAstTable*
ParseTable(WasmParseContext& c)
{
WasmAstTableElemVector elems(c.lifo);
WasmToken token;
while (c.ts.getIf(WasmToken::Index, &token)) {
if (!elems.append(token.index()))
return nullptr;
}
return new(c.lifo) WasmAstTable(Move(elems));
}
static WasmAstModule*
ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
{
@ -2526,6 +2644,16 @@ ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
return nullptr;
break;
}
case WasmToken::Table: {
WasmAstTable* table = ParseTable(c);
if (!table)
return nullptr;
if (!module->initTable(table)) {
c.ts.generateError(section, c.error);
return nullptr;
}
break;
}
case WasmToken::Func: {
WasmAstFunc* func = ParseFunc(c, module);
if (!func || !module->append(func))
@ -2575,6 +2703,17 @@ EncodeBlock(Encoder& e, WasmAstBlock& b)
return true;
}
static bool
EncodeArgs(Encoder& e, const WasmAstExprVector& args)
{
for (WasmAstExpr* arg : args) {
if (!EncodeExpr(e, *arg))
return false;
}
return true;
}
static bool
EncodeCall(Encoder& e, WasmAstCall& c)
{
@ -2584,10 +2723,26 @@ EncodeCall(Encoder& e, WasmAstCall& c)
if (!e.writeU32(c.index()))
return false;
for (WasmAstExpr* arg : c.args()) {
if (!EncodeExpr(e, *arg))
return false;
}
if (!EncodeArgs(e, c.args()))
return false;
return true;
}
static bool
EncodeCallIndirect(Encoder& e, WasmAstCallIndirect& c)
{
if (!e.writeExpr(Expr::CallIndirect))
return false;
if (!e.writeU32(c.sigIndex()))
return false;
if (!EncodeExpr(e, *c.index()))
return false;
if (!EncodeArgs(e, c.args()))
return false;
return true;
}
@ -2703,6 +2858,8 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr)
return EncodeBlock(e, expr.as<WasmAstBlock>());
case WasmAstExprKind::Call:
return EncodeCall(e, expr.as<WasmAstCall>());
case WasmAstExprKind::CallIndirect:
return EncodeCallIndirect(e, expr.as<WasmAstCallIndirect>());
case WasmAstExprKind::ComparisonOperator:
return EncodeComparisonOperator(e, expr.as<WasmAstComparisonOperator>());
case WasmAstExprKind::Const:
@ -2922,6 +3079,31 @@ EncodeExportSection(Encoder& e, WasmAstModule& module)
return true;
}
static bool
EncodeTableSection(Encoder& e, WasmAstModule& module)
{
if (!module.maybeTable())
return true;
if (!e.writeCString(TableSection))
return false;
size_t offset;
if (!e.startSection(&offset))
return false;
if (!e.writeVarU32(module.maybeTable()->elems().length()))
return false;
for (uint32_t index : module.maybeTable()->elems()) {
if (!e.writeVarU32(index))
return false;
}
e.finishSection(offset);
return true;
}
static bool
EncodeFunc(Encoder& e, WasmAstFunc& func)
{
@ -3061,6 +3243,9 @@ EncodeModule(WasmAstModule& module)
if (!EncodeDeclarationSection(e, module))
return nullptr;
if (!EncodeTableSection(e, module))
return nullptr;
if (!EncodeMemorySection(e, module))
return nullptr;

View File

@ -70,6 +70,13 @@ OnImpreciseConversion()
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SIMD_FAILED_CONVERSION);
}
static void
BadIndirectCall()
{
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IND_CALL);
}
static int32_t
CoerceInPlace_ToInt32(MutableHandleValue val)
{
@ -175,6 +182,8 @@ wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
return FuncCast(OnOutOfBounds, Args_General0);
case SymbolicAddress::OnImpreciseConversion:
return FuncCast(OnImpreciseConversion, Args_General0);
case SymbolicAddress::BadIndirectCall:
return FuncCast(BadIndirectCall, Args_General0);
case SymbolicAddress::HandleExecutionInterrupt:
return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
case SymbolicAddress::InvokeImport_Void:

View File

@ -553,6 +553,7 @@ enum class SymbolicAddress
ReportOverRecursed,
OnOutOfBounds,
OnImpreciseConversion,
BadIndirectCall,
HandleExecutionInterrupt,
InvokeImport_Void,
InvokeImport_I32,
@ -575,6 +576,7 @@ enum class JumpTarget
StackOverflow,
OutOfBounds,
ConversionError,
BadIndirectCall,
Throw,
Limit
};

View File

@ -288,3 +288,49 @@ assertEq(wasmEvalText(code.replace('BODY', '(call_import 0)'), imports)(), 1);
assertEq(wasmEvalText(code.replace('BODY', '(call_import 1)'), imports)(), 2);
assertEq(wasmEvalText(code.replace('BODY', '(call 0)'), imports)(), 3);
assertEq(wasmEvalText(code.replace('BODY', '(call 1)'), imports)(), 4);
var {v2i, i2i, i2v} = wasmEvalText(`(module
(type (func (result i32)))
(type (func (param i32) (result i32)))
(type (func (param i32)))
(func (type 0) (i32.const 13))
(func (type 0) (i32.const 42))
(func (type 1) (i32.add (get_local 0) (i32.const 1)))
(func (type 1) (i32.add (get_local 0) (i32.const 2)))
(func (type 1) (i32.add (get_local 0) (i32.const 3)))
(func (type 1) (i32.add (get_local 0) (i32.const 4)))
(table 0 1 2 3 4 5)
(func (param i32) (result i32) (call_indirect 0 (get_local 0)))
(func (param i32) (param i32) (result i32) (call_indirect 1 (get_local 0) (get_local 1)))
(func (param i32) (call_indirect 2 (get_local 0) (i32.const 0)))
(export "v2i" 6)
(export "i2i" 7)
(export "i2v" 8)
)`);
assertEq(v2i(0), 13);
assertEq(v2i(1), 42);
assertErrorMessage(() => v2i(2), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => v2i(3), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => v2i(4), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => v2i(5), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2i(0), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2i(1), Error, /wasm indirect call signature mismatch/);
assertEq(i2i(2, 100), 101);
assertEq(i2i(3, 100), 102);
assertEq(i2i(4, 100), 103);
assertEq(i2i(5, 100), 104);
assertErrorMessage(() => i2v(0), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(1), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(2), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(3), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(4), Error, /wasm indirect call signature mismatch/);
assertErrorMessage(() => i2v(5), Error, /wasm indirect call signature mismatch/);
for (bad of [6, 7, 100, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math.pow(2,32)-2, Math.pow(2,32)-1]) {
assertThrowsInstanceOf(() => v2i(bad), RangeError);
assertThrowsInstanceOf(() => i2i(bad, 0), RangeError);
assertThrowsInstanceOf(() => i2v(bad, 0), RangeError);
}

View File

@ -20,6 +20,7 @@ const exportSectionStr = "export";
const codeSectionStr = "code";
const dataSectionStr = "data";
const funcSubsectionStr = "func";
const tableStr = "table";
const magicError = /failed to match magic number/;
const versionError = /failed to match binary version/;
@ -135,9 +136,20 @@ function importSection(imports) {
return { name: importSectionStr, body };
}
const trivialSigSection = sigSection([{args:[], ret:VoidCode}]);
function tableSection(elems) {
var body = [];
body.push(...varU32(elems.length));
for (let i of elems)
body.push(...varU32(i));
return { name: tableStr, body };
}
const v2vSig = {args:[], ret:VoidCode};
const i2vSig = {args:[I32Code], ret:VoidCode};
const trivialSigSection = sigSection([v2vSig]);
const trivialDeclSection = declSection([0]);
const trivialCodeSection = codeSection([{locals:[], body:[0, 0]}]);
const nopFunc = {locals:[], body:[0, 0]};
const trivialCodeSection = codeSection([nopFunc]);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigSectionStr, body: U32MAX_LEB, } ]))), TypeError, /too many signatures/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigSectionStr, body: [1, ...U32MAX_LEB], } ]))), TypeError, /too many arguments in signature/);
@ -149,15 +161,16 @@ assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectio
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1, 1, 0]}]))), TypeError);
wasmEval(toBuf(moduleWithSections([sigSection([])])));
wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:VoidCode}])])));
wasmEval(toBuf(moduleWithSections([sigSection([{args:[I32Code], ret:VoidCode}])])));
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig])])));
wasmEval(toBuf(moduleWithSections([sigSection([i2vSig])])));
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig, i2vSig])])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:100}])]))), TypeError, /bad expression type/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])]))), TypeError, /bad value type/);
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([]), declSection([0])]))), TypeError, /signature index out of range/);
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([1])]))), TypeError, /signature index out of range/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([0])]))), TypeError, /fewer function definitions than declarations/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([0])]))), TypeError, /different number of definitions than declarations/);
wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, trivialCodeSection])));
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: importSectionStr, body:[]}]))), TypeError);
@ -172,4 +185,12 @@ wasmEval(toBuf(moduleWithSections([
trivialDeclSection,
trivialCodeSection])), {a:()=>{}});
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: dataSectionStr, body: [], } ]))), Error, /data section requires a memory section/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: dataSectionStr, body: [], } ]))), TypeError, /data section requires a memory section/);
wasmEval(toBuf(moduleWithSections([tableSection([])])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([tableSection([0])]))), TypeError, /table element out of range/);
wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, tableSection([0]), trivialCodeSection])));
wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, tableSection([0,0]), trivialCodeSection])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, tableSection([0,1]), trivialCodeSection]))), TypeError, /table element out of range/);
wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([0,0,0]), tableSection([0,1,0,2]), codeSection([nopFunc,nopFunc,nopFunc])])));
wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), codeSection([nopFunc,nopFunc,nopFunc])])));

View File

@ -4591,6 +4591,9 @@ HashNumber
MAsmJSLoadFuncPtr::valueHash() const
{
HashNumber hash = MDefinition::valueHash();
hash = addU32ToHash(hash, hasLimit_);
hash = addU32ToHash(hash, limit_);
hash = addU32ToHash(hash, alwaysThrow_);
hash = addU32ToHash(hash, globalDataOffset_);
return hash;
}
@ -4600,7 +4603,10 @@ MAsmJSLoadFuncPtr::congruentTo(const MDefinition* ins) const
{
if (ins->isAsmJSLoadFuncPtr()) {
const MAsmJSLoadFuncPtr* load = ins->toAsmJSLoadFuncPtr();
return globalDataOffset_ == load->globalDataOffset_;
return hasLimit_ == load->hasLimit_ &&
limit_ == load->limit_ &&
alwaysThrow_ == load->alwaysThrow_ &&
globalDataOffset_ == load->globalDataOffset_;
}
return false;
}

View File

@ -14093,25 +14093,39 @@ class MAsmJSLoadFuncPtr
: public MUnaryInstruction,
public NoTypePolicy::Data
{
MAsmJSLoadFuncPtr(unsigned globalDataOffset, MDefinition* index)
: MUnaryInstruction(index), globalDataOffset_(globalDataOffset)
MAsmJSLoadFuncPtr(MDefinition* index, bool hasLimit, uint32_t limit, bool alwaysThrow,
unsigned globalDataOffset)
: MUnaryInstruction(index), hasLimit_(hasLimit), limit_(limit), alwaysThrow_(alwaysThrow),
globalDataOffset_(globalDataOffset)
{
setResultType(MIRType_Pointer);
}
bool hasLimit_;
uint32_t limit_;
bool alwaysThrow_;
unsigned globalDataOffset_;
public:
INSTRUCTION_HEADER(AsmJSLoadFuncPtr)
static MAsmJSLoadFuncPtr* New(TempAllocator& alloc, unsigned globalDataOffset,
MDefinition* index)
static MAsmJSLoadFuncPtr* New(TempAllocator& alloc, MDefinition* index, uint32_t limit,
bool alwaysThrow, unsigned globalDataOffset)
{
return new(alloc) MAsmJSLoadFuncPtr(globalDataOffset, index);
return new(alloc) MAsmJSLoadFuncPtr(index, true, limit, alwaysThrow, globalDataOffset);
}
static MAsmJSLoadFuncPtr* New(TempAllocator& alloc, MDefinition* index,
unsigned globalDataOffset)
{
return new(alloc) MAsmJSLoadFuncPtr(index, false, 0, false, globalDataOffset);
}
unsigned globalDataOffset() const { return globalDataOffset_; }
MDefinition* index() const { return getOperand(0); }
bool hasLimit() const { return hasLimit_; }
uint32_t limit() const { MOZ_ASSERT(hasLimit_); return limit_; }
bool alwaysThrow() const { return alwaysThrow_; }
unsigned globalDataOffset() const { return globalDataOffset_; }
HashNumber valueHash() const override;
bool congruentTo(const MDefinition* ins) const override;

View File

@ -779,7 +779,8 @@ class MacroAssembler : public MacroAssemblerSpecific
// Branch functions
inline void branch32(Condition cond, Register lhs, Register rhs, Label* label) PER_SHARED_ARCH;
inline void branch32(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
template <class L>
inline void branch32(Condition cond, Register lhs, Imm32 rhs, L label) PER_SHARED_ARCH;
inline void branch32(Condition cond, const Address& lhs, Register rhs, Label* label) PER_SHARED_ARCH;
inline void branch32(Condition cond, const Address& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;

View File

@ -2658,10 +2658,20 @@ CodeGeneratorARM::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins)
Register index = ToRegister(ins->index());
Register tmp = ToRegister(ins->temp());
Register out = ToRegister(ins->output());
unsigned addr = mir->globalDataOffset();
masm.ma_mov(Imm32(addr - AsmJSGlobalRegBias), tmp);
masm.as_add(tmp, tmp, lsl(index, 2));
masm.ma_ldr(DTRAddr(GlobalReg, DtrRegImmShift(tmp, LSL, 0)), out);
if (mir->hasLimit()) {
masm.branch32(Assembler::Condition::AboveOrEqual, index, Imm32(mir->limit()),
wasm::JumpTarget::OutOfBounds);
}
if (mir->alwaysThrow()) {
masm.jump(wasm::JumpTarget::BadIndirectCall);
} else {
unsigned addr = mir->globalDataOffset();
masm.ma_mov(Imm32(addr - AsmJSGlobalRegBias), tmp);
masm.as_add(tmp, tmp, lsl(index, 2));
masm.ma_ldr(DTRAddr(GlobalReg, DtrRegImmShift(tmp, LSL, 0)), out);
}
}
void

View File

@ -400,8 +400,9 @@ MacroAssembler::branch32(Condition cond, Register lhs, Register rhs, Label* labe
ma_b(label, cond);
}
template <class L>
void
MacroAssembler::branch32(Condition cond, Register lhs, Imm32 rhs, Label* label)
MacroAssembler::branch32(Condition cond, Register lhs, Imm32 rhs, L label)
{
ma_cmp(lhs, rhs);
ma_b(label, cond);

View File

@ -445,8 +445,9 @@ MacroAssembler::branch32(Condition cond, Register lhs, Register rhs, Label* labe
B(label, cond);
}
template <class L>
void
MacroAssembler::branch32(Condition cond, Register lhs, Imm32 imm, Label* label)
MacroAssembler::branch32(Condition cond, Register lhs, Imm32 imm, L label)
{
cmp32(lhs, imm);
B(label, cond);

View File

@ -1992,8 +1992,17 @@ CodeGeneratorMIPSShared::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins)
Register out = ToRegister(ins->output());
unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
BaseIndex source(GlobalReg, index, ScalePointer, addr);
masm.loadPtr(source, out);
if (mir->hasLimit()) {
masm.branch32(Assembler::Condition::AboveOrEqual, index, Imm32(mir->limit()),
wasm::JumpTarget::OutOfBounds);
}
if (mir->alwaysThrow()) {
masm.jump(wasm::JumpTarget::BadIndirectCall);
} else {
BaseIndex source(GlobalReg, index, ScalePointer, addr);
masm.loadPtr(source, out);
}
}
void

View File

@ -200,8 +200,9 @@ MacroAssembler::branch32(Condition cond, Register lhs, Register rhs, Label* labe
ma_b(lhs, rhs, label, cond);
}
template <class L>
void
MacroAssembler::branch32(Condition cond, Register lhs, Imm32 imm, Label* label)
MacroAssembler::branch32(Condition cond, Register lhs, Imm32 imm, L label)
{
ma_b(lhs, imm, label, cond);
}

View File

@ -779,9 +779,18 @@ CodeGeneratorX64::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins)
Register tmp = ToRegister(ins->temp());
Register out = ToRegister(ins->output());
CodeOffset label = masm.leaRipRelative(tmp);
masm.loadPtr(Operand(tmp, index, TimesEight, 0), out);
masm.append(AsmJSGlobalAccess(label, mir->globalDataOffset()));
if (mir->hasLimit()) {
masm.branch32(Assembler::Condition::AboveOrEqual, index, Imm32(mir->limit()),
wasm::JumpTarget::OutOfBounds);
}
if (mir->alwaysThrow()) {
masm.jump(wasm::JumpTarget::BadIndirectCall);
} else {
CodeOffset label = masm.leaRipRelative(tmp);
masm.loadPtr(Operand(tmp, index, TimesEight, 0), out);
masm.append(AsmJSGlobalAccess(label, mir->globalDataOffset()));
}
}
void

View File

@ -190,8 +190,9 @@ MacroAssembler::branch32(Condition cond, Register lhs, Register rhs, Label* labe
j(cond, label);
}
template <class L>
void
MacroAssembler::branch32(Condition cond, Register lhs, Imm32 rhs, Label* label)
MacroAssembler::branch32(Condition cond, Register lhs, Imm32 rhs, L label)
{
cmp32(lhs, rhs);
j(cond, label);

View File

@ -828,8 +828,18 @@ CodeGeneratorX86::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins)
Register index = ToRegister(ins->index());
Register out = ToRegister(ins->output());
CodeOffset label = masm.movlWithPatch(PatchedAbsoluteAddress(), index, TimesFour, out);
masm.append(AsmJSGlobalAccess(label, mir->globalDataOffset()));
if (mir->hasLimit()) {
masm.branch32(Assembler::Condition::AboveOrEqual, index, Imm32(mir->limit()),
wasm::JumpTarget::OutOfBounds);
}
if (mir->alwaysThrow()) {
masm.jump(wasm::JumpTarget::BadIndirectCall);
} else {
CodeOffset label = masm.movlWithPatch(PatchedAbsoluteAddress(), index, TimesFour, out);
masm.append(AsmJSGlobalAccess(label, mir->globalDataOffset()));
}
}
void

View File

@ -366,6 +366,7 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 1, JSEXN_NONE, "Successfully compiled
MSG_DEF(JSMSG_WASM_FAIL, 1, JSEXN_TYPEERR, "wasm error: {0}")
MSG_DEF(JSMSG_WASM_DECODE_FAIL, 2, JSEXN_TYPEERR, "wasm validation error at offset {0}: {1}")
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
MSG_DEF(JSMSG_WASM_BAD_IND_CALL, 0, JSEXN_ERR, "wasm indirect call signature mismatch")
// Proxy
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")

View File

@ -35,7 +35,7 @@ static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 346;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
static_assert(JSErr_Limit == 444,
static_assert(JSErr_Limit == 445,
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
"removed MSG_DEFs from js.msg, you should increment "
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "