Compile Add with String and Objects (Bug 713693, r=dvander)
authorNicolas Pierron <nioclas.b.pierron@mozilla.com>
Thu, 12 Jan 2012 19:28:02 +0100
changeset 105561 41a9e0d849ea49b063d3919d88e33ae5693b551a
parent 105560 dac569ab767c4cec743ccf198e4a50ca8066f182
child 105562 ee506186bc0629bd6970ff94f7e4f1134f0017bc
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersdvander
bugs713693
milestone12.0a1
Compile Add with String and Objects (Bug 713693, r=dvander)
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.cpp
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/TypeOracle.cpp
js/src/ion/TypeOracle.h
js/src/ion/TypePolicy.cpp
js/src/ion/TypePolicy.h
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
js/src/ion/arm/Lowering-arm.cpp
js/src/ion/arm/Lowering-arm.h
js/src/ion/x64/Lowering-x64.cpp
js/src/ion/x64/Lowering-x64.h
js/src/ion/x86/Lowering-x86.cpp
js/src/ion/x86/Lowering-x86.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsstr.h
js/src/vm/String.cpp
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -237,16 +237,29 @@ CodeGenerator::visitTruncateDToInt32(LTr
     emitTruncateDouble(ToFloatRegister(lir->input()), ToRegister(lir->output()), &fails);
     if (!bailoutFrom(&fails, lir->snapshot()))
         return false;
 
     return true;
 }
 
 bool
+CodeGenerator::visitIntToString(LIntToString *lir)
+{
+    typedef JSString *(*pf)(JSContext *, jsint);
+    static const VMFunction js_IntToStringInfo =
+        FunctionInfo<pf>(js_IntToString);
+
+    pushArg(ToRegister(lir->input()));
+    if (!callVM(js_IntToStringInfo, lir))
+        return false;
+    return true;
+}
+
+bool
 CodeGenerator::visitLabel(LLabel *lir)
 {
     masm.bind(lir->label());
     return true;
 }
 
 bool
 CodeGenerator::visitCaptureAllocations(LCaptureAllocations *)
@@ -701,16 +714,41 @@ CodeGenerator::visitStringLength(LString
     Address lengthAndFlags(ToRegister(lir->string()), JSString::offsetOfLengthAndFlags());
     Register output = ToRegister(lir->output());
 
     masm.loadPtr(lengthAndFlags, output);
     masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), output);
     return true;
 }
 
+bool
+CodeGenerator::visitAddV(LAddV *lir)
+{
+    typedef bool (*pf)(JSContext *, const Value &, const Value &, Value *);
+    static const VMFunction AddValuesInfo = FunctionInfo<pf>(js::ion::AddValues);
+
+    pushArg(ToValue(lir, LAddV::RhsInput));
+    pushArg(ToValue(lir, LAddV::LhsInput));
+    if (!callVM(AddValuesInfo, lir))
+        return false;
+    return true;
+}
+
+bool
+CodeGenerator::visitConcat(LConcat *lir)
+{
+    typedef JSString *(*pf)(JSContext *, JSString *, JSString *);
+    static const VMFunction js_ConcatStringsInfo = FunctionInfo<pf>(js_ConcatStrings);
+
+    pushArg(ToRegister(lir->rhs()));
+    pushArg(ToRegister(lir->lhs()));
+    if (!callVM(js_ConcatStringsInfo, lir))
+        return false;
+    return true;
+}
 
 bool
 CodeGenerator::visitInitializedLength(LInitializedLength *lir)
 {
     Address initLength(ToRegister(lir->elements()), ObjectElements::offsetOfInitializedLength());
     masm.load32(initLength, ToRegister(lir->output()));
     return true;
 }
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -80,28 +80,31 @@ class CodeGenerator : public CodeGenerat
     bool visitOsrEntry(LOsrEntry *lir);
     bool visitOsrScopeChain(LOsrScopeChain *lir);
     bool visitStackArg(LStackArg *lir);
     bool visitValueToInt32(LValueToInt32 *lir);
     bool visitValueToDouble(LValueToDouble *lir);
     bool visitInt32ToDouble(LInt32ToDouble *lir);
     bool visitTestVAndBranch(LTestVAndBranch *lir);
     bool visitTruncateDToInt32(LTruncateDToInt32 *lir);
+    bool visitIntToString(LIntToString *lir);
     bool visitInteger(LInteger *lir);
     bool visitPointer(LPointer *lir);
     bool visitSlots(LSlots *lir);
     bool visitStoreSlotV(LStoreSlotV *store);
     bool visitElements(LElements *lir);
     bool visitTypeBarrier(LTypeBarrier *lir);
     bool visitCallGeneric(LCallGeneric *lir);
     bool visitDoubleToInt32(LDoubleToInt32 *lir);
     bool visitNewArray(LNewArray *builder);
     bool visitArrayLength(LArrayLength *lir);
     bool visitStringLength(LStringLength *lir);
     bool visitInitializedLength(LInitializedLength *lir);
+    bool visitAddV(LAddV *lir);
+    bool visitConcat(LConcat *lir);
     bool visitFunctionEnvironment(LFunctionEnvironment *lir);
     bool visitCallGetProperty(LCallGetProperty *lir);
     bool visitCallGetName(LCallGetName *lir);
     bool visitCallGetNameTypeOf(LCallGetNameTypeOf *lir);
     bool visitGetPropertyCacheV(LGetPropertyCacheV *load) { return visitGetPropertyCache(load); }
     bool visitGetPropertyCacheT(LGetPropertyCacheT *load) { return visitGetPropertyCache(load); }
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -1998,23 +1998,98 @@ IonBuilder::jsop_bitop(JSOp op)
     current->add(ins);
     ins->infer(oracle->binaryOp(script, pc));
 
     current->push(ins);
     return true;
 }
 
 bool
+IonBuilder::jsop_add(MDefinition *left, MDefinition *right)
+{
+    if (jsop_add_specialized(left, right))
+        return true;
+
+    MAddGeneric *ins = MAddGeneric::New(left, right);
+    current->add(ins);
+    current->push(ins);
+
+    if (!resumeAfter(ins))
+        return false;
+    return true;
+}
+
+bool
+IonBuilder::jsop_add_specialized(MDefinition *left, MDefinition *right)
+{
+    TypeOracle::BinaryTypes b = oracle->binaryTypes(script, pc);
+
+    if (!b.lhsTypes || !b.rhsTypes || !b.outTypes)
+        return false;
+
+    if (b.outTypes->getKnownTypeTag(cx) == JSVAL_TYPE_INT32 ||
+        b.outTypes->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE)
+    {
+
+        if (b.lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 &&
+            b.lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_DOUBLE &&
+            b.lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_UNDEFINED)
+         {
+            return false;
+         }
+
+        if (b.rhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 &&
+            b.rhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_DOUBLE &&
+            b.rhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_UNDEFINED)
+         {
+            return false;
+         }
+
+        MBinaryArithInstruction *ins = MAdd::New(left, right);
+        current->add(ins);
+        ins->infer(oracle->binaryOp(script, pc));
+        current->push(ins);
+
+    } else if (b.outTypes->getKnownTypeTag(cx) == JSVAL_TYPE_STRING) {
+        if (b.lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 &&
+            b.lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_STRING)
+         {
+            return false;
+         }
+
+        if (b.lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_STRING &&
+            b.rhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_STRING)
+         {
+            return false;
+         }
+
+        if (b.rhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 &&
+            b.rhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_STRING)
+         {
+            return false;
+         }
+
+        MConcat *ins = MConcat::New(left, right);
+        current->add(ins);
+        current->push(ins);
+
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 IonBuilder::jsop_binary(JSOp op, MDefinition *left, MDefinition *right)
 {
     MBinaryArithInstruction *ins;
     switch (op) {
       case JSOP_ADD:
-        ins = MAdd::New(left, right);
-        break;
+        return jsop_add(left, right);
 
       case JSOP_SUB:
         ins = MSub::New(left, right);
         break;
 
       case JSOP_MUL:
         ins = MMul::New(left, right);
         break;
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -268,16 +268,18 @@ class IonBuilder : public MIRGenerator
     bool resumeAfter(MInstruction *ins);
 
     void insertRecompileCheck();
 
     bool initParameters();
     void rewriteParameters();
     bool pushConstant(const Value &v);
     bool pushTypeBarrier(MInstruction *ins, types::TypeSet *actual, types::TypeSet *observed);
+    bool jsop_add(MDefinition *left, MDefinition *right);
+    bool jsop_add_specialized(MDefinition *left, MDefinition *right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary(JSOp op);
     bool jsop_binary(JSOp op, MDefinition *left, MDefinition *right);
     bool jsop_pos();
     bool jsop_neg();
     bool jsop_notearg();
     bool jsop_call(uint32 argc);
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -597,16 +597,49 @@ class LMathD : public LBinaryMath<0>
       : jsop_(jsop)
     { }
 
     JSOp jsop() const {
         return jsop_;
     }
 };
 
+// Adds two string, returning a string.
+class LAddV : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(AddV);
+    BOX_OUTPUT_ACCESSORS();
+
+    static const size_t LhsInput = 0;
+    static const size_t RhsInput = BOX_PIECES;
+};
+
+// Adds two string, returning a string.
+class LConcat : public LCallInstructionHelper<1, 2, 0>
+{
+  public:
+    LIR_HEADER(Concat);
+
+    LConcat(const LAllocation &lhs, const LAllocation &rhs) {
+        setOperand(0, lhs);
+        setOperand(1, rhs);
+    }
+
+    const LAllocation *lhs() {
+        return this->getOperand(0);
+    }
+    const LAllocation *rhs() {
+        return this->getOperand(1);
+    }
+    const LDefinition *output() {
+        return this->getDef(0);
+    }
+};
+
 // Convert a 32-bit integer to a double.
 class LInt32ToDouble : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(Int32ToDouble);
 
     LInt32ToDouble(const LAllocation &input) {
         setOperand(0, input);
@@ -708,16 +741,38 @@ class LTruncateDToInt32 : public LInstru
     const LAllocation *input() {
         return getOperand(0);
     }
     const LDefinition *output() {
         return getDef(0);
     }
 };
 
+// Convert a any input type hosted on one definition to a string with a function
+// call.
+class LIntToString : public LCallInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(IntToString);
+
+    LIntToString(const LAllocation &input) {
+        setOperand(0, input);
+    }
+
+    const LAllocation *input() {
+        return getOperand(0);
+    }
+    const LDefinition *output() {
+        return getDef(0);
+    }
+    const MToString *mir() {
+        return mir_->toToString();
+    }
+};
+
 // No-op instruction that is used to hold the entry snapshot. This simplifies
 // register allocation as it doesn't need to sniff the snapshot out of the
 // LIRGraph.
 class LStart : public LInstructionHelper<0, 0, 0>
 {
   public:
     LIR_HEADER(Start);
 };
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -70,21 +70,24 @@
     _(CompareI)                     \
     _(CompareD)                     \
     _(CompareIAndBranch)            \
     _(CompareDAndBranch)            \
     _(AddI)                         \
     _(SubI)                         \
     _(MulI)                         \
     _(MathD)                        \
+    _(AddV)                         \
+    _(Concat)                       \
     _(Int32ToDouble)                \
     _(ValueToDouble)                \
     _(ValueToInt32)                 \
     _(DoubleToInt32)                \
     _(TruncateDToInt32)             \
+    _(IntToString)                  \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(ImplicitThis)                 \
     _(Slots)                        \
     _(Elements)                     \
     _(LoadSlotV)                    \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -382,23 +382,20 @@ LIRGenerator::visitAdd(MAdd *ins)
     if (ins->specialization() == MIRType_Int32) {
         JS_ASSERT(lhs->type() == MIRType_Int32);
         ReorderCommutative(&lhs, &rhs);
         LAddI *lir = new LAddI;
         if (!assignSnapshot(lir))
             return false;
         return lowerForALU(lir, ins, lhs, rhs);
     }
-    if (ins->specialization() == MIRType_Double) {
-        JS_ASSERT(lhs->type() == MIRType_Double);
-        return lowerForFPU(new LMathD(JSOP_ADD), ins, lhs, rhs);
-    }
 
-    JS_NOT_REACHED("NYI");
-    return false;
+    JS_ASSERT(ins->specialization() == MIRType_Double);
+    JS_ASSERT(lhs->type() == MIRType_Double);
+    return lowerForFPU(new LMathD(JSOP_ADD), ins, lhs, rhs);
 }
 
 bool
 LIRGenerator::visitSub(MSub *ins)
 {
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
 
@@ -474,16 +471,50 @@ LIRGenerator::visitMod(MMod *ins)
     }
     // TODO: Implement for ins->specialization() == MIRType_Double
 
     JS_NOT_REACHED("NYI");
     return false;
 }
 
 bool
