зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1286948 - Adds prolog and epilog debug traps and handlers. r=luke
Using toggled call/traps to invoke handler to process enter and leave frame events. MozReview-Commit-ID: APTt3N6Zt0P --HG-- extra : rebase_source : 1121020f29539e2155bfaea1dc36f07d9a45d003
This commit is contained in:
Родитель
89670717cb
Коммит
76fa09762a
|
@ -127,7 +127,7 @@ testError(
|
||||||
(func (export "") (call $foo))
|
(func (export "") (call $foo))
|
||||||
)`,
|
)`,
|
||||||
WebAssembly.RuntimeError,
|
WebAssembly.RuntimeError,
|
||||||
["", ">", "1,>", "0,1,>", "trap handling,0,1,>", "inline stub,0,1,>", ""]);
|
["", ">", "1,>", "0,1,>", "trap handling,0,1,>", "inline stub,0,1,>", "trap handling,0,1,>", "inline stub,0,1,>", ""]);
|
||||||
|
|
||||||
testError(
|
testError(
|
||||||
`(module
|
`(module
|
||||||
|
@ -142,7 +142,7 @@ WebAssembly.RuntimeError,
|
||||||
// Technically we have this one *one-instruction* interval where
|
// Technically we have this one *one-instruction* interval where
|
||||||
// the caller is lost (the stack with "1,>"). It's annoying to fix and shouldn't
|
// the caller is lost (the stack with "1,>"). It's annoying to fix and shouldn't
|
||||||
// mess up profiles in practice so we ignore it.
|
// mess up profiles in practice so we ignore it.
|
||||||
["", ">", "0,>", "1,0,>", "1,>", "trap handling,0,>", "inline stub,0,>", ""]);
|
["", ">", "0,>", "1,0,>", "1,>", "trap handling,0,>", "inline stub,0,>", "trap handling,0,>", "inline stub,0,>", ""]);
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var e = wasmEvalText(`
|
var e = wasmEvalText(`
|
||||||
|
|
|
@ -529,6 +529,12 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
static void patchNopToNearJump(uint8_t* jump, uint8_t* target) PER_SHARED_ARCH;
|
static void patchNopToNearJump(uint8_t* jump, uint8_t* target) PER_SHARED_ARCH;
|
||||||
static void patchNearJumpToNop(uint8_t* jump) PER_SHARED_ARCH;
|
static void patchNearJumpToNop(uint8_t* jump) PER_SHARED_ARCH;
|
||||||
|
|
||||||
|
// Emit a nop that can be patched to and from a nop and a call with int32
|
||||||
|
// relative displacement.
|
||||||
|
CodeOffset nopPatchableToCall(const wasm::CallSiteDesc& desc) PER_SHARED_ARCH;
|
||||||
|
static void patchNopToCall(uint8_t* callsite, uint8_t* target) PER_SHARED_ARCH;
|
||||||
|
static void patchCallToNop(uint8_t* callsite) PER_SHARED_ARCH;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// ABI function calls.
|
// ABI function calls.
|
||||||
|
|
|
@ -5128,6 +5128,34 @@ MacroAssembler::patchNearJumpToNop(uint8_t* jump)
|
||||||
new (jump) InstNOP();
|
new (jump) InstNOP();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CodeOffset
|
||||||
|
MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
|
||||||
|
{
|
||||||
|
CodeOffset offset(currentOffset());
|
||||||
|
ma_nop();
|
||||||
|
append(desc, CodeOffset(currentOffset()), framePushed());
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target)
|
||||||
|
{
|
||||||
|
uint8_t* inst = call - 4;
|
||||||
|
MOZ_ASSERT(reinterpret_cast<Instruction*>(inst)->is<InstBLImm>() ||
|
||||||
|
reinterpret_cast<Instruction*>(inst)->is<InstNOP>());
|
||||||
|
|
||||||
|
new (inst) InstBLImm(BOffImm(target - inst), Assembler::Always);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::patchCallToNop(uint8_t* call)
|
||||||
|
{
|
||||||
|
uint8_t* inst = call - 4;
|
||||||
|
MOZ_ASSERT(reinterpret_cast<Instruction*>(inst)->is<InstBLImm>() ||
|
||||||
|
reinterpret_cast<Instruction*>(inst)->is<InstNOP>());
|
||||||
|
new (inst) InstNOP();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::pushReturnAddress()
|
MacroAssembler::pushReturnAddress()
|
||||||
{
|
{
|
||||||
|
|
|
@ -587,6 +587,25 @@ MacroAssembler::patchNearJumpToNop(uint8_t* jump)
|
||||||
MOZ_CRASH("NYI");
|
MOZ_CRASH("NYI");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CodeOffset
|
||||||
|
MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
return CodeOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::patchCallToNop(uint8_t* call)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::pushReturnAddress()
|
MacroAssembler::pushReturnAddress()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1678,6 +1678,25 @@ MacroAssembler::call(JitCode* c)
|
||||||
callJitNoProfiler(ScratchRegister);
|
callJitNoProfiler(ScratchRegister);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CodeOffset
|
||||||
|
MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
return CodeOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::patchCallToNop(uint8_t* call)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::pushReturnAddress()
|
MacroAssembler::pushReturnAddress()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1106,6 +1106,13 @@ class AssemblerX86Shared : public AssemblerShared
|
||||||
X86Encoding::BaseAssembler::patchJumpToTwoByteNop(jump);
|
X86Encoding::BaseAssembler::patchJumpToTwoByteNop(jump);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void patchFiveByteNopToCall(uint8_t* callsite, uint8_t* target) {
|
||||||
|
X86Encoding::BaseAssembler::patchFiveByteNopToCall(callsite, target);
|
||||||
|
}
|
||||||
|
static void patchCallToFiveByteNop(uint8_t* callsite) {
|
||||||
|
X86Encoding::BaseAssembler::patchCallToFiveByteNop(callsite);
|
||||||
|
}
|
||||||
|
|
||||||
void breakpoint() {
|
void breakpoint() {
|
||||||
masm.int3();
|
masm.int3();
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,40 @@ public:
|
||||||
jump[1] = OP_NOP;
|
jump[1] = OP_NOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void patchFiveByteNopToCall(uint8_t* callsite, uint8_t* target)
|
||||||
|
{
|
||||||
|
// Note: the offset is relative to the address of the instruction after
|
||||||
|
// the call which is five bytes.
|
||||||
|
uint8_t* inst = callsite - sizeof(int32_t) - 1;
|
||||||
|
// The nop can be already patched as call, overriding the call.
|
||||||
|
// See also nop_five.
|
||||||
|
MOZ_ASSERT(inst[0] == OP_NOP_0F || inst[0] == OP_CALL_rel32);
|
||||||
|
MOZ_ASSERT_IF(inst[0] == OP_NOP_0F, inst[1] == OP_NOP_1F ||
|
||||||
|
inst[2] == OP_NOP_44 ||
|
||||||
|
inst[3] == OP_NOP_00 ||
|
||||||
|
inst[4] == OP_NOP_00);
|
||||||
|
inst[0] = OP_CALL_rel32;
|
||||||
|
SetRel32(callsite, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void patchCallToFiveByteNop(uint8_t* callsite)
|
||||||
|
{
|
||||||
|
// See also patchFiveByteNopToCall and nop_five.
|
||||||
|
uint8_t* inst = callsite - sizeof(int32_t) - 1;
|
||||||
|
// The call can be already patched as nop.
|
||||||
|
if (inst[0] == OP_NOP_0F) {
|
||||||
|
MOZ_ASSERT(inst[1] == OP_NOP_1F || inst[2] == OP_NOP_44 ||
|
||||||
|
inst[3] == OP_NOP_00 || inst[4] == OP_NOP_00);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(inst[0] == OP_CALL_rel32);
|
||||||
|
inst[0] = OP_NOP_0F;
|
||||||
|
inst[1] = OP_NOP_1F;
|
||||||
|
inst[2] = OP_NOP_44;
|
||||||
|
inst[3] = OP_NOP_00;
|
||||||
|
inst[4] = OP_NOP_00;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The nop multibytes sequences are directly taken from the Intel's
|
* The nop multibytes sequences are directly taken from the Intel's
|
||||||
* architecture software developer manual.
|
* architecture software developer manual.
|
||||||
|
|
|
@ -741,6 +741,28 @@ MacroAssembler::patchNearJumpToNop(uint8_t* jump)
|
||||||
Assembler::patchJumpToTwoByteNop(jump);
|
Assembler::patchJumpToTwoByteNop(jump);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CodeOffset
|
||||||
|
MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
|
||||||
|
{
|
||||||
|
CodeOffset offset(currentOffset());
|
||||||
|
masm.nop_five();
|
||||||
|
append(desc, CodeOffset(currentOffset()), framePushed());
|
||||||
|
MOZ_ASSERT_IF(!oom(), size() - offset.offset() == ToggledCallSize(nullptr));
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::patchNopToCall(uint8_t* callsite, uint8_t* target)
|
||||||
|
{
|
||||||
|
Assembler::patchFiveByteNopToCall(callsite, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::patchCallToNop(uint8_t* callsite)
|
||||||
|
{
|
||||||
|
Assembler::patchCallToFiveByteNop(callsite);
|
||||||
|
}
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Jit Frames.
|
// Jit Frames.
|
||||||
|
|
||||||
|
|
|
@ -2080,6 +2080,11 @@ class BaseCompiler
|
||||||
labelPool_.free(label);
|
labelPool_.free(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void insertBreakablePoint(CallSiteDesc::Kind kind) {
|
||||||
|
const uint32_t offset = iter_.currentOffset();
|
||||||
|
masm.nopPatchableToCall(CallSiteDesc(offset, kind));
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Function prologue and epilogue.
|
// Function prologue and epilogue.
|
||||||
|
@ -2167,6 +2172,58 @@ class BaseCompiler
|
||||||
for (int32_t i = varLow_ ; i < varHigh_ ; i += 4)
|
for (int32_t i = varLow_ ; i < varHigh_ ; i += 4)
|
||||||
storeToFrameI32(scratch, i + 4);
|
storeToFrameI32(scratch, i + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (debugEnabled_)
|
||||||
|
insertBreakablePoint(CallSiteDesc::EnterFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveResult() {
|
||||||
|
MOZ_ASSERT(debugEnabled_);
|
||||||
|
size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
|
||||||
|
Address resultsAddress(StackPointer, debugFrameOffset + DebugFrame::offsetOfResults());
|
||||||
|
switch (func_.sig().ret()) {
|
||||||
|
case ExprType::Void:
|
||||||
|
break;
|
||||||
|
case ExprType::I32:
|
||||||
|
masm.store32(RegI32(ReturnReg), resultsAddress);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExprType::I64:
|
||||||
|
masm.store64(RegI64(ReturnReg64), resultsAddress);
|
||||||
|
break;
|
||||||
|
case ExprType::F64:
|
||||||
|
masm.storeDouble(RegF64(ReturnDoubleReg), resultsAddress);
|
||||||
|
break;
|
||||||
|
case ExprType::F32:
|
||||||
|
masm.storeFloat32(RegF32(ReturnFloat32Reg), resultsAddress);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Function return type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreResult() {
|
||||||
|
MOZ_ASSERT(debugEnabled_);
|
||||||
|
size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
|
||||||
|
Address resultsAddress(StackPointer, debugFrameOffset + DebugFrame::offsetOfResults());
|
||||||
|
switch (func_.sig().ret()) {
|
||||||
|
case ExprType::Void:
|
||||||
|
break;
|
||||||
|
case ExprType::I32:
|
||||||
|
masm.load32(resultsAddress, RegI32(ReturnReg));
|
||||||
|
break;
|
||||||
|
case ExprType::I64:
|
||||||
|
masm.load64(resultsAddress, RegI64(ReturnReg64));
|
||||||
|
break;
|
||||||
|
case ExprType::F64:
|
||||||
|
masm.loadDouble(resultsAddress, RegF64(ReturnDoubleReg));
|
||||||
|
break;
|
||||||
|
case ExprType::F32:
|
||||||
|
masm.loadFloat32(resultsAddress, RegF32(ReturnFloat32Reg));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Function return type");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool endFunction() {
|
bool endFunction() {
|
||||||
|
@ -2188,6 +2245,14 @@ class BaseCompiler
|
||||||
|
|
||||||
masm.bind(&returnLabel_);
|
masm.bind(&returnLabel_);
|
||||||
|
|
||||||
|
if (debugEnabled_) {
|
||||||
|
// Store and reload the return value from DebugFrame::return so that
|
||||||
|
// it can be clobbered, and/or modified by the debug trap.
|
||||||
|
saveResult();
|
||||||
|
insertBreakablePoint(CallSiteDesc::LeaveFrame);
|
||||||
|
restoreResult();
|
||||||
|
}
|
||||||
|
|
||||||
// Restore the TLS register in case it was overwritten by the function.
|
// Restore the TLS register in case it was overwritten by the function.
|
||||||
loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||||
|
|
||||||
|
|
|
@ -346,7 +346,8 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
|
||||||
kind_(kind)
|
kind_(kind)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(begin_ <= end_);
|
MOZ_ASSERT(begin_ <= end_);
|
||||||
MOZ_ASSERT(kind_ == Entry || kind_ == Inline || kind_ == FarJumpIsland);
|
MOZ_ASSERT(kind_ == Entry || kind_ == Inline ||
|
||||||
|
kind_ == FarJumpIsland || kind_ == DebugTrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
|
CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
|
||||||
|
@ -458,7 +459,7 @@ Metadata::serializedSize() const
|
||||||
uint8_t*
|
uint8_t*
|
||||||
Metadata::serialize(uint8_t* cursor) const
|
Metadata::serialize(uint8_t* cursor) const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!debugEnabled);
|
MOZ_ASSERT(!debugEnabled && debugTrapFarJumpOffsets.empty());
|
||||||
cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
|
cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
|
||||||
cursor = SerializeVector(cursor, funcImports);
|
cursor = SerializeVector(cursor, funcImports);
|
||||||
cursor = SerializeVector(cursor, funcExports);
|
cursor = SerializeVector(cursor, funcExports);
|
||||||
|
@ -496,6 +497,7 @@ Metadata::deserialize(const uint8_t* cursor)
|
||||||
(cursor = DeserializePodVector(cursor, &customSections)) &&
|
(cursor = DeserializePodVector(cursor, &customSections)) &&
|
||||||
(cursor = filename.deserialize(cursor));
|
(cursor = filename.deserialize(cursor));
|
||||||
debugEnabled = false;
|
debugEnabled = false;
|
||||||
|
debugTrapFarJumpOffsets.clear();
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,6 +575,7 @@ Code::Code(UniqueCodeSegment segment,
|
||||||
: segment_(Move(segment)),
|
: segment_(Move(segment)),
|
||||||
metadata_(&metadata),
|
metadata_(&metadata),
|
||||||
maybeBytecode_(maybeBytecode),
|
maybeBytecode_(maybeBytecode),
|
||||||
|
enterAndLeaveFrameTrapsCounter_(0),
|
||||||
profilingEnabled_(false)
|
profilingEnabled_(false)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(metadata_->debugEnabled, maybeBytecode);
|
MOZ_ASSERT_IF(metadata_->debugEnabled, maybeBytecode);
|
||||||
|
@ -820,6 +823,52 @@ Code::ensureProfilingState(JSRuntime* rt, bool newProfilingEnabled)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Code::toggleDebugTrap(uint32_t offset, bool enabled)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(offset);
|
||||||
|
uint8_t* trap = segment_->base() + offset;
|
||||||
|
const Uint32Vector& farJumpOffsets = metadata_->debugTrapFarJumpOffsets;
|
||||||
|
if (enabled) {
|
||||||
|
MOZ_ASSERT(farJumpOffsets.length() > 0);
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < farJumpOffsets.length() && offset < farJumpOffsets[i])
|
||||||
|
i++;
|
||||||
|
if (i >= farJumpOffsets.length() ||
|
||||||
|
(i > 0 && offset - farJumpOffsets[i - 1] < farJumpOffsets[i] - offset))
|
||||||
|
i--;
|
||||||
|
uint8_t* farJump = segment_->base() + farJumpOffsets[i];
|
||||||
|
MacroAssembler::patchNopToCall(trap, farJump);
|
||||||
|
} else {
|
||||||
|
MacroAssembler::patchCallToNop(trap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Code::adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(metadata_->debugEnabled);
|
||||||
|
MOZ_ASSERT_IF(!enabled, enterAndLeaveFrameTrapsCounter_ > 0);
|
||||||
|
|
||||||
|
bool wasEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
|
||||||
|
if (enabled)
|
||||||
|
++enterAndLeaveFrameTrapsCounter_;
|
||||||
|
else
|
||||||
|
--enterAndLeaveFrameTrapsCounter_;
|
||||||
|
bool stillEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
|
||||||
|
if (wasEnabled == stillEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AutoWritableJitCode awjc(cx->runtime(), segment_->base(), segment_->codeLength());
|
||||||
|
AutoFlushICache afc("Code::adjustEnterAndLeaveFrameTrapsState");
|
||||||
|
AutoFlushICache::setRange(uintptr_t(segment_->base()), segment_->codeLength());
|
||||||
|
for (const CallSite& callSite : metadata_->callSites) {
|
||||||
|
if (callSite.kind() != CallSite::EnterFrame && callSite.kind() != CallSite::LeaveFrame)
|
||||||
|
continue;
|
||||||
|
toggleDebugTrap(callSite.returnAddressOffset(), stillEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Code::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
Code::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||||
Metadata::SeenSet* seenMetadata,
|
Metadata::SeenSet* seenMetadata,
|
||||||
|
|
|
@ -19,16 +19,19 @@
|
||||||
#ifndef wasm_code_h
|
#ifndef wasm_code_h
|
||||||
#define wasm_code_h
|
#define wasm_code_h
|
||||||
|
|
||||||
|
#include "js/HashTable.h"
|
||||||
#include "wasm/WasmTypes.h"
|
#include "wasm/WasmTypes.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
struct AsmJSMetadata;
|
struct AsmJSMetadata;
|
||||||
|
class WasmActivation;
|
||||||
|
|
||||||
namespace wasm {
|
namespace wasm {
|
||||||
|
|
||||||
struct LinkData;
|
struct LinkData;
|
||||||
struct Metadata;
|
struct Metadata;
|
||||||
|
class FrameIterator;
|
||||||
|
|
||||||
// A wasm CodeSegment owns the allocated executable code for a wasm module.
|
// A wasm CodeSegment owns the allocated executable code for a wasm module.
|
||||||
// This allocation also currently includes the global data segment, which allows
|
// This allocation also currently includes the global data segment, which allows
|
||||||
|
@ -240,6 +243,8 @@ 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
|
||||||
TrapExit, // calls C++ to report and jumps to throw stub
|
TrapExit, // calls C++ to report and jumps to throw stub
|
||||||
|
DebugTrap, // calls C++ to handle debug event such as
|
||||||
|
// enter/leave frame or breakpoint
|
||||||
FarJumpIsland, // inserted to connect otherwise out-of-range insns
|
FarJumpIsland, // inserted to connect otherwise out-of-range insns
|
||||||
Inline // stub that is jumped-to, not called, and thus
|
Inline // stub that is jumped-to, not called, and thus
|
||||||
// replaces/loses preceding innermost frame
|
// replaces/loses preceding innermost frame
|
||||||
|
@ -469,6 +474,7 @@ struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
|
||||||
|
|
||||||
// Debug-enabled code is not serialized.
|
// Debug-enabled code is not serialized.
|
||||||
bool debugEnabled;
|
bool debugEnabled;
|
||||||
|
Uint32Vector debugTrapFarJumpOffsets;
|
||||||
|
|
||||||
bool usesMemory() const { return UsesMemory(memoryUsage); }
|
bool usesMemory() const { return UsesMemory(memoryUsage); }
|
||||||
bool hasSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
|
bool hasSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
|
||||||
|
@ -574,8 +580,11 @@ class Code
|
||||||
const SharedBytes maybeBytecode_;
|
const SharedBytes maybeBytecode_;
|
||||||
UniqueGeneratedSourceMap maybeSourceMap_;
|
UniqueGeneratedSourceMap maybeSourceMap_;
|
||||||
CacheableCharsVector funcLabels_;
|
CacheableCharsVector funcLabels_;
|
||||||
|
uint32_t enterAndLeaveFrameTrapsCounter_;
|
||||||
bool profilingEnabled_;
|
bool profilingEnabled_;
|
||||||
|
|
||||||
|
void toggleDebugTrap(uint32_t offset, bool enabled);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Code(UniqueCodeSegment segment,
|
Code(UniqueCodeSegment segment,
|
||||||
const Metadata& metadata,
|
const Metadata& metadata,
|
||||||
|
@ -614,6 +623,12 @@ class Code
|
||||||
bool profilingEnabled() const { return profilingEnabled_; }
|
bool profilingEnabled() const { return profilingEnabled_; }
|
||||||
const char* profilingLabel(uint32_t funcIndex) const { return funcLabels_[funcIndex].get(); }
|
const char* profilingLabel(uint32_t funcIndex) const { return funcLabels_[funcIndex].get(); }
|
||||||
|
|
||||||
|
// The Code can track enter/leave frame events. Any such event triggers
|
||||||
|
// debug trap. The enter frame events enabled across all functions, but
|
||||||
|
// the leave frame events only for particular function.
|
||||||
|
|
||||||
|
void adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled);
|
||||||
|
|
||||||
// about:memory reporting:
|
// about:memory reporting:
|
||||||
|
|
||||||
void addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
void addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||||
|
|
|
@ -50,7 +50,7 @@ DebugFrame::observeFrame(JSContext* cx)
|
||||||
if (observing_)
|
if (observing_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO make sure wasm::Code onLeaveFrame traps are on
|
instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ true);
|
||||||
observing_ = true;
|
observing_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +60,6 @@ DebugFrame::leaveFrame(JSContext* cx)
|
||||||
if (!observing_)
|
if (!observing_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO make sure wasm::Code onLeaveFrame traps are off
|
instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ false);
|
||||||
observing_ = false;
|
observing_ = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,6 +151,7 @@ FrameIterator::settle()
|
||||||
case CodeRange::ImportJitExit:
|
case CodeRange::ImportJitExit:
|
||||||
case CodeRange::ImportInterpExit:
|
case CodeRange::ImportInterpExit:
|
||||||
case CodeRange::TrapExit:
|
case CodeRange::TrapExit:
|
||||||
|
case CodeRange::DebugTrap:
|
||||||
case CodeRange::Inline:
|
case CodeRange::Inline:
|
||||||
case CodeRange::FarJumpIsland:
|
case CodeRange::FarJumpIsland:
|
||||||
MOZ_CRASH("Should not encounter an exit during iteration");
|
MOZ_CRASH("Should not encounter an exit during iteration");
|
||||||
|
@ -240,6 +241,14 @@ FrameIterator::debugFrame() const
|
||||||
return static_cast<DebugFrame*>(buf);
|
return static_cast<DebugFrame*>(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CallSite*
|
||||||
|
FrameIterator::debugTrapCallsite() const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!done() && debugEnabled());
|
||||||
|
MOZ_ASSERT(callsite_->kind() == CallSite::EnterFrame || callsite_->kind() == CallSite::LeaveFrame);
|
||||||
|
return callsite_;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
// Prologue/epilogue code generation
|
// Prologue/epilogue code generation
|
||||||
|
|
||||||
|
@ -593,6 +602,7 @@ ProfilingFrameIterator::initFromFP()
|
||||||
case CodeRange::ImportJitExit:
|
case CodeRange::ImportJitExit:
|
||||||
case CodeRange::ImportInterpExit:
|
case CodeRange::ImportInterpExit:
|
||||||
case CodeRange::TrapExit:
|
case CodeRange::TrapExit:
|
||||||
|
case CodeRange::DebugTrap:
|
||||||
case CodeRange::Inline:
|
case CodeRange::Inline:
|
||||||
case CodeRange::FarJumpIsland:
|
case CodeRange::FarJumpIsland:
|
||||||
MOZ_CRASH("Unexpected CodeRange kind");
|
MOZ_CRASH("Unexpected CodeRange kind");
|
||||||
|
@ -721,6 +731,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
||||||
callerFP_ = nullptr;
|
callerFP_ = nullptr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CodeRange::DebugTrap:
|
||||||
case CodeRange::Inline: {
|
case CodeRange::Inline: {
|
||||||
// The throw stub clears WasmActivation::fp on it's way out.
|
// The throw stub clears WasmActivation::fp on it's way out.
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
|
@ -777,6 +788,7 @@ ProfilingFrameIterator::operator++()
|
||||||
case CodeRange::ImportJitExit:
|
case CodeRange::ImportJitExit:
|
||||||
case CodeRange::ImportInterpExit:
|
case CodeRange::ImportInterpExit:
|
||||||
case CodeRange::TrapExit:
|
case CodeRange::TrapExit:
|
||||||
|
case CodeRange::DebugTrap:
|
||||||
case CodeRange::Inline:
|
case CodeRange::Inline:
|
||||||
case CodeRange::FarJumpIsland:
|
case CodeRange::FarJumpIsland:
|
||||||
stackAddress_ = callerFP_;
|
stackAddress_ = callerFP_;
|
||||||
|
@ -803,6 +815,7 @@ ProfilingFrameIterator::label() const
|
||||||
const char* importInterpDescription = "slow FFI trampoline (in asm.js)";
|
const char* importInterpDescription = "slow FFI trampoline (in asm.js)";
|
||||||
const char* nativeDescription = "native call (in asm.js)";
|
const char* nativeDescription = "native call (in asm.js)";
|
||||||
const char* trapDescription = "trap handling (in asm.js)";
|
const char* trapDescription = "trap handling (in asm.js)";
|
||||||
|
const char* debugTrapDescription = "debug trap handling (in asm.js)";
|
||||||
|
|
||||||
switch (exitReason_) {
|
switch (exitReason_) {
|
||||||
case ExitReason::None:
|
case ExitReason::None:
|
||||||
|
@ -815,6 +828,8 @@ ProfilingFrameIterator::label() const
|
||||||
return nativeDescription;
|
return nativeDescription;
|
||||||
case ExitReason::Trap:
|
case ExitReason::Trap:
|
||||||
return trapDescription;
|
return trapDescription;
|
||||||
|
case ExitReason::DebugTrap:
|
||||||
|
return debugTrapDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (codeRange_->kind()) {
|
switch (codeRange_->kind()) {
|
||||||
|
@ -823,6 +838,7 @@ ProfilingFrameIterator::label() const
|
||||||
case CodeRange::ImportJitExit: return importJitDescription;
|
case CodeRange::ImportJitExit: return importJitDescription;
|
||||||
case CodeRange::ImportInterpExit: return importInterpDescription;
|
case CodeRange::ImportInterpExit: return importInterpDescription;
|
||||||
case CodeRange::TrapExit: return trapDescription;
|
case CodeRange::TrapExit: return trapDescription;
|
||||||
|
case CodeRange::DebugTrap: return debugTrapDescription;
|
||||||
case CodeRange::Inline: return "inline stub (in asm.js)";
|
case CodeRange::Inline: return "inline stub (in asm.js)";
|
||||||
case CodeRange::FarJumpIsland: return "interstitial (in asm.js)";
|
case CodeRange::FarJumpIsland: return "interstitial (in asm.js)";
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ class FrameIterator
|
||||||
Instance* instance() const;
|
Instance* instance() const;
|
||||||
bool debugEnabled() const;
|
bool debugEnabled() const;
|
||||||
DebugFrame* debugFrame() const;
|
DebugFrame* debugFrame() const;
|
||||||
|
const CallSite* debugTrapCallsite() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An ExitReason describes the possible reasons for leaving compiled wasm code
|
// An ExitReason describes the possible reasons for leaving compiled wasm code
|
||||||
|
@ -85,7 +86,8 @@ enum class ExitReason : uint32_t
|
||||||
ImportJit, // fast-path call directly into JIT code
|
ImportJit, // fast-path call directly into JIT code
|
||||||
ImportInterp, // slow-path call into C++ Invoke()
|
ImportInterp, // slow-path call into C++ Invoke()
|
||||||
Native, // call to native C++ code (e.g., Math.sin, ToInt32(), interrupt)
|
Native, // call to native C++ code (e.g., Math.sin, ToInt32(), interrupt)
|
||||||
Trap // call to trap handler for the trap in WasmActivation::trap
|
Trap, // call to trap handler for the trap in WasmActivation::trap
|
||||||
|
DebugTrap // call to debug trap handler
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterates over the frames of a single WasmActivation, given an
|
// Iterates over the frames of a single WasmActivation, given an
|
||||||
|
|
|
@ -378,6 +378,28 @@ ModuleGenerator::patchCallSites(TrapExitOffsetArray* maybeTrapExits)
|
||||||
masm_.patchCall(callerOffset, *existingTrapFarJumps[cs.trap()]);
|
masm_.patchCall(callerOffset, *existingTrapFarJumps[cs.trap()]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CallSiteDesc::EnterFrame:
|
||||||
|
case CallSiteDesc::LeaveFrame: {
|
||||||
|
Uint32Vector& jumps = metadata_->debugTrapFarJumpOffsets;
|
||||||
|
if (jumps.empty() ||
|
||||||
|
uint32_t(abs(int32_t(jumps.back()) - int32_t(callerOffset))) >= JumpRange())
|
||||||
|
{
|
||||||
|
Offsets offsets;
|
||||||
|
offsets.begin = masm_.currentOffset();
|
||||||
|
uint32_t jumpOffset = masm_.farJumpWithPatch().offset();
|
||||||
|
offsets.end = masm_.currentOffset();
|
||||||
|
if (masm_.oom())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!metadata_->codeRanges.emplaceBack(CodeRange::FarJumpIsland, offsets))
|
||||||
|
return false;
|
||||||
|
if (!debugTrapFarJumps_.emplaceBack(jumpOffset))
|
||||||
|
return false;
|
||||||
|
if (!jumps.emplaceBack(offsets.begin))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +407,7 @@ ModuleGenerator::patchCallSites(TrapExitOffsetArray* maybeTrapExits)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits)
|
ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits, const Offsets& debugTrapStub)
|
||||||
{
|
{
|
||||||
for (CallThunk& callThunk : metadata_->callThunks) {
|
for (CallThunk& callThunk : metadata_->callThunks) {
|
||||||
uint32_t funcIndex = callThunk.u.funcIndex;
|
uint32_t funcIndex = callThunk.u.funcIndex;
|
||||||
|
@ -397,6 +419,9 @@ ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits)
|
||||||
for (const TrapFarJump& farJump : masm_.trapFarJumps())
|
for (const TrapFarJump& farJump : masm_.trapFarJumps())
|
||||||
masm_.patchFarJump(farJump.jump, trapExits[farJump.trap].begin);
|
masm_.patchFarJump(farJump.jump, trapExits[farJump.trap].begin);
|
||||||
|
|
||||||
|
for (uint32_t debugTrapFarJump : debugTrapFarJumps_)
|
||||||
|
masm_.patchFarJump(CodeOffset(debugTrapFarJump), debugTrapStub.begin);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,6 +537,7 @@ ModuleGenerator::finishCodegen()
|
||||||
Offsets unalignedAccessExit;
|
Offsets unalignedAccessExit;
|
||||||
Offsets interruptExit;
|
Offsets interruptExit;
|
||||||
Offsets throwStub;
|
Offsets throwStub;
|
||||||
|
Offsets debugTrapStub;
|
||||||
|
|
||||||
{
|
{
|
||||||
TempAllocator alloc(&lifo_);
|
TempAllocator alloc(&lifo_);
|
||||||
|
@ -539,6 +565,7 @@ ModuleGenerator::finishCodegen()
|
||||||
unalignedAccessExit = GenerateUnalignedExit(masm, &throwLabel);
|
unalignedAccessExit = GenerateUnalignedExit(masm, &throwLabel);
|
||||||
interruptExit = GenerateInterruptExit(masm, &throwLabel);
|
interruptExit = GenerateInterruptExit(masm, &throwLabel);
|
||||||
throwStub = GenerateThrowStub(masm, &throwLabel);
|
throwStub = GenerateThrowStub(masm, &throwLabel);
|
||||||
|
debugTrapStub = GenerateDebugTrapStub(masm, &throwLabel);
|
||||||
|
|
||||||
if (masm.oom() || !masm_.asmMergeWith(masm))
|
if (masm.oom() || !masm_.asmMergeWith(masm))
|
||||||
return false;
|
return false;
|
||||||
|
@ -588,6 +615,10 @@ ModuleGenerator::finishCodegen()
|
||||||
if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, throwStub))
|
if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, throwStub))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
debugTrapStub.offsetBy(offsetInWhole);
|
||||||
|
if (!metadata_->codeRanges.emplaceBack(CodeRange::DebugTrap, debugTrapStub))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Fill in LinkData with the offsets of these stubs.
|
// Fill in LinkData with the offsets of these stubs.
|
||||||
|
|
||||||
linkData_.outOfBoundsOffset = outOfBoundsExit.begin;
|
linkData_.outOfBoundsOffset = outOfBoundsExit.begin;
|
||||||
|
@ -600,7 +631,7 @@ ModuleGenerator::finishCodegen()
|
||||||
if (!patchCallSites(&trapExits))
|
if (!patchCallSites(&trapExits))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!patchFarJumps(trapExits))
|
if (!patchFarJumps(trapExits, debugTrapStub))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Code-generation is complete!
|
// Code-generation is complete!
|
||||||
|
@ -1151,6 +1182,7 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
|
||||||
metadata_->codeRanges.podResizeToFit();
|
metadata_->codeRanges.podResizeToFit();
|
||||||
metadata_->callSites.podResizeToFit();
|
metadata_->callSites.podResizeToFit();
|
||||||
metadata_->callThunks.podResizeToFit();
|
metadata_->callThunks.podResizeToFit();
|
||||||
|
metadata_->debugTrapFarJumpOffsets.podResizeToFit();
|
||||||
|
|
||||||
// For asm.js, the tables vector is over-allocated (to avoid resize during
|
// For asm.js, the tables vector is over-allocated (to avoid resize during
|
||||||
// parallel copilation). Shrink it back down to fit.
|
// parallel copilation). Shrink it back down to fit.
|
||||||
|
@ -1168,6 +1200,15 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Assert debugTrapFarJumpOffsets are sorted.
|
||||||
|
#ifdef DEBUG
|
||||||
|
uint32_t lastOffset = 0;
|
||||||
|
for (uint32_t debugTrapFarJumpOffset : metadata_->debugTrapFarJumpOffsets) {
|
||||||
|
MOZ_ASSERT(debugTrapFarJumpOffset >= lastOffset);
|
||||||
|
lastOffset = debugTrapFarJumpOffset;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!finishLinkData(code))
|
if (!finishLinkData(code))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|
|
@ -234,6 +234,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||||
Uint32Set exportedFuncs_;
|
Uint32Set exportedFuncs_;
|
||||||
uint32_t lastPatchedCallsite_;
|
uint32_t lastPatchedCallsite_;
|
||||||
uint32_t startOfUnpatchedCallsites_;
|
uint32_t startOfUnpatchedCallsites_;
|
||||||
|
Uint32Vector debugTrapFarJumps_;
|
||||||
|
|
||||||
// Parallel compilation
|
// Parallel compilation
|
||||||
bool parallel_;
|
bool parallel_;
|
||||||
|
@ -254,7 +255,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||||
const CodeRange& funcCodeRange(uint32_t funcIndex) const;
|
const CodeRange& funcCodeRange(uint32_t funcIndex) const;
|
||||||
uint32_t numFuncImports() const;
|
uint32_t numFuncImports() const;
|
||||||
MOZ_MUST_USE bool patchCallSites(TrapExitOffsetArray* maybeTrapExits = nullptr);
|
MOZ_MUST_USE bool patchCallSites(TrapExitOffsetArray* maybeTrapExits = nullptr);
|
||||||
MOZ_MUST_USE bool patchFarJumps(const TrapExitOffsetArray& trapExits);
|
MOZ_MUST_USE bool patchFarJumps(const TrapExitOffsetArray& trapExits, const Offsets& debugTrapStub);
|
||||||
MOZ_MUST_USE bool finishTask(CompileTask* task);
|
MOZ_MUST_USE bool finishTask(CompileTask* task);
|
||||||
MOZ_MUST_USE bool finishOutstandingTask();
|
MOZ_MUST_USE bool finishOutstandingTask();
|
||||||
MOZ_MUST_USE bool finishFuncExports();
|
MOZ_MUST_USE bool finishFuncExports();
|
||||||
|
|
|
@ -329,7 +329,8 @@ Instance::Instance(JSContext* cx,
|
||||||
object_(object),
|
object_(object),
|
||||||
code_(Move(code)),
|
code_(Move(code)),
|
||||||
memory_(memory),
|
memory_(memory),
|
||||||
tables_(Move(tables))
|
tables_(Move(tables)),
|
||||||
|
enterFrameTrapsEnabled_(false)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(funcImports.length() == metadata().funcImports.length());
|
MOZ_ASSERT(funcImports.length() == metadata().funcImports.length());
|
||||||
MOZ_ASSERT(tables_.length() == metadata().tables.length());
|
MOZ_ASSERT(tables_.length() == metadata().tables.length());
|
||||||
|
@ -832,6 +833,16 @@ Instance::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Instance::ensureEnterFrameTrapsState(JSContext* cx, bool enabled)
|
||||||
|
{
|
||||||
|
if (enterFrameTrapsEnabled_ == enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
code_->adjustEnterAndLeaveFrameTrapsState(cx, enabled);
|
||||||
|
enterFrameTrapsEnabled_ = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||||
Metadata::SeenSet* seenMetadata,
|
Metadata::SeenSet* seenMetadata,
|
||||||
|
|
|
@ -41,6 +41,7 @@ class Instance
|
||||||
GCPtrWasmMemoryObject memory_;
|
GCPtrWasmMemoryObject memory_;
|
||||||
SharedTableVector tables_;
|
SharedTableVector tables_;
|
||||||
TlsData tlsData_;
|
TlsData tlsData_;
|
||||||
|
bool enterFrameTrapsEnabled_;
|
||||||
|
|
||||||
// Internal helpers:
|
// Internal helpers:
|
||||||
const void** addressOfSigId(const SigIdDesc& sigId) const;
|
const void** addressOfSigId(const SigIdDesc& sigId) const;
|
||||||
|
@ -124,6 +125,11 @@ class Instance
|
||||||
|
|
||||||
MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled);
|
MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled);
|
||||||
|
|
||||||
|
// Debug support:
|
||||||
|
bool debugEnabled() const { return code_->metadata().debugEnabled; }
|
||||||
|
bool enterFrameTrapsEnabled() const { return enterFrameTrapsEnabled_; }
|
||||||
|
void ensureEnterFrameTrapsState(JSContext* cx, bool enabled);
|
||||||
|
|
||||||
// about:memory reporting:
|
// about:memory reporting:
|
||||||
|
|
||||||
void addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
void addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||||
|
|
|
@ -946,6 +946,10 @@ static const LiveRegisterSet AllRegsExceptSP(
|
||||||
GeneralRegisterSet(Registers::AllMask & ~(uint32_t(1) << Registers::StackPointer)),
|
GeneralRegisterSet(Registers::AllMask & ~(uint32_t(1) << Registers::StackPointer)),
|
||||||
FloatRegisterSet(FloatRegisters::AllMask));
|
FloatRegisterSet(FloatRegisters::AllMask));
|
||||||
|
|
||||||
|
static const LiveRegisterSet AllAllocatableRegs = LiveRegisterSet(
|
||||||
|
GeneralRegisterSet(Registers::AllocatableMask),
|
||||||
|
FloatRegisterSet(FloatRegisters::AllMask));
|
||||||
|
|
||||||
// The async interrupt-callback exit is called from arbitrarily-interrupted wasm
|
// The async interrupt-callback exit is called from arbitrarily-interrupted wasm
|
||||||
// code. That means we must first save *all* registers and restore *all*
|
// code. That means we must first save *all* registers and restore *all*
|
||||||
// registers (except the stack pointer) when we resume. The address to resume to
|
// registers (except the stack pointer) when we resume. The address to resume to
|
||||||
|
@ -1127,6 +1131,11 @@ wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
|
||||||
Offsets offsets;
|
Offsets offsets;
|
||||||
offsets.begin = masm.currentOffset();
|
offsets.begin = masm.currentOffset();
|
||||||
|
|
||||||
|
masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
|
||||||
|
if (ShadowStackSpace)
|
||||||
|
masm.subFromStackPtr(Imm32(ShadowStackSpace));
|
||||||
|
masm.call(SymbolicAddress::HandleDebugThrow);
|
||||||
|
|
||||||
// We are about to pop all frames in this WasmActivation. Set fp to null to
|
// We are about to pop all frames in this WasmActivation. Set fp to null to
|
||||||
// maintain the invariant that fp is either null or pointing to a valid
|
// maintain the invariant that fp is either null or pointing to a valid
|
||||||
// frame.
|
// frame.
|
||||||
|
@ -1146,3 +1155,50 @@ wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
|
||||||
offsets.end = masm.currentOffset();
|
offsets.end = masm.currentOffset();
|
||||||
return offsets;
|
return offsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a stub that handle toggable enter/leave frame traps or breakpoints.
|
||||||
|
// The trap records frame pointer (via GenerateExitPrologue) and saves most of
|
||||||
|
// registers to not affect the code generated by WasmBaselineCompile.
|
||||||
|
Offsets
|
||||||
|
wasm::GenerateDebugTrapStub(MacroAssembler& masm, Label* throwLabel)
|
||||||
|
{
|
||||||
|
masm.haltingAlign(CodeAlignment);
|
||||||
|
|
||||||
|
masm.setFramePushed(0);
|
||||||
|
|
||||||
|
ProfilingOffsets offsets;
|
||||||
|
GenerateExitPrologue(masm, 0, ExitReason::DebugTrap, &offsets);
|
||||||
|
|
||||||
|
// Save all registers used between baseline compiler operations.
|
||||||
|
masm.PushRegsInMask(AllAllocatableRegs);
|
||||||
|
|
||||||
|
uint32_t framePushed = masm.framePushed();
|
||||||
|
|
||||||
|
// This method might be called with unaligned stack -- aligning and
|
||||||
|
// saving old stack pointer at the top.
|
||||||
|
Register scratch = ABINonArgReturnReg0;
|
||||||
|
masm.moveStackPtrTo(scratch);
|
||||||
|
masm.subFromStackPtr(Imm32(sizeof(intptr_t)));
|
||||||
|
masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
|
||||||
|
masm.storePtr(scratch, Address(masm.getStackPointer(), 0));
|
||||||
|
|
||||||
|
if (ShadowStackSpace)
|
||||||
|
masm.subFromStackPtr(Imm32(ShadowStackSpace));
|
||||||
|
masm.assertStackAlignment(ABIStackAlignment);
|
||||||
|
masm.call(SymbolicAddress::HandleDebugTrap);
|
||||||
|
|
||||||
|
masm.branchIfFalseBool(ReturnReg, throwLabel);
|
||||||
|
|
||||||
|
if (ShadowStackSpace)
|
||||||
|
masm.addToStackPtr(Imm32(ShadowStackSpace));
|
||||||
|
masm.Pop(scratch);
|
||||||
|
masm.moveToStackPtr(scratch);
|
||||||
|
|
||||||
|
masm.setFramePushed(framePushed);
|
||||||
|
masm.PopRegsInMask(AllAllocatableRegs);
|
||||||
|
|
||||||
|
GenerateExitEpilogue(masm, 0, ExitReason::DebugTrap, &offsets);
|
||||||
|
|
||||||
|
offsets.end = masm.currentOffset();
|
||||||
|
return offsets;
|
||||||
|
}
|
||||||
|
|
|
@ -58,6 +58,10 @@ GenerateInterruptExit(jit::MacroAssembler& masm, jit::Label* throwLabel);
|
||||||
extern Offsets
|
extern Offsets
|
||||||
GenerateThrowStub(jit::MacroAssembler& masm, jit::Label* throwLabel);
|
GenerateThrowStub(jit::MacroAssembler& masm, jit::Label* throwLabel);
|
||||||
|
|
||||||
|
extern Offsets
|
||||||
|
GenerateDebugTrapStub(jit::MacroAssembler& masm, jit::Label* throwLabel);
|
||||||
|
|
||||||
|
|
||||||
} // namespace wasm
|
} // namespace wasm
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,52 @@ WasmHandleExecutionInterrupt()
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
WasmHandleDebugTrap()
|
||||||
|
{
|
||||||
|
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||||
|
JSContext* cx = activation->cx();
|
||||||
|
|
||||||
|
FrameIterator iter(*activation);
|
||||||
|
MOZ_ASSERT(iter.debugEnabled());
|
||||||
|
const CallSite* site = iter.debugTrapCallsite();
|
||||||
|
MOZ_ASSERT(site);
|
||||||
|
if (site->kind() == CallSite::EnterFrame) {
|
||||||
|
if (!iter.instance()->enterFrameTrapsEnabled())
|
||||||
|
return true;
|
||||||
|
DebugFrame* frame = iter.debugFrame();
|
||||||
|
frame->setIsDebuggee();
|
||||||
|
frame->observeFrame(cx);
|
||||||
|
// TODO call onEnterFrame
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (site->kind() == CallSite::LeaveFrame) {
|
||||||
|
DebugFrame* frame = iter.debugFrame();
|
||||||
|
// TODO call onLeaveFrame
|
||||||
|
frame->leaveFrame(cx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO baseline debug traps
|
||||||
|
MOZ_CRASH();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
WasmHandleDebugThrow()
|
||||||
|
{
|
||||||
|
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||||
|
JSContext* cx = activation->cx();
|
||||||
|
|
||||||
|
for (FrameIterator iter(*activation); !iter.done(); ++iter) {
|
||||||
|
if (!iter.debugEnabled())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DebugFrame* frame = iter.debugFrame();
|
||||||
|
// TODO call onExceptionUnwind and onLeaveFrame
|
||||||
|
frame->leaveFrame(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WasmReportTrap(int32_t trapIndex)
|
WasmReportTrap(int32_t trapIndex)
|
||||||
{
|
{
|
||||||
|
@ -277,6 +323,10 @@ wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
|
||||||
return FuncCast(WasmReportOverRecursed, Args_General0);
|
return FuncCast(WasmReportOverRecursed, Args_General0);
|
||||||
case SymbolicAddress::HandleExecutionInterrupt:
|
case SymbolicAddress::HandleExecutionInterrupt:
|
||||||
return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
|
return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
|
||||||
|
case SymbolicAddress::HandleDebugTrap:
|
||||||
|
return FuncCast(WasmHandleDebugTrap, Args_General0);
|
||||||
|
case SymbolicAddress::HandleDebugThrow:
|
||||||
|
return FuncCast(WasmHandleDebugThrow, Args_General0);
|
||||||
case SymbolicAddress::ReportTrap:
|
case SymbolicAddress::ReportTrap:
|
||||||
return FuncCast(WasmReportTrap, Args_General1);
|
return FuncCast(WasmReportTrap, Args_General1);
|
||||||
case SymbolicAddress::ReportOutOfBounds:
|
case SymbolicAddress::ReportOutOfBounds:
|
||||||
|
|
|
@ -890,14 +890,16 @@ struct TrapOffset
|
||||||
|
|
||||||
class CallSiteDesc
|
class CallSiteDesc
|
||||||
{
|
{
|
||||||
uint32_t lineOrBytecode_ : 30;
|
uint32_t lineOrBytecode_ : 29;
|
||||||
uint32_t kind_ : 2;
|
uint32_t kind_ : 3;
|
||||||
public:
|
public:
|
||||||
enum Kind {
|
enum Kind {
|
||||||
Func, // pc-relative call to a specific function
|
Func, // pc-relative call to a specific function
|
||||||
Dynamic, // dynamic callee called via register
|
Dynamic, // dynamic callee called via register
|
||||||
Symbolic, // call to a single symbolic callee
|
Symbolic, // call to a single symbolic callee
|
||||||
TrapExit // call to a trap exit
|
TrapExit, // call to a trap exit
|
||||||
|
EnterFrame, // call to a enter frame handler
|
||||||
|
LeaveFrame // call to a leave frame handler
|
||||||
};
|
};
|
||||||
CallSiteDesc() {}
|
CallSiteDesc() {}
|
||||||
explicit CallSiteDesc(Kind kind)
|
explicit CallSiteDesc(Kind kind)
|
||||||
|
@ -1014,6 +1016,8 @@ enum class SymbolicAddress
|
||||||
InterruptUint32,
|
InterruptUint32,
|
||||||
ReportOverRecursed,
|
ReportOverRecursed,
|
||||||
HandleExecutionInterrupt,
|
HandleExecutionInterrupt,
|
||||||
|
HandleDebugTrap,
|
||||||
|
HandleDebugThrow,
|
||||||
ReportTrap,
|
ReportTrap,
|
||||||
ReportOutOfBounds,
|
ReportOutOfBounds,
|
||||||
ReportUnalignedAccess,
|
ReportUnalignedAccess,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче