Bug 1103032: IonMonkey: Get LSubstr working with limited amount of available registers, r=jandem
authorHannes Verschore <hv1989@gmail.com>
Tue, 25 Nov 2014 12:43:21 +0100
changeset 241783 0164b161e0d88b50e1a24b301708ef973c68843f
parent 241782 351a308ffe20c33bb7d740d8925209226bce673a
child 241784 89edc525856a6f98cee69a4fba2b9552bcd68f0f
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1103032
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1103032: IonMonkey: Get LSubstr working with limited amount of available registers, r=jandem
js/src/jit/CodeGenerator.cpp
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/arm/Lowering-arm.h
js/src/jit/mips/Lowering-mips.cpp
js/src/jit/mips/Lowering-mips.h
js/src/jit/none/Lowering-none.h
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x64/Lowering-x64.h
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/Lowering-x86.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1212,18 +1212,18 @@ PrepareAndExecuteRegExp(JSContext *cx, M
     masm.storePtr(temp3, lazySourceAddress);
     masm.load32(Address(temp2, RegExpShared::offsetOfFlags()), temp3);
     masm.store32(temp3, Address(temp1, RegExpStatics::offsetOfLazyFlags()));
 
     return true;
 }
 
 static void
-CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
-                size_t fromWidth, size_t toWidth);
+CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
+                Register byteOpScratch, size_t fromWidth, size_t toWidth);
 
 static void
 CreateDependentString(MacroAssembler &masm, const JSAtomState &names,
                       bool latin1, Register string,
                       Register base, Register temp1, Register temp2,
                       BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
                       Label *failure)
 {
@@ -5916,18 +5916,18 @@ CodeGenerator::visitConcatPar(LConcatPar
     MOZ_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
     MOZ_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
     MOZ_ASSERT(output == CallTempReg5);
 
     return emitConcat(lir, lhs, rhs, output);
 }
 
 static void
-CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
-                size_t fromWidth, size_t toWidth)
+CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
+                Register byteOpScratch, size_t fromWidth, size_t toWidth)
 {
     // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
     // (checked below in debug builds), and when done |to| must point to the
     // next available char.
 
 #ifdef DEBUG
     Label ok;
     masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
@@ -5937,23 +5937,23 @@ CopyStringChars(MacroAssembler &masm, Re
 
     MOZ_ASSERT(fromWidth == 1 || fromWidth == 2);
     MOZ_ASSERT(toWidth == 1 || toWidth == 2);
     MOZ_ASSERT_IF(toWidth == 1, fromWidth == 1);
 
     Label start;
     masm.bind(&start);
     if (fromWidth == 2)
-        masm.load16ZeroExtend(Address(from, 0), scratch);
+        masm.load16ZeroExtend(Address(from, 0), byteOpScratch);
     else
-        masm.load8ZeroExtend(Address(from, 0), scratch);
+        masm.load8ZeroExtend(Address(from, 0), byteOpScratch);
     if (toWidth == 2)
-        masm.store16(scratch, Address(to, 0));
+        masm.store16(byteOpScratch, Address(to, 0));
     else
-        masm.store8(scratch, Address(to, 0));
+        masm.store8(byteOpScratch, Address(to, 0));
     masm.addPtr(Imm32(fromWidth), from);
     masm.addPtr(Imm32(toWidth), to);
     masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
 }
 
 static void
 CopyStringCharsMaybeInflate(MacroAssembler &masm, Register input, Register destChars,
                             Register temp1, Register temp2)
@@ -6060,18 +6060,22 @@ static const VMFunction SubstringKernelI
 
 bool CodeGenerator::visitSubstr(LSubstr *lir)
 {
     Register string = ToRegister(lir->string());
     Register begin = ToRegister(lir->begin());
     Register length = ToRegister(lir->length());
     Register output = ToRegister(lir->output());
     Register temp = ToRegister(lir->temp());
-    Register temp2 = ToRegister(lir->temp2());
     Register temp3 = ToRegister(lir->temp3());
+
+    // On x86 there are not enough registers. In that case reuse the string
+    // register as temporary.
+    Register temp2 = lir->temp2()->isBogusTemp() ? string : ToRegister(lir->temp2());
+
     Address stringFlags(string, JSString::offsetOfFlags());
 
     Label isLatin1, notInline, nonZero, isInlinedLatin1;
 
     // For every edge case use the C++ variant.
     // Note: we also use this upon allocation failure in newGCString and
     // newGCFatInlineString. To squeeze out even more performance those failures
     // can be handled by allocate in ool code and returning to jit code to fill
@@ -6105,35 +6109,43 @@ bool CodeGenerator::visitSubstr(LSubstr 
     Address outputStorage(output, JSInlineString::offsetOfInlineStorage());
 
     masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT),
                       &isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
                      Address(output, JSString::offsetOfFlags()));
         masm.computeEffectiveAddress(stringStorage, temp);
+        if (temp2 == string)
+            masm.push(string);
         BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
         masm.computeEffectiveAddress(chars, temp2);
         masm.computeEffectiveAddress(outputStorage, temp);
         CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t));
         masm.load32(Address(output, JSString::offsetOfLength()), length);
         masm.store16(Imm32(0), Address(temp, 0));
