Bug 1092833 - Deal with uninitialized slots in MacroAssembler::initGCSlots. (r=terrence)
☠☠ backed out by 3c88f1fc617c ☠ ☠
authorShu-yu Guo <shu@rfrn.org>
Tue, 04 Nov 2014 16:52:38 -0800
changeset 214025 5e6d8b6023e32f464ac8595969531f40b5113c05
parent 214024 d95e85773aa64e8efec26b623cbaf34e48d2aa8f
child 214026 13d940a1c8a0953678ea05359d89ffbe22be7edd
push id27771
push userryanvm@gmail.com
push dateWed, 05 Nov 2014 19:04:24 +0000
treeherdermozilla-central@305b4fecce99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1092833
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1092833 - Deal with uninitialized slots in MacroAssembler::initGCSlots. (r=terrence)
js/src/jit-test/tests/ion/bug1092833.js
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1092833.js
@@ -0,0 +1,49 @@
+// Test that lexicals work with functions with many bindings.
+
+(function() {
+    var a01
+    var b02
+    var c03
+    var d04
+    var e05
+    var f06
+    var g07
+    var h08
+    let i09
+    var j10
+    var k11
+    var l12
+    var m13
+    var n14
+    var o15
+    (function n14() {
+      assertEq(i09, undefined);
+    })()
+})();
+
+try {
+  (function() {
+      var a01
+      var b02
+      var c03
+      var d04
+      var e05
+      var f06
+      var g07
+      var h08
+      let i09
+      var j10
+      var k11
+      var l12
+      var m13
+      var n14
+      var o15
+      (function n14() {
+        i12++
+      })()
+      let i12
+  })()
+} catch (e) {
+  assertEq(e instanceof ReferenceError, true);
+  assertEq(e.message.indexOf("i12") > 0, true);
+}
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -22,16 +22,17 @@
 #include "vm/ForkJoin.h"
 #include "vm/TraceLogging.h"
 
 #ifdef JSGC_GENERATIONAL
 # include "jsgcinlines.h"
 #endif
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
+#include "vm/Interpreter-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using JS::GenericNaN;
 
 namespace {
 
@@ -951,49 +952,76 @@ MacroAssembler::copySlotsFromTemplate(Re
                                       uint32_t start, uint32_t end)
 {
     uint32_t nfixed = Min(templateObj->numFixedSlots(), end);
     for (unsigned i = start; i < nfixed; i++)
         storeValue(templateObj->getFixedSlot(i), Address(obj, NativeObject::getFixedSlotOffset(i)));
 }
 
 void
-MacroAssembler::fillSlotsWithUndefined(Address base, Register temp, uint32_t start, uint32_t end)
+MacroAssembler::fillSlotsWithConstantValue(Address base, Register temp,
+                                           uint32_t start, uint32_t end, const Value &v)
 {
+    MOZ_ASSERT(v.isUndefined() || IsUninitializedLexical(v));
+
+    if (start >= end)
+        return;
+
 #ifdef JS_NUNBOX32
     // We only have a single spare register, so do the initialization as two
     // strided writes of the tag and body.
-    jsval_layout jv = JSVAL_TO_IMPL(UndefinedValue());
+    jsval_layout jv = JSVAL_TO_IMPL(v);
 
     Address addr = base;
     move32(Imm32(jv.s.payload.i32), temp);
     for (unsigned i = start; i < end; ++i, addr.offset += sizeof(HeapValue))
         store32(temp, ToPayload(addr));
 
     addr = base;
     move32(Imm32(jv.s.tag), temp);
     for (unsigned i = start; i < end; ++i, addr.offset += sizeof(HeapValue))
         store32(temp, ToType(addr));
 #else
-    moveValue(UndefinedValue(), temp);
+    moveValue(v, temp);
     for (uint32_t i = start; i < end; ++i, base.offset += sizeof(HeapValue))
         storePtr(temp, base);
 #endif
 }
 
-static uint32_t
-FindStartOfUndefinedSlots(NativeObject *templateObj, uint32_t nslots)
+void
+MacroAssembler::fillSlotsWithUndefined(Address base, Register temp, uint32_t start, uint32_t end)
+{
+    fillSlotsWithConstantValue(base, temp, start, end, UndefinedValue());
+}
+
+void
+MacroAssembler::fillSlotsWithUninitialized(Address base, Register temp, uint32_t start, uint32_t end)
+{
+    fillSlotsWithConstantValue(base, temp, start, end, MagicValue(JS_UNINITIALIZED_LEXICAL));
+}
+
+static void
+FindStartOfUndefinedAndUninitializedSlots(NativeObject *templateObj, uint32_t nslots,
+                                          uint32_t *startOfUndefined, uint32_t *startOfUninitialized)
 {
     MOZ_ASSERT(nslots == templateObj->lastProperty()->slotSpan(templateObj->getClass()));
     MOZ_ASSERT(nslots > 0);
-    for (uint32_t first = nslots; first != 0; --first) {
-        if (templateObj->getSlot(first - 1) != UndefinedValue())
-            return first;
+    uint32_t first = nslots;
+    for (; first != 0; --first) {
+        if (!IsUninitializedLexical(templateObj->getSlot(first - 1)))
+            break;
     }
-    return 0;
+    *startOfUninitialized = first;
+    for (; first != 0; --first) {
+        if (templateObj->getSlot(first - 1) != UndefinedValue()) {
+            *startOfUndefined = first;
+            return;
+        }
+    }
+    *startOfUndefined = 0;
 }
 
 void
 MacroAssembler::initGCSlots(Register obj, Register slots, NativeObject *templateObj,
                             bool initFixedSlots)
 {
     // Slots of non-array objects are required to be initialized.
     // Use the values currently in the template object.
@@ -1006,34 +1034,53 @@ MacroAssembler::initGCSlots(Register obj
 
     // Attempt to group slot writes such that we minimize the amount of
     // duplicated data we need to embed in code and load into registers. In
     // general, most template object slots will be undefined except for any
     // reserved slots. Since reserved slots come first, we split the object
     // logically into independent non-UndefinedValue writes to the head and
     // duplicated writes of UndefinedValue to the tail. For the majority of
     // objects, the "tail" will be the entire slot range.
-    uint32_t startOfUndefined = FindStartOfUndefinedSlots(templateObj, nslots);
+    //
+    // The template object may be a CallObject, in which case we need to
+    // account for uninitialized lexical slots as well as undefined
+    // slots. Unitialized lexical slots always appear at the very end of
+    // slots, after undefined.
+    uint32_t startOfUndefined = nslots;
+    uint32_t startOfUninitialized = nslots;
+    FindStartOfUndefinedAndUninitializedSlots(templateObj, nslots,
+                                              &startOfUndefined, &startOfUninitialized);
     MOZ_ASSERT(startOfUndefined <= nfixed); // Reserved slots must be fixed.
+    MOZ_ASSERT_IF(startOfUndefined != nfixed, startOfUndefined <= startOfUninitialized);
+    MOZ_ASSERT_IF(!templateObj->is<CallObject>(), startOfUninitialized == nslots);
 
     // Copy over any preserved reserved slots.
     copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined);
 
-    // Fill the rest of the fixed slots with undefined.
+    // Fill the rest of the fixed slots with undefined and uninitialized.
     if (initFixedSlots) {
         fillSlotsWithUndefined(Address(obj, NativeObject::getFixedSlotOffset(startOfUndefined)), slots,
-                               startOfUndefined, nfixed);
+                               startOfUndefined, Min(startOfUninitialized, nfixed));
+        size_t offset = NativeObject::getFixedSlotOffset(startOfUninitialized);
+        fillSlotsWithUninitialized(Address(obj, offset), slots, startOfUninitialized, nfixed);
     }
 
     if (ndynamic) {
         // We are short one register to do this elegantly. Borrow the obj
         // register briefly for our slots base address.
         push(obj);
         loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj);
+
+        // Initially fill all dynamic slots with undefined.
         fillSlotsWithUndefined(Address(obj, 0), slots, 0, ndynamic);
+
+        // Fill uninitialized slots if necessary.
+        fillSlotsWithUninitialized(Address(obj, 0), slots, startOfUninitialized - nfixed,
+                                   nslots - startOfUninitialized);
+
         pop(obj);
     }
 }
 
 void
 MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj,
                             bool initFixedSlots)
 {
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -805,17 +805,20 @@ class MacroAssembler : public MacroAssem
     void nurseryAllocate(Register result, Register slots, gc::AllocKind allocKind,
                          size_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
     void freeListAllocate(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
     void allocateObject(Register result, Register slots, gc::AllocKind allocKind,
                         uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
     void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
     void copySlotsFromTemplate(Register obj, const NativeObject *templateObj,
                                uint32_t start, uint32_t end);
+    void fillSlotsWithConstantValue(Address addr, Register temp, uint32_t start, uint32_t end,
+                                    const Value &v);
     void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end);
+    void fillSlotsWithUninitialized(Address addr, Register temp, uint32_t start, uint32_t end);
     void initGCSlots(Register obj, Register temp, NativeObject *templateObj, bool initFixedSlots);
 
   public:
     void callMallocStub(size_t nbytes, Register result, Label *fail);
     void callFreeStub(Register slots);
     void createGCObject(Register result, Register temp, JSObject *templateObj,
                         gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots = true);