[INFER] Check result type of property access stubcalls in jitcode, bug 686000.
authorBrian Hackett <bhackett1024@gmail.com>
Sat, 10 Sep 2011 09:58:41 +0200
changeset 77063 f1c585415dd4532e6d64b8ac8323d935e92b8553
parent 77062 cc2daf6cbaab3bef234511c8dedbb81c6bc21eff
child 77064 323595f354b119ccc91ea41ac7ab9ca25fc7c991
push id21171
push userbhackett@mozilla.com
push dateSat, 17 Sep 2011 03:32:16 +0000
treeherdermozilla-central@acc41ac331ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs686000
milestone9.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
[INFER] Check result type of property access stubcalls in jitcode, bug 686000.
js/src/jit-test/tests/jaeger/getelem-sanity-8.js
js/src/jsanalyze.h
js/src/jsinterp.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PolyIC.h
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/getelem-sanity-8.js
@@ -0,0 +1,13 @@
+
+// TI does not account for GETELEM accessing strings, so the GETELEM PIC must
+// update type constraints according to generated stubs.
+function foo(a, b) {
+  for (var j = 0; j < 5; j++)
+    a[b[j]] + " what";
+}
+var a = {a:"zero", b:"one", c:"two", d:"three", e:"four"};
+var b = ["a", "b", "c", "d", "e"];
+foo(a, b);
+foo(a, b);
+a.e = 4;
+foo(a, b);
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -140,16 +140,17 @@ class Bytecode
     /* Call whose result should be monitored. */
     bool monitoredTypesReturn : 1;
 
     /*
      * Dynamically observed state about the execution of this opcode. These are
      * hints about the script for use during compilation.
      */
     bool arrayWriteHole: 1;  /* SETELEM which has written to an array hole. */
+    bool getStringElement:1; /* GETELEM which has accessed string properties. */
     bool accessGetter: 1;    /* Property read on a shape with a getter hook. */
 
     /* Stack depth before this opcode. */
     uint32 stackDepth;
 
   private:
     /*
      * The set of locals defined at this point. This does not include locals which
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3916,23 +3916,23 @@ BEGIN_CASE(JSOP_GETELEM)
             id = INT_TO_JSID(i);
         } else {
           intern_big_int:
             if (!js_InternNonIntElementId(cx, obj, rref, &id))
                 goto error;
         }
     }
 
+    if (JSID_IS_STRING(id) && script->hasAnalysis())
+        script->analysis()->getCode(regs.pc).getStringElement = true;
+
     if (!obj->getProperty(cx, id, &rval))
         goto error;
     copyFrom = &rval;
 
-    if (!JSID_IS_INT(id))
-        TypeScript::MonitorUnknown(cx, script, regs.pc);
-
   end_getelem:
     regs.sp--;
     regs.sp[-1] = *copyFrom;
     assertSameCompartment(cx, regs.sp[-1]);
     TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
 }
 END_CASE(JSOP_GETELEM)
 
@@ -3960,18 +3960,16 @@ BEGIN_CASE(JSOP_CALLELEM)
         if (!OnUnknownMethod(cx, regs.sp - 2))
             goto error;
     } else
 #endif
     {
         regs.sp[-1] = thisv;
     }
 
-    if (!JSID_IS_INT(id))
-        TypeScript::MonitorUnknown(cx, script, regs.pc);
     TypeScript::Monitor(cx, script, regs.pc, regs.sp[-2]);
 }
 END_CASE(JSOP_CALLELEM)
 
 BEGIN_CASE(JSOP_SETELEM)
 {
     JSObject *obj;
     FETCH_OBJECT(cx, -3, obj);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -4185,31 +4185,33 @@ void
 mjit::Compiler::jsop_getprop_slow(JSAtom *atom, bool usePropCache)
 {
     /* See ::jsop_getprop */
     RejoinState rejoin = usePropCache ? REJOIN_GETTER : REJOIN_THIS_PROTOTYPE;
 
     prepareStubCall(Uses(1));
     if (usePropCache) {
         INLINE_STUBCALL(stubs::GetProp, rejoin);
+        testPushedType(rejoin, -1, /* ool = */ false);
     } else {
         masm.move(ImmPtr(atom), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::GetPropNoCache, rejoin);
     }
 
     frame.pop();
     frame.pushSynced(JSVAL_TYPE_UNKNOWN);
 }
 
 bool
 mjit::Compiler::jsop_callprop_slow(JSAtom *atom)
 {
     prepareStubCall(Uses(1));
     masm.move(ImmPtr(atom), Registers::ArgReg1);
     INLINE_STUBCALL(stubs::CallProp, REJOIN_FALLTHROUGH);
+    testPushedType(REJOIN_FALLTHROUGH, -1, /* ool = */ false);
     frame.pop();
     pushSyncedEntry(0);
     pushSyncedEntry(1);
     return true;
 }
 
 #ifdef JS_MONOIC
 void
@@ -4295,16 +4297,18 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
          */
         if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) {
             bool isObject = top->isTypeKnown();
             if (!isObject) {
                 Jump notObject = frame.testObject(Assembler::NotEqual, top);
                 stubcc.linkExit(notObject, Uses(1));
                 stubcc.leave();
                 OOL_STUBCALL(stubs::GetProp, rejoin);
+                if (rejoin == REJOIN_GETTER)
+                    testPushedType(rejoin, -1);
             }
             RegisterID reg = frame.tempRegForData(top);
             frame.pop();
             frame.pushWord(Address(reg, offsetof(JSObject, privateData)), JSVAL_TYPE_INT32);
             if (!isObject)
                 stubcc.rejoin(Changes(1));
             return true;
         }
@@ -4315,16 +4319,18 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
          */
         if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) {
             bool isObject = top->isTypeKnown();
             if (!isObject) {
                 Jump notObject = frame.testObject(Assembler::NotEqual, top);
                 stubcc.linkExit(notObject, Uses(1));
                 stubcc.leave();
                 OOL_STUBCALL(stubs::GetProp, rejoin);
+                if (rejoin == REJOIN_GETTER)
+                    testPushedType(rejoin, -1);
             }
             RegisterID reg = frame.copyDataIntoReg(top);
             frame.pop();
             frame.pushWord(Address(reg, TypedArray::lengthOffset()), JSVAL_TYPE_INT32);
             frame.freeReg(reg);
             if (!isObject)
                 stubcc.rejoin(Changes(1));
             return true;
@@ -4377,16 +4383,18 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
             types->addFreeze(cx);
             uint32 slot = propertyTypes->definiteSlot();
             bool isObject = top->isTypeKnown();
             if (!isObject) {
                 Jump notObject = frame.testObject(Assembler::NotEqual, top);
                 stubcc.linkExit(notObject, Uses(1));
                 stubcc.leave();
                 OOL_STUBCALL(stubs::GetProp, rejoin);
+                if (rejoin == REJOIN_GETTER)
+                    testPushedType(rejoin, -1);
             }
             RegisterID reg = frame.tempRegForData(top);
             frame.pop();
 
             Address address(reg, JSObject::getFixedSlotOffset(slot));
             BarrierState barrier = pushAddressMaybeBarrier(address, knownType, false);
             if (!isObject)
                 stubcc.rejoin(Changes(1));
@@ -4440,17 +4448,18 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
     /*
      * If this access has been on a shape with a getter hook, make preparations
      * so that we can generate a stub to call the hook directly (rather than be
      * forced to make a stub call). Sync the stack up front and kill all
      * registers so that PIC stubs can contain calls, and always generate a
      * type barrier if inference is enabled (known property types do not
      * reflect properties with getter hooks).
      */
-    pic.canCallHook = usePropCache && JSOp(*PC) == JSOP_GETPROP && analysis->getCode(PC).accessGetter;
+    pic.canCallHook = pic.forcedTypeBarrier =
+        usePropCache && JSOp(*PC) == JSOP_GETPROP && analysis->getCode(PC).accessGetter;
     if (pic.canCallHook)
         frame.syncAndKillEverything();
 
     pic.shapeReg = shapeReg;
     pic.atom = atom;
 
     /* Guard on shape. */
     masm.loadShape(objReg, shapeReg);
@@ -4464,16 +4473,18 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
 
     RESERVE_OOL_SPACE(stubcc.masm);
     pic.slowPathStart = stubcc.linkExit(j, Uses(1));
 
     stubcc.leave();
     passICAddress(&pic);
     pic.slowPathCall = OOL_STUBCALL(usePropCache ? ic::GetProp : ic::GetPropNoCache, rejoin);
     CHECK_OOL_SPACE();
+    if (rejoin == REJOIN_GETTER)
+        testPushedType(rejoin, -1);
 
     /* Load the base slot address. */
     Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)),
                                                                objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
     frame.pop();
@@ -4547,17 +4558,17 @@ mjit::Compiler::jsop_callprop_generic(JS
     RETURN_IF_OOM(false);
 
     pic.typeCheck = stubcc.linkExit(typeCheckJump, Uses(1));
     pic.hasTypeCheck = true;
     pic.objReg = objReg;
     pic.shapeReg = shapeReg;
     pic.atom = atom;
 
-    pic.canCallHook = analysis->getCode(PC).accessGetter;
+    pic.canCallHook = pic.forcedTypeBarrier = analysis->getCode(PC).accessGetter;
     if (pic.canCallHook)
         frame.syncAndKillEverything();
 
     /*
      * Store the type and object back. Don't bother keeping them in registers,
      * since a sync will be needed for the upcoming call.
      */
     uint32 thisvSlot = frame.totalDepth();
@@ -4585,16 +4596,18 @@ mjit::Compiler::jsop_callprop_generic(JS
     /* Slow path. */
     RESERVE_OOL_SPACE(stubcc.masm);
     pic.slowPathStart = stubcc.linkExit(j, Uses(1));
     stubcc.leave();
     passICAddress(&pic);
     pic.slowPathCall = OOL_STUBCALL(ic::CallProp, REJOIN_FALLTHROUGH);
     CHECK_OOL_SPACE();
 
+    testPushedType(REJOIN_FALLTHROUGH, -1);
+
     /* Load the base slot address. */
     Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)),
                                                                objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
 
     Label fastValueLoad = masm.loadValueWithAddressOffsetPatch(slot, shapeReg, objReg);
@@ -4711,17 +4724,17 @@ mjit::Compiler::jsop_callprop_obj(JSAtom
     RegisterID objReg;
     if (top->isConstant()) {
         objReg = frame.allocReg();
         masm.move(ImmPtr(&top->getValue().toObject()), objReg);
     } else {
         objReg = frame.copyDataIntoReg(top);
     }
 
-    pic.canCallHook = analysis->getCode(PC).accessGetter;
+    pic.canCallHook = pic.forcedTypeBarrier = analysis->getCode(PC).accessGetter;
     if (pic.canCallHook)
         frame.syncAndKillEverything();
 
     /* Guard on shape. */
     masm.loadShape(objReg, shapeReg);
     pic.shapeGuard = masm.label();
 
     DataLabel32 inlineShapeLabel;
@@ -4733,16 +4746,18 @@ mjit::Compiler::jsop_callprop_obj(JSAtom
     /* Slow path. */
     RESERVE_OOL_SPACE(stubcc.masm);
     pic.slowPathStart = stubcc.linkExit(j, Uses(1));
     stubcc.leave();
     passICAddress(&pic);
     pic.slowPathCall = OOL_STUBCALL(ic::CallProp, REJOIN_FALLTHROUGH);
     CHECK_OOL_SPACE();
 
+    testPushedType(REJOIN_FALLTHROUGH, -1);
+
     /* Load the base slot address. */
     Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)),
                                                                objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
 
     Label fastValueLoad = masm.loadValueWithAddressOffsetPatch(slot, shapeReg, objReg);
@@ -5024,16 +5039,17 @@ mjit::Compiler::jsop_callprop_dispatch(J
     }
 
     for (unsigned i = 0; i < rejoins.length(); i++)
         rejoins[i].linkTo(masm.label(), &masm);
 
     stubcc.leave();
     stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1);
     OOL_STUBCALL(stubs::CallProp, REJOIN_FALLTHROUGH);
+    testPushedType(REJOIN_FALLTHROUGH, -1);
 
     frame.dup();
     // THIS THIS
 
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, pushreg);
     // THIS THIS FUN
 
     frame.shift(-2);
@@ -5054,16 +5070,17 @@ mjit::Compiler::jsop_callprop(JSAtom *at
     if (singleton && singleton->isFunction() && !hasTypeBarriers(PC) &&
         testSingletonPropertyTypes(top, ATOM_TO_JSID(atom), &testObject)) {
         if (testObject) {
             Jump notObject = frame.testObject(Assembler::NotEqual, top);
             stubcc.linkExit(notObject, Uses(1));
             stubcc.leave();
             stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1);
             OOL_STUBCALL(stubs::CallProp, REJOIN_FALLTHROUGH);
+            testPushedType(REJOIN_FALLTHROUGH, -1);
         }
 
         // THIS
 
         frame.dup();
         // THIS THIS
 
         frame.push(ObjectValue(*singleton));
@@ -5324,16 +5341,17 @@ mjit::Compiler::jsop_name(JSAtom *atom, 
     Jump inlineJump = masm.jump();
     {
         RESERVE_OOL_SPACE(stubcc.masm);
         pic.slowPathStart = stubcc.linkExit(inlineJump, Uses(0));
         stubcc.leave();
         passICAddress(&pic);
         pic.slowPathCall = OOL_STUBCALL(isCall ? ic::CallName : ic::Name, rejoin);
         CHECK_OOL_SPACE();
+        testPushedType(rejoin, 0);
     }
     pic.fastPathRejoin = masm.label();
 
     /* Initialize op labels. */
     ScopeNameLabels &labels = pic.scopeNameLabels();
     labels.setInlineJump(masm, pic.fastPathStart, inlineJump);
 
     CHECK_IC_SPACE();
@@ -5411,16 +5429,17 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
     Jump inlineJump = masm.jump();
     {
         RESERVE_OOL_SPACE(stubcc.masm);
         pic.slowPathStart = stubcc.linkExit(inlineJump, Uses(1));
         stubcc.leave();
         passICAddress(&pic);
         pic.slowPathCall = OOL_STUBCALL(ic::XName, REJOIN_GETTER);
         CHECK_OOL_SPACE();
+        testPushedType(REJOIN_GETTER, -1);
     }
 
     pic.fastPathRejoin = masm.label();
 
     RETURN_IF_OOM(false);
 
     /* Initialize op labels. */
     ScopeNameLabels &labels = pic.scopeNameLabels();
@@ -5507,17 +5526,18 @@ mjit::Compiler::jsop_bindname(JSAtom *at
 }
 
 #else /* !JS_POLYIC */
 
 void
 mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type, bool isCall)
 {
     prepareStubCall(Uses(0));
-        INLINE_STUBCALL(isCall ? stubs::CallName : stubs::Name, REJOIN_FALLTHROUGH);
+    INLINE_STUBCALL(isCall ? stubs::CallName : stubs::Name, REJOIN_FALLTHROUGH);
+    testPushedType(REJOIN_FALLTHROUGH, 0, /* ool = */ false);
     frame.pushSynced(type);
     if (isCall)
         frame.pushSynced(JSVAL_TYPE_UNKNOWN);
 }
 
 bool
 mjit::Compiler::jsop_xname(JSAtom *atom)
 {
@@ -5883,16 +5903,17 @@ mjit::Compiler::iterEnd()
     stubcc.rejoin(Changes(1));
 }
 
 void
 mjit::Compiler::jsop_getgname_slow(uint32 index)
 {
     prepareStubCall(Uses(0));
     INLINE_STUBCALL(stubs::GetGlobalName, REJOIN_GETTER);
+    testPushedType(REJOIN_GETTER, 0, /* ool = */ false);
     frame.pushSynced(JSVAL_TYPE_UNKNOWN);
 }
 
 void
 mjit::Compiler::jsop_bindgname()
 {
     if (globalObj) {
         frame.push(ObjectValue(*globalObj));
@@ -5996,31 +6017,33 @@ mjit::Compiler::jsop_getgname(uint32 ind
         frame.freeReg(reg);
     }
     stubcc.linkExit(shapeGuard, Uses(0));
 
     stubcc.leave();
     passMICAddress(ic);
     ic.slowPathCall = OOL_STUBCALL(ic::GetGlobalName, REJOIN_GETTER);
 
+    CHECK_IC_SPACE();
+
+    testPushedType(REJOIN_GETTER, 0);
+
     /* Garbage value. */
     uint32 slot = 1 << 24;
 
     masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
     Address address(objReg, slot);
 
     /* Allocate any register other than objReg. */
     RegisterID treg = frame.allocReg();
     /* After dreg is loaded, it's safe to clobber objReg. */
     RegisterID dreg = objReg;
 
     ic.load = masm.loadValueWithAddressOffsetPatch(address, treg, dreg);
 
-    CHECK_IC_SPACE();
-
     frame.pushRegs(treg, dreg, type);
 
     /*
      * Note: no undefined check is needed for GNAME opcodes. These were not
      * declared with 'var', so cannot be undefined without triggering an error
      * or having been a pre-existing global whose value is undefined (which
      * type inference will know about).
      */
@@ -6269,16 +6292,17 @@ mjit::Compiler::jsop_setelem_slow()
     frame.pushSynced(JSVAL_TYPE_UNKNOWN);
 }
 
 void
 mjit::Compiler::jsop_getelem_slow()
 {
     prepareStubCall(Uses(2));
     INLINE_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
+    testPushedType(REJOIN_FALLTHROUGH, -2, /* ool = */ false);
     frame.popn(2);
     pushSyncedEntry(0);
 }
 
 void
 mjit::Compiler::jsop_unbrand()
 {
     prepareStubCall(Uses(1));
@@ -7013,16 +7037,17 @@ mjit::Compiler::jsop_tableswitch(jsbytec
 #endif
 }
 
 void
 mjit::Compiler::jsop_callelem_slow()
 {
     prepareStubCall(Uses(2));
     INLINE_STUBCALL(stubs::CallElem, REJOIN_FALLTHROUGH);
+    testPushedType(REJOIN_FALLTHROUGH, -2, /* ool = */ false);
     frame.popn(2);
     pushSyncedEntry(0);
     pushSyncedEntry(1);
 }
 
 void
 mjit::Compiler::jsop_toid()
 {
@@ -7515,8 +7540,43 @@ mjit::Compiler::finishBarrier(const Barr
 
     stubcc.syncExit(Uses(0));
     stubcc.leave();
 
     stubcc.masm.move(ImmPtr((void *) which), Registers::ArgReg1);
     OOL_STUBCALL(stubs::TypeBarrierHelper, rejoin);
     stubcc.rejoin(Changes(0));
 }
+
+void
+mjit::Compiler::testPushedType(RejoinState rejoin, int which, bool ool)
+{
+    if (!cx->typeInferenceEnabled() || !(js_CodeSpec[*PC].format & JOF_TYPESET))
+        return;
+
+    types::TypeSet *types = analysis->bytecodeTypes(PC);
+    if (types->unknown())
+        return;
+
+    Assembler &masm = ool ? stubcc.masm : this->masm;
+
+    JS_ASSERT(which <= 0);
+    Address address = (which == 0) ? frame.addressOfTop() : frame.addressOf(frame.peek(which));
+
+    Vector<Jump> mismatches(cx);
+    if (!masm.generateTypeCheck(cx, address, types, &mismatches)) {
+        oomInVector = true;
+        return;
+    }
+
+    Jump j = masm.jump();
+
+    for (unsigned i = 0; i < mismatches.length(); i++)
+        mismatches[i].linkTo(masm.label(), &masm);
+
+    masm.move(Imm32(which), Registers::ArgReg1);
+    if (ool)
+        OOL_STUBCALL(stubs::StubTypeHelper, rejoin);
+    else
+        INLINE_STUBCALL(stubs::StubTypeHelper, rejoin);
+
+    j.linkTo(masm.label(), &masm);
+}
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -179,32 +179,34 @@ class Compiler : public BaseCompiler
         DataLabelPtr fastNcodePatch;
         DataLabelPtr slowNcodePatch;
         bool hasFastNcode;
         bool hasSlowNcode;
         bool joinSlow;
     };
 
     struct BaseICInfo {
-        BaseICInfo(JSOp op) : op(op), canCallHook(false)
+        BaseICInfo(JSOp op) : op(op), canCallHook(false), forcedTypeBarrier(false)
         { }
         Label fastPathStart;
         Label fastPathRejoin;
         Label slowPathStart;
         Call slowPathCall;
         DataLabelPtr paramAddr;
         JSOp op;
         bool canCallHook;
+        bool forcedTypeBarrier;
 
         void copyTo(ic::BaseIC &to, JSC::LinkBuffer &full, JSC::LinkBuffer &stub) {
             to.fastPathStart = full.locationOf(fastPathStart);
             to.fastPathRejoin = full.locationOf(fastPathRejoin);
             to.slowPathStart = stub.locationOf(slowPathStart);
             to.slowPathCall = stub.locationOf(slowPathCall);
             to.canCallHook = canCallHook;
+            to.forcedTypeBarrier = forcedTypeBarrier;
             to.op = op;
             JS_ASSERT(to.op == op);
         }
     };
 
     struct GetElementICInfo : public BaseICInfo {
         GetElementICInfo(JSOp op) : BaseICInfo(op)
         { }
@@ -565,16 +567,18 @@ class Compiler : public BaseCompiler
     Jump addTypeTest(types::TypeSet *types, RegisterID typeReg, RegisterID dataReg);
     BarrierState pushAddressMaybeBarrier(Address address, JSValueType type, bool reuseBase,
                                          bool testUndefined = false);
     BarrierState testBarrier(RegisterID typeReg, RegisterID dataReg,
                              bool testUndefined = false, bool testReturn = false,
                              bool force = false);
     void finishBarrier(const BarrierState &barrier, RejoinState rejoin, uint32 which);
 
+    void testPushedType(RejoinState rejoin, int which, bool ool = true);
+
     /* Non-emitting helpers. */
     void pushSyncedEntry(uint32 pushed);
     uint32 fullAtomIndex(jsbytecode *pc);
     bool jumpInScript(Jump j, jsbytecode *pc);
     bool compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const Value &rhs);
     bool canUseApplyTricks();
 
     /* Emitting helpers. */
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1741,16 +1741,17 @@ mjit::Compiler::jsop_getelem_dense(bool 
         holeCheck = masm.fastArrayLoadSlot(slot, !isPacked, typeReg, dataReg);
     }
 
     if (!isPacked && !allowUndefined)
         stubcc.linkExit(holeCheck, Uses(2));
 
     stubcc.leave();
     OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
+    testPushedType(REJOIN_FALLTHROUGH, -2);
 
     frame.popn(2);
 
     BarrierState barrier;
     if (typeReg.isSet()) {
         frame.pushRegs(typeReg.reg(), dataReg, type);
         barrier = testBarrier(typeReg.reg(), dataReg, false);
     } else {
@@ -1832,16 +1833,17 @@ mjit::Compiler::jsop_getelem_args()
     } else {
         JS_ASSERT(key.reg() != dataReg);
         BaseIndex arg(actualsReg, key.reg(), masm.JSVAL_SCALE);
         masm.loadValueAsComponents(arg, typeReg, dataReg);
     }
 
     stubcc.leave();
     OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
+    testPushedType(REJOIN_FALLTHROUGH, -2);
 
     frame.popn(2);
     frame.pushRegs(typeReg, dataReg, knownPushedType(0));
     BarrierState barrier = testBarrier(typeReg, dataReg, false);
 
     stubcc.rejoin(Changes(2));
 
     finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
@@ -1963,16 +1965,17 @@ mjit::Compiler::jsop_getelem_typed(int a
     if (atype == TypedArray::TYPE_UINT32 &&
         !pushedTypes->hasType(types::Type::DoubleType())) {
         Jump isDouble = masm.testDouble(Assembler::Equal, typeReg.reg());
         stubcc.linkExit(isDouble, Uses(2));
     }
 
     stubcc.leave();
     OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
+    testPushedType(REJOIN_FALLTHROUGH, -2);
 
     frame.popn(2);
 
     BarrierState barrier;
     if (dataReg.isFPReg()) {
         frame.pushDouble(dataReg.fpreg());
     } else if (typeReg.isSet()) {
         frame.pushRegs(typeReg.reg(), dataReg.reg(), knownPushedType(0));
@@ -2144,23 +2147,27 @@ mjit::Compiler::jsop_getelem(bool isCall
         ic.slowPathCall = OOL_STUBCALL(ic::GetElement, REJOIN_FALLTHROUGH);
 #else
     if (isCall)
         ic.slowPathCall = OOL_STUBCALL(stubs::CallElem, REJOIN_FALLTHROUGH);
     else
         ic.slowPathCall = OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
 #endif
 
+    testPushedType(REJOIN_FALLTHROUGH, -2);
+
     ic.fastPathRejoin = masm.label();
+    ic.forcedTypeBarrier = analysis->getCode(PC).getStringElement;
 
     CHECK_IC_SPACE();
 
     frame.popn(2);
     frame.pushRegs(ic.typeReg, ic.objReg, knownPushedType(0));
-    BarrierState barrier = testBarrier(ic.typeReg, ic.objReg, false);
+    BarrierState barrier = testBarrier(ic.typeReg, ic.objReg, false, false,
+                                       /* force = */ ic.forcedTypeBarrier);
     if (isCall)
         frame.pushSynced(knownPushedType(1));
 
     stubcc.rejoin(Changes(isCall ? 2 : 1));
 
 #ifdef JS_POLYIC
     if (!getElemICs.append(ic))
         return false;
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -1355,20 +1355,16 @@ js_InternalInterpret(void *returnData, v
             } else {
                 nextsp[-1] = nextsp[0];
             }
         }
 
         /* Release this reference on the orphaned native stub. */
         RemoveOrphanedNative(cx, fp);
 
-        /*
-         * Note: there is no need to monitor the result of the native, the stub
-         * will always do a type check before finishing.
-         */
         f.regs.pc = nextpc;
         break;
       }
 
       case REJOIN_PUSH_BOOLEAN:
         nextsp[-1].setBoolean(returnReg != NULL);
         f.regs.pc = nextpc;
         break;
@@ -1577,15 +1573,25 @@ js_InternalInterpret(void *returnData, v
       default:
         JS_NOT_REACHED("Missing rejoin");
     }
 
     if (nextDepth == uint32(-1))
         nextDepth = analysis->getCode(f.regs.pc).stackDepth;
     f.regs.sp = fp->base() + nextDepth;
 
+    /*
+     * Monitor the result of the previous op when finishing a JOF_TYPESET op.
+     * The result may not have been marked if we bailed out while inside a stub
+     * for the op.
+     */
+    if (js_CodeSpec[op].format & JOF_TYPESET) {
+        int which = (js_CodeSpec[op].format & JOF_CALLOP) ? -2 : -1;  /* Yuck. */
+        types::TypeScript::Monitor(cx, script, pc, f.regs.sp[which]);
+    }
+
     /* Mark the entry frame as unfinished, and update the regs to resume at. */
     JaegerStatus status = skipTrap ? Jaeger_UnfinishedAtTrap : Jaeger_Unfinished;
     cx->compartment->jaegerCompartment()->setLastUnfinished(status);
     *f.oldregs = f.regs;
 
     return NULL;
 }
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1933,21 +1933,19 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
     if (atom == f.cx->runtime->atomState.lengthAtom) {
         if (f.regs.sp[-1].isString()) {
             GetPropCompiler cc(f, script, NULL, *pic, NULL, DisabledGetPropIC);
             LookupStatus status = cc.generateStringLengthStub();
             if (status == Lookup_Error)
                 THROW();
             JSString *str = f.regs.sp[-1].toString();
             f.regs.sp[-1].setInt32(str->length());
-            types::TypeScript::Monitor(f.cx, f.script(), f.pc(), f.regs.sp[-1]);
             return;
         } else if (f.regs.sp[-1].isMagic(JS_LAZY_ARGUMENTS)) {
             f.regs.sp[-1].setInt32(f.regs.fp()->numActualArgs());
-            types::TypeScript::Monitor(f.cx, f.script(), f.pc(), f.regs.sp[-1]);
             return;
         } else if (!f.regs.sp[-1].isPrimitive()) {
             JSObject *obj = &f.regs.sp[-1].toObject();
             if (obj->isArray() ||
                 (obj->isArguments() && !obj->asArguments()->hasOverriddenLength()) ||
                 obj->isString()) {
                 GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledGetPropIC);
                 if (obj->isArray()) {
@@ -1962,25 +1960,22 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
                     f.regs.sp[-1].setInt32(int32_t(obj->asArguments()->initialLength()));
                 } else if (obj->isString()) {
                     LookupStatus status = cc.generateStringObjLengthStub();
                     if (status == Lookup_Error)
                         THROW();
                     JSString *str = obj->getPrimitiveThis().toString();
                     f.regs.sp[-1].setInt32(str->length());
                 }
-                types::TypeScript::Monitor(f.cx, f.script(), f.pc(), f.regs.sp[-1]);
                 return;
             }
         }
         atom = f.cx->runtime->atomState.lengthAtom;
     }
 
-    bool usePropCache = pic->usePropCache;
-
     /*
      * ValueToObject can trigger recompilations if it lazily initializes any
      * of the primitive classes (Boolean, Number, String). :XXX: if these
      * classes are made eager then this monitoring is not necessary.
      */
     RecompilationMonitor monitor(f.cx);
 
     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
@@ -1997,26 +1992,16 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
             THROW();
         }
     }
 
     Value v;
     if (!obj->getProperty(f.cx, ATOM_TO_JSID(atom), &v))
         THROW();
 
-    /*
-     * Ignore undefined reads for the 'prototype' property in constructors,
-     * which will be at the start of the script and are never holes due to fun_resolve.
-     * Any undefined value was explicitly stored here, and is known by inference.
-     * :FIXME: looking under the usePropCache abstraction, which is only unset for
-     * reads of the prototype.
-     */
-    if (usePropCache)
-        types::TypeScript::Monitor(f.cx, f.script(), f.pc(), v);
-
     f.regs.sp[-1] = v;
 }
 
 void JS_FASTCALL
 ic::GetPropNoCache(VMFrame &f, ic::PICInfo *pic)
 {
     /*
      * The PIC stores whether to use the property cache or not. We use two different
@@ -2176,18 +2161,16 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pi
 #if JS_HAS_NO_SUCH_METHOD
     if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) {
         regs.sp[-2].setString(JSID_TO_STRING(id));
         if (!OnUnknownMethod(cx, regs.sp - 2))
             THROW();
     }
 #endif
 
-    types::TypeScript::Monitor(f.cx, f.script(), f.pc(), regs.sp[-2]);
-
     if (monitor.recompiled())
         return;
 
     GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, DisabledCallPropIC);
     if (lval.isObject()) {
         if (pic->shouldUpdate(cx)) {
             LookupStatus status = cc.update();
             if (status == Lookup_Error)
@@ -2227,18 +2210,16 @@ ic::XName(VMFrame &f, ic::PICInfo *pic)
     LookupStatus status = cc.updateForXName();
     if (status == Lookup_Error)
         THROW();
 
     Value rval;
     if (!cc.retrieve(&rval, NULL, PICInfo::XNAME))
         THROW();
     f.regs.sp[-1] = rval;
-
-    types::TypeScript::Monitor(f.cx, f.script(), f.pc(), rval);
 }
 
 void JS_FASTCALL
 ic::Name(VMFrame &f, ic::PICInfo *pic)
 {
     JSScript *script = f.fp()->script();
 
     ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, DisabledNameIC);
@@ -2246,18 +2227,16 @@ ic::Name(VMFrame &f, ic::PICInfo *pic)
     LookupStatus status = cc.updateForName();
     if (status == Lookup_Error)
         THROW();
 
     Value rval;
     if (!cc.retrieve(&rval, NULL, PICInfo::NAME))
         THROW();
     f.regs.sp[0] = rval;
-
-    types::TypeScript::Monitor(f.cx, f.script(), f.pc(), rval);
 }
 
 static void JS_FASTCALL
 DisabledCallNameIC(VMFrame &f, ic::PICInfo *pic)
 {
     stubs::CallName(f);
 }
 
@@ -2273,18 +2252,16 @@ ic::CallName(VMFrame &f, ic::PICInfo *pi
         THROW();
 
     Value rval, thisval;
     if (!cc.retrieve(&rval, &thisval, PICInfo::CALLNAME))
         THROW();
 
     f.regs.sp[0] = rval;
     f.regs.sp[1] = thisval;
-
-    types::TypeScript::Monitor(f.cx, f.script(), f.pc(), rval);
 }
 
 static void JS_FASTCALL
 DisabledBindNameIC(VMFrame &f, ic::PICInfo *pic)
 {
     stubs::BindName(f);
 }
 
@@ -2449,16 +2426,22 @@ GetElementIC::attachGetProp(VMFrame &f, 
 {
     JS_ASSERT(v.isString());
 
     GetPropertyHelper<GetElementIC> getprop(cx, obj, JSID_TO_ATOM(id), *this, f);
     LookupStatus status = getprop.lookupAndTest();
     if (status != Lookup_Cacheable)
         return status;
 
+    // With TI enabled, string property stubs can only be added to an opcode if
+    // the value read will go through a type barrier afterwards. TI only
+    // accounts for integer-valued properties accessed by GETELEM/CALLELEM.
+    if (cx->typeInferenceEnabled() && !forcedTypeBarrier)
+        return disable(cx, "string element access may not have type barrier");
+
     Assembler masm;
 
     // Guard on the string's type and identity.
     MaybeJump atomTypeGuard;
     if (hasInlineTypeGuard() && !inlineTypeGuardPatched) {
         // We link all string-key dependent stubs together, and store the
         // first set of guards in the IC, separately, from int-key dependent
         // stubs. As long as we guarantee that the first string-key dependent
@@ -2912,19 +2895,16 @@ ic::CallElement(VMFrame &f, ic::GetEleme
         LookupStatus status = ic->update(f, cx, thisObj, idval, id, &f.regs.sp[-2]);
         if (status != Lookup_Uncacheable) {
             if (status == Lookup_Error)
                 THROW();
 
             // If the result can be cached, the value was already retrieved.
             JS_ASSERT(!f.regs.sp[-2].isMagic());
             f.regs.sp[-1].setObject(*thisObj);
-            if (!JSID_IS_INT(id))
-                types::TypeScript::MonitorUnknown(f.cx, f.script(), f.pc());
-            types::TypeScript::Monitor(f.cx, f.script(), f.pc(), f.regs.sp[-2]);
             return;
         }
     }
 
     /* Get or set the element. */
     if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &f.regs.sp[-2]))
         THROW();
 