+LIRGenerator::visitAddGeneric(MAddGeneric *ins)
+{
+    MDefinition *lhs = ins->getOperand(0);
+    MDefinition *rhs = ins->getOperand(1);
+
+    JS_ASSERT(lhs->type() == MIRType_Value);
+    JS_ASSERT(rhs->type() == MIRType_Value);
+
+    LAddV *lir = new LAddV;
+    if (!useBoxAtStart(lir, LAddV::LhsInput, lhs))
+        return false;
+    if (!useBoxAtStart(lir, LAddV::RhsInput, rhs))
+        return false;
+    if (!defineVMReturn(lir, ins))
+        return false;
+    return assignSafepoint(lir, ins);
+}
+
+bool
+LIRGenerator::visitConcat(MConcat *ins)
+{
+    MDefinition *lhs = ins->getOperand(0);
+    MDefinition *rhs = ins->getOperand(1);
+
+    JS_ASSERT(lhs->type() == MIRType_String);
+    JS_ASSERT(rhs->type() == MIRType_String);
+
+    LConcat *lir = new LConcat(useRegisterAtStart(lhs), useRegisterAtStart(rhs));
+    if (!defineVMReturn(lir, ins))
+        return false;
+    return assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitStart(MStart *start)
 {
     // Create a snapshot that captures the initial state of the function.
     LStart *lir = new LStart;
     if (!assignSnapshot(lir))
         return false;
 
     if (start->startType() == MStart::StartType_Default)
@@ -620,16 +651,45 @@ LIRGenerator::visitTruncateToInt32(MTrun
         // Strings are complicated - we don't handle them yet.
         JS_NOT_REACHED("unexpected type");
     }
 
     return false;
 }
 
 bool
+LIRGenerator::visitToString(MToString *ins)
+{
+    MDefinition *opd = ins->input();
+
+    switch (opd->type()) {
+      case MIRType_Double:
+      case MIRType_Null:
+      case MIRType_Undefined:
+      case MIRType_Boolean:
+        JS_NOT_REACHED("NYI: Lower MToString");
+        break;
+
+      case MIRType_Int32: {
+        LIntToString *lir = new LIntToString(useRegisterAtStart(opd));
+
+        if (!defineVMReturn(lir, ins))
+            return false;
+        return assignSafepoint(lir, ins);
+      }
+
+      default:
+        // Objects might be effectful. (see ToPrimitive)
+        JS_NOT_REACHED("unexpected type");
+        break;
+    }
+    return false;
+}
+
+bool
 LIRGenerator::visitCopy(MCopy *ins)
 {
     JS_NOT_REACHED("unexpected copy");
     return false;
 }
 
 bool
 LIRGenerator::visitImplicitThis(MImplicitThis *ins)
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -76,16 +76,22 @@ class LIRGenerator : public LIRGenerator
     LIRGenerator(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph)
       : LIRGeneratorSpecific(gen, graph, lirGraph),
         argslots_(0), maxargslots_(0)
     { }
 
     bool generate();
 
   private:
+
+    bool useBoxAtStart(LInstruction *lir, size_t n, MDefinition *mir,
+                       LUse::Policy policy = LUse::REGISTER) {
+        return useBox(lir, n, mir, policy, true);
+    }
+
     bool lowerBitOp(JSOp op, MInstruction *ins);
     bool lowerShiftOp(JSOp op, MInstruction *ins);
     bool precreatePhi(LBlock *block, MPhi *phi);
     bool definePhis();
 
     // Allocate argument slots for a future function call.
     void allocateArguments(uint32 argc);
     // Map an MPassArg's argument number to a slot in the frame arg vector.
@@ -121,23 +127,26 @@ class LIRGenerator : public LIRGenerator
     bool visitLsh(MLsh *ins);
     bool visitRsh(MRsh *ins);
     bool visitUrsh(MUrsh *ins);
     bool visitAdd(MAdd *ins);
     bool visitSub(MSub *ins);
     bool visitMul(MMul *ins);
     bool visitDiv(MDiv *ins);
     bool visitMod(MMod *ins);
+    bool visitAddGeneric(MAddGeneric *ins);
+    bool visitConcat(MConcat *ins);
     bool visitStart(MStart *start);
     bool visitOsrEntry(MOsrEntry *entry);
     bool visitOsrValue(MOsrValue *value);
     bool visitOsrScopeChain(MOsrScopeChain *object);
     bool visitToDouble(MToDouble *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
+    bool visitToString(MToString *convert);
     bool visitCopy(MCopy *ins);
     bool visitImplicitThis(MImplicitThis *ins);
     bool visitSlots(MSlots *ins);
     bool visitElements(MElements *ins);
     bool visitLoadSlot(MLoadSlot *ins);
     bool visitFunctionEnvironment(MFunctionEnvironment *ins);
     bool visitStoreSlot(MStoreSlot *ins);
     bool visitTypeBarrier(MTypeBarrier *ins);
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -774,8 +774,14 @@ MToDouble::foldsTo(bool useValueNumbers)
             JS_ASSERT(ok);
 
             return MConstant::New(DoubleValue(out));
         }
     }
 
     return this;
 }
+
+MDefinition *
+MToString::foldsTo(bool useValueNumbers)
+{
+    return this;
+}
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -1038,16 +1038,25 @@ class MBinaryInstruction : public MAryIn
 {
   protected:
     MBinaryInstruction(MDefinition *left, MDefinition *right)
     {
         initOperand(0, left);
         initOperand(1, right);
     }
 
+  public:
+    MDefinition *lhs() const {
+        return getOperand(0);
+    }
+    MDefinition *rhs() const {
+        return getOperand(1);
+    }
+
+  protected:
     HashNumber valueHash() const
     {
         MDefinition *lhs = getOperand(0);
         MDefinition *rhs = getOperand(1);
 
         return op() ^ lhs->valueNumber() ^ rhs->valueNumber();
     }
 
@@ -1343,16 +1352,45 @@ class MTruncateToInt32 : public MUnaryIn
     MDefinition *input() const {
         return getOperand(0);
     }
     virtual AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+// Converts any type to a string
+class MToString : public MUnaryInstruction
+{
+    MToString(MDefinition *def)
+      : MUnaryInstruction(def)
+    {
+        setResultType(MIRType_String);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(ToString);
+    static MToString *New(MDefinition *def)
+    {
+        return new MToString(def);
+    }
+
+    MDefinition *input() const {
+        return getOperand(0);
+    }
+
+    MDefinition *foldsTo(bool useValueNumbers);
+
+    virtual AliasSet getAliasSet() const {
+        JS_ASSERT(input()->type() < MIRType_Object);
+        return AliasSet::None();
+    }
+};
+
 class MBitNot
   : public MUnaryInstruction,
     public BitwisePolicy
 {
   protected:
     MBitNot(MDefinition *input)
       : MUnaryInstruction(input)
     {
@@ -1563,22 +1601,16 @@ class MBinaryArithInstruction
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
     MIRType specialization() const {
         return specialization_;
     }
-    MDefinition *lhs() const {
-        return getOperand(0);
-    }
-    MDefinition *rhs() const {
-        return getOperand(1);
-    }
 
     MDefinition *foldsTo(bool useValueNumbers);
 
     virtual double getIdentity() = 0;
 
     void infer(const TypeOracle::Binary &b);
 
     virtual AliasSet getAliasSet() const {
@@ -1701,16 +1733,70 @@ class MMod : public MBinaryArithInstruct
 
     MDefinition *foldsTo(bool useValueNumbers);
     double getIdentity() {
         JS_NOT_REACHED("not used");
         return 1;
     }
 };
 
+class MAddGeneric :
+    public MBinaryInstruction,
+    public BoxInputsPolicy
+{
+    MAddGeneric(MDefinition *left, MDefinition *right)
+      : MBinaryInstruction(left, right)
+    {
+        setResultType(MIRType_Value);
+    }
+
+  public:
+    INSTRUCTION_HEADER(AddGeneric);
+    static MAddGeneric *New(MDefinition *left, MDefinition *right) {
+        return new MAddGeneric(left, right);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    bool congruentTo(MDefinition * const &ins) const {
+        return false;
+    }
+    virtual AliasSet getAliasSet() const {
+        return AliasSet::Store(AliasSet::Any);
+    }
+};
+
+class MConcat
+  : public MBinaryInstruction,
+    public BinaryStringPolicy
+{
+    MConcat(MDefinition *left, MDefinition *right)
+      : MBinaryInstruction(left, right)
+    {
+        setMovable();
+        setResultType(MIRType_String);
+    }
+
+  public:
+    INSTRUCTION_HEADER(Concat);
+    static MConcat *New(MDefinition *left, MDefinition *right) {
+        return new MConcat(left, right);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    virtual AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
 class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
 {
     js::Vector<MDefinition *, 2, IonAllocPolicy> inputs_;
     uint32 slot_;
     bool triedToSpecialize_;
 
     MPhi(uint32 slot)
       : slot_(slot),
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -69,23 +69,26 @@ namespace ion {
     _(Lsh)                                                                  \
     _(Rsh)                                                                  \
     _(Ursh)                                                                 \
     _(Add)                                                                  \
     _(Sub)                                                                  \
     _(Mul)                                                                  \
     _(Div)                                                                  \
     _(Mod)                                                                  \
+    _(AddGeneric)                                                           \
+    _(Concat)                                                               \
     _(Return)                                                               \
     _(Copy)                                                                 \
     _(Box)                                                                  \
     _(Unbox)                                                                \
     _(ToDouble)                                                             \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
+    _(ToString)                                                             \
     _(NewArray)                                                             \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
     _(ImplicitThis)                                                         \
     _(Slots)                                                                \
     _(Elements)                                                             \
     _(LoadSlot)                                                             \
     _(StoreSlot)                                                            \
--- a/js/src/ion/TypeOracle.cpp
+++ b/js/src/ion/TypeOracle.cpp
@@ -90,16 +90,28 @@ TypeInferenceOracle::unaryTypes(JSScript
     JS_ASSERT(script == this->script);
 
     UnaryTypes res;
     res.inTypes = script->analysis()->poppedTypes(pc, 0);
     res.outTypes = script->analysis()->pushedTypes(pc, 0);
     return res;
 }
 
+TypeOracle::BinaryTypes
+TypeInferenceOracle::binaryTypes(JSScript *script, jsbytecode *pc)
+{
+    JS_ASSERT(script == this->script);
+
+    BinaryTypes res;
+    res.lhsTypes = script->analysis()->poppedTypes(pc, 1);
+    res.rhsTypes = script->analysis()->poppedTypes(pc, 0);
+    res.outTypes = script->analysis()->pushedTypes(pc, 0);
+    return res;
+}
+
 TypeOracle::Unary
 TypeInferenceOracle::unaryOp(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(script == this->script);
 
     Unary res;
     res.ival = getMIRType(script->analysis()->poppedTypes(pc, 0));
     res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
--- a/js/src/ion/TypeOracle.h
+++ b/js/src/ion/TypeOracle.h
@@ -71,28 +71,35 @@ enum MIRType
 class TypeOracle
 {
   public:
     struct UnaryTypes {
         types::TypeSet *inTypes;
         types::TypeSet *outTypes;
     };
 
+    struct BinaryTypes {
+        types::TypeSet *lhsTypes;
+        types::TypeSet *rhsTypes;
+        types::TypeSet *outTypes;
+    };
+
     struct Unary {
         MIRType ival;
         MIRType rval;
     };
     struct Binary {
         MIRType lhs;
         MIRType rhs;
         MIRType rval;
     };
 
   public:
     virtual UnaryTypes unaryTypes(JSScript *script, jsbytecode *pc) = 0;
+    virtual BinaryTypes binaryTypes(JSScript *script, jsbytecode *pc) = 0;
     virtual Unary unaryOp(JSScript *script, jsbytecode *pc) = 0;
     virtual Binary binaryOp(JSScript *script, jsbytecode *pc) = 0;
     virtual types::TypeSet *thisTypeSet(JSScript *script) { return NULL; }
     virtual types::TypeSet *parameterTypeSet(JSScript *script, size_t index) { return NULL; }
     virtual types::TypeSet *globalPropertyTypeSet(JSScript *script, jsbytecode *pc, jsid id) {
         return NULL;
     }
     virtual types::TypeSet *propertyRead(JSScript *script, jsbytecode *pc) {
@@ -153,16 +160,23 @@ class DummyOracle : public TypeOracle
 {
   public:
     UnaryTypes unaryTypes(JSScript *script, jsbytecode *pc) {
         UnaryTypes u;
         u.inTypes = NULL;
         u.outTypes = NULL;
         return u;
     }
+    BinaryTypes binaryTypes(JSScript *script, jsbytecode *pc) {
+        BinaryTypes b;
+        b.lhsTypes = NULL;
+        b.rhsTypes = NULL;
+        b.outTypes = NULL;
+        return b;
+    }
     Unary unaryOp(JSScript *script, jsbytecode *pc) {
         Unary u;
         u.ival = MIRType_Int32;
         u.rval = MIRType_Int32;
         return u;
     }
     Binary binaryOp(JSScript *script, jsbytecode *pc) {
         Binary b;
@@ -181,16 +195,17 @@ class TypeInferenceOracle : public TypeO
     MIRType getMIRType(types::TypeSet *types);
 
   public:
     TypeInferenceOracle() : cx(NULL), script(NULL) {}
 
     bool init(JSContext *cx, JSScript *script);
 
     UnaryTypes unaryTypes(JSScript *script, jsbytecode *pc);
+    BinaryTypes binaryTypes(JSScript *script, jsbytecode *pc);
     Unary unaryOp(JSScript *script, jsbytecode *pc);
     Binary binaryOp(JSScript *script, jsbytecode *pc);
     types::TypeSet *thisTypeSet(JSScript *script);
     types::TypeSet *parameterTypeSet(JSScript *script, size_t index);
     types::TypeSet *globalPropertyTypeSet(JSScript *script, jsbytecode *pc, jsid id);
     types::TypeSet *propertyRead(JSScript *script, jsbytecode *pc);
     types::TypeSet *propertyReadBarrier(JSScript *script, jsbytecode *pc);
     types::TypeSet *globalPropertyWrite(JSScript *script, jsbytecode *pc, jsid id, bool *canSpecialize);
--- a/js/src/ion/TypePolicy.cpp
+++ b/js/src/ion/TypePolicy.cpp
@@ -98,16 +98,40 @@ BinaryArithPolicy::adjustInputs(MInstruc
         ins->block()->insertBefore(ins, replace);
         ins->replaceOperand(i, replace);
     }
 
     return true;
 }
 
 bool
+BinaryStringPolicy::adjustInputs(MInstruction *ins)
+{
+    for (size_t i = 0; i < 2; i++) {
+        MDefinition *in = ins->getOperand(i);
+        if (in->type() == MIRType_String)
+            continue;
+
+        MInstruction *replace = NULL;
+        if (in->type() == MIRType_Int32) {
+            replace = MToString::New(in);
+        } else {
+            if (in->type() != MIRType_Value)
+                in = boxAt(ins, in);
+            replace = MUnbox::New(in, MIRType_String, MUnbox::Fallible);
+        }
+
+        ins->block()->insertBefore(ins, replace);
+        ins->replaceOperand(i, replace);
+    }
+
+    return true;
+}
+
+bool
 ComparePolicy::adjustInputs(MInstruction *def)
 {
     if (specialization_ == MIRType_None)
         return BoxInputsPolicy::adjustInputs(def);
 
     for (size_t i = 0; i < 2; i++) {
         MDefinition *in = def->getOperand(i);
         if (in->type() == specialization_)
--- a/js/src/ion/TypePolicy.h
+++ b/js/src/ion/TypePolicy.h
@@ -81,16 +81,22 @@ class BinaryArithPolicy : public BoxInpu
     //  - == Any. Inputs are probably primitive.
     //  - == None. This op should not be specialized.
     MIRType specialization_;
 
   public:
     bool adjustInputs(MInstruction *def);
 };
 
+class BinaryStringPolicy : public BoxInputsPolicy
+{
+  public:
+    bool adjustInputs(MInstruction *def);
+};
+
 class BitwisePolicy : public BoxInputsPolicy
 {
   protected:
     // Specifies three levels of specialization:
     //  - < Value. This input is expected and required.
     //  - == Any. Inputs are probably primitive.
     //  - == None. This op should not be specialized.
     MIRType specialization_;
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -35,16 +35,18 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "Ion.h"
 #include "jsinterp.h"
+#include "ion/Snapshots.h"
+#include "ion/IonFrames.h"
 
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
 bool InvokeFunction(JSContext *cx, JSFunction *fun, uint32 argc, Value *argv, Value *rval)
@@ -63,11 +65,21 @@ bool InvokeFunction(JSContext *cx, JSFun
 bool ReportOverRecursed(JSContext *cx)
 {
     js_ReportOverRecursed(cx);
 
     // Cause an InternalError.
     return false;
 }
 
+bool AddValues(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
+{
+    FrameRecovery fr = FrameRecovery::FromFrameIterator(
+        IonFrameIterator(JS_THREAD_DATA(cx)->ionTop));
+    SnapshotIterator si(fr);
+    jsbytecode *pc = fr.script()->code + si.pcOffset();
+
+    return js::AddValues(cx, fr.script(), pc, lhs, rhs, res);
+}
+
 } // namespace ion
 } // namespace js
 
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -285,13 +285,16 @@ struct FunctionInfo<R (*)(JSContext *, A
 #undef COMPUTE_OUTPARAM_RESULT
 #undef COMPUTE_ARG_PROP
 #undef SEP_OR
 #undef NOTHING
 
 bool InvokeFunction(JSContext *cx, JSFunction *fun, uint32 argc, Value *argv, Value *rval);
 bool ReportOverRecursed(JSContext *cx);
 
+// Wrapper on top of the interpreter function.
+bool AddValues(JSContext *cx, const Value &lhs, const Value &rhs, Value *res);
+
 } // namespace ion
 } // namespace js
 
 #endif // jsion_vm_functions_h_
 
--- a/js/src/ion/arm/Lowering-arm.cpp
+++ b/js/src/ion/arm/Lowering-arm.cpp
@@ -44,23 +44,23 @@
 #include "Assembler-arm.h"
 #include "ion/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 bool
 LIRGeneratorARM::useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                        LUse::Policy policy)
+                        LUse::Policy policy, bool useAtStart)
 {
     JS_ASSERT(mir->type() == MIRType_Value);
     if (!ensureDefined(mir))
         return false;
-    lir->setOperand(n, LUse(mir->id(), policy));
-    lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy));
+    lir->setOperand(n, LUse(mir->id(), policy, useAtStart));
+    lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
     return true;
 }
 
 bool
 LIRGeneratorARM::lowerConstantDouble(double d, MInstruction *mir)
 {
     uint32 index;
     if (!lirGraph_.addConstantToPool(d, &index))
--- a/js/src/ion/arm/Lowering-arm.h
+++ b/js/src/ion/arm/Lowering-arm.h
@@ -53,17 +53,17 @@ class LIRGeneratorARM : public LIRGenera
     LIRGeneratorARM(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph)
       : LIRGeneratorShared(gen, graph, lirGraph)
     { }
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                LUse::Policy policy = LUse::REGISTER);
+                LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
 
     void lowerUntypedPhiInput(MPhi *phi, uint32 inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
     bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, 
                        MDefinition *rhs);
 
     bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
                      MDefinition *input);
--- a/js/src/ion/x64/Lowering-x64.cpp
+++ b/js/src/ion/x64/Lowering-x64.cpp
@@ -43,23 +43,24 @@
 #include "Lowering-x64.h"
 #include "Assembler-x64.h"
 #include "ion/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 bool
-LIRGeneratorX64::useBox(LInstruction *lir, size_t n, MDefinition *mir, LUse::Policy policy)
+LIRGeneratorX64::useBox(LInstruction *lir, size_t n, MDefinition *mir,
+                        LUse::Policy policy, bool useAtStart)
 {
     JS_ASSERT(mir->type() == MIRType_Value);
 
     if (!ensureDefined(mir))
         return false;
-    lir->setOperand(n, LUse(mir->virtualRegister(), policy));
+    lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
     return true;
 }
 
 bool
 LIRGeneratorX64::lowerConstantDouble(double d, MInstruction *mir)
 {
     return define(new LDouble(d), mir);
 }
--- a/js/src/ion/x64/Lowering-x64.h
+++ b/js/src/ion/x64/Lowering-x64.h
@@ -55,17 +55,17 @@ class LIRGeneratorX64 : public LIRGenera
     { }
 
   protected:
     void lowerUntypedPhiInput(MPhi *phi, uint32 inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
     // Adds a use at operand |n| of a value-typed insturction.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                LUse::Policy policy = LUse::REGISTER);
+                LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
 
     bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
                        MDefinition *rhs);
 
     bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input);
     bool lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
                      MDefinition *rhs);
     bool lowerForFPU(LMathD *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs);
--- a/js/src/ion/x86/Lowering-x86.cpp
+++ b/js/src/ion/x86/Lowering-x86.cpp
@@ -44,24 +44,24 @@
 #include "Assembler-x86.h"
 #include "ion/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 bool
 LIRGeneratorX86::useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                        LUse::Policy policy)
+                        LUse::Policy policy, bool useAtStart)
 {
     JS_ASSERT(mir->type() == MIRType_Value);
 
     if (!ensureDefined(mir))
         return false;
-    lir->setOperand(n, LUse(mir->virtualRegister(), policy));
-    lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy));
+    lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
+    lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
     return true;
 }
 
 bool
 LIRGeneratorX86::lowerConstantDouble(double d, MInstruction *mir)
 {
     uint32 index;
     if (!lirGraph_.addConstantToPool(d, &index))
--- a/js/src/ion/x86/Lowering-x86.h
+++ b/js/src/ion/x86/Lowering-x86.h
@@ -53,17 +53,17 @@ class LIRGeneratorX86 : public LIRGenera
     LIRGeneratorX86(MIRGenerator *gen, MIRGraph &graph, LIRGraph &lirGraph)
       : LIRGeneratorX86Shared(gen, graph, lirGraph)
     { }
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
-                LUse::Policy policy = LUse::REGISTER);
+                LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
 
     void lowerUntypedPhiInput(MPhi *phi, uint32 inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
     bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, 
                        MDefinition *rhs);
 
     bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2443,88 +2443,21 @@ BEGIN_CASE(JSOP_URSH)
     regs.sp--;
     if (!regs.sp[-1].setNumber(uint32_t(u)))
         TypeScript::MonitorOverflow(cx, script, regs.pc);
 }
 END_CASE(JSOP_URSH)
 
 BEGIN_CASE(JSOP_ADD)
 {
-    Value rval = regs.sp[-1];
-    Value lval = regs.sp[-2];
-
-    if (lval.isInt32() && rval.isInt32()) {
-        int32_t l = lval.toInt32(), r = rval.toInt32();
-        int32_t sum = l + r;
-        if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) {
-            regs.sp[-2].setDouble(double(l) + double(r));
-            TypeScript::MonitorOverflow(cx, script, regs.pc);
-        } else {
-            regs.sp[-2].setInt32(sum);
-        }
-        regs.sp--;
-    } else
-#if JS_HAS_XML_SUPPORT
-    if (IsXML(lval) && IsXML(rval)) {
-        if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), &rval))
-            goto error;
-        regs.sp[-2] = rval;
-        regs.sp--;
-        TypeScript::MonitorUnknown(cx, script, regs.pc);
-    } else
-#endif
-    {
-        /*
-         * If either operand is an object, any non-integer result must be
-         * reported to inference.
-         */
-        bool lIsObject = lval.isObject(), rIsObject = rval.isObject();
-
-        if (!ToPrimitive(cx, &lval))
-            goto error;
-        if (!ToPrimitive(cx, &rval))
-            goto error;
-        bool lIsString, rIsString;
-        if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
-            JSString *lstr, *rstr;
-            if (lIsString) {
-                lstr = lval.toString();
-            } else {
-                lstr = ToString(cx, lval);
-                if (!lstr)
-                    goto error;
-                regs.sp[-2].setString(lstr);
-            }
-            if (rIsString) {
-                rstr = rval.toString();
-            } else {
-                rstr = ToString(cx, rval);
-                if (!rstr)
-                    goto error;
-                regs.sp[-1].setString(rstr);
-            }
-            JSString *str = js_ConcatStrings(cx, lstr, rstr);
-            if (!str)
-                goto error;
-            if (lIsObject || rIsObject)
-                TypeScript::MonitorString(cx, script, regs.pc);
-            regs.sp[-2].setString(str);
-            regs.sp--;
-        } else {
-            double l, r;
-            if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))
-                goto error;
-            l += r;
-            if (!regs.sp[-2].setNumber(l) &&
-                (lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble()))) {
-                TypeScript::MonitorOverflow(cx, script, regs.pc);
-            }
-            regs.sp--;
-        }
-    }
+    Value &lval = regs.sp[-2];
+    Value &rval = regs.sp[-1];
+    if (!AddValues(cx, script, regs.pc, lval, rval, &regs.sp[-2]))
+        goto error;
+    regs.sp--;
 }
 END_CASE(JSOP_ADD)
 
 #define BINARY_OP(OP)                                                         \
     JS_BEGIN_MACRO                                                            \
         Value rval = regs.sp[-1];                                             \
         Value lval = regs.sp[-2];                                             \
         double d1, d2;                                                        \
@@ -5011,8 +4944,80 @@ js::GetScopeNameForTypeOf(JSContext *cx,
 
     if (!prop) {
         vp->setUndefined();
         return true;
     }
 
     return obj->getProperty(cx, name, vp);
 }
+
+bool
+js::AddValues(JSContext *cx, JSScript *script, jsbytecode *pc,
+              const Value &lhs, const Value &rhs, Value *res)
+{
+    Value rval = rhs;
+    Value lval = lhs;
+
+    if (lval.isInt32() && rval.isInt32()) {
+        int32_t l = lval.toInt32(), r = rval.toInt32();
+        int32_t sum = l + r;
+        if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) {
+            res->setDouble(double(l) + double(r));
+            TypeScript::MonitorOverflow(cx, script, pc);
+        } else {
+            res->setInt32(sum);
+        }
+    } else
+#if JS_HAS_XML_SUPPORT
+    if (IsXML(lval) && IsXML(rval)) {
+        if (!js_ConcatenateXML(cx, &lval.toObject(), &rval.toObject(), res))
+            return false;
+        TypeScript::MonitorUnknown(cx, script, pc);
+    } else
+#endif
+    {
+        /*
+         * If either operand is an object, any non-integer result must be
+         * reported to inference.
+         */
+        bool lIsObject = lval.isObject(), rIsObject = rval.isObject();
+
+        if (!ToPrimitive(cx, &lval))
+            return false;
+        if (!ToPrimitive(cx, &rval))
+            return false;
+        bool lIsString, rIsString;
+        if ((lIsString = lval.isString()) | (rIsString = rval.isString())) {
+            js::AutoStringRooter lstr(cx), rstr(cx);
+            if (lIsString) {
+                lstr.setString(lval.toString());
+            } else {
+                lstr.setString(ToString(cx, lval));
+                if (!lstr.string())
+                    return false;
+            }
+            if (rIsString) {
+                rstr.setString(rval.toString());
+            } else {
+                rstr.setString(ToString(cx, rval));
+                if (!rstr.string())
+                    return false;
+            }
+            JSString *str = js_ConcatStrings(cx, lstr.string(), rstr.string());
+            if (!str)
+                return false;
+            if (lIsObject || rIsObject)
+                TypeScript::MonitorString(cx, script, pc);
+            res->setString(str);
+        } else {
+            double l, r;
+            if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))
+                return false;
+            l += r;
+            if (!res->setNumber(l) &&
+                (lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble()))) {
+                TypeScript::MonitorOverflow(cx, script, pc);
+            }
+        }
+    }
+    return true;
+}
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -371,11 +371,15 @@ bool
 GetObjectProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp);
 
 bool
 GetScopeName(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp);
 
 bool
 GetScopeNameForTypeOf(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp);
 
+bool
+AddValues(JSContext *cx, JSScript *script, jsbytecode *pc,
+          const Value &lhs, const Value &rhs, Value *res);
+
 }  /* namespace js */
 
 #endif /* jsinterp_h___ */
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -531,17 +531,17 @@ ToCStringBuf::ToCStringBuf() :dbuf(NULL)
 }
 
 ToCStringBuf::~ToCStringBuf()
 {
     if (dbuf)
         UnwantedForeground::free_(dbuf);
 }
 
-JSString * JS_FASTCALL
+JSString *
 js_IntToString(JSContext *cx, int32_t si)
 {
     uint32_t ui;
     if (si >= 0) {
         if (StaticStrings::hasInt(si))
             return cx->runtime->staticStrings.getInt(si);
         ui = si;
     } else {
@@ -1154,17 +1154,17 @@ js_NumberToStringWithBase(JSContext *cx,
                      cbuf.dbuf && cbuf.dbuf == numStr);
     }
 
     JSFixedString *s = js_NewStringCopyZ(cx, numStr);
     c->dtoaCache.cache(base, d, s);
     return s;
 }
 
-JSString * JS_FASTCALL
+JSString *
 js_NumberToString(JSContext *cx, jsdouble d)
 {
     return js_NumberToStringWithBase(cx, d, 10);
 }
 
 namespace js {
 
 JSFixedString *
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -163,25 +163,25 @@ extern const char js_NaN_str[];
 extern const char js_isNaN_str[];
 extern const char js_isFinite_str[];
 extern const char js_parseFloat_str[];
 extern const char js_parseInt_str[];
 
 class JSString;
 class JSFixedString;
 
-extern JSString * JS_FASTCALL
+extern JSString *
 js_IntToString(JSContext *cx, jsint i);
 
 /*
  * When base == 10, this function implements ToString() as specified by
  * ECMA-262-5 section 9.8.1; but note that it handles integers specially for
  * performance.  See also js::NumberToCString().
  */
-extern JSString * JS_FASTCALL
+extern JSString *
 js_NumberToString(JSContext *cx, jsdouble d);
 
 namespace js {
 
 /*
  * Convert an integer or double (contained in the given value) to a string and
  * append to the given buffer.
  */
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -67,17 +67,17 @@ class MutatingRopeSegmentRange;
 
 /*
  * Utility for building a rope (lazy concatenation) of strings.
  */
 class RopeBuilder;
 
 }  /* namespace js */
 
-extern JSString * JS_FASTCALL
+extern JSString *
 js_ConcatStrings(JSContext *cx, JSString *s1, JSString *s2);
 
 extern JSString * JS_FASTCALL
 js_toLowerCase(JSContext *cx, JSString *str);
 
 extern JSString * JS_FASTCALL
 js_toUpperCase(JSContext *cx, JSString *str);
 
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -266,17 +266,17 @@ JSRope::flatten(JSContext *maybecx)
         return flattenInternal<WithIncrementalBarrier>(maybecx);
     else
         return flattenInternal<NoBarrier>(maybecx);
 #else
     return flattenInternal<NoBarrier>(maybecx);
 #endif
 }
 
-JSString * JS_FASTCALL
+JSString *
 js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
 {
     JS_ASSERT_IF(!left->isAtom(), left->compartment() == cx->compartment);
     JS_ASSERT_IF(!right->isAtom(), right->compartment() == cx->compartment);
 
     size_t leftLen = left->length();
     if (leftLen == 0)
         return right;