mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-12 10:40:12 +00:00
Bug 1360211: Merge WasmActivation into JitActivation and make wasm->jit calls faster; r=luke, r=jandem
This is a folded patch containing the patches in the bug named as: - Handle the "untrusted FP" in wasm->jit exit; - Handle failures in wasm->jit fast path; - Profiling frame iteration; MozReview-Commit-ID: 2hs8yyKGQ9v --HG-- extra : rebase_source : e71f6bb5558a4c78c7ae4ce0b6b8cfa11d0b10f7
This commit is contained in:
parent
5335ccdcde
commit
8f85f6c936
@ -46,9 +46,17 @@ struct ForEachTrackedOptimizationTypeInfoOp;
|
||||
// contents to become out of date.
|
||||
class MOZ_NON_PARAM JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
{
|
||||
public:
|
||||
enum class Kind : bool {
|
||||
JSJit,
|
||||
Wasm
|
||||
};
|
||||
|
||||
private:
|
||||
JSContext* cx_;
|
||||
uint32_t sampleBufferGen_;
|
||||
js::Activation* activation_;
|
||||
Kind kind_;
|
||||
|
||||
static const unsigned StorageSpace = 8 * sizeof(void*);
|
||||
alignas(void*) unsigned char storage_[StorageSpace];
|
||||
@ -79,6 +87,7 @@ class MOZ_NON_PARAM JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
return *static_cast<const js::jit::JitProfilingFrameIterator*>(storage());
|
||||
}
|
||||
|
||||
void settleFrames();
|
||||
void settle();
|
||||
|
||||
bool hasSampleBufferGen() const {
|
||||
|
@ -40,7 +40,8 @@ SizeOfFramePrefix = {
|
||||
'JitFrame_BaselineJS': 'JitFrameLayout',
|
||||
'JitFrame_BaselineStub': 'BaselineStubFrameLayout',
|
||||
'JitFrame_IonStub': 'JitStubFrameLayout',
|
||||
'JitFrame_Entry': 'JitFrameLayout',
|
||||
'JitFrame_CppToJSJit': 'JitFrameLayout',
|
||||
'JitFrame_WasmToJSJit': 'JitFrameLayout',
|
||||
'JitFrame_Rectifier': 'RectifierFrameLayout',
|
||||
'JitFrame_IonAccessorIC': 'IonAccessorICFrameLayout',
|
||||
'JitFrame_IonICCall': 'IonICCallFrameLayout',
|
||||
@ -367,7 +368,7 @@ class UnwinderState(object):
|
||||
self.typecache.FRAME_HEADER_SIZE_MASK)
|
||||
header_size = header_size * self.typecache.void_starstar.sizeof
|
||||
frame_type = long(value & self.typecache.FRAMETYPE_MASK)
|
||||
if frame_type == self.typecache.JitFrame_Entry:
|
||||
if frame_type == self.typecache.JitFrame_CppToJSJit:
|
||||
# Trampoline-x64.cpp pushes a JitFrameLayout object, but
|
||||
# the stack pointer is actually adjusted as if a
|
||||
# CommonFrameLayout object was pushed.
|
||||
@ -451,7 +452,7 @@ class UnwinderState(object):
|
||||
def unwind_entry_frame(self, pc, pending_frame):
|
||||
sp = self.next_sp
|
||||
# Notify the frame filter.
|
||||
self.add_frame(sp, name = 'JitFrame_Entry')
|
||||
self.add_frame(sp, name = 'JitFrame_CppToJSJit')
|
||||
# Make an unwind_info for the per-architecture code to fill in.
|
||||
frame_id = SpiderMonkeyFrameId(sp, pc)
|
||||
unwind_info = pending_frame.create_unwind_info(frame_id)
|
||||
@ -472,7 +473,7 @@ class UnwinderState(object):
|
||||
return None
|
||||
|
||||
if self.next_sp is not None:
|
||||
if self.next_type == self.typecache.JitFrame_Entry:
|
||||
if self.next_type == self.typecache.JitFrame_CppToJSJit:
|
||||
return self.unwind_entry_frame(pc, pending_frame)
|
||||
return self.unwind_ordinary(pc, pending_frame)
|
||||
# Maybe we've found an exit frame. FIXME I currently don't
|
||||
|
@ -27,7 +27,7 @@ def do_unwinder_test():
|
||||
first = False
|
||||
elif frame.function() == "<<JitFrame_Exit>>":
|
||||
found_exit = True
|
||||
elif frame.function() == "<<JitFrame_Entry>>":
|
||||
elif frame.function() == "<<JitFrame_CppToJSJit>>":
|
||||
found_entry = True
|
||||
elif frame.function() == "main":
|
||||
found_main = True
|
||||
|
@ -102,7 +102,12 @@ static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2;
|
||||
|
||||
// This address is a magic number made to cause crashes while indicating that we
|
||||
// are making an attempt to mark the stack during a bailout.
|
||||
static uint8_t* const FAKE_EXITFP_FOR_BAILOUT = reinterpret_cast<uint8_t*>(0xba1);
|
||||
static const uint32_t FAKE_EXITFP_FOR_BAILOUT_ADDR = 0xba2;
|
||||
static uint8_t* const FAKE_EXITFP_FOR_BAILOUT =
|
||||
reinterpret_cast<uint8_t*>(FAKE_EXITFP_FOR_BAILOUT_ADDR);
|
||||
|
||||
static_assert(!(FAKE_EXITFP_FOR_BAILOUT_ADDR & JitActivation::ExitFpWasmBit),
|
||||
"FAKE_EXITFP_FOR_BAILOUT could be mistaken as a low-bit tagged wasm exit fp");
|
||||
|
||||
// BailoutStack is an architecture specific pointer to the stack, given by the
|
||||
// bailout handler.
|
||||
|
@ -362,7 +362,7 @@ struct BaselineStackBuilder
|
||||
// in the baseline frame is meaningless, since Ion saves all registers
|
||||
// before calling other ion frames, and the entry frame saves all
|
||||
// registers too.
|
||||
if (type == JitFrame_IonJS || type == JitFrame_Entry || type == JitFrame_IonICCall)
|
||||
if (JSJitFrameIter::isEntry(type) || type == JitFrame_IonJS || type == JitFrame_IonICCall)
|
||||
return nullptr;
|
||||
|
||||
// BaselineStub - Baseline calling into Ion.
|
||||
@ -1515,14 +1515,14 @@ jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation,
|
||||
// The caller of the top frame must be one of the following:
|
||||
// IonJS - Ion calling into Ion.
|
||||
// BaselineStub - Baseline calling into Ion.
|
||||
// Entry - Interpreter or other calling into Ion.
|
||||
// Entry / WasmToJSJit - Interpreter or other (wasm) calling into Ion.
|
||||
// Rectifier - Arguments rectifier calling into Ion.
|
||||
MOZ_ASSERT(iter.isBailoutJS());
|
||||
#if defined(DEBUG) || defined(JS_JITSPEW)
|
||||
FrameType prevFrameType = iter.prevType();
|
||||
MOZ_ASSERT(prevFrameType == JitFrame_IonJS ||
|
||||
MOZ_ASSERT(JSJitFrameIter::isEntry(prevFrameType) ||
|
||||
prevFrameType == JitFrame_IonJS ||
|
||||
prevFrameType == JitFrame_BaselineStub ||
|
||||
prevFrameType == JitFrame_Entry ||
|
||||
prevFrameType == JitFrame_Rectifier ||
|
||||
prevFrameType == JitFrame_IonICCall);
|
||||
#endif
|
||||
|
@ -93,7 +93,7 @@ class DebugModeOSRVolatileJitFrameIter : public JitFrameIter
|
||||
|
||||
public:
|
||||
explicit DebugModeOSRVolatileJitFrameIter(JSContext* cx)
|
||||
: JitFrameIter(cx->activation())
|
||||
: JitFrameIter(cx->activation()->asJit())
|
||||
{
|
||||
stack = &cx->liveVolatileJitFrameIter_.ref();
|
||||
prev = *stack;
|
||||
|
@ -3005,9 +3005,12 @@ InvalidateActivation(FreeOp* fop, const JitActivationIterator& activations, bool
|
||||
case JitFrame_IonICCall:
|
||||
JitSpew(JitSpew_IonInvalidate, "#%zu ion IC call frame @ %p", frameno, frame.fp());
|
||||
break;
|
||||
case JitFrame_Entry:
|
||||
case JitFrame_CppToJSJit:
|
||||
JitSpew(JitSpew_IonInvalidate, "#%zu entry frame @ %p", frameno, frame.fp());
|
||||
break;
|
||||
case JitFrame_WasmToJSJit:
|
||||
JitSpew(JitSpew_IonInvalidate, "#%zu wasm frames @ %p", frameno, frame.fp());
|
||||
break;
|
||||
}
|
||||
#endif // JS_JITSPEW
|
||||
|
||||
|
@ -153,15 +153,15 @@ JSJitFrameIter::prevFp() const
|
||||
void
|
||||
JSJitFrameIter::operator++()
|
||||
{
|
||||
MOZ_ASSERT(type_ != JitFrame_Entry);
|
||||
MOZ_ASSERT(!isEntry());
|
||||
|
||||
frameSize_ = prevFrameLocalSize();
|
||||
cachedSafepointIndex_ = nullptr;
|
||||
|
||||
// If the next frame is the entry frame, just exit. Don't update current_,
|
||||
// since the entry and first frames overlap.
|
||||
if (current()->prevType() == JitFrame_Entry) {
|
||||
type_ = JitFrame_Entry;
|
||||
if (isEntry(current()->prevType())) {
|
||||
type_ = current()->prevType();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -337,7 +337,7 @@ void
|
||||
JSJitFrameIter::dump() const
|
||||
{
|
||||
switch (type_) {
|
||||
case JitFrame_Entry:
|
||||
case JitFrame_CppToJSJit:
|
||||
fprintf(stderr, " Entry frame\n");
|
||||
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
|
||||
break;
|
||||
@ -368,6 +368,10 @@ JSJitFrameIter::dump() const
|
||||
fprintf(stderr, " Ion IC call\n");
|
||||
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
|
||||
break;
|
||||
case JitFrame_WasmToJSJit:
|
||||
fprintf(stderr, " Fast wasm-to-JS entry frame\n");
|
||||
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
|
||||
break;
|
||||
case JitFrame_Exit:
|
||||
fprintf(stderr, " Exit frame\n");
|
||||
break;
|
||||
|
@ -36,7 +36,7 @@ enum FrameType
|
||||
|
||||
// The entry frame is the initial prologue block transitioning from the VM
|
||||
// into the Ion world.
|
||||
JitFrame_Entry,
|
||||
JitFrame_CppToJSJit,
|
||||
|
||||
// A rectifier frame sits in between two JS frames, adapting argc != nargs
|
||||
// mismatches in calls.
|
||||
@ -55,6 +55,11 @@ enum FrameType
|
||||
// frame is always the last frame in a JitActivation iff the bailout frame
|
||||
// information is recorded on the JitActivation.
|
||||
JitFrame_Bailout,
|
||||
|
||||
// A wasm to JS frame is constructed during fast calls from wasm to the JS
|
||||
// jits, used as a marker to interleave JS jit and wasm frames. From the
|
||||
// point of view of JS JITs, this is just another kind of entry frame.
|
||||
JitFrame_WasmToJSJit,
|
||||
};
|
||||
|
||||
enum ReadFrameArgsBehavior {
|
||||
@ -173,9 +178,13 @@ class JSJitFrameIter
|
||||
bool isBareExit() const;
|
||||
template <typename T> bool isExitFrameLayout() const;
|
||||
|
||||
bool isEntry() const {
|
||||
return type_ == JitFrame_Entry;
|
||||
static bool isEntry(FrameType type) {
|
||||
return type == JitFrame_CppToJSJit || type == JitFrame_WasmToJSJit;
|
||||
}
|
||||
bool isEntry() const {
|
||||
return isEntry(type_);
|
||||
}
|
||||
|
||||
bool isFunctionFrame() const;
|
||||
|
||||
bool isConstructing() const;
|
||||
@ -206,10 +215,10 @@ class JSJitFrameIter
|
||||
return frameSize_;
|
||||
}
|
||||
|
||||
// Functions used to iterate on frames. When prevType is JitFrame_Entry,
|
||||
// the current frame is the last frame.
|
||||
// Functions used to iterate on frames. When prevType is an entry,
|
||||
// the current frame is the last JS Jit frame.
|
||||
bool done() const {
|
||||
return type_ == JitFrame_Entry;
|
||||
return isEntry();
|
||||
}
|
||||
void operator++();
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "jit/JitFrames-inl.h"
|
||||
|
||||
|
||||
#include "jsfun.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsscript.h"
|
||||
@ -33,6 +32,7 @@
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
#include "vm/TypeInference.h"
|
||||
#include "wasm/WasmBuiltins.h"
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
#include "gc/Nursery-inl.h"
|
||||
@ -574,6 +574,7 @@ struct AutoResetLastProfilerFrameOnReturnFromException
|
||||
void* getLastProfilingFrame() {
|
||||
switch (rfe->kind) {
|
||||
case ResumeFromException::RESUME_ENTRY_FRAME:
|
||||
case ResumeFromException::RESUME_WASM:
|
||||
return nullptr;
|
||||
|
||||
// The following all return into baseline frames.
|
||||
@ -593,6 +594,19 @@ struct AutoResetLastProfilerFrameOnReturnFromException
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
HandleExceptionWasm(JSContext* cx, wasm::WasmFrameIter* iter, ResumeFromException* rfe)
|
||||
{
|
||||
// Maintain the wasm invariant that we have wasm frames when unwinding.
|
||||
JitActivation* act = cx->activation()->asJit();
|
||||
act->setWasmExitFP((const wasm::Frame*) act->jsExitFP());
|
||||
|
||||
rfe->kind = ResumeFromException::RESUME_WASM;
|
||||
rfe->framePointer = (uint8_t*) wasm::FailFP;
|
||||
rfe->stackPointer = (uint8_t*) wasm::HandleThrow(cx, *iter);
|
||||
MOZ_ASSERT(iter->done());
|
||||
}
|
||||
|
||||
void
|
||||
HandleException(ResumeFromException* rfe)
|
||||
{
|
||||
@ -626,9 +640,14 @@ HandleException(ResumeFromException* rfe)
|
||||
// iterating, we need a variant here that is automatically updated should
|
||||
// on-stack recompilation occur.
|
||||
DebugModeOSRVolatileJitFrameIter iter(cx);
|
||||
while (!iter.isJSJit() || !iter.asJSJit().isEntry()) {
|
||||
while (!iter.isJSJit())
|
||||
++iter;
|
||||
while (!iter.done()) {
|
||||
if (iter.isWasm()) {
|
||||
HandleExceptionWasm(cx, &iter.asWasm(), rfe);
|
||||
if (!iter.done())
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
const JSJitFrameIter& frame = iter.asJSJit();
|
||||
|
||||
bool overrecursed = false;
|
||||
@ -742,7 +761,9 @@ HandleException(ResumeFromException* rfe)
|
||||
}
|
||||
}
|
||||
|
||||
rfe->stackPointer = iter.asJSJit().fp();
|
||||
// Wasm sets its own value of SP in HandleExceptionWasm.
|
||||
if (iter.isJSJit())
|
||||
rfe->stackPointer = iter.asJSJit().fp();
|
||||
}
|
||||
|
||||
// Turns a JitFrameLayout into an ExitFrameLayout. Note that it has to be a
|
||||
@ -1275,51 +1296,50 @@ TraceJitActivation(JSTracer* trc, JitActivation* activation)
|
||||
activation->traceRematerializedFrames(trc);
|
||||
activation->traceIonRecovery(trc);
|
||||
|
||||
for (JSJitFrameIter frames(activation); !frames.done(); ++frames) {
|
||||
switch (frames.type()) {
|
||||
case JitFrame_Exit:
|
||||
TraceJitExitFrame(trc, frames);
|
||||
break;
|
||||
case JitFrame_BaselineJS:
|
||||
frames.baselineFrame()->trace(trc, frames);
|
||||
break;
|
||||
case JitFrame_IonJS:
|
||||
TraceIonJSFrame(trc, frames);
|
||||
break;
|
||||
case JitFrame_BaselineStub:
|
||||
TraceBaselineStubFrame(trc, frames);
|
||||
break;
|
||||
case JitFrame_Bailout:
|
||||
TraceBailoutFrame(trc, frames);
|
||||
break;
|
||||
case JitFrame_Rectifier:
|
||||
TraceRectifierFrame(trc, frames);
|
||||
break;
|
||||
case JitFrame_IonICCall:
|
||||
TraceIonICCallFrame(trc, frames);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected frame type");
|
||||
for (JitFrameIter frames(activation); !frames.done(); ++frames) {
|
||||
if (frames.isJSJit()) {
|
||||
const JSJitFrameIter& jitFrame = frames.asJSJit();
|
||||
switch (jitFrame.type()) {
|
||||
case JitFrame_Exit:
|
||||
TraceJitExitFrame(trc, jitFrame);
|
||||
break;
|
||||
case JitFrame_BaselineJS:
|
||||
jitFrame.baselineFrame()->trace(trc, jitFrame);
|
||||
break;
|
||||
case JitFrame_IonJS:
|
||||
TraceIonJSFrame(trc, jitFrame);
|
||||
break;
|
||||
case JitFrame_BaselineStub:
|
||||
TraceBaselineStubFrame(trc, jitFrame);
|
||||
break;
|
||||
case JitFrame_Bailout:
|
||||
TraceBailoutFrame(trc, jitFrame);
|
||||
break;
|
||||
case JitFrame_Rectifier:
|
||||
TraceRectifierFrame(trc, jitFrame);
|
||||
break;
|
||||
case JitFrame_IonICCall:
|
||||
TraceIonICCallFrame(trc, jitFrame);
|
||||
break;
|
||||
case JitFrame_WasmToJSJit:
|
||||
// Ignore: this is a marked used to let the JitFrameIter the
|
||||
// frame above is a wasm frame, handled in the next iteration.
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected frame type");
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(frames.isWasm());
|
||||
frames.asWasm().instance()->trace(trc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
TraceWasmActivation(JSTracer* trc, WasmActivation* act)
|
||||
{
|
||||
for (wasm::WasmFrameIter iter(act); !iter.done(); ++iter)
|
||||
iter.instance()->trace(trc);
|
||||
}
|
||||
|
||||
void
|
||||
TraceJitActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc)
|
||||
{
|
||||
for (ActivationIterator activations(cx, target); !activations.done(); ++activations) {
|
||||
if (activations->isJit())
|
||||
TraceJitActivation(trc, activations->asJit());
|
||||
if (activations->isWasm())
|
||||
TraceWasmActivation(trc, activations->asWasm());
|
||||
}
|
||||
for (JitActivationIterator activations(cx, target); !activations.done(); ++activations)
|
||||
TraceJitActivation(trc, activations->asJit());
|
||||
}
|
||||
|
||||
void
|
||||
@ -2395,7 +2415,7 @@ JitProfilingFrameIterator::JitProfilingFrameIterator(
|
||||
// If no profilingActivation is live, initialize directly to
|
||||
// end-of-iteration state.
|
||||
if (!cx->profilingActivation()) {
|
||||
type_ = JitFrame_Entry;
|
||||
type_ = JitFrame_CppToJSJit;
|
||||
fp_ = nullptr;
|
||||
returnAddressToFp_ = nullptr;
|
||||
return;
|
||||
@ -2409,7 +2429,7 @@ JitProfilingFrameIterator::JitProfilingFrameIterator(
|
||||
// it's a trivially empty activation, and initialize directly
|
||||
// to end-of-iteration state.
|
||||
if (!act->lastProfilingFrame()) {
|
||||
type_ = JitFrame_Entry;
|
||||
type_ = JitFrame_CppToJSJit;
|
||||
fp_ = nullptr;
|
||||
returnAddressToFp_ = nullptr;
|
||||
return;
|
||||
@ -2504,7 +2524,7 @@ JitProfilingFrameIterator::tryInitWithTable(JitcodeGlobalTable* table, void* pc,
|
||||
|
||||
// Treat dummy lookups as an empty frame sequence.
|
||||
if (entry->isDummy()) {
|
||||
type_ = JitFrame_Entry;
|
||||
type_ = JitFrame_CppToJSJit;
|
||||
fp_ = nullptr;
|
||||
returnAddressToFp_ = nullptr;
|
||||
return true;
|
||||
@ -2592,6 +2612,8 @@ JitProfilingFrameIterator::moveToNextFrame(CommonFrameLayout* frame)
|
||||
* |
|
||||
* ^--- Baseline Stub <---- Baseline
|
||||
* |
|
||||
* ^--- WasmToJSJit <---- (other wasm frames, not handled by this iterator)
|
||||
* |
|
||||
* ^--- Argument Rectifier
|
||||
* | ^
|
||||
* | |
|
||||
@ -2673,12 +2695,21 @@ JitProfilingFrameIterator::moveToNextFrame(CommonFrameLayout* frame)
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevType == JitFrame_Entry) {
|
||||
// No previous frame, set to null to indicate that OnlyJSJitFrameIter is
|
||||
// done().
|
||||
if (prevType == JitFrame_WasmToJSJit) {
|
||||
// No previous js jit frame, this is a transition frame, used to pass
|
||||
// a wasm iterator the correct value of FP.
|
||||
returnAddressToFp_ = nullptr;
|
||||
fp_ = GetPreviousRawFrame<uint8_t*>(frame);
|
||||
type_ = JitFrame_WasmToJSJit;
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevType == JitFrame_CppToJSJit) {
|
||||
// No previous frame, set to null to indicate that
|
||||
// JitProfilingFrameIterator is done().
|
||||
returnAddressToFp_ = nullptr;
|
||||
fp_ = nullptr;
|
||||
type_ = JitFrame_Entry;
|
||||
type_ = JitFrame_CppToJSJit;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2710,71 +2741,78 @@ void
|
||||
AssertJitStackInvariants(JSContext* cx)
|
||||
{
|
||||
for (JitActivationIterator activations(cx); !activations.done(); ++activations) {
|
||||
JitFrameIter iter(activations.activation());
|
||||
size_t prevFrameSize = 0;
|
||||
size_t frameSize = 0;
|
||||
bool isScriptedCallee = false;
|
||||
for (; !iter.done(); ++iter) {
|
||||
const JSJitFrameIter& frames = iter.asJSJit();
|
||||
size_t calleeFp = reinterpret_cast<size_t>(frames.fp());
|
||||
size_t callerFp = reinterpret_cast<size_t>(frames.prevFp());
|
||||
MOZ_ASSERT(callerFp >= calleeFp);
|
||||
prevFrameSize = frameSize;
|
||||
frameSize = callerFp - calleeFp;
|
||||
JitFrameIter iter(activations->asJit());
|
||||
if (iter.isJSJit()) {
|
||||
JSJitFrameIter& frames = iter.asJSJit();
|
||||
size_t prevFrameSize = 0;
|
||||
size_t frameSize = 0;
|
||||
bool isScriptedCallee = false;
|
||||
for (; !frames.done(); ++frames) {
|
||||
size_t calleeFp = reinterpret_cast<size_t>(frames.fp());
|
||||
size_t callerFp = reinterpret_cast<size_t>(frames.prevFp());
|
||||
MOZ_ASSERT(callerFp >= calleeFp);
|
||||
prevFrameSize = frameSize;
|
||||
frameSize = callerFp - calleeFp;
|
||||
|
||||
if (frames.prevType() == JitFrame_Rectifier) {
|
||||
MOZ_RELEASE_ASSERT(frameSize % JitStackAlignment == 0,
|
||||
"The rectifier frame should keep the alignment");
|
||||
if (frames.prevType() == JitFrame_Rectifier) {
|
||||
MOZ_RELEASE_ASSERT(frameSize % JitStackAlignment == 0,
|
||||
"The rectifier frame should keep the alignment");
|
||||
|
||||
size_t expectedFrameSize = 0
|
||||
size_t expectedFrameSize = 0
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
+ sizeof(void*) /* frame pointer */
|
||||
+ sizeof(void*) /* frame pointer */
|
||||
#endif
|
||||
+ sizeof(Value) * (frames.callee()->nargs() +
|
||||
1 /* |this| argument */ +
|
||||
frames.isConstructing() /* new.target */)
|
||||
+ sizeof(JitFrameLayout);
|
||||
MOZ_RELEASE_ASSERT(frameSize >= expectedFrameSize,
|
||||
"The frame is large enough to hold all arguments");
|
||||
MOZ_RELEASE_ASSERT(expectedFrameSize + JitStackAlignment > frameSize,
|
||||
"The frame size is optimal");
|
||||
}
|
||||
|
||||
if (frames.isExitFrame()) {
|
||||
// For the moment, we do not keep the JitStackAlignment
|
||||
// alignment for exit frames.
|
||||
frameSize -= ExitFrameLayout::Size();
|
||||
}
|
||||
|
||||
if (frames.isIonJS()) {
|
||||
// Ideally, we should not have such requirement, but keep the
|
||||
// alignment-delta as part of the Safepoint such that we can pad
|
||||
// accordingly when making out-of-line calls. In the mean time,
|
||||
// let us have check-points where we can garantee that
|
||||
// everything can properly be aligned before adding complexity.
|
||||
MOZ_RELEASE_ASSERT(frames.ionScript()->frameSize() % JitStackAlignment == 0,
|
||||
"Ensure that if the Ion frame is aligned, then the spill base is also aligned");
|
||||
|
||||
if (isScriptedCallee) {
|
||||
MOZ_RELEASE_ASSERT(prevFrameSize % JitStackAlignment == 0,
|
||||
"The ion frame should keep the alignment");
|
||||
+ sizeof(Value) * (frames.callee()->nargs() +
|
||||
1 /* |this| argument */ +
|
||||
frames.isConstructing() /* new.target */)
|
||||
+ sizeof(JitFrameLayout);
|
||||
MOZ_RELEASE_ASSERT(frameSize >= expectedFrameSize,
|
||||
"The frame is large enough to hold all arguments");
|
||||
MOZ_RELEASE_ASSERT(expectedFrameSize + JitStackAlignment > frameSize,
|
||||
"The frame size is optimal");
|
||||
}
|
||||
|
||||
if (frames.isExitFrame()) {
|
||||
// For the moment, we do not keep the JitStackAlignment
|
||||
// alignment for exit frames.
|
||||
frameSize -= ExitFrameLayout::Size();
|
||||
}
|
||||
|
||||
if (frames.isIonJS()) {
|
||||
// Ideally, we should not have such requirement, but keep the
|
||||
// alignment-delta as part of the Safepoint such that we can pad
|
||||
// accordingly when making out-of-line calls. In the mean time,
|
||||
// let us have check-points where we can garantee that
|
||||
// everything can properly be aligned before adding complexity.
|
||||
MOZ_RELEASE_ASSERT(frames.ionScript()->frameSize() % JitStackAlignment == 0,
|
||||
"Ensure that if the Ion frame is aligned, then the spill base is also aligned");
|
||||
|
||||
if (isScriptedCallee) {
|
||||
MOZ_RELEASE_ASSERT(prevFrameSize % JitStackAlignment == 0,
|
||||
"The ion frame should keep the alignment");
|
||||
}
|
||||
}
|
||||
|
||||
// The stack is dynamically aligned by baseline stubs before calling
|
||||
// any jitted code.
|
||||
if (frames.prevType() == JitFrame_BaselineStub && isScriptedCallee) {
|
||||
MOZ_RELEASE_ASSERT(calleeFp % JitStackAlignment == 0,
|
||||
"The baseline stub restores the stack alignment");
|
||||
}
|
||||
|
||||
isScriptedCallee = frames.isScripted() || frames.type() == JitFrame_Rectifier;
|
||||
}
|
||||
|
||||
// The stack is dynamically aligned by baseline stubs before calling
|
||||
// any jitted code.
|
||||
if (frames.prevType() == JitFrame_BaselineStub && isScriptedCallee) {
|
||||
MOZ_RELEASE_ASSERT(calleeFp % JitStackAlignment == 0,
|
||||
"The baseline stub restores the stack alignment");
|
||||
}
|
||||
|
||||
isScriptedCallee = frames.isScripted() || frames.type() == JitFrame_Rectifier;
|
||||
MOZ_RELEASE_ASSERT(JSJitFrameIter::isEntry(frames.type()),
|
||||
"The first frame of a Jit activation should be an entry frame");
|
||||
MOZ_RELEASE_ASSERT(reinterpret_cast<size_t>(frames.fp()) % JitStackAlignment == 0,
|
||||
"The entry frame should be properly aligned");
|
||||
} else {
|
||||
MOZ_ASSERT(iter.isWasm());
|
||||
wasm::WasmFrameIter& frames = iter.asWasm();
|
||||
while (!frames.done())
|
||||
++frames;
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(iter.asJSJit().type() == JitFrame_Entry,
|
||||
"The first frame of a Jit activation should be an entry frame");
|
||||
MOZ_RELEASE_ASSERT(reinterpret_cast<size_t>(iter.asJSJit().fp()) % JitStackAlignment == 0,
|
||||
"The entry frame should be properly aligned");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,6 +269,7 @@ struct ResumeFromException
|
||||
static const uint32_t RESUME_FINALLY = 2;
|
||||
static const uint32_t RESUME_FORCED_RETURN = 3;
|
||||
static const uint32_t RESUME_BAILOUT = 4;
|
||||
static const uint32_t RESUME_WASM = 5;
|
||||
|
||||
uint8_t* framePointer;
|
||||
uint8_t* stackPointer;
|
||||
@ -444,6 +445,14 @@ class RectifierFrameLayout : public JitFrameLayout
|
||||
}
|
||||
};
|
||||
|
||||
class WasmFrameLayout : public JitFrameLayout
|
||||
{
|
||||
public:
|
||||
static inline size_t Size() {
|
||||
return sizeof(WasmFrameLayout);
|
||||
}
|
||||
};
|
||||
|
||||
class IonICCallFrameLayout : public CommonFrameLayout
|
||||
{
|
||||
protected:
|
||||
|
@ -3118,10 +3118,11 @@ void
|
||||
MacroAssembler::wasmAssertNonExitInvariants(Register activation)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// WasmActivation.exitFP should be null when outside any exit frame.
|
||||
// packedExitFP should not be tagged as wasm. Note this includes nullptr.
|
||||
Label ok;
|
||||
Address exitFP(activation, WasmActivation::offsetOfExitFP());
|
||||
branchPtr(Assembler::Equal, exitFP, ImmWord(0), &ok);
|
||||
Address packedExitFP(activation, JitActivation::offsetOfPackedExitFP());
|
||||
branchTestPtr(Assembler::Zero, packedExitFP, Imm32(uintptr_t(JitActivation::ExitFpWasmBit)),
|
||||
&ok);
|
||||
breakpoint();
|
||||
bind(&ok);
|
||||
#endif
|
||||
|
@ -3605,6 +3605,7 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler)
|
||||
Label finally;
|
||||
Label return_;
|
||||
Label bailout;
|
||||
Label wasm;
|
||||
|
||||
{
|
||||
ScratchRegisterScope scratch(asMasm());
|
||||
@ -3618,6 +3619,7 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler)
|
||||
asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FORCED_RETURN),
|
||||
&return_);
|
||||
asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
|
||||
asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_WASM), &wasm);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
|
||||
@ -3697,6 +3699,17 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler)
|
||||
ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1, scratch);
|
||||
}
|
||||
jump(r1);
|
||||
|
||||
// If we are throwing and the innermost frame was a wasm frame, reset SP and
|
||||
// FP; SP is pointing to the unwound return address to the wasm entry, so
|
||||
// we can just ret().
|
||||
bind(&wasm);
|
||||
{
|
||||
ScratchRegisterScope scratch(asMasm());
|
||||
ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch);
|
||||
ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
|
||||
}
|
||||
as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
|
@ -1547,14 +1547,14 @@ Simulator::exclusiveMonitorClear()
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::startInterrupt(WasmActivation* activation)
|
||||
Simulator::startWasmInterrupt(JitActivation* activation)
|
||||
{
|
||||
JS::ProfilingFrameIterator::RegisterState state;
|
||||
state.pc = (void*) get_pc();
|
||||
state.fp = (void*) get_register(fp);
|
||||
state.sp = (void*) get_register(sp);
|
||||
state.lr = (void*) get_register(lr);
|
||||
activation->startInterrupt(state);
|
||||
activation->startWasmInterrupt(state);
|
||||
}
|
||||
|
||||
// The signal handler only redirects the PC to the interrupt stub when the PC is
|
||||
@ -1576,7 +1576,7 @@ Simulator::handleWasmInterrupt()
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
startInterrupt(wasm::ActivationIfInnermost(cx_));
|
||||
startWasmInterrupt(cx_->activation()->asJit());
|
||||
set_pc(int32_t(cs->interruptCode()));
|
||||
}
|
||||
|
||||
@ -1589,9 +1589,9 @@ Simulator::handleWasmInterrupt()
|
||||
bool
|
||||
Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
|
||||
{
|
||||
WasmActivation* act = wasm::ActivationIfInnermost(cx_);
|
||||
if (!act)
|
||||
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));
|
||||
@ -1609,7 +1609,7 @@ Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
|
||||
const wasm::CodeSegment* segment;
|
||||
const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc, &segment);
|
||||
if (!memoryAccess) {
|
||||
startInterrupt(act);
|
||||
startWasmInterrupt(act);
|
||||
if (!instance->code().containsCodePC(pc, &segment))
|
||||
MOZ_CRASH("Cannot map PC to trap handler");
|
||||
set_pc(int32_t(segment->outOfBoundsCode()));
|
||||
|
@ -41,11 +41,9 @@
|
||||
#include "wasm/WasmCode.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
|
||||
namespace jit {
|
||||
|
||||
class JitActivation;
|
||||
class Simulator;
|
||||
class Redirection;
|
||||
class CachePage;
|
||||
@ -293,7 +291,7 @@ class Simulator
|
||||
|
||||
// Handle a wasm interrupt triggered by an async signal handler.
|
||||
void handleWasmInterrupt();
|
||||
void startInterrupt(WasmActivation* act);
|
||||
void startWasmInterrupt(JitActivation* act);
|
||||
|
||||
// Handle any wasm faults, returning true if the fault was handled.
|
||||
bool handleWasmFault(int32_t addr, unsigned numBytes);
|
||||
|
@ -205,7 +205,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
||||
}
|
||||
|
||||
masm.ma_sub(r8, sp, r8);
|
||||
masm.makeFrameDescriptor(r8, JitFrame_Entry, JitFrameLayout::Size());
|
||||
masm.makeFrameDescriptor(r8, JitFrame_CppToJSJit, JitFrameLayout::Size());
|
||||
|
||||
masm.startDataTransferM(IsStore, sp, IB, NoWriteBack);
|
||||
// [sp] = return address (written later)
|
||||
@ -1205,7 +1205,10 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonICCall), &handle_IonICCall);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_CppToJSJit), &handle_Entry);
|
||||
|
||||
// The WasmToJSJit is just another kind of entry.
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_WasmToJSJit), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
|
||||
@ -1424,9 +1427,11 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
// JitFrame_CppToJSJit / JitFrame_WasmJSToJit
|
||||
//
|
||||
// If at an entry frame, store null into both fields.
|
||||
// A fast-path wasm->jit transition frame is an entry frame from the point
|
||||
// of view of the JIT.
|
||||
//
|
||||
masm.bind(&handle_Entry);
|
||||
{
|
||||
|
@ -163,7 +163,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
||||
masm.subStackPtrFrom(r19);
|
||||
|
||||
// Push the frameDescriptor.
|
||||
masm.makeFrameDescriptor(r19, JitFrame_Entry, JitFrameLayout::Size());
|
||||
masm.makeFrameDescriptor(r19, JitFrame_CppToJSJit, JitFrameLayout::Size());
|
||||
masm.Push(r19);
|
||||
|
||||
Label osrReturnPoint;
|
||||
@ -989,7 +989,10 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonICCall), &handle_IonICCall);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_CppToJSJit), &handle_Entry);
|
||||
|
||||
// The WasmToJSJit is just another kind of entry.
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_WasmToJSJit), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
|
||||
@ -1213,9 +1216,11 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
// JitFrame_CppToJSJit / JitFrame_WasmJSToJit
|
||||
//
|
||||
// If at an entry frame, store null into both fields.
|
||||
// A fast-path wasm->jit transition frame is an entry frame from the point
|
||||
// of view of the JIT.
|
||||
//
|
||||
masm.bind(&handle_Entry);
|
||||
{
|
||||
|
@ -257,7 +257,7 @@ void Simulator::handle_wasm_interrupt() {
|
||||
state.fp = fp;
|
||||
state.lr = (uint8_t*) xreg(30);
|
||||
state.sp = (uint8_t*) xreg(31);
|
||||
js::wasm::ActivationIfInnermost(cx_)->startInterrupt(state);
|
||||
cx_->activation_->asJit()->startWasmInterrupt(state);
|
||||
|
||||
set_pc((Instruction*)cs->interruptCode());
|
||||
}
|
||||
|
@ -1334,8 +1334,8 @@ class Redirection
|
||||
next_(nullptr)
|
||||
{
|
||||
next_ = SimulatorProcess::redirection();
|
||||
if (!SimulatorProcess::ICacheCheckingDisableCount) {
|
||||
FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
|
||||
if (!SimulatorProcess::ICacheCheckingDisableCount) {
|
||||
FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
|
||||
SimInstruction::kInstrSize);
|
||||
}
|
||||
SimulatorProcess::setRedirection(this);
|
||||
@ -1619,14 +1619,14 @@ Simulator::get_pc() const
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::startInterrupt(WasmActivation* activation)
|
||||
Simulator::startInterrupt(JitActivation* activation)
|
||||
{
|
||||
JS::ProfilingFrameIterator::RegisterState state;
|
||||
state.pc = (void*) get_pc();
|
||||
state.fp = (void*) getRegister(fp);
|
||||
state.sp = (void*) getRegister(sp);
|
||||
state.lr = (void*) getRegister(ra);
|
||||
activation->startInterrupt(state);
|
||||
activation->startWasmInterrupt(state);
|
||||
}
|
||||
|
||||
// The signal handler only redirects the PC to the interrupt stub when the PC is
|
||||
@ -1640,7 +1640,7 @@ Simulator::handleWasmInterrupt()
|
||||
void* pc = (void*)get_pc();
|
||||
void* fp = (void*)getRegister(Register::fp);
|
||||
|
||||
WasmActivation* activation = wasm::ActivationIfInnermost(TlsContext.get());
|
||||
JitActivation* activation = TlsContext.get()->activation()->asJit();
|
||||
const wasm::CodeSegment* segment;
|
||||
const wasm::Code* code = activation->compartment()->wasm.lookupCode(pc, &segment);
|
||||
if (!code || !segment->containsFunctionPC(pc))
|
||||
@ -1665,9 +1665,9 @@ bool
|
||||
Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
|
||||
{
|
||||
JSContext* cx = TlsContext.get();
|
||||
WasmActivation* act = wasm::ActivationIfInnermost(cx);
|
||||
if (!act)
|
||||
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*>(getRegister(Register::fp));
|
||||
|
@ -40,10 +40,10 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
|
||||
namespace jit {
|
||||
|
||||
class JitActivation;
|
||||
|
||||
class Simulator;
|
||||
class Redirection;
|
||||
class CachePage;
|
||||
@ -296,7 +296,7 @@ class Simulator {
|
||||
|
||||
// Handle a wasm interrupt triggered by an async signal handler.
|
||||
void handleWasmInterrupt();
|
||||
void startInterrupt(WasmActivation* act);
|
||||
void startInterrupt(JitActivation* act);
|
||||
|
||||
// Handle any wasm faults, returning true if the fault was handled.
|
||||
bool handleWasmFault(int32_t addr, unsigned numBytes);
|
||||
|
@ -200,7 +200,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
||||
masm.storePtr(s2, Address(StackPointer, 0)); // callee token
|
||||
|
||||
masm.subPtr(StackPointer, s4);
|
||||
masm.makeFrameDescriptor(s4, JitFrame_Entry, JitFrameLayout::Size());
|
||||
masm.makeFrameDescriptor(s4, JitFrame_CppToJSJit, JitFrameLayout::Size());
|
||||
masm.push(s4); // descriptor
|
||||
|
||||
CodeLabel returnLabel;
|
||||
@ -1181,7 +1181,10 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonICCall), &handle_IonICCall);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_CppToJSJit), &handle_Entry);
|
||||
|
||||
// The WasmToJSJit is just another kind of entry.
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_WasmToJSJit), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
|
||||
@ -1400,9 +1403,11 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
// JitFrame_CppToJSJit / JitFrame_WasmJSToJit
|
||||
//
|
||||
// If at an entry frame, store null into both fields.
|
||||
// A fast-path wasm->jit transition frame is an entry frame from the point
|
||||
// of view of the JIT.
|
||||
//
|
||||
masm.bind(&handle_Entry);
|
||||
{
|
||||
|
@ -1613,7 +1613,7 @@ Simulator::get_pc() const
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::startInterrupt(WasmActivation* activation)
|
||||
Simulator::startInterrupt(JitActivation* activation)
|
||||
{
|
||||
MOZ_CRASH("NIY");
|
||||
}
|
||||
|
@ -40,10 +40,10 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
|
||||
namespace jit {
|
||||
|
||||
class JitActivation;
|
||||
|
||||
class Simulator;
|
||||
class Redirection;
|
||||
class CachePage;
|
||||
@ -305,7 +305,7 @@ class Simulator {
|
||||
|
||||
// Handle a wasm interrupt triggered by an async signal handler.
|
||||
void handleWasmInterrupt();
|
||||
void startInterrupt(WasmActivation* act);
|
||||
void startInterrupt(JitActivation* act);
|
||||
|
||||
// Executes one instruction.
|
||||
void instructionDecode(SimInstruction* instr);
|
||||
|
@ -222,7 +222,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
||||
masm.storePtr(reg_token, Address(StackPointer, 0)); // callee token
|
||||
|
||||
masm.subPtr(StackPointer, s4);
|
||||
masm.makeFrameDescriptor(s4, JitFrame_Entry, JitFrameLayout::Size());
|
||||
masm.makeFrameDescriptor(s4, JitFrame_CppToJSJit, JitFrameLayout::Size());
|
||||
masm.push(s4); // descriptor
|
||||
|
||||
CodeLabel returnLabel;
|
||||
@ -1126,7 +1126,10 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonICCall), &handle_IonICCall);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_CppToJSJit), &handle_Entry);
|
||||
|
||||
// The WasmToJSJit is just another kind of entry.
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_WasmToJSJit), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
|
||||
@ -1345,9 +1348,11 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
// JitFrame_CppToJSJit / JitFrame_WasmJSToJit
|
||||
//
|
||||
// If at an entry frame, store null into both fields.
|
||||
// A fast-path wasm->jit transition frame is an entry frame from the point
|
||||
// of view of the JIT.
|
||||
//
|
||||
masm.bind(&handle_Entry);
|
||||
{
|
||||
|
@ -314,6 +314,7 @@ MacroAssemblerX64::handleFailureWithHandlerTail(void* handler)
|
||||
Label finally;
|
||||
Label return_;
|
||||
Label bailout;
|
||||
Label wasm;
|
||||
|
||||
load32(Address(rsp, offsetof(ResumeFromException, kind)), rax);
|
||||
asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
@ -321,6 +322,7 @@ MacroAssemblerX64::handleFailureWithHandlerTail(void* handler)
|
||||
asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
|
||||
asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
|
||||
asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_WASM), &wasm);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
|
||||
@ -380,6 +382,14 @@ MacroAssemblerX64::handleFailureWithHandlerTail(void* handler)
|
||||
loadPtr(Address(esp, offsetof(ResumeFromException, bailoutInfo)), r9);
|
||||
mov(ImmWord(BAILOUT_RETURN_OK), rax);
|
||||
jmp(Operand(rsp, offsetof(ResumeFromException, target)));
|
||||
|
||||
// If we are throwing and the innermost frame was a wasm frame, reset SP and
|
||||
// FP; SP is pointing to the unwound return address to the wasm entry, so
|
||||
// we can just ret().
|
||||
bind(&wasm);
|
||||
loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp);
|
||||
loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -161,7 +161,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
||||
masm.subq(rsp, r14);
|
||||
|
||||
// Create a frame descriptor.
|
||||
masm.makeFrameDescriptor(r14, JitFrame_Entry, JitFrameLayout::Size());
|
||||
masm.makeFrameDescriptor(r14, JitFrame_CppToJSJit, JitFrameLayout::Size());
|
||||
masm.push(r14);
|
||||
|
||||
CodeLabel returnLabel;
|
||||
@ -1102,7 +1102,10 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonICCall), &handle_IonICCall);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_CppToJSJit), &handle_Entry);
|
||||
|
||||
// The WasmToJSJit is just another kind of entry.
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_WasmToJSJit), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
|
||||
@ -1314,9 +1317,11 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
// JitFrame_CppToJSJit / JitFrame_WasmJSToJit
|
||||
//
|
||||
// If at an entry frame, store null into both fields.
|
||||
// A fast-path wasm->jit transition frame is an entry frame from the point
|
||||
// of view of the JIT.
|
||||
//
|
||||
masm.bind(&handle_Entry);
|
||||
{
|
||||
|
@ -213,6 +213,7 @@ MacroAssemblerX86::handleFailureWithHandlerTail(void* handler)
|
||||
Label finally;
|
||||
Label return_;
|
||||
Label bailout;
|
||||
Label wasm;
|
||||
|
||||
loadPtr(Address(esp, offsetof(ResumeFromException, kind)), eax);
|
||||
asMasm().branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME),
|
||||
@ -222,6 +223,7 @@ MacroAssemblerX86::handleFailureWithHandlerTail(void* handler)
|
||||
asMasm().branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_FORCED_RETURN),
|
||||
&return_);
|
||||
asMasm().branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
|
||||
asMasm().branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_WASM), &wasm);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
|
||||
@ -283,6 +285,14 @@ MacroAssemblerX86::handleFailureWithHandlerTail(void* handler)
|
||||
loadPtr(Address(esp, offsetof(ResumeFromException, bailoutInfo)), ecx);
|
||||
movl(Imm32(BAILOUT_RETURN_OK), eax);
|
||||
jmp(Operand(esp, offsetof(ResumeFromException, target)));
|
||||
|
||||
// If we are throwing and the innermost frame was a wasm frame, reset SP and
|
||||
// FP; SP is pointing to the unwound return address to the wasm entry, so
|
||||
// we can just ret().
|
||||
bind(&wasm);
|
||||
loadPtr(Address(esp, offsetof(ResumeFromException, framePointer)), ebp);
|
||||
loadPtr(Address(esp, offsetof(ResumeFromException, stackPointer)), esp);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -157,7 +157,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
|
||||
*****************************************************************/
|
||||
// Create a frame descriptor.
|
||||
masm.subl(esp, esi);
|
||||
masm.makeFrameDescriptor(esi, JitFrame_Entry, JitFrameLayout::Size());
|
||||
masm.makeFrameDescriptor(esi, JitFrame_CppToJSJit, JitFrameLayout::Size());
|
||||
masm.push(esi);
|
||||
|
||||
CodeLabel returnLabel;
|
||||
@ -1133,7 +1133,10 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonICCall), &handle_IonICCall);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_CppToJSJit), &handle_Entry);
|
||||
|
||||
// The WasmToJSJit is just another kind of entry.
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_WasmToJSJit), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
|
||||
@ -1347,9 +1350,11 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
// JitFrame_CppToJSJit / JitFrame_WasmJSToJit
|
||||
//
|
||||
// If at an entry frame, store null into both fields.
|
||||
// A fast-path wasm->jit transition frame is an entry frame from the point
|
||||
// of view of the JIT.
|
||||
//
|
||||
masm.bind(&handle_Entry);
|
||||
{
|
||||
|
@ -376,7 +376,11 @@ struct JSContext : public JS::RootingContext,
|
||||
}
|
||||
static size_t offsetOfProfilingActivation() {
|
||||
return offsetof(JSContext, profilingActivation_);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t offsetOfJitActivation() {
|
||||
return offsetof(JSContext, jitActivation);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static size_t offsetOfInUnsafeCallWithABI() {
|
||||
|
@ -570,15 +570,14 @@ JSContext::currentScript(jsbytecode** ppc,
|
||||
return nullptr;
|
||||
|
||||
if (act->isJit()) {
|
||||
if (act->hasWasmExitFP())
|
||||
return nullptr;
|
||||
JSScript* script = nullptr;
|
||||
js::jit::GetPcScript(const_cast<JSContext*>(this), &script, ppc);
|
||||
MOZ_ASSERT(allowCrossCompartment || script->compartment() == compartment());
|
||||
return script;
|
||||
}
|
||||
|
||||
if (act->isWasm())
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(act->isInterpreter());
|
||||
|
||||
js::InterpreterFrame* fp = act->asInterpreter()->current();
|
||||
|
@ -95,7 +95,6 @@ ReportOverRecursed(JSContext* cx);
|
||||
|
||||
class Activation;
|
||||
class ActivationIterator;
|
||||
class WasmActivation;
|
||||
|
||||
namespace jit {
|
||||
class JitRuntime;
|
||||
@ -292,7 +291,6 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
||||
friend class js::Activation;
|
||||
friend class js::ActivationIterator;
|
||||
friend class js::jit::JitActivation;
|
||||
friend class js::WasmActivation;
|
||||
friend class js::jit::CompileRuntime;
|
||||
|
||||
public:
|
||||
|
@ -160,7 +160,7 @@ struct SavedFrame::Lookup {
|
||||
{
|
||||
MOZ_ASSERT(source);
|
||||
MOZ_ASSERT_IF(framePtr.isSome(), activation);
|
||||
MOZ_ASSERT_IF(framePtr.isSome() && !activation->isWasm(), pc);
|
||||
MOZ_ASSERT_IF(framePtr.isSome() && !activation->hasWasmExitFP(), pc);
|
||||
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
column = 0;
|
||||
|
@ -930,11 +930,8 @@ Activation::isProfiling() const
|
||||
if (isInterpreter())
|
||||
return asInterpreter()->isProfiling();
|
||||
|
||||
if (isJit())
|
||||
return asJit()->isProfiling();
|
||||
|
||||
MOZ_ASSERT(isWasm());
|
||||
return asWasm()->isProfiling();
|
||||
MOZ_ASSERT(isJit());
|
||||
return asJit()->isProfiling();
|
||||
}
|
||||
|
||||
Activation*
|
||||
|
@ -500,6 +500,8 @@ JitFrameIter::operator=(const JitFrameIter& another)
|
||||
{
|
||||
MOZ_ASSERT(this != &another);
|
||||
|
||||
act_ = another.act_;
|
||||
|
||||
if (isSome())
|
||||
iter_.destroy();
|
||||
if (!another.isSome())
|
||||
@ -515,15 +517,17 @@ JitFrameIter::operator=(const JitFrameIter& another)
|
||||
return *this;
|
||||
}
|
||||
|
||||
JitFrameIter::JitFrameIter(Activation* act)
|
||||
JitFrameIter::JitFrameIter(jit::JitActivation* act)
|
||||
{
|
||||
MOZ_ASSERT(act->isJit() || act->isWasm());
|
||||
if (act->isJit()) {
|
||||
iter_.construct<jit::JSJitFrameIter>(act->asJit());
|
||||
act_ = act;
|
||||
MOZ_ASSERT(act->hasExitFP(), "packedExitFP is used to determine if JSJit or wasm");
|
||||
if (act->hasJSExitFP()) {
|
||||
iter_.construct<jit::JSJitFrameIter>(act);
|
||||
} else {
|
||||
MOZ_ASSERT(act->isWasm());
|
||||
iter_.construct<wasm::WasmFrameIter>(act->asWasm());
|
||||
MOZ_ASSERT(act->hasWasmExitFP());
|
||||
iter_.construct<wasm::WasmFrameIter>(act);
|
||||
}
|
||||
settle();
|
||||
}
|
||||
|
||||
void
|
||||
@ -549,29 +553,56 @@ JitFrameIter::done() const
|
||||
MOZ_CRASH("unhandled case");
|
||||
}
|
||||
|
||||
void
|
||||
JitFrameIter::settle()
|
||||
{
|
||||
if (isJSJit()) {
|
||||
const jit::JSJitFrameIter& jitFrame = asJSJit();
|
||||
if (jitFrame.type() != jit::JitFrame_WasmToJSJit)
|
||||
return;
|
||||
|
||||
// Transition from js jit frames to wasm frames: we're on the
|
||||
// wasm-to-jit fast path. The current stack layout is as follows:
|
||||
// (stack grows downward)
|
||||
//
|
||||
// [--------------------]
|
||||
// [WASM FUNC ]
|
||||
// [WASM JIT EXIT FRAME ]
|
||||
// [JIT WASM ENTRY FRAME] <-- we're here.
|
||||
//
|
||||
// So prevFP points to the wasm jit exit FP, maintaing the invariant in
|
||||
// WasmFrameIter that the first frame is an exit frame and can be
|
||||
// popped.
|
||||
|
||||
wasm::Frame* prevFP = (wasm::Frame*) jitFrame.prevFp();
|
||||
iter_.destroy();
|
||||
iter_.construct<wasm::WasmFrameIter>(act_, prevFP);
|
||||
MOZ_ASSERT(!asWasm().done());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JitFrameIter::operator++()
|
||||
{
|
||||
MOZ_ASSERT(isSome());
|
||||
if (isJSJit()) {
|
||||
if (isJSJit())
|
||||
++asJSJit();
|
||||
return;
|
||||
}
|
||||
if (isWasm()) {
|
||||
else if (isWasm())
|
||||
++asWasm();
|
||||
return;
|
||||
}
|
||||
MOZ_CRASH("unhandled case");
|
||||
else
|
||||
MOZ_CRASH("unhandled case");
|
||||
settle();
|
||||
}
|
||||
|
||||
OnlyJSJitFrameIter::OnlyJSJitFrameIter(Activation* act)
|
||||
OnlyJSJitFrameIter::OnlyJSJitFrameIter(jit::JitActivation* act)
|
||||
: JitFrameIter(act)
|
||||
{
|
||||
settle();
|
||||
}
|
||||
|
||||
OnlyJSJitFrameIter::OnlyJSJitFrameIter(JSContext* cx)
|
||||
: OnlyJSJitFrameIter(cx->activation())
|
||||
: OnlyJSJitFrameIter(cx->activation()->asJit())
|
||||
{
|
||||
}
|
||||
|
||||
@ -627,8 +658,8 @@ FrameIter::settleOnActivation()
|
||||
}
|
||||
}
|
||||
|
||||
if (activation->isJit() || activation->isWasm()) {
|
||||
data_.jitFrames_ = JitFrameIter(activation);
|
||||
if (activation->isJit()) {
|
||||
data_.jitFrames_ = JitFrameIter(activation->asJit());
|
||||
data_.jitFrames_.skipNonScriptedJSFrames();
|
||||
if (data_.jitFrames_.done()) {
|
||||
// It's possible to have an JitActivation with no scripted
|
||||
@ -1142,7 +1173,7 @@ FrameIter::wasmUpdateBytecodeOffset()
|
||||
wasm::DebugFrame* frame = wasmFrame().debugFrame();
|
||||
|
||||
// Relookup the current frame, updating the bytecode offset in the process.
|
||||
data_.jitFrames_ = JitFrameIter(data_.activations_->asWasm());
|
||||
data_.jitFrames_ = JitFrameIter(data_.activations_->asJit());
|
||||
while (wasmFrame().debugFrame() != frame)
|
||||
++data_.jitFrames_;
|
||||
|
||||
@ -1522,6 +1553,8 @@ jit::JitActivation::~JitActivation()
|
||||
// JitActivations.
|
||||
MOZ_ASSERT(!bailoutData_);
|
||||
|
||||
MOZ_ASSERT(!isWasmInterrupted());
|
||||
|
||||
clearRematerializedFrames();
|
||||
js_delete(rematerializedFrames_);
|
||||
}
|
||||
@ -1712,32 +1745,8 @@ jit::JitActivation::traceIonRecovery(JSTracer* trc)
|
||||
it->trace(trc);
|
||||
}
|
||||
|
||||
WasmActivation::WasmActivation(JSContext* cx)
|
||||
: Activation(cx, Wasm),
|
||||
exitFP_(nullptr)
|
||||
{
|
||||
// Now that the WasmActivation is fully initialized, make it visible to
|
||||
// asynchronous profiling.
|
||||
registerProfiling();
|
||||
}
|
||||
|
||||
WasmActivation::~WasmActivation()
|
||||
{
|
||||
// Hide this activation from the profiler before is is destroyed.
|
||||
unregisterProfiling();
|
||||
|
||||
MOZ_ASSERT(!interrupted());
|
||||
MOZ_ASSERT(exitFP_ == nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
WasmActivation::unwindExitFP(wasm::Frame* exitFP)
|
||||
{
|
||||
exitFP_ = exitFP;
|
||||
}
|
||||
|
||||
void
|
||||
WasmActivation::startInterrupt(const JS::ProfilingFrameIterator::RegisterState& state)
|
||||
jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::RegisterState& state)
|
||||
{
|
||||
MOZ_ASSERT(state.pc);
|
||||
MOZ_ASSERT(state.fp);
|
||||
@ -1745,7 +1754,7 @@ WasmActivation::startInterrupt(const JS::ProfilingFrameIterator::RegisterState&
|
||||
// Execution can only be interrupted in function code. Afterwards, control
|
||||
// flow does not reenter function code and thus there should be no
|
||||
// interrupt-during-interrupt.
|
||||
MOZ_ASSERT(!interrupted());
|
||||
MOZ_ASSERT(!isWasmInterrupted());
|
||||
|
||||
bool ignoredUnwound;
|
||||
wasm::UnwindState unwindState;
|
||||
@ -1755,52 +1764,54 @@ WasmActivation::startInterrupt(const JS::ProfilingFrameIterator::RegisterState&
|
||||
MOZ_ASSERT(compartment()->wasm.lookupCode(unwindPC)->lookupRange(unwindPC)->isFunction());
|
||||
|
||||
cx_->runtime()->startWasmInterrupt(state.pc, unwindPC);
|
||||
exitFP_ = reinterpret_cast<wasm::Frame*>(unwindState.fp);
|
||||
setWasmExitFP(unwindState.fp);
|
||||
|
||||
MOZ_ASSERT(compartment() == exitFP_->tls->instance->compartment());
|
||||
MOZ_ASSERT(interrupted());
|
||||
MOZ_ASSERT(compartment() == unwindState.fp->tls->instance->compartment());
|
||||
MOZ_ASSERT(isWasmInterrupted());
|
||||
}
|
||||
|
||||
void
|
||||
WasmActivation::finishInterrupt()
|
||||
jit::JitActivation::finishWasmInterrupt()
|
||||
{
|
||||
MOZ_ASSERT(interrupted());
|
||||
MOZ_ASSERT(exitFP_);
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
MOZ_ASSERT(isWasmInterrupted());
|
||||
|
||||
cx_->runtime()->finishWasmInterrupt();
|
||||
exitFP_ = nullptr;
|
||||
packedExitFP_ = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
WasmActivation::interrupted() const
|
||||
jit::JitActivation::isWasmInterrupted() const
|
||||
{
|
||||
void* pc = cx_->runtime()->wasmUnwindPC();
|
||||
if (!pc)
|
||||
return false;
|
||||
|
||||
Activation* act = cx_->activation();
|
||||
while (act && !act->isWasm())
|
||||
while (act && !act->hasWasmExitFP())
|
||||
act = act->prev();
|
||||
|
||||
if (act->asWasm() != this)
|
||||
if (act != this)
|
||||
return false;
|
||||
|
||||
DebugOnly<wasm::Frame*> fp = act->asWasm()->exitFP();
|
||||
DebugOnly<const wasm::Frame*> fp = wasmExitFP();
|
||||
MOZ_ASSERT(fp && fp->instance()->code().containsCodePC(pc));
|
||||
return true;
|
||||
}
|
||||
|
||||
void*
|
||||
WasmActivation::unwindPC() const
|
||||
jit::JitActivation::wasmUnwindPC() const
|
||||
{
|
||||
MOZ_ASSERT(interrupted());
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
MOZ_ASSERT(isWasmInterrupted());
|
||||
return cx_->runtime()->wasmUnwindPC();
|
||||
}
|
||||
|
||||
void*
|
||||
WasmActivation::resumePC() const
|
||||
jit::JitActivation::wasmResumePC() const
|
||||
{
|
||||
MOZ_ASSERT(interrupted());
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
MOZ_ASSERT(isWasmInterrupted());
|
||||
return cx_->runtime()->wasmResumePC();
|
||||
}
|
||||
|
||||
@ -1926,21 +1937,31 @@ void
|
||||
JS::ProfilingFrameIterator::operator++()
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
|
||||
|
||||
if (activation_->isWasm()) {
|
||||
MOZ_ASSERT(activation_->isJit());
|
||||
if (isWasm())
|
||||
++wasmIter();
|
||||
settle();
|
||||
return;
|
||||
}
|
||||
|
||||
++jitIter();
|
||||
else
|
||||
++jitIter();
|
||||
settle();
|
||||
}
|
||||
|
||||
void
|
||||
JS::ProfilingFrameIterator::settleFrames()
|
||||
{
|
||||
// Handle transition frames (see comment in JitFrameIter::operator++).
|
||||
if (isJit() && !jitIter().done() && jitIter().frameType() == jit::JitFrame_WasmToJSJit) {
|
||||
wasm::Frame* fp = (wasm::Frame*) jitIter().fp();
|
||||
iteratorDestroy();
|
||||
new (storage()) wasm::ProfilingFrameIterator(*activation_->asJit(), fp);
|
||||
kind_ = Kind::Wasm;
|
||||
MOZ_ASSERT(!wasmIter().done());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JS::ProfilingFrameIterator::settle()
|
||||
{
|
||||
settleFrames();
|
||||
while (iteratorDone()) {
|
||||
iteratorDestroy();
|
||||
activation_ = activation_->prevProfiling();
|
||||
@ -1952,6 +1973,7 @@ JS::ProfilingFrameIterator::settle()
|
||||
if (!activation_)
|
||||
return;
|
||||
iteratorConstruct();
|
||||
settleFrames();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1959,39 +1981,57 @@ void
|
||||
JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state)
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
|
||||
MOZ_ASSERT(activation_->isJit());
|
||||
|
||||
if (activation_->isWasm()) {
|
||||
new (storage()) wasm::ProfilingFrameIterator(*activation_->asWasm(), state);
|
||||
jit::JitActivation* activation = activation_->asJit();
|
||||
MOZ_ASSERT(activation->isActive());
|
||||
|
||||
// We want to know if we should start with a wasm profiling frame iterator
|
||||
// or not. To determine this, there are three possibilities:
|
||||
// - we've exited to C++ from wasm, in which case the activation
|
||||
// exitFP low bit is tagged and we can test hasWasmExitFP().
|
||||
// - we're in wasm code, so we can do a lookup on PC.
|
||||
// - in all the other cases, we're not in wasm or we haven't exited from
|
||||
// wasm.
|
||||
if (activation->hasWasmExitFP() || wasm::InCompiledCode(state.pc)) {
|
||||
new (storage()) wasm::ProfilingFrameIterator(*activation, state);
|
||||
kind_ = Kind::Wasm;
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(activation_->asJit()->isActive());
|
||||
new (storage()) jit::JitProfilingFrameIterator(cx_, state);
|
||||
kind_ = Kind::JSJit;
|
||||
}
|
||||
|
||||
void
|
||||
JS::ProfilingFrameIterator::iteratorConstruct()
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
|
||||
MOZ_ASSERT(activation_->isJit());
|
||||
|
||||
if (activation_->isWasm()) {
|
||||
new (storage()) wasm::ProfilingFrameIterator(*activation_->asWasm());
|
||||
jit::JitActivation* activation = activation_->asJit();
|
||||
MOZ_ASSERT(activation->isActive());
|
||||
|
||||
// The same reasoning as in the above iteratorConstruct variant applies
|
||||
// here, except that it's even simpler: since this activation is higher up
|
||||
// on the stack, it can only have exited to C++, through wasm or ion.
|
||||
if (activation->hasWasmExitFP()) {
|
||||
new (storage()) wasm::ProfilingFrameIterator(*activation);
|
||||
kind_ = Kind::Wasm;
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(activation_->asJit()->isActive());
|
||||
new (storage()) jit::JitProfilingFrameIterator(activation_->asJit()->exitFP());
|
||||
new (storage()) jit::JitProfilingFrameIterator(activation->jsExitFP());
|
||||
kind_ = Kind::JSJit;
|
||||
}
|
||||
|
||||
void
|
||||
JS::ProfilingFrameIterator::iteratorDestroy()
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
|
||||
MOZ_ASSERT(activation_->isJit());
|
||||
|
||||
if (activation_->isWasm()) {
|
||||
if (isWasm()) {
|
||||
wasmIter().~ProfilingFrameIterator();
|
||||
return;
|
||||
}
|
||||
@ -2003,9 +2043,9 @@ bool
|
||||
JS::ProfilingFrameIterator::iteratorDone()
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
|
||||
MOZ_ASSERT(activation_->isJit());
|
||||
|
||||
if (activation_->isWasm())
|
||||
if (isWasm())
|
||||
return wasmIter().done();
|
||||
|
||||
return jitIter().done();
|
||||
@ -2015,9 +2055,9 @@ void*
|
||||
JS::ProfilingFrameIterator::stackAddress() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
|
||||
MOZ_ASSERT(activation_->isJit());
|
||||
|
||||
if (activation_->isWasm())
|
||||
if (isWasm())
|
||||
return wasmIter().stackAddress();
|
||||
|
||||
return jitIter().stackAddress();
|
||||
@ -2108,11 +2148,11 @@ bool
|
||||
JS::ProfilingFrameIterator::isWasm() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
return activation_->isWasm();
|
||||
return kind_ == Kind::Wasm;
|
||||
}
|
||||
|
||||
bool
|
||||
JS::ProfilingFrameIterator::isJit() const
|
||||
{
|
||||
return activation_->isJit();
|
||||
return kind_ == Kind::JSJit;
|
||||
}
|
||||
|
@ -1241,7 +1241,6 @@ static_assert(sizeof(LiveSavedFrameCache) == sizeof(uintptr_t),
|
||||
/*****************************************************************************/
|
||||
|
||||
class InterpreterActivation;
|
||||
class WasmActivation;
|
||||
|
||||
namespace jit {
|
||||
class JitActivation;
|
||||
@ -1305,7 +1304,7 @@ class Activation
|
||||
// callFunctionWithAsyncStack.
|
||||
bool asyncCallIsExplicit_;
|
||||
|
||||
enum Kind { Interpreter, Jit, Wasm };
|
||||
enum Kind { Interpreter, Jit };
|
||||
Kind kind_;
|
||||
|
||||
inline Activation(JSContext* cx, Kind kind);
|
||||
@ -1330,9 +1329,7 @@ class Activation
|
||||
bool isJit() const {
|
||||
return kind_ == Jit;
|
||||
}
|
||||
bool isWasm() const {
|
||||
return kind_ == Wasm;
|
||||
}
|
||||
inline bool hasWasmExitFP() const;
|
||||
|
||||
inline bool isProfiling() const;
|
||||
void registerProfiling();
|
||||
@ -1346,10 +1343,6 @@ class Activation
|
||||
MOZ_ASSERT(isJit());
|
||||
return (jit::JitActivation*)this;
|
||||
}
|
||||
WasmActivation* asWasm() const {
|
||||
MOZ_ASSERT(isWasm());
|
||||
return (WasmActivation*)this;
|
||||
}
|
||||
|
||||
void hideScriptedCaller() {
|
||||
hideScriptedCallerCount_++;
|
||||
@ -1491,8 +1484,13 @@ class BailoutFrameInfo;
|
||||
// A JitActivation is used for frames running in Baseline or Ion.
|
||||
class JitActivation : public Activation
|
||||
{
|
||||
// If Baseline or Ion code is on the stack, and has called into C++, this
|
||||
// will be aligned to an ExitFrame.
|
||||
public:
|
||||
static const uintptr_t ExitFpWasmBit = 0x1;
|
||||
|
||||
private:
|
||||
// If Baseline, Ion or Wasm code is on the stack, and has called into C++,
|
||||
// this will be aligned to an ExitFrame. The last bit indicates if it's a
|
||||
// wasm frame (bit set to ExitFpWasmBit) or not (bit set to !ExitFpWasmBit).
|
||||
uint8_t* packedExitFP_;
|
||||
|
||||
JitActivation* prevJitActivation_;
|
||||
@ -1563,14 +1561,18 @@ class JitActivation : public Activation
|
||||
return offsetof(JitActivation, prevJitActivation_);
|
||||
}
|
||||
|
||||
uint8_t* packedExitFP() const {
|
||||
return packedExitFP_;
|
||||
bool hasExitFP() const {
|
||||
return !!packedExitFP_;
|
||||
}
|
||||
static size_t offsetOfPackedExitFP() {
|
||||
return offsetof(JitActivation, packedExitFP_);
|
||||
}
|
||||
|
||||
bool hasJSExitFP() const {
|
||||
return !(uintptr_t(packedExitFP_) & ExitFpWasmBit);
|
||||
}
|
||||
uint8_t* jsExitFP() const {
|
||||
MOZ_ASSERT(hasJSExitFP());
|
||||
return packedExitFP_;
|
||||
}
|
||||
void setJSExitFP(uint8_t* fp) {
|
||||
@ -1661,6 +1663,33 @@ class JitActivation : public Activation
|
||||
void setLastProfilingCallSite(void* ptr) {
|
||||
lastProfilingCallSite_ = ptr;
|
||||
}
|
||||
|
||||
// WebAssembly specific attributes.
|
||||
bool hasWasmExitFP() const {
|
||||
return uintptr_t(packedExitFP_) & ExitFpWasmBit;
|
||||
}
|
||||
wasm::Frame* wasmExitFP() const {
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
return (wasm::Frame*)(uintptr_t(packedExitFP_) & ~ExitFpWasmBit);
|
||||
}
|
||||
void setWasmExitFP(const wasm::Frame* fp) {
|
||||
if (fp) {
|
||||
MOZ_ASSERT(!(uintptr_t(fp) & ExitFpWasmBit));
|
||||
packedExitFP_ = (uint8_t*)(uintptr_t(fp) | ExitFpWasmBit);
|
||||
MOZ_ASSERT(hasWasmExitFP());
|
||||
} else {
|
||||
packedExitFP_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 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* wasmUnwindPC() const;
|
||||
void* wasmResumePC() const;
|
||||
};
|
||||
|
||||
// A filtering of the ActivationIterator to only stop at JitActivations.
|
||||
@ -1693,6 +1722,12 @@ class JitActivationIterator : public ActivationIterator
|
||||
|
||||
} // namespace jit
|
||||
|
||||
inline bool
|
||||
Activation::hasWasmExitFP() const
|
||||
{
|
||||
return isJit() && asJit()->hasWasmExitFP();
|
||||
}
|
||||
|
||||
// Iterates over the frames of a single InterpreterActivation.
|
||||
class InterpreterFrameIterator
|
||||
{
|
||||
@ -1735,42 +1770,6 @@ class InterpreterFrameIterator
|
||||
}
|
||||
};
|
||||
|
||||
// An eventual goal is to remove WasmActivation and to run asm code in a
|
||||
// JitActivation interleaved with Ion/Baseline jit code. This would allow
|
||||
// efficient calls back and forth but requires that we can walk the stack for
|
||||
// all kinds of jit code.
|
||||
class WasmActivation : public Activation
|
||||
{
|
||||
wasm::Frame* exitFP_;
|
||||
|
||||
public:
|
||||
explicit WasmActivation(JSContext* cx);
|
||||
~WasmActivation();
|
||||
|
||||
bool isProfiling() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns null or the final wasm::Frame* when wasm exited this
|
||||
// WasmActivation.
|
||||
wasm::Frame* exitFP() const { return exitFP_; }
|
||||
|
||||
// Written by JIT code:
|
||||
static unsigned offsetOfExitFP() { return offsetof(WasmActivation, exitFP_); }
|
||||
|
||||
// Interrupts are started from the interrupt signal handler (or the ARM
|
||||
// simulator) and cleared by WasmHandleExecutionInterrupt or WasmHandleThrow
|
||||
// when the interrupt is handled.
|
||||
void startInterrupt(const JS::ProfilingFrameIterator::RegisterState& state);
|
||||
void finishInterrupt();
|
||||
bool interrupted() const;
|
||||
void* unwindPC() const;
|
||||
void* resumePC() const;
|
||||
|
||||
// Used by wasm::WasmFrameIter during stack unwinding.
|
||||
void unwindExitFP(wasm::Frame* exitFP);
|
||||
};
|
||||
|
||||
// A JitFrameIter can iterate over all kind of frames emitted by our code
|
||||
// generators, be they composed of JS jit frames or wasm frames, interleaved or
|
||||
// not, in any order.
|
||||
@ -1798,11 +1797,14 @@ class WasmActivation : public Activation
|
||||
class JitFrameIter
|
||||
{
|
||||
protected:
|
||||
jit::JitActivation* act_;
|
||||
mozilla::MaybeOneOf<jit::JSJitFrameIter, wasm::WasmFrameIter> iter_;
|
||||
|
||||
void settle();
|
||||
|
||||
public:
|
||||
JitFrameIter() : iter_() {}
|
||||
explicit JitFrameIter(Activation* activation);
|
||||
JitFrameIter() : act_(nullptr), iter_() {}
|
||||
explicit JitFrameIter(jit::JitActivation* activation);
|
||||
|
||||
explicit JitFrameIter(const JitFrameIter& another);
|
||||
JitFrameIter& operator=(const JitFrameIter& another);
|
||||
@ -1819,6 +1821,7 @@ class JitFrameIter
|
||||
const wasm::WasmFrameIter& asWasm() const { return iter_.ref<wasm::WasmFrameIter>(); }
|
||||
|
||||
// Operations common to all frame iterators.
|
||||
const jit::JitActivation* activation() const { return act_; }
|
||||
bool done() const;
|
||||
void operator++();
|
||||
|
||||
@ -1837,7 +1840,7 @@ class OnlyJSJitFrameIter : public JitFrameIter
|
||||
}
|
||||
|
||||
public:
|
||||
explicit OnlyJSJitFrameIter(Activation* act);
|
||||
explicit OnlyJSJitFrameIter(jit::JitActivation* act);
|
||||
explicit OnlyJSJitFrameIter(JSContext* cx);
|
||||
explicit OnlyJSJitFrameIter(const ActivationIterator& cx);
|
||||
|
||||
|
@ -260,20 +260,27 @@ TraceLoggerThread::enable(JSContext* cx)
|
||||
int32_t engine = 0;
|
||||
|
||||
if (act->isJit()) {
|
||||
JSJitFrameIter frame(iter->asJit());
|
||||
JitFrameIter frame(iter->asJit());
|
||||
|
||||
while (!frame.isScripted() && !frame.done())
|
||||
while (!frame.done()) {
|
||||
if (frame.isWasm()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_TRACELOGGER_ENABLE_FAIL,
|
||||
"not yet supported in wasm code");
|
||||
return false;
|
||||
}
|
||||
if (frame.asJSJit().isScripted())
|
||||
break;
|
||||
++frame;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!frame.done());
|
||||
MOZ_ASSERT(frame.isIonJS() || frame.isBaselineJS());
|
||||
|
||||
script = frame.script();
|
||||
engine = frame.isIonJS() ? TraceLogger_IonMonkey : TraceLogger_Baseline;
|
||||
} else if (act->isWasm()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TRACELOGGER_ENABLE_FAIL,
|
||||
"not yet supported in wasm code");
|
||||
return false;
|
||||
const JSJitFrameIter& jitFrame = frame.asJSJit();
|
||||
MOZ_ASSERT(jitFrame.isIonJS() || jitFrame.isBaselineJS());
|
||||
|
||||
script = jitFrame.script();
|
||||
engine = jitFrame.isIonJS() ? TraceLogger_IonMonkey : TraceLogger_Baseline;
|
||||
} else {
|
||||
MOZ_ASSERT(act->isInterpreter());
|
||||
InterpreterFrame* fp = act->asInterpreter()->current();
|
||||
|
@ -506,6 +506,11 @@ static const unsigned MaxMemoryMaximumPages = 65536;
|
||||
static const unsigned MaxModuleBytes = 1024 * 1024 * 1024;
|
||||
static const unsigned MaxFunctionBytes = 128 * 1024;
|
||||
|
||||
// A magic value of the FramePointer to indicate after a return to the entry
|
||||
// stub that an exception has been caught and that we should throw.
|
||||
|
||||
static const unsigned FailFP = 0xbad;
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
|
@ -61,43 +61,42 @@ __aeabi_uidivmod(int, int);
|
||||
#endif
|
||||
|
||||
// This utility function can only be called for builtins that are called
|
||||
// directly from wasm code. Note that WasmCall pushes both an outer
|
||||
// WasmActivation and an inner JitActivation that becomes active when calling
|
||||
// JIT code.
|
||||
static WasmActivation*
|
||||
// directly from wasm code.
|
||||
static JitActivation*
|
||||
CallingActivation()
|
||||
{
|
||||
Activation* act = TlsContext.get()->activation();
|
||||
MOZ_ASSERT(!act->asJit()->isActive(), "WasmCall pushes an inactive JitActivation");
|
||||
return act->prev()->asWasm();
|
||||
MOZ_ASSERT(act->asJit()->hasWasmExitFP());
|
||||
MOZ_ASSERT(act->asJit()->isActive(), "WasmCall pushes an active JitActivation");
|
||||
return act->asJit();
|
||||
}
|
||||
|
||||
static void*
|
||||
WasmHandleExecutionInterrupt()
|
||||
{
|
||||
WasmActivation* activation = CallingActivation();
|
||||
MOZ_ASSERT(activation->interrupted());
|
||||
JitActivation* activation = CallingActivation();
|
||||
MOZ_ASSERT(activation->isWasmInterrupted());
|
||||
|
||||
if (!CheckForInterrupt(activation->cx())) {
|
||||
// If CheckForInterrupt failed, it is time to interrupt execution.
|
||||
// Returning nullptr to the caller will jump to the throw stub which
|
||||
// will call WasmHandleThrow. The WasmActivation must stay in the
|
||||
// will call HandleThrow. The JitActivation must stay in the
|
||||
// interrupted state until then so that stack unwinding works in
|
||||
// WasmHandleThrow.
|
||||
// HandleThrow.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If CheckForInterrupt succeeded, then execution can proceed and the
|
||||
// interrupt is over.
|
||||
void* resumePC = activation->resumePC();
|
||||
activation->finishInterrupt();
|
||||
void* resumePC = activation->wasmResumePC();
|
||||
activation->finishWasmInterrupt();
|
||||
return resumePC;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmHandleDebugTrap()
|
||||
{
|
||||
WasmActivation* activation = CallingActivation();
|
||||
JitActivation* activation = CallingActivation();
|
||||
MOZ_ASSERT(activation);
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
@ -162,12 +161,10 @@ WasmHandleDebugTrap()
|
||||
// is responsible for notifying the debugger of each unwound frame. The return
|
||||
// value is the new stack address which the calling stub will set to the sp
|
||||
// register before executing a return instruction.
|
||||
static void*
|
||||
WasmHandleThrow()
|
||||
{
|
||||
WasmActivation* activation = CallingActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
void*
|
||||
wasm::HandleThrow(JSContext* cx, WasmFrameIter& iter)
|
||||
{
|
||||
// WasmFrameIter iterates down wasm frames in the activation starting at
|
||||
// JitActivation::wasmExitFP(). Pass Unwind::True to pop
|
||||
// JitActivation::wasmExitFP() once each time WasmFrameIter is incremented,
|
||||
@ -180,10 +177,11 @@ WasmHandleThrow()
|
||||
// we'll be able to switch to ion / other wasm state from here, and we'll
|
||||
// need to do things differently.
|
||||
|
||||
WasmFrameIter iter(activation, WasmFrameIter::Unwind::True);
|
||||
MOZ_ASSERT(CallingActivation() == iter.activation());
|
||||
MOZ_ASSERT(!iter.done());
|
||||
iter.setUnwind(WasmFrameIter::Unwind::True);
|
||||
|
||||
// Live wasm code on the stack is kept alive (in wasm::TraceActivations) by
|
||||
// Live wasm code on the stack is kept alive (in TraceJitActivation) by
|
||||
// marking the instance of every wasm::Frame found by WasmFrameIter.
|
||||
// However, as explained above, we're popping frames while iterating which
|
||||
// means that a GC during this loop could collect the code of frames whose
|
||||
@ -220,12 +218,22 @@ WasmHandleThrow()
|
||||
JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
|
||||
}
|
||||
frame->leave(cx);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!cx->activation()->asJit()->isWasmInterrupted(), "unwinding clears the interrupt");
|
||||
|
||||
MOZ_ASSERT(!activation->interrupted(), "unwinding clears the interrupt");
|
||||
return iter.unwoundAddressOfReturnAddress();
|
||||
}
|
||||
|
||||
static void*
|
||||
WasmHandleThrow()
|
||||
{
|
||||
JitActivation* activation = CallingActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
WasmFrameIter iter(activation);
|
||||
return HandleThrow(cx, iter);
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportTrap(int32_t trapIndex)
|
||||
{
|
||||
|
@ -24,6 +24,8 @@
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
class WasmFrameIter;
|
||||
|
||||
// A SymbolicAddress that NeedsBuiltinThunk() will call through a thunk to the
|
||||
// C++ function. This will be true for all normal calls from normal wasm
|
||||
// function code. Only calls to C++ from other exits/thunks do not need a thunk.
|
||||
@ -47,6 +49,9 @@ LookupBuiltinThunk(void* pc, const CodeRange** codeRange, uint8_t** codeBase);
|
||||
bool
|
||||
EnsureBuiltinThunksInitialized();
|
||||
|
||||
void*
|
||||
HandleThrow(JSContext* cx, WasmFrameIter& iter);
|
||||
|
||||
void*
|
||||
SymbolicAddressTarget(SymbolicAddress sym);
|
||||
|
||||
|
@ -22,9 +22,6 @@
|
||||
#include "wasm/WasmJS.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
class Code;
|
||||
@ -41,8 +38,6 @@ class Compartment
|
||||
InstanceVector instances_;
|
||||
volatile bool mutatingInstances_;
|
||||
|
||||
friend class js::WasmActivation;
|
||||
|
||||
struct AutoMutateInstances {
|
||||
Compartment &c;
|
||||
explicit AutoMutateInstances(Compartment& c) : c(c) {
|
||||
|
@ -26,7 +26,6 @@
|
||||
namespace js {
|
||||
|
||||
class Debugger;
|
||||
class WasmActivation;
|
||||
class WasmBreakpoint;
|
||||
class WasmBreakpointSite;
|
||||
class WasmInstanceObject;
|
||||
|
@ -32,22 +32,22 @@ using mozilla::Swap;
|
||||
/*****************************************************************************/
|
||||
// WasmFrameIter implementation
|
||||
|
||||
WasmFrameIter::WasmFrameIter(WasmActivation* activation, Unwind unwind)
|
||||
WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp)
|
||||
: activation_(activation),
|
||||
code_(nullptr),
|
||||
callsite_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
fp_(activation->exitFP()),
|
||||
unwind_(unwind)
|
||||
fp_(fp ? fp : activation->wasmExitFP()),
|
||||
unwind_(Unwind::False)
|
||||
{
|
||||
MOZ_ASSERT(fp_);
|
||||
|
||||
// Normally, execution exits wasm code via an exit stub which sets exitFP to
|
||||
// the exit stub's frame. Thus, in this case, we want to start iteration at
|
||||
// the caller of the exit frame, whose Code, CodeRange and CallSite are
|
||||
// Normally, execution exits wasm code via an exit stub which sets exitFP
|
||||
// to the exit stub's frame. Thus, in this case, we want to start iteration
|
||||
// at the caller of the exit frame, whose Code, CodeRange and CallSite are
|
||||
// indicated by the returnAddress of the exit stub's frame.
|
||||
|
||||
if (!activation->interrupted()) {
|
||||
if (!activation->isWasmInterrupted()) {
|
||||
popFrame();
|
||||
MOZ_ASSERT(!done());
|
||||
return;
|
||||
@ -55,15 +55,16 @@ WasmFrameIter::WasmFrameIter(WasmActivation* activation, Unwind unwind)
|
||||
|
||||
// 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 WasmActivation, which are set when control
|
||||
// flow was interrupted. There is no CallSite (b/c the interrupt was async),
|
||||
// but this is fine because CallSite is only used for line number for which
|
||||
// we can use the beginning of the function from the CodeRange instead.
|
||||
// Code and CodeRange from the JitActivation, which are set when control
|
||||
// flow was interrupted. There is no CallSite (b/c the interrupt was
|
||||
// async), but this is fine because CallSite is only used for line number
|
||||
// for which we can use the beginning of the function from the CodeRange
|
||||
// instead.
|
||||
|
||||
code_ = &fp_->tls->instance->code();
|
||||
MOZ_ASSERT(code_ == activation->compartment()->wasm.lookupCode(activation->unwindPC()));
|
||||
MOZ_ASSERT(code_ == activation->compartment()->wasm.lookupCode(activation->wasmUnwindPC()));
|
||||
|
||||
codeRange_ = code_->lookupRange(activation->unwindPC());
|
||||
codeRange_ = code_->lookupRange(activation->wasmUnwindPC());
|
||||
MOZ_ASSERT(codeRange_->kind() == CodeRange::Function);
|
||||
|
||||
MOZ_ASSERT(!done());
|
||||
@ -82,20 +83,20 @@ WasmFrameIter::operator++()
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
|
||||
// When the iterator is set to unwind, each time the iterator pops a
|
||||
// frame, the WasmActivation is updated so that the just-popped frame
|
||||
// is no longer visible. This is necessary since Debugger::onLeaveFrame is
|
||||
// called before popping each frame and, once onLeaveFrame is called for a
|
||||
// given frame, that frame must not be visible to subsequent stack iteration
|
||||
// (or it could be added as a "new" frame just as it becomes garbage).
|
||||
// When the frame is "interrupted", then exitFP is included in the callstack
|
||||
// When the iterator is set to unwind, each time the iterator pops a frame,
|
||||
// the JitActivation is updated so that the just-popped frame is no longer
|
||||
// visible. This is necessary since Debugger::onLeaveFrame is called before
|
||||
// popping each frame and, once onLeaveFrame is called for a given frame,
|
||||
// that frame must not be visible to subsequent stack iteration (or it
|
||||
// could be added as a "new" frame just as it becomes garbage). When the
|
||||
// frame is "interrupted", then exitFP is included in the callstack
|
||||
// (otherwise, it is skipped, as explained above). So to unwind the
|
||||
// innermost frame, we just clear the interrupt state.
|
||||
|
||||
if (unwind_ == Unwind::True) {
|
||||
if (activation_->interrupted())
|
||||
activation_->finishInterrupt();
|
||||
activation_->unwindExitFP(fp_);
|
||||
if (activation_->isWasmInterrupted())
|
||||
activation_->finishWasmInterrupt();
|
||||
activation_->setWasmExitFP(fp_);
|
||||
}
|
||||
|
||||
popFrame();
|
||||
@ -106,6 +107,7 @@ WasmFrameIter::popFrame()
|
||||
{
|
||||
Frame* prevFP = fp_;
|
||||
fp_ = prevFP->callerFP;
|
||||
MOZ_ASSERT(!(uintptr_t(fp_) & JitActivation::ExitFpWasmBit));
|
||||
|
||||
if (!fp_) {
|
||||
code_ = nullptr;
|
||||
@ -113,7 +115,8 @@ WasmFrameIter::popFrame()
|
||||
callsite_ = nullptr;
|
||||
|
||||
if (unwind_ == Unwind::True) {
|
||||
activation_->unwindExitFP(nullptr);
|
||||
// TODO with bug 1319203, there may be other JIT frames above.
|
||||
activation_->setWasmExitFP(nullptr);
|
||||
unwoundAddressOfReturnAddress_ = &prevFP->returnAddress;
|
||||
}
|
||||
|
||||
@ -175,7 +178,7 @@ unsigned
|
||||
WasmFrameIter::lineOrBytecode() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT_IF(!callsite_, activation_->interrupted());
|
||||
MOZ_ASSERT_IF(!callsite_, activation_->isWasmInterrupted());
|
||||
return callsite_ ? callsite_->lineOrBytecode() : codeRange_->funcLineOrBytecode();
|
||||
}
|
||||
|
||||
@ -305,14 +308,28 @@ PushRetAddr(MacroAssembler& masm, unsigned entry)
|
||||
}
|
||||
|
||||
static void
|
||||
LoadActivation(MacroAssembler& masm, Register dest)
|
||||
LoadActivation(MacroAssembler& masm, const Register& dest)
|
||||
{
|
||||
// WasmCall pushes a WasmActivation and an inactive JitActivation. The
|
||||
// JitActivation only becomes active when calling into JS from wasm.
|
||||
// WasmCall pushes a JitActivation.
|
||||
masm.loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, addressOfContext)), dest);
|
||||
masm.loadPtr(Address(dest, 0), dest);
|
||||
masm.loadPtr(Address(dest, JSContext::offsetOfActivation()), dest);
|
||||
masm.loadPtr(Address(dest, Activation::offsetOfPrev()), dest);
|
||||
}
|
||||
|
||||
void
|
||||
wasm::SetExitFP(MacroAssembler& masm, Register scratch)
|
||||
{
|
||||
masm.orPtr(Imm32(JitActivation::ExitFpWasmBit), FramePointer);
|
||||
LoadActivation(masm, scratch);
|
||||
masm.storePtr(FramePointer, Address(scratch, JitActivation::offsetOfPackedExitFP()));
|
||||
masm.andPtr(Imm32(int32_t(~JitActivation::ExitFpWasmBit)), FramePointer);
|
||||
}
|
||||
|
||||
void
|
||||
wasm::ClearExitFP(MacroAssembler& masm, Register scratch)
|
||||
{
|
||||
LoadActivation(masm, scratch);
|
||||
masm.storePtr(ImmWord(0x0), Address(scratch, JitActivation::offsetOfPackedExitFP()));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -366,7 +383,9 @@ GenerateCallablePrologue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
*tierEntry = masm.currentOffset();
|
||||
}
|
||||
|
||||
if (!reason.isNone()) {
|
||||
// To make the jit exit faster, we don't set exitFP although we set the
|
||||
// exit reason: the profiling iteration knows how to recover FP.
|
||||
if (!reason.isNone() && !(reason.isFixed() && reason.fixed() == ExitReason::Fixed::ImportJit)) {
|
||||
Register act = ABINonArgReg0;
|
||||
|
||||
// Native callers expect the native ABI, which assume that non-saved
|
||||
@ -375,9 +394,7 @@ GenerateCallablePrologue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
if (reason.isNative() && !act.volatile_())
|
||||
masm.Push(act);
|
||||
|
||||
LoadActivation(masm, act);
|
||||
masm.wasmAssertNonExitInvariants(act);
|
||||
masm.storePtr(FramePointer, Address(act, WasmActivation::offsetOfExitFP()));
|
||||
SetExitFP(masm, act);
|
||||
|
||||
if (reason.isNative() && !act.volatile_())
|
||||
masm.Pop(act);
|
||||
@ -401,8 +418,7 @@ GenerateCallableEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
if (reason.isNative() && !act.volatile_())
|
||||
masm.Push(act);
|
||||
|
||||
LoadActivation(masm, act);
|
||||
masm.storePtr(ImmWord(0), Address(act, WasmActivation::offsetOfExitFP()));
|
||||
ClearExitFP(masm, act);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Check the passed exitReason is the same as the one on entry.
|
||||
@ -518,6 +534,31 @@ wasm::GenerateExitEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReaso
|
||||
masm.setFramePushed(0);
|
||||
}
|
||||
|
||||
void
|
||||
wasm::GenerateJitExitPrologue(MacroAssembler& masm, unsigned framePushed, CallableOffsets* offsets)
|
||||
{
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
GenerateCallablePrologue(masm, framePushed, ExitReason(ExitReason::Fixed::ImportJit),
|
||||
&offsets->begin, nullptr, CompileMode::Once, 0);
|
||||
masm.setFramePushed(framePushed);
|
||||
}
|
||||
|
||||
void
|
||||
wasm::GenerateJitExitEpilogue(MacroAssembler& masm, unsigned framePushed, CallableOffsets* offsets)
|
||||
{
|
||||
// Inverse of GenerateJitExitPrologue:
|
||||
MOZ_ASSERT(masm.framePushed() == framePushed);
|
||||
|
||||
#ifdef DEBUG
|
||||
Register scratch = ABINonArgReturnReg0;
|
||||
LoadActivation(masm, scratch);
|
||||
masm.wasmAssertNonExitInvariants(scratch);
|
||||
#endif
|
||||
|
||||
GenerateCallableEpilogue(masm, framePushed, ExitReason::None(), &offsets->ret);
|
||||
masm.setFramePushed(0);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// ProfilingFrameIterator
|
||||
|
||||
@ -533,7 +574,7 @@ ProfilingFrameIterator::ProfilingFrameIterator()
|
||||
MOZ_ASSERT(done());
|
||||
}
|
||||
|
||||
ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation)
|
||||
ProfilingFrameIterator::ProfilingFrameIterator(const JitActivation& activation, const Frame* fp)
|
||||
: activation_(&activation),
|
||||
code_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
@ -542,11 +583,11 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation)
|
||||
stackAddress_(nullptr),
|
||||
exitReason_(ExitReason::Fixed::None)
|
||||
{
|
||||
initFromExitFP();
|
||||
initFromExitFP(fp);
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertMatchesCallSite(const WasmActivation& activation, void* callerPC, Frame* callerFP)
|
||||
AssertMatchesCallSite(const JitActivation& activation, void* callerPC, Frame* callerFP)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
const Code* code = activation.compartment()->wasm.lookupCode(callerPC);
|
||||
@ -566,16 +607,18 @@ AssertMatchesCallSite(const WasmActivation& activation, void* callerPC, Frame* c
|
||||
}
|
||||
|
||||
void
|
||||
ProfilingFrameIterator::initFromExitFP()
|
||||
ProfilingFrameIterator::initFromExitFP(const Frame* fp)
|
||||
{
|
||||
Frame* fp = activation_->exitFP();
|
||||
if (!fp)
|
||||
fp = activation_->wasmExitFP();
|
||||
|
||||
void* pc = fp->returnAddress;
|
||||
|
||||
// The iterator inserts a pretend innermost frame for ExitReasons.
|
||||
// This allows the variety of exit reasons to show up in the callstack.
|
||||
exitReason_ = ExitReason::Decode(fp->encodedExitReason);
|
||||
|
||||
stackAddress_ = fp;
|
||||
stackAddress_ = (void*)fp;
|
||||
|
||||
code_ = activation_->compartment()->wasm.lookupCode(pc);
|
||||
MOZ_ASSERT(code_);
|
||||
@ -618,18 +661,21 @@ ProfilingFrameIterator::initFromExitFP()
|
||||
}
|
||||
|
||||
bool
|
||||
js::wasm::StartUnwinding(const WasmActivation& activation, const RegisterState& registers,
|
||||
js::wasm::StartUnwinding(const JitActivation& activation, const RegisterState& registers,
|
||||
UnwindState* unwindState, bool* unwoundCaller)
|
||||
{
|
||||
// Shorthands.
|
||||
uint8_t* const pc = (uint8_t*) registers.pc;
|
||||
Frame* const fp = (Frame*) registers.fp;
|
||||
void** const sp = (void**) registers.sp;
|
||||
|
||||
// The frame pointer might be in the process of tagging/untagging; make
|
||||
// sure it's untagged.
|
||||
Frame* const fp = (Frame*) (intptr_t(registers.fp) & ~JitActivation::ExitFpWasmBit);
|
||||
|
||||
// Get the CodeRange describing pc and the base address to which the
|
||||
// CodeRange is relative. If the pc is not in a wasm module or a builtin
|
||||
// thunk, then execution must be entering from or leaving to the C++ caller
|
||||
// that pushed the WasmActivation.
|
||||
// that pushed the JitActivation.
|
||||
const CodeRange* codeRange;
|
||||
uint8_t* codeBase;
|
||||
const Code* code = activation.compartment()->wasm.lookupCode(pc);
|
||||
@ -731,6 +777,16 @@ js::wasm::StartUnwinding(const WasmActivation& activation, const RegisterState&
|
||||
fixedFP = fp;
|
||||
AssertMatchesCallSite(activation, fixedPC, fixedFP);
|
||||
} else {
|
||||
if (codeRange->kind() == CodeRange::ImportJitExit) {
|
||||
// The jit exit contains a range where the value of FP can't be
|
||||
// trusted. Technically, we could recover fp from sp, but since
|
||||
// the range is so short, for now just drop the stack.
|
||||
if (offsetInCode >= codeRange->jitExitUntrustedFPStart() &&
|
||||
offsetInCode < codeRange->jitExitUntrustedFPEnd())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Not in the prologue/epilogue.
|
||||
fixedPC = pc;
|
||||
fixedFP = fp;
|
||||
@ -749,9 +805,9 @@ js::wasm::StartUnwinding(const WasmActivation& activation, const RegisterState&
|
||||
AssertMatchesCallSite(activation, fp->returnAddress, fp->callerFP);
|
||||
break;
|
||||
case CodeRange::Entry:
|
||||
// The entry trampoline is the final frame in an WasmActivation. The entry
|
||||
// trampoline also doesn't GeneratePrologue/Epilogue so we can't use
|
||||
// the general unwinding logic above.
|
||||
// The entry trampoline is the final frame in an wasm JitActivation. The
|
||||
// entry trampoline also doesn't GeneratePrologue/Epilogue so we can't
|
||||
// use the general unwinding logic above.
|
||||
break;
|
||||
case CodeRange::Throw:
|
||||
// The throw stub executes a small number of instructions before popping
|
||||
@ -772,7 +828,7 @@ js::wasm::StartUnwinding(const WasmActivation& activation, const RegisterState&
|
||||
return true;
|
||||
}
|
||||
|
||||
ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
||||
ProfilingFrameIterator::ProfilingFrameIterator(const JitActivation& activation,
|
||||
const RegisterState& state)
|
||||
: activation_(&activation),
|
||||
code_(nullptr),
|
||||
@ -784,7 +840,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
||||
{
|
||||
// In the case of ImportJitExit, the fp register may be temporarily
|
||||
// clobbered on return from Ion so always use activation.fp when it is set.
|
||||
if (activation.exitFP()) {
|
||||
if (activation.hasWasmExitFP()) {
|
||||
initFromExitFP();
|
||||
return;
|
||||
}
|
||||
@ -1052,19 +1108,6 @@ wasm::LookupFaultingInstance(const Code& code, void* pc, void* fp)
|
||||
return instance;
|
||||
}
|
||||
|
||||
WasmActivation*
|
||||
wasm::ActivationIfInnermost(JSContext* cx)
|
||||
{
|
||||
// WasmCall pushes both an outer WasmActivation and an inner JitActivation
|
||||
// that only becomes active when calling JIT code.
|
||||
Activation* act = cx->activation();
|
||||
while (act && act->isJit() && !act->asJit()->isActive())
|
||||
act = act->prev();
|
||||
if (!act || !act->isWasm())
|
||||
return nullptr;
|
||||
return act->asWasm();
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::InCompiledCode(void* pc)
|
||||
{
|
||||
|
@ -26,8 +26,10 @@ class JSAtom;
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
namespace jit { class MacroAssembler; }
|
||||
namespace jit {
|
||||
class MacroAssembler;
|
||||
struct Register;
|
||||
} // namespace jit
|
||||
|
||||
namespace wasm {
|
||||
|
||||
@ -42,7 +44,7 @@ struct Frame;
|
||||
struct FuncOffsets;
|
||||
struct CallableOffsets;
|
||||
|
||||
// Iterates over a linear group of wasm frames of a single WasmActivation,
|
||||
// Iterates over a linear group of wasm frames of a single wasm JitActivation,
|
||||
// called synchronously from C++ in the wasm thread. It will stop at the first
|
||||
// frame that is not of the same kind, or at the end of an activation.
|
||||
//
|
||||
@ -61,7 +63,7 @@ class WasmFrameIter
|
||||
enum class Unwind { True, False };
|
||||
|
||||
private:
|
||||
WasmActivation* activation_;
|
||||
jit::JitActivation* activation_;
|
||||
const Code* code_;
|
||||
const CallSite* callsite_;
|
||||
const CodeRange* codeRange_;
|
||||
@ -73,7 +75,9 @@ class WasmFrameIter
|
||||
|
||||
public:
|
||||
// See comment above this class definition.
|
||||
explicit WasmFrameIter(WasmActivation* activation, Unwind unwind = Unwind::False);
|
||||
explicit WasmFrameIter(jit::JitActivation* activation, Frame* fp = nullptr);
|
||||
const jit::JitActivation* activation() const { return activation_; }
|
||||
void setUnwind(Unwind unwind) { unwind_ = unwind; }
|
||||
void operator++();
|
||||
bool done() const;
|
||||
const char* filename() const;
|
||||
@ -108,7 +112,7 @@ class ExitReason
|
||||
ImportJit, // fast-path call directly into JIT code
|
||||
ImportInterp, // slow-path call into C++ Invoke()
|
||||
BuiltinNative, // fast-path call directly into native C++ code
|
||||
Trap, // call to trap handler for the trap in WasmActivation::trap
|
||||
Trap, // call to trap handler
|
||||
DebugTrap // call to debug trap handler
|
||||
};
|
||||
|
||||
@ -150,11 +154,11 @@ class ExitReason
|
||||
}
|
||||
};
|
||||
|
||||
// Iterates over the frames of a single WasmActivation, given an
|
||||
// Iterates over the frames of a single wasm JitActivation, given an
|
||||
// asynchronously-interrupted thread's state.
|
||||
class ProfilingFrameIterator
|
||||
{
|
||||
const WasmActivation* activation_;
|
||||
const jit::JitActivation* activation_;
|
||||
const Code* code_;
|
||||
const CodeRange* codeRange_;
|
||||
Frame* callerFP_;
|
||||
@ -162,12 +166,13 @@ class ProfilingFrameIterator
|
||||
void* stackAddress_;
|
||||
ExitReason exitReason_;
|
||||
|
||||
void initFromExitFP();
|
||||
void initFromExitFP(const Frame* fp = nullptr);
|
||||
|
||||
public:
|
||||
ProfilingFrameIterator();
|
||||
explicit ProfilingFrameIterator(const WasmActivation& activation);
|
||||
ProfilingFrameIterator(const WasmActivation& activation,
|
||||
explicit ProfilingFrameIterator(const jit::JitActivation& activation,
|
||||
const Frame* fp = nullptr);
|
||||
ProfilingFrameIterator(const jit::JitActivation& activation,
|
||||
const JS::ProfilingFrameIterator::RegisterState& state);
|
||||
void operator++();
|
||||
bool done() const { return !codeRange_; }
|
||||
@ -178,6 +183,11 @@ class ProfilingFrameIterator
|
||||
|
||||
// Prologue/epilogue code generation
|
||||
|
||||
void
|
||||
SetExitFP(jit::MacroAssembler& masm, jit::Register scratch);
|
||||
void
|
||||
ClearExitFP(jit::MacroAssembler& masm, jit::Register scratch);
|
||||
|
||||
void
|
||||
GenerateExitPrologue(jit::MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
CallableOffsets* offsets);
|
||||
@ -185,6 +195,10 @@ void
|
||||
GenerateExitEpilogue(jit::MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
CallableOffsets* offsets);
|
||||
void
|
||||
GenerateJitExitPrologue(jit::MacroAssembler& masm, unsigned framePushed, CallableOffsets* offsets);
|
||||
void
|
||||
GenerateJitExitEpilogue(jit::MacroAssembler& masm, unsigned framePushed, CallableOffsets* offsets);
|
||||
void
|
||||
GenerateFunctionPrologue(jit::MacroAssembler& masm, unsigned framePushed, const SigIdDesc& sigId,
|
||||
FuncOffsets* offsets, CompileMode mode = CompileMode::Once,
|
||||
uint32_t funcIndex = 0);
|
||||
@ -197,11 +211,6 @@ GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOf
|
||||
Instance*
|
||||
LookupFaultingInstance(const Code& code, void* pc, void* fp);
|
||||
|
||||
// If the innermost (active) Activation is a WasmActivation, return it.
|
||||
|
||||
WasmActivation*
|
||||
ActivationIfInnermost(JSContext* cx);
|
||||
|
||||
// Return whether the given PC is in wasm code.
|
||||
|
||||
bool
|
||||
@ -232,7 +241,7 @@ typedef JS::ProfilingFrameIterator::RegisterState RegisterState;
|
||||
// frame should be ignored.
|
||||
|
||||
bool
|
||||
StartUnwinding(const WasmActivation& activation, const RegisterState& registers,
|
||||
StartUnwinding(const jit::JitActivation& activation, const RegisterState& registers,
|
||||
UnwindState* unwindState, bool* unwoundCaller);
|
||||
|
||||
} // namespace wasm
|
||||
|
@ -674,13 +674,7 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
|
||||
}
|
||||
|
||||
{
|
||||
// Push a WasmActivation to describe the wasm frames we're about to push
|
||||
// when running this module. Additionally, push a JitActivation so that
|
||||
// the optimized wasm-to-Ion FFI call path (which we want to be very
|
||||
// fast) can avoid doing so. The JitActivation is marked as inactive so
|
||||
// stack iteration will skip over it.
|
||||
WasmActivation activation(cx);
|
||||
JitActivation jitActivation(cx, /* active */ false);
|
||||
JitActivation activation(cx);
|
||||
|
||||
// Call the per-exported-function trampoline created by GenerateEntry.
|
||||
auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeBase(tier) + func.entryOffset());
|
||||
|
@ -781,7 +781,7 @@ ComputeAccessAddress(EMULATOR_CONTEXT* context, const Disassembler::ComplexAddre
|
||||
|
||||
MOZ_COLD static void
|
||||
HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
|
||||
const Instance& instance, WasmActivation* activation, uint8_t** ppc)
|
||||
const Instance& instance, JitActivation* activation, uint8_t** ppc)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(instance.code().containsCodePC(pc));
|
||||
|
||||
@ -792,7 +792,7 @@ HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddr
|
||||
// experimental SIMD.js or Atomics. When these are converted to
|
||||
// non-experimental wasm features, this case, as well as outOfBoundsCode,
|
||||
// can be removed.
|
||||
activation->startInterrupt(ToRegisterState(context));
|
||||
activation->startWasmInterrupt(ToRegisterState(context));
|
||||
if (!instance.code().containsCodePC(pc, &segment))
|
||||
MOZ_CRASH("Cannot map PC to trap handler");
|
||||
*ppc = segment->outOfBoundsCode();
|
||||
@ -930,7 +930,7 @@ HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddr
|
||||
|
||||
MOZ_COLD static void
|
||||
HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
|
||||
const Instance& instance, WasmActivation* activation, uint8_t** ppc)
|
||||
const Instance& instance, JitActivation* activation, uint8_t** ppc)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(instance.code().containsCodePC(pc));
|
||||
|
||||
@ -938,7 +938,7 @@ HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddr
|
||||
const MemoryAccess* memoryAccess = instance.code().lookupMemoryAccess(pc, &segment);
|
||||
if (!memoryAccess) {
|
||||
// See explanation in the WASM_HUGE_MEMORY HandleMemoryAccess.
|
||||
activation->startInterrupt(ToRegisterState(context));
|
||||
activation->startWasmInterrupt(ToRegisterState(context));
|
||||
if (!instance.code().containsCodePC(pc, &segment))
|
||||
MOZ_CRASH("Cannot map PC to trap handler");
|
||||
*ppc = segment->outOfBoundsCode();
|
||||
@ -990,9 +990,9 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||
return false;
|
||||
AutoSetHandlingSegFault handling(cx);
|
||||
|
||||
WasmActivation* activation = ActivationIfInnermost(cx);
|
||||
if (!activation)
|
||||
if (!cx->activation() || !cx->activation()->isJit())
|
||||
return false;
|
||||
JitActivation* activation = cx->activation()->asJit();
|
||||
|
||||
const CodeSegment* codeSegment;
|
||||
const Code* code = activation->compartment()->wasm.lookupCode(pc, &codeSegment);
|
||||
@ -1013,8 +1013,8 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||
|
||||
for (auto t : code->tiers()) {
|
||||
if (pc == code->segment(t).interruptCode() &&
|
||||
activation->interrupted() &&
|
||||
code->segment(t).containsCodePC(activation->resumePC()))
|
||||
activation->isWasmInterrupted() &&
|
||||
code->segment(t).containsCodePC(activation->wasmResumePC()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -1031,14 +1031,14 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||
|
||||
// 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, interrupted() is
|
||||
// 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->interrupted()) {
|
||||
MOZ_ASSERT(activation->resumePC() == pc);
|
||||
activation->finishInterrupt();
|
||||
if (activation->isWasmInterrupted()) {
|
||||
MOZ_ASSERT(activation->wasmResumePC() == pc);
|
||||
activation->finishWasmInterrupt();
|
||||
}
|
||||
|
||||
HandleMemoryAccess(context, pc, faultingAddress, *instance, activation, ppc);
|
||||
@ -1133,9 +1133,9 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request)
|
||||
// normally only be accessed by the cx's active thread.
|
||||
AutoNoteSingleThreadedRegion anstr;
|
||||
|
||||
WasmActivation* activation = ActivationIfInnermost(cx);
|
||||
if (!activation)
|
||||
if (!cx->activation() || !cx->activation()->isJit())
|
||||
return false;
|
||||
JitActivation* activation = cx->activation()->asJit();
|
||||
|
||||
const Code* code = activation->compartment()->wasm.lookupCode(pc);
|
||||
if (!code)
|
||||
@ -1344,9 +1344,9 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
||||
return false;
|
||||
AutoSetHandlingSegFault handling(cx);
|
||||
|
||||
WasmActivation* activation = ActivationIfInnermost(cx);
|
||||
if (!activation)
|
||||
if (!cx->activation() || !cx->activation()->isJit())
|
||||
return false;
|
||||
JitActivation* activation = cx->activation()->asJit();
|
||||
|
||||
const CodeSegment* segment;
|
||||
const Code* code = activation->compartment()->wasm.lookupCode(pc, &segment);
|
||||
@ -1384,7 +1384,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
||||
// partly overlaps the end of the heap. In this case, it is an out-of-bounds
|
||||
// error and we should signal that properly, but to do so we must inspect
|
||||
// the operand of the failed access.
|
||||
activation->startInterrupt(ToRegisterState(context));
|
||||
activation->startWasmInterrupt(ToRegisterState(context));
|
||||
*ppc = segment->unalignedAccessCode();
|
||||
return true;
|
||||
}
|
||||
@ -1491,29 +1491,27 @@ RedirectJitCodeToInterruptCheck(JSContext* cx, CONTEXT* context)
|
||||
if (!InInterruptibleCode(cx, pc, &codeSegment))
|
||||
return false;
|
||||
|
||||
// Only probe cx->activation() via ActivationIfInnermost after we know the
|
||||
// pc is in wasm code. This way we don't depend on signal-safe update of
|
||||
// cx->activation().
|
||||
WasmActivation* activation = ActivationIfInnermost(cx);
|
||||
MOZ_ASSERT(activation);
|
||||
|
||||
#ifdef JS_SIMULATOR
|
||||
// The checks performed by the !JS_SIMULATOR path happen in
|
||||
// Simulator::handleWasmInterrupt.
|
||||
cx->simulator()->trigger_wasm_interrupt();
|
||||
#else
|
||||
// Only probe cx->activation() after we know the pc is in wasm code. This
|
||||
// way we don't depend on signal-safe update of cx->activation().
|
||||
JitActivation* activation = cx->activation()->asJit();
|
||||
|
||||
// fp may be null when first entering wasm code from an entry stub.
|
||||
uint8_t* fp = ContextToFP(context);
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
// The out-of-bounds/unaligned trap paths which call startInterrupt() go
|
||||
// The out-of-bounds/unaligned trap paths which call startWasmInterrupt() go
|
||||
// through function code, so test if already interrupted. These paths are
|
||||
// temporary though, so this case can be removed later.
|
||||
if (activation->interrupted())
|
||||
if (activation->isWasmInterrupted())
|
||||
return false;
|
||||
|
||||
activation->startInterrupt(ToRegisterState(context));
|
||||
activation->startWasmInterrupt(ToRegisterState(context));
|
||||
*ContextToPC(context) = codeSegment->interruptCode();
|
||||
#endif
|
||||
|
||||
|
@ -35,8 +35,6 @@ namespace js {
|
||||
extern void
|
||||
InterruptRunningJitCode(JSContext* cx);
|
||||
|
||||
class WasmActivation;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
// Ensure the given JSRuntime is set up to use signals. Failure to enable signal
|
||||
|
@ -249,7 +249,6 @@ static const unsigned NonVolatileRegsPushSize = NonVolatileRegs.gprs().size() *
|
||||
NonVolatileRegs.fpus().getPushSizeInBytes();
|
||||
#endif
|
||||
static const unsigned FramePushedBeforeAlign = NonVolatileRegsPushSize + sizeof(void*);
|
||||
static const unsigned FailFP = 0xbad;
|
||||
|
||||
// Generate a stub that enters wasm from a C++ caller via the native ABI. The
|
||||
// signature of the entry point is Module::ExportFuncPtr. The exported wasm
|
||||
@ -699,7 +698,7 @@ GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint32_t fu
|
||||
// having boxed all the ABI arguments into the JIT stack frame layout.
|
||||
static bool
|
||||
GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLabel,
|
||||
CallableOffsets* offsets)
|
||||
JitExitOffsets* offsets)
|
||||
{
|
||||
masm.setFramePushed(0);
|
||||
|
||||
@ -711,17 +710,19 @@ GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLa
|
||||
// the return address.
|
||||
static_assert(WasmStackAlignment >= JitStackAlignment, "subsumes");
|
||||
unsigned sizeOfRetAddr = sizeof(void*);
|
||||
unsigned jitFrameBytes = 3 * sizeof(void*) + (1 + fi.sig().args().length()) * sizeof(Value);
|
||||
unsigned totalJitFrameBytes = sizeOfRetAddr + jitFrameBytes;
|
||||
unsigned sizeOfPreFrame = WasmFrameLayout::Size() - sizeOfRetAddr;
|
||||
unsigned sizeOfThisAndArgs = (1 + fi.sig().args().length()) * sizeof(Value);
|
||||
unsigned totalJitFrameBytes = sizeOfRetAddr + sizeOfPreFrame + sizeOfThisAndArgs;
|
||||
unsigned jitFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalJitFrameBytes) -
|
||||
sizeOfRetAddr;
|
||||
unsigned sizeOfThisAndArgsAndPadding = jitFramePushed - sizeOfPreFrame;
|
||||
|
||||
GenerateExitPrologue(masm, jitFramePushed, ExitReason::Fixed::ImportJit, offsets);
|
||||
GenerateJitExitPrologue(masm, jitFramePushed, offsets);
|
||||
|
||||
// 1. Descriptor
|
||||
size_t argOffset = 0;
|
||||
uint32_t descriptor = MakeFrameDescriptor(jitFramePushed, JitFrame_Entry,
|
||||
JitFrameLayout::Size());
|
||||
uint32_t descriptor = MakeFrameDescriptor(sizeOfThisAndArgsAndPadding, JitFrame_WasmToJSJit,
|
||||
WasmFrameLayout::Size());
|
||||
masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(masm.getStackPointer(), argOffset));
|
||||
argOffset += sizeof(size_t);
|
||||
|
||||
@ -744,6 +745,7 @@ GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLa
|
||||
unsigned argc = fi.sig().args().length();
|
||||
masm.storePtr(ImmWord(uintptr_t(argc)), Address(masm.getStackPointer(), argOffset));
|
||||
argOffset += sizeof(size_t);
|
||||
MOZ_ASSERT(argOffset == sizeOfPreFrame);
|
||||
|
||||
// 4. |this| value
|
||||
masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), argOffset));
|
||||
@ -753,70 +755,21 @@ GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLa
|
||||
unsigned offsetToCallerStackArgs = jitFramePushed + sizeof(Frame);
|
||||
FillArgumentArray(masm, fi.sig().args(), argOffset, offsetToCallerStackArgs, scratch, ToValue(true));
|
||||
argOffset += fi.sig().args().length() * sizeof(Value);
|
||||
MOZ_ASSERT(argOffset == jitFrameBytes);
|
||||
|
||||
{
|
||||
// Enable Activation.
|
||||
//
|
||||
// This sequence requires two registers, and needs to preserve the
|
||||
// 'callee' register, so there are three live registers.
|
||||
MOZ_ASSERT(callee == WasmIonExitRegCallee);
|
||||
Register cx = WasmIonExitRegE0;
|
||||
Register act = WasmIonExitRegE1;
|
||||
|
||||
// JitActivation* act = cx->activation();
|
||||
masm.loadPtr(Address(WasmTlsReg, offsetof(TlsData, addressOfContext)), cx);
|
||||
masm.loadPtr(Address(cx, 0), cx);
|
||||
masm.loadPtr(Address(cx, JSContext::offsetOfActivation()), act);
|
||||
|
||||
// act.active_ = true;
|
||||
masm.store8(Imm32(1), Address(act, JitActivation::offsetOfActiveUint8()));
|
||||
|
||||
// cx->jitActivation = act;
|
||||
masm.storePtr(act, Address(cx, offsetof(JSContext, jitActivation)));
|
||||
|
||||
// cx->profilingActivation_ = act;
|
||||
masm.storePtr(act, Address(cx, JSContext::offsetOfProfilingActivation()));
|
||||
}
|
||||
MOZ_ASSERT(argOffset == sizeOfThisAndArgs + sizeOfPreFrame);
|
||||
|
||||
AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
|
||||
masm.callJitNoProfiler(callee);
|
||||
AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
|
||||
|
||||
// The JIT callee clobbers all registers, including WasmTlsReg and
|
||||
// FramePointer, so restore those here.
|
||||
// FramePointer, so restore those here. During this sequence of
|
||||
// instructions, FP can't be trusted by the profiling frame iterator.
|
||||
offsets->untrustedFPStart = masm.currentOffset();
|
||||
AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
|
||||
|
||||
masm.loadWasmTlsRegFromFrame();
|
||||
masm.moveStackPtrTo(FramePointer);
|
||||
masm.addPtr(Imm32(masm.framePushed()), FramePointer);
|
||||
|
||||
{
|
||||
// Disable Activation.
|
||||
//
|
||||
// This sequence needs three registers and must preserve WasmTlsReg,
|
||||
// JSReturnReg_Data and JSReturnReg_Type.
|
||||
MOZ_ASSERT(JSReturnReg_Data == WasmIonExitRegReturnData);
|
||||
MOZ_ASSERT(JSReturnReg_Type == WasmIonExitRegReturnType);
|
||||
MOZ_ASSERT(WasmTlsReg == WasmIonExitTlsReg);
|
||||
Register cx = WasmIonExitRegD0;
|
||||
Register act = WasmIonExitRegD1;
|
||||
Register tmp = WasmIonExitRegD2;
|
||||
|
||||
// JitActivation* act = cx->activation();
|
||||
masm.loadPtr(Address(WasmTlsReg, offsetof(TlsData, addressOfContext)), cx);
|
||||
masm.loadPtr(Address(cx, 0), cx);
|
||||
masm.loadPtr(Address(cx, JSContext::offsetOfActivation()), act);
|
||||
|
||||
// cx->jitActivation = act->prevJitActivation_;
|
||||
masm.loadPtr(Address(act, JitActivation::offsetOfPrevJitActivation()), tmp);
|
||||
masm.storePtr(tmp, Address(cx, offsetof(JSContext, jitActivation)));
|
||||
|
||||
// cx->profilingActivation = act->prevProfilingActivation_;
|
||||
masm.loadPtr(Address(act, Activation::offsetOfPrevProfiling()), tmp);
|
||||
masm.storePtr(tmp, Address(cx, JSContext::offsetOfProfilingActivation()));
|
||||
|
||||
// act->active_ = false;
|
||||
masm.store8(Imm32(0), Address(act, JitActivation::offsetOfActiveUint8()));
|
||||
}
|
||||
offsets->untrustedFPEnd = masm.currentOffset();
|
||||
|
||||
// As explained above, the frame was aligned for the JIT ABI such that
|
||||
// (sp + sizeof(void*)) % JitStackAlignment == 0
|
||||
@ -863,7 +816,7 @@ GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLa
|
||||
Label done;
|
||||
masm.bind(&done);
|
||||
|
||||
GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::Fixed::ImportJit, offsets);
|
||||
GenerateJitExitEpilogue(masm, masm.framePushed(), offsets);
|
||||
|
||||
if (oolConvert.used()) {
|
||||
masm.bind(&oolConvert);
|
||||
@ -880,6 +833,14 @@ GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLa
|
||||
// Store return value into argv[0]
|
||||
masm.storeValue(JSReturnOperand, Address(masm.getStackPointer(), offsetToCoerceArgv));
|
||||
|
||||
// From this point, it's safe to reuse the scratch register (which
|
||||
// might be part of the JSReturnOperand).
|
||||
|
||||
// The JIT might have clobbered exitFP at this point. Since there's
|
||||
// going to be a CoerceInPlace call, pretend we're still doing the JIT
|
||||
// call by restoring our tagged exitFP.
|
||||
SetExitFP(masm, scratch);
|
||||
|
||||
// argument 0: argv
|
||||
ABIArgMIRTypeIter i(coerceArgTypes);
|
||||
Address argv(masm.getStackPointer(), offsetToCoerceArgv);
|
||||
@ -892,7 +853,8 @@ GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLa
|
||||
i++;
|
||||
MOZ_ASSERT(i.done());
|
||||
|
||||
// Call coercion function
|
||||
// Call coercion function. Note that right after the call, the value of
|
||||
// FP is correct because FP is non-volatile in the native ABI.
|
||||
AssertStackAlignment(masm, ABIStackAlignment);
|
||||
switch (fi.sig().ret()) {
|
||||
case ExprType::I32:
|
||||
@ -915,6 +877,10 @@ GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLa
|
||||
MOZ_CRASH("Unsupported convert type");
|
||||
}
|
||||
|
||||
// Maintain the invariant that exitFP is either unset or not set to a
|
||||
// wasm tagged exitFP, per the jit exit contract.
|
||||
ClearExitFP(masm, scratch);
|
||||
|
||||
masm.jump(&done);
|
||||
masm.setFramePushed(0);
|
||||
}
|
||||
@ -1015,7 +981,7 @@ wasm::GenerateBuiltinThunk(MacroAssembler& masm, ABIFunctionType abiType, ExitRe
|
||||
// Generate a stub that calls into ReportTrap 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
|
||||
// current WasmActivation. Unwinding will begin at the caller of this trap exit.
|
||||
// current JitActivation. Unwinding will begin at the caller of this trap exit.
|
||||
static bool
|
||||
GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel, CallableOffsets* offsets)
|
||||
{
|
||||
@ -1280,9 +1246,9 @@ GenerateThrowStub(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
|
||||
if (ShadowStackSpace)
|
||||
masm.subFromStackPtr(Imm32(ShadowStackSpace));
|
||||
|
||||
// WasmHandleThrow unwinds WasmActivation::exitFP and returns the address of
|
||||
// the return address on the stack this stub should return to. Set the
|
||||
// FramePointer to a magic value to indicate a return by throw.
|
||||
// WasmHandleThrow unwinds JitActivation::wasmExitFP() and returns the
|
||||
// address of the return address on the stack this stub should return to.
|
||||
// Set the FramePointer to a magic value to indicate a return by throw.
|
||||
masm.call(SymbolicAddress::HandleThrow);
|
||||
masm.moveToStackPtr(ReturnReg);
|
||||
masm.move32(Imm32(FailFP), FramePointer);
|
||||
@ -1357,16 +1323,16 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
|
||||
for (uint32_t funcIndex = 0; funcIndex < imports.length(); funcIndex++) {
|
||||
const FuncImport& fi = imports[funcIndex];
|
||||
|
||||
CallableOffsets offsets;
|
||||
|
||||
if (!GenerateImportInterpExit(masm, fi, funcIndex, &throwLabel, &offsets))
|
||||
CallableOffsets interpOffsets;
|
||||
if (!GenerateImportInterpExit(masm, fi, funcIndex, &throwLabel, &interpOffsets))
|
||||
return false;
|
||||
if (!code->codeRanges.emplaceBack(CodeRange::ImportInterpExit, funcIndex, offsets))
|
||||
if (!code->codeRanges.emplaceBack(CodeRange::ImportInterpExit, funcIndex, interpOffsets))
|
||||
return false;
|
||||
|
||||
if (!GenerateImportJitExit(masm, fi, &throwLabel, &offsets))
|
||||
JitExitOffsets jitOffsets;
|
||||
if (!GenerateImportJitExit(masm, fi, &throwLabel, &jitOffsets))
|
||||
return false;
|
||||
if (!code->codeRanges.emplaceBack(CodeRange::ImportJitExit, funcIndex, offsets))
|
||||
if (!code->codeRanges.emplaceBack(funcIndex, jitOffsets))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -679,13 +679,10 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(0),
|
||||
end_(offsets.end),
|
||||
funcIndex_(0),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
funcBeginToTierEntry_(0),
|
||||
kind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(begin_ <= end_);
|
||||
PodZero(&u);
|
||||
#ifdef DEBUG
|
||||
switch (kind_) {
|
||||
case FarJumpIsland:
|
||||
@ -704,12 +701,12 @@ CodeRange::CodeRange(Kind kind, uint32_t funcIndex, Offsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(0),
|
||||
end_(offsets.end),
|
||||
funcIndex_(funcIndex),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
funcBeginToTierEntry_(0),
|
||||
kind_(kind)
|
||||
{
|
||||
u.funcIndex_ = funcIndex;
|
||||
u.func.lineOrBytecode_ = 0;
|
||||
u.func.beginToNormalEntry_ = 0;
|
||||
u.func.beginToTierEntry_ = 0;
|
||||
MOZ_ASSERT(kind == Entry);
|
||||
MOZ_ASSERT(begin_ <= end_);
|
||||
}
|
||||
@ -718,14 +715,11 @@ CodeRange::CodeRange(Kind kind, CallableOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
funcIndex_(0),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
funcBeginToTierEntry_(0),
|
||||
kind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
PodZero(&u);
|
||||
#ifdef DEBUG
|
||||
switch (kind_) {
|
||||
case TrapExit:
|
||||
@ -742,45 +736,56 @@ CodeRange::CodeRange(Kind kind, uint32_t funcIndex, CallableOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
funcIndex_(funcIndex),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
funcBeginToTierEntry_(0),
|
||||
kind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(isImportExit());
|
||||
MOZ_ASSERT(isImportExit() && !isImportJitExit());
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
u.funcIndex_ = funcIndex;
|
||||
u.func.lineOrBytecode_ = 0;
|
||||
u.func.beginToNormalEntry_ = 0;
|
||||
u.func.beginToTierEntry_ = 0;
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(uint32_t funcIndex, JitExitOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
kind_(ImportJitExit)
|
||||
{
|
||||
MOZ_ASSERT(isImportJitExit());
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
u.funcIndex_ = funcIndex;
|
||||
u.jitExit.beginToUntrustedFPStart_ = offsets.untrustedFPStart - begin_;
|
||||
u.jitExit.beginToUntrustedFPEnd_ = offsets.untrustedFPEnd - begin_;
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(Trap trap, CallableOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
trap_(trap),
|
||||
funcLineOrBytecode_(0),
|
||||
funcBeginToNormalEntry_(0),
|
||||
funcBeginToTierEntry_(0),
|
||||
kind_(TrapExit)
|
||||
{
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
u.trap_ = trap;
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
|
||||
: begin_(offsets.begin),
|
||||
ret_(offsets.ret),
|
||||
end_(offsets.end),
|
||||
funcIndex_(funcIndex),
|
||||
funcLineOrBytecode_(funcLineOrBytecode),
|
||||
funcBeginToNormalEntry_(offsets.normalEntry - begin_),
|
||||
funcBeginToTierEntry_(offsets.tierEntry - begin_),
|
||||
kind_(Function)
|
||||
{
|
||||
MOZ_ASSERT(begin_ < ret_);
|
||||
MOZ_ASSERT(ret_ < end_);
|
||||
MOZ_ASSERT(offsets.normalEntry - begin_ <= UINT8_MAX);
|
||||
MOZ_ASSERT(offsets.tierEntry - begin_ <= UINT8_MAX);
|
||||
u.funcIndex_ = funcIndex;
|
||||
u.func.lineOrBytecode_ = funcLineOrBytecode;
|
||||
u.func.beginToNormalEntry_ = offsets.normalEntry - begin_;
|
||||
u.func.beginToTierEntry_ = offsets.tierEntry - begin_;
|
||||
}
|
||||
|
||||
const CodeRange*
|
||||
|
@ -42,7 +42,6 @@
|
||||
namespace js {
|
||||
|
||||
class PropertyName;
|
||||
class WasmActivation;
|
||||
class WasmFunctionCallObject;
|
||||
namespace jit {
|
||||
struct BaselineScript;
|
||||
@ -948,6 +947,19 @@ struct CallableOffsets : Offsets
|
||||
uint32_t ret;
|
||||
};
|
||||
|
||||
struct JitExitOffsets : CallableOffsets
|
||||
{
|
||||
MOZ_IMPLICIT JitExitOffsets()
|
||||
: CallableOffsets(), untrustedFPStart(0), untrustedFPEnd(0)
|
||||
{}
|
||||
|
||||
// There are a few instructions in the Jit exit where FP may be trash
|
||||
// (because it may have been clobbered by the JS Jit), known as the
|
||||
// untrusted FP zone.
|
||||
uint32_t untrustedFPStart;
|
||||
uint32_t untrustedFPEnd;
|
||||
};
|
||||
|
||||
struct FuncOffsets : CallableOffsets
|
||||
{
|
||||
MOZ_IMPLICIT FuncOffsets()
|
||||
@ -1000,12 +1012,22 @@ class CodeRange
|
||||
uint32_t ret_;
|
||||
uint32_t end_;
|
||||
union {
|
||||
uint32_t funcIndex_;
|
||||
struct {
|
||||
uint32_t funcIndex_;
|
||||
union {
|
||||
struct {
|
||||
uint32_t lineOrBytecode_;
|
||||
uint8_t beginToNormalEntry_;
|
||||
uint8_t beginToTierEntry_;
|
||||
} func;
|
||||
struct {
|
||||
uint8_t beginToUntrustedFPStart_;
|
||||
uint8_t beginToUntrustedFPEnd_;
|
||||
} jitExit;
|
||||
};
|
||||
};
|
||||
Trap trap_;
|
||||
};
|
||||
uint32_t funcLineOrBytecode_;
|
||||
uint8_t funcBeginToNormalEntry_;
|
||||
uint8_t funcBeginToTierEntry_;
|
||||
} u;
|
||||
Kind kind_ : 8;
|
||||
|
||||
public:
|
||||
@ -1014,6 +1036,7 @@ class CodeRange
|
||||
CodeRange(Kind kind, uint32_t funcIndex, Offsets offsets);
|
||||
CodeRange(Kind kind, CallableOffsets offsets);
|
||||
CodeRange(Kind kind, uint32_t funcIndex, CallableOffsets);
|
||||
CodeRange(uint32_t funcIndex, JitExitOffsets offsets);
|
||||
CodeRange(Trap trap, CallableOffsets offsets);
|
||||
CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
|
||||
|
||||
@ -1045,6 +1068,9 @@ class CodeRange
|
||||
bool isImportExit() const {
|
||||
return kind() == ImportJitExit || kind() == ImportInterpExit || kind() == BuiltinThunk;
|
||||
}
|
||||
bool isImportJitExit() const {
|
||||
return kind() == ImportJitExit;
|
||||
}
|
||||
bool isTrapExit() const {
|
||||
return kind() == TrapExit;
|
||||
}
|
||||
@ -1072,14 +1098,14 @@ class CodeRange
|
||||
}
|
||||
uint32_t funcIndex() const {
|
||||
MOZ_ASSERT(hasFuncIndex());
|
||||
return funcIndex_;
|
||||
return u.funcIndex_;
|
||||
}
|
||||
|
||||
// TrapExit CodeRanges have a Trap field.
|
||||
|
||||
Trap trap() const {
|
||||
MOZ_ASSERT(isTrapExit());
|
||||
return trap_;
|
||||
return u.trap_;
|
||||
}
|
||||
|
||||
// Function CodeRanges have two entry points: one for normal calls (with a
|
||||
@ -1092,15 +1118,27 @@ class CodeRange
|
||||
}
|
||||
uint32_t funcNormalEntry() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return begin_ + funcBeginToNormalEntry_;
|
||||
return begin_ + u.func.beginToNormalEntry_;
|
||||
}
|
||||
uint32_t funcTierEntry() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return begin_ + funcBeginToTierEntry_;
|
||||
return begin_ + u.func.beginToTierEntry_;
|
||||
}
|
||||
uint32_t funcLineOrBytecode() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return funcLineOrBytecode_;
|
||||
return u.func.lineOrBytecode_;
|
||||
}
|
||||
|
||||
// ImportJitExit have a particular range where the value of FP can't be
|
||||
// trusted for profiling and thus must be ignored.
|
||||
|
||||
uint32_t jitExitUntrustedFPStart() const {
|
||||
MOZ_ASSERT(isImportJitExit());
|
||||
return begin_ + u.jitExit.beginToUntrustedFPStart_;
|
||||
}
|
||||
uint32_t jitExitUntrustedFPEnd() const {
|
||||
MOZ_ASSERT(isImportJitExit());
|
||||
return begin_ + u.jitExit.beginToUntrustedFPEnd_;
|
||||
}
|
||||
|
||||
// A sorted array of CodeRanges can be looked up via BinarySearch and
|
||||
@ -1435,7 +1473,6 @@ struct TlsData
|
||||
// and are inline in this structure. 16-byte alignment is required for SIMD
|
||||
// data.
|
||||
MOZ_ALIGNED_DECL(char globalArea, 16);
|
||||
|
||||
};
|
||||
|
||||
static_assert(offsetof(TlsData, globalArea) % 16 == 0, "aligned");
|
||||
|
Loading…
x
Reference in New Issue
Block a user