[INFER] Reapply 7db908db3669 (bug 684084) 19ed9da5789d (bug 684824) a250c3cb749a (bug 686178) 820f11a3fdb1 (bug 686179) e678ced82a6a (bug 686418) 300e1f974f55 (bug 669715) 5c131d458c53 (bug 686396) 3a8b5e4a286b (bug 683140).
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 18 Sep 2011 07:36:51 -0700
changeset 77146 c943bbf9dac4263560f51f303e146cf2a5710775
parent 77145 b961a248e94d41ee2b7995aaca34b4dd37dc4286
child 77147 648d084ca28ee49ffd502255b47fb421bed0aebc
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs684084, 684824, 686178, 686179, 686418, 669715, 686396, 683140
milestone9.0a1
[INFER] Reapply 7db908db3669 (bug 684084) 19ed9da5789d (bug 684824) a250c3cb749a (bug 686178) 820f11a3fdb1 (bug 686179) e678ced82a6a (bug 686418) 300e1f974f55 (bug 669715) 5c131d458c53 (bug 686396) 3a8b5e4a286b (bug 683140).
js/src/assembler/assembler/ARMAssembler.h
js/src/assembler/assembler/AssemblerBufferWithConstantPool.h
js/src/assembler/assembler/MacroAssemblerARM.h
js/src/jit-test/tests/basic/bug683140.js
js/src/jit-test/tests/basic/bug686396.js
js/src/jit-test/tests/jaeger/bug684084-2.js
js/src/jit-test/tests/jaeger/bug684824.js
js/src/jsarray.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/methodjit/BaseCompiler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/MachineRegs.h
js/src/methodjit/Retcon.cpp
--- a/js/src/assembler/assembler/ARMAssembler.h
+++ b/js/src/assembler/assembler/ARMAssembler.h
@@ -942,22 +942,20 @@ namespace JSC {
             m_buffer.ensureSpace(space);
         }
 
         int sizeOfConstantPool()
         {
             return m_buffer.sizeOfConstantPool();
         }
 
-#ifdef DEBUG
-        void allowPoolFlush(bool allowFlush)
+        int flushCount()
         {
-            m_buffer.allowPoolFlush(allowFlush);
+            return m_buffer.flushCount();
         }
-#endif
 
         JmpDst label()
         {
             JmpDst label(m_buffer.size());
             js::JaegerSpew(js::JSpew_Insns, IPFX "#label     ((%d))\n", MAYBE_PAD, label.m_offset);
             return label;
         }
 
--- a/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h
+++ b/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h
@@ -101,19 +101,17 @@ public:
         UnusedEntry
     };
 
     AssemblerBufferWithConstantPool()
         : AssemblerBuffer()
         , m_numConsts(0)
         , m_maxDistance(maxPoolSize)
         , m_lastConstDelta(0)
-#ifdef DEBUG
-        , m_allowFlush(true)
-#endif
+        , m_flushCount(0)
     {
         m_pool = static_cast<uint32_t*>(malloc(maxPoolSize));
         m_mask = static_cast<char*>(malloc(maxPoolSize / sizeof(uint32_t)));
     }
 
     ~AssemblerBufferWithConstantPool()
     {
         free(m_mask);
@@ -236,24 +234,20 @@ public:
         return m_pool;
     }
 
     int sizeOfConstantPool()
     {
         return m_numConsts;
     }
 
-#ifdef DEBUG
-    // Guard constant pool flushes to ensure that they don't occur during
-    // regions where offsets into the code have to be maintained (such as PICs).
-    void allowPoolFlush(bool allowFlush)
+    int flushCount()
     {
-        m_allowFlush = allowFlush;
+        return m_flushCount;
     }
-#endif
 
 private:
     void correctDeltas(int insnSize)
     {
         m_maxDistance -= insnSize;
         ASSERT(m_maxDistance >= 0);
         m_lastConstDelta -= insnSize;
         if (m_lastConstDelta < 0)
@@ -268,19 +262,19 @@ private:
         ASSERT(m_maxDistance >= 0);
         m_lastConstDelta = constSize;
     }
 
     void flushConstantPool(bool useBarrier = true)
     {
         js::JaegerSpew(js::JSpew_Insns, " -- FLUSHING CONSTANT POOL WITH %d CONSTANTS --\n",
                        m_numConsts);
-        ASSERT(m_allowFlush);
         if (m_numConsts == 0)
             return;
+        m_flushCount++;
         int alignPool = (AssemblerBuffer::size() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
 
         if (alignPool)
             alignPool = sizeof(uint64_t) - alignPool;
 
         // Callback to protect the constant pool from execution
         if (useBarrier)
             AssemblerBuffer::putInt(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool));
@@ -334,19 +328,16 @@ private:
 
     uint32_t* m_pool;
     char* m_mask;
     LoadOffsets m_loadOffsets;
 
     int m_numConsts;
     int m_maxDistance;
     int m_lastConstDelta;
-
-#ifdef DEBUG
-    bool    m_allowFlush;
-#endif
+    int m_flushCount;
 };
 
 } // namespace JSC
 
 #endif // ENABLE(ASSEMBLER)
 
 #endif // AssemblerBufferWithConstantPool_h
--- a/js/src/assembler/assembler/MacroAssemblerARM.h
+++ b/js/src/assembler/assembler/MacroAssemblerARM.h
@@ -1375,22 +1375,20 @@ public:
         m_assembler.ensureSpace(space);
     }
 
     void forceFlushConstantPool()
     {
         m_assembler.forceFlushConstantPool();
     }
 
-#ifdef DEBUG
-    void allowPoolFlush(bool allowFlush)
+    int flushCount()
     {
-        m_assembler.allowPoolFlush(allowFlush);
+        return m_assembler.flushCount();
     }
-#endif
 
 protected:
     ARMAssembler::Condition ARMCondition(Condition cond)
     {
         return static_cast<ARMAssembler::Condition>(cond);
     }
 
     void ensureSpace(int insnSpace, int constSpace)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug683140.js
