Bug 1073033 part 2.1 - InlineFrameIterator: Recover the non-default value of a function. r=shu

This commit is contained in:
Nicolas B. Pierron 2014-12-19 15:28:29 +01:00
parent 0de4913caf
commit 600518974a
6 changed files with 152 additions and 46 deletions

View File

@ -498,26 +498,20 @@ class SnapshotIterator
// provides a |Default| value. This is useful to avoid invalidations of the
// frame while we are only interested in a few properties which are provided
// by the |Default| value.
Value readWithDefault() {
return allocationValue(readAllocation(), RM_NormalOrDefault);
}
Value maybeRead(MaybeReadFallback &fallback) {
Value readWithDefault(RValueAllocation *alloc) {
*alloc = RValueAllocation();
RValueAllocation a = readAllocation();
if (allocationReadable(a))
return allocationValue(a);
if (fallback.canRecoverResults()) {
if (!initInstructionResults(fallback))
js::CrashAtUnhandlableOOM("Unable to recover allocations.");
*alloc = a;
return allocationValue(a, RM_AlwaysDefault);
}
if (allocationReadable(a))
return allocationValue(a);
MOZ_ASSERT_UNREACHABLE("All allocations should be readable.");
}
return fallback.unreadablePlaceholder();
Value maybeRead(const RValueAllocation &a, MaybeReadFallback &fallback);
Value maybeRead(MaybeReadFallback &fallback) {
RValueAllocation a = readAllocation();
return maybeRead(a, fallback);
}
void traceAllocation(JSTracer *trc);
@ -596,7 +590,16 @@ class InlineFrameIterator
// frames contained in the recover buffer.
uint32_t frameCount_;
RootedFunction callee_;
// The |calleeTemplate_| fields contains either the JSFunction or the
// template from which it is supposed to be cloned. The |calleeRVA_| is an
// Invalid value allocation, if the |calleeTemplate_| field is the effective
// JSFunction, and not its template. On the other hand, any other value
// allocation implies that the |calleeTemplate_| is the template JSFunction
// from which the effective one would be derived and cached by the Recover
// instruction result.
RootedFunction calleeTemplate_;
RValueAllocation calleeRVA_;
RootedScript script_;
jsbytecode *pc_;
uint32_t numActualArgs_;
@ -617,14 +620,24 @@ class InlineFrameIterator
bool more() const {
return frame_ && framesRead_ < frameCount_;
}
JSFunction *callee() const {
MOZ_ASSERT(callee_);
return callee_;
// Due to optimizations, we are not always capable of reading the callee of
// inlined frames without invalidating the IonCode. This function might
// return either the effective callee of the JSFunction which might be used
// to create it.
//
// As such, the |calleeTemplate()| can be used to read most of the metadata
// which are conserved across clones.
JSFunction *calleeTemplate() const {
MOZ_ASSERT(isFunctionFrame());
return calleeTemplate_;
}
JSFunction *maybeCallee() const {
return callee_;
JSFunction *maybeCalleeTemplate() const {
return calleeTemplate_;
}
JSFunction *callee(MaybeReadFallback &fallback) const;
unsigned numActualArgs() const {
// The number of actual arguments of inline frames is recovered by the
// iteration process. It is recovered from the bytecode because this

View File

@ -1900,6 +1900,25 @@ SnapshotIterator::allocationValue(const RValueAllocation &alloc, ReadMethod rm)
}
}
Value
SnapshotIterator::maybeRead(const RValueAllocation &a, MaybeReadFallback &fallback)
{
if (allocationReadable(a))
return allocationValue(a);
if (fallback.canRecoverResults()) {
if (!initInstructionResults(fallback))
js::CrashAtUnhandlableOOM("Unable to recover allocations.");
if (allocationReadable(a))
return allocationValue(a);
MOZ_ASSERT_UNREACHABLE("All allocations should be readable.");
}
return fallback.unreadablePlaceholder();
}
void
SnapshotIterator::writeAllocationValuePayload(const RValueAllocation &alloc, Value v)
{
@ -2232,14 +2251,16 @@ JitFrameIterator::osiIndex() const
}
InlineFrameIterator::InlineFrameIterator(ThreadSafeContext *cx, const JitFrameIterator *iter)
: callee_(cx),
: calleeTemplate_(cx),
calleeRVA_(),
script_(cx)
{
resetOn(iter);
}
InlineFrameIterator::InlineFrameIterator(JSRuntime *rt, const JitFrameIterator *iter)
: callee_(rt),
: calleeTemplate_(rt),
calleeRVA_(),
script_(rt)
{
resetOn(iter);
@ -2249,7 +2270,8 @@ InlineFrameIterator::InlineFrameIterator(ThreadSafeContext *cx, const InlineFram
: frame_(iter ? iter->frame_ : nullptr),
framesRead_(0),
frameCount_(iter ? iter->frameCount_ : UINT32_MAX),
callee_(cx),
calleeTemplate_(cx),
calleeRVA_(),
script_(cx)
{
if (frame_) {
@ -2283,7 +2305,8 @@ InlineFrameIterator::findNextFrame()
si_ = start_;
// Read the initial frame out of the C stack.
callee_ = frame_->maybeCallee();
calleeTemplate_ = frame_->maybeCallee();
calleeRVA_ = RValueAllocation();
script_ = frame_->script();
MOZ_ASSERT(script_->hasBaselineScript());
@ -2332,7 +2355,7 @@ InlineFrameIterator::findNextFrame()
// the time, these functions are stored as JSFunction constants,
// register which are holding the JSFunction pointer, or recover
// instruction with Default value.
Value funval = si_.readWithDefault();
Value funval = si_.readWithDefault(&calleeRVA_);
// Skip extra value allocations.
while (si_.moreAllocations())
@ -2340,12 +2363,12 @@ InlineFrameIterator::findNextFrame()
si_.nextFrame();
callee_ = &funval.toObject().as<JSFunction>();
calleeTemplate_ = &funval.toObject().as<JSFunction>();
// Inlined functions may be clones that still point to the lazy script
// for the executed script, if they are clones. The actual script
// exists though, just make sure the function points to it.
script_ = callee_->existingScriptForInlinedFunction();
script_ = calleeTemplate_->existingScriptForInlinedFunction();
MOZ_ASSERT(script_->hasBaselineScript());
pc_ = script_->offsetToPC(si_.pcOffset());
@ -2362,6 +2385,19 @@ InlineFrameIterator::findNextFrame()
framesRead_++;
}
JSFunction *
InlineFrameIterator::callee(MaybeReadFallback &fallback) const
{
MOZ_ASSERT(isFunctionFrame());
if (calleeRVA_.mode() == RValueAllocation::INVALID || !fallback.canRecoverResults())
return calleeTemplate_;
SnapshotIterator s(si_);
// :TODO: Handle allocation failures from recover instruction.
Value funval = s.maybeRead(calleeRVA_, fallback);
return &funval.toObject().as<JSFunction>();
}
JSObject *
InlineFrameIterator::computeScopeChain(Value scopeChainValue, bool *hasCallObj) const
{
@ -2386,7 +2422,7 @@ InlineFrameIterator::computeScopeChain(Value scopeChainValue, bool *hasCallObj)
bool
InlineFrameIterator::isFunctionFrame() const
{
return !!callee_;
return !!calleeTemplate_;
}
MachineState

View File

@ -1999,6 +1999,14 @@ js::NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobjArg, Native na
return fun;
}
bool
js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction fun)
{
return compartment == fun->compartment() &&
!fun->hasSingletonType() &&
!types::UseNewTypeForClone(fun);
}
JSFunction *
js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, gc::AllocKind allocKind,
NewObjectKind newKindArg /* = GenericObject */)
@ -2006,9 +2014,7 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
MOZ_ASSERT(parent);
MOZ_ASSERT(!fun->isBoundFunction());
bool useSameScript = cx->compartment() == fun->compartment() &&
!fun->hasSingletonType() &&
!types::UseNewTypeForClone(fun);
bool useSameScript = CloneFunctionObjectUseSameScript(cx->compartment(), fun);
if (!useSameScript && fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
return nullptr;

View File

@ -55,7 +55,9 @@ class JSFunction : public js::NativeObject
ASMJS_CTOR = ASMJS | NATIVE_CTOR,
ASMJS_LAMBDA_CTOR = ASMJS | NATIVE_CTOR | LAMBDA,
INTERPRETED_LAMBDA = INTERPRETED | LAMBDA,
INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW
INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW,
STABLE_ACROSS_CLONES = NATIVE_CTOR | IS_FUN_PROTO | EXPR_CLOSURE | HAS_GUESSED_ATOM |
LAMBDA | SELF_HOSTED | SELF_HOSTED_CTOR | HAS_REST | ASMJS | ARROW
};
static_assert(INTERPRETED == JS_FUNCTION_INTERPRETED_BIT,
@ -578,6 +580,9 @@ class FunctionExtended : public JSFunction
HeapValue extendedSlots[NUM_EXTENDED_SLOTS];
};
extern bool
CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction fun);
extern JSFunction *
CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
gc::AllocKind kind = JSFunction::FinalizeKind,

View File

@ -1062,7 +1062,7 @@ FrameIter::updatePcQuadratic()
}
JSFunction *
FrameIter::callee() const
FrameIter::calleeTemplate() const
{
switch (data_.state_) {
case DONE:
@ -1075,27 +1075,63 @@ FrameIter::callee() const
if (data_.jitFrames_.isBaselineJS())
return data_.jitFrames_.callee();
MOZ_ASSERT(data_.jitFrames_.isIonScripted());
return ionInlineFrames_.callee();
return ionInlineFrames_.calleeTemplate();
}
MOZ_CRASH("Unexpected state");
}
Value
FrameIter::calleev() const
JSFunction *
FrameIter::callee(JSContext *cx) const
{
switch (data_.state_) {
case DONE:
case ASMJS:
break;
case INTERP:
MOZ_ASSERT(isFunctionFrame());
return interpFrame()->calleev();
return calleeTemplate();
case JIT:
return ObjectValue(*callee());
if (data_.jitFrames_.isIonScripted()) {
jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_);
return ionInlineFrames_.callee(recover);
}
MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
return calleeTemplate();
}
MOZ_CRASH("Unexpected state");
}
bool
FrameIter::matchCallee(JSContext *cx, HandleFunction fun) const
{
RootedFunction currentCallee(cx, calleeTemplate());
// As we do not know if the calleeTemplate is the real function, or the
// template from which it would be cloned, we compare properties which are
// stable across the cloning of JSFunctions.
if (((currentCallee->flags() ^ fun->flags()) & JSFunction::STABLE_ACROSS_CLONES) != 0 ||
currentCallee->nargs() != fun->nargs())
{
return false;
}
// Only some lambdas are optimized in a way which cannot be recovered without
// invalidating the frame. Thus, if one of the function is not a lambda we can just
// compare it against the calleeTemplate.
if (!fun->isLambda() || !currentCallee->isLambda())
return currentCallee == fun;
// Use the same condition as |js::CloneFunctionObject|, to know if we should
// expect both functions to have the same JSScript. If so, and if they are
// different, then they cannot be equal.
bool useSameScript = CloneFunctionObjectUseSameScript(fun->compartment(), currentCallee);
if (useSameScript && currentCallee->nonLazyScript() != fun->nonLazyScript())
return false;
// If none of the previous filters worked, then take the risk of
// invalidating the frame to identify the JSFunction.
return callee(cx) == fun;
}
unsigned
FrameIter::numActualArgs() const
{

View File

@ -1598,8 +1598,22 @@ class FrameIter
bool isConstructing() const;
jsbytecode *pc() const { MOZ_ASSERT(!done()); return data_.pc_; }
void updatePcQuadratic();
JSFunction *callee() const;
Value calleev() const;
// The function |calleeTemplate()| returns either the function from which
// the current |callee| was cloned or the |callee| if it can be read. As
// long as we do not have to investigate the scope chain or build a new
// frame, we should prefer to use |calleeTemplate| instead of |callee|, as
// requesting the |callee| might cause the invalidation of the frame. (see
// js::Lambda)
JSFunction *calleeTemplate() const;
JSFunction *callee(JSContext *cx) const;
JSFunction *maybeCallee(JSContext *cx) const {
return isFunctionFrame() ? callee(cx) : nullptr;
}
bool matchCallee(JSContext *cx, HandleFunction fun) const;
unsigned numActualArgs() const;
unsigned numFormalArgs() const;
Value unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
@ -1626,10 +1640,6 @@ class FrameIter
Value returnValue() const;
void setReturnValue(const Value &v);
JSFunction *maybeCallee() const {
return isFunctionFrame() ? callee() : nullptr;
}
// These are only valid for the top frame.
size_t numFrameSlots() const;
Value frameSlotValue(size_t index) const;