Bug 969012 - Make jit allocations of CallObject with external slots use the nursery; r=jandem
☠☠ backed out by 9d1e797181e3 ☠ ☠
authorTerrence Cole <terrence@mozilla.com>
Tue, 11 Feb 2014 11:53:53 -0800
changeset 187429 bea3f06585ecc4ca8e86303a8fa471dd22527615
parent 187420 5775ed3239faed055c5605d1b002538a811f3800
child 187430 0ca4125d6adcf649bcd1eadd3252843a6db76181
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs969012
milestone30.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 969012 - Make jit allocations of CallObject with external slots use the nursery; r=jandem
js/src/builtin/TestingFunctions.cpp
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
js/src/jit-test/tests/basic/bug908915.js
js/src/jit-test/tests/ion/callobj-malloc-slots.js
js/src/jit-test/tests/parallel/closure-nested-branch.js
js/src/jit-test/tests/parallel/closure-nested-compute.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/shared/Assembler-x86-shared.h
js/src/jsfriendapi.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/vm/ObjectImpl.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -252,16 +252,38 @@ MinorGC(JSContext *cx, unsigned argc, js
         cx->runtime()->gcStoreBuffer.setAboutToOverflow();
 
     MinorGC(cx, gcreason::API);
 #endif
     args.rval().setUndefined();
     return true;
 }
 
+static bool
+DisableGGC(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+#ifdef JSGC_GENERATIONAL
+    JS::DisableGenerationalGC(cx->runtime());
+#endif
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+EnableGGC(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+#ifdef JSGC_GENERATIONAL
+    JS::EnableGenerationalGC(cx->runtime());
+#endif
+    args.rval().setUndefined();
+    return true;
+}
+
 static const struct ParamPair {
     const char      *name;
     JSGCParamKey    param;
 } paramMap[] = {
     {"maxBytes",            JSGC_MAX_BYTES },
     {"maxMallocBytes",      JSGC_MAX_MALLOC_BYTES},
     {"gcBytes",             JSGC_BYTES},
     {"gcNumber",            JSGC_NUMBER},
@@ -1424,16 +1446,25 @@ static const JSFunctionSpecWithHelp Test
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
 
     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
 "minorgc([aboutToOverflow])",
 "  Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
 "  the store buffer as about-to-overflow before collecting."),
 
+    JS_FN_HELP("disableGenerationalGC", ::DisableGGC, 0, 0,
+"disableGenerationalGC()",
+"  In builds with support for generational GC, disable generational GC."),
+
+    JS_FN_HELP("enableGenerationalGC", ::EnableGGC, 0, 0,
+"enableGenerationalGC()",
+"  In builds with support for generational GC, re-enable a disabled\n"
+"  generational GC."),
+
     JS_FN_HELP("gcparam", GCParameter, 2, 0,
 "gcparam(name [, value])",
 "  Wrapper for JS_[GS]etGCParameter. The name is one of " GC_PARAMETER_ARGS_LIST),
 
     JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 0, 0,
 "getBuildConfiguration()",
 "  Return an object describing some of the configuration options SpiderMonkey\n"
 "  was built with."),
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -264,29 +264,23 @@ js::Nursery::allocateHugeSlots(JSContext
 {
     HeapSlot *slots = cx->pod_malloc<HeapSlot>(nslots);
     /* If this put fails, we will only leak the slots. */
     (void)hugeSlots.put(slots);
     return slots;
 }
 
 void
-js::Nursery::notifyInitialSlots(Cell *cell, HeapSlot *slots)
-{
-    if (isInside(cell) && !isInside(slots)) {
-        /* If this put fails, we will only leak the slots. */
-        (void)hugeSlots.put(slots);
-    }
-}
-
-void
 js::Nursery::notifyNewElements(gc::Cell *cell, ObjectElements *elements)
 {
     JS_ASSERT(!isInside(elements));
-    notifyInitialSlots(cell, reinterpret_cast<HeapSlot *>(elements));
+    if (isInside(cell)) {
+        /* If this put fails, we will only leak the slots. */
+        (void)hugeSlots.put(reinterpret_cast<HeapSlot *>(elements));
+    }
 }
 
 void
 js::Nursery::notifyRemovedElements(gc::Cell *cell, ObjectElements *oldElements)
 {
     JS_ASSERT(cell);
     JS_ASSERT(oldElements);
     JS_ASSERT(!isInside(oldElements));
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -49,16 +49,19 @@ class BaselineCompiler;
 class Nursery
 {
   public:
     static const int NumNurseryChunks = 16;
     static const int LastNurseryChunk = NumNurseryChunks - 1;
     static const size_t Alignment = gc::ChunkSize;
     static const size_t NurserySize = gc::ChunkSize * NumNurseryChunks;
 
+    /* The maximum number of slots allowed to reside inline in the nursery. */
+    static const size_t MaxNurserySlots = 128;
+
     explicit Nursery(JSRuntime *rt)
       : runtime_(rt),
         position_(0),
         currentStart_(0),
         currentEnd_(0),
         currentChunk_(0),
         numActiveChunks_(0)
     {}
@@ -96,19 +99,16 @@ class Nursery
 
     /* Resize an existing elements vector. */
     ObjectElements *reallocateElements(JSContext *cx, JSObject *obj, ObjectElements *oldHeader,
                                        uint32_t oldCount, uint32_t newCount);
 
     /* Free a slots array. */
     void freeSlots(JSContext *cx, HeapSlot *slots);
 
-    /* Add a slots to our tracking list if it is out-of-line. */
-    void notifyInitialSlots(gc::Cell *cell, HeapSlot *slots);
-
     /* Add elements to our tracking list if it is out-of-line. */
     void notifyNewElements(gc::Cell *cell, ObjectElements *elements);
 
     /* Remove elements to our tracking list if it is out-of-line. */
     void notifyRemovedElements(gc::Cell *cell, ObjectElements *oldElements);
 
     typedef Vector<types::TypeObject *, 0, SystemAllocPolicy> TypeObjectList;
 
@@ -177,19 +177,16 @@ class Nursery
     /*
      * The set of externally malloced slots potentially kept live by objects
      * stored in the nursery. Any external slots that do not belong to a
      * tenured thing at the end of a minor GC must be freed.
      */
     typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet;
     HugeSlotsSet hugeSlots;
 
-    /* The maximum number of slots allowed to reside inline in the nursery. */
-    static const size_t MaxNurserySlots = 128;
-
     /* The amount of space in the mapped nursery available to allocations. */
     static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);
 
     struct NurseryChunkLayout {
         char data[NurseryChunkUsableSize];
         gc::ChunkTrailer trailer;
         uintptr_t start() { return uintptr_t(&data); }
         uintptr_t end() { return uintptr_t(&trailer); }
--- a/js/src/jit-test/tests/basic/bug908915.js
+++ b/js/src/jit-test/tests/basic/bug908915.js
@@ -1,26 +1,26 @@
 // |jit-test| error: 42
 function f(y) {}
 for each(let e in newGlobal()) {
     if (e.name === "quit" || e.name == "readline" || e.name == "terminate" ||
-	e.name == "nestedShell")
+	e.name == "nestedShell" || e.name.endsWith("ableGenerationalGC"))
 	continue;
     try {
         e();
     } catch (r) {}
 }
 (function() {
     arguments.__proto__.__proto__ = newGlobal()
     function f(y) {
         y()
     }
     for each(b in []) {
 	if (b.name === "quit" || b.name == "readline" || b.name == "terminate" ||
-	    b.name == "nestedShell")
+	    b.name == "nestedShell" || b.name.endsWith("ableGenerationalGC"))
 	    continue;
         try {
             f(b)
         } catch (e) {}
     }
 })();
 
 throw 42;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/callobj-malloc-slots.js
@@ -0,0 +1,308 @@
+// |jit-test| tz-pacific
+
+// This is a copy of date-format-tofte: one of the only current jit-tests that
+// exercises CallObject creation with external slots. We disable GGC here to
+// test the fallback path that gets excercised without it enabled.
+disableGenerationalGC();
+
+function arrayExists(array, x) {
+    for (var i = 0; i < array.length; i++) {
+        if (array[i] == x) return true;
+    }
+    return false;
+}
+
+Date.prototype.formatDate = function (input,time) {
+    // formatDate :
+    // a PHP date like function, for formatting date strings
+    // See: http://www.php.net/date
+    //
+    // input : format string
+    // time : epoch time (seconds, and optional)
+    //
+    // if time is not passed, formatting is based on 
+    // the current "this" date object's set time.
+    //
+    // supported:
+    // a, A, B, d, D, F, g, G, h, H, i, j, l (lowercase L), L, 
+    // m, M, n, O, r, s, S, t, U, w, W, y, Y, z
+    //
+    // unsupported:
+    // I (capital i), T, Z    
+
+    var switches =    ["a", "A", "B", "d", "D", "F", "g", "G", "h", "H", 
+                       "i", "j", "l", "L", "m", "M", "n", "O", "r", "s", 
+                       "S", "t", "U", "w", "W", "y", "Y", "z"];
+    var daysLong =    ["Sunday", "Monday", "Tuesday", "Wednesday", 
+                       "Thursday", "Friday", "Saturday"];
+    var daysShort =   ["Sun", "Mon", "Tue", "Wed", 
+                       "Thu", "Fri", "Sat"];
+    var monthsShort = ["Jan", "Feb", "Mar", "Apr",
+                       "May", "Jun", "Jul", "Aug", "Sep",
+                       "Oct", "Nov", "Dec"];
+    var monthsLong =  ["January", "February", "March", "April",
+                       "May", "June", "July", "August", "September",
+                       "October", "November", "December"];
+    var daysSuffix = ["st", "nd", "rd", "th", "th", "th", "th", // 1st - 7th
+                      "th", "th", "th", "th", "th", "th", "th", // 8th - 14th
+                      "th", "th", "th", "th", "th", "th", "st", // 15th - 21st
+                      "nd", "rd", "th", "th", "th", "th", "th", // 22nd - 28th
+                      "th", "th", "st"];                        // 29th - 31st
+
+    function a() {
+        // Lowercase Ante meridiem and Post meridiem
+        return self.getHours() > 11? "pm" : "am";
+    }
+    function A() {
+        // Uppercase Ante meridiem and Post meridiem
+        return self.getHours() > 11? "PM" : "AM";
+    }
+
+    function B(){
+        // Swatch internet time. code simply grabbed from ppk,
+        // since I was feeling lazy:
+        // http://www.xs4all.nl/~ppk/js/beat.html
+        var off = (self.getTimezoneOffset() + 60)*60;
+        var theSeconds = (self.getHours() * 3600) + 
+                         (self.getMinutes() * 60) + 
+                          self.getSeconds() + off;
+        var beat = Math.floor(theSeconds/86.4);
+        if (beat > 1000) beat -= 1000;
+        if (beat < 0) beat += 1000;
+        if ((""+beat).length == 1) beat = "00"+beat;
+        if ((""+beat).length == 2) beat = "0"+beat;
+        return beat;
+    }
+    
+    function d() {
+        // Day of the month, 2 digits with leading zeros
+        return new String(self.getDate()).length == 1?
+        "0"+self.getDate() : self.getDate();
+    }
+    function D() {
+        // A textual representation of a day, three letters
+        return daysShort[self.getDay()];
+    }
+    function F() {
+        // A full textual representation of a month
+        return monthsLong[self.getMonth()];
+    }
+    function g() {
+        // 12-hour format of an hour without leading zeros
+        return self.getHours() > 12? self.getHours()-12 : self.getHours();
+    }
+    function G() {
+        // 24-hour format of an hour without leading zeros
+        return self.getHours();
+    }
+    function h() {
+        // 12-hour format of an hour with leading zeros
+        if (self.getHours() > 12) {
+          var s = new String(self.getHours()-12);
+          return s.length == 1?
+          "0"+ (self.getHours()-12) : self.getHours()-12;
+        } else { 
+          var s = new String(self.getHours());
+          return s.length == 1?
+          "0"+self.getHours() : self.getHours();
+        }  
+    }
+    function H() {
+        // 24-hour format of an hour with leading zeros
+        return new String(self.getHours()).length == 1?
+        "0"+self.getHours() : self.getHours();
+    }
+    function i() {
+        // Minutes with leading zeros
+        return new String(self.getMinutes()).length == 1? 
+        "0"+self.getMinutes() : self.getMinutes(); 
+    }
+    function j() {
+        // Day of the month without leading zeros
+        return self.getDate();
+    }    
+    function l() {
+        // A full textual representation of the day of the week
+        return daysLong[self.getDay()];
+    }
+    function L() {
+        // leap year or not. 1 if leap year, 0 if not.
+        // the logic should match iso's 8601 standard.
+        var y_ = Y();
+        if (         
+            (y_ % 4 == 0 && y_ % 100 != 0) ||
+            (y_ % 4 == 0 && y_ % 100 == 0 && y_ % 400 == 0)
+            ) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+    function m() {
+        // Numeric representation of a month, with leading zeros
+        return self.getMonth() < 9?
+        "0"+(self.getMonth()+1) : 
+        self.getMonth()+1;
+    }
+    function M() {
+        // A short textual representation of a month, three letters
+        return monthsShort[self.getMonth()];
+    }
+    function n() {
+        // Numeric representation of a month, without leading zeros
+        return self.getMonth()+1;
+    }
+    function O() {
+        // Difference to Greenwich time (GMT) in hours
+        var os = Math.abs(self.getTimezoneOffset());
+        var h = ""+Math.floor(os/60);
+        var m = ""+(os%60);
+        h.length == 1? h = "0"+h:1;
+        m.length == 1? m = "0"+m:1;
+        return self.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m;
+    }
+    function r() {
+        // RFC 822 formatted date
+        var r; // result
+        //  Thu    ,     21          Dec         2000
+        r = D() + ", " + j() + " " + M() + " " + Y() +
+        //        16     :    01     :    07          +0200
+            " " + H() + ":" + i() + ":" + s() + " " + O();
+        return r;
+    }
+    function S() {
+        // English ordinal suffix for the day of the month, 2 characters
+        return daysSuffix[self.getDate()-1];
+    }
+    function s() {
+        // Seconds, with leading zeros
+        return new String(self.getSeconds()).length == 1?
+        "0"+self.getSeconds() : self.getSeconds();
+    }
+    function t() {
+
+        // thanks to Matt Bannon for some much needed code-fixes here!
+        var daysinmonths = [null,31,28,31,30,31,30,31,31,30,31,30,31];
+        if (L()==1 && n()==2) return 29; // leap day
+        return daysinmonths[n()];
+    }
+    function U() {
+        // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
+        return Math.round(self.getTime()/1000);
+    }
+    function W() {
+        // Weeknumber, as per ISO specification:
+        // http://www.cl.cam.ac.uk/~mgk25/iso-time.html
+        
+        // if the day is three days before newyears eve,
+        // there's a chance it's "week 1" of next year.
+        // here we check for that.
+        var beforeNY = 364+L() - z();
+        var afterNY  = z();
+        var weekday = w()!=0?w()-1:6; // makes sunday (0), into 6.
+        if (beforeNY <= 2 && weekday <= 2-beforeNY) {
+            return 1;
+        }
+        // similarly, if the day is within threedays of newyears
+        // there's a chance it belongs in the old year.
+        var ny = new Date("January 1 " + Y() + " 00:00:00");
+        var nyDay = ny.getDay()!=0?ny.getDay()-1:6;
+        if (
+            (afterNY <= 2) && 
+            (nyDay >=4)  && 
+            (afterNY >= (6-nyDay))
+            ) {
+            // Since I'm not sure we can just always return 53,
+            // i call the function here again, using the last day
+            // of the previous year, as the date, and then just
+            // return that week.
+            var prevNY = new Date("December 31 " + (Y()-1) + " 00:00:00");
+            return prevNY.formatDate("W");
+        }
+        
+        // week 1, is the week that has the first thursday in it.
+        // note that this value is not zero index.
+        if (nyDay <= 3) {
+            // first day of the year fell on a thursday, or earlier.
+            return 1 + Math.floor( ( z() + nyDay ) / 7 );
+        } else {
+            // first day of the year fell on a friday, or later.
+            return 1 + Math.floor( ( z() - ( 7 - nyDay ) ) / 7 );
+        }
+    }
+    function w() {
+        // Numeric representation of the day of the week
+        return self.getDay();
+    }
+    
+    function Y() {
+        // A full numeric representation of a year, 4 digits
+
+        // we first check, if getFullYear is supported. if it
+        // is, we just use that. ppks code is nice, but wont
+        // work with dates outside 1900-2038, or something like that
+        if (self.getFullYear) {
+            var newDate = new Date("January 1 2001 00:00:00 +0000");
+            var x = newDate .getFullYear();
+            if (x == 2001) {              
+                // i trust the method now
+                return self.getFullYear();
+            }
+        }
+        // else, do this:
+        // codes thanks to ppk:
+        // http://www.xs4all.nl/~ppk/js/introdate.html
+        var x = self.getYear();
+        var y = x % 100;
+        y += (y < 38) ? 2000 : 1900;
+        return y;
+    }
+    function y() {
+        // A two-digit representation of a year
+        var y = Y()+"";
+        return y.substring(y.length-2,y.length);
+    }
+    function z() {
+        // The day of the year, zero indexed! 0 through 366
+        var t = new Date("January 1 " + Y() + " 00:00:00");
+        var diff = self.getTime() - t.getTime();
+        return Math.floor(diff/1000/60/60/24);
+    }
+        
+    var self = this;
+    if (time) {
+        // save time
+        var prevTime = self.getTime();
+        self.setTime(time);
+    }
+    
+    var ia = input.split("");
+    var ij = 0;
+    while (ia[ij]) {
+        if (ia[ij] == "\\") {
+            // this is our way of allowing users to escape stuff
+            ia.splice(ij,1);
+        } else {
+            if (arrayExists(switches,ia[ij])) {
+                ia[ij] = eval(ia[ij] + "()");
+            }
+        }
+        ij++;
+    }
+    // reset time, back to what it was
+    if (prevTime) {
+        self.setTime(prevTime);
+    }
+    return ia.join("");
+}
+
+var date = new Date("1/1/2007 1:11:11");
+var ret = "";
+for (i = 0; i < 10; ++i) {
+    var shortFormat = date.formatDate("Y-m-d");
+    var longFormat = date.formatDate("l, F d, Y g:i:s A");
+    ret += shortFormat + longFormat;
+    date.setTime(date.getTime() + 84266956);
+}
+var expected = "2007-01-01Monday, January 01, 2007 1:11:11 AM2007-01-02Tuesday, January 02, 2007 0:35:37 AM2007-01-03Wednesday, January 03, 2007 0:00:04 AM2007-01-03Wednesday, January 03, 2007 11:24:31 PM2007-01-04Thursday, January 04, 2007 10:48:58 PM2007-01-05Friday, January 05, 2007 10:13:25 PM2007-01-06Saturday, January 06, 2007 9:37:52 PM2007-01-07Sunday, January 07, 2007 9:02:19 PM2007-01-08Monday, January 08, 2007 8:26:46 PM2007-01-09Tuesday, January 09, 2007 7:51:13 PM";
+assertEq(ret, expected);
--- a/js/src/jit-test/tests/parallel/closure-nested-branch.js
+++ b/js/src/jit-test/tests/parallel/closure-nested-branch.js
@@ -1,22 +1,13 @@
 load(libdir + "parallelarray-helpers.js");
 
 function testClosureCreationAndInvocation() {
   var a = range(0, 64);
   function makeaddv(v) {
-    var u = 1;
-    var t = 2;
-    var s = 3;
-    var r = 4;
-    var q = 5;
-    var p = 6;
-    var o = 7;
-    var n = 8;
-    var m = 9;
     var l = 10;
     var k = 11;
     var j = 12;
     var i = 13;
     var h = 14;
     var g = 15;
     var f = 16;
     var e = 17;
@@ -29,21 +20,16 @@ function testClosureCreationAndInvocatio
             : function (x) {
               switch (x) {
               case 0: return a; case 1: return b;
               case 2: return c; case 3: return d;
               case 4: return e; case 5: return f;
               case 6: return g; case 7: return h;
               case 8: return i; case 9: return j;
               case 10: return k; case 11: return l;
-              case 12: return m; case 13: return n;
-              case 14: return o; case 15: return p;
-              case 16: return q; case 17: return r;
-              case 18: return s; case 19: return t;
-              case 20: return u;
               }
             });
   }
   var m;
   for (var i in MODES) m = a.mapPar(makeaddv, MODES[i]);
   assertEq(m[21](1), 20); // v == 21; x == 1 ==> inner function returns b == 20
 
   var n = a.mapPar(function (v) { return function (x) { return v; }});
--- a/js/src/jit-test/tests/parallel/closure-nested-compute.js
+++ b/js/src/jit-test/tests/parallel/closure-nested-compute.js
@@ -1,22 +1,13 @@
 load(libdir + "parallelarray-helpers.js");
 
 function testClosureCreationAndInvocation() {
   var a = range(0, 64);
   function makeaddv(v) {
-    var u = 1;
-    var t = 2;
-    var s = 3;
-    var r = 4;
-    var q = 5;
-    var p = 6;
-    var o = 7;
-    var n = 8;
-    var m = 9;
     var l = 10;
     var k = 11;
     var j = 12;
     var i = 13;
     var h = 14;
     var g = 15;
     var f = 16;
     var e = 17;
@@ -27,21 +18,16 @@ function testClosureCreationAndInvocatio
     return function (x) {
       switch (x) {
       case 0: return a; case 1: return b;
       case 2: return c; case 3: return d;
       case 4: return e; case 5: return f;
       case 6: return g; case 7: return h;
       case 8: return i; case 9: return j;
       case 10: return k; case 11: return l;
-      case 12: return m; case 13: return n;
-      case 14: return o; case 15: return p;
-      case 16: return q; case 17: return r;
-      case 18: return s; case 19: return t;
-      case 20: return u;
       }
     };
   };
   for (var i in MODES) {
     var m = a.mapPar(makeaddv, MODES[i]);
     assertEq(m[21](1), 20); // v == 21; x == 1 ==> inner function returns b == 20
   }
 }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -137,16 +137,143 @@ CodeGenerator::visitOutOfLineCache(OutOf
     return cache->accept(this, ool);
 }
 
 StringObject *
 MNewStringObject::templateObj() const {
     return &templateObj_->as<StringObject>();
 }
 
+// API call to malloc for slots.
+class OutOfLineMallocSlots : public OutOfLineCodeBase<CodeGenerator>
+{
+    const Register result_;
+    int32_t nDynamicSlots_;
+    Label *failure_;
+
+  public:
+    OutOfLineMallocSlots(const Register &result, size_t nslots, Label *failure)
+      : result_(result), nDynamicSlots_(int32_t(nslots)), failure_(failure)
+    {
+        JS_ASSERT(nslots < INT32_MAX);
+    }
+
+    bool accept(CodeGenerator *codegen) {
+        return codegen->visitOutOfLineMallocSlots(this);
+    }
+
+    const Register &result() const {
+        return result_;
+    }
+
+    int32_t numDynamicSlots() const {
+        return nDynamicSlots_;
+    }
+
+    Label *failure() const {
+        return failure_;
+    }
+};
+
+bool
+CodeGenerator::visitOutOfLineMallocSlots(OutOfLineMallocSlots *ool)
+{
+    saveVolatile(ool->result());
+
+    RegisterSet regs = RegisterSet::Volatile();
+    regs.takeUnchecked(ool->result());
+    Register regRuntime = regs.takeGeneral();
+    Register regNSlots = regs.takeGeneral();
+
+    masm.setupUnalignedABICall(2, ool->result());
+    masm.movePtr(ImmPtr(GetIonContext()->runtime), regRuntime);
+    masm.passABIArg(regRuntime);
+    masm.move32(Imm32(uint32_t(ool->numDynamicSlots())), regNSlots);
+    masm.passABIArg(regNSlots);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewSlots));
+    masm.storeCallResult(ool->result());
+
+    restoreVolatile(ool->result());
+
+    masm.branchTestPtr(Assembler::Zero, ool->result(), ool->result(), ool->failure());
+
+    masm.jump(ool->rejoin());
+    return true;
+}
+
+// API call to free slots data on allocation failure.
+class OutOfLineFreeSlots : public OutOfLineCodeBase<CodeGenerator>
+{
+    const Register slots_;
+    Label *fallback_;
+
+  public:
+    OutOfLineFreeSlots(const Register &reg, Label *fallback)
+      : slots_(reg), fallback_(fallback)
+    { }
+
+    bool accept(CodeGenerator *codegen) {
+        return codegen->visitOutOfLineFreeSlots(this);
+    }
+
+    Register slots() const {
+        return slots_;
+    }
+
+    Label *fallback() const {
+        return fallback_;
+    }
+};
+
+bool
+CodeGenerator::visitOutOfLineFreeSlots(OutOfLineFreeSlots *ool)
+{
+    saveVolatile(ool->slots());
+
+    RegisterSet regs = RegisterSet::Volatile();
+    regs.takeUnchecked(ool->slots());
+    Register temp = regs.takeGeneral();
+
+    masm.setupUnalignedABICall(1, temp);
+    masm.passABIArg(ool->slots());
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FreeSlots));
+    restoreVolatile(ool->slots());
+
+    masm.jump(ool->fallback());
+    return true;
+}
+
+// Emit out-of-line code to provide malloc and free paths to allocate slots for
+// objects that require them. In the case that such paths are not required, the
+// given labels are returned as nullptr.
+bool
+CodeGenerator::emitOutOfLineMallocFree(const Register &reg, size_t nDynamicSlots,
+                                       Label *fallback, Label **mallocEntry, Label **mallocRejoin,
+                                       Label **freeEntry)
+{
+    *mallocEntry = nullptr;
+    *mallocRejoin = nullptr;
+    *freeEntry = nullptr;
+    if (!nDynamicSlots)
+        return true;
+
+    OutOfLineMallocSlots *oolMalloc = new(alloc()) OutOfLineMallocSlots(reg, nDynamicSlots, fallback);
+    if (!addOutOfLineCode(oolMalloc))
+        return false;
+
+    OutOfLineFreeSlots *oolFree = new(alloc()) OutOfLineFreeSlots(reg, fallback);
+    if (!addOutOfLineCode(oolFree))
+        return false;
+
+    *mallocEntry = oolMalloc->entry();
+    *mallocRejoin = oolMalloc->rejoin();
+    *freeEntry = oolFree->entry();
+    return true;
+}
+
 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
   : CodeGeneratorSpecific(gen, graph, masm)
 #ifdef DEBUG
   , ionScriptLabels_(gen->alloc())
 #endif
   , unassociatedScriptCounts_(nullptr)
 {
 }
