зеркало из 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))
|
||||
)`,
|
||||
WebAssembly.RuntimeError,
|
||||
["", ">", "1,>", "0,1,>", "1,>", "", ">", ""]);
|
||||
["", ">", "1,>", "0,1,>", "interstitial,0,1,>", "trap handling,0,1,>", "", ">", ""]);
|
||||
|
||||
testError(
|
||||
`(module
|
||||
|
|
|
@ -12350,7 +12350,7 @@ CodeGenerator::visitWasmTrap(LWasmTrap* lir)
|
|||
MOZ_ASSERT(gen->compilingWasm());
|
||||
const MWasmTrap* mir = lir->mir();
|
||||
|
||||
masm.wasmTrap(mir->trap(), mir->bytecodeOffset());
|
||||
masm.jump(oldTrap(mir, mir->trap()));
|
||||
}
|
||||
|
||||
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
|
||||
MacroAssembler::wasmCallImport(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee)
|
||||
{
|
||||
|
|
|
@ -1399,9 +1399,6 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// ========================================================================
|
||||
// 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.
|
||||
// Required when WASM_HUGE_MEMORY is not defined.
|
||||
template <class L>
|
||||
|
|
|
@ -3005,14 +3005,6 @@ Assembler::as_bkpt()
|
|||
hit++;
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
Assembler::as_illegal_trap()
|
||||
{
|
||||
// Encoding of the permanently-undefined 'udf' instruction, with the imm16
|
||||
// set to 0.
|
||||
return writeInst(0xe7f000f0);
|
||||
}
|
||||
|
||||
void
|
||||
Assembler::flushBuffer()
|
||||
{
|
||||
|
|
|
@ -1733,7 +1733,6 @@ class Assembler : public AssemblerShared
|
|||
static void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target);
|
||||
|
||||
void as_bkpt();
|
||||
BufferOffset as_illegal_trap();
|
||||
|
||||
public:
|
||||
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
|
||||
|
|
|
@ -4911,12 +4911,6 @@ template void
|
|||
MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
|
||||
const BaseIndex& dest, MIRType slotType);
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::illegalInstruction()
|
||||
{
|
||||
return CodeOffset(as_illegal_trap().getOffset());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
|
||||
{
|
||||
|
|
|
@ -260,11 +260,6 @@ class SimInstruction {
|
|||
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.
|
||||
inline bool hasS() const { return sValue() == 1; }
|
||||
inline bool hasB() const { return bValue() == 1; }
|
||||
|
@ -1588,16 +1583,6 @@ Simulator::handleWasmInterrupt()
|
|||
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
|
||||
// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
|
||||
// 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
|
||||
// redirecting in the simulator before the real handler would have been hit.
|
||||
bool
|
||||
Simulator::handleWasmSegFault(int32_t addr, unsigned numBytes)
|
||||
Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
|
||||
{
|
||||
JitActivation* act = GetJitActivation(cx_);
|
||||
if (!act)
|
||||
if (!wasm::CodeExists)
|
||||
return false;
|
||||
if (!cx_->activation() || !cx_->activation()->isJit())
|
||||
return false;
|
||||
JitActivation* act = cx_->activation()->asJit();
|
||||
|
||||
void* pc = reinterpret_cast<void*>(get_pc());
|
||||
uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
|
||||
|
@ -1636,34 +1623,10 @@ Simulator::handleWasmSegFault(int32_t addr, unsigned numBytes)
|
|||
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
|
||||
Simulator::readQ(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 8))
|
||||
if (handleWasmFault(addr, 8))
|
||||
return UINT64_MAX;
|
||||
|
||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||
|
@ -1686,7 +1649,7 @@ Simulator::readQ(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
|||
void
|
||||
Simulator::writeQ(int32_t addr, uint64_t value, SimInstruction* instr, UnalignedPolicy f)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 8))
|
||||
if (handleWasmFault(addr, 8))
|
||||
return;
|
||||
|
||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||
|
@ -1709,7 +1672,7 @@ Simulator::writeQ(int32_t addr, uint64_t value, SimInstruction* instr, Unaligned
|
|||
int
|
||||
Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 4))
|
||||
if (handleWasmFault(addr, 4))
|
||||
return -1;
|
||||
|
||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||
|
@ -1735,7 +1698,7 @@ Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
|
|||
void
|
||||
Simulator::writeW(int32_t addr, int value, SimInstruction* instr, UnalignedPolicy f)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 4))
|
||||
if (handleWasmFault(addr, 4))
|
||||
return;
|
||||
|
||||
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
|
||||
|
@ -1780,7 +1743,7 @@ Simulator::readExW(int32_t addr, SimInstruction* instr)
|
|||
if (addr & 3)
|
||||
MOZ_CRASH("Unaligned exclusive read");
|
||||
|
||||
if (handleWasmSegFault(addr, 4))
|
||||
if (handleWasmFault(addr, 4))
|
||||
return -1;
|
||||
|
||||
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)
|
||||
MOZ_CRASH("Unaligned exclusive write");
|
||||
|
||||
if (handleWasmSegFault(addr, 4))
|
||||
if (handleWasmFault(addr, 4))
|
||||
return -1;
|
||||
|
||||
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
|
||||
Simulator::readHU(int32_t addr, SimInstruction* instr)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 2))
|
||||
if (handleWasmFault(addr, 2))
|
||||
return UINT16_MAX;
|
||||
|
||||
// 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
|
||||
Simulator::readH(int32_t addr, SimInstruction* instr)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 2))
|
||||
if (handleWasmFault(addr, 2))
|
||||
return -1;
|
||||
|
||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||
|
@ -1860,7 +1823,7 @@ Simulator::readH(int32_t addr, SimInstruction* instr)
|
|||
void
|
||||
Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 2))
|
||||
if (handleWasmFault(addr, 2))
|
||||
return;
|
||||
|
||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||
|
@ -1883,7 +1846,7 @@ Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr)
|
|||
void
|
||||
Simulator::writeH(int32_t addr, int16_t value, SimInstruction* instr)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 2))
|
||||
if (handleWasmFault(addr, 2))
|
||||
return;
|
||||
|
||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||
|
@ -1909,7 +1872,7 @@ Simulator::readExHU(int32_t addr, SimInstruction* instr)
|
|||
if (addr & 1)
|
||||
MOZ_CRASH("Unaligned exclusive read");
|
||||
|
||||
if (handleWasmSegFault(addr, 2))
|
||||
if (handleWasmFault(addr, 2))
|
||||
return UINT16_MAX;
|
||||
|
||||
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)
|
||||
MOZ_CRASH("Unaligned exclusive write");
|
||||
|
||||
if (handleWasmSegFault(addr, 2))
|
||||
if (handleWasmFault(addr, 2))
|
||||
return -1;
|
||||
|
||||
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
|
||||
Simulator::readBU(int32_t addr)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 1))
|
||||
if (handleWasmFault(addr, 1))
|
||||
return UINT8_MAX;
|
||||
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
|
||||
|
@ -1949,7 +1912,7 @@ Simulator::readBU(int32_t addr)
|
|||
uint8_t
|
||||
Simulator::readExBU(int32_t addr)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 1))
|
||||
if (handleWasmFault(addr, 1))
|
||||
return UINT8_MAX;
|
||||
|
||||
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
|
||||
|
@ -1961,7 +1924,7 @@ Simulator::readExBU(int32_t addr)
|
|||
int32_t
|
||||
Simulator::writeExB(int32_t addr, uint8_t value)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 1))
|
||||
if (handleWasmFault(addr, 1))
|
||||
return -1;
|
||||
|
||||
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
|
||||
Simulator::readB(int32_t addr)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 1))
|
||||
if (handleWasmFault(addr, 1))
|
||||
return -1;
|
||||
|
||||
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
||||
|
@ -1986,7 +1949,7 @@ Simulator::readB(int32_t addr)
|
|||
void
|
||||
Simulator::writeB(int32_t addr, uint8_t value)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 1))
|
||||
if (handleWasmFault(addr, 1))
|
||||
return;
|
||||
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
|
||||
|
@ -1996,7 +1959,7 @@ Simulator::writeB(int32_t addr, uint8_t value)
|
|||
void
|
||||
Simulator::writeB(int32_t addr, int8_t value)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 1))
|
||||
if (handleWasmFault(addr, 1))
|
||||
return;
|
||||
|
||||
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
||||
|
@ -2006,7 +1969,7 @@ Simulator::writeB(int32_t addr, int8_t value)
|
|||
int32_t*
|
||||
Simulator::readDW(int32_t addr)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 8))
|
||||
if (handleWasmFault(addr, 8))
|
||||
return nullptr;
|
||||
|
||||
if ((addr & 3) == 0) {
|
||||
|
@ -2021,7 +1984,7 @@ Simulator::readDW(int32_t addr)
|
|||
void
|
||||
Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2)
|
||||
{
|
||||
if (handleWasmSegFault(addr, 8))
|
||||
if (handleWasmFault(addr, 8))
|
||||
return;
|
||||
|
||||
if ((addr & 3) == 0) {
|
||||
|
@ -2041,7 +2004,7 @@ Simulator::readExDW(int32_t addr, int32_t* hibits)
|
|||
if (addr & 3)
|
||||
MOZ_CRASH("Unaligned exclusive read");
|
||||
|
||||
if (handleWasmSegFault(addr, 8))
|
||||
if (handleWasmFault(addr, 8))
|
||||
return -1;
|
||||
|
||||
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)
|
||||
MOZ_CRASH("Unaligned exclusive write");
|
||||
|
||||
if (handleWasmSegFault(addr, 8))
|
||||
if (handleWasmFault(addr, 8))
|
||||
return -1;
|
||||
|
||||
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
|
||||
Simulator::decodeType3(SimInstruction* instr)
|
||||
{
|
||||
if (MOZ_UNLIKELY(instr->isUDF())) {
|
||||
if (handleWasmIllFault())
|
||||
return;
|
||||
MOZ_CRASH("illegal instruction encountered");
|
||||
}
|
||||
|
||||
int rd = instr->rdValue();
|
||||
int rn = instr->rnValue();
|
||||
int32_t rn_val = get_register(rn);
|
||||
|
|
|
@ -296,8 +296,7 @@ class Simulator
|
|||
void startWasmInterrupt(JitActivation* act);
|
||||
|
||||
// Handle any wasm faults, returning true if the fault was handled.
|
||||
bool handleWasmSegFault(int32_t addr, unsigned numBytes);
|
||||
bool handleWasmIllFault();
|
||||
bool handleWasmFault(int32_t addr, unsigned numBytes);
|
||||
|
||||
// Read and write memory.
|
||||
inline uint8_t readBU(int32_t addr);
|
||||
|
|
|
@ -852,12 +852,6 @@ MacroAssembler::comment(const char* msg)
|
|||
// ========================================================================
|
||||
// wasm support
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::illegalInstruction()
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
|
||||
{
|
||||
|
|
|
@ -1863,14 +1863,6 @@ MacroAssembler::comment(const char* msg)
|
|||
Assembler::comment(msg);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// WebAssembly
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::illegalInstruction()
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry)
|
||||
|
|
|
@ -871,7 +871,6 @@ class AssemblerShared
|
|||
{
|
||||
wasm::CallSiteVector callSites_;
|
||||
wasm::CallSiteTargetVector callSiteTargets_;
|
||||
wasm::TrapSiteVectorArray trapSites_;
|
||||
wasm::OldTrapSiteVector oldTrapSites_;
|
||||
wasm::OldTrapFarJumpVector oldTrapFarJumps_;
|
||||
wasm::CallFarJumpVector callFarJumps_;
|
||||
|
@ -931,9 +930,6 @@ class AssemblerShared
|
|||
enoughMemory_ &= callSites_.emplaceBack(desc, retAddr.offset());
|
||||
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) {
|
||||
enoughMemory_ &= oldTrapSites_.append(trapSite);
|
||||
}
|
||||
|
@ -970,7 +966,6 @@ class AssemblerShared
|
|||
|
||||
wasm::CallSiteVector& callSites() { return callSites_; }
|
||||
wasm::CallSiteTargetVector& callSiteTargets() { return callSiteTargets_; }
|
||||
wasm::TrapSiteVectorArray& trapSites() { return trapSites_; }
|
||||
wasm::OldTrapSiteVector& oldTrapSites() { return oldTrapSites_; }
|
||||
wasm::OldTrapFarJumpVector& oldTrapFarJumps() { return oldTrapFarJumps_; }
|
||||
wasm::CallFarJumpVector& callFarJumps() { return callFarJumps_; }
|
||||
|
|
|
@ -1101,11 +1101,6 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
void breakpoint() {
|
||||
masm.int3();
|
||||
}
|
||||
CodeOffset ud2() {
|
||||
CodeOffset off(masm.currentOffset());
|
||||
masm.ud2();
|
||||
return off;
|
||||
}
|
||||
|
||||
static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
|
||||
static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
|
||||
|
|
|
@ -668,14 +668,7 @@ MacroAssembler::pushFakeReturnAddress(Register scratch)
|
|||
return retAddr;
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// WebAssembly
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::illegalInstruction()
|
||||
{
|
||||
return ud2();
|
||||
}
|
||||
// wasm specific methods, used in both the wasm baseline compiler and ion.
|
||||
|
||||
// RAII class that generates the jumps to traps when it's destructed, to
|
||||
// prevent some code duplication in the outOfLineWasmTruncateXtoY methods.
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#include "vm/ErrorReporting.h"
|
||||
#include "vm/MallocProvider.h"
|
||||
#include "vm/Runtime.h"
|
||||
#ifdef XP_DARWIN
|
||||
# include "wasm/WasmSignalHandlers.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DoublyLinkedList.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/MaybeOneOf.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
|
@ -50,7 +49,6 @@
|
|||
#include "vm/Stack.h"
|
||||
#include "vm/Stopwatch.h"
|
||||
#include "vm/Symbol.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
@ -1051,17 +1049,34 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
public:
|
||||
js::RuntimeCaches& caches() { return caches_.ref(); }
|
||||
|
||||
// When wasm traps or is interrupted, the signal handler records some data
|
||||
// for unwinding purposes. Wasm code can't interrupt or trap reentrantly.
|
||||
js::ActiveThreadData<
|
||||
mozilla::MaybeOneOf<js::wasm::TrapData, js::wasm::InterruptData>
|
||||
> wasmUnwindData;
|
||||
private:
|
||||
// When wasm is interrupted, the pc at which we should return if the
|
||||
// interrupt hasn't stopped execution of the current running code. Since
|
||||
// this is used only by the interrupt handler and the latter is not
|
||||
// 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() {
|
||||
return wasmUnwindData.ref().ref<js::wasm::TrapData>();
|
||||
// To ensure a consistent state of fp/pc, the unwound pc might be
|
||||
// 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() {
|
||||
return wasmUnwindData.ref().ref<js::wasm::InterruptData>();
|
||||
void finishWasmInterrupt() {
|
||||
MOZ_ASSERT(wasmResumePC_ && wasmUnwindPC_);
|
||||
wasmResumePC_ = nullptr;
|
||||
wasmUnwindPC_ = nullptr;
|
||||
}
|
||||
void* wasmResumePC() const {
|
||||
return wasmResumePC_;
|
||||
}
|
||||
void* wasmUnwindPC() const {
|
||||
return wasmUnwindPC_;
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
@ -1552,7 +1552,6 @@ jit::JitActivation::~JitActivation()
|
|||
MOZ_ASSERT(!bailoutData_);
|
||||
|
||||
MOZ_ASSERT(!isWasmInterrupted());
|
||||
MOZ_ASSERT(!isWasmTrapping());
|
||||
|
||||
clearRematerializedFrames();
|
||||
js_delete(rematerializedFrames_);
|
||||
|
@ -1726,8 +1725,9 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
|
|||
MOZ_ASSERT(state.fp);
|
||||
|
||||
// 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.
|
||||
MOZ_ASSERT(!isWasmInterrupted());
|
||||
|
||||
bool ignoredUnwound;
|
||||
wasm::UnwindState unwindState;
|
||||
|
@ -1736,7 +1736,7 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
|
|||
void* pc = unwindState.pc;
|
||||
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);
|
||||
|
||||
MOZ_ASSERT(compartment() == unwindState.fp->tls->instance->compartment());
|
||||
|
@ -1746,17 +1746,18 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
|
|||
void
|
||||
jit::JitActivation::finishWasmInterrupt()
|
||||
{
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
MOZ_ASSERT(isWasmInterrupted());
|
||||
|
||||
cx_->runtime()->wasmUnwindData.ref().destroy();
|
||||
cx_->runtime()->finishWasmInterrupt();
|
||||
packedExitFP_ = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
jit::JitActivation::isWasmInterrupted() const
|
||||
{
|
||||
JSRuntime* rt = cx_->runtime();
|
||||
if (!rt->wasmUnwindData.ref().constructed<wasm::InterruptData>())
|
||||
void* pc = cx_->runtime()->wasmUnwindPC();
|
||||
if (!pc)
|
||||
return false;
|
||||
|
||||
Activation* act = cx_->activation();
|
||||
|
@ -1767,76 +1768,24 @@ jit::JitActivation::isWasmInterrupted() const
|
|||
return false;
|
||||
|
||||
DebugOnly<const wasm::Frame*> fp = wasmExitFP();
|
||||
DebugOnly<void*> unwindPC = rt->wasmInterruptData().unwindPC;
|
||||
MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
|
||||
MOZ_ASSERT(fp && fp->instance()->code().containsCodePC(pc));
|
||||
return true;
|
||||
}
|
||||
|
||||
void*
|
||||
jit::JitActivation::wasmInterruptUnwindPC() const
|
||||
jit::JitActivation::wasmUnwindPC() const
|
||||
{
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
MOZ_ASSERT(isWasmInterrupted());
|
||||
return cx_->runtime()->wasmInterruptData().unwindPC;
|
||||
return cx_->runtime()->wasmUnwindPC();
|
||||
}
|
||||
|
||||
void*
|
||||
jit::JitActivation::wasmInterruptResumePC() const
|
||||
jit::JitActivation::wasmResumePC() const
|
||||
{
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
MOZ_ASSERT(isWasmInterrupted());
|
||||
return cx_->runtime()->wasmInterruptData().resumePC;
|
||||
}
|
||||
|
||||
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;
|
||||
return cx_->runtime()->wasmResumePC();
|
||||
}
|
||||
|
||||
InterpreterFrameIterator&
|
||||
|
|
|
@ -1666,18 +1666,11 @@ class JitActivation : public Activation
|
|||
// Interrupts are started from the interrupt signal handler (or the ARM
|
||||
// simulator) and cleared by WasmHandleExecutionInterrupt or WasmHandleThrow
|
||||
// when the interrupt is handled.
|
||||
|
||||
void startWasmInterrupt(const JS::ProfilingFrameIterator::RegisterState& state);
|
||||
void finishWasmInterrupt();
|
||||
bool isWasmInterrupted() const;
|
||||
void* wasmInterruptUnwindPC() const;
|
||||
void* wasmInterruptResumePC() 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;
|
||||
void* wasmUnwindPC() const;
|
||||
void* wasmResumePC() const;
|
||||
};
|
||||
|
||||
// A filtering of the ActivationIterator to only stop at JitActivations.
|
||||
|
|
|
@ -3807,6 +3807,15 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
#endif
|
||||
}
|
||||
|
||||
void unreachableTrap()
|
||||
{
|
||||
masm.jump(oldTrap(Trap::Unreachable));
|
||||
#ifdef DEBUG
|
||||
masm.breakpoint();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
supportsRoundInstruction(RoundingMode mode)
|
||||
{
|
||||
|
@ -4896,10 +4905,6 @@ class BaseCompiler final : public BaseCompilerInterface
|
|||
return iter_.bytecodeOffset();
|
||||
}
|
||||
|
||||
void trap(Trap t) const {
|
||||
masm.wasmTrap(t, bytecodeOffset());
|
||||
}
|
||||
|
||||
OldTrapDesc oldTrap(Trap t) const {
|
||||
// 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
|
||||
|
@ -8528,7 +8533,7 @@ BaseCompiler::emitBody()
|
|||
case uint16_t(Op::Unreachable):
|
||||
CHECK(iter_.readUnreachable());
|
||||
if (!deadCode_) {
|
||||
trap(Trap::Unreachable);
|
||||
unreachableTrap();
|
||||
deadCode_ = true;
|
||||
}
|
||||
NEXT();
|
||||
|
|
|
@ -87,7 +87,7 @@ WasmHandleExecutionInterrupt()
|
|||
|
||||
// If CheckForInterrupt succeeded, then execution can proceed and the
|
||||
// interrupt is over.
|
||||
void* resumePC = activation->wasmInterruptResumePC();
|
||||
void* resumePC = activation->wasmResumePC();
|
||||
activation->finishWasmInterrupt();
|
||||
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()->isWasmTrapping(), "unwinding clears the trapping state");
|
||||
|
||||
return iter.unwoundAddressOfReturnAddress();
|
||||
}
|
||||
|
@ -289,13 +288,6 @@ WasmOldReportTrap(int32_t trapIndex)
|
|||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportTrap()
|
||||
{
|
||||
Trap trap = TlsContext.get()->runtime()->wasmTrapData().trap;
|
||||
WasmOldReportTrap(int32_t(trap));
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportOutOfBounds()
|
||||
{
|
||||
|
@ -452,9 +444,6 @@ AddressOf(SymbolicAddress imm, ABIFunctionType* abiType)
|
|||
case SymbolicAddress::HandleThrow:
|
||||
*abiType = Args_General0;
|
||||
return FuncCast(WasmHandleThrow, *abiType);
|
||||
case SymbolicAddress::ReportTrap:
|
||||
*abiType = Args_General0;
|
||||
return FuncCast(WasmReportTrap, *abiType);
|
||||
case SymbolicAddress::OldReportTrap:
|
||||
*abiType = Args_General1;
|
||||
return FuncCast(WasmOldReportTrap, *abiType);
|
||||
|
@ -611,7 +600,6 @@ wasm::NeedsBuiltinThunk(SymbolicAddress sym)
|
|||
case SymbolicAddress::HandleExecutionInterrupt: // GenerateInterruptExit
|
||||
case SymbolicAddress::HandleDebugTrap: // GenerateDebugTrapStub
|
||||
case SymbolicAddress::HandleThrow: // GenerateThrowStub
|
||||
case SymbolicAddress::ReportTrap: // GenerateTrapExit
|
||||
case SymbolicAddress::OldReportTrap: // GenerateOldTrapExit
|
||||
case SymbolicAddress::ReportOutOfBounds: // GenerateOutOfBoundsExit
|
||||
case SymbolicAddress::ReportUnalignedAccess: // GeneratesUnalignedExit
|
||||
|
@ -908,7 +896,6 @@ wasm::EnsureBuiltinThunksInitialized()
|
|||
MOZ_ASSERT(masm.callSites().empty());
|
||||
MOZ_ASSERT(masm.callSiteTargets().empty());
|
||||
MOZ_ASSERT(masm.callFarJumps().empty());
|
||||
MOZ_ASSERT(masm.trapSites().empty());
|
||||
MOZ_ASSERT(masm.oldTrapSites().empty());
|
||||
MOZ_ASSERT(masm.oldTrapFarJumps().empty());
|
||||
MOZ_ASSERT(masm.callFarJumps().empty());
|
||||
|
|
|
@ -242,6 +242,9 @@ CodeSegment::create(Tier tier,
|
|||
const Metadata& metadata)
|
||||
{
|
||||
// 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>();
|
||||
if (!cs)
|
||||
|
@ -265,7 +268,6 @@ CodeSegment::initialize(Tier tier,
|
|||
MOZ_ASSERT(linkData.interruptOffset);
|
||||
MOZ_ASSERT(linkData.outOfBoundsOffset);
|
||||
MOZ_ASSERT(linkData.unalignedAccessOffset);
|
||||
MOZ_ASSERT(linkData.trapOffset);
|
||||
|
||||
tier_ = tier;
|
||||
bytes_ = Move(codeBytes);
|
||||
|
@ -273,7 +275,6 @@ CodeSegment::initialize(Tier tier,
|
|||
interruptCode_ = bytes_.get() + linkData.interruptOffset;
|
||||
outOfBoundsCode_ = bytes_.get() + linkData.outOfBoundsOffset;
|
||||
unalignedAccessCode_ = bytes_.get() + linkData.unalignedAccessOffset;
|
||||
trapCode_ = bytes_.get() + linkData.trapOffset;
|
||||
|
||||
if (!StaticallyLink(*this, linkData))
|
||||
return false;
|
||||
|
@ -458,7 +459,6 @@ MetadataTier::serializedSize() const
|
|||
return SerializedPodVectorSize(memoryAccesses) +
|
||||
SerializedPodVectorSize(codeRanges) +
|
||||
SerializedPodVectorSize(callSites) +
|
||||
trapSites.serializedSize() +
|
||||
SerializedVectorSize(funcImports) +
|
||||
SerializedVectorSize(funcExports);
|
||||
}
|
||||
|
@ -469,7 +469,6 @@ MetadataTier::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
|||
return memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
|
||||
codeRanges.sizeOfExcludingThis(mallocSizeOf) +
|
||||
callSites.sizeOfExcludingThis(mallocSizeOf) +
|
||||
trapSites.sizeOfExcludingThis(mallocSizeOf) +
|
||||
SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
|
||||
SizeOfVectorExcludingThis(funcExports, mallocSizeOf);
|
||||
}
|
||||
|
@ -481,7 +480,6 @@ MetadataTier::serialize(uint8_t* cursor) const
|
|||
cursor = SerializePodVector(cursor, memoryAccesses);
|
||||
cursor = SerializePodVector(cursor, codeRanges);
|
||||
cursor = SerializePodVector(cursor, callSites);
|
||||
cursor = trapSites.serialize(cursor);
|
||||
cursor = SerializeVector(cursor, funcImports);
|
||||
cursor = SerializeVector(cursor, funcExports);
|
||||
return cursor;
|
||||
|
@ -493,7 +491,6 @@ MetadataTier::deserialize(const uint8_t* cursor)
|
|||
(cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
|
||||
(cursor = DeserializePodVector(cursor, &codeRanges)) &&
|
||||
(cursor = DeserializePodVector(cursor, &callSites)) &&
|
||||
(cursor = trapSites.deserialize(cursor)) &&
|
||||
(cursor = DeserializeVector(cursor, &funcImports)) &&
|
||||
(cursor = DeserializeVector(cursor, &funcExports));
|
||||
debugTrapFarJumpOffsets.clear();
|
||||
|
@ -753,7 +750,7 @@ Code::segment(Tier tier) const
|
|||
bool
|
||||
Code::containsCodePC(const void* pc) const
|
||||
{
|
||||
for (Tier t : tiers()) {
|
||||
for (auto t : tiers()) {
|
||||
const CodeSegment& cs = segment(t);
|
||||
if (cs.containsCodePC(pc))
|
||||
return true;
|
||||
|
@ -773,7 +770,7 @@ struct CallSiteRetAddrOffset
|
|||
const CallSite*
|
||||
Code::lookupCallSite(void* returnAddress) const
|
||||
{
|
||||
for (Tier t : tiers()) {
|
||||
for (auto t : tiers()) {
|
||||
uint32_t target = ((uint8_t*)returnAddress) - segment(t).base();
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = metadata(t).callSites.length();
|
||||
|
@ -790,7 +787,7 @@ Code::lookupCallSite(void* returnAddress) const
|
|||
const CodeRange*
|
||||
Code::lookupRange(void* pc) const
|
||||
{
|
||||
for (Tier t : tiers()) {
|
||||
for (auto t : tiers()) {
|
||||
CodeRange::OffsetInCode target((uint8_t*)pc - segment(t).base());
|
||||
const CodeRange* result = LookupInSorted(metadata(t).codeRanges, target);
|
||||
if (result)
|
||||
|
@ -812,7 +809,7 @@ struct MemoryAccessOffset
|
|||
const MemoryAccess*
|
||||
Code::lookupMemoryAccess(void* pc) const
|
||||
{
|
||||
for (Tier t : tiers()) {
|
||||
for (auto t : tiers()) {
|
||||
const MemoryAccessVector& memoryAccesses = metadata(t).memoryAccesses;
|
||||
|
||||
uint32_t target = ((uint8_t*)pc) - segment(t).base();
|
||||
|
@ -830,40 +827,6 @@ Code::lookupMemoryAccess(void* pc) const
|
|||
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
|
||||
// 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
|
||||
|
|
|
@ -81,7 +81,6 @@ class CodeSegment
|
|||
uint8_t* interruptCode_;
|
||||
uint8_t* outOfBoundsCode_;
|
||||
uint8_t* unalignedAccessCode_;
|
||||
uint8_t* trapCode_;
|
||||
|
||||
bool registered_;
|
||||
|
||||
|
@ -109,7 +108,6 @@ class CodeSegment
|
|||
interruptCode_(nullptr),
|
||||
outOfBoundsCode_(nullptr),
|
||||
unalignedAccessCode_(nullptr),
|
||||
trapCode_(nullptr),
|
||||
registered_(false)
|
||||
{}
|
||||
|
||||
|
@ -141,7 +139,6 @@ class CodeSegment
|
|||
uint8_t* interruptCode() const { return interruptCode_; }
|
||||
uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
|
||||
uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
|
||||
uint8_t* trapCode() const { return trapCode_; }
|
||||
|
||||
bool containsCodePC(const void* pc) const {
|
||||
return pc >= base() && pc < (base() + length_);
|
||||
|
@ -354,7 +351,6 @@ struct MetadataTier
|
|||
MemoryAccessVector memoryAccesses;
|
||||
CodeRangeVector codeRanges;
|
||||
CallSiteVector callSites;
|
||||
TrapSiteVectorArray trapSites;
|
||||
FuncImportVector funcImports;
|
||||
FuncExportVector funcExports;
|
||||
|
||||
|
@ -492,7 +488,6 @@ class Code : public ShareableBase<Code>
|
|||
const CodeRange* lookupRange(void* pc) const;
|
||||
const MemoryAccess* lookupMemoryAccess(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
|
||||
// is enabled.
|
||||
|
|
|
@ -42,23 +42,6 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* 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
|
||||
// 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
|
||||
|
@ -69,9 +52,9 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp)
|
|||
|
||||
if (activation->isWasmInterrupted()) {
|
||||
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);
|
||||
|
||||
lineOrBytecode_ = codeRange_->funcLineOrBytecode();
|
||||
|
@ -115,8 +98,6 @@ WasmFrameIter::operator++()
|
|||
if (unwind_ == Unwind::True) {
|
||||
if (activation_->isWasmInterrupted())
|
||||
activation_->finishWasmInterrupt();
|
||||
else if (activation_->isWasmTrapping())
|
||||
activation_->finishWasmTrap();
|
||||
activation_->setWasmExitFP(fp_);
|
||||
}
|
||||
|
||||
|
@ -643,7 +624,6 @@ ProfilingFrameIterator::initFromExitFP(const Frame* fp)
|
|||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::BuiltinThunk:
|
||||
case CodeRange::TrapExit:
|
||||
case CodeRange::OldTrapExit:
|
||||
case CodeRange::DebugTrap:
|
||||
case CodeRange::OutOfBoundsExit:
|
||||
|
@ -814,7 +794,6 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case CodeRange::TrapExit:
|
||||
case CodeRange::OutOfBoundsExit:
|
||||
case CodeRange::UnalignedExit:
|
||||
// These code stubs execute after the prologue/epilogue have completed
|
||||
|
@ -923,7 +902,6 @@ ProfilingFrameIterator::operator++()
|
|||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::BuiltinThunk:
|
||||
case CodeRange::TrapExit:
|
||||
case CodeRange::OldTrapExit:
|
||||
case CodeRange::DebugTrap:
|
||||
case CodeRange::OutOfBoundsExit:
|
||||
|
@ -952,7 +930,6 @@ ThunkedNativeToDescription(SymbolicAddress func)
|
|||
case SymbolicAddress::HandleExecutionInterrupt:
|
||||
case SymbolicAddress::HandleDebugTrap:
|
||||
case SymbolicAddress::HandleThrow:
|
||||
case SymbolicAddress::ReportTrap:
|
||||
case SymbolicAddress::OldReportTrap:
|
||||
case SymbolicAddress::ReportOutOfBounds:
|
||||
case SymbolicAddress::ReportUnalignedAccess:
|
||||
|
@ -1083,7 +1060,6 @@ ProfilingFrameIterator::label() const
|
|||
case CodeRange::ImportJitExit: return importJitDescription;
|
||||
case CodeRange::BuiltinThunk: return builtinNativeDescription;
|
||||
case CodeRange::ImportInterpExit: return importInterpDescription;
|
||||
case CodeRange::TrapExit: return trapDescription;
|
||||
case CodeRange::OldTrapExit: return trapDescription;
|
||||
case CodeRange::DebugTrap: return debugTrapDescription;
|
||||
case CodeRange::OutOfBoundsExit: return "out-of-bounds stub (in wasm)";
|
||||
|
|
|
@ -47,7 +47,6 @@ CompiledCode::swap(MacroAssembler& masm)
|
|||
|
||||
callSites.swap(masm.callSites());
|
||||
callSiteTargets.swap(masm.callSiteTargets());
|
||||
trapSites.swap(masm.trapSites());
|
||||
oldTrapSites.swap(masm.oldTrapSites());
|
||||
callFarJumps.swap(masm.callFarJumps());
|
||||
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 EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>> TrapMaybeOffsetArray;
|
||||
typedef EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>> TrapOffsetArray;
|
||||
|
||||
bool
|
||||
ModuleGenerator::linkCallSites()
|
||||
|
@ -400,7 +399,7 @@ ModuleGenerator::linkCallSites()
|
|||
if (!existingCallFarJumps.init())
|
||||
return false;
|
||||
|
||||
TrapMaybeOffsetArray existingTrapFarJumps;
|
||||
TrapOffsetArray existingTrapFarJumps;
|
||||
|
||||
for (; lastPatchedCallSite_ < metadataTier_->callSites.length(); lastPatchedCallSite_++) {
|
||||
const CallSite& callSite = metadataTier_->callSites[lastPatchedCallSite_];
|
||||
|
@ -524,10 +523,6 @@ ModuleGenerator::noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRan
|
|||
MOZ_ASSERT(!linkDataTier_->interruptOffset);
|
||||
linkDataTier_->interruptOffset = codeRange.begin();
|
||||
break;
|
||||
case CodeRange::TrapExit:
|
||||
MOZ_ASSERT(!linkDataTier_->trapOffset);
|
||||
linkDataTier_->trapOffset = codeRange.begin();
|
||||
break;
|
||||
case CodeRange::Throw:
|
||||
// Jumped to by other stubs, so nothing to do.
|
||||
break;
|
||||
|
@ -585,12 +580,6 @@ ModuleGenerator::linkCompiledCode(const CompiledCode& code)
|
|||
if (!callSiteTargets_.appendAll(code.callSiteTargets))
|
||||
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());
|
||||
|
||||
auto trapFarJumpOp = [=](uint32_t, OldTrapFarJump* tfj) { tfj->offsetBy(offsetInModule); };
|
||||
|
@ -809,7 +798,6 @@ ModuleGenerator::finishCode()
|
|||
|
||||
MOZ_ASSERT(masm_.callSites().empty());
|
||||
MOZ_ASSERT(masm_.callSiteTargets().empty());
|
||||
MOZ_ASSERT(masm_.trapSites().empty());
|
||||
MOZ_ASSERT(masm_.oldTrapSites().empty());
|
||||
MOZ_ASSERT(masm_.oldTrapFarJumps().empty());
|
||||
MOZ_ASSERT(masm_.callFarJumps().empty());
|
||||
|
@ -824,32 +812,19 @@ ModuleGenerator::finishCode()
|
|||
bool
|
||||
ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
|
||||
{
|
||||
// Assert all sorted metadata is sorted.
|
||||
#ifdef DEBUG
|
||||
uint32_t last = 0;
|
||||
// Assert CodeRanges are sorted.
|
||||
uint32_t lastEnd = 0;
|
||||
for (const CodeRange& codeRange : metadataTier_->codeRanges) {
|
||||
MOZ_ASSERT(codeRange.begin() >= last);
|
||||
last = codeRange.end();
|
||||
MOZ_ASSERT(codeRange.begin() >= lastEnd);
|
||||
lastEnd = codeRange.end();
|
||||
}
|
||||
|
||||
last = 0;
|
||||
for (const CallSite& callSite : metadataTier_->callSites) {
|
||||
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;
|
||||
// Assert debugTrapFarJumpOffsets are sorted.
|
||||
uint32_t lastOffset = 0;
|
||||
for (uint32_t debugTrapFarJumpOffset : metadataTier_->debugTrapFarJumpOffsets) {
|
||||
MOZ_ASSERT(debugTrapFarJumpOffset >= last);
|
||||
last = debugTrapFarJumpOffset;
|
||||
MOZ_ASSERT(debugTrapFarJumpOffset >= lastOffset);
|
||||
lastOffset = debugTrapFarJumpOffset;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -874,7 +849,7 @@ ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
|
|||
|
||||
metadataTier_->memoryAccesses.podResizeToFit();
|
||||
metadataTier_->codeRanges.podResizeToFit();
|
||||
metadataTier_->trapSites.podResizeToFit();
|
||||
metadataTier_->callSites.podResizeToFit();
|
||||
metadataTier_->debugTrapFarJumpOffsets.podResizeToFit();
|
||||
metadataTier_->debugFuncToCodeRange.podResizeToFit();
|
||||
|
||||
|
|
|
@ -64,7 +64,6 @@ struct CompiledCode
|
|||
CodeRangeVector codeRanges;
|
||||
CallSiteVector callSites;
|
||||
CallSiteTargetVector callSiteTargets;
|
||||
TrapSiteVectorArray trapSites;
|
||||
OldTrapSiteVector oldTrapSites;
|
||||
OldTrapFarJumpVector oldTrapFarJumps;
|
||||
CallFarJumpVector callFarJumps;
|
||||
|
@ -79,7 +78,6 @@ struct CompiledCode
|
|||
codeRanges.clear();
|
||||
callSites.clear();
|
||||
callSiteTargets.clear();
|
||||
trapSites.clear();
|
||||
oldTrapSites.clear();
|
||||
oldTrapFarJumps.clear();
|
||||
callFarJumps.clear();
|
||||
|
@ -94,7 +92,6 @@ struct CompiledCode
|
|||
codeRanges.empty() &&
|
||||
callSites.empty() &&
|
||||
callSiteTargets.empty() &&
|
||||
trapSites.empty() &&
|
||||
oldTrapSites.empty() &&
|
||||
oldTrapFarJumps.empty() &&
|
||||
callFarJumps.empty() &&
|
||||
|
|
|
@ -45,7 +45,6 @@ struct LinkDataTierCacheablePod
|
|||
uint32_t interruptOffset;
|
||||
uint32_t outOfBoundsOffset;
|
||||
uint32_t unalignedAccessOffset;
|
||||
uint32_t trapOffset;
|
||||
|
||||
LinkDataTierCacheablePod() { mozilla::PodZero(this); }
|
||||
};
|
||||
|
|
|
@ -975,15 +975,15 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
|||
EXCEPTION_RECORD* record = exception->ExceptionRecord;
|
||||
CONTEXT* context = exception->ContextRecord;
|
||||
|
||||
if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
|
||||
record->ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION)
|
||||
{
|
||||
if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t** ppc = ContextToPC(context);
|
||||
uint8_t* pc = *ppc;
|
||||
|
||||
if (record->NumberParameters < 2)
|
||||
return false;
|
||||
|
||||
const CodeSegment* codeSegment = LookupCodeSegment(pc);
|
||||
if (!codeSegment)
|
||||
return false;
|
||||
|
@ -994,7 +994,7 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
|||
const Instance* instance = LookupFaultingInstance(*codeSegment, pc, ContextToFP(context));
|
||||
if (!instance) {
|
||||
// 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,
|
||||
// after ResumeThread, the exception handler is called with pc equal to
|
||||
// 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).
|
||||
return activation->isWasmInterrupted() &&
|
||||
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]);
|
||||
|
||||
// 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());
|
||||
|
||||
// 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);
|
||||
return true;
|
||||
}
|
||||
|
@ -1128,11 +1113,8 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request)
|
|||
uint8_t** ppc = ContextToPC(&context);
|
||||
uint8_t* pc = *ppc;
|
||||
|
||||
if (request.body.exception != EXC_BAD_ACCESS &&
|
||||
request.body.exception != EXC_BAD_INSTRUCTION)
|
||||
{
|
||||
if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
|
||||
return false;
|
||||
}
|
||||
|
||||
// The faulting thread is suspended so we can access cx fields that can
|
||||
// normally only be accessed by the cx's active thread.
|
||||
|
@ -1146,31 +1128,17 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request)
|
|||
if (!instance)
|
||||
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();
|
||||
MOZ_ASSERT(activation->compartment() == instance->compartment());
|
||||
|
||||
if (request.body.exception == EXC_BAD_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();
|
||||
} 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);
|
||||
}
|
||||
HandleMemoryAccess(&context, pc, faultingAddress, codeSegment, *instance, activation, ppc);
|
||||
|
||||
// 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);
|
||||
|
@ -1343,7 +1311,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
|||
return false;
|
||||
AutoSignalHandler ash;
|
||||
|
||||
MOZ_RELEASE_ASSERT(signum == SIGSEGV || signum == SIGBUS || signum == SIGILL);
|
||||
MOZ_RELEASE_ASSERT(signum == SIGSEGV || signum == SIGBUS);
|
||||
|
||||
CONTEXT* context = (CONTEXT*)ctx;
|
||||
uint8_t** ppc = ContextToPC(context);
|
||||
|
@ -1357,20 +1325,6 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
|||
if (!instance)
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
JitActivation* activation = TlsContext.get()->activation()->asJit();
|
||||
MOZ_ASSERT(activation->compartment() == instance->compartment());
|
||||
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
if (signum == SIGBUS) {
|
||||
// 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 sPrevSIGBUSHandler;
|
||||
static struct sigaction sPrevSIGILLHandler;
|
||||
|
||||
static void
|
||||
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))
|
||||
return;
|
||||
|
||||
struct sigaction* previousSignal = nullptr;
|
||||
switch (signum) {
|
||||
case SIGSEGV: previousSignal = &sPrevSEGVHandler; break;
|
||||
case SIGBUS: previousSignal = &sPrevSIGBUSHandler; break;
|
||||
case SIGILL: previousSignal = &sPrevSIGILLHandler; break;
|
||||
}
|
||||
MOZ_ASSERT(previousSignal);
|
||||
struct sigaction* previousSignal = signum == SIGSEGV
|
||||
? &sPrevSEGVHandler
|
||||
: &sPrevSIGBUSHandler;
|
||||
|
||||
// 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
|
||||
|
@ -1665,15 +1617,6 @@ ProcessHasSignalHandlers()
|
|||
if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler))
|
||||
MOZ_CRASH("unable to install sigbus handler");
|
||||
# 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
|
||||
|
||||
sHaveSignalHandlers = true;
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
# include <mach/mach.h>
|
||||
#endif
|
||||
#include "threading/Thread.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
|
||||
struct JSContext;
|
||||
struct JSRuntime;
|
||||
|
@ -80,35 +79,6 @@ class MachExceptionHandler
|
|||
};
|
||||
#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 js
|
||||
|
||||
|
|
|
@ -1001,29 +1001,6 @@ wasm::GenerateBuiltinThunk(MacroAssembler& masm, ABIFunctionType abiType, ExitRe
|
|||
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.
|
||||
// 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
|
||||
|
@ -1391,30 +1368,11 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
|
|||
}
|
||||
|
||||
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
|
||||
switch (trap) {
|
||||
case Trap::Unreachable:
|
||||
break;
|
||||
// The TODO list of "old" traps to convert to new traps:
|
||||
case Trap::IntegerOverflow:
|
||||
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");
|
||||
}
|
||||
CallableOffsets offsets;
|
||||
if (!GenerateOldTrapExit(masm, trap, &throwLabel, &offsets))
|
||||
return false;
|
||||
if (!code->codeRanges.emplaceBack(trap, offsets))
|
||||
return false;
|
||||
}
|
||||
|
||||
Offsets offsets;
|
||||
|
@ -1429,11 +1387,6 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
|
|||
if (!code->codeRanges.emplaceBack(CodeRange::UnalignedExit, offsets))
|
||||
return false;
|
||||
|
||||
if (!GenerateTrapExit(masm, &throwLabel, &offsets))
|
||||
return false;
|
||||
if (!code->codeRanges.emplaceBack(CodeRange::TrapExit, offsets))
|
||||
return false;
|
||||
|
||||
if (!GenerateInterruptExit(masm, &throwLabel, &offsets))
|
||||
return false;
|
||||
if (!code->codeRanges.emplaceBack(CodeRange::Interrupt, offsets))
|
||||
|
|
|
@ -30,7 +30,6 @@ using namespace js::jit;
|
|||
using namespace js::wasm;
|
||||
|
||||
using mozilla::IsPowerOfTwo;
|
||||
using mozilla::MakeEnumeratedRange;
|
||||
|
||||
// A sanity check. We have only tested WASM_HUGE_MEMORY on x64, and only tested
|
||||
// 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)
|
||||
: begin_(offsets.begin),
|
||||
ret_(0),
|
||||
|
@ -773,7 +703,6 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
|
|||
case FarJumpIsland:
|
||||
case OutOfBoundsExit:
|
||||
case UnalignedExit:
|
||||
case TrapExit:
|
||||
case Throw:
|
||||
case Interrupt:
|
||||
break;
|
||||
|
|
|
@ -935,52 +935,6 @@ enum class Trap
|
|||
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
|
||||
// different key points in a CodeRange during compilation.
|
||||
|
||||
|
@ -1057,7 +1011,6 @@ class CodeRange
|
|||
ImportJitExit, // fast-path calling from wasm into JIT code
|
||||
ImportInterpExit, // slow-path calling from wasm into C++ interp
|
||||
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
|
||||
DebugTrap, // calls C++ to handle debug event
|
||||
FarJumpIsland, // inserted to connect otherwise out-of-range insns
|
||||
|
@ -1134,7 +1087,7 @@ class CodeRange
|
|||
return kind() == ImportJitExit;
|
||||
}
|
||||
bool isTrapExit() const {
|
||||
return kind() == OldTrapExit || kind() == TrapExit;
|
||||
return kind() == OldTrapExit;
|
||||
}
|
||||
bool isDebugTrap() const {
|
||||
return kind() == DebugTrap;
|
||||
|
@ -1148,7 +1101,7 @@ class CodeRange
|
|||
// the return instruction to calculate the frame pointer.
|
||||
|
||||
bool hasReturn() const {
|
||||
return isFunction() || isImportExit() || kind() == OldTrapExit || isDebugTrap();
|
||||
return isFunction() || isImportExit() || isTrapExit() || isDebugTrap();
|
||||
}
|
||||
uint32_t ret() const {
|
||||
MOZ_ASSERT(hasReturn());
|
||||
|
@ -1226,6 +1179,22 @@ WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
|
|||
extern const CodeRange*
|
||||
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
|
||||
// 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
|
||||
|
@ -1364,7 +1333,6 @@ enum class SymbolicAddress
|
|||
HandleExecutionInterrupt,
|
||||
HandleDebugTrap,
|
||||
HandleThrow,
|
||||
ReportTrap,
|
||||
OldReportTrap,
|
||||
ReportOutOfBounds,
|
||||
ReportUnalignedAccess,
|
||||
|
|
Загрузка…
Ссылка в новой задаче