Bug 1150783 - Use a special JitFrame to record when LazyLink stubs are on the stack. r=h4writer

This commit is contained in:
Nicolas B. Pierron 2015-05-04 15:14:39 +02:00
parent d6c76e96a1
commit 5432be44a0
9 changed files with 106 additions and 8 deletions

View File

@ -0,0 +1,34 @@
var path = '';
// trigger off-main-thread compilation
for (var i = 0; i < 11; i++)
path.substr(-1);
// maybe link to the the result of the off-main-thread compilation.
function load(unsigned) {
if (unsigned)
path.substr(-1);
}
(function(global, env) {
'use asm';
var load = env.load;
function _main() {
var $l1 = 0, $l2 = 0, $l3 = 0;
do {
load();
$l1 = $l1 + 1 | 0;
} while (($l1 | 0) != 10);
load(1);
load(1);
do {
load();
$l2 = $l2 + 1 | 0;
} while (($l2 | 0) != 1024);
while (($l3 | 0) < 10000) {
load(1);
$l3 = $l3 + 1 | 0;
}
}
return _main;
})({}, { 'load':load })();

View File

@ -436,10 +436,32 @@ FinishAllOffThreadCompilations(JSCompartment* comp)
} }
} }
class AutoLazyLinkExitFrame
{
JitActivation* jitActivation_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
explicit AutoLazyLinkExitFrame(JitActivation* jitActivation
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: jitActivation_(jitActivation)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
MOZ_ASSERT(!jitActivation_->isLazyLinkExitFrame(),
"Cannot stack multiple lazy-link frames.");
jitActivation_->setLazyLinkExitFrame(true);
}
~AutoLazyLinkExitFrame() {
jitActivation_->setLazyLinkExitFrame(false);
}
};
uint8_t* uint8_t*
jit::LazyLinkTopActivation(JSContext* cx) jit::LazyLinkTopActivation(JSContext* cx)
{ {
JitActivationIterator iter(cx->runtime()); JitActivationIterator iter(cx->runtime());
AutoLazyLinkExitFrame lazyLinkExitFrame(iter->asJit());
// First frame should be an exit frame. // First frame should be an exit frame.
JitFrameIterator it(iter); JitFrameIterator it(iter);
@ -2499,11 +2521,12 @@ InvalidateActivation(FreeOp* fop, const JitActivationIterator& activations, bool
size_t frameno = 1; size_t frameno = 1;
for (JitFrameIterator it(activations); !it.done(); ++it, ++frameno) { for (JitFrameIterator it(activations); !it.done(); ++it, ++frameno) {
MOZ_ASSERT_IF(frameno == 1, it.type() == JitFrame_Exit || it.type() == JitFrame_Bailout); MOZ_ASSERT_IF(frameno == 1, it.isExitFrame() || it.type() == JitFrame_Bailout);
#ifdef DEBUG #ifdef DEBUG
switch (it.type()) { switch (it.type()) {
case JitFrame_Exit: case JitFrame_Exit:
case JitFrame_LazyLink:
JitSpew(JitSpew_IonInvalidate, "#%d exit frame @ %p", frameno, it.fp()); JitSpew(JitSpew_IonInvalidate, "#%d exit frame @ %p", frameno, it.fp());
break; break;
case JitFrame_BaselineJS: case JitFrame_BaselineJS:

View File

@ -476,7 +476,8 @@ static void*
GetReturnAddressToIonCode(JSContext* cx) GetReturnAddressToIonCode(JSContext* cx)
{ {
JitFrameIterator iter(cx); JitFrameIterator iter(cx);
MOZ_ASSERT(iter.type() == JitFrame_Exit); MOZ_ASSERT(iter.type() == JitFrame_Exit,
"An exit frame is expected as update functions are called with a VMFunction.");
void* returnAddr = iter.returnAddress(); void* returnAddr = iter.returnAddress();
#ifdef DEBUG #ifdef DEBUG

View File

@ -40,7 +40,7 @@ template <typename T>
bool bool
JitFrameIterator::isExitFrameLayout() const JitFrameIterator::isExitFrameLayout() const
{ {
if (type_ != JitFrame_Exit || isFakeExitFrame()) if (!isExitFrame() || isFakeExitFrame())
return false; return false;
return exitFrame()->is<T>(); return exitFrame()->is<T>();
} }

View File

@ -65,7 +65,14 @@ enum FrameType
// the reconstruction of the BaselineJS frame. From within C++, a bailout // the reconstruction of the BaselineJS frame. From within C++, a bailout
// frame is always the last frame in a JitActivation iff the bailout frame // frame is always the last frame in a JitActivation iff the bailout frame
// information is recorded on the JitActivation. // information is recorded on the JitActivation.
JitFrame_Bailout JitFrame_Bailout,
// A lazy link frame is a special exit frame where a IonJS frame is reused
// for linking the newly compiled code. A special frame is needed to
// work-around the fact that we can make stack patterns which are similar to
// unwound frames. As opposed to unwound frames, we still have to mark all
// the arguments of the original IonJS frame.
JitFrame_LazyLink
}; };
enum ReadFrameArgsBehavior { enum ReadFrameArgsBehavior {
@ -142,6 +149,9 @@ class JitFrameIterator
bool checkInvalidation(IonScript** ionScript) const; bool checkInvalidation(IonScript** ionScript) const;
bool checkInvalidation() const; bool checkInvalidation() const;
bool isExitFrame() const {
return type_ == JitFrame_Exit || type_ == JitFrame_LazyLink;
}
bool isScripted() const { bool isScripted() const {
return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS || type_ == JitFrame_Bailout; return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS || type_ == JitFrame_Bailout;
} }
@ -198,7 +208,7 @@ class JitFrameIterator
// Returns the stack space used by the current frame, in bytes. This does // Returns the stack space used by the current frame, in bytes. This does
// not include the size of its fixed header. // not include the size of its fixed header.
size_t frameSize() const { size_t frameSize() const {
MOZ_ASSERT(type_ != JitFrame_Exit); MOZ_ASSERT(!isExitFrame());
return frameSize_; return frameSize_;
} }

View File

@ -51,6 +51,8 @@ JitFrameIterator::prevType() const
inline bool inline bool
JitFrameIterator::isFakeExitFrame() const JitFrameIterator::isFakeExitFrame() const
{ {
if (type() == JitFrame_LazyLink)
return false;
bool res = (prevType() == JitFrame_Unwound_Rectifier || bool res = (prevType() == JitFrame_Unwound_Rectifier ||
prevType() == JitFrame_Unwound_IonJS || prevType() == JitFrame_Unwound_IonJS ||
prevType() == JitFrame_Unwound_BaselineJS || prevType() == JitFrame_Unwound_BaselineJS ||
@ -64,7 +66,7 @@ JitFrameIterator::isFakeExitFrame() const
inline ExitFrameLayout* inline ExitFrameLayout*
JitFrameIterator::exitFrame() const JitFrameIterator::exitFrame() const
{ {
MOZ_ASSERT(type() == JitFrame_Exit); MOZ_ASSERT(isExitFrame());
MOZ_ASSERT(!isFakeExitFrame()); MOZ_ASSERT(!isFakeExitFrame());
return (ExitFrameLayout*) fp(); return (ExitFrameLayout*) fp();
} }

View File

@ -117,6 +117,9 @@ JitFrameIterator::JitFrameIterator(JSContext* cx)
current_ = activation_->bailoutData()->fp(); current_ = activation_->bailoutData()->fp();
frameSize_ = activation_->bailoutData()->topFrameSize(); frameSize_ = activation_->bailoutData()->topFrameSize();
type_ = JitFrame_Bailout; type_ = JitFrame_Bailout;
} else if (activation_->isLazyLinkExitFrame()) {
type_ = JitFrame_LazyLink;
MOZ_ASSERT(isExitFrameLayout<LazyLinkExitFrameLayout>());
} }
} }
@ -132,6 +135,9 @@ JitFrameIterator::JitFrameIterator(const ActivationIterator& activations)
current_ = activation_->bailoutData()->fp(); current_ = activation_->bailoutData()->fp();
frameSize_ = activation_->bailoutData()->topFrameSize(); frameSize_ = activation_->bailoutData()->topFrameSize();
type_ = JitFrame_Bailout; type_ = JitFrame_Bailout;
} else if (activation_->isLazyLinkExitFrame()) {
type_ = JitFrame_LazyLink;
MOZ_ASSERT(isExitFrameLayout<LazyLinkExitFrameLayout>());
} }
} }
@ -264,6 +270,7 @@ SizeOfFramePrefix(FrameType type)
case JitFrame_Unwound_Rectifier: case JitFrame_Unwound_Rectifier:
return IonUnwoundRectifierFrameLayout::Size(); return IonUnwoundRectifierFrameLayout::Size();
case JitFrame_Exit: case JitFrame_Exit:
case JitFrame_LazyLink:
return ExitFrameLayout::Size(); return ExitFrameLayout::Size();
case JitFrame_IonAccessorIC: case JitFrame_IonAccessorIC:
case JitFrame_Unwound_IonAccessorIC: case JitFrame_Unwound_IonAccessorIC:
@ -957,6 +964,7 @@ EnsureExitFrame(CommonFrameLayout* frame)
case JitFrame_Exit: case JitFrame_Exit:
case JitFrame_Bailout: case JitFrame_Bailout:
case JitFrame_LazyLink:
// Fall-through to MOZ_CRASH below. // Fall-through to MOZ_CRASH below.
break; break;
} }
@ -1495,6 +1503,7 @@ MarkJitActivation(JSTracer* trc, const JitActivationIterator& activations)
for (JitFrameIterator frames(activations); !frames.done(); ++frames) { for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
switch (frames.type()) { switch (frames.type()) {
case JitFrame_Exit: case JitFrame_Exit:
case JitFrame_LazyLink:
MarkJitExitFrame(trc, frames); MarkJitExitFrame(trc, frames);
break; break;
case JitFrame_BaselineJS: case JitFrame_BaselineJS:
@ -1569,7 +1578,7 @@ GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes)
JitActivationIterator iter(rt); JitActivationIterator iter(rt);
JitFrameIterator it(iter); JitFrameIterator it(iter);
uint8_t* retAddr; uint8_t* retAddr;
if (it.type() == JitFrame_Exit) { if (it.isExitFrame()) {
++it; ++it;
// Skip rectifier frames. // Skip rectifier frames.
@ -2771,6 +2780,7 @@ JitFrameIterator::dump() const
fprintf(stderr, "Warning! Unwound JS frames are not observable.\n"); fprintf(stderr, "Warning! Unwound JS frames are not observable.\n");
break; break;
case JitFrame_Exit: case JitFrame_Exit:
case JitFrame_LazyLink:
break; break;
}; };
fputc('\n', stderr); fputc('\n', stderr);
@ -3219,7 +3229,7 @@ AssertJitStackInvariants(JSContext* cx)
"The frame size is optimal"); "The frame size is optimal");
} }
if (frames.type() == JitFrame_Exit) { if (frames.isExitFrame()) {
// For the moment, we do not keep the JitStackAlignment // For the moment, we do not keep the JitStackAlignment
// alignment for exit frames. // alignment for exit frames.
frameSize -= ExitFrameLayout::Size(); frameSize -= ExitFrameLayout::Size();

View File

@ -1391,6 +1391,7 @@ AbstractFramePtr::hasPushedSPSFrame() const
jit::JitActivation::JitActivation(JSContext* cx, bool active) jit::JitActivation::JitActivation(JSContext* cx, bool active)
: Activation(cx, Jit), : Activation(cx, Jit),
active_(active), active_(active),
isLazyLinkExitFrame_(false),
rematerializedFrames_(nullptr), rematerializedFrames_(nullptr),
ionRecovery_(cx), ionRecovery_(cx),
bailoutData_(nullptr), bailoutData_(nullptr),

View File

@ -1286,6 +1286,15 @@ class JitActivation : public Activation
JSContext* prevJitJSContext_; JSContext* prevJitJSContext_;
bool active_; bool active_;
// The lazy link stub reuse the frame pushed for calling a function as an
// exit frame. In a few cases, such as after calls from asm.js, we might
// have an entry frame followed by an exit frame. This pattern can be
// assimilated as a fake exit frame (unwound frame), in which case we skip
// marking during a GC. To ensure that we do mark the stack as expected we
// have to keep a flag set by the LazyLink VM function to safely mark the
// stack if a GC happens during the link phase.
bool isLazyLinkExitFrame_;
// Rematerialized Ion frames which has info copied out of snapshots. Maps // Rematerialized Ion frames which has info copied out of snapshots. Maps
// frame pointers (i.e. jitTop) to a vector of rematerializations of all // frame pointers (i.e. jitTop) to a vector of rematerializations of all
// inline frames associated with that frame. // inline frames associated with that frame.
@ -1417,6 +1426,14 @@ class JitActivation : public Activation
// Unregister the bailout data when the frame is reconstructed. // Unregister the bailout data when the frame is reconstructed.
void cleanBailoutData(); void cleanBailoutData();
// Return the bailout information if it is registered.
bool isLazyLinkExitFrame() const { return isLazyLinkExitFrame_; }
// Register the bailout data when it is constructed.
void setLazyLinkExitFrame(bool isExitFrame) {
isLazyLinkExitFrame_ = isExitFrame;
}
static size_t offsetOfLastProfilingFrame() { static size_t offsetOfLastProfilingFrame() {
return offsetof(JitActivation, lastProfilingFrame_); return offsetof(JitActivation, lastProfilingFrame_);
} }