Bug 976536 - Don't relazify inlined functions. r=jandem, a=sledru
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4104,16 +4104,20 @@ IonBuilder::makeInliningDecision(JSFunct
return DontInline(targetScript, "Vetoed: callee is insufficiently hot.");
}
}
// TI calls ObjectStateChange to trigger invalidation of the caller.
types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
targetType->watchStateChangeForInlinedCall(constraints());
+ // We mustn't relazify functions that have been inlined, because there's
+ // no way to tell if it safe to do so.
+ script()->setHasBeenInlined();
+
return InliningDecision_Inline;
}
bool
IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, BoolVector &choiceSet,
uint32_t *numInlineable)
{
*numInlineable = 0;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -565,16 +565,17 @@ JSFunction::trace(JSTracer *trc)
// for self-hosted code.
if (hasScript() && u.i.s.script_) {
// Functions can be relazified under the following conditions:
// - their compartment isn't currently executing scripts or being
// debugged
// - they are not in the self-hosting compartment
// - they aren't generators
// - they don't have JIT code attached
+ // - they haven't ever been inlined
// - they don't have child functions
// - they have information for un-lazifying them again later
// This information can either be a LazyScript, or the name of a
// self-hosted function which can be cloned over again. The latter
// is stored in the first extended slot.
if (IS_GC_MARKING_TRACER(trc) && !compartment()->hasBeenEntered() &&
!compartment()->debugMode() && !compartment()->isSelfHosting &&
u.i.s.script_->isRelazifiable() && (!isSelfHostedBuiltin() || isExtended()))
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -834,16 +834,19 @@ class JSScript : public js::gc::Barriere
bool treatAsRunOnce_:1;
// If treatAsRunOnce, whether script has executed.
bool hasRunOnce_:1;
// Script has been reused for a clone.
bool hasBeenCloned_:1;
+ // Script has been inlined at least once, and can't be relazified.
+ bool hasBeenInlined_:1;
+
// Script came from eval(), and is still active.
bool isActiveEval_:1;
// Script came from eval(), and is in eval cache.
bool isCachedEval_:1;
// Set for functions defined at the top level within an 'eval' script.
bool directlyInsideEval_:1;
@@ -1023,20 +1026,22 @@ class JSScript : public js::gc::Barriere
}
bool hasSingletons() const { return hasSingletons_; }
bool treatAsRunOnce() const {
return treatAsRunOnce_;
}
bool hasRunOnce() const { return hasRunOnce_; }
bool hasBeenCloned() const { return hasBeenCloned_; }
+ bool hasBeenInlined() const { return hasBeenInlined_; }
void setTreatAsRunOnce() { treatAsRunOnce_ = true; }
void setHasRunOnce() { hasRunOnce_ = true; }
void setHasBeenCloned() { hasBeenCloned_ = true; }
+ void setHasBeenInlined() { hasBeenInlined_ = true; }
bool isActiveEval() const { return isActiveEval_; }
bool isCachedEval() const { return isCachedEval_; }
bool directlyInsideEval() const { return directlyInsideEval_; }
void cacheForEval() {
JS_ASSERT(isActiveEval() && !isCachedEval());
isActiveEval_ = false;
@@ -1241,17 +1246,17 @@ class JSScript : public js::gc::Barriere
return offsetof(JSScript, baselineOrIonRaw);
}
static size_t offsetOfBaselineOrIonSkipArgCheck() {
return offsetof(JSScript, baselineOrIonSkipArgCheck);
}
bool isRelazifiable() const {
return (selfHosted() || lazyScript) &&
- !isGenerator() && !hasBaselineScript() && !hasAnyIonScript();
+ !isGenerator() && !hasBaselineScript() && !hasAnyIonScript() && !hasBeenInlined();
}
void setLazyScript(js::LazyScript *lazy) {
lazyScript = lazy;
}
js::LazyScript *maybeLazyScript() {
return lazyScript;
}