+        if (temp2 == string)
+            masm.pop(string);
         masm.jump(done);
     }
     masm.bind(&isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
                      Address(output, JSString::offsetOfFlags()));
+        if (temp2 == string)
+            masm.push(string);
         masm.computeEffectiveAddress(stringStorage, temp2);
         static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
         masm.addPtr(begin, temp2);
         masm.computeEffectiveAddress(outputStorage, temp);
         CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char));
         masm.load32(Address(output, JSString::offsetOfLength()), length);
         masm.store8(Imm32(0), Address(temp, 0));
+        if (temp2 == string)
+            masm.pop(string);
         masm.jump(done);
     }
 
     // Handle other cases with a DependentString.
     masm.bind(&notInline);
     masm.newGCString(output, temp, slowPath);
     masm.store32(length, Address(output, JSString::offsetOfLength()));
     masm.storePtr(string, Address(output, JSDependentString::offsetOfBase()));
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2177,31 +2177,16 @@ LIRGenerator::visitStringReplace(MString
 
     LStringReplace *lir = new(alloc()) LStringReplace(useRegisterOrConstantAtStart(ins->string()),
                                                       useRegisterAtStart(ins->pattern()),
                                                       useRegisterOrConstantAtStart(ins->replacement()));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
-LIRGenerator::visitSubstr(MSubstr *ins)
-{
-    // The last temporary need to be a register that can handle 8bit moves, but
-    // there is no way to signal that to register allocator, except to give a
-    // fixed temporary that is able to do this.
-    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
-                                         useRegister(ins->begin()),
-                                         useRegister(ins->length()),
-                                         temp(),
-                                         temp(),
-                                         tempFixed(CallTempReg1));
-    return define(lir, ins) && assignSafepoint(lir, ins);
-}
-
-bool
 LIRGenerator::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
         // If the function has a singleton type, this instruction will only be
         // executed once so we don't bother inlining it.
         //
         // If UseNewTypeForClone is true, we will assign a singleton type to
         // the clone and we have to clone the script, we can't do that inline.
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -141,17 +141,16 @@ class LIRGenerator : public LIRGenerator
     bool visitMul(MMul *ins);
     bool visitDiv(MDiv *ins);
     bool visitMod(MMod *ins);
     bool visitConcat(MConcat *ins);
     bool visitConcatPar(MConcatPar *ins);
     bool visitCharCodeAt(MCharCodeAt *ins);
     bool visitFromCharCode(MFromCharCode *ins);
     bool visitStringSplit(MStringSplit *ins);
-    bool visitSubstr(MSubstr *ins);
     bool visitStart(MStart *start);
     bool visitOsrEntry(MOsrEntry *entry);
     bool visitNop(MNop *nop);
     bool visitLimitedTruncate(MLimitedTruncate *nop);
     bool visitOsrValue(MOsrValue *value);
     bool visitOsrScopeChain(MOsrScopeChain *object);
     bool visitOsrReturnValue(MOsrReturnValue *value);
     bool visitOsrArgumentsObject(MOsrArgumentsObject *object);
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -50,16 +50,22 @@ LIRGeneratorARM::useByteOpRegister(MDefi
 }
 
 LAllocation
 LIRGeneratorARM::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     return useRegisterOrNonDoubleConstant(mir);
 }
 
+LDefinition
+LIRGeneratorARM::tempByteOpRegister()
+{
+    return temp();
+}
+
 bool
 LIRGeneratorARM::lowerConstantDouble(double d, MInstruction *mir)
 {
     return define(new(alloc()) LDouble(d), mir);
 }
 
 bool
 LIRGeneratorARM::lowerConstantFloat32(float d, MInstruction *mir)
@@ -669,8 +675,20 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHe
 
     LAsmJSAtomicBinopHeap *lir =
         new(alloc()) LAsmJSAtomicBinopHeap(useRegister(ptr),
                                            useRegister(ins->value()),
                                            LDefinition::BogusTemp());
 
     return define(lir, ins);
 }
+
+bool
+LIRGeneratorARM::visitSubstr(MSubstr *ins)
+{
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
+                                         useRegister(ins->begin()),
+                                         useRegister(ins->length()),
+                                         temp(),
+                                         temp(),
+                                         tempByteOpRegister());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -25,16 +25,17 @@ class LIRGeneratorARM : public LIRGenera
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on ARM all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
+    LDefinition tempByteOpRegister();
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
     bool needTempForPostBarrier() { return false; }
 
     // x64 has a scratch register, so no need for another temp for dispatch
@@ -106,16 +107,17 @@ class LIRGeneratorARM : public LIRGenera
     bool visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
     bool visitForkJoinGetSlice(MForkJoinGetSlice *ins);
     bool visitSimdTernaryBitwise(MSimdTernaryBitwise *ins);
     bool visitSimdSplatX4(MSimdSplatX4 *ins);
     bool visitSimdValueX4(MSimdValueX4 *ins);
     bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
     bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
+    bool visitSubstr(MSubstr *ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Lowering_arm_h */
--- a/js/src/jit/mips/Lowering-mips.cpp
+++ b/js/src/jit/mips/Lowering-mips.cpp
@@ -52,16 +52,22 @@ LIRGeneratorMIPS::useByteOpRegister(MDef
 }
 
 LAllocation
 LIRGeneratorMIPS::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     return useRegisterOrNonDoubleConstant(mir);
 }
 
+LDefinition
+LIRGeneratorMIPS::tempByteOpRegister()
+{
+    return temp();
+}
+
 bool
 LIRGeneratorMIPS::lowerConstantDouble(double d, MInstruction *mir)
 {
     return define(new(alloc()) LDouble(d), mir);
 }
 
 bool
 LIRGeneratorMIPS::lowerConstantFloat32(float d, MInstruction *mir)
@@ -521,16 +527,28 @@ LIRGeneratorMIPS::lowerTruncateFToInt32(
 {
     MDefinition *opd = ins->input();
     MOZ_ASSERT(opd->type() == MIRType_Float32);
 
     return define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
 }
 
 bool
+LIRGeneratorMIPS::visitSubstr(MSubstr *ins)
+{
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
+                                         useRegister(ins->begin()),
+                                         useRegister(ins->length()),
+                                         temp(),
+                                         temp(),
+                                         tempByteOpRegister());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGeneratorMIPS::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
 {
     MOZ_CRASH("NYI");
 }
 
 bool
 LIRGeneratorMIPS::visitForkJoinGetSlice(MForkJoinGetSlice *ins)
 {
--- a/js/src/jit/mips/Lowering-mips.h
+++ b/js/src/jit/mips/Lowering-mips.h
@@ -25,16 +25,17 @@ class LIRGeneratorMIPS : public LIRGener
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on MIPS all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
+    LDefinition tempByteOpRegister();
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
     bool needTempForPostBarrier() { return false; }
 
     // MIPS has a scratch register, so no need for another temp for dispatch
@@ -104,16 +105,17 @@ class LIRGeneratorMIPS : public LIRGener
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
     bool visitForkJoinGetSlice(MForkJoinGetSlice *ins);
     bool visitSimdTernaryBitwise(MSimdTernaryBitwise *ins);
     bool visitSimdSplatX4(MSimdSplatX4 *ins);
     bool visitSimdValueX4(MSimdValueX4 *ins);
     bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
     bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
+    bool visitSubstr(MSubstr *ins);
 };
 
 typedef LIRGeneratorMIPS LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_Lowering_mips_h */
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -24,16 +24,17 @@ class LIRGeneratorNone : public LIRGener
     bool useBox(LInstruction *, size_t, MDefinition *,
                 LUse::Policy a = LUse::REGISTER, bool b = false) {
         MOZ_CRASH();
     }
     bool useBoxFixed(LInstruction *, size_t, MDefinition *, Register, Register) { MOZ_CRASH(); }
 
     LAllocation useByteOpRegister(MDefinition *) { MOZ_CRASH(); }
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *) { MOZ_CRASH(); }
+    LDefinition tempByteOpRegister() { MOZ_CRASH(); }
     LDefinition tempToUnbox() { MOZ_CRASH(); }
     bool needTempForPostBarrier() { MOZ_CRASH(); }
     LDefinition tempForDispatchCache(MIRType v = MIRType_None) { MOZ_CRASH(); }
     void lowerUntypedPhiInput(MPhi *, uint32_t, LBlock *, size_t) { MOZ_CRASH(); }
     bool defineUntypedPhi(MPhi *, size_t) { MOZ_CRASH(); }
     bool lowerForShift(LInstructionHelper<1, 2, 0> *, MDefinition *, MDefinition *, MDefinition *) {
         MOZ_CRASH();
     }
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -45,16 +45,22 @@ LIRGeneratorX64::useByteOpRegister(MDefi
 
 LAllocation
 LIRGeneratorX64::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     return useRegisterOrNonDoubleConstant(mir);
 }
 
 LDefinition
+LIRGeneratorX64::tempByteOpRegister()
+{
+    return temp();
+}
+
+LDefinition
 LIRGeneratorX64::tempToUnbox()
 {
     return temp();
 }
 
 bool
 LIRGeneratorX64::visitBox(MBox *box)
 {
@@ -186,12 +192,24 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAs
 
 bool
 LIRGeneratorX64::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins);
 }
 
 bool
+LIRGeneratorX64::visitSubstr(MSubstr *ins)
+{
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
+                                         useRegister(ins->begin()),
+                                         useRegister(ins->length()),
+                                         temp(),
+                                         temp(),
+                                         tempByteOpRegister());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins)
 {
     MOZ_CRASH("NYI");
 }
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -27,16 +27,17 @@ class LIRGeneratorX64 : public LIRGenera
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register);
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on x64 all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
+    LDefinition tempByteOpRegister();
 
     LDefinition tempToUnbox();
 
     bool needTempForPostBarrier() { return false; }
 
     // x64 has a scratch register, so no need for another temp for dispatch
     // ICs.
     LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) {
@@ -48,16 +49,17 @@ class LIRGeneratorX64 : public LIRGenera
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
+    bool visitSubstr(MSubstr *ins);
 
     static bool allowInlineForkJoinGetSlice() {
         return true;
     }
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -71,16 +71,22 @@ LIRGeneratorX86::useByteOpRegister(MDefi
 }
 
 LAllocation
 LIRGeneratorX86::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     return useFixed(mir, eax);
 }
 
+LDefinition
+LIRGeneratorX86::tempByteOpRegister()
+{
+    return tempFixed(eax);
+}
+
 bool
 LIRGeneratorX86::visitBox(MBox *box)
 {
     MDefinition *inner = box->getOperand(0);
 
     // If the box wrapped a double, it needs a new register.
     if (IsFloatingPointType(inner->type()))
         return defineBox(new(alloc()) LBoxFloatingPoint(useRegisterAtStart(inner), tempCopy(inner, 0),
@@ -304,8 +310,23 @@ LIRGeneratorX86::visitStoreTypedArrayEle
     return add(lir, ins);
 }
 
 bool
 LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new(alloc()) LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins);
 }
+
+bool
+LIRGeneratorX86::visitSubstr(MSubstr *ins)
+{
+    // Due to lack of registers on x86, we reuse the string register as
+    // temporary. As a result we only need two temporary registers and take a
+    // bugos temporary as fifth argument.
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
+                                         useRegister(ins->begin()),
+                                         useRegister(ins->length()),
+                                         temp(),
+                                         LDefinition::BogusTemp(),
+                                         tempByteOpRegister());
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -28,16 +28,17 @@ class LIRGeneratorX86 : public LIRGenera
 
     // It's a trap! On x86, the 1-byte store can only use one of
     // {al,bl,cl,dl,ah,bh,ch,dh}. That means if the register allocator
     // gives us one of {edi,esi,ebp,esp}, we're out of luck. (The formatter
     // will assert on us.) Ideally, we'd just ask the register allocator to
     // give us one of {al,bl,cl,dl}. For now, just useFixed(al).
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
+    LDefinition tempByteOpRegister();
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
     bool needTempForPostBarrier() { return true; }
 
     LDefinition tempForDispatchCache(MIRType outputType = MIRType_None);
@@ -50,16 +51,17 @@ class LIRGeneratorX86 : public LIRGenera
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
+    bool visitSubstr(MSubstr *ins);
     bool lowerPhi(MPhi *phi);
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;