Bug 1143758: Make LexicalCheck movable and bailout when a lexical isn't initialized; r=shu
authorBenjamin Bouvier <benj@benj.me>
Fri, 20 Mar 2015 16:28:58 +0100
changeset 252095 fbe012eea2e65eed38d068849ada6609b96ade30
parent 252094 657c26d7d5d22a2bc6036d566b7b821b1218d680
child 252096 2cb6af5972f85e649faa2d2778b198b06a4c5dc8
push id1174
push usernsm.nikhil@gmail.com
push dateSun, 22 Mar 2015 01:24:25 +0000
reviewersshu
bugs1143758
milestone39.0a1
Bug 1143758: Make LexicalCheck movable and bailout when a lexical isn't initialized; r=shu
js/src/jit-test/tests/ion/lexical-check-1.js
js/src/jit-test/tests/ion/lexical-check-2.js
js/src/jit/BaselineBailouts.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonTypes.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/lexical-check-1.js
@@ -0,0 +1,15 @@
+function f() {
+    const x = 42;
+    function g() {
+        var s = 0;
+        for (var i = 100; i--;)
+            s += x;
+        return s;
+    }
+    return g;
+}
+
+var func = f();
+for (var i = 200; i--;)
+    assertEq(func(), 4200);
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/lexical-check-2.js
@@ -0,0 +1,22 @@
+function f(i) {
+    if (i == 1500)
+        g();
+    const x = 42;
+    function g() {
+        return x;
+    }
+    return g;
+}
+
+var caught = false;
+var i;
+try {
+    for (i = 0; i < 2000; i++)
+        assertEq(f(i)(), 42);
+} catch(e) {
+    assertEq(e instanceof ReferenceError, true);
+    assertEq(i, 1500);
+    caught = true;
+}
+assertEq(caught, true);
+
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1804,16 +1804,17 @@ 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:
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -9724,32 +9724,30 @@ CodeGenerator::visitRecompileCheck(LReco
     masm.branch32(Assembler::Equal,
                   Address(tmp, IonScript::offsetOfRecompiling()),
                   Imm32(0),
                   ool->entry());
     masm.bind(ool->rejoin());
     masm.bind(&done);
 }
 
+void
+CodeGenerator::visitLexicalCheck(LLexicalCheck *ins)
+{
+    ValueOperand inputValue = ToValue(ins, LLexicalCheck::Input);
+    Label bail;
+    masm.branchTestMagicValue(Assembler::Equal, inputValue, JS_UNINITIALIZED_LEXICAL, &bail);
+    bailoutFrom(&bail, ins->snapshot());
+}
+
 typedef bool (*ThrowUninitializedLexicalFn)(JSContext *);
 static const VMFunction ThrowUninitializedLexicalInfo =
     FunctionInfo<ThrowUninitializedLexicalFn>(ThrowUninitializedLexical);
 
 void
-CodeGenerator::visitLexicalCheck(LLexicalCheck *ins)
-{
-    OutOfLineCode *ool = oolCallVM(ThrowUninitializedLexicalInfo, ins, (ArgList()),
-                                   StoreNothing());
-    ValueOperand inputValue = ToValue(ins, LLexicalCheck::Input);
-    masm.branchTestMagicValue(Assembler::Equal, inputValue, JS_UNINITIALIZED_LEXICAL,
-                              ool->entry());
-    masm.bind(ool->rejoin());
-}
-
-void
 CodeGenerator::visitThrowUninitializedLexical(LThrowUninitializedLexical *ins)
 {
     callVM(ThrowUninitializedLexicalInfo, ins);
 }
 
 void
 CodeGenerator::visitDebugger(LDebugger *ins)
 {
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12797,22 +12797,27 @@ IonBuilder::getCallee()
     return inlineCallInfo_->fun();
 }
 
 MDefinition *
 IonBuilder::addLexicalCheck(MDefinition *input)
 {
     MOZ_ASSERT(JSOp(*pc) == JSOP_CHECKLEXICAL || JSOp(*pc) == JSOP_CHECKALIASEDLEXICAL);
 
+    MInstruction *lexicalCheck;
+
     // If we're guaranteed to not be JS_UNINITIALIZED_LEXICAL, no need to check.
-    MInstruction *lexicalCheck;
-    if (input->type() == MIRType_MagicUninitializedLexical)
+    if (input->type() == MIRType_MagicUninitializedLexical) {
         lexicalCheck = MThrowUninitializedLexical::New(alloc());
-    else if (input->type() == MIRType_Value)
+        current->add(lexicalCheck);
+        if (!resumeAfter(lexicalCheck))
+            return nullptr;
+        return constant(UndefinedValue());
+    }
+
+    if (input->type() == MIRType_Value) {
         lexicalCheck = MLexicalCheck::New(alloc(), input);
-    else
-        return input;
-
-    current->add(lexicalCheck);
-    if (!resumeAfter(lexicalCheck))
-        return nullptr;
-    return lexicalCheck->isLexicalCheck() ? lexicalCheck : constant(UndefinedValue());
-}
+        current->add(lexicalCheck);
+        return lexicalCheck;
+    }
+
+    return input;
+}
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -106,16 +106,19 @@ 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,
@@ -201,16 +204,18 @@ 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";
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4079,18 +4079,18 @@ LIRGenerator::visitSimdShift(MSimdShift 
 
 void
 LIRGenerator::visitLexicalCheck(MLexicalCheck *ins)
 {
     MDefinition *input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Value);
     LLexicalCheck *lir = new(alloc()) LLexicalCheck();
     useBox(lir, LLexicalCheck::Input, input);
+    assignSnapshot(lir, Bailout_UninitializedLexical);
     add(lir, ins);
-    assignSafepoint(lir, ins);
     redefine(ins, input);
 }
 
 void
 LIRGenerator::visitThrowUninitializedLexical(MThrowUninitializedLexical *ins)
 {
     LThrowUninitializedLexical *lir = new(alloc()) LThrowUninitializedLexical();
     add(lir, ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6980,43 +6980,48 @@ class MAsmJSInterruptCheck
     Label *interruptExit() const {
         return interruptExit_;
     }
     const CallSiteDesc &funcDesc() const {
         return funcDesc_;
     }
 };
 
-// Checks if a value is JS_UNINITIALIZED_LEXICAL, throwing if so.
+// Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
+// it to baseline to throw at the correct pc.
 class MLexicalCheck
   : public MUnaryInstruction,
     public BoxPolicy<0>::Data
 {
     explicit MLexicalCheck(MDefinition *input)
       : MUnaryInstruction(input)
     {
-        setGuard();
         setResultType(MIRType_Value);
         setResultTypeSet(input->resultTypeSet());
+        setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(LexicalCheck)
 
     static MLexicalCheck *New(TempAllocator &alloc, MDefinition *input) {
         return new(alloc) MLexicalCheck(input);
     }
 
     AliasSet getAliasSet() const MOZ_OVERRIDE {
         return AliasSet::None();
     }
 
     MDefinition *input() const {
         return getOperand(0);
     }
+
+    bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
+        return congruentIfOperandsEqual(ins);
+    }
 };
 
 // Unconditionally throw an uninitialized let error.
 class MThrowUninitializedLexical : public MNullaryInstruction
 {
     MThrowUninitializedLexical() {
         setGuard();
         setResultType(MIRType_None);