@@ -0,0 +1,13 @@
+
+var g = newGlobal("same-compartment");
+g.eval("this.f = function(a) {" +
+       "assertEq(a instanceof Array, false);" +
+       "a = Array.prototype.slice.call(a);" +
+       "assertEq(a instanceof Array, true); }");
+g.f([1, 2, 3]);
+
+var g2 = newGlobal("new-compartment");
+g2.a = g2.Array(10);
+assertEq(g2.a instanceof Array, false);
+g2.a = Array.prototype.slice(g2.a);
+assertEq(g2.a instanceof Array, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug686396.js
@@ -0,0 +1,16 @@
+
+(function () { 
+  assertEquals = function assertEquals(expected, found, name_opt) {  };
+})();
+function testOne(receiver, key, result) {
+  for(var i = 0; i != 10; i++ ) {
+    assertEquals(result, receiver[key]());
+  }
+}
+function TypeOfThis() { return typeof this; }
+Number.prototype.type = TypeOfThis;
+String.prototype.type = TypeOfThis;
+Boolean.prototype.type = TypeOfThis;
+testOne(2.3, 'type', 'object');
+testOne('x', 'type', 'object');
+testOne(true, 'type', 'object');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug684084-2.js
@@ -0,0 +1,8 @@
+function Function() {
+    try {
+    var g = this;
+    g.c("evil", eval);
+    } catch(b) {}
+}
+var o0 = Function.prototype;
+var f = new Function( (null ) );
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug684824.js
@@ -0,0 +1,7 @@
+
+function X(n) {
+    while ('' + (n--)) {
+        break;
+    }
+}
+X();
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2610,44 +2610,46 @@ array_unshift(JSContext *cx, uintN argc,
     if (!js_SetLengthProperty(cx, obj, newlen))
         return JS_FALSE;
 
     /* Follow Perl by returning the new array length. */
     vp->setNumber(newlen);
     return JS_TRUE;
 }
 
+static inline void
+TryReuseArrayType(JSObject *obj, JSObject *nobj)
+{
+    /*
+     * Try to change the type of a newly created array nobj to the same type
+     * as obj. This can only be performed if the original object is an array
+     * and has the same prototype.
+     */
+    JS_ASSERT(nobj->isDenseArray());
+    JS_ASSERT(nobj->type() == nobj->getProto()->newType);
+
+    if (obj->isArray() && !obj->hasSingletonType() && obj->getProto() == nobj->getProto())
+        nobj->setType(obj->type());
+}
+
 static JSBool
 array_splice(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ToObject(cx, &vp[1]);
     if (!obj)
         return false;
 
     jsuint length, begin, end, count, delta, last;
     JSBool hole;
 
-    /*
-     * Get the type of the result object: the original type when splicing an
-     * array, a generic array type otherwise.
-     */
-    TypeObject *type;
-    if (obj->isArray() && !obj->hasSingletonType()) {
-        type = obj->type();
-    } else {
-        type = GetTypeNewObject(cx, JSProto_Array);
-        if (!type)
-            return false;
-    }
-
     /* Create a new array value to return. */
     JSObject *obj2 = NewDenseEmptyArray(cx);
     if (!obj2)
         return JS_FALSE;
-    obj2->setType(type);
+    TryReuseArrayType(obj, obj2);
     vp->setObject(*obj2);
 
     /* Nothing to do if no args.  Otherwise get length. */
     if (argc == 0)
         return JS_TRUE;
     Value *argv = JS_ARGV(cx, vp);
     if (!js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
@@ -2796,18 +2798,17 @@ array_concat(JSContext *cx, uintN argc, 
     jsuint length;
     if (aobj->isDenseArray()) {
         length = aobj->getArrayLength();
         const Value *vector = aobj->getDenseArrayElements();
         jsuint initlen = aobj->getDenseArrayInitializedLength();
         nobj = NewDenseCopiedArray(cx, initlen, vector);
         if (!nobj)
             return JS_FALSE;
-        if (nobj->getProto() == aobj->getProto() && !aobj->hasSingletonType())
-            nobj->setType(aobj->type());
+        TryReuseArrayType(aobj, nobj);
         nobj->setArrayLength(cx, length);
         if (!aobj->isPackedDenseArray())
             nobj->markDenseArrayNotPacked(cx);
         vp->setObject(*nobj);
         if (argc == 0)
             return JS_TRUE;
         argc--;
         p++;
@@ -2900,43 +2901,33 @@ array_slice(JSContext *cx, uintN argc, V
             }
             end = (jsuint)d;
         }
     }
 
     if (begin > end)
         begin = end;
 
-    /* Get the type object for the returned array, as for array_splice. */
-    TypeObject *type;
-    if (obj->isArray() && !obj->hasSingletonType()) {
-        type = obj->type();
-    } else {
-        type = GetTypeNewObject(cx, JSProto_Array);
-        if (!type)
-            return false;
-    }
-
     if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
         nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin);
         if (!nobj)
             return JS_FALSE;
-        nobj->setType(type);
+        TryReuseArrayType(obj, nobj);
         if (!obj->isPackedDenseArray())
             nobj->markDenseArrayNotPacked(cx);
         vp->setObject(*nobj);
         return JS_TRUE;
     }
 
     /* Create a new Array object and root it using *vp. */
     nobj = NewDenseAllocatedArray(cx, end - begin);
     if (!nobj)
         return JS_FALSE;
-    nobj->setType(type);
+    TryReuseArrayType(obj, nobj);
     vp->setObject(*nobj);
 
     AutoValueRooter tvr(cx);
     for (slot = begin; slot < end; slot++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetElement(cx, obj, slot, &hole, tvr.addr())) {
             return JS_FALSE;
         }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -686,34 +686,35 @@ TypeSet::addTransformThis(JSContext *cx,
  * discovered scripted functions.
  */
 class TypeConstraintPropagateThis : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *callpc;
     Type type;
-
-    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type)
-        : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type)
+    TypeSet *types;
+
+    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, TypeSet *types)
+        : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type), types(types)
     {}
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
+TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type, TypeSet *types)
 {
     /* Don't add constraints when the call will be 'new' (see addCallProperty). */
     jsbytecode *callpc = script->analysis()->getCallPC(pc);
     UntrapOpcode untrap(cx, script, callpc);
     if (JSOp(*callpc) == JSOP_NEW)
         return;
 
-    add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type));
+    add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, callpc, type, types));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
     TypeSet::FilterKind filter;