@@ -967,18 +1094,18 @@ CodeGenerator::visitLambda(LLambda *lir)
 
     OutOfLineCode *ool = oolCallVM(LambdaInfo, lir, (ArgList(), ImmGCPtr(info.fun), scopeChain),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
     JS_ASSERT(!info.singletonType);
 
-    masm.newGCThing(output, info.fun, ool->entry(), gc::DefaultHeap);
-    masm.initGCThing(output, info.fun);
+    masm.newGCObject(output, info.fun, ool->entry(), gc::DefaultHeap);
+    masm.initGCObject(output, info.fun);
 
     emitLambdaInit(output, scopeChain, info);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 void
@@ -3355,39 +3482,16 @@ CodeGenerator::visitNewDerivedTypedObjec
     JS_ASSERT(gen->info().executionMode() == SequentialExecution);
 
     pushArg(ToRegister(lir->offset()));
     pushArg(ToRegister(lir->owner()));
     pushArg(ToRegister(lir->type()));
     return callVM(CreateDerivedTypedObjInfo, lir);
 }
 
-bool
-CodeGenerator::visitNewSlots(LNewSlots *lir)
-{
-    Register temp1 = ToRegister(lir->temp1());
-    Register temp2 = ToRegister(lir->temp2());
-    Register temp3 = ToRegister(lir->temp3());
-    Register output = ToRegister(lir->output());
-
-    masm.mov(ImmPtr(GetIonContext()->runtime), temp1);
-    masm.mov(ImmWord(lir->mir()->nslots()), temp2);
-
-    masm.setupUnalignedABICall(2, temp3);
-    masm.passABIArg(temp1);
-    masm.passABIArg(temp2);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewSlots));
-
-    masm.testPtr(output, output);
-    if (!bailoutIf(Assembler::Zero, lir->snapshot()))
-        return false;
-
-    return true;
-}
-
 bool CodeGenerator::visitAtan2D(LAtan2D *lir)
 {
     Register temp = ToRegister(lir->temp());
     FloatRegister y = ToFloatRegister(lir->y());
     FloatRegister x = ToFloatRegister(lir->x());
 
     masm.setupUnalignedABICall(2, temp);
     masm.passABIArg(y, MoveOp::DOUBLE);
@@ -3425,18 +3529,18 @@ CodeGenerator::visitNewArray(LNewArray *
 
     if (lir->mir()->shouldUseVM())
         return visitNewArrayCallVM(lir);
 
     OutOfLineNewArray *ool = new(alloc()) OutOfLineNewArray(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
-    masm.newGCThing(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
-    masm.initGCThing(objReg, templateObject);
+    masm.newGCObject(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
+    masm.initGCObject(objReg, templateObject);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray *ool)
 {
@@ -3502,27 +3606,27 @@ CodeGenerator::visitNewObjectVMCall(LNew
     return true;
 }
 
 bool
 CodeGenerator::visitNewObject(LNewObject *lir)
 {
     JS_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
-    JSObject *templateObject = lir->mir()->templateObject();
+    JSObject *templateObj = lir->mir()->templateObject();
 
     if (lir->mir()->shouldUseVM())
         return visitNewObjectVMCall(lir);
 
     OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
-    masm.newGCThing(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
-    masm.initGCThing(objReg, templateObject);
+    masm.newGCObject(objReg, templateObj, ool->entry(), lir->mir()->initialHeap());
+    masm.initGCObject(objReg, templateObj);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
 {
@@ -3546,90 +3650,72 @@ CodeGenerator::visitNewDeclEnvObject(LNe
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
                                    (ArgList(), ImmGCPtr(info.funMaybeLazy()),
                                     Imm32(gc::DefaultHeap)),
                                    StoreRegisterTo(obj));
     if (!ool)
         return false;
 
-    masm.newGCThing(obj, templateObj, ool->entry(), gc::DefaultHeap);
-    masm.initGCThing(obj, templateObj);
+    masm.newGCObject(obj, templateObj, ool->entry(), gc::DefaultHeap);
+    masm.initGCObject(obj, templateObj);
     masm.bind(ool->rejoin());
     return true;
 }
 
-typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape,
-                                     HandleTypeObject, HeapSlot *);
-static const VMFunction NewCallObjectInfo =
-    FunctionInfo<NewCallObjectFn>(NewCallObject);
+typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape, HandleTypeObject);
+static const VMFunction NewCallObjectInfo = FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
-    Register obj = ToRegister(lir->output());
+    Register objReg = ToRegister(lir->output());
 
     JSObject *templateObj = lir->mir()->templateObject();
 
     // If we have a template object, we can inline call object creation.
-    OutOfLineCode *ool;
-    if (lir->slots()->isRegister()) {
-        ool = oolCallVM(NewCallObjectInfo, lir,
-                        (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
-                                    ImmGCPtr(templateObj->lastProperty()),
-                                    ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()),
-                                    ToRegister(lir->slots())),
-                        StoreRegisterTo(obj));
-    } else {
-        ool = oolCallVM(NewCallObjectInfo, lir,
-                        (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
-                                    ImmGCPtr(templateObj->lastProperty()),
-                                    ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()),
-                                    ImmPtr(nullptr)),
-                        StoreRegisterTo(obj));
-    }
+    OutOfLineCode *ool =
+        oolCallVM(NewCallObjectInfo, lir,
+                  (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
+                              ImmGCPtr(templateObj->lastProperty()),
+                              ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type())),
+                  StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
+    Label *mallocEntry, *mallocRejoin, *freeEntry;
+    if (!emitOutOfLineMallocFree(objReg, templateObj->numDynamicSlots(), ool->entry(),
+                                 &mallocEntry, &mallocRejoin, &freeEntry))
+    {
+        return false;
+    }
+
     if (lir->mir()->needsSingletonType()) {
         // Objects can only be given singleton types in VM calls.
         masm.jump(ool->entry());
     } else {
-        masm.newGCThing(obj, templateObj, ool->entry(), gc::DefaultHeap);
-        masm.initGCThing(obj, templateObj);
-
-        if (lir->slots()->isRegister())
-            masm.storePtr(ToRegister(lir->slots()), Address(obj, JSObject::offsetOfSlots()));
+        masm.newGCObjectAndSlots(objReg, templateObj, ool->entry(), mallocEntry, mallocRejoin,
+                                 freeEntry, gc::DefaultHeap);
+        masm.initGCObject(objReg, templateObj);
     }
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
 {
     Register resultReg = ToRegister(lir->output());
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
     JSObject *templateObj = lir->mir()->templateObj();
 
     emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj);
-
-    // NB: !lir->slots()->isRegister() implies that there is no slots
-    // array at all, and the memory is already zeroed when copying
-    // from the template object
-
-    if (lir->slots()->isRegister()) {
-        Register slotsReg = ToRegister(lir->slots());
-        JS_ASSERT(slotsReg != resultReg);
-        masm.storePtr(slotsReg, Address(resultReg, JSObject::offsetOfSlots()));
-    }
-
     return true;
 }
 
 bool
 CodeGenerator::visitNewDenseArrayPar(LNewDenseArrayPar *lir)
 {
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register lengthReg = ToRegister(lir->length());
@@ -3643,17 +3729,17 @@ CodeGenerator::visitNewDenseArrayPar(LNe
     emitAllocateGCThingPar(lir, tempReg2, cxReg, tempReg0, tempReg1, templateObj);
 
     // Invoke a C helper to allocate the elements.  For convenience,
     // this helper also returns the array back to us, or nullptr, which
     // obviates the need to preserve the register across the call.  In
     // reality, we should probably just have the C helper also
     // *allocate* the array, but that would require that it initialize
     // the various fields of the object, and I didn't want to
-    // duplicate the code in initGCThing() that already does such an
+    // duplicate the code in initGCObject() that already does such an
     // admirable job.
     masm.setupUnalignedABICall(3, tempReg0);
     masm.passABIArg(cxReg);
     masm.passABIArg(tempReg2);
     masm.passABIArg(lengthReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ExtendArrayPar));
 
     Register resultReg = ToRegister(lir->output());
@@ -3678,18 +3764,18 @@ CodeGenerator::visitNewStringObject(LNew
 
     StringObject *templateObj = lir->mir()->templateObj();
 
     OutOfLineCode *ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
-    masm.newGCThing(output, templateObj, ool->entry(), gc::DefaultHeap);
-    masm.initGCThing(output, templateObj);
+    masm.newGCObject(output, templateObj, ool->entry(), gc::DefaultHeap);
+    masm.initGCObject(output, templateObj);
 
     masm.loadStringLength(input, temp);
 
     masm.storeValue(JSVAL_TYPE_STRING, input, Address(output, StringObject::offsetOfPrimitiveValue()));
     masm.storeValue(JSVAL_TYPE_INT32, temp, Address(output, StringObject::offsetOfLength()));
 
     masm.bind(ool->rejoin());
     return true;
@@ -3732,17 +3818,17 @@ CodeGenerator::emitAllocateGCThingPar(LI
 {
     gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
     OutOfLineNewGCThingPar *ool = new(alloc()) OutOfLineNewGCThingPar(lir, allocKind, objReg, cxReg);
     if (!ool || !addOutOfLineCode(ool))
         return false;
 
     masm.newGCThingPar(objReg, cxReg, tempReg1, tempReg2, templateObj, ool->entry());
     masm.bind(ool->rejoin());
-    masm.initGCThing(objReg, templateObj);
+    masm.initGCObject(objReg, templateObj);
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool)
 {
     // As a fallback for allocation in par. exec. mode, we invoke the
     // C helper NewGCThingPar(), which calls into the GC code.  If it
@@ -3924,21 +4010,21 @@ CodeGenerator::visitCreateThisWithTempla
 
     OutOfLineCode *ool = oolCallVM(NewGCObjectInfo, lir,
                                    (ArgList(), Imm32(allocKind), Imm32(initialHeap)),
                                    StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
     // Allocate. If the FreeList is empty, call to VM, which may GC.
-    masm.newGCThing(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
+    masm.newGCObject(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
 
     // Initialize based on the templateObject.
     masm.bind(ool->rejoin());
-    masm.initGCThing(objReg, templateObject);
+    masm.initGCObject(objReg, templateObject);
 
     return true;
 }
 
 typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, IonJSFrameLayout *frame, HandleObject);
 static const VMFunction NewIonArgumentsObjectInfo =
     FunctionInfo<NewIonArgumentsObjectFn>((NewIonArgumentsObjectFn) ArgumentsObject::createForIon);
 
@@ -5642,18 +5728,18 @@ CodeGenerator::visitArrayConcat(LArrayCo
     masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
 
     masm.loadPtr(Address(rhs, JSObject::offsetOfElements()), temp1);
     masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
     masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
 
     // Try to allocate an object.
     JSObject *templateObj = lir->mir()->templateObj();
-    masm.newGCThing(temp1, templateObj, &fail, lir->mir()->initialHeap());
-    masm.initGCThing(temp1, templateObj);
+    masm.newGCObject(temp1, templateObj, &fail, lir->mir()->initialHeap());
+    masm.initGCObject(temp1, templateObj);
     masm.jump(&call);
     {
         masm.bind(&fail);
         masm.movePtr(ImmPtr(nullptr), temp1);
     }
     masm.bind(&call);
 
     pushArg(temp1);
@@ -6014,18 +6100,18 @@ CodeGenerator::visitRest(LRest *lir)
     Register numActuals = ToRegister(lir->numActuals());
     Register temp0 = ToRegister(lir->getTemp(0));
     Register temp1 = ToRegister(lir->getTemp(1));
     Register temp2 = ToRegister(lir->getTemp(2));
     unsigned numFormals = lir->mir()->numFormals();
     JSObject *templateObject = lir->mir()->templateObject();
 
     Label joinAlloc, failAlloc;
-    masm.newGCThing(temp2, templateObject, &failAlloc, gc::DefaultHeap);
-    masm.initGCThing(temp2, templateObject);
+    masm.newGCObject(temp2, templateObject, &failAlloc, gc::DefaultHeap);
+    masm.initGCObject(temp2, templateObject);
     masm.jump(&joinAlloc);
     {
         masm.bind(&failAlloc);
         masm.movePtr(ImmPtr(nullptr), temp2);
     }
     masm.bind(&joinAlloc);
 
     return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -34,16 +34,18 @@ class OutOfLineInterruptCheckPar;
 class OutOfLineInterruptCheckImplicit;
 class OutOfLineUnboxFloatingPoint;
 class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineLoadTypedArray;
 class OutOfLineNewGCThingPar;
 class OutOfLineUpdateCache;
 class OutOfLineCallPostWriteBarrier;
+class OutOfLineMallocSlots;
+class OutOfLineFreeSlots;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     bool generateArgumentsChecks(bool bailout = true);
     bool generateBody();
 
   public:
     CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm = nullptr);
@@ -127,17 +129,20 @@ class CodeGenerator : public CodeGenerat
     bool visitBail(LBail *lir);
     bool visitGetDynamicName(LGetDynamicName *lir);
     bool visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS *lir);
     bool visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV *lir);
     bool visitCallDirectEvalS(LCallDirectEvalS *lir);
     bool visitCallDirectEvalV(LCallDirectEvalV *lir);
     bool visitDoubleToInt32(LDoubleToInt32 *lir);
     bool visitFloat32ToInt32(LFloat32ToInt32 *lir);
-    bool visitNewSlots(LNewSlots *lir);
+    bool visitOutOfLineMallocSlots(OutOfLineMallocSlots *ool);
+    bool visitOutOfLineFreeSlots(OutOfLineFreeSlots *ool);
+    bool emitOutOfLineMallocFree(const Register &reg, size_t nDynamicSlots, Label *fallback,
+                                 Label **mallocEntry, Label **mallocRejoin, Label **freeEntry);
     bool visitNewArrayCallVM(LNewArray *lir);
     bool visitNewArray(LNewArray *lir);
     bool visitOutOfLineNewArray(OutOfLineNewArray *ool);
     bool visitNewObjectVMCall(LNewObject *lir);
     bool visitNewObject(LNewObject *lir);
     bool visitOutOfLineNewObject(OutOfLineNewObject *ool);
     bool visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     bool visitNewCallObject(LNewCallObject *lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4591,49 +4591,41 @@ IonBuilder::createDeclEnvObject(MDefinit
 
 MInstruction *
 IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope)
 {
     // Get a template CallObject that we'll use to generate inline object
     // creation.
     CallObject *templateObj = inspector->templateCallObject();
 
-    // If the CallObject needs dynamic slots, allocate those now.
-    MInstruction *slots;
-    if (templateObj->hasDynamicSlots()) {
-        size_t nslots = JSObject::dynamicSlotsCount(templateObj->numFixedSlots(),
-                                                    templateObj->lastProperty()->slotSpan(templateObj->getClass()),
-                                                    templateObj->getClass());
-        slots = MNewSlots::New(alloc(), nslots);
-    } else {
-        slots = MConstant::New(alloc(), NullValue());
-    }
-    current->add(slots);
-
-    // Allocate the actual object. It is important that no intervening
-    // instructions could potentially bailout, thus leaking the dynamic slots
-    // pointer. Run-once scripts need a singleton type, so always do a VM call
-    // in such cases.
-    MNewCallObject *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce(), slots);
+    // Allocate the actual object. Run-once scripts need a singleton type, so
+    // always do a VM call in such cases.
+    MNewCallObject *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce());
     current->add(callObj);
 
     // Initialize the object's reserved slots. No post barrier is needed here,
     // for the same reason as in createDeclEnvObject.
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::enclosingScopeSlot(), scope));
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee));
 
     // Initialize argument slots.
+    MSlots *slots = nullptr;
     for (AliasedFormalIter i(script()); i; i++) {
         unsigned slot = i.scopeSlot();
         unsigned formal = i.frameIndex();
         MDefinition *param = current->getSlot(info().argSlotUnchecked(formal));
-        if (slot >= templateObj->numFixedSlots())
+        if (slot >= templateObj->numFixedSlots()) {
+            if (!slots) {
+                slots = MSlots::New(alloc(), callObj);
+                current->add(slots);
+            }
             current->add(MStoreSlot::New(alloc(), slots, slot - templateObj->numFixedSlots(), param));
-        else
+        } else {
             current->add(MStoreFixedSlot::New(alloc(), callObj, slot, param));
+        }
     }
 
     return callObj;
 }
 
 MDefinition *
 IonBuilder::createThisScripted(MDefinition *callee)
 {
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -628,88 +628,190 @@ MacroAssembler::clampDoubleToUint8(Float
     {
         move32(Imm32(255), output);
     }
 
     bind(&done);
 #endif
 }
 
+// Inlined version of gc::CheckAllocatorState that checks the bare essentials
+// and bails for anything that cannot be handled with our jit allocators.
 void
-MacroAssembler::newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail,
-                           gc::InitialHeap initialHeap /* = gc::DefaultHeap */)
+MacroAssembler::checkAllocatorState(Label *fail)
 {
-    // Inlined equivalent of js::gc::NewGCThing() without failure case handling.
-
-    int thingSize = int(gc::Arena::thingSize(allocKind));
-
 #ifdef JS_GC_ZEAL
     // Don't execute the inline path if gcZeal is active.
     branch32(Assembler::NotEqual,
              AbsoluteAddress(GetIonContext()->runtime->addressOfGCZeal()), Imm32(0),
              fail);
 #endif
 
     // Don't execute the inline path if the compartment has an object metadata callback,
     // as the metadata to use for the object may vary between executions of the op.
     if (GetIonContext()->compartment->hasObjectMetadataCallback())
         jump(fail);
+}
+
+// Inline Nursery::allocateObject, when it is okay to do so. Return true if we
+// emitted a nursery allocation path.
+bool
+MacroAssembler::nurseryAllocate(const Register &result, gc::AllocKind allocKind, Label *fail,
+                                size_t nDynamicSlots, gc::InitialHeap initialHeap)
+{
+    JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
 #ifdef JSGC_GENERATIONAL
     const Nursery &nursery = GetIonContext()->runtime->gcNursery();
-    if (nursery.isEnabled() &&
-        allocKind <= gc::FINALIZE_OBJECT_LAST &&
-        initialHeap != gc::TenuredHeap)
-    {
-        // Inline Nursery::allocate. No explicit check for nursery.isEnabled()
-        // is needed, as the comparison with the nursery's end will always fail
-        // in such cases.
-        loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result);
-        addPtr(Imm32(thingSize), result);
-        branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(nursery.addressOfCurrentEnd()), result, fail);
-        storePtr(result, AbsoluteAddress(nursery.addressOfPosition()));
+
+    // When the nursery is not enabled, the nursery's position == end pointer,
+    // so allocations will naturally fall back to the interpreter. Thus, the
+    // following check is not technically necessary. However, the nursery is
+    // generally only perma-disabled or disabled when using zeal, so prefer to
+    // compile the tenured fast-path instead so we don't bail constantly.
+    if (!nursery.isEnabled())
+        return false;
+
+    // Explicitly request for tenured allocation for these objects.
+    if (initialHeap == gc::TenuredHeap)
+        return false;
+
+    // This allocation site is requesting too many dynamic slots.
+    if (nDynamicSlots > Nursery::MaxNurserySlots)
+        return false;
+
+    // No explicit check for nursery.isEnabled() is needed, as the comparison
+    // with the nursery's end will always fail in such cases.
+    int thingSize = int(gc::Arena::thingSize(allocKind));
+    int totalSize = thingSize + nDynamicSlots * sizeof(HeapSlot);
+    loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result);
+    addPtr(Imm32(totalSize), result);
+    branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(nursery.addressOfCurrentEnd()), result, fail);
+    storePtr(result, AbsoluteAddress(nursery.addressOfPosition()));
+
+    if (!nDynamicSlots) {
+        JS_ASSERT(thingSize == totalSize);
+        subPtr(Imm32(totalSize), result);
+    } else {
+        // Offset to get the slots pointer in |result|.
+        subPtr(Imm32(totalSize - thingSize), result);
+        // Store the slots pointer into slots address as an offset from the
+        // slots so that we do not need to take a second register.
+        JS_ASSERT(int(JSObject::offsetOfSlots()) - thingSize < 0);
+        storePtr(result, Address(result, int(JSObject::offsetOfSlots()) - thingSize));
+        // Load the start of the object into result.
         subPtr(Imm32(thingSize), result);
-        return;
     }
+
+    return true;
+#else
+    return false;
 #endif // JSGC_GENERATIONAL
+}
 
+// Inlined version of FreeSpan::allocate.
+void
+MacroAssembler::freeSpanAllocate(const Register &result, gc::AllocKind allocKind, Label *fail)
+{
     CompileZone *zone = GetIonContext()->compartment->zone();
+    int thingSize = int(gc::Arena::thingSize(allocKind));
 
-    // Inline FreeSpan::allocate.
-    // There is always exactly one FreeSpan per allocKind per JSCompartment.
-    // If a FreeSpan is replaced, its members are updated in the freeLists table,
-    // which the code below always re-reads.
+    // Load FreeSpan::first of |zone|'s freeLists for |allocKind|. If there is
+    // no room remaining in the span, we bail to finish the allocation. The
+    // interpreter will call |refillFreeLists|, setting up a new FreeSpan so
+    // that we can continue allocating in the jit.
     loadPtr(AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)), result);
     branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(zone->addressOfFreeListLast(allocKind)), result, fail);
-
     addPtr(Imm32(thingSize), result);
     storePtr(result, AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)));
     subPtr(Imm32(thingSize), result);
 }
 
+// Inlined equivalent of gc::AllocateObject, without failure case handling.
 void
-MacroAssembler::newGCThing(const Register &result, JSObject *templateObject,
-                           Label *fail, gc::InitialHeap initialHeap)
+MacroAssembler::allocateObject(const Register &result, gc::AllocKind allocKind, Label *fallback,
+                               Label *mallocEntry, Label *mallocRejoin, Label *freeEntry,
+                               size_t nDynamicSlots, gc::InitialHeap initialHeap)
+{
+    JS_ASSERT_IF(nDynamicSlots, mallocEntry && mallocRejoin && freeEntry);
+
+    checkAllocatorState(fallback);
+    if (nurseryAllocate(result, allocKind, fallback, nDynamicSlots, initialHeap))
+        return;
+
+    if (!nDynamicSlots) {
+        freeSpanAllocate(result, allocKind, fallback);
+    } else {
+        // Exit to do the malloc out-of-line.
+        jump(mallocEntry);
+
+        // Since we have to jump out-of-line anyway, insert our failure
+        // handling code into the jumped-over block so that it does not force a
+        // jump in the success case below.
+        Label allocFail;
+        bind(&allocFail);
+        pop(result);
+        jump(freeEntry);
+
+        // Rejoin from successful malloc; malloc failure falls straight though
+        // to the fallback since there is no cleanup to do yet.
+        bind(mallocRejoin);
+
+        // Save our malloc pointer to the stack so we can re-use the register.
+        push(result);
+
+        // Allocate the object and write the malloced slots.
+        freeSpanAllocate(result, allocKind, &allocFail);
+        pop(Address(result, JSObject::offsetOfSlots()));
+    }
+}
+
+// Inlined equivalent of gc::AllocateNonObject, without failure case handling.
+// Non-object allocation does not need to worry about slots, so can take a
+// simpler path.
+void
+MacroAssembler::allocateNonObject(const Register &result, gc::AllocKind allocKind, Label *fail)
+{
+    checkAllocatorState(fail);
+    freeSpanAllocate(result, allocKind, fail);
+}
+
+void
+MacroAssembler::newGCObject(const Register &result, JSObject *templateObject, Label *fallback,
+                            gc::InitialHeap initialHeap)
+{
+    gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
+    JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
+    JS_ASSERT(templateObject->numDynamicSlots() == 0);
+
+    allocateObject(result, allocKind, fallback, nullptr, nullptr, nullptr, 0, initialHeap);
+}
+
+void
+MacroAssembler::newGCObjectAndSlots(const Register &result, JSObject *templateObject, Label *fallback,
+                                    Label *mallocEntry, Label *mallocRejoin, Label *freeEntry,
+                                    gc::InitialHeap initialHeap)
 {
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
     JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
-    newGCThing(result, allocKind, fail, initialHeap);
+    allocateObject(result, allocKind, fallback, mallocEntry, mallocRejoin, freeEntry,
+                   templateObject->numDynamicSlots(), initialHeap);
 }
 
 void
 MacroAssembler::newGCString(const Register &result, Label *fail)
 {
-    newGCThing(result, js::gc::FINALIZE_STRING, fail);
+    allocateNonObject(result, js::gc::FINALIZE_STRING, fail);
 }
 
 void
 MacroAssembler::newGCShortString(const Register &result, Label *fail)
 {
-    newGCThing(result, js::gc::FINALIZE_SHORT_STRING, fail);
+    allocateNonObject(result, js::gc::FINALIZE_SHORT_STRING, fail);
 }
 
 void
 MacroAssembler::newGCThingPar(const Register &result, const Register &cx,
                               const Register &tempReg1, const Register &tempReg2,
                               gc::AllocKind allocKind, Label *fail)
 {
     // Similar to ::newGCThing(), except that it allocates from a custom
@@ -748,16 +850,19 @@ MacroAssembler::newGCThingPar(const Regi
     // result = tempReg2;
     // tempReg2 += thingSize;
     movePtr(tempReg2, result);
     addPtr(Imm32(thingSize), tempReg2);
 
     // Update `first`
     // tempReg1->first = tempReg2;
     storePtr(tempReg2, Address(tempReg1, offsetof(gc::FreeSpan, first)));
+
+    // Initialize slots to nullptr, so that we can share initGCObject.
+    storePtr(ImmPtr(nullptr), Address(result, JSObject::offsetOfSlots()));
 }
 
 void
 MacroAssembler::newGCThingPar(const Register &result, const Register &cx,
                               const Register &tempReg1, const Register &tempReg2,
                               JSObject *templateObject, Label *fail)
 {
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
@@ -778,25 +883,51 @@ void
 MacroAssembler::newGCShortStringPar(const Register &result, const Register &cx,
                                     const Register &tempReg1, const Register &tempReg2,
                                     Label *fail)
 {
     newGCThingPar(result, cx, tempReg1, tempReg2, js::gc::FINALIZE_SHORT_STRING, fail);
 }
 
 void
-MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject)
+MacroAssembler::initGCSlots(const Register &obj, JSObject *templateObject)
+{
+    // Slots of non-array objects are required to be initialized.
+    // Use the values currently in the template object.
+    uint32_t nslots = templateObject->lastProperty()->slotSpan(templateObject->getClass());
+    if (nslots == 0)
+        return;
+
+    uint32_t nfixed = Min(templateObject->numFixedSlots(), nslots);
+    for (unsigned i = 0; i < nfixed; i++)
+        storeValue(templateObject->getFixedSlot(i), Address(obj, JSObject::getFixedSlotOffset(i)));
+
+    if (nfixed < nslots) {
+        JS_ASSERT(nslots - nfixed <= templateObject->numDynamicSlots());
+
+        push(obj);
+        loadPtr(Address(obj, JSObject::offsetOfSlots()), obj);
+        for (unsigned i = 0; i < nslots - nfixed; ++i)
+            storeValue(templateObject->getDynamicSlot(i), Address(obj, i * sizeof(HeapSlot)));
+        pop(obj);
+    }
+}
+
+void
+MacroAssembler::initGCObject(const Register &obj, JSObject *templateObject)
 {
     // Fast initialization of an empty object returned by NewGCThing().
 
     JS_ASSERT(!templateObject->hasDynamicElements());
 
     storePtr(ImmGCPtr(templateObject->lastProperty()), Address(obj, JSObject::offsetOfShape()));
     storePtr(ImmGCPtr(templateObject->type()), Address(obj, JSObject::offsetOfType()));
-    storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots()));
+    // Note: if we needed external slots, |slots| was initialized by object allocation.
+    if (!templateObject->hasDynamicSlots())
+        storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots()));
 
     if (templateObject->is<ArrayObject>()) {
         JS_ASSERT(!templateObject->getDenseInitializedLength());
 
         int elementsOffset = JSObject::offsetOfFixedElements();
 
         addPtr(Imm32(elementsOffset), obj);
         storePtr(obj, Address(obj, -elementsOffset + JSObject::offsetOfElements()));
@@ -808,34 +939,26 @@ MacroAssembler::initGCThing(const Regist
         store32(Imm32(templateObject->getDenseInitializedLength()),
                 Address(obj, elementsOffset + ObjectElements::offsetOfInitializedLength()));
         store32(Imm32(templateObject->as<ArrayObject>().length()),
                 Address(obj, elementsOffset + ObjectElements::offsetOfLength()));
         store32(Imm32(templateObject->shouldConvertDoubleElements()
                       ? ObjectElements::CONVERT_DOUBLE_ELEMENTS
                       : 0),
                 Address(obj, elementsOffset + ObjectElements::offsetOfFlags()));
+        JS_ASSERT(!templateObject->hasPrivate());
     } else {
         storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements()));
-
-        // Fixed slots of non-array objects are required to be initialized.
-        // Use the values currently in the template object.
-        size_t nslots = Min(templateObject->numFixedSlots(),
-                            templateObject->lastProperty()->slotSpan(templateObject->getClass()));
-        for (unsigned i = 0; i < nslots; i++) {
-            storeValue(templateObject->getFixedSlot(i),
-                       Address(obj, JSObject::getFixedSlotOffset(i)));
+        initGCSlots(obj, templateObject);
+        if (templateObject->hasPrivate()) {
+            uint32_t nfixed = templateObject->numFixedSlots();
+            storePtr(ImmPtr(templateObject->getPrivate()),
+                     Address(obj, JSObject::getPrivateDataOffset(nfixed)));
         }
     }
-
-    if (templateObject->hasPrivate()) {
-        uint32_t nfixed = templateObject->numFixedSlots();
-        storePtr(ImmPtr(templateObject->getPrivate()),
-                 Address(obj, JSObject::getPrivateDataOffset(nfixed)));
-    }
 }
 
 void
 MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
                                Register temp, Label *fail)
 {
     JS_ASSERT(IsEqualityOp(op));
 
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -778,36 +778,50 @@ class MacroAssembler : public MacroAssem
     }
 
     // Emit type case branch on tag matching if the type tag in the definition
     // might actually be that type.
     void branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
                                  Label *label);
 
     // Inline allocation.
-    void newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail,
-                    gc::InitialHeap initialHeap = gc::DefaultHeap);
-    void newGCThing(const Register &result, JSObject *templateObject, Label *fail,
-                    gc::InitialHeap initialHeap);
+  private:
+    void checkAllocatorState(Label *fail);
+    bool nurseryAllocate(const Register &result, gc::AllocKind allocKind, Label *fail,
+                         size_t nDynamicSlots, gc::InitialHeap initialHeap);
+    void freeSpanAllocate(const Register &result, gc::AllocKind allocKind, Label *fail);
+    void allocateObject(const Register &result, gc::AllocKind allocKind, Label *fail,
+                        Label *oolMalloc, Label *oolMallocExit, Label *oolFree,
+                        size_t nDynamicSlots, gc::InitialHeap initialHeap);
+    void allocateNonObject(const Register &result, gc::AllocKind allocKind, Label *fail);
+    void initGCSlots(const Register &obj, JSObject *templateObject);
+  public:
+    void newGCObject(const Register &result, JSObject *templateObject, Label *fail,
+                     gc::InitialHeap initialHeap);
+    void newGCObjectAndSlots(const Register &result, JSObject *templateObject, Label *fail,
+                             Label *oolMalloc, Label *oolMallocExit, Label *oolFree,
+                             gc::InitialHeap initialHeap);
+    void initGCObject(const Register &obj, JSObject *templateObject);
+
     void newGCString(const Register &result, Label *fail);
     void newGCShortString(const Register &result, Label *fail);
 
     void newGCThingPar(const Register &result, const Register &cx,
                        const Register &tempReg1, const Register &tempReg2,
                        gc::AllocKind allocKind, Label *fail);
     void newGCThingPar(const Register &result, const Register &cx,
                        const Register &tempReg1, const Register &tempReg2,
                        JSObject *templateObject, Label *fail);
+
     void newGCStringPar(const Register &result, const Register &cx,
                         const Register &tempReg1, const Register &tempReg2,
                         Label *fail);
     void newGCShortStringPar(const Register &result, const Register &cx,
                              const Register &tempReg1, const Register &tempReg2,
                              Label *fail);
-    void initGCThing(const Register &obj, JSObject *templateObject);
 
     // Compares two strings for equality based on the JSOP.
     // This checks for identical pointers, atoms and length and fails for everything else.
     void compareStrings(JSOp op, Register left, Register right, Register result,
                         Register temp, Label *fail);
 
     // Checks the flags that signal that parallel code may need to interrupt or
     // abort.  Branches to fail in that case.
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -297,42 +297,16 @@ class LGoto : public LControlInstruction
          setSuccessor(0, block);
     }
 
     MBasicBlock *target() const {
         return getSuccessor(0);
     }
 };
 
-class LNewSlots : public LCallInstructionHelper<1, 0, 3>
-{
-  public:
-    LIR_HEADER(NewSlots)
-
-    LNewSlots(const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3) {
-        setTemp(0, temp1);
-        setTemp(1, temp2);
-        setTemp(2, temp3);
-    }
-
-    const LDefinition *temp1() {
-        return getTemp(0);
-    }
-    const LDefinition *temp2() {
-        return getTemp(1);
-    }
-    const LDefinition *temp3() {
-        return getTemp(2);
-    }
-
-    MNewSlots *mir() const {
-        return mir_->toNewSlots();
-    }
-};
-
 class LNewArray : public LInstructionHelper<1, 0, 0>
 {
   public:
     LIR_HEADER(NewArray)
 
     const char *extraName() const {
         return mir()->shouldUseVM() ? "VMCall" : nullptr;
     }
@@ -435,87 +409,54 @@ class LNewDeclEnvObject : public LInstru
   public:
     LIR_HEADER(NewDeclEnvObject);
 
     MNewDeclEnvObject *mir() const {
         return mir_->toNewDeclEnvObject();
     }
 };
 
-// Allocates a new CallObject. The inputs are:
-//      slots: either a reg representing a HeapSlot *, or a placeholder
-//             meaning that no slots pointer is needed.
+// Allocates a new CallObject.
 //
 // This instruction generates two possible instruction sets:
 //   (1) If the call object is extensible, this is a callVM to create the
 //       call object.
 //   (2) Otherwise, an inline allocation of the call object is attempted.
 //
-class LNewCallObject : public LInstructionHelper<1, 1, 0>
+class LNewCallObject : public LInstructionHelper<1, 0, 0>
 {
   public:
     LIR_HEADER(NewCallObject)
 
-    LNewCallObject(const LAllocation &slots) {
-        setOperand(0, slots);
-    }
-
-    const LAllocation *slots() {
-        return getOperand(0);
-    }
     MNewCallObject *mir() const {
         return mir_->toNewCallObject();
     }
 };
 
-class LNewCallObjectPar : public LInstructionHelper<1, 2, 2>
-{
-    LNewCallObjectPar(const LAllocation &cx, const LAllocation &slots,
-                      const LDefinition &temp1, const LDefinition &temp2)
-    {
+class LNewCallObjectPar : public LInstructionHelper<1, 1, 2>
+{
+    LNewCallObjectPar(const LAllocation &cx, const LDefinition &temp1, const LDefinition &temp2) {
         setOperand(0, cx);
-        setOperand(1, slots);
         setTemp(0, temp1);
         setTemp(1, temp2);
     }
 
 public:
     LIR_HEADER(NewCallObjectPar);
 
-    static LNewCallObjectPar *NewWithSlots(TempAllocator &alloc,
-                                           const LAllocation &cx, const LAllocation &slots,
-                                           const LDefinition &temp1, const LDefinition &temp2)
+    static LNewCallObjectPar *New(TempAllocator &alloc, const LAllocation &cx,
+                                  const LDefinition &temp1, const LDefinition &temp2)
     {
-        return new(alloc) LNewCallObjectPar(cx, slots, temp1, temp2);
-    }
-
-    static LNewCallObjectPar *NewSansSlots(TempAllocator &alloc,
-                                           const LAllocation &cx,
-                                           const LDefinition &temp1, const LDefinition &temp2)
-    {
-        LAllocation slots = LConstantIndex::Bogus();
-        return new(alloc) LNewCallObjectPar(cx, slots, temp1, temp2);
+        return new(alloc) LNewCallObjectPar(cx, temp1, temp2);
     }
 
     const LAllocation *forkJoinContext() {
         return getOperand(0);
     }
 
-    const LAllocation *slots() {
-        return getOperand(1);
-    }
-
-    const bool hasDynamicSlots() {
-        // TO INVESTIGATE: Felix tried using isRegister() method here,
-        // but for useFixed(_, CallTempN), isRegister() is false (and
-        // isUse() is true).  So for now ignore that and try to match
-        // the LConstantIndex::Bogus() generated above instead.
-        return slots() && ! slots()->isConstant();
-    }
-
     const MNewCallObjectPar *mir() const {
         return mir_->toNewCallObjectPar();
     }
 
     const LDefinition *getTemp0() {
         return getTemp(0);
     }
 
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -20,17 +20,16 @@
     _(CloneLiteral)                 \
     _(Parameter)                    \
     _(Callee)                       \
     _(TableSwitch)                  \
     _(TableSwitchV)                 \
     _(Goto)                         \
     _(NewArray)                     \
     _(NewObject)                    \
-    _(NewSlots)                     \
     _(NewDeclEnvObject)             \
     _(NewCallObject)                \
     _(NewStringObject)              \
     _(NewPar)                       \
     _(NewDenseArrayPar)             \
     _(NewCallObjectPar)             \
     _(NewDerivedTypedObject)        \
     _(AbortPar)                     \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -156,27 +156,16 @@ LIRGenerator::visitDefVar(MDefVar *ins)
 bool
 LIRGenerator::visitDefFun(MDefFun *ins)
 {
     LDefFun *lir = new(alloc()) LDefFun(useRegisterAtStart(ins->scopeChain()));
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
-LIRGenerator::visitNewSlots(MNewSlots *ins)
-{
-    // No safepoint needed, since we don't pass a cx.
-    LNewSlots *lir = new(alloc()) LNewSlots(tempFixed(CallTempReg0), tempFixed(CallTempReg1),
-                                            tempFixed(CallTempReg2));
-    if (!assignSnapshot(lir))
-        return false;
-    return defineReturn(lir, ins);
-}
-
-bool
 LIRGenerator::visitNewArray(MNewArray *ins)
 {
     LNewArray *lir = new(alloc()) LNewArray();
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewObject(MNewObject *ins)
@@ -190,23 +179,17 @@ LIRGenerator::visitNewDeclEnvObject(MNew
 {
     LNewDeclEnvObject *lir = new(alloc()) LNewDeclEnvObject();
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewCallObject(MNewCallObject *ins)
 {
-    LAllocation slots;
-    if (ins->slots()->type() == MIRType_Slots)
-        slots = useRegister(ins->slots());
-    else
-        slots = LConstantIndex::Bogus();
-
-    LNewCallObject *lir = new(alloc()) LNewCallObject(slots);
+    LNewCallObject *lir = new(alloc()) LNewCallObject();
     if (!define(lir, ins))
         return false;
 
     if (!assignSafepoint(lir, ins))
         return false;
 
     return true;
 }
@@ -223,24 +206,17 @@ LIRGenerator::visitNewDerivedTypedObject
 
 bool
 LIRGenerator::visitNewCallObjectPar(MNewCallObjectPar *ins)
 {
     const LAllocation &parThreadContext = useRegister(ins->forkJoinContext());
     const LDefinition &temp1 = temp();
     const LDefinition &temp2 = temp();
 
-    LNewCallObjectPar *lir;
-    if (ins->slots()->type() == MIRType_Slots) {
-        const LAllocation &slots = useRegister(ins->slots());
-        lir = LNewCallObjectPar::NewWithSlots(alloc(), parThreadContext, slots, temp1, temp2);
-    } else {
-        lir = LNewCallObjectPar::NewSansSlots(alloc(), parThreadContext, temp1, temp2);
-    }
-
+    LNewCallObjectPar *lir = LNewCallObjectPar::New(alloc(), parThreadContext, temp1, temp2);
     return define(lir, ins);
 }
 
 bool
 LIRGenerator::visitNewStringObject(MNewStringObject *ins)
 {
     JS_ASSERT(ins->input()->type() == MIRType_String);
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -61,17 +61,16 @@ class LIRGenerator : public LIRGenerator
 
     // Visitor hooks are explicit, to give CPU-specific versions a chance to
     // intercept without a bunch of explicit gunk in the .cpp.
     bool visitCloneLiteral(MCloneLiteral *ins);
     bool visitParameter(MParameter *param);
     bool visitCallee(MCallee *callee);
     bool visitGoto(MGoto *ins);
     bool visitTableSwitch(MTableSwitch *tableswitch);
-    bool visitNewSlots(MNewSlots *ins);
     bool visitNewArray(MNewArray *ins);
     bool visitNewObject(MNewObject *ins);
     bool visitNewDeclEnvObject(MNewDeclEnvObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
     bool visitNewStringObject(MNewStringObject *ins);
     bool visitNewDerivedTypedObject(MNewDerivedTypedObject *ins);
     bool visitNewPar(MNewPar *ins);
     bool visitNewCallObjectPar(MNewCallObjectPar *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8855,43 +8855,16 @@ class MPostWriteBarrier
     bool isConsistentFloat32Use() const {
         // During lowering, values that neither have object nor value MIR type
         // are ignored, thus Float32 can show up at this point without any issue.
         return true;
     }
 #endif
 };
 
-class MNewSlots : public MNullaryInstruction
-{
-    unsigned nslots_;
-
-    MNewSlots(unsigned nslots)
-      : nslots_(nslots)
-    {
-        setResultType(MIRType_Slots);
-    }
-
-  public:
-    INSTRUCTION_HEADER(NewSlots)
-
-    static MNewSlots *New(TempAllocator &alloc, unsigned nslots) {
-        return new(alloc) MNewSlots(nslots);
-    }
-    unsigned nslots() const {
-        return nslots_;
-    }
-    AliasSet getAliasSet() const {
-        return AliasSet::None();
-    }
-    bool possiblyCalls() const {
-        return true;
-    }
-};
-
 class MNewDeclEnvObject : public MNullaryInstruction
 {
     CompilerRootObject templateObj_;
 
     MNewDeclEnvObject(JSObject *templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
@@ -8908,78 +8881,71 @@ class MNewDeclEnvObject : public MNullar
     JSObject *templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
-class MNewCallObject : public MUnaryInstruction
+class MNewCallObject : public MNullaryInstruction
 {
     CompilerRootObject templateObj_;
     bool needsSingletonType_;
 
-    MNewCallObject(JSObject *templateObj, bool needsSingletonType, MDefinition *slots)
-      : MUnaryInstruction(slots),
+    MNewCallObject(JSObject *templateObj, bool needsSingletonType)
+      : MNullaryInstruction(),
         templateObj_(templateObj),
         needsSingletonType_(needsSingletonType)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObject)
 
-    static MNewCallObject *New(TempAllocator &alloc, JSObject *templateObj, bool needsSingletonType,
-                               MDefinition *slots)
-    {
-        return new(alloc) MNewCallObject(templateObj, needsSingletonType, slots);
-    }
-
-    MDefinition *slots() {
-        return getOperand(0);
-    }
+    static MNewCallObject *New(TempAllocator &alloc, JSObject *templateObj, bool needsSingletonType)
+    {
+        return new(alloc) MNewCallObject(templateObj, needsSingletonType);
+    }
+
     JSObject *templateObject() {
         return templateObj_;
     }
     bool needsSingletonType() const {
         return needsSingletonType_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
-class MNewCallObjectPar : public MBinaryInstruction
+class MNewCallObjectPar : public MUnaryInstruction
 {
     CompilerRootObject templateObj_;
 
-    MNewCallObjectPar(MDefinition *cx, JSObject *templateObj, MDefinition *slots)
-        : MBinaryInstruction(cx, slots),
-          templateObj_(templateObj)
+    MNewCallObjectPar(MDefinition *cx, JSObject *templateObj)
+      : MUnaryInstruction(cx),
+        templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObjectPar);
 
     static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObject *callObj) {
-        return new(alloc) MNewCallObjectPar(cx, callObj->templateObject(), callObj->slots());
+        JS_ASSERT(!callObj->templateObject()->hasDynamicSlots());
+        return new(alloc) MNewCallObjectPar(cx, callObj->templateObject());
     }
 
     MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
-    MDefinition *slots() const {
-        return getOperand(1);
-    }
-
     JSObject *templateObj() const {
         return templateObj_;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -80,17 +80,16 @@ namespace jit {
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
     _(ToFloat32)                                                            \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
-    _(NewSlots)                                                             \
     _(NewArray)                                                             \
     _(NewObject)                                                            \
     _(NewDeclEnvObject)                                                     \
     _(NewCallObject)                                                        \
     _(NewStringObject)                                                      \
     _(InitElem)                                                             \
     _(InitElemGetterSetter)                                                 \
     _(MutateProto)                                                          \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -174,17 +174,16 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(Unbox)
     SAFE_OP(GuardObject)
     SAFE_OP(ToDouble)
     SAFE_OP(ToFloat32)
     SAFE_OP(ToInt32)
     SAFE_OP(TruncateToInt32)
     SAFE_OP(MaybeToDoubleElement)
     CUSTOM_OP(ToString)
-    SAFE_OP(NewSlots)
     CUSTOM_OP(NewArray)
     CUSTOM_OP(NewObject)
     CUSTOM_OP(NewCallObject)
     UNSAFE_OP(NewDerivedTypedObject)
     UNSAFE_OP(InitElem)
     UNSAFE_OP(InitElemGetterSetter)
     UNSAFE_OP(MutateProto)
     UNSAFE_OP(InitProp)
@@ -518,16 +517,20 @@ bool
 ParallelSafetyVisitor::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins)
 {
     return replaceWithNewPar(ins, ins->templateObject());
 }
 
 bool
 ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins)
 {
+    if (ins->templateObject()->hasDynamicSlots()) {
+        SpewMIR(ins, "call with dynamic slots");
+        return markUnsafe();
+    }
     replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
     return true;
 }
 
 bool
 ParallelSafetyVisitor::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
@@ -632,21 +635,16 @@ ParallelSafetyVisitor::insertWriteGuard(
         break;
 
       case MIRType_Slots:
         switch (valueBeingWritten->op()) {
           case MDefinition::Op_Slots:
             object = valueBeingWritten->toSlots()->object();
             break;
 
-          case MDefinition::Op_NewSlots:
-            // Values produced by new slots will ALWAYS be
-            // thread-local.
-            return true;
-
           default:
             SpewMIR(writeInstruction, "cannot insert write guard for %s",
                     valueBeingWritten->opName());
             return markUnsafe();
         }
         break;
 
       case MIRType_Elements:
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -509,31 +509,33 @@ InterruptCheck(JSContext *cx)
     return !!js_HandleExecutionInterrupt(cx);
 }
 
 HeapSlot *
 NewSlots(JSRuntime *rt, unsigned nslots)
 {
     JS_STATIC_ASSERT(sizeof(Value) == sizeof(HeapSlot));
 
-    Value *slots = reinterpret_cast<Value *>(rt->malloc_(nslots * sizeof(Value)));
+    Value *slots = rt->pod_malloc<Value>(nslots);
     if (!slots)
         return nullptr;
 
-    for (unsigned i = 0; i < nslots; i++)
-        slots[i] = UndefinedValue();
-
     return reinterpret_cast<HeapSlot *>(slots);
 }
 
+void
+FreeSlots(HeapSlot *slots)
+{
+    js_free(slots);
+}
+
 JSObject *
-NewCallObject(JSContext *cx, HandleScript script,
-              HandleShape shape, HandleTypeObject type, HeapSlot *slots)
+NewCallObject(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type)
 {
-    JSObject *obj = CallObject::create(cx, script, shape, type, slots);
+    JSObject *obj = CallObject::create(cx, script, shape, type);
     if (!obj)
         return nullptr;
 
 #ifdef JSGC_GENERATIONAL
     // The JIT creates call objects in the nursery, so elides barriers for
     // the initializing writes. The interpreter, however, may have allocated
     // the call object tenured, so barrier as needed before re-entering.
     if (!IsInsideNursery(cx->runtime(), obj))
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -613,18 +613,19 @@ bool CharCodeAt(JSContext *cx, HandleStr
 JSFlatString *StringFromCharCode(JSContext *cx, int32_t code);
 
 bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
                  bool strict, jsbytecode *pc);
 
 bool InterruptCheck(JSContext *cx);
 
 HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots);
-JSObject *NewCallObject(JSContext *cx, HandleScript script,
-                        HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+void FreeSlots(HeapSlot *slots);
+JSObject *NewCallObject(JSContext *cx, HandleScript script, HandleShape shape,
+                        HandleTypeObject type);
 JSObject *NewStringObject(JSContext *cx, HandleString str);
 
 bool SPSEnter(JSContext *cx, HandleScript script);
 bool SPSExit(JSContext *cx, HandleScript script);
 
 bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out);
 bool OperatorInI(JSContext *cx, uint32_t index, HandleObject obj, bool *out);
 
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -621,16 +621,20 @@ class MacroAssemblerARMCompat : public M
     }
 
     void pop(const Register &reg) {
         ma_pop(reg);
     }
     void pop(const FloatRegister &reg) {
         ma_vpop(VFPRegister(reg));
     }
+    void pop(const Address &address) {
+        ma_ldr(Operand(address.base, address.offset), ScratchRegister);
+        ma_pop(ScratchRegister);
+    }
 
     void popN(const Register &reg, Imm32 extraSpace) {
         Imm32 totSpace = Imm32(extraSpace.value + 4);
         ma_dtr(IsLoad, sp, totSpace, reg, PostIndex);
     }
 
     CodeOffsetLabel toggledJump(Label *label);
 
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -1176,30 +1176,33 @@ class AssemblerX86Shared
     }
     void push(const Register &src) {
         masm.push_r(src.code());
     }
     void push(const Address &src) {
         masm.push_m(src.offset, src.base.code());
     }
 
-    void pop(const Operand &src) {
-        switch (src.kind()) {
+    void pop(const Operand &dest) {
+        switch (dest.kind()) {
           case Operand::REG:
-            masm.pop_r(src.reg());
+            masm.pop_r(dest.reg());
             break;
           case Operand::MEM_REG_DISP:
-            masm.pop_m(src.disp(), src.base());
+            masm.pop_m(dest.disp(), dest.base());
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
-    void pop(const Register &src) {
-        masm.pop_r(src.code());
+    void pop(const Register &dest) {
+        masm.pop_r(dest.code());
+    }
+    void pop(const Address &dest) {
+        masm.pop_m(dest.offset, dest.base.code());
     }
 
     void pushFlags() {
         masm.push_flags();
     }
     void popFlags() {
         masm.pop_flags();
     }
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -935,36 +935,37 @@ JS::DisableGenerationalGC(JSRuntime *rt)
 {
 #ifdef JSGC_GENERATIONAL
     if (IsGenerationalGCEnabled(rt)) {
         MinorGC(rt, JS::gcreason::API);
         rt->gcNursery.disable();
         rt->gcStoreBuffer.disable();
     }
 #endif
-    ++rt->gcGenerationalDisabled;
 }
 
 extern JS_FRIEND_API(void)
 JS::EnableGenerationalGC(JSRuntime *rt)
 {
-    JS_ASSERT(rt->gcGenerationalDisabled > 0);
-    --rt->gcGenerationalDisabled;
 #ifdef JSGC_GENERATIONAL
-    if (IsGenerationalGCEnabled(rt)) {
+    if (!IsGenerationalGCEnabled(rt)) {
         rt->gcNursery.enable();
         rt->gcStoreBuffer.enable();
     }
 #endif
 }
 
 extern JS_FRIEND_API(bool)
 JS::IsGenerationalGCEnabled(JSRuntime *rt)
 {
-    return rt->gcGenerationalDisabled == 0;
+#ifdef JSGC_GENERATIONAL
+    return rt->gcNursery.isEnabled();
+#else
+    return false;
+#endif
 }
 
 JS_FRIEND_API(bool)
 JS::IsIncrementalBarrierNeeded(JSRuntime *rt)
 {
     return (rt->gcIncrementalState == gc::MARK && !rt->isHeapBusy());
 }
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -250,18 +250,17 @@ class JSObject : public js::ObjectImpl
     /*
      * Make a non-array object with the specified initial state. This method
      * takes ownership of any extantSlots it is passed.
      */
     static inline JSObject *create(js::ExclusiveContext *cx,
                                    js::gc::AllocKind kind,
                                    js::gc::InitialHeap heap,
                                    js::HandleShape shape,
-                                   js::HandleTypeObject type,
-                                   js::HeapSlot *extantSlots = nullptr);
+                                   js::HandleTypeObject type);
 
     /* Make an array object with the specified initial state. */
     static inline js::ArrayObject *createArray(js::ExclusiveContext *cx,
                                                js::gc::AllocKind kind,
                                                js::gc::InitialHeap heap,
                                                js::HandleShape shape,
                                                js::HandleTypeObject type,
                                                uint32_t length);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -475,51 +475,40 @@ inline bool JSObject::isVarObj()
 {
     if (is<js::DebugScopeObject>())
         return as<js::DebugScopeObject>().scope().isVarObj();
     return lastProperty()->hasObjectFlag(js::BaseShape::VAROBJ);
 }
 
 /* static */ inline JSObject *
 JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
-                 js::HandleShape shape, js::HandleTypeObject type,
-                 js::HeapSlot *extantSlots /* = nullptr */)
+                 js::HandleShape shape, js::HandleTypeObject type)
 {
     /*
      * Callers must use dynamicSlotsCount to size the initial slot array of the
      * object. We can't check the allocated capacity of the dynamic slots, but
      * make sure their presence is consistent with the shape.
      */
     JS_ASSERT(shape && type);
     JS_ASSERT(type->clasp() == shape->getObjectClass());
     JS_ASSERT(type->clasp() != &js::ArrayObject::class_);
     JS_ASSERT(js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots());
     JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind));
     JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
-    JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(),
-                                                type->clasp()));
 
     const js::Class *clasp = type->clasp();
-    size_t nDynamicSlots = 0;
-    if (!extantSlots)
-        nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp);
+    size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp);
 
     JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap);
     if (!obj)
         return nullptr;
 
     obj->shape_.init(shape);
     obj->type_.init(type);
-    if (extantSlots) {
-#ifdef JSGC_GENERATIONAL
-        if (cx->isJSContext())
-            cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, extantSlots);
-#endif
-        obj->slots = extantSlots;
-    }
+    JS_ASSERT_IF(nDynamicSlots, obj->slots);
     obj->elements = js::emptyObjectElements;
 
     if (clasp->hasPrivate())
         obj->privateRef(shape->numFixedSlots()) = nullptr;
 
     size_t span = shape->slotSpan();
     if (span && clasp != &js::ArrayBufferObject::class_)
         obj->initializeSlotRange(0, span);
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -1382,16 +1382,22 @@ class ObjectImpl : public gc::BarrieredC
         return fixedSlots()[slot];
     }
 
     const Value &getFixedSlot(uint32_t slot) const {
         MOZ_ASSERT(slot < numFixedSlots());
         return fixedSlots()[slot];
     }
 
+    const Value &getDynamicSlot(uint32_t slot) const {
+        MOZ_ASSERT(slots);
+        MOZ_ASSERT(slot < numDynamicSlots());
+        return slots[slot];
+    }
+
     void setFixedSlot(uint32_t slot, const Value &value) {
         MOZ_ASSERT(slot < numFixedSlots());
         fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
     }
 
     void initFixedSlot(uint32_t slot, const Value &value) {
         MOZ_ASSERT(slot < numFixedSlots());
         fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -211,17 +211,16 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     gcAbortSweepAfterCurrentGroup(false),
     gcArenasAllocatedDuringSweep(nullptr),
 #ifdef DEBUG
     gcMarkingValidator(nullptr),
 #endif
     gcInterFrameGC(0),
     gcSliceBudget(SliceBudget::Unlimited),
     gcIncrementalEnabled(true),
-    gcGenerationalDisabled(0),
     gcManipulatingDeadZones(false),
     gcObjectsMarkedInDeadZones(0),
     gcPoke(false),
     heapState(Idle),
 #ifdef JSGC_GENERATIONAL
     gcNursery(thisFromCtor()),
     gcStoreBuffer(thisFromCtor(), gcNursery),
 #endif
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1112,21 +1112,16 @@ struct JSRuntime : public JS::shadow::Ru
 
     /*
      * We disable incremental GC if we encounter a js::Class with a trace hook
      * that does not implement write barriers.
      */
     bool                gcIncrementalEnabled;
 
     /*
-     * GGC can be enabled from the command line while testing.
-     */
-    unsigned            gcGenerationalDisabled;
-
-    /*
      * This is true if we are in the middle of a brain transplant (e.g.,
      * JS_TransplantObject) or some other operation that can manipulate
      * dead zones.
      */
     bool                gcManipulatingDeadZones;
 
     /*
      * This field is incremented each time we mark an object inside a
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -132,28 +132,28 @@ void
 ScopeObject::setEnclosingScope(HandleObject obj)
 {
     JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
                  obj->isDelegate());
     setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
 }
 
 /*
- * Construct a bare-bones call object given a shape, type, and slots pointer.
+ * Construct a bare-bones call object given a shape and type.
  * The call object must be further initialized to be usable.
  */
 CallObject *
-CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
+CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     JS_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
     gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap;
-    JSObject *obj = JSObject::create(cx, kind, heap, shape, type, slots);
+    JSObject *obj = JSObject::create(cx, kind, heap, shape, type);
     if (!obj)
         return nullptr;
 
     if (script->treatAsRunOnce()) {
         RootedObject nobj(cx, obj);
         if (!JSObject::setSingletonType(cx, nobj))
             return nullptr;
         return &nobj->as<CallObject>();
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -232,17 +232,17 @@ class CallObject : public ScopeObject
     static CallObject *
     create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee);
 
   public:
     static const Class class_;
 
     /* These functions are internal and are exposed only for JITs. */
     static CallObject *
-    create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+    create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type);
 
     static CallObject *
     createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap);
 
     static const uint32_t RESERVED_SLOTS = 2;
 
     static CallObject *createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee);