зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset f56ef640d6c0 (bug 1428453) for failing web platform tests on /_mozilla/wasm/select.wast.js r=backout on a CLOSED TREE
This commit is contained in:
Родитель
2de79f83da
Коммит
4eaec4d64b
|
@ -194,7 +194,7 @@ for (let type of ['f32', 'f64']) {
|
||||||
(func (export "") (call $foo))
|
(func (export "") (call $foo))
|
||||||
)`,
|
)`,
|
||||||
WebAssembly.RuntimeError,
|
WebAssembly.RuntimeError,
|
||||||
["", ">", "1,>", "0,1,>", "1,>", "", ">", ""]);
|
["", ">", "1,>", "0,1,>", "interstitial,0,1,>", "trap handling,0,1,>", "", ">", ""]);
|
||||||
|
|
||||||
testError(
|
testError(
|
||||||
`(module
|
`(module
|
||||||
|
|
|
@ -12350,7 +12350,7 @@ CodeGenerator::visitWasmTrap(LWasmTrap* lir)
|
||||||
MOZ_ASSERT(gen->compilingWasm());
|
MOZ_ASSERT(gen->compilingWasm());
|
||||||
const MWasmTrap* mir = lir->mir();
|
const MWasmTrap* mir = lir->mir();
|
||||||
|
|
||||||
masm.wasmTrap(mir->trap(), mir->bytecodeOffset());
|
masm.jump(oldTrap(mir, mir->trap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -2930,12 +2930,6 @@ MacroAssembler::maybeBranchTestType(MIRType type, MDefinition* maybeDef, Registe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MacroAssembler::wasmTrap(wasm::Trap trap, wasm::BytecodeOffset bytecodeOffset)
|
|
||||||
{
|
|
||||||
append(trap, wasm::TrapSite(illegalInstruction().offset(), bytecodeOffset));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::wasmCallImport(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee)
|
MacroAssembler::wasmCallImport(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1399,9 +1399,6 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// wasm support
|
// wasm support
|
||||||
|
|
||||||
CodeOffset illegalInstruction() PER_SHARED_ARCH;
|
|
||||||
void wasmTrap(wasm::Trap trap, wasm::BytecodeOffset bytecodeOffset);
|
|
||||||
|
|
||||||
// Emit a bounds check against the wasm heap limit, jumping to 'label' if 'cond' holds.
|
// Emit a bounds check against the wasm heap limit, jumping to 'label' if 'cond' holds.
|
||||||
// Required when WASM_HUGE_MEMORY is not defined.
|
// Required when WASM_HUGE_MEMORY is not defined.
|
||||||
template <class L>
|
template <class L>
|
||||||
|
|
|
@ -3005,14 +3005,6 @@ Assembler::as_bkpt()
|
||||||
hit++;
|
hit++;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferOffset
|
|
||||||
Assembler::as_illegal_trap()
|
|
||||||
{
|
|
||||||
// Encoding of the permanently-undefined 'udf' instruction, with the imm16
|
|
||||||
// set to 0.
|
|
||||||
return writeInst(0xe7f000f0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Assembler::flushBuffer()
|
Assembler::flushBuffer()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1733,7 +1733,6 @@ class Assembler : public AssemblerShared
|
||||||
static void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target);
|
static void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target);
|
||||||
|
|
||||||
void as_bkpt();
|
void as_bkpt();
|
||||||
BufferOffset as_illegal_trap();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
|
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
|
||||||
|
|
|
@ -4911,12 +4911,6 @@ template void
|
||||||
MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
|
MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
|
||||||
const BaseIndex& dest, MIRType slotType);
|
const BaseIndex& dest, MIRType slotType);
|
||||||
|
|
||||||
CodeOffset
|
|
||||||
MacroAssembler::illegalInstruction()
|
|
||||||
{
|
|
||||||
return CodeOffset(as_illegal_trap().getOffset());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
|
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
|
||||||
{
|
{
|
||||||
|
|
|
@ -260,11 +260,6 @@ class SimInstruction {
|
||||||
return typeValue() == 7 && bit(24) == 1 && svcValue() >= kStopCode;
|
return typeValue() == 7 && bit(24) == 1 && svcValue() >= kStopCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test for a udf instruction, which falls under type 3.
|
|
||||||
inline bool isUDF() const {
|
|
||||||
return (instructionBits() & 0xfff000f0) == 0xe7f000f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special accessors that test for existence of a value.
|
// Special accessors that test for existence of a value.
|
||||||
inline bool hasS() const { return sValue() == 1; }
|
inline bool hasS() const { return sValue() == 1; }
|
||||||
inline bool hasB() const { return bValue() == 1; }
|
inline bool hasB() const { return bValue() == 1; }
|
||||||
|
@ -1588,16 +1583,6 @@ Simulator::handleWasmInterrupt()
|
||||||
set_pc(int32_t(cs->interruptCode()));
|
set_pc(int32_t(cs->interruptCode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline JitActivation*
|
|
||||||
GetJitActivation(JSContext* cx)
|
|
||||||
{
|
|
||||||
if (!wasm::CodeExists)
|
|
||||||
return nullptr;
|
|
||||||
if (!cx->activation() || !cx->activation()->isJit())
|
|
||||||
return nullptr;
|
|
||||||
return cx->activation()->asJit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebAssembly memories contain an extra region of guard pages (see
|
// WebAssembly memories contain an extra region of guard pages (see
|
||||||
// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
|
// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
|
||||||
// using a signal handler that redirects PC to a stub that safely reports an
|
// using a signal handler that redirects PC to a stub that safely reports an
|
||||||
|
@ -1605,11 +1590,13 @@ GetJitActivation(JSContext* cx)
|
||||||
// and cannot be redirected. Therefore, we must avoid hitting the handler by
|
// and cannot be redirected. Therefore, we must avoid hitting the handler by
|
||||||
// redirecting in the simulator before the real handler would have been hit.
|
// redirecting in the simulator before the real handler would have been hit.
|
||||||
bool
|
bool
|
||||||
Simulator::handleWasmSegFault(int32_t addr, unsigned numBytes)
|
Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
|
||||||
{
|
{
|
||||||
JitActivation* act = GetJitActivation(cx_);
|
if (!wasm::CodeExists)
|
||||||
if (!act)
|
|
||||||
return false;
|
return false;
|
||||||
|
if (!cx_->activation() || !cx_->activation()->isJit())
|
||||||
|
return false;
|
||||||
|
JitActivation* act = cx_->activation()->asJit();
|
||||||
|
|
||||||
void* pc = reinterpret_cast<void*>(get_pc());
|
void* pc = reinterpret_cast<void*>(get_pc());
|
||||||
uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
|
uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
|
||||||
|
@ -1636,34 +1623,10 @@ Simulator::handleWasmSegFault(int32_t addr, unsigned numBytes)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
Simulator::handleWasmIllFault()
|
|
||||||
{
|
|
||||||
JitActivation* act = GetJitActivation(cx_);
|
|
||||||
if (!act)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
void* pc = reinterpret_cast<void*>(get_pc());
|
|
||||||
uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
|
|
||||||
|
|
||||||
const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
|
|
||||||
if (!segment)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wasm::Trap trap;
|
|
||||||
wasm::BytecodeOffset bytecode;
|
|
||||||
if (!segment->code().lookupTrap(pc, &trap, &bytecode))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
act->startWasmTrap(trap, bytecode.offset, pc, fp);
|
|
||||||
set_pc(int32_t(segment->trapCode()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
Simulator::readQ(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
Simulator::readQ(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 8))
|
if (handleWasmFault(addr, 8))
|
||||||
return UINT64_MAX;
|
return UINT64_MAX;
|
||||||
|
|
||||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||||
|
@ -1686,7 +1649,7 @@ Simulator::readQ(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
||||||
void
|
void
|
||||||
Simulator::writeQ(int32_t addr, uint64_t value, SimInstruction* instr, UnalignedPolicy f)
|
Simulator::writeQ(int32_t addr, uint64_t value, SimInstruction* instr, UnalignedPolicy f)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 8))
|
if (handleWasmFault(addr, 8))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||||
|
@ -1709,7 +1672,7 @@ Simulator::writeQ(int32_t addr, uint64_t value, SimInstruction* instr, Unaligned
|
||||||
int
|
int
|
||||||
Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 4))
|
if (handleWasmFault(addr, 4))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||||
|
@ -1735,7 +1698,7 @@ Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
||||||
void
|
void
|
||||||
Simulator::writeW(int32_t addr, int value, SimInstruction* instr, UnalignedPolicy f)
|
Simulator::writeW(int32_t addr, int value, SimInstruction* instr, UnalignedPolicy f)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 4))
|
if (handleWasmFault(addr, 4))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||||
|
@ -1780,7 +1743,7 @@ Simulator::readExW(int32_t addr, SimInstruction* instr)
|
||||||
if (addr & 3)
|
if (addr & 3)
|
||||||
MOZ_CRASH("Unaligned exclusive read");
|
MOZ_CRASH("Unaligned exclusive read");
|
||||||
|
|
||||||
if (handleWasmSegFault(addr, 4))
|
if (handleWasmFault(addr, 4))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
|
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
|
||||||
|
@ -1795,7 +1758,7 @@ Simulator::writeExW(int32_t addr, int value, SimInstruction* instr)
|
||||||
if (addr & 3)
|
if (addr & 3)
|
||||||
MOZ_CRASH("Unaligned exclusive write");
|
MOZ_CRASH("Unaligned exclusive write");
|
||||||
|
|
||||||
if (handleWasmSegFault(addr, 4))
|
if (handleWasmFault(addr, 4))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
|
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
|
||||||
|
@ -1810,7 +1773,7 @@ Simulator::writeExW(int32_t addr, int value, SimInstruction* instr)
|
||||||
uint16_t
|
uint16_t
|
||||||
Simulator::readHU(int32_t addr, SimInstruction* instr)
|
Simulator::readHU(int32_t addr, SimInstruction* instr)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 2))
|
if (handleWasmFault(addr, 2))
|
||||||
return UINT16_MAX;
|
return UINT16_MAX;
|
||||||
|
|
||||||
// The regexp engine emits unaligned loads, so we don't check for them here
|
// The regexp engine emits unaligned loads, so we don't check for them here
|
||||||
|
@ -1836,7 +1799,7 @@ Simulator::readHU(int32_t addr, SimInstruction* instr)
|
||||||
int16_t
|
int16_t
|
||||||
Simulator::readH(int32_t addr, SimInstruction* instr)
|
Simulator::readH(int32_t addr, SimInstruction* instr)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 2))
|
if (handleWasmFault(addr, 2))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||||
|
@ -1860,7 +1823,7 @@ Simulator::readH(int32_t addr, SimInstruction* instr)
|
||||||
void
|
void
|
||||||
Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr)
|
Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 2))
|
if (handleWasmFault(addr, 2))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||||
|
@ -1883,7 +1846,7 @@ Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr)
|
||||||
void
|
void
|
||||||
Simulator::writeH(int32_t addr, int16_t value, SimInstruction* instr)
|
Simulator::writeH(int32_t addr, int16_t value, SimInstruction* instr)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 2))
|
if (handleWasmFault(addr, 2))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||||
|
@ -1909,7 +1872,7 @@ Simulator::readExHU(int32_t addr, SimInstruction* instr)
|
||||||
if (addr & 1)
|
if (addr & 1)
|
||||||
MOZ_CRASH("Unaligned exclusive read");
|
MOZ_CRASH("Unaligned exclusive read");
|
||||||
|
|
||||||
if (handleWasmSegFault(addr, 2))
|
if (handleWasmFault(addr, 2))
|
||||||
return UINT16_MAX;
|
return UINT16_MAX;
|
||||||
|
|
||||||
SharedMem<uint16_t*> ptr = SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
|
SharedMem<uint16_t*> ptr = SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
|
||||||
|
@ -1924,7 +1887,7 @@ Simulator::writeExH(int32_t addr, uint16_t value, SimInstruction* instr)
|
||||||
if (addr & 1)
|
if (addr & 1)
|
||||||
MOZ_CRASH("Unaligned exclusive write");
|
MOZ_CRASH("Unaligned exclusive write");
|
||||||
|
|
||||||
if (handleWasmSegFault(addr, 2))
|
if (handleWasmFault(addr, 2))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
SharedMem<uint16_t*> ptr = SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
|
SharedMem<uint16_t*> ptr = SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
|
||||||
|
@ -1939,7 +1902,7 @@ Simulator::writeExH(int32_t addr, uint16_t value, SimInstruction* instr)
|
||||||
uint8_t
|
uint8_t
|
||||||
Simulator::readBU(int32_t addr)
|
Simulator::readBU(int32_t addr)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 1))
|
if (handleWasmFault(addr, 1))
|
||||||
return UINT8_MAX;
|
return UINT8_MAX;
|
||||||
|
|
||||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
|
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
|
||||||
|
@ -1949,7 +1912,7 @@ Simulator::readBU(int32_t addr)
|
||||||
uint8_t
|
uint8_t
|
||||||
Simulator::readExBU(int32_t addr)
|
Simulator::readExBU(int32_t addr)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 1))
|
if (handleWasmFault(addr, 1))
|
||||||
return UINT8_MAX;
|
return UINT8_MAX;
|
||||||
|
|
||||||
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
|
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
|
||||||
|
@ -1961,7 +1924,7 @@ Simulator::readExBU(int32_t addr)
|
||||||
int32_t
|
int32_t
|
||||||
Simulator::writeExB(int32_t addr, uint8_t value)
|
Simulator::writeExB(int32_t addr, uint8_t value)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 1))
|
if (handleWasmFault(addr, 1))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
|
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
|
||||||
|
@ -1976,7 +1939,7 @@ Simulator::writeExB(int32_t addr, uint8_t value)
|
||||||
int8_t
|
int8_t
|
||||||
Simulator::readB(int32_t addr)
|
Simulator::readB(int32_t addr)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 1))
|
if (handleWasmFault(addr, 1))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
||||||
|
@ -1986,7 +1949,7 @@ Simulator::readB(int32_t addr)
|
||||||
void
|
void
|
||||||
Simulator::writeB(int32_t addr, uint8_t value)
|
Simulator::writeB(int32_t addr, uint8_t value)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 1))
|
if (handleWasmFault(addr, 1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
|
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
|
||||||
|
@ -1996,7 +1959,7 @@ Simulator::writeB(int32_t addr, uint8_t value)
|
||||||
void
|
void
|
||||||
Simulator::writeB(int32_t addr, int8_t value)
|
Simulator::writeB(int32_t addr, int8_t value)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 1))
|
if (handleWasmFault(addr, 1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
||||||
|
@ -2006,7 +1969,7 @@ Simulator::writeB(int32_t addr, int8_t value)
|
||||||
int32_t*
|
int32_t*
|
||||||
Simulator::readDW(int32_t addr)
|
Simulator::readDW(int32_t addr)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 8))
|
if (handleWasmFault(addr, 8))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if ((addr & 3) == 0) {
|
if ((addr & 3) == 0) {
|
||||||
|
@ -2021,7 +1984,7 @@ Simulator::readDW(int32_t addr)
|
||||||
void
|
void
|
||||||
Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2)
|
Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2)
|
||||||
{
|
{
|
||||||
if (handleWasmSegFault(addr, 8))
|
if (handleWasmFault(addr, 8))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((addr & 3) == 0) {
|
if ((addr & 3) == 0) {
|
||||||
|
@ -2041,7 +2004,7 @@ Simulator::readExDW(int32_t addr, int32_t* hibits)
|
||||||
if (addr & 3)
|
if (addr & 3)
|
||||||
MOZ_CRASH("Unaligned exclusive read");
|
MOZ_CRASH("Unaligned exclusive read");
|
||||||
|
|
||||||
if (handleWasmSegFault(addr, 8))
|
if (handleWasmFault(addr, 8))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
SharedMem<uint64_t*> ptr = SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
|
SharedMem<uint64_t*> ptr = SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
|
||||||
|
@ -2062,7 +2025,7 @@ Simulator::writeExDW(int32_t addr, int32_t value1, int32_t value2)
|
||||||
if (addr & 3)
|
if (addr & 3)
|
||||||
MOZ_CRASH("Unaligned exclusive write");
|
MOZ_CRASH("Unaligned exclusive write");
|
||||||
|
|
||||||
if (handleWasmSegFault(addr, 8))
|
if (handleWasmFault(addr, 8))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
SharedMem<uint64_t*> ptr = SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
|
SharedMem<uint64_t*> ptr = SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
|
||||||
|
@ -3588,12 +3551,6 @@ rotateBytes(uint32_t val, int32_t rotate)
|
||||||
void
|
void
|
||||||
Simulator::decodeType3(SimInstruction* instr)
|
Simulator::decodeType3(SimInstruction* instr)
|
||||||
{
|
{
|
||||||
if (MOZ_UNLIKELY(instr->isUDF())) {
|
|
||||||
if (handleWasmIllFault())
|
|
||||||
return;
|
|
||||||
MOZ_CRASH("illegal instruction encountered");
|
|
||||||
}
|
|
||||||
|
|
||||||
int rd = instr->rdValue();
|
int rd = instr->rdValue();
|
||||||
int rn = instr->rnValue();
|
int rn = instr->rnValue();
|
||||||
int32_t rn_val = get_register(rn);
|
int32_t rn_val = get_register(rn);
|
||||||
|
|
|
@ -296,8 +296,7 @@ class Simulator
|
||||||
void startWasmInterrupt(JitActivation* act);
|
void startWasmInterrupt(JitActivation* act);
|
||||||
|
|
||||||
// Handle any wasm faults, returning true if the fault was handled.
|
// Handle any wasm faults, returning true if the fault was handled.
|
||||||
bool handleWasmSegFault(int32_t addr, unsigned numBytes);
|
bool handleWasmFault(int32_t addr, unsigned numBytes);
|
||||||
bool handleWasmIllFault();
|
|
||||||
|
|
||||||
// Read and write memory.
|
// Read and write memory.
|
||||||
inline uint8_t readBU(int32_t addr);
|
inline uint8_t readBU(int32_t addr);
|
||||||
|
|
|
@ -852,12 +852,6 @@ MacroAssembler::comment(const char* msg)
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// wasm support
|
// wasm support
|
||||||
|
|
||||||
CodeOffset
|
|
||||||
MacroAssembler::illegalInstruction()
|
|
||||||
{
|
|
||||||
MOZ_CRASH("NYI");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
|
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1863,14 +1863,6 @@ MacroAssembler::comment(const char* msg)
|
||||||
Assembler::comment(msg);
|
Assembler::comment(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===============================================================
|
|
||||||
// WebAssembly
|
|
||||||
|
|
||||||
CodeOffset
|
|
||||||
MacroAssembler::illegalInstruction()
|
|
||||||
{
|
|
||||||
MOZ_CRASH("NYI");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry)
|
MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry)
|
||||||
|
|
|
@ -871,7 +871,6 @@ class AssemblerShared
|
||||||
{
|
{
|
||||||
wasm::CallSiteVector callSites_;
|
wasm::CallSiteVector callSites_;
|
||||||
wasm::CallSiteTargetVector callSiteTargets_;
|
wasm::CallSiteTargetVector callSiteTargets_;
|
||||||
wasm::TrapSiteVectorArray trapSites_;
|
|
||||||
wasm::OldTrapSiteVector oldTrapSites_;
|
wasm::OldTrapSiteVector oldTrapSites_;
|
||||||
wasm::OldTrapFarJumpVector oldTrapFarJumps_;
|
wasm::OldTrapFarJumpVector oldTrapFarJumps_;
|
||||||
wasm::CallFarJumpVector callFarJumps_;
|
wasm::CallFarJumpVector callFarJumps_;
|
||||||
|
@ -931,9 +930,6 @@ class AssemblerShared
|
||||||
enoughMemory_ &= callSites_.emplaceBack(desc, retAddr.offset());
|
enoughMemory_ &= callSites_.emplaceBack(desc, retAddr.offset());
|
||||||
enoughMemory_ &= callSiteTargets_.emplaceBack(mozilla::Forward<Args>(args)...);
|
enoughMemory_ &= callSiteTargets_.emplaceBack(mozilla::Forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
void append(wasm::Trap trap, wasm::TrapSite site) {
|
|
||||||
enoughMemory_ &= trapSites_[trap].append(site);
|
|
||||||
}
|
|
||||||
void append(wasm::OldTrapSite trapSite) {
|
void append(wasm::OldTrapSite trapSite) {
|
||||||
enoughMemory_ &= oldTrapSites_.append(trapSite);
|
enoughMemory_ &= oldTrapSites_.append(trapSite);
|
||||||
}
|
}
|
||||||
|
@ -970,7 +966,6 @@ class AssemblerShared
|
||||||
|
|
||||||
wasm::CallSiteVector& callSites() { return callSites_; }
|
wasm::CallSiteVector& callSites() { return callSites_; }
|
||||||
wasm::CallSiteTargetVector& callSiteTargets() { return callSiteTargets_; }
|
wasm::CallSiteTargetVector& callSiteTargets() { return callSiteTargets_; }
|
||||||
wasm::TrapSiteVectorArray& trapSites() { return trapSites_; }
|
|
||||||
wasm::OldTrapSiteVector& oldTrapSites() { return oldTrapSites_; }
|
wasm::OldTrapSiteVector& oldTrapSites() { return oldTrapSites_; }
|
||||||
wasm::OldTrapFarJumpVector& oldTrapFarJumps() { return oldTrapFarJumps_; }
|
wasm::OldTrapFarJumpVector& oldTrapFarJumps() { return oldTrapFarJumps_; }
|
||||||
wasm::CallFarJumpVector& callFarJumps() { return callFarJumps_; }
|
wasm::CallFarJumpVector& callFarJumps() { return callFarJumps_; }
|
||||||
|
|
|
@ -1101,11 +1101,6 @@ class AssemblerX86Shared : public AssemblerShared
|
||||||
void breakpoint() {
|
void breakpoint() {
|
||||||
masm.int3();
|
masm.int3();
|
||||||
}
|
}
|
||||||
CodeOffset ud2() {
|
|
||||||
CodeOffset off(masm.currentOffset());
|
|
||||||
masm.ud2();
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
|
static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
|
||||||
static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
|
static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
|
||||||
|
|
|
@ -668,14 +668,7 @@ MacroAssembler::pushFakeReturnAddress(Register scratch)
|
||||||
return retAddr;
|
return retAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===============================================================
|
// wasm specific methods, used in both the wasm baseline compiler and ion.
|
||||||
// WebAssembly
|
|
||||||
|
|
||||||
CodeOffset
|
|
||||||
MacroAssembler::illegalInstruction()
|
|
||||||
{
|
|
||||||
return ud2();
|
|
||||||
}
|
|
||||||
|
|
||||||
// RAII class that generates the jumps to traps when it's destructed, to
|
// RAII class that generates the jumps to traps when it's destructed, to
|
||||||
// prevent some code duplication in the outOfLineWasmTruncateXtoY methods.
|
// prevent some code duplication in the outOfLineWasmTruncateXtoY methods.
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
#include "vm/ErrorReporting.h"
|
#include "vm/ErrorReporting.h"
|
||||||
#include "vm/MallocProvider.h"
|
#include "vm/MallocProvider.h"
|
||||||
#include "vm/Runtime.h"
|
#include "vm/Runtime.h"
|
||||||
|
#ifdef XP_DARWIN
|
||||||
|
# include "wasm/WasmSignalHandlers.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/DoublyLinkedList.h"
|
#include "mozilla/DoublyLinkedList.h"
|
||||||
#include "mozilla/LinkedList.h"
|
#include "mozilla/LinkedList.h"
|
||||||
#include "mozilla/MaybeOneOf.h"
|
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
#include "mozilla/PodOperations.h"
|
#include "mozilla/PodOperations.h"
|
||||||
#include "mozilla/Scoped.h"
|
#include "mozilla/Scoped.h"
|
||||||
|
@ -50,7 +49,6 @@
|
||||||
#include "vm/Stack.h"
|
#include "vm/Stack.h"
|
||||||
#include "vm/Stopwatch.h"
|
#include "vm/Stopwatch.h"
|
||||||
#include "vm/Symbol.h"
|
#include "vm/Symbol.h"
|
||||||
#include "wasm/WasmSignalHandlers.h"
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
|
@ -1051,17 +1049,34 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
||||||
public:
|
public:
|
||||||
js::RuntimeCaches& caches() { return caches_.ref(); }
|
js::RuntimeCaches& caches() { return caches_.ref(); }
|
||||||
|
|
||||||
// When wasm traps or is interrupted, the signal handler records some data
|
private:
|
||||||
// for unwinding purposes. Wasm code can't interrupt or trap reentrantly.
|
// When wasm is interrupted, the pc at which we should return if the
|
||||||
js::ActiveThreadData<
|
// interrupt hasn't stopped execution of the current running code. Since
|
||||||
mozilla::MaybeOneOf<js::wasm::TrapData, js::wasm::InterruptData>
|
// this is used only by the interrupt handler and the latter is not
|
||||||
> wasmUnwindData;
|
// reentrant, this value can't be clobbered so there is at most one
|
||||||
|
// resume PC at a time.
|
||||||
|
js::ActiveThreadData<void*> wasmResumePC_;
|
||||||
|
|
||||||
js::wasm::TrapData& wasmTrapData() {
|
// To ensure a consistent state of fp/pc, the unwound pc might be
|
||||||
return wasmUnwindData.ref().ref<js::wasm::TrapData>();
|
// different from the resumePC, especially at call boundaries.
|
||||||
|
js::ActiveThreadData<void*> wasmUnwindPC_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void startWasmInterrupt(void* resumePC, void* unwindPC) {
|
||||||
|
MOZ_ASSERT(resumePC && unwindPC);
|
||||||
|
wasmResumePC_ = resumePC;
|
||||||
|
wasmUnwindPC_ = unwindPC;
|
||||||
}
|
}
|
||||||
js::wasm::InterruptData& wasmInterruptData() {
|
void finishWasmInterrupt() {
|
||||||
return wasmUnwindData.ref().ref<js::wasm::InterruptData>();
|
MOZ_ASSERT(wasmResumePC_ && wasmUnwindPC_);
|
||||||
|
wasmResumePC_ = nullptr;
|
||||||
|
wasmUnwindPC_ = nullptr;
|
||||||
|
}
|
||||||
|
void* wasmResumePC() const {
|
||||||
|
return wasmResumePC_;
|
||||||
|
}
|
||||||
|
void* wasmUnwindPC() const {
|
||||||
|
return wasmUnwindPC_;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1552,7 +1552,6 @@ jit::JitActivation::~JitActivation()
|
||||||
MOZ_ASSERT(!bailoutData_);
|
MOZ_ASSERT(!bailoutData_);
|
||||||
|
|
||||||
MOZ_ASSERT(!isWasmInterrupted());
|
MOZ_ASSERT(!isWasmInterrupted());
|
||||||
MOZ_ASSERT(!isWasmTrapping());
|
|
||||||
|
|
||||||
clearRematerializedFrames();
|
clearRematerializedFrames();
|
||||||
js_delete(rematerializedFrames_);
|
js_delete(rematerializedFrames_);
|
||||||
|
@ -1726,8 +1725,9 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
|
||||||
MOZ_ASSERT(state.fp);
|
MOZ_ASSERT(state.fp);
|
||||||
|
|
||||||
// Execution can only be interrupted in function code. Afterwards, control
|
// Execution can only be interrupted in function code. Afterwards, control
|
||||||
// flow does not reenter function code and thus there can be no
|
// flow does not reenter function code and thus there should be no
|
||||||
// interrupt-during-interrupt.
|
// interrupt-during-interrupt.
|
||||||
|
MOZ_ASSERT(!isWasmInterrupted());
|
||||||
|
|
||||||
bool ignoredUnwound;
|
bool ignoredUnwound;
|
||||||
wasm::UnwindState unwindState;
|
wasm::UnwindState unwindState;
|
||||||
|
@ -1736,7 +1736,7 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
|
||||||
void* pc = unwindState.pc;
|
void* pc = unwindState.pc;
|
||||||
MOZ_ASSERT(wasm::LookupCode(pc)->lookupRange(pc)->isFunction());
|
MOZ_ASSERT(wasm::LookupCode(pc)->lookupRange(pc)->isFunction());
|
||||||
|
|
||||||
cx_->runtime()->wasmUnwindData.ref().construct<wasm::InterruptData>(pc, state.pc);
|
cx_->runtime()->startWasmInterrupt(state.pc, pc);
|
||||||
setWasmExitFP(unwindState.fp);
|
setWasmExitFP(unwindState.fp);
|
||||||
|
|
||||||
MOZ_ASSERT(compartment() == unwindState.fp->tls->instance->compartment());
|
MOZ_ASSERT(compartment() == unwindState.fp->tls->instance->compartment());
|
||||||
|
@ -1746,17 +1746,18 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
|
||||||
void
|
void
|
||||||
jit::JitActivation::finishWasmInterrupt()
|
jit::JitActivation::finishWasmInterrupt()
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(hasWasmExitFP());
|
||||||
MOZ_ASSERT(isWasmInterrupted());
|
MOZ_ASSERT(isWasmInterrupted());
|
||||||
|
|
||||||
cx_->runtime()->wasmUnwindData.ref().destroy();
|
cx_->runtime()->finishWasmInterrupt();
|
||||||
packedExitFP_ = nullptr;
|
packedExitFP_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
jit::JitActivation::isWasmInterrupted() const
|
jit::JitActivation::isWasmInterrupted() const
|
||||||
{
|
{
|
||||||
JSRuntime* rt = cx_->runtime();
|
void* pc = cx_->runtime()->wasmUnwindPC();
|
||||||
if (!rt->wasmUnwindData.ref().constructed<wasm::InterruptData>())
|
if (!pc)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Activation* act = cx_->activation();
|
Activation* act = cx_->activation();
|
||||||
|
@ -1767,76 +1768,24 @@ jit::JitActivation::isWasmInterrupted() const
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DebugOnly<const wasm::Frame*> fp = wasmExitFP();
|
DebugOnly<const wasm::Frame*> fp = wasmExitFP();
|
||||||
DebugOnly<void*> unwindPC = rt->wasmInterruptData().unwindPC;
|
MOZ_ASSERT(fp && fp->instance()->code().containsCodePC(pc));
|
||||||
MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
jit::JitActivation::wasmInterruptUnwindPC() const
|
jit::JitActivation::wasmUnwindPC() const
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(hasWasmExitFP());
|
||||||
MOZ_ASSERT(isWasmInterrupted());
|
MOZ_ASSERT(isWasmInterrupted());
|
||||||
return cx_->runtime()->wasmInterruptData().unwindPC;
|
return cx_->runtime()->wasmUnwindPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
jit::JitActivation::wasmInterruptResumePC() const
|
jit::JitActivation::wasmResumePC() const
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(hasWasmExitFP());
|
||||||
MOZ_ASSERT(isWasmInterrupted());
|
MOZ_ASSERT(isWasmInterrupted());
|
||||||
return cx_->runtime()->wasmInterruptData().resumePC;
|
return cx_->runtime()->wasmResumePC();
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
jit::JitActivation::startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset, void* pc, void* fp)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(pc);
|
|
||||||
MOZ_ASSERT(fp);
|
|
||||||
|
|
||||||
cx_->runtime()->wasmUnwindData.ref().construct<wasm::TrapData>(pc, trap, bytecodeOffset);
|
|
||||||
setWasmExitFP((wasm::Frame*)fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
jit::JitActivation::finishWasmTrap()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(isWasmTrapping());
|
|
||||||
|
|
||||||
cx_->runtime()->wasmUnwindData.ref().destroy();
|
|
||||||
packedExitFP_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
jit::JitActivation::isWasmTrapping() const
|
|
||||||
{
|
|
||||||
JSRuntime* rt = cx_->runtime();
|
|
||||||
if (!rt->wasmUnwindData.ref().constructed<wasm::TrapData>())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Activation* act = cx_->activation();
|
|
||||||
while (act && !act->hasWasmExitFP())
|
|
||||||
act = act->prev();
|
|
||||||
|
|
||||||
if (act != this)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DebugOnly<const wasm::Frame*> fp = wasmExitFP();
|
|
||||||
DebugOnly<void*> unwindPC = rt->wasmTrapData().pc;
|
|
||||||
MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void*
|
|
||||||
jit::JitActivation::wasmTrapPC() const
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(isWasmTrapping());
|
|
||||||
return cx_->runtime()->wasmTrapData().pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t
|
|
||||||
jit::JitActivation::wasmTrapBytecodeOffset() const
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(isWasmTrapping());
|
|
||||||
return cx_->runtime()->wasmTrapData().bytecodeOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InterpreterFrameIterator&
|
InterpreterFrameIterator&
|
||||||
|
|
|
@ -1666,18 +1666,11 @@ class JitActivation : public Activation
|
||||||
// Interrupts are started from the interrupt signal handler (or the ARM
|
// Interrupts are started from the interrupt signal handler (or the ARM
|
||||||
// simulator) and cleared by WasmHandleExecutionInterrupt or WasmHandleThrow
|
// simulator) and cleared by WasmHandleExecutionInterrupt or WasmHandleThrow
|
||||||
// when the interrupt is handled.
|
// when the interrupt is handled.
|
||||||
|
|
||||||
void startWasmInterrupt(const JS::ProfilingFrameIterator::RegisterState& state);
|
void startWasmInterrupt(const JS::ProfilingFrameIterator::RegisterState& state);
|
||||||
void finishWasmInterrupt();
|
void finishWasmInterrupt();
|
||||||
bool isWasmInterrupted() const;
|
bool isWasmInterrupted() const;
|
||||||
void* wasmInterruptUnwindPC() const;
|
void* wasmUnwindPC() const;
|
||||||
void* wasmInterruptResumePC() const;
|
void* wasmResumePC() const;
|
||||||
|
|
||||||
void startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset, void* pc, void* fp);
|
|
||||||
void finishWasmTrap();
|
|
||||||
bool isWasmTrapping() const;
|
|
||||||
void* wasmTrapPC() const;
|
|
||||||
uint32_t wasmTrapBytecodeOffset() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A filtering of the ActivationIterator to only stop at JitActivations.
|
// A filtering of the ActivationIterator to only stop at JitActivations.
|
||||||
|
|
|
@ -3807,6 +3807,15 @@ class BaseCompiler final : public BaseCompilerInterface
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unreachableTrap()
|
||||||
|
{
|
||||||
|
masm.jump(oldTrap(Trap::Unreachable));
|
||||||
|
#ifdef DEBUG
|
||||||
|
masm.breakpoint();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool
|
MOZ_MUST_USE bool
|
||||||
supportsRoundInstruction(RoundingMode mode)
|
supportsRoundInstruction(RoundingMode mode)
|
||||||
{
|
{
|
||||||
|
@ -4896,10 +4905,6 @@ class BaseCompiler final : public BaseCompilerInterface
|
||||||
return iter_.bytecodeOffset();
|
return iter_.bytecodeOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void trap(Trap t) const {
|
|
||||||
masm.wasmTrap(t, bytecodeOffset());
|
|
||||||
}
|
|
||||||
|
|
||||||
OldTrapDesc oldTrap(Trap t) const {
|
OldTrapDesc oldTrap(Trap t) const {
|
||||||
// Use masm.framePushed() because the value needed by the trap machinery
|
// Use masm.framePushed() because the value needed by the trap machinery
|
||||||
// is the size of the frame overall, not the height of the stack area of
|
// is the size of the frame overall, not the height of the stack area of
|
||||||
|
@ -8528,7 +8533,7 @@ BaseCompiler::emitBody()
|
||||||
case uint16_t(Op::Unreachable):
|
case uint16_t(Op::Unreachable):
|
||||||
CHECK(iter_.readUnreachable());
|
CHECK(iter_.readUnreachable());
|
||||||
if (!deadCode_) {
|
if (!deadCode_) {
|
||||||
trap(Trap::Unreachable);
|
unreachableTrap();
|
||||||
deadCode_ = true;
|
deadCode_ = true;
|
||||||
}
|
}
|
||||||
NEXT();
|
NEXT();
|
||||||
|
|
|
@ -87,7 +87,7 @@ WasmHandleExecutionInterrupt()
|
||||||
|
|
||||||
// If CheckForInterrupt succeeded, then execution can proceed and the
|
// If CheckForInterrupt succeeded, then execution can proceed and the
|
||||||
// interrupt is over.
|
// interrupt is over.
|
||||||
void* resumePC = activation->wasmInterruptResumePC();
|
void* resumePC = activation->wasmResumePC();
|
||||||
activation->finishWasmInterrupt();
|
activation->finishWasmInterrupt();
|
||||||
return resumePC;
|
return resumePC;
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,6 @@ wasm::HandleThrow(JSContext* cx, WasmFrameIter& iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(!cx->activation()->asJit()->isWasmInterrupted(), "unwinding clears the interrupt");
|
MOZ_ASSERT(!cx->activation()->asJit()->isWasmInterrupted(), "unwinding clears the interrupt");
|
||||||
MOZ_ASSERT(!cx->activation()->asJit()->isWasmTrapping(), "unwinding clears the trapping state");
|
|
||||||
|
|
||||||
return iter.unwoundAddressOfReturnAddress();
|
return iter.unwoundAddressOfReturnAddress();
|
||||||
}
|
}
|
||||||
|
@ -289,13 +288,6 @@ WasmOldReportTrap(int32_t trapIndex)
|
||||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
WasmReportTrap()
|
|
||||||
{
|
|
||||||
Trap trap = TlsContext.get()->runtime()->wasmTrapData().trap;
|
|
||||||
WasmOldReportTrap(int32_t(trap));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WasmReportOutOfBounds()
|
WasmReportOutOfBounds()
|
||||||
{
|
{
|
||||||
|
@ -452,9 +444,6 @@ AddressOf(SymbolicAddress imm, ABIFunctionType* abiType)
|
||||||
case SymbolicAddress::HandleThrow:
|
case SymbolicAddress::HandleThrow:
|
||||||
*abiType = Args_General0;
|
*abiType = Args_General0;
|
||||||
return FuncCast(WasmHandleThrow, *abiType);
|
return FuncCast(WasmHandleThrow, *abiType);
|
||||||
case SymbolicAddress::ReportTrap:
|
|
||||||
*abiType = Args_General0;
|
|
||||||
return FuncCast(WasmReportTrap, *abiType);
|
|
||||||
case SymbolicAddress::OldReportTrap:
|
case SymbolicAddress::OldReportTrap:
|
||||||
*abiType = Args_General1;
|
*abiType = Args_General1;
|
||||||
return FuncCast(WasmOldReportTrap, *abiType);
|
return FuncCast(WasmOldReportTrap, *abiType);
|
||||||
|
@ -611,7 +600,6 @@ wasm::NeedsBuiltinThunk(SymbolicAddress sym)
|
||||||
case SymbolicAddress::HandleExecutionInterrupt: // GenerateInterruptExit
|
case SymbolicAddress::HandleExecutionInterrupt: // GenerateInterruptExit
|
||||||
case SymbolicAddress::HandleDebugTrap: // GenerateDebugTrapStub
|
case SymbolicAddress::HandleDebugTrap: // GenerateDebugTrapStub
|
||||||
case SymbolicAddress::HandleThrow: // GenerateThrowStub
|
case SymbolicAddress::HandleThrow: // GenerateThrowStub
|
||||||
case SymbolicAddress::ReportTrap: // GenerateTrapExit
|
|
||||||
case SymbolicAddress::OldReportTrap: // GenerateOldTrapExit
|
case SymbolicAddress::OldReportTrap: // GenerateOldTrapExit
|
||||||
case SymbolicAddress::ReportOutOfBounds: // GenerateOutOfBoundsExit
|
case SymbolicAddress::ReportOutOfBounds: // GenerateOutOfBoundsExit
|
||||||
case SymbolicAddress::ReportUnalignedAccess: // GeneratesUnalignedExit
|
case SymbolicAddress::ReportUnalignedAccess: // GeneratesUnalignedExit
|
||||||
|
@ -908,7 +896,6 @@ wasm::EnsureBuiltinThunksInitialized()
|
||||||
MOZ_ASSERT(masm.callSites().empty());
|
MOZ_ASSERT(masm.callSites().empty());
|
||||||
MOZ_ASSERT(masm.callSiteTargets().empty());
|
MOZ_ASSERT(masm.callSiteTargets().empty());
|
||||||
MOZ_ASSERT(masm.callFarJumps().empty());
|
MOZ_ASSERT(masm.callFarJumps().empty());
|
||||||
MOZ_ASSERT(masm.trapSites().empty());
|
|
||||||
MOZ_ASSERT(masm.oldTrapSites().empty());
|
MOZ_ASSERT(masm.oldTrapSites().empty());
|
||||||
MOZ_ASSERT(masm.oldTrapFarJumps().empty());
|
MOZ_ASSERT(masm.oldTrapFarJumps().empty());
|
||||||
MOZ_ASSERT(masm.callFarJumps().empty());
|
MOZ_ASSERT(masm.callFarJumps().empty());
|
||||||
|
|
|
@ -242,6 +242,9 @@ CodeSegment::create(Tier tier,
|
||||||
const Metadata& metadata)
|
const Metadata& metadata)
|
||||||
{
|
{
|
||||||
// These should always exist and should never be first in the code segment.
|
// These should always exist and should never be first in the code segment.
|
||||||
|
MOZ_ASSERT(linkData.interruptOffset != 0);
|
||||||
|
MOZ_ASSERT(linkData.outOfBoundsOffset != 0);
|
||||||
|
MOZ_ASSERT(linkData.unalignedAccessOffset != 0);
|
||||||
|
|
||||||
auto cs = js::MakeUnique<CodeSegment>();
|
auto cs = js::MakeUnique<CodeSegment>();
|
||||||
if (!cs)
|
if (!cs)
|
||||||
|
@ -265,7 +268,6 @@ CodeSegment::initialize(Tier tier,
|
||||||
MOZ_ASSERT(linkData.interruptOffset);
|
MOZ_ASSERT(linkData.interruptOffset);
|
||||||
MOZ_ASSERT(linkData.outOfBoundsOffset);
|
MOZ_ASSERT(linkData.outOfBoundsOffset);
|
||||||
MOZ_ASSERT(linkData.unalignedAccessOffset);
|
MOZ_ASSERT(linkData.unalignedAccessOffset);
|
||||||
MOZ_ASSERT(linkData.trapOffset);
|
|
||||||
|
|
||||||
tier_ = tier;
|
tier_ = tier;
|
||||||
bytes_ = Move(codeBytes);
|
bytes_ = Move(codeBytes);
|
||||||
|
@ -273,7 +275,6 @@ CodeSegment::initialize(Tier tier,
|
||||||
interruptCode_ = bytes_.get() + linkData.interruptOffset;
|
interruptCode_ = bytes_.get() + linkData.interruptOffset;
|
||||||
outOfBoundsCode_ = bytes_.get() + linkData.outOfBoundsOffset;
|
outOfBoundsCode_ = bytes_.get() + linkData.outOfBoundsOffset;
|
||||||
unalignedAccessCode_ = bytes_.get() + linkData.unalignedAccessOffset;
|
unalignedAccessCode_ = bytes_.get() + linkData.unalignedAccessOffset;
|
||||||
trapCode_ = bytes_.get() + linkData.trapOffset;
|
|
||||||
|
|
||||||
if (!StaticallyLink(*this, linkData))
|
if (!StaticallyLink(*this, linkData))
|
||||||
return false;
|
return false;
|
||||||
|
@ -458,7 +459,6 @@ MetadataTier::serializedSize() const
|
||||||
return SerializedPodVectorSize(memoryAccesses) +
|
return SerializedPodVectorSize(memoryAccesses) +
|
||||||
SerializedPodVectorSize(codeRanges) +
|
SerializedPodVectorSize(codeRanges) +
|
||||||
SerializedPodVectorSize(callSites) +
|
SerializedPodVectorSize(callSites) +
|
||||||
trapSites.serializedSize() +
|
|
||||||
SerializedVectorSize(funcImports) +
|
SerializedVectorSize(funcImports) +
|
||||||
SerializedVectorSize(funcExports);
|
SerializedVectorSize(funcExports);
|
||||||
}
|
}
|
||||||
|
@ -469,7 +469,6 @@ MetadataTier::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
||||||
return memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
|
return memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
|
||||||
codeRanges.sizeOfExcludingThis(mallocSizeOf) +
|
codeRanges.sizeOfExcludingThis(mallocSizeOf) +
|
||||||
callSites.sizeOfExcludingThis(mallocSizeOf) +
|
callSites.sizeOfExcludingThis(mallocSizeOf) +
|
||||||
trapSites.sizeOfExcludingThis(mallocSizeOf) +
|
|
||||||
SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
|
SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
|
||||||
SizeOfVectorExcludingThis(funcExports, mallocSizeOf);
|
SizeOfVectorExcludingThis(funcExports, mallocSizeOf);
|
||||||
}
|
}
|
||||||
|
@ -481,7 +480,6 @@ MetadataTier::serialize(uint8_t* cursor) const
|
||||||
cursor = SerializePodVector(cursor, memoryAccesses);
|
cursor = SerializePodVector(cursor, memoryAccesses);
|
||||||
cursor = SerializePodVector(cursor, codeRanges);
|
cursor = SerializePodVector(cursor, codeRanges);
|
||||||
cursor = SerializePodVector(cursor, callSites);
|
cursor = SerializePodVector(cursor, callSites);
|
||||||
cursor = trapSites.serialize(cursor);
|
|
||||||
cursor = SerializeVector(cursor, funcImports);
|
cursor = SerializeVector(cursor, funcImports);
|
||||||
cursor = SerializeVector(cursor, funcExports);
|
cursor = SerializeVector(cursor, funcExports);
|
||||||
return cursor;
|
return cursor;
|
||||||
|
@ -493,7 +491,6 @@ MetadataTier::deserialize(const uint8_t* cursor)
|
||||||
(cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
|
(cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
|
||||||
(cursor = DeserializePodVector(cursor, &codeRanges)) &&
|
(cursor = DeserializePodVector(cursor, &codeRanges)) &&
|
||||||
(cursor = DeserializePodVector(cursor, &callSites)) &&
|
(cursor = DeserializePodVector(cursor, &callSites)) &&
|
||||||
(cursor = trapSites.deserialize(cursor)) &&
|
|
||||||
(cursor = DeserializeVector(cursor, &funcImports)) &&
|
(cursor = DeserializeVector(cursor, &funcImports)) &&
|
||||||
(cursor = DeserializeVector(cursor, &funcExports));
|
(cursor = DeserializeVector(cursor, &funcExports));
|
||||||
debugTrapFarJumpOffsets.clear();
|
debugTrapFarJumpOffsets.clear();
|
||||||
|
@ -753,7 +750,7 @@ Code::segment(Tier tier) const
|
||||||
bool
|
bool
|
||||||
Code::containsCodePC(const void* pc) const
|
Code::containsCodePC(const void* pc) const
|
||||||
{
|
{
|
||||||
for (Tier t : tiers()) {
|
for (auto t : tiers()) {
|
||||||
const CodeSegment& cs = segment(t);
|
const CodeSegment& cs = segment(t);
|
||||||
if (cs.containsCodePC(pc))
|
if (cs.containsCodePC(pc))
|
||||||
return true;
|
return true;
|
||||||
|
@ -773,7 +770,7 @@ struct CallSiteRetAddrOffset
|
||||||
const CallSite*
|
const CallSite*
|
||||||
Code::lookupCallSite(void* returnAddress) const
|
Code::lookupCallSite(void* returnAddress) const
|
||||||
{
|
{
|
||||||
for (Tier t : tiers()) {
|
for (auto t : tiers()) {
|
||||||
uint32_t target = ((uint8_t*)returnAddress) - segment(t).base();
|
uint32_t target = ((uint8_t*)returnAddress) - segment(t).base();
|
||||||
size_t lowerBound = 0;
|
size_t lowerBound = 0;
|
||||||
size_t upperBound = metadata(t).callSites.length();
|
size_t upperBound = metadata(t).callSites.length();
|
||||||
|
@ -790,7 +787,7 @@ Code::lookupCallSite(void* returnAddress) const
|
||||||
const CodeRange*
|
const CodeRange*
|
||||||
Code::lookupRange(void* pc) const
|
Code::lookupRange(void* pc) const
|
||||||
{
|
{
|
||||||
for (Tier t : tiers()) {
|
for (auto t : tiers()) {
|
||||||
CodeRange::OffsetInCode target((uint8_t*)pc - segment(t).base());
|
CodeRange::OffsetInCode target((uint8_t*)pc - segment(t).base());
|
||||||
const CodeRange* result = LookupInSorted(metadata(t).codeRanges, target);
|
const CodeRange* result = LookupInSorted(metadata(t).codeRanges, target);
|
||||||
if (result)
|
if (result)
|
||||||
|
@ -812,7 +809,7 @@ struct MemoryAccessOffset
|
||||||
const MemoryAccess*
|
const MemoryAccess*
|
||||||
Code::lookupMemoryAccess(void* pc) const
|
Code::lookupMemoryAccess(void* pc) const
|
||||||
{
|
{
|
||||||
for (Tier t : tiers()) {
|
for (auto t : tiers()) {
|
||||||
const MemoryAccessVector& memoryAccesses = metadata(t).memoryAccesses;
|
const MemoryAccessVector& memoryAccesses = metadata(t).memoryAccesses;
|
||||||
|
|
||||||
uint32_t target = ((uint8_t*)pc) - segment(t).base();
|
uint32_t target = ((uint8_t*)pc) - segment(t).base();
|
||||||
|
@ -830,40 +827,6 @@ Code::lookupMemoryAccess(void* pc) const
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrapSitePCOffset
|
|
||||||
{
|
|
||||||
const TrapSiteVector& trapSites;
|
|
||||||
explicit TrapSitePCOffset(const TrapSiteVector& trapSites) : trapSites(trapSites) {}
|
|
||||||
uint32_t operator[](size_t index) const {
|
|
||||||
return trapSites[index].pcOffset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
Code::lookupTrap(void* pc, Trap* trapOut, BytecodeOffset* bytecode) const
|
|
||||||
{
|
|
||||||
for (Tier t : tiers()) {
|
|
||||||
const TrapSiteVectorArray& trapSitesArray = metadata(t).trapSites;
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
|
|
||||||
const TrapSiteVector& trapSites = trapSitesArray[trap];
|
|
||||||
|
|
||||||
uint32_t target = ((uint8_t*)pc) - segment(t).base();
|
|
||||||
size_t lowerBound = 0;
|
|
||||||
size_t upperBound = trapSites.length();
|
|
||||||
|
|
||||||
size_t match;
|
|
||||||
if (BinarySearch(TrapSitePCOffset(trapSites), lowerBound, upperBound, target, &match)) {
|
|
||||||
MOZ_ASSERT(segment(t).containsCodePC(pc));
|
|
||||||
*trapOut = trap;
|
|
||||||
*bytecode = trapSites[match].bytecode;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When enabled, generate profiling labels for every name in funcNames_ that is
|
// When enabled, generate profiling labels for every name in funcNames_ that is
|
||||||
// the name of some Function CodeRange. This involves malloc() so do it now
|
// the name of some Function CodeRange. This involves malloc() so do it now
|
||||||
// since, once we start sampling, we'll be in a signal-handing context where we
|
// since, once we start sampling, we'll be in a signal-handing context where we
|
||||||
|
|
|
@ -81,7 +81,6 @@ class CodeSegment
|
||||||
uint8_t* interruptCode_;
|
uint8_t* interruptCode_;
|
||||||
uint8_t* outOfBoundsCode_;
|
uint8_t* outOfBoundsCode_;
|
||||||
uint8_t* unalignedAccessCode_;
|
uint8_t* unalignedAccessCode_;
|
||||||
uint8_t* trapCode_;
|
|
||||||
|
|
||||||
bool registered_;
|
bool registered_;
|
||||||
|
|
||||||
|
@ -109,7 +108,6 @@ class CodeSegment
|
||||||
interruptCode_(nullptr),
|
interruptCode_(nullptr),
|
||||||
outOfBoundsCode_(nullptr),
|
outOfBoundsCode_(nullptr),
|
||||||
unalignedAccessCode_(nullptr),
|
unalignedAccessCode_(nullptr),
|
||||||
trapCode_(nullptr),
|
|
||||||
registered_(false)
|
registered_(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -141,7 +139,6 @@ class CodeSegment
|
||||||
uint8_t* interruptCode() const { return interruptCode_; }
|
uint8_t* interruptCode() const { return interruptCode_; }
|
||||||
uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
|
uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
|
||||||
uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
|
uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
|
||||||
uint8_t* trapCode() const { return trapCode_; }
|
|
||||||
|
|
||||||
bool containsCodePC(const void* pc) const {
|
bool containsCodePC(const void* pc) const {
|
||||||
return pc >= base() && pc < (base() + length_);
|
return pc >= base() && pc < (base() + length_);
|
||||||
|
@ -354,7 +351,6 @@ struct MetadataTier
|
||||||
MemoryAccessVector memoryAccesses;
|
MemoryAccessVector memoryAccesses;
|
||||||
CodeRangeVector codeRanges;
|
CodeRangeVector codeRanges;
|
||||||
CallSiteVector callSites;
|
CallSiteVector callSites;
|
||||||
TrapSiteVectorArray trapSites;
|
|
||||||
FuncImportVector funcImports;
|
FuncImportVector funcImports;
|
||||||
FuncExportVector funcExports;
|
FuncExportVector funcExports;
|
||||||
|
|
||||||
|
@ -492,7 +488,6 @@ class Code : public ShareableBase<Code>
|
||||||
const CodeRange* lookupRange(void* pc) const;
|
const CodeRange* lookupRange(void* pc) const;
|
||||||
const MemoryAccess* lookupMemoryAccess(void* pc) const;
|
const MemoryAccess* lookupMemoryAccess(void* pc) const;
|
||||||
bool containsCodePC(const void* pc) const;
|
bool containsCodePC(const void* pc) const;
|
||||||
bool lookupTrap(void* pc, Trap* trap, BytecodeOffset* bytecode) const;
|
|
||||||
|
|
||||||
// To save memory, profilingLabels_ are generated lazily when profiling mode
|
// To save memory, profilingLabels_ are generated lazily when profiling mode
|
||||||
// is enabled.
|
// is enabled.
|
||||||
|
|
|
@ -42,23 +42,6 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(fp_);
|
MOZ_ASSERT(fp_);
|
||||||
|
|
||||||
// When the stack is captured during a trap (viz., to create the .stack
|
|
||||||
// for an Error object), use the pc/bytecode information captured by the
|
|
||||||
// signal handler in the runtime.
|
|
||||||
|
|
||||||
if (activation->isWasmTrapping()) {
|
|
||||||
code_ = &fp_->tls->instance->code();
|
|
||||||
MOZ_ASSERT(code_ == LookupCode(activation->wasmTrapPC()));
|
|
||||||
|
|
||||||
codeRange_ = code_->lookupRange(activation->wasmTrapPC());
|
|
||||||
MOZ_ASSERT(codeRange_->kind() == CodeRange::Function);
|
|
||||||
|
|
||||||
lineOrBytecode_ = activation->wasmTrapBytecodeOffset();
|
|
||||||
|
|
||||||
MOZ_ASSERT(!done());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When asynchronously interrupted, exitFP is set to the interrupted frame
|
// When asynchronously interrupted, exitFP is set to the interrupted frame
|
||||||
// itself and so we do not want to skip it. Instead, we can recover the
|
// itself and so we do not want to skip it. Instead, we can recover the
|
||||||
// Code and CodeRange from the JitActivation, which are set when control
|
// Code and CodeRange from the JitActivation, which are set when control
|
||||||
|
@ -69,9 +52,9 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp)
|
||||||
|
|
||||||
if (activation->isWasmInterrupted()) {
|
if (activation->isWasmInterrupted()) {
|
||||||
code_ = &fp_->tls->instance->code();
|
code_ = &fp_->tls->instance->code();
|
||||||
MOZ_ASSERT(code_ == LookupCode(activation->wasmInterruptUnwindPC()));
|
MOZ_ASSERT(code_ == LookupCode(activation->wasmUnwindPC()));
|
||||||
|
|
||||||
codeRange_ = code_->lookupRange(activation->wasmInterruptUnwindPC());
|
codeRange_ = code_->lookupRange(activation->wasmUnwindPC());
|
||||||
MOZ_ASSERT(codeRange_->kind() == CodeRange::Function);
|
MOZ_ASSERT(codeRange_->kind() == CodeRange::Function);
|
||||||
|
|
||||||
lineOrBytecode_ = codeRange_->funcLineOrBytecode();
|
lineOrBytecode_ = codeRange_->funcLineOrBytecode();
|
||||||
|
@ -115,8 +98,6 @@ WasmFrameIter::operator++()
|
||||||
if (unwind_ == Unwind::True) {
|
if (unwind_ == Unwind::True) {
|
||||||
if (activation_->isWasmInterrupted())
|
if (activation_->isWasmInterrupted())
|
||||||
activation_->finishWasmInterrupt();
|
activation_->finishWasmInterrupt();
|
||||||
else if (activation_->isWasmTrapping())
|
|
||||||
activation_->finishWasmTrap();
|
|
||||||
activation_->setWasmExitFP(fp_);
|
activation_->setWasmExitFP(fp_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,7 +624,6 @@ ProfilingFrameIterator::initFromExitFP(const Frame* fp)
|
||||||
case CodeRange::ImportJitExit:
|
case CodeRange::ImportJitExit:
|
||||||
case CodeRange::ImportInterpExit:
|
case CodeRange::ImportInterpExit:
|
||||||
case CodeRange::BuiltinThunk:
|
case CodeRange::BuiltinThunk:
|
||||||
case CodeRange::TrapExit:
|
|
||||||
case CodeRange::OldTrapExit:
|
case CodeRange::OldTrapExit:
|
||||||
case CodeRange::DebugTrap:
|
case CodeRange::DebugTrap:
|
||||||
case CodeRange::OutOfBoundsExit:
|
case CodeRange::OutOfBoundsExit:
|
||||||
|
@ -814,7 +794,6 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CodeRange::TrapExit:
|
|
||||||
case CodeRange::OutOfBoundsExit:
|
case CodeRange::OutOfBoundsExit:
|
||||||
case CodeRange::UnalignedExit:
|
case CodeRange::UnalignedExit:
|
||||||
// These code stubs execute after the prologue/epilogue have completed
|
// These code stubs execute after the prologue/epilogue have completed
|
||||||
|
@ -923,7 +902,6 @@ ProfilingFrameIterator::operator++()
|
||||||
case CodeRange::ImportJitExit:
|
case CodeRange::ImportJitExit:
|
||||||
case CodeRange::ImportInterpExit:
|
case CodeRange::ImportInterpExit:
|
||||||
case CodeRange::BuiltinThunk:
|
case CodeRange::BuiltinThunk:
|
||||||
case CodeRange::TrapExit:
|
|
||||||
case CodeRange::OldTrapExit:
|
case CodeRange::OldTrapExit:
|
||||||
case CodeRange::DebugTrap:
|
case CodeRange::DebugTrap:
|
||||||
case CodeRange::OutOfBoundsExit:
|
case CodeRange::OutOfBoundsExit:
|
||||||
|
@ -952,7 +930,6 @@ ThunkedNativeToDescription(SymbolicAddress func)
|
||||||
case SymbolicAddress::HandleExecutionInterrupt:
|
case SymbolicAddress::HandleExecutionInterrupt:
|
||||||
case SymbolicAddress::HandleDebugTrap:
|
case SymbolicAddress::HandleDebugTrap:
|
||||||
case SymbolicAddress::HandleThrow:
|
case SymbolicAddress::HandleThrow:
|
||||||
case SymbolicAddress::ReportTrap:
|
|
||||||
case SymbolicAddress::OldReportTrap:
|
case SymbolicAddress::OldReportTrap:
|
||||||
case SymbolicAddress::ReportOutOfBounds:
|
case SymbolicAddress::ReportOutOfBounds:
|
||||||
case SymbolicAddress::ReportUnalignedAccess:
|
case SymbolicAddress::ReportUnalignedAccess:
|
||||||
|
@ -1083,7 +1060,6 @@ ProfilingFrameIterator::label() const
|
||||||
case CodeRange::ImportJitExit: return importJitDescription;
|
case CodeRange::ImportJitExit: return importJitDescription;
|
||||||
case CodeRange::BuiltinThunk: return builtinNativeDescription;
|
case CodeRange::BuiltinThunk: return builtinNativeDescription;
|
||||||
case CodeRange::ImportInterpExit: return importInterpDescription;
|
case CodeRange::ImportInterpExit: return importInterpDescription;
|
||||||
case CodeRange::TrapExit: return trapDescription;
|
|
||||||
case CodeRange::OldTrapExit: return trapDescription;
|
case CodeRange::OldTrapExit: return trapDescription;
|
||||||
case CodeRange::DebugTrap: return debugTrapDescription;
|
case CodeRange::DebugTrap: return debugTrapDescription;
|
||||||
case CodeRange::OutOfBoundsExit: return "out-of-bounds stub (in wasm)";
|
case CodeRange::OutOfBoundsExit: return "out-of-bounds stub (in wasm)";
|
||||||
|
|
|
@ -47,7 +47,6 @@ CompiledCode::swap(MacroAssembler& masm)
|
||||||
|
|
||||||
callSites.swap(masm.callSites());
|
callSites.swap(masm.callSites());
|
||||||
callSiteTargets.swap(masm.callSiteTargets());
|
callSiteTargets.swap(masm.callSiteTargets());
|
||||||
trapSites.swap(masm.trapSites());
|
|
||||||
oldTrapSites.swap(masm.oldTrapSites());
|
oldTrapSites.swap(masm.oldTrapSites());
|
||||||
callFarJumps.swap(masm.callFarJumps());
|
callFarJumps.swap(masm.callFarJumps());
|
||||||
oldTrapFarJumps.swap(masm.oldTrapFarJumps());
|
oldTrapFarJumps.swap(masm.oldTrapFarJumps());
|
||||||
|
@ -381,7 +380,7 @@ InRange(uint32_t caller, uint32_t callee)
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> OffsetMap;
|
typedef HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> OffsetMap;
|
||||||
typedef EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>> TrapMaybeOffsetArray;
|
typedef EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>> TrapOffsetArray;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ModuleGenerator::linkCallSites()
|
ModuleGenerator::linkCallSites()
|
||||||
|
@ -400,7 +399,7 @@ ModuleGenerator::linkCallSites()
|
||||||
if (!existingCallFarJumps.init())
|
if (!existingCallFarJumps.init())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
TrapMaybeOffsetArray existingTrapFarJumps;
|
TrapOffsetArray existingTrapFarJumps;
|
||||||
|
|
||||||
for (; lastPatchedCallSite_ < metadataTier_->callSites.length(); lastPatchedCallSite_++) {
|
for (; lastPatchedCallSite_ < metadataTier_->callSites.length(); lastPatchedCallSite_++) {
|
||||||
const CallSite& callSite = metadataTier_->callSites[lastPatchedCallSite_];
|
const CallSite& callSite = metadataTier_->callSites[lastPatchedCallSite_];
|
||||||
|
@ -524,10 +523,6 @@ ModuleGenerator::noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRan
|
||||||
MOZ_ASSERT(!linkDataTier_->interruptOffset);
|
MOZ_ASSERT(!linkDataTier_->interruptOffset);
|
||||||
linkDataTier_->interruptOffset = codeRange.begin();
|
linkDataTier_->interruptOffset = codeRange.begin();
|
||||||
break;
|
break;
|
||||||
case CodeRange::TrapExit:
|
|
||||||
MOZ_ASSERT(!linkDataTier_->trapOffset);
|
|
||||||
linkDataTier_->trapOffset = codeRange.begin();
|
|
||||||
break;
|
|
||||||
case CodeRange::Throw:
|
case CodeRange::Throw:
|
||||||
// Jumped to by other stubs, so nothing to do.
|
// Jumped to by other stubs, so nothing to do.
|
||||||
break;
|
break;
|
||||||
|
@ -585,12 +580,6 @@ ModuleGenerator::linkCompiledCode(const CompiledCode& code)
|
||||||
if (!callSiteTargets_.appendAll(code.callSiteTargets))
|
if (!callSiteTargets_.appendAll(code.callSiteTargets))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
|
|
||||||
auto trapSiteOp = [=](uint32_t, TrapSite* ts) { ts->offsetBy(offsetInModule); };
|
|
||||||
if (!AppendForEach(&metadataTier_->trapSites[trap], code.trapSites[trap], trapSiteOp))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(code.oldTrapSites.empty());
|
MOZ_ASSERT(code.oldTrapSites.empty());
|
||||||
|
|
||||||
auto trapFarJumpOp = [=](uint32_t, OldTrapFarJump* tfj) { tfj->offsetBy(offsetInModule); };
|
auto trapFarJumpOp = [=](uint32_t, OldTrapFarJump* tfj) { tfj->offsetBy(offsetInModule); };
|
||||||
|
@ -809,7 +798,6 @@ ModuleGenerator::finishCode()
|
||||||
|
|
||||||
MOZ_ASSERT(masm_.callSites().empty());
|
MOZ_ASSERT(masm_.callSites().empty());
|
||||||
MOZ_ASSERT(masm_.callSiteTargets().empty());
|
MOZ_ASSERT(masm_.callSiteTargets().empty());
|
||||||
MOZ_ASSERT(masm_.trapSites().empty());
|
|
||||||
MOZ_ASSERT(masm_.oldTrapSites().empty());
|
MOZ_ASSERT(masm_.oldTrapSites().empty());
|
||||||
MOZ_ASSERT(masm_.oldTrapFarJumps().empty());
|
MOZ_ASSERT(masm_.oldTrapFarJumps().empty());
|
||||||
MOZ_ASSERT(masm_.callFarJumps().empty());
|
MOZ_ASSERT(masm_.callFarJumps().empty());
|
||||||
|
@ -824,32 +812,19 @@ ModuleGenerator::finishCode()
|
||||||
bool
|
bool
|
||||||
ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
|
ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
|
||||||
{
|
{
|
||||||
// Assert all sorted metadata is sorted.
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
uint32_t last = 0;
|
// Assert CodeRanges are sorted.
|
||||||
|
uint32_t lastEnd = 0;
|
||||||
for (const CodeRange& codeRange : metadataTier_->codeRanges) {
|
for (const CodeRange& codeRange : metadataTier_->codeRanges) {
|
||||||
MOZ_ASSERT(codeRange.begin() >= last);
|
MOZ_ASSERT(codeRange.begin() >= lastEnd);
|
||||||
last = codeRange.end();
|
lastEnd = codeRange.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
last = 0;
|
// Assert debugTrapFarJumpOffsets are sorted.
|
||||||
for (const CallSite& callSite : metadataTier_->callSites) {
|
uint32_t lastOffset = 0;
|
||||||
MOZ_ASSERT(callSite.returnAddressOffset() >= last);
|
|
||||||
last = callSite.returnAddressOffset();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
|
|
||||||
last = 0;
|
|
||||||
for (const TrapSite& trapSite : metadataTier_->trapSites[trap]) {
|
|
||||||
MOZ_ASSERT(trapSite.pcOffset >= last);
|
|
||||||
last = trapSite.pcOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
last = 0;
|
|
||||||
for (uint32_t debugTrapFarJumpOffset : metadataTier_->debugTrapFarJumpOffsets) {
|
for (uint32_t debugTrapFarJumpOffset : metadataTier_->debugTrapFarJumpOffsets) {
|
||||||
MOZ_ASSERT(debugTrapFarJumpOffset >= last);
|
MOZ_ASSERT(debugTrapFarJumpOffset >= lastOffset);
|
||||||
last = debugTrapFarJumpOffset;
|
lastOffset = debugTrapFarJumpOffset;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -874,7 +849,7 @@ ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
|
||||||
|
|
||||||
metadataTier_->memoryAccesses.podResizeToFit();
|
metadataTier_->memoryAccesses.podResizeToFit();
|
||||||
metadataTier_->codeRanges.podResizeToFit();
|
metadataTier_->codeRanges.podResizeToFit();
|
||||||
metadataTier_->trapSites.podResizeToFit();
|
metadataTier_->callSites.podResizeToFit();
|
||||||
metadataTier_->debugTrapFarJumpOffsets.podResizeToFit();
|
metadataTier_->debugTrapFarJumpOffsets.podResizeToFit();
|
||||||
metadataTier_->debugFuncToCodeRange.podResizeToFit();
|
metadataTier_->debugFuncToCodeRange.podResizeToFit();
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,6 @@ struct CompiledCode
|
||||||
CodeRangeVector codeRanges;
|
CodeRangeVector codeRanges;
|
||||||
CallSiteVector callSites;
|
CallSiteVector callSites;
|
||||||
CallSiteTargetVector callSiteTargets;
|
CallSiteTargetVector callSiteTargets;
|
||||||
TrapSiteVectorArray trapSites;
|
|
||||||
OldTrapSiteVector oldTrapSites;
|
OldTrapSiteVector oldTrapSites;
|
||||||
OldTrapFarJumpVector oldTrapFarJumps;
|
OldTrapFarJumpVector oldTrapFarJumps;
|
||||||
CallFarJumpVector callFarJumps;
|
CallFarJumpVector callFarJumps;
|
||||||
|
@ -79,7 +78,6 @@ struct CompiledCode
|
||||||
codeRanges.clear();
|
codeRanges.clear();
|
||||||
callSites.clear();
|
callSites.clear();
|
||||||
callSiteTargets.clear();
|
callSiteTargets.clear();
|
||||||
trapSites.clear();
|
|
||||||
oldTrapSites.clear();
|
oldTrapSites.clear();
|
||||||
oldTrapFarJumps.clear();
|
oldTrapFarJumps.clear();
|
||||||
callFarJumps.clear();
|
callFarJumps.clear();
|
||||||
|
@ -94,7 +92,6 @@ struct CompiledCode
|
||||||
codeRanges.empty() &&
|
codeRanges.empty() &&
|
||||||
callSites.empty() &&
|
callSites.empty() &&
|
||||||
callSiteTargets.empty() &&
|
callSiteTargets.empty() &&
|
||||||
trapSites.empty() &&
|
|
||||||
oldTrapSites.empty() &&
|
oldTrapSites.empty() &&
|
||||||
oldTrapFarJumps.empty() &&
|
oldTrapFarJumps.empty() &&
|
||||||
callFarJumps.empty() &&
|
callFarJumps.empty() &&
|
||||||
|
|
|
@ -45,7 +45,6 @@ struct LinkDataTierCacheablePod
|
||||||
uint32_t interruptOffset;
|
uint32_t interruptOffset;
|
||||||
uint32_t outOfBoundsOffset;
|
uint32_t outOfBoundsOffset;
|
||||||
uint32_t unalignedAccessOffset;
|
uint32_t unalignedAccessOffset;
|
||||||
uint32_t trapOffset;
|
|
||||||
|
|
||||||
LinkDataTierCacheablePod() { mozilla::PodZero(this); }
|
LinkDataTierCacheablePod() { mozilla::PodZero(this); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -975,15 +975,15 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||||
EXCEPTION_RECORD* record = exception->ExceptionRecord;
|
EXCEPTION_RECORD* record = exception->ExceptionRecord;
|
||||||
CONTEXT* context = exception->ContextRecord;
|
CONTEXT* context = exception->ContextRecord;
|
||||||
|
|
||||||
if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
|
if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
|
||||||
record->ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t** ppc = ContextToPC(context);
|
uint8_t** ppc = ContextToPC(context);
|
||||||
uint8_t* pc = *ppc;
|
uint8_t* pc = *ppc;
|
||||||
|
|
||||||
|
if (record->NumberParameters < 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
const CodeSegment* codeSegment = LookupCodeSegment(pc);
|
const CodeSegment* codeSegment = LookupCodeSegment(pc);
|
||||||
if (!codeSegment)
|
if (!codeSegment)
|
||||||
return false;
|
return false;
|
||||||
|
@ -994,7 +994,7 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||||
const Instance* instance = LookupFaultingInstance(*codeSegment, pc, ContextToFP(context));
|
const Instance* instance = LookupFaultingInstance(*codeSegment, pc, ContextToFP(context));
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
// On Windows, it is possible for InterruptRunningJitCode to execute
|
// On Windows, it is possible for InterruptRunningJitCode to execute
|
||||||
// between a faulting instruction and the handling of the fault due
|
// between a faulting heap access and the handling of the fault due
|
||||||
// to InterruptRunningJitCode's use of SuspendThread. When this happens,
|
// to InterruptRunningJitCode's use of SuspendThread. When this happens,
|
||||||
// after ResumeThread, the exception handler is called with pc equal to
|
// after ResumeThread, the exception handler is called with pc equal to
|
||||||
// CodeSegment.interrupt, which is logically wrong. The Right Thing would
|
// CodeSegment.interrupt, which is logically wrong. The Right Thing would
|
||||||
|
@ -1004,36 +1004,9 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||||
// retrigger after the interrupt jumps back to resumePC).
|
// retrigger after the interrupt jumps back to resumePC).
|
||||||
return activation->isWasmInterrupted() &&
|
return activation->isWasmInterrupted() &&
|
||||||
pc == codeSegment->interruptCode() &&
|
pc == codeSegment->interruptCode() &&
|
||||||
codeSegment->containsCodePC(activation->wasmInterruptResumePC());
|
codeSegment->containsCodePC(activation->wasmResumePC());
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the same race-with-interrupt situation above, it's *also* possible
|
|
||||||
// that the reported 'pc' is the pre-interrupt pc, not post-interrupt
|
|
||||||
// codeSegment->interruptCode (this may be windows-version-specific). In
|
|
||||||
// this case, lookupTrap()/lookupMemoryAccess() will all succeed causing the
|
|
||||||
// pc to be redirected *again* (to a trap stub), leading to the interrupt
|
|
||||||
// stub never being called. Since the goal of the async interrupt is to break
|
|
||||||
// out iloops and trapping does just that, this is fine, we just clear the
|
|
||||||
// "interrupted" state.
|
|
||||||
if (activation->isWasmInterrupted()) {
|
|
||||||
MOZ_ASSERT(activation->wasmInterruptResumePC() == pc);
|
|
||||||
activation->finishWasmInterrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (record->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) {
|
|
||||||
Trap trap;
|
|
||||||
BytecodeOffset bytecode;
|
|
||||||
if (!codeSegment->code().lookupTrap(pc, &trap, &bytecode))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
activation->startWasmTrap(trap, bytecode.offset, pc, ContextToFP(context));
|
|
||||||
*ppc = codeSegment->trapCode();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (record->NumberParameters < 2)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
|
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
|
||||||
|
|
||||||
// This check isn't necessary, but, since we can, check anyway to make
|
// This check isn't necessary, but, since we can, check anyway to make
|
||||||
|
@ -1043,6 +1016,18 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||||
|
|
||||||
MOZ_ASSERT(activation->compartment() == instance->compartment());
|
MOZ_ASSERT(activation->compartment() == instance->compartment());
|
||||||
|
|
||||||
|
// Similar to the non-atomic situation above, on Windows, an OOB fault at a
|
||||||
|
// PC can trigger *after* an async interrupt observed that PC and attempted
|
||||||
|
// to redirect to the async stub. In this unique case, isWasmInterrupted() is
|
||||||
|
// already true when the OOB handler is called. Since the point of the async
|
||||||
|
// interrupt is to get out of an iloop and the OOB trap will do just that,
|
||||||
|
// we can simply clear the interrupt. (The update to CONTEXT.pc made by
|
||||||
|
// HandleMemoryAccess will clobber the interrupt's previous update.)
|
||||||
|
if (activation->isWasmInterrupted()) {
|
||||||
|
MOZ_ASSERT(activation->wasmResumePC() == pc);
|
||||||
|
activation->finishWasmInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
HandleMemoryAccess(context, pc, faultingAddress, codeSegment, *instance, activation, ppc);
|
HandleMemoryAccess(context, pc, faultingAddress, codeSegment, *instance, activation, ppc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1128,11 +1113,8 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request)
|
||||||
uint8_t** ppc = ContextToPC(&context);
|
uint8_t** ppc = ContextToPC(&context);
|
||||||
uint8_t* pc = *ppc;
|
uint8_t* pc = *ppc;
|
||||||
|
|
||||||
if (request.body.exception != EXC_BAD_ACCESS &&
|
if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
|
||||||
request.body.exception != EXC_BAD_INSTRUCTION)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// The faulting thread is suspended so we can access cx fields that can
|
// The faulting thread is suspended so we can access cx fields that can
|
||||||
// normally only be accessed by the cx's active thread.
|
// normally only be accessed by the cx's active thread.
|
||||||
|
@ -1146,31 +1128,17 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request)
|
||||||
if (!instance)
|
if (!instance)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
|
||||||
|
|
||||||
|
// This check isn't necessary, but, since we can, check anyway to make
|
||||||
|
// sure we aren't covering up a real bug.
|
||||||
|
if (!IsHeapAccessAddress(*instance, faultingAddress))
|
||||||
|
return false;
|
||||||
|
|
||||||
JitActivation* activation = cx->activation()->asJit();
|
JitActivation* activation = cx->activation()->asJit();
|
||||||
MOZ_ASSERT(activation->compartment() == instance->compartment());
|
MOZ_ASSERT(activation->compartment() == instance->compartment());
|
||||||
|
|
||||||
if (request.body.exception == EXC_BAD_INSTRUCTION) {
|
HandleMemoryAccess(&context, pc, faultingAddress, codeSegment, *instance, activation, ppc);
|
||||||
Trap trap;
|
|
||||||
BytecodeOffset bytecode;
|
|
||||||
if (!codeSegment->code().lookupTrap(pc, &trap, &bytecode))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
activation->startWasmTrap(trap, bytecode.offset, pc, ContextToFP(&context));
|
|
||||||
*ppc = codeSegment->trapCode();
|
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(request.body.exception == EXC_BAD_ACCESS);
|
|
||||||
if (request.body.codeCnt != 2)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
|
|
||||||
|
|
||||||
// This check isn't necessary, but, since we can, check anyway to make
|
|
||||||
// sure we aren't covering up a real bug.
|
|
||||||
if (!IsHeapAccessAddress(*instance, faultingAddress))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
HandleMemoryAccess(&context, pc, faultingAddress, codeSegment, *instance, activation, ppc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the thread state with the new pc and register values.
|
// Update the thread state with the new pc and register values.
|
||||||
kret = thread_set_state(cxThread, float_state, (thread_state_t)&context.float_, float_state_count);
|
kret = thread_set_state(cxThread, float_state, (thread_state_t)&context.float_, float_state_count);
|
||||||
|
@ -1343,7 +1311,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
||||||
return false;
|
return false;
|
||||||
AutoSignalHandler ash;
|
AutoSignalHandler ash;
|
||||||
|
|
||||||
MOZ_RELEASE_ASSERT(signum == SIGSEGV || signum == SIGBUS || signum == SIGILL);
|
MOZ_RELEASE_ASSERT(signum == SIGSEGV || signum == SIGBUS);
|
||||||
|
|
||||||
CONTEXT* context = (CONTEXT*)ctx;
|
CONTEXT* context = (CONTEXT*)ctx;
|
||||||
uint8_t** ppc = ContextToPC(context);
|
uint8_t** ppc = ContextToPC(context);
|
||||||
|
@ -1357,20 +1325,6 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
||||||
if (!instance)
|
if (!instance)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JitActivation* activation = TlsContext.get()->activation()->asJit();
|
|
||||||
MOZ_ASSERT(activation->compartment() == instance->compartment());
|
|
||||||
|
|
||||||
if (signum == SIGILL) {
|
|
||||||
Trap trap;
|
|
||||||
BytecodeOffset bytecode;
|
|
||||||
if (!segment->code().lookupTrap(pc, &trap, &bytecode))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
activation->startWasmTrap(trap, bytecode.offset, pc, ContextToFP(context));
|
|
||||||
*ppc = segment->trapCode();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
|
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
|
||||||
|
|
||||||
// Although it's not strictly necessary, to make sure we're not covering up
|
// Although it's not strictly necessary, to make sure we're not covering up
|
||||||
|
@ -1392,6 +1346,9 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JitActivation* activation = TlsContext.get()->activation()->asJit();
|
||||||
|
MOZ_ASSERT(activation->compartment() == instance->compartment());
|
||||||
|
|
||||||
#ifdef JS_CODEGEN_ARM
|
#ifdef JS_CODEGEN_ARM
|
||||||
if (signum == SIGBUS) {
|
if (signum == SIGBUS) {
|
||||||
// TODO: We may see a bus error for something that is an unaligned access that
|
// TODO: We may see a bus error for something that is an unaligned access that
|
||||||
|
@ -1410,7 +1367,6 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
||||||
|
|
||||||
static struct sigaction sPrevSEGVHandler;
|
static struct sigaction sPrevSEGVHandler;
|
||||||
static struct sigaction sPrevSIGBUSHandler;
|
static struct sigaction sPrevSIGBUSHandler;
|
||||||
static struct sigaction sPrevSIGILLHandler;
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WasmFaultHandler(int signum, siginfo_t* info, void* context)
|
WasmFaultHandler(int signum, siginfo_t* info, void* context)
|
||||||
|
@ -1418,13 +1374,9 @@ WasmFaultHandler(int signum, siginfo_t* info, void* context)
|
||||||
if (HandleFault(signum, info, context))
|
if (HandleFault(signum, info, context))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
struct sigaction* previousSignal = nullptr;
|
struct sigaction* previousSignal = signum == SIGSEGV
|
||||||
switch (signum) {
|
? &sPrevSEGVHandler
|
||||||
case SIGSEGV: previousSignal = &sPrevSEGVHandler; break;
|
: &sPrevSIGBUSHandler;
|
||||||
case SIGBUS: previousSignal = &sPrevSIGBUSHandler; break;
|
|
||||||
case SIGILL: previousSignal = &sPrevSIGILLHandler; break;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(previousSignal);
|
|
||||||
|
|
||||||
// This signal is not for any asm.js code we expect, so we need to forward
|
// This signal is not for any asm.js code we expect, so we need to forward
|
||||||
// the signal to the next handler. If there is no next handler (SIG_IGN or
|
// the signal to the next handler. If there is no next handler (SIG_IGN or
|
||||||
|
@ -1665,15 +1617,6 @@ ProcessHasSignalHandlers()
|
||||||
if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler))
|
if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler))
|
||||||
MOZ_CRASH("unable to install sigbus handler");
|
MOZ_CRASH("unable to install sigbus handler");
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
// Install a SIGILL handler to handle the ud2 instructions that are emitted
|
|
||||||
// to implement wasm traps.
|
|
||||||
struct sigaction illegalHandler;
|
|
||||||
illegalHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
|
|
||||||
illegalHandler.sa_sigaction = WasmFaultHandler;
|
|
||||||
sigemptyset(&illegalHandler.sa_mask);
|
|
||||||
if (sigaction(SIGILL, &illegalHandler, &sPrevSIGILLHandler))
|
|
||||||
MOZ_CRASH("unable to install segv handler");
|
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
sHaveSignalHandlers = true;
|
sHaveSignalHandlers = true;
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
# include <mach/mach.h>
|
# include <mach/mach.h>
|
||||||
#endif
|
#endif
|
||||||
#include "threading/Thread.h"
|
#include "threading/Thread.h"
|
||||||
#include "wasm/WasmTypes.h"
|
|
||||||
|
|
||||||
struct JSContext;
|
struct JSContext;
|
||||||
struct JSRuntime;
|
struct JSRuntime;
|
||||||
|
@ -80,35 +79,6 @@ class MachExceptionHandler
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Typed wrappers encapsulating the data saved by the signal handler on async
|
|
||||||
// interrupt or trap. On interrupt, the PC at which to resume is saved. On trap,
|
|
||||||
// the bytecode offset to be reported in callstacks is saved.
|
|
||||||
|
|
||||||
struct InterruptData
|
|
||||||
{
|
|
||||||
// The pc to use for unwinding purposes which is kept consistent with fp at
|
|
||||||
// call boundaries.
|
|
||||||
void* unwindPC;
|
|
||||||
|
|
||||||
// The pc at which we should return if the interrupt doesn't stop execution.
|
|
||||||
void* resumePC;
|
|
||||||
|
|
||||||
InterruptData(void* unwindPC, void* resumePC)
|
|
||||||
: unwindPC(unwindPC), resumePC(resumePC)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TrapData
|
|
||||||
{
|
|
||||||
void* pc;
|
|
||||||
Trap trap;
|
|
||||||
uint32_t bytecodeOffset;
|
|
||||||
|
|
||||||
TrapData(void* pc, Trap trap, uint32_t bytecodeOffset)
|
|
||||||
: pc(pc), trap(trap), bytecodeOffset(bytecodeOffset)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace wasm
|
} // namespace wasm
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
|
|
@ -1001,29 +1001,6 @@ wasm::GenerateBuiltinThunk(MacroAssembler& masm, ABIFunctionType abiType, ExitRe
|
||||||
return FinishOffsets(masm, offsets);
|
return FinishOffsets(masm, offsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a stub which calls WasmReportTrap() and can be executed by having
|
|
||||||
// the signal handler redirect PC from any trapping instruction.
|
|
||||||
static bool
|
|
||||||
GenerateTrapExit(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
|
|
||||||
{
|
|
||||||
masm.haltingAlign(CodeAlignment);
|
|
||||||
|
|
||||||
offsets->begin = masm.currentOffset();
|
|
||||||
|
|
||||||
// We know that StackPointer is word-aligned, but not necessarily
|
|
||||||
// stack-aligned, so we need to align it dynamically.
|
|
||||||
masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
|
|
||||||
if (ShadowStackSpace)
|
|
||||||
masm.subFromStackPtr(Imm32(ShadowStackSpace));
|
|
||||||
|
|
||||||
masm.assertStackAlignment(ABIStackAlignment);
|
|
||||||
masm.call(SymbolicAddress::ReportTrap);
|
|
||||||
|
|
||||||
masm.jump(throwLabel);
|
|
||||||
|
|
||||||
return FinishOffsets(masm, offsets);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a stub that calls into WasmOldReportTrap with the right trap reason.
|
// Generate a stub that calls into WasmOldReportTrap with the right trap reason.
|
||||||
// This stub is called with ABIStackAlignment by a trap out-of-line path. An
|
// This stub is called with ABIStackAlignment by a trap out-of-line path. An
|
||||||
// exit prologue/epilogue is used so that stack unwinding picks up the
|
// exit prologue/epilogue is used so that stack unwinding picks up the
|
||||||
|
@ -1391,30 +1368,11 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
|
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
|
||||||
switch (trap) {
|
CallableOffsets offsets;
|
||||||
case Trap::Unreachable:
|
if (!GenerateOldTrapExit(masm, trap, &throwLabel, &offsets))
|
||||||
break;
|
return false;
|
||||||
// The TODO list of "old" traps to convert to new traps:
|
if (!code->codeRanges.emplaceBack(trap, offsets))
|
||||||
case Trap::IntegerOverflow:
|
return false;
|
||||||
case Trap::InvalidConversionToInteger:
|
|
||||||
case Trap::IntegerDivideByZero:
|
|
||||||
case Trap::OutOfBounds:
|
|
||||||
case Trap::UnalignedAccess:
|
|
||||||
case Trap::IndirectCallToNull:
|
|
||||||
case Trap::IndirectCallBadSig:
|
|
||||||
case Trap::ImpreciseSimdConversion:
|
|
||||||
case Trap::StackOverflow:
|
|
||||||
case Trap::ThrowReported: {
|
|
||||||
CallableOffsets offsets;
|
|
||||||
if (!GenerateOldTrapExit(masm, trap, &throwLabel, &offsets))
|
|
||||||
return false;
|
|
||||||
if (!code->codeRanges.emplaceBack(trap, offsets))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Trap::Limit:
|
|
||||||
MOZ_CRASH("impossible");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Offsets offsets;
|
Offsets offsets;
|
||||||
|
@ -1429,11 +1387,6 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
|
||||||
if (!code->codeRanges.emplaceBack(CodeRange::UnalignedExit, offsets))
|
if (!code->codeRanges.emplaceBack(CodeRange::UnalignedExit, offsets))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!GenerateTrapExit(masm, &throwLabel, &offsets))
|
|
||||||
return false;
|
|
||||||
if (!code->codeRanges.emplaceBack(CodeRange::TrapExit, offsets))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!GenerateInterruptExit(masm, &throwLabel, &offsets))
|
if (!GenerateInterruptExit(masm, &throwLabel, &offsets))
|
||||||
return false;
|
return false;
|
||||||
if (!code->codeRanges.emplaceBack(CodeRange::Interrupt, offsets))
|
if (!code->codeRanges.emplaceBack(CodeRange::Interrupt, offsets))
|
||||||
|
|
|
@ -30,7 +30,6 @@ using namespace js::jit;
|
||||||
using namespace js::wasm;
|
using namespace js::wasm;
|
||||||
|
|
||||||
using mozilla::IsPowerOfTwo;
|
using mozilla::IsPowerOfTwo;
|
||||||
using mozilla::MakeEnumeratedRange;
|
|
||||||
|
|
||||||
// A sanity check. We have only tested WASM_HUGE_MEMORY on x64, and only tested
|
// A sanity check. We have only tested WASM_HUGE_MEMORY on x64, and only tested
|
||||||
// x64 with WASM_HUGE_MEMORY.
|
// x64 with WASM_HUGE_MEMORY.
|
||||||
|
@ -691,75 +690,6 @@ DebugFrame::leave(JSContext* cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
TrapSiteVectorArray::empty() const
|
|
||||||
{
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
|
|
||||||
if (!(*this)[trap].empty())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TrapSiteVectorArray::clear()
|
|
||||||
{
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
|
|
||||||
(*this)[trap].clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TrapSiteVectorArray::swap(TrapSiteVectorArray& rhs)
|
|
||||||
{
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
|
|
||||||
(*this)[trap].swap(rhs[trap]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TrapSiteVectorArray::podResizeToFit()
|
|
||||||
{
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
|
|
||||||
(*this)[trap].podResizeToFit();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
TrapSiteVectorArray::serializedSize() const
|
|
||||||
{
|
|
||||||
size_t ret = 0;
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
|
|
||||||
ret += SerializedPodVectorSize((*this)[trap]);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t*
|
|
||||||
TrapSiteVectorArray::serialize(uint8_t* cursor) const
|
|
||||||
{
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
|
|
||||||
cursor = SerializePodVector(cursor, (*this)[trap]);
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t*
|
|
||||||
TrapSiteVectorArray::deserialize(const uint8_t* cursor)
|
|
||||||
{
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
|
|
||||||
cursor = DeserializePodVector(cursor, &(*this)[trap]);
|
|
||||||
if (!cursor)
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
TrapSiteVectorArray::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
|
||||||
{
|
|
||||||
size_t ret = 0;
|
|
||||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
|
|
||||||
ret += (*this)[trap].sizeOfExcludingThis(mallocSizeOf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeRange::CodeRange(Kind kind, Offsets offsets)
|
CodeRange::CodeRange(Kind kind, Offsets offsets)
|
||||||
: begin_(offsets.begin),
|
: begin_(offsets.begin),
|
||||||
ret_(0),
|
ret_(0),
|
||||||
|
@ -773,7 +703,6 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
|
||||||
case FarJumpIsland:
|
case FarJumpIsland:
|
||||||
case OutOfBoundsExit:
|
case OutOfBoundsExit:
|
||||||
case UnalignedExit:
|
case UnalignedExit:
|
||||||
case TrapExit:
|
|
||||||
case Throw:
|
case Throw:
|
||||||
case Interrupt:
|
case Interrupt:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -935,52 +935,6 @@ enum class Trap
|
||||||
Limit
|
Limit
|
||||||
};
|
};
|
||||||
|
|
||||||
// A wrapper around the bytecode offset of a wasm instruction within a whole
|
|
||||||
// module, used for trap offsets or call offsets. These offsets should refer to
|
|
||||||
// the first byte of the instruction that triggered the trap / did the call and
|
|
||||||
// should ultimately derive from OpIter::bytecodeOffset.
|
|
||||||
|
|
||||||
struct BytecodeOffset
|
|
||||||
{
|
|
||||||
static const uint32_t INVALID = -1;
|
|
||||||
uint32_t offset;
|
|
||||||
|
|
||||||
BytecodeOffset() : offset(INVALID) {}
|
|
||||||
explicit BytecodeOffset(uint32_t offset) : offset(offset) {}
|
|
||||||
|
|
||||||
bool isValid() const { return offset != INVALID; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// A TrapSite (in the TrapSiteVector for a given Trap code) represents a wasm
|
|
||||||
// instruction at a given bytecode offset that can fault at the given pc offset.
|
|
||||||
// When such a fault occurs, a signal/exception handler looks up the TrapSite to
|
|
||||||
// confirm the fault is intended/safe and redirects pc to the trap stub.
|
|
||||||
|
|
||||||
struct TrapSite
|
|
||||||
{
|
|
||||||
uint32_t pcOffset;
|
|
||||||
BytecodeOffset bytecode;
|
|
||||||
|
|
||||||
TrapSite() : pcOffset(-1), bytecode() {}
|
|
||||||
TrapSite(uint32_t pcOffset, BytecodeOffset bytecode) : pcOffset(pcOffset), bytecode(bytecode) {}
|
|
||||||
|
|
||||||
void offsetBy(uint32_t offset) {
|
|
||||||
pcOffset += offset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WASM_DECLARE_POD_VECTOR(TrapSite, TrapSiteVector)
|
|
||||||
|
|
||||||
struct TrapSiteVectorArray : EnumeratedArray<Trap, Trap::Limit, TrapSiteVector>
|
|
||||||
{
|
|
||||||
bool empty() const;
|
|
||||||
void clear();
|
|
||||||
void swap(TrapSiteVectorArray& rhs);
|
|
||||||
void podResizeToFit();
|
|
||||||
|
|
||||||
WASM_DECLARE_SERIALIZABLE(TrapSiteVectorArray)
|
|
||||||
};
|
|
||||||
|
|
||||||
// The (,Callable,Func)Offsets classes are used to record the offsets of
|
// The (,Callable,Func)Offsets classes are used to record the offsets of
|
||||||
// different key points in a CodeRange during compilation.
|
// different key points in a CodeRange during compilation.
|
||||||
|
|
||||||
|
@ -1057,7 +1011,6 @@ class CodeRange
|
||||||
ImportJitExit, // fast-path calling from wasm into JIT code
|
ImportJitExit, // fast-path calling from wasm into JIT code
|
||||||
ImportInterpExit, // slow-path calling from wasm into C++ interp
|
ImportInterpExit, // slow-path calling from wasm into C++ interp
|
||||||
BuiltinThunk, // fast-path calling from wasm into a C++ native
|
BuiltinThunk, // fast-path calling from wasm into a C++ native
|
||||||
TrapExit, // calls C++ to report and jumps to throw stub
|
|
||||||
OldTrapExit, // calls C++ to report and jumps to throw stub
|
OldTrapExit, // calls C++ to report and jumps to throw stub
|
||||||
DebugTrap, // calls C++ to handle debug event
|
DebugTrap, // calls C++ to handle debug event
|
||||||
FarJumpIsland, // inserted to connect otherwise out-of-range insns
|
FarJumpIsland, // inserted to connect otherwise out-of-range insns
|
||||||
|
@ -1134,7 +1087,7 @@ class CodeRange
|
||||||
return kind() == ImportJitExit;
|
return kind() == ImportJitExit;
|
||||||
}
|
}
|
||||||
bool isTrapExit() const {
|
bool isTrapExit() const {
|
||||||
return kind() == OldTrapExit || kind() == TrapExit;
|
return kind() == OldTrapExit;
|
||||||
}
|
}
|
||||||
bool isDebugTrap() const {
|
bool isDebugTrap() const {
|
||||||
return kind() == DebugTrap;
|
return kind() == DebugTrap;
|
||||||
|
@ -1148,7 +1101,7 @@ class CodeRange
|
||||||
// the return instruction to calculate the frame pointer.
|
// the return instruction to calculate the frame pointer.
|
||||||
|
|
||||||
bool hasReturn() const {
|
bool hasReturn() const {
|
||||||
return isFunction() || isImportExit() || kind() == OldTrapExit || isDebugTrap();
|
return isFunction() || isImportExit() || isTrapExit() || isDebugTrap();
|
||||||
}
|
}
|
||||||
uint32_t ret() const {
|
uint32_t ret() const {
|
||||||
MOZ_ASSERT(hasReturn());
|
MOZ_ASSERT(hasReturn());
|
||||||
|
@ -1226,6 +1179,22 @@ WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
|
||||||
extern const CodeRange*
|
extern const CodeRange*
|
||||||
LookupInSorted(const CodeRangeVector& codeRanges, CodeRange::OffsetInCode target);
|
LookupInSorted(const CodeRangeVector& codeRanges, CodeRange::OffsetInCode target);
|
||||||
|
|
||||||
|
// A wrapper around the bytecode offset of a wasm instruction within a whole
|
||||||
|
// module, used for trap offsets or call offsets. These offsets should refer to
|
||||||
|
// the first byte of the instruction that triggered the trap / did the call and
|
||||||
|
// should ultimately derive from OpIter::bytecodeOffset.
|
||||||
|
|
||||||
|
struct BytecodeOffset
|
||||||
|
{
|
||||||
|
static const uint32_t INVALID = -1;
|
||||||
|
uint32_t offset;
|
||||||
|
|
||||||
|
BytecodeOffset() : offset(INVALID) {}
|
||||||
|
explicit BytecodeOffset(uint32_t offset) : offset(offset) {}
|
||||||
|
|
||||||
|
bool isValid() const { return offset != INVALID; }
|
||||||
|
};
|
||||||
|
|
||||||
// While the frame-pointer chain allows the stack to be unwound without
|
// While the frame-pointer chain allows the stack to be unwound without
|
||||||
// metadata, Error.stack still needs to know the line/column of every call in
|
// metadata, Error.stack still needs to know the line/column of every call in
|
||||||
// the chain. A CallSiteDesc describes a single callsite to which CallSite adds
|
// the chain. A CallSiteDesc describes a single callsite to which CallSite adds
|
||||||
|
@ -1364,7 +1333,6 @@ enum class SymbolicAddress
|
||||||
HandleExecutionInterrupt,
|
HandleExecutionInterrupt,
|
||||||
HandleDebugTrap,
|
HandleDebugTrap,
|
||||||
HandleThrow,
|
HandleThrow,
|
||||||
ReportTrap,
|
|
||||||
OldReportTrap,
|
OldReportTrap,
|
||||||
ReportOutOfBounds,
|
ReportOutOfBounds,
|
||||||
ReportUnalignedAccess,
|
ReportUnalignedAccess,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче