Bug 1143758: Invalidate script when a lexical check has failed; r=shu
authorBenjamin Bouvier <benj@benj.me>
Tue, 17 Mar 2015 19:23:44 +0100
changeset 252096 2cb6af5972f85e649faa2d2778b198b06a4c5dc8
parent 252095 fbe012eea2e65eed38d068849ada6609b96ade30
child 252097 347dbe653fb3083e0bbebee31ec890af8b23e70a
push id1174
push usernsm.nikhil@gmail.com
push dateSun, 22 Mar 2015 01:24:25 +0000
reviewersshu
bugs1143758
milestone39.0a1
Bug 1143758: Invalidate script when a lexical check has failed; r=shu
js/src/jit-test/tests/ion/lexical-check-3.js
js/src/jit/BaselineBailouts.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonTypes.h
js/src/jsscript.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/lexical-check-3.js
@@ -0,0 +1,23 @@
+function f() {
+
+    function g(n) {
+        n = n|0;
+        var s = 0;
+        for (var i = 0; (i = i + 1 |0) < 1000;) {
+            s = s * i;
+            if (!n) {
+                s = x;
+            }
+        }
+        return s;
+    }
+
+    return g;
+    let x;
+}
+
+var func = f();
+var r;
+for (var i = 0; i < 2000; i++)
+    r = func(i + 1);
+assertEq(r, 0);
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1618,16 +1618,38 @@ HandleBaselineInfoBailout(JSContext *cx,
 
     MOZ_ASSERT(!outerScript->ionScript()->invalidated());
 
     JitSpew(JitSpew_BaselineBailouts, "Invalidating due to invalid baseline info");
     return Invalidate(cx, outerScript);
 }
 
 static bool
+HandleLexicalCheckFailure(JSContext *cx, HandleScript outerScript, HandleScript innerScript)
+{
+    JitSpew(JitSpew_IonBailouts, "Lexical check failure %s:%d, inlined into %s:%d",
+            innerScript->filename(), innerScript->lineno(),
+            outerScript->filename(), outerScript->lineno());
+
+    MOZ_ASSERT(!outerScript->ionScript()->invalidated());
+
+    if (!innerScript->failedLexicalCheck())
+        innerScript->setFailedLexicalCheck();
+
+    JitSpew(JitSpew_BaselineBailouts, "Invalidating due to lexical check failure");
+    if (!Invalidate(cx, outerScript))
+        return false;
+
+    if (innerScript->hasIonScript() && !Invalidate(cx, innerScript))
+        return false;
+
+    return true;
+}
+
+static bool
 CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size_t inlineDepth,
                             BaselineFrame *frame)
 {
     RematerializedFrame *rematFrame = act->lookupRematerializedFrame(fp, inlineDepth);
 
     // We might not have rematerialized a frame if the user never requested a
     // Debugger.Frame for it.
     if (!rematFrame)
@@ -1804,17 +1826,16 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_NonBooleanInput:
       case Bailout_NonObjectInput:
       case Bailout_NonStringInput:
       case Bailout_NonSymbolInput:
       case Bailout_NonSimdInt32x4Input:
       case Bailout_NonSimdFloat32x4Input:
       case Bailout_InitialState:
       case Bailout_Debugger:
-      case Bailout_UninitializedLexical:
         // Do nothing.
         break;
 
       // Invalid assumption based on baseline code.
       case Bailout_OverflowInvalidate:
       case Bailout_NonStringInputInvalidate:
       case Bailout_DoubleOutput:
       case Bailout_ObjectIdentityOrTypeGuard:
@@ -1829,16 +1850,20 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_Neutered:
         if (!HandleBoundsCheckFailure(cx, outerScript, innerScript))
             return false;
         break;
       case Bailout_ShapeGuard:
         if (!HandleShapeGuardFailure(cx, outerScript, innerScript))
             return false;
         break;
+      case Bailout_UninitializedLexical:
+        if (!HandleLexicalCheckFailure(cx, outerScript, innerScript))
+            return false;
+        break;
       case Bailout_IonExceptionDebugMode:
         // Return false to resume in HandleException with reconstructed
         // baseline frame.
         return false;
       default:
         MOZ_CRASH("Unknown bailout kind!");
     }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -145,16 +145,17 @@ IonBuilder::IonBuilder(JSContext *analys
     iterators_(*temp),
     loopHeaders_(*temp),
     inspector(inspector),
     inliningDepth_(inliningDepth),
     inlinedBytecodeLength_(0),
     numLoopRestarts_(0),
     failedBoundsCheck_(info->script()->failedBoundsCheck()),
     failedShapeGuard_(info->script()->failedShapeGuard()),
+    failedLexicalCheck_(info->script()->failedLexicalCheck()),
     nonStringIteration_(false),
     lazyArguments_(nullptr),
     inlineCallInfo_(nullptr),
     maybeFallbackFunctionGetter_(nullptr)
 {
     script_ = info->script();
     pc = info->startPC();
     abortReason_ = AbortReason_Disable;
@@ -957,16 +958,19 @@ IonBuilder::buildInline(IonBuilder *call
     callerResumePoint_ = callerResumePoint;
 
     if (callerBuilder->failedBoundsCheck_)
         failedBoundsCheck_ = true;
 
     if (callerBuilder->failedShapeGuard_)
         failedShapeGuard_ = true;
 
+    if (callerBuilder->failedLexicalCheck_)
+        failedLexicalCheck_ = true;
+
     // Generate single entrance block.
     if (!setCurrentAndSpecializePhis(newBlock(pc)))
         return false;
     if (!current)
         return false;
 
     current->setCallerResumePoint(callerResumePoint);
 
@@ -12811,13 +12815,15 @@ IonBuilder::addLexicalCheck(MDefinition 
         if (!resumeAfter(lexicalCheck))
             return nullptr;
         return constant(UndefinedValue());
     }
 
     if (input->type() == MIRType_Value) {
         lexicalCheck = MLexicalCheck::New(alloc(), input);
         current->add(lexicalCheck);
+        if (failedLexicalCheck_)
+            lexicalCheck->setNotMovableUnchecked();
         return lexicalCheck;
     }
 
     return input;
 }
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -1109,16 +1109,20 @@ class IonBuilder
     // True if script->failedBoundsCheck is set for the current script or
     // an outer script.
     bool failedBoundsCheck_;
 
     // True if script->failedShapeGuard is set for the current script or
     // an outer script.
     bool failedShapeGuard_;
 
+    // True if script->failedLexicalCheck_ is set for the current script or
+    // an outer script.
+    bool failedLexicalCheck_;
+
     // Has an iterator other than 'for in'.
     bool nonStringIteration_;
 
     // If this script can use a lazy arguments object, it will be pre-created
     // here.
     MInstruction *lazyArguments_;
 
     // If this is an inline builder, the call info for the builder.
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -106,19 +106,16 @@ enum BailoutKind
     Bailout_NonSimdFloat32x4Input,
 
     // For the initial snapshot when entering a function.
     Bailout_InitialState,
 
     // We hit a |debugger;| statement.
     Bailout_Debugger,
 
-    // When we're trying to use an uninitialized lexical.
-    Bailout_UninitializedLexical,
-
     // END Normal bailouts
 
 
     // Bailouts caused by invalid assumptions based on Baseline code.
     // Causes immediate invalidation.
 
     // Like Bailout_Overflow, but causes immediate invalidation.
     Bailout_OverflowInvalidate,
@@ -145,16 +142,19 @@ enum BailoutKind
     // A bailout triggered by a neutered typed object.
     Bailout_Neutered,
 
     // A shape guard based on TI information failed.
     // (We saw an object whose shape does not match that / any of those observed
     // by the baseline IC.)
     Bailout_ShapeGuard,
 
+    // When we're trying to use an uninitialized lexical.
+    Bailout_UninitializedLexical,
+
     // A bailout to baseline from Ion on exception to handle Debugger hooks.
     Bailout_IonExceptionDebugMode,
 };
 
 inline const char *
 BailoutKindString(BailoutKind kind)
 {
     switch (kind) {
@@ -204,18 +204,16 @@ BailoutKindString(BailoutKind kind)
       case Bailout_NonSimdInt32x4Input:
         return "Bailout_NonSimdInt32x4Input";
       case Bailout_NonSimdFloat32x4Input:
         return "Bailout_NonSimdFloat32x4Input";
       case Bailout_InitialState:
         return "Bailout_InitialState";
       case Bailout_Debugger:
         return "Bailout_Debugger";
-      case Bailout_UninitializedLexical:
-        return "Bailout_UninitializedLexical";
 
       // Bailouts caused by invalid assumptions.
       case Bailout_OverflowInvalidate:
         return "Bailout_OverflowInvalidate";
       case Bailout_NonStringInputInvalidate:
         return "Bailout_NonStringInputInvalidate";
       case Bailout_DoubleOutput:
         return "Bailout_DoubleOutput";
@@ -224,16 +222,18 @@ BailoutKindString(BailoutKind kind)
       case Bailout_ArgumentCheck:
         return "Bailout_ArgumentCheck";
       case Bailout_BoundsCheck:
         return "Bailout_BoundsCheck";
       case Bailout_Neutered:
         return "Bailout_Neutered";
       case Bailout_ShapeGuard:
         return "Bailout_ShapeGuard";
+      case Bailout_UninitializedLexical:
+        return "Bailout_UninitializedLexical";
       case Bailout_IonExceptionDebugMode:
         return "Bailout_IonExceptionDebugMode";
       default:
         MOZ_CRASH("Invalid BailoutKind");
     }
 }
 
 static const uint32_t ELEMENT_TYPE_BITS = 5;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -972,16 +972,19 @@ class JSScript : public js::gc::TenuredC
     bool failedBoundsCheck_:1; /* script has had hoisted bounds checks fail */
     bool failedShapeGuard_:1; /* script has had hoisted shape guard fail */
     bool hadFrequentBailouts_:1;
     bool uninlineable_:1;    /* explicitly marked as uninlineable */
 
     // Idempotent cache has triggered invalidation.
     bool invalidatedIdempotentCache_:1;
 
+    // Lexical check did fail and bail out.
+    bool failedLexicalCheck_:1;
+
     // If the generator was created implicitly via a generator expression,
     // isGeneratorExp will be true.
     bool isGeneratorExp_:1;
 
     // Script has an entry in JSCompartment::scriptCountsMap.
     bool hasScriptCounts_:1;
 
     // Script has an entry in JSCompartment::debugScriptMap.
@@ -1218,22 +1221,26 @@ class JSScript : public js::gc::TenuredC
         return hadFrequentBailouts_;
     }
     bool uninlineable() const {
         return uninlineable_;
     }
     bool invalidatedIdempotentCache() const {
         return invalidatedIdempotentCache_;
     }
+    bool failedLexicalCheck() const {
+        return failedLexicalCheck_;
+    }
 
     void setFailedBoundsCheck() { failedBoundsCheck_ = true; }
     void setFailedShapeGuard() { failedShapeGuard_ = true; }
     void setHadFrequentBailouts() { hadFrequentBailouts_ = true; }
     void setUninlineable() { uninlineable_ = true; }
     void setInvalidatedIdempotentCache() { invalidatedIdempotentCache_ = true; }
+    void setFailedLexicalCheck() { failedLexicalCheck_ = true; }
 
     bool hasScriptCounts() const { return hasScriptCounts_; }
 
     bool hasFreezeConstraints() const { return hasFreezeConstraints_; }
     void setHasFreezeConstraints() { hasFreezeConstraints_ = true; }
 
     bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; }
     void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; }