mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1247846 - Baldr: add indirect function table and call_indirect (r=sunfish)
MozReview-Commit-ID: HFq8Nh0XCkB
This commit is contained in:
parent
d4618c01b3
commit
1aab84c5af
@ -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_; }
|
||||
|
@ -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(§ionStart))
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -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)";
|
||||
}
|
||||
|
@ -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)
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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)),
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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])])));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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 "
|
||||
|
Loading…
Reference in New Issue
Block a user