@@ -1058,20 +1059,20 @@ TypeConstraintProp::newType(JSContext *c
 }
 
 void
 TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type)
 {
     UntrapOpcode untrap(cx, script, callpc);
 
     /*
-     * For CALLPROP and CALLELEM, we need to update not just the pushed types
-     * but also the 'this' types of possible callees. If we can't figure out
-     * that set of callees, monitor the call to make sure discovered callees
-     * get their 'this' types updated.
+     * For CALLPROP, we need to update not just the pushed types but also the
+     * 'this' types of possible callees. If we can't figure out that set of
+     * callees, monitor the call to make sure discovered callees get their
+     * 'this' types updated.
      */
 
     if (UnknownPropertyAccess(script, type)) {
         cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         return;
     }
 
     TypeObject *object = GetPropertyObject(cx, script, type);
@@ -1081,17 +1082,18 @@ TypeConstraintCallProp::newType(JSContex
         } else {
             TypeSet *types = object->getProperty(cx, id, false);
             if (!types)
                 return;
             if (!types->hasPropagatedProperty())
                 object->getFromPrototypes(cx, id, types);
             /* Bypass addPropagateThis, we already have the callpc. */
             types->add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool,
-                                                                 script, callpc, type));
+                                                                 script, callpc, type,
+                                                                 (TypeSet *) NULL));
         }
     }
 }
 
 void
 TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type)
 {
     if (type.isUnknown() ||
@@ -1225,18 +1227,18 @@ TypeConstraintCall::newType(JSContext *c
 
 void
 TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type)
 {
     if (type.isUnknown() || type.isAnyObject()) {
         /*
          * The callee is unknown, make sure the call is monitored so we pick up
          * possible this/callee correlations. This only comes into play for
-         * CALLPROP and CALLELEM, for other calls we are past the type barrier
-         * already and a TypeConstraintCall will also monitor the call.
+         * CALLPROP, for other calls we are past the type barrier and a
+         * TypeConstraintCall will also monitor the call.
          */
         cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         return;
     }
 
     /* Ignore calls to natives, these will be handled by TypeConstraintCall. */
     JSFunction *callee = NULL;
 
@@ -1253,17 +1255,21 @@ TypeConstraintPropagateThis::newType(JSC
     } else {
         /* Ignore calls to primitives, these will go through a stub. */
         return;
     }
 
     if (!callee->script()->ensureHasTypes(cx, callee))
         return;
 
-    TypeScript::ThisTypes(callee->script())->addType(cx, this->type);
+    TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
+    if (this->types)
+        this->types->addSubset(cx, thisTypes);
+    else
+        thisTypes->addType(cx, this->type);
 }
 
 void
 TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
 {
     /*
      * We only model a subset of the arithmetic behavior that is actually
      * possible. The following need to be watched for at runtime:
@@ -3677,22 +3683,22 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
        * which is accessing a non-integer property must be monitored.
        */
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM: {
         TypeSet *seen = script->analysis()->bytecodeTypes(pc);
 
         poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
-        if (op == JSOP_CALLELEM)
-            poppedTypes(pc, 1)->addCallProperty(cx, script, pc, JSID_VOID);
 
         seen->addSubset(cx, &pushed[0]);
-        if (op == JSOP_CALLELEM)
+        if (op == JSOP_CALLELEM) {
             poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID);
+            pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), &pushed[1]);
+        }
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_SETELEM:
         poppedTypes(pc, 1)->addSetElement(cx, script, pc, poppedTypes(pc, 2), poppedTypes(pc, 0));
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -447,17 +447,18 @@ class TypeSet
     void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
     void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
                        TypeSet *objectTypes, TypeSet *valueTypes);
     void addCall(JSContext *cx, TypeCallsite *site);
     void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL);
     void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
-    void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type);
+    void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
+                          Type type, TypeSet *types = NULL);
     void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter);
     void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
     void addLazyArguments(JSContext *cx, TypeSet *target);
 
     /*
      * Make an type set with the specified debugging name, not embedded in
      * another structure.
      */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5683,16 +5683,18 @@ js_NativeGetInline(JSContext *cx, JSObje
     int32 sample;
 
     JS_ASSERT(pobj->isNative());
 
     slot = shape->slot;
     if (slot != SHAPE_INVALID_SLOT) {
         *vp = pobj->nativeGetSlot(slot);
         JS_ASSERT(!vp->isMagic());
+        JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetterOrIsMethod(),
+                     js::types::TypeHasProperty(cx, pobj->type(), shape->propid, *vp));
     } else {
         vp->setUndefined();
     }
     if (shape->hasDefaultGetter())
         return true;
 
     if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
         JS_ASSERT(shape->methodObject() == vp->toObject());
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -139,18 +139,16 @@ JSObject::getProperty(JSContext *cx, JSO
 {
     js::PropertyIdOp op = getOps()->getProperty;
     if (op) {
         if (!op(cx, this, receiver, id, vp))
             return false;
     } else {
         if (!js_GetProperty(cx, this, receiver, id, vp))
             return false;
-        JS_ASSERT_IF(!hasSingletonType() && nativeContains(cx, js_CheckForStringIndex(id)),
-                     js::types::TypeHasProperty(cx, type(), id, *vp));
     }
     return true;
 }
 
 inline JSBool
 JSObject::getProperty(JSContext *cx, jsid id, js::Value *vp)
 {
     return getProperty(cx, this, id, vp);
--- a/js/src/methodjit/BaseCompiler.h
+++ b/js/src/methodjit/BaseCompiler.h
@@ -222,77 +222,53 @@ NativeStubEpilogue(VMFrame &f, Assembler
  * up front to prevent this from happening.
  */
 #ifdef JS_CPU_ARM
 template <size_t reservedSpace>
 class AutoReserveICSpace {
     typedef Assembler::Label Label;
 
     Assembler           &masm;
-#ifdef DEBUG
-    Label               startLabel;
     bool                didCheck;
-#endif
+    bool                *overflowSpace;
+    int                 flushCount;
 
   public:
-    AutoReserveICSpace(Assembler &masm) : masm(masm) {
+    AutoReserveICSpace(Assembler &masm, bool *overflowSpace)
+        : masm(masm), didCheck(false), overflowSpace(overflowSpace)
+    {
         masm.ensureSpace(reservedSpace);
-#ifdef DEBUG
-        didCheck = false;
-
-        startLabel = masm.label();
-
-        /* Assert that the constant pool is not flushed until we reach a safe point. */
-        masm.allowPoolFlush(false);
-
-        JaegerSpew(JSpew_Insns, " -- BEGIN CONSTANT-POOL-FREE REGION -- \n");
-#endif
+        flushCount = masm.flushCount();
     }
 
     /* Allow manual IC space checks so that non-patchable code at the end of an IC section can be
      * free to use constant pools. */
     void check() {
-#ifdef DEBUG
         JS_ASSERT(!didCheck);
         didCheck = true;
 
-        Label endLabel = masm.label();
-        int spaceUsed = masm.differenceBetween(startLabel, endLabel);
-
-        /* Spew the space used, to help tuning of reservedSpace. */
-        JaegerSpew(JSpew_Insns,
-                   " -- END CONSTANT-POOL-FREE REGION: %u bytes used of %u reserved. -- \n",
-                   spaceUsed, reservedSpace);
-
-        /* Assert that we didn't emit more code than we protected. */
-        JS_ASSERT(spaceUsed >= 0);
-        JS_ASSERT(size_t(spaceUsed) <= reservedSpace);
-
-        /* Allow the pool to be flushed. */
-        masm.allowPoolFlush(true);
-#endif
+        if (masm.flushCount() != flushCount)
+            *overflowSpace = true;
     }
 
     ~AutoReserveICSpace() {
-#ifdef DEBUG
         /* Automatically check the IC space if we didn't already do it manually. */
         if (!didCheck) {
             check();
         }
-#endif
     }
 };
 
-# define RESERVE_IC_SPACE(__masm)       AutoReserveICSpace<256> arics(__masm)
+# define RESERVE_IC_SPACE(__masm)       AutoReserveICSpace<256> arics(__masm, &this->overflowICSpace)
 # define CHECK_IC_SPACE()               arics.check()
 
 /* The OOL path can need a lot of space because we save and restore a lot of registers. The actual
  * sequene varies. However, dumping the literal pool before an OOL block is probably a good idea
  * anyway, as we branch directly to the start of the block from the fast path. */
-# define RESERVE_OOL_SPACE(__masm)      AutoReserveICSpace<2048> arics_ool(__masm)
+# define RESERVE_OOL_SPACE(__masm)      AutoReserveICSpace<2048> arics_ool(__masm, &this->overflowICSpace)
 
 /* Allow the OOL patch to be checked before object destruction. Often, non-patchable epilogues or
  * rejoining sequences are emitted, and it isn't necessary to protect these from literal pools. */
 # define CHECK_OOL_SPACE()              arics_ool.check()
 #else
 # define RESERVE_IC_SPACE(__masm)       /* Do nothing. */
 # define CHECK_IC_SPACE()               /* Do nothing. */
 # define RESERVE_OOL_SPACE(__masm)      /* Do nothing. */
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -128,16 +128,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
 #if defined JS_TRACER
     addTraceHints(cx->traceJitEnabled),
 #else
     addTraceHints(false),
 #endif
     inlining_(false),
     hasGlobalReallocation(false),
     oomInVector(false),
+    overflowICSpace(false),
     gcNumber(cx->runtime->gcNumber),
     applyTricks(NoApplyTricks),
     pcLengths(NULL)
 {
     /* :FIXME: bug 637856 disabling traceJit if inference is enabled */
     if (cx->typeInferenceEnabled())
         addTraceHints = false;
 
@@ -889,16 +890,21 @@ mjit::Compiler::finishThisUp(JITScript *
 
     /*
      * Watch for GCs which occurred during compilation. These may have
      * renumbered shapes baked into the jitcode.
      */
     if (cx->runtime->gcNumber != gcNumber)
         return Compile_Retry;
 
+    if (overflowICSpace) {
+        JaegerSpew(JSpew_Scripts, "dumped a constant pool while generating an IC\n");
+        return Compile_Abort;
+    }
+
     for (size_t i = 0; i < branchPatches.length(); i++) {
         Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex);
         branchPatches[i].jump.linkTo(label, &masm);
     }
 
 #ifdef JS_CPU_ARM
     masm.forceFlushConstantPool();
     stubcc.masm.forceFlushConstantPool();
@@ -6892,49 +6898,64 @@ mjit::Compiler::leaveBlock()
 //
 bool
 mjit::Compiler::constructThis()
 {
     JS_ASSERT(isConstructing);
 
     JSFunction *fun = script->function();
 
-    if (cx->typeInferenceEnabled() && !fun->getType(cx)->unknownProperties()) {
+    do {
+        if (!cx->typeInferenceEnabled() || fun->getType(cx)->unknownProperties())
+            break;
+
         jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
         types::TypeSet *protoTypes = fun->getType(cx)->getProperty(cx, id, false);
 
         JSObject *proto = protoTypes->getSingleton(cx, true);
-        if (proto) {
-            JSObject *templateObject = js_CreateThisForFunctionWithProto(cx, fun, proto);
-            if (!templateObject)
-                return false;
-
-            /*
-             * The template incorporates a shape and/or fixed slots from any
-             * newScript on its type, so make sure recompilation is triggered
-             * should this information change later.
-             */
-            if (templateObject->type()->newScript)
-                types::TypeSet::WatchObjectStateChange(cx, templateObject->type());
-
-            RegisterID result = frame.allocReg();
-            Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
-
-            stubcc.linkExit(emptyFreeList, Uses(0));
-            stubcc.leave();
-
-            stubcc.masm.move(ImmPtr(proto), Registers::ArgReg1);
-            OOL_STUBCALL(stubs::CreateThis, REJOIN_RESUME);
-
-            frame.setThis(result);
-
-            stubcc.rejoin(Changes(1));
-            return true;
-        }
-    }
+        if (!proto)
+            break;
+
+        /*
+         * Generate an inline path to create a 'this' object with the given
+         * prototype. Only do this if the type is actually known as a possible
+         * 'this' type of the script.
+         */
+        types::TypeObject *type = proto->getNewType(cx, fun);
+        if (!type)
+            return false;
+        if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::ObjectType(type)))
+            break;
+
+        JSObject *templateObject = js_CreateThisForFunctionWithProto(cx, fun, proto);
+        if (!templateObject)
+            return false;
+
+        /*
+         * The template incorporates a shape and/or fixed slots from any
+         * newScript on its type, so make sure recompilation is triggered
+         * should this information change later.
+         */
+        if (templateObject->type()->newScript)
+            types::TypeSet::WatchObjectStateChange(cx, templateObject->type());
+
+        RegisterID result = frame.allocReg();
+        Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
+
+        stubcc.linkExit(emptyFreeList, Uses(0));
+        stubcc.leave();
+
+        stubcc.masm.move(ImmPtr(proto), Registers::ArgReg1);
+        OOL_STUBCALL(stubs::CreateThis, REJOIN_RESUME);
+
+        frame.setThis(result);
+
+        stubcc.rejoin(Changes(1));
+        return true;
+    } while (false);
 
     // Load the callee.
     frame.pushCallee();
 
     // Get callee.prototype.
     if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, JSVAL_TYPE_UNKNOWN, false, false))
         return false;
 
@@ -7211,17 +7232,22 @@ mjit::Compiler::updateJoinVarTypes()
 
     /* Update variable types for all new values at this bytecode. */
     const SlotValue *newv = analysis->newValues(PC);
     if (newv) {
         while (newv->slot) {
             if (newv->slot < TotalSlots(script)) {
                 VarType &vt = a->varTypes[newv->slot];
                 vt.types = analysis->getValueTypes(newv->value);
-                vt.type = vt.types->getKnownTypeTag(cx);
+                JSValueType newType = vt.types->getKnownTypeTag(cx);
+                if (newType != vt.type) {
+                    FrameEntry *fe = frame.getSlotEntry(newv->slot);
+                    frame.forgetLoopReg(fe);
+                }
+                vt.type = newType;
             }
             newv++;
         }
     }
 }
 
 void
 mjit::Compiler::restoreVarType()
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -467,16 +467,17 @@ class Compiler : public BaseCompiler
     Label argsCheckFallthrough;
     Jump argsCheckJump;
 #endif
     bool debugMode_;
     bool addTraceHints;
     bool inlining_;
     bool hasGlobalReallocation;
     bool oomInVector;       // True if we have OOM'd appending to a vector. 
+    bool overflowICSpace;   // True if we added a constant pool in a reserved space.
     uint32 gcNumber;
     enum { NoApplyTricks, LazyArgsObj } applyTricks;
     PCLengthEntry *pcLengths;
 
     Compiler *thisFromCtor() { return this; }
 
     friend class CompilerAllocPolicy;
   public:
--- a/js/src/methodjit/MachineRegs.h
+++ b/js/src/methodjit/MachineRegs.h
@@ -125,21 +125,23 @@ struct Registers {
 
 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
     static const RegisterID ReturnReg = JSC::X86Registers::eax;
 # if defined(JS_CPU_X86) || defined(_WIN64)
     static const RegisterID ArgReg0 = JSC::X86Registers::ecx;
     static const RegisterID ArgReg1 = JSC::X86Registers::edx;
 #  if defined(JS_CPU_X64)
     static const RegisterID ArgReg2 = JSC::X86Registers::r8;
+    static const RegisterID ArgReg3 = JSC::X86Registers::r9;
 #  endif
 # else
     static const RegisterID ArgReg0 = JSC::X86Registers::edi;
     static const RegisterID ArgReg1 = JSC::X86Registers::esi;
     static const RegisterID ArgReg2 = JSC::X86Registers::edx;
+    static const RegisterID ArgReg3 = JSC::X86Registers::ecx;
 # endif
 #elif JS_CPU_ARM
     static const RegisterID ReturnReg = JSC::ARMRegisters::r0;
     static const RegisterID ArgReg0 = JSC::ARMRegisters::r0;
     static const RegisterID ArgReg1 = JSC::ARMRegisters::r1;
     static const RegisterID ArgReg2 = JSC::ARMRegisters::r2;
 #elif JS_CPU_SPARC
     static const RegisterID ReturnReg = JSC::SparcRegisters::o0;
@@ -422,17 +424,17 @@ struct Registers {
 
     /* Get a register which is not live before a normal ABI call with at most four args. */
     static inline Registers tempCallRegMask() {
         Registers regs(AvailRegs);
 #ifndef JS_CPU_X86
         regs.takeReg(ArgReg0);
         regs.takeReg(ArgReg1);
         regs.takeReg(ArgReg2);
-#ifdef JS_CPU_SPARC
+#if defined(JS_CPU_SPARC) || defined(JS_CPU_X64)
         regs.takeReg(ArgReg3);
 #endif
 #endif
         return regs;
     }
 
     Registers(uint32 freeMask)
       : freeMask(freeMask)
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -80,17 +80,16 @@ AutoScriptRetrapper::untrap(jsbytecode *
 
 static inline JSRejoinState ScriptedRejoin(uint32 pcOffset)
 {
     return REJOIN_SCRIPTED | (pcOffset << 1);
 }
 
 static inline JSRejoinState StubRejoin(RejoinState rejoin)
 {
-    JS_ASSERT(rejoin != REJOIN_NONE);
     return rejoin << 1;
 }
 
 static inline void
 SetRejoinState(StackFrame *fp, const CallSite &site, void **location)
 {
     if (site.rejoin == REJOIN_SCRIPTED) {
         fp->setRejoin(ScriptedRejoin(site.pcOffset));