@@ -2934,19 +2914,16 @@ ic::CallElement(VMFrame &f, ic::GetEleme
         f.regs.sp[-1].setObject(*thisObj);
         if (!OnUnknownMethod(cx, f.regs.sp - 2))
             THROW();
     } else
 #endif
     {
         f.regs.sp[-1] = thisv;
     }
-    if (!JSID_IS_INT(id))
-        types::TypeScript::MonitorUnknown(f.cx, f.script(), f.pc());
-    types::TypeScript::Monitor(f.cx, f.script(), f.pc(), f.regs.sp[-2]);
 }
 
 void JS_FASTCALL
 ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
 {
     JSContext *cx = f.cx;
 
     // Right now, we don't optimize for strings or lazy arguments.
@@ -2978,28 +2955,22 @@ ic::GetElement(VMFrame &f, ic::GetElemen
 #endif
         LookupStatus status = ic->update(f, cx, obj, idval, id, &f.regs.sp[-2]);
         if (status != Lookup_Uncacheable) {
             if (status == Lookup_Error)
                 THROW();
 
             // If the result can be cached, the value was already retrieved.
             JS_ASSERT(!f.regs.sp[-2].isMagic());
-            if (!JSID_IS_INT(id))
-                types::TypeScript::MonitorUnknown(f.cx, f.script(), f.pc());
-            types::TypeScript::Monitor(f.cx, f.script(), f.pc(), f.regs.sp[-2]);
             return;
         }
     }
 
     if (!obj->getProperty(cx, id, &f.regs.sp[-2]))
         THROW();
-    if (!JSID_IS_INT(id))
-        types::TypeScript::MonitorUnknown(f.cx, f.script(), f.pc());
-    types::TypeScript::Monitor(f.cx, f.script(), f.pc(), f.regs.sp[-2]);
 }
 
 #define APPLY_STRICTNESS(f, s)                          \
     (FunctionTemplateConditional(s, f<true>, f<false>))
 
 LookupStatus
 SetElementIC::disable(JSContext *cx, const char *reason)
 {
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -91,25 +91,29 @@ struct BaseIC : public MacroAssemblerTyp
 
     // Whether or not the callsite has been hit at least once.
     bool hit : 1;
     bool slowCallPatched : 1;
 
     // Whether getter/setter hooks can be called from IC stubs.
     bool canCallHook : 1;
 
+    // Whether a type barrier is in place for the result of the op.
+    bool forcedTypeBarrier : 1;
+
     // Number of stubs generated.
     uint32 stubsGenerated : 5;
 
     // Opcode this was compiled for.
     JSOp op : 9;
 
     void reset() {
         hit = false;
         slowCallPatched = false;
+        forcedTypeBarrier = false;
         stubsGenerated = 0;
         secondShapeGuard = 0;
     }
     bool shouldUpdate(JSContext *cx);
     void spew(JSContext *cx, const char *event, const char *reason);
     LookupStatus disable(JSContext *cx, const char *reason, void *stub);
     void updatePCCounters(JSContext *cx, Assembler &masm);
     bool isCallOp();
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -370,17 +370,16 @@ NameOp(VMFrame &f, JSObject *obj, bool c
         if (!js_FindPropertyHelper(cx, id, true, global, &obj, &obj2, &prop))
             return NULL;
         if (!prop) {
             /* Kludge to allow (typeof foo == "undefined") tests. */
             JSOp op2 = js_GetOpcode(cx, f.script(), f.pc() + JSOP_NAME_LENGTH);
             if (op2 == JSOP_TYPEOF) {
                 f.regs.sp++;
                 f.regs.sp[-1].setUndefined();
-                TypeScript::Monitor(cx, f.script(), f.pc(), f.regs.sp[-1]);
                 return obj;
             }
             ReportAtomNotDefined(cx, atom);
             return NULL;
         }
 
         /* Take the slow path if prop was not found in a native object. */
         if (!obj->isNative() || !obj2->isNative()) {
@@ -397,18 +396,16 @@ NameOp(VMFrame &f, JSObject *obj, bool c
         /*
          * If this is an incop, update the property's types themselves,
          * to capture the type effect on the intermediate value.
          */
         if (rval.isUndefined() && (js_CodeSpec[*f.pc()].format & (JOF_INC|JOF_DEC)))
             AddTypePropertyId(cx, obj, id, Type::UndefinedType());
     }
 
-    TypeScript::Monitor(cx, f.script(), f.pc(), rval);
-
     *f.regs.sp++ = rval;
 
     if (callname)
         PushImplicitThis(f, obj, rval);
 
     return obj;
 }
 
@@ -438,25 +435,23 @@ stubs::GetElem(VMFrame &f)
     if (lref.isString() && rref.isInt32()) {
         JSString *str = lref.toString();
         int32_t i = rref.toInt32();
         if ((size_t)i < str->length()) {
             str = JSAtom::getUnitStringForElement(cx, str, (size_t)i);
             if (!str)
                 THROW();
             f.regs.sp[-2].setString(str);
-            TypeScript::Monitor(cx, f.script(), f.pc(), f.regs.sp[-2]);
             return;
         }
     }
 
     if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
         if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) {
             regs.sp[-2] = regs.fp()->canonicalActualArg(rref.toInt32());
-            TypeScript::Monitor(cx, f.script(), f.pc(), regs.sp[-2]);
             return;
         }
         MarkArgumentsCreated(cx, f.script());
         JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
     }
 
     JSObject *obj = ValueToObject(cx, &lref);
     if (!obj)
@@ -503,22 +498,18 @@ stubs::GetElem(VMFrame &f)
                 THROW();
         }
     }
 
     if (!obj->getProperty(cx, id, &rval))
         THROW();
     copyFrom = &rval;
 
-    if (!JSID_IS_INT(id))
-        TypeScript::MonitorUnknown(cx, f.script(), f.pc());
-
   end_getelem:
     f.regs.sp[-2] = *copyFrom;
-    TypeScript::Monitor(cx, f.script(), f.pc(), f.regs.sp[-2]);
 }
 
 static inline bool
 FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *vp)
 {
     int32_t i_;
     if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
         id = INT_TO_JSID(i_);
@@ -554,19 +545,16 @@ stubs::CallElem(VMFrame &f)
         regs.sp[-1].setObject(*thisObj);
         if (!OnUnknownMethod(cx, regs.sp - 2))
             THROW();
     } else
 #endif
     {
         regs.sp[-1] = thisv;
     }
-    if (!JSID_IS_INT(id))
-        TypeScript::MonitorUnknown(cx, f.script(), f.pc());
-    TypeScript::Monitor(cx, f.script(), f.pc(), regs.sp[-2]);
 }
 
 template<JSBool strict>
 void JS_FASTCALL
 stubs::SetElem(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
@@ -1568,17 +1556,16 @@ InlineGetProp(VMFrame &f)
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
 
     Value *vp = &f.regs.sp[-1];
 
     if (vp->isMagic(JS_LAZY_ARGUMENTS)) {
         JS_ASSERT(js_GetOpcode(cx, f.script(), f.pc()) == JSOP_LENGTH);
         regs.sp[-1] = Int32Value(regs.fp()->numActualArgs());
-        TypeScript::Monitor(cx, f.script(), f.pc(), regs.sp[-1]);
         return true;
     }
 
     JSObject *obj = ValueToObject(f.cx, vp);
     if (!obj)
         return false;
 
     Value rval;
@@ -1617,18 +1604,16 @@ InlineGetProp(VMFrame &f)
                     ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
                     : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
                     &rval)
                 : !obj->getProperty(cx, id, &rval)) {
             return false;
         }
     } while(0);
 
-    TypeScript::Monitor(cx, f.script(), f.pc(), rval);
-
     regs.sp[-1] = rval;
     return true;
 }
 
 void JS_FASTCALL
 stubs::GetProp(VMFrame &f)
 {
     if (!InlineGetProp(f))
@@ -1737,17 +1722,16 @@ stubs::CallProp(VMFrame &f, JSAtom *orig
     }
 #if JS_HAS_NO_SUCH_METHOD
     if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) {
         regs.sp[-2].setString(origAtom);
         if (!OnUnknownMethod(cx, regs.sp - 2))
             THROW();
     }
 #endif
-    TypeScript::Monitor(cx, f.script(), f.pc(), rval);
 }
 
 void JS_FASTCALL
 stubs::Iter(VMFrame &f, uint32 flags)
 {
     if (!js_ValueToIterator(f.cx, flags, &f.regs.sp[-1]))
         THROW();
     JS_ASSERT(!f.regs.sp[-1].isPrimitive());
@@ -2389,16 +2373,29 @@ stubs::TypeBarrierHelper(VMFrame &f, uin
     if (f.script()->hasAnalysis() && f.script()->analysis()->ranInference()) {
         AutoEnterTypeInference enter(f.cx);
         f.script()->analysis()->breakTypeBarriers(f.cx, f.pc() - f.script()->code, false);
     }
 
     TypeScript::Monitor(f.cx, f.script(), f.pc(), result);
 }
 
+void JS_FASTCALL
+stubs::StubTypeHelper(VMFrame &f, int32 which)
+{
+    const Value &result = f.regs.sp[which];
+
+    if (f.script()->hasAnalysis() && f.script()->analysis()->ranInference()) {
+        AutoEnterTypeInference enter(f.cx);
+        f.script()->analysis()->breakTypeBarriers(f.cx, f.pc() - f.script()->code, false);
+    }
+
+    TypeScript::Monitor(f.cx, f.script(), f.pc(), result);
+}
+
 /*
  * Variant of TypeBarrierHelper for checking types after making a native call.
  * The stack is already correct, and no fixup should be performed.
  */
 void JS_FASTCALL
 stubs::TypeBarrierReturn(VMFrame &f, Value *vp)
 {
     TypeScript::Monitor(f.cx, f.script(), f.pc(), vp[0]);
@@ -2407,35 +2404,16 @@ stubs::TypeBarrierReturn(VMFrame &f, Val
 void JS_FASTCALL
 stubs::NegZeroHelper(VMFrame &f)
 {
     f.regs.sp[-1].setDouble(-0.0);
     TypeScript::MonitorOverflow(f.cx, f.script(), f.pc());
 }
 
 void JS_FASTCALL
-stubs::CallPropSwap(VMFrame &f)
-{
-    /*
-     * CALLPROP operations on strings are implemented in terms of GETPROP.
-     * If we rejoin from such a GETPROP, we come here at the end of the
-     * CALLPROP to fix up the stack. Right now the stack looks like:
-     *
-     * STRING PROP
-     *
-     * We need it to be:
-     *
-     * PROP STRING
-     */
-    Value v = f.regs.sp[-1];
-    f.regs.sp[-1] = f.regs.sp[-2];
-    f.regs.sp[-2] = v;
-}
-
-void JS_FASTCALL
 stubs::CheckArgumentTypes(VMFrame &f)
 {
     StackFrame *fp = f.fp();
     JSFunction *fun = fp->fun();
     JSScript *script = fun->script();
     RecompilationMonitor monitor(f.cx);
 
     {
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -205,17 +205,18 @@ void JS_FASTCALL UnbrandThis(VMFrame &f)
 /*
  * Helper for triggering recompilation should a name read miss a type barrier,
  * produce undefined or -0.
  */
 void JS_FASTCALL TypeBarrierHelper(VMFrame &f, uint32 which);
 void JS_FASTCALL TypeBarrierReturn(VMFrame &f, Value *vp);
 void JS_FASTCALL NegZeroHelper(VMFrame &f);
 
-void JS_FASTCALL CallPropSwap(VMFrame &f);
+void JS_FASTCALL StubTypeHelper(VMFrame &f, int32 which);
+
 void JS_FASTCALL CheckArgumentTypes(VMFrame &f);
 
 #ifdef DEBUG
 void JS_FASTCALL AssertArgumentTypes(VMFrame &f);
 #endif
 
 void JS_FASTCALL MissedBoundsCheckEntry(VMFrame &f);
 void JS_FASTCALL MissedBoundsCheckHead(VMFrame &f);