Backed out changeset 2e9f81c5d00c (bug 1052839) for ggc bustage
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 05 Nov 2014 14:03:58 +0100
changeset 214100 21402c47765a5ab35ebcc7749822daa4aa2c6d15
parent 214099 1f590c2c40f0b850c4062ee651e1fb21a76dff8e
child 214101 ab001e4e0efe7940830b07e00ee3e1d0e777fbd2
push id27771
push userryanvm@gmail.com
push dateWed, 05 Nov 2014 19:04:24 +0000
treeherdermozilla-central@305b4fecce99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1052839
milestone36.0a1
backs out2e9f81c5d00c0b5bc7df99645557eb9f4dd04283
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
Backed out changeset 2e9f81c5d00c (bug 1052839) for ggc bustage
js/src/builtin/String.js
js/src/builtin/Utilities.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jscntxt.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/vm/SelfHosting.cpp
js/src/vm/String.h
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -1,134 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*global intl_Collator: false, */
 
-/* ES6 Draft Oct 14, 2014 21.1.3.19 */
-function String_substring(start, end) {
-    // Steps 1-3.
-    CheckObjectCoercible(this);
-    var str = ToString(this);
-
-    // Step 4.
-    var len = str.length;
-
-    // Step 5.
-    var intStart = ToInteger(start);
-
-    // Step 6.
-    var intEnd = (end === undefined) ? len : ToInteger(end);
-
-    // Step 7.
-    var finalStart = std_Math_min(std_Math_max(intStart, 0), len);
-
-    // Step 8.
-    var finalEnd = std_Math_min(std_Math_max(intEnd, 0), len);
-
-    // Steps 9-10.
-    var from, to;
-    if (finalStart < finalEnd) {
-        from = finalStart;
-        to = finalEnd;
-    } else {
-        from = finalEnd;
-        to = finalStart;
-    }
-
-    // Step 11.
-    // While |from| and |to - from| are bounded to the length of |str| and this
-    // and thus definitely in the int32 range, they can still be typed as
-    // double. Eagerly truncate since SubstringKernel only accepts int32.
-    return SubstringKernel(str, from | 0, (to - from) | 0);
-}
-
-function String_static_substring(string, start, end) {
-    if (arguments.length < 1)
-        ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.substring');
-    return callFunction(String_substring, string, start, end);
-}
-
-/* ES6 Draft Oct 14, 2014 B.2.3.1 */
-function String_substr(start, length) {
-    // Steps 1-2.
-    CheckObjectCoercible(this);
-    var str = ToString(this);
-
-    // Steps 3-4.
-    var intStart = ToInteger(start);
-
-    // Steps 5-7.
-    var size = str.length;
-    // Use |size| instead of +Infinity to avoid performing calculations with
-    // doubles. (The result is the same either way.)
-    var end = (length === undefined) ? size : ToInteger(length);
-
-    // Step 8.
-    if (intStart < 0)
-        intStart = std_Math_max(intStart + size, 0);
-
-    // Step 9.
-    var resultLength = std_Math_min(std_Math_max(end, 0), size - intStart)
-
-    // Step 10.
-    if (resultLength <= 0)
-        return "";
-
-    // Step 11.
-    // While |intStart| and |resultLength| are bounded to the length of |str|
-    // and thus definitely in the int32 range, they can still be typed as
-    // double. Eagerly truncate since SubstringKernel only accepts int32.
-    return SubstringKernel(str, intStart | 0, resultLength | 0);
-}
-
-function String_static_substr(string, start, length) {
-    if (arguments.length < 1)
-        ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.substr');
-    return callFunction(String_substr, string, start, length);
-}
-
-/* ES6 Draft Oct 14, 2014 21.1.3.16 */
-function String_slice(start, end) {
-    // Steps 1-3.
-    CheckObjectCoercible(this);
-    var str = ToString(this);
-
-    // Step 4.
-    var len = str.length;
-
-    // Step 5.
-    var intStart = ToInteger(start);
-
-    // Step 6.
-    var intEnd = (end === undefined) ? len : ToInteger(end);
-
-    // Step 7.
-    var from = (intStart < 0) ? std_Math_max(len + intStart, 0) : std_Math_min(intStart, len);
-
-    // Step 8.
-    var to = (intEnd < 0) ? std_Math_max(len + intEnd, 0) : std_Math_min(intEnd, len);
-
-    // Step 9.
-    var span = std_Math_max(to - from, 0);
-
-    // Step 10.
-    // While |from| and |span| are bounded to the length of |str|
-    // and thus definitely in the int32 range, they can still be typed as
-    // double. Eagerly truncate since SubstringKernel only accepts int32.
-    return SubstringKernel(str, from | 0, span | 0);
-}
-
-function String_static_slice(string, start, end) {
-    if (arguments.length < 1)
-        ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.slice');
-    return callFunction(String_slice, string, start, end);
-}
-
 /* ES6 Draft September 5, 2013 21.1.3.3 */
 function String_codePointAt(pos) {
     // Steps 1-3.
     CheckObjectCoercible(this);
     var S = ToString(this);
 
     // Steps 4-5.
     var position = ToInteger(pos);
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -27,17 +27,16 @@
 
 // All C++-implemented standard builtins library functions used in self-hosted
 // code are installed via the std_functions JSFunctionSpec[] in
 // SelfHosting.cpp.
 //
 // The few items below here are either self-hosted or installing them under a
 // std_Foo name would require ugly contortions, so they just get aliased here.
 var std_Array_indexOf = ArrayIndexOf;
-var std_String_substring = String_substring;
 // WeakMap is a bare constructor without properties or methods.
 var std_WeakMap = WeakMap;
 // StopIteration is a bare constructor without properties or methods.
 var std_StopIteration = StopIteration;
 
 
 /********** List specification type **********/
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6006,120 +6006,16 @@ ConcatFatInlineString(MacroAssembler &ma
 
         if (mode == ParallelExecution)
             masm.pop(temp3);
     }
 
     masm.ret();
 }
 
-typedef JSString *(*SubstringKernelFn)(JSContext *cx, HandleString str, int32_t begin, int32_t len);
-static const VMFunction SubstringKernelInfo =
-    FunctionInfo<SubstringKernelFn>(SubstringKernel);
-
-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());
-    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
-    // in all data.
-    OutOfLineCode *ool = oolCallVM(SubstringKernelInfo, lir,
-                                   (ArgList(), string, begin, length),
-                                    StoreRegisterTo(output));
-    if (!ool)
-        return false;
-    Label *slowPath = ool->entry();
-    Label *done = ool->rejoin();
-
-    // Zero length, return emptystring.
-    masm.branchTest32(Assembler::NonZero, length, length, &nonZero);
-    const JSAtomState &names = GetIonContext()->runtime->names();
-    masm.movePtr(ImmGCPtr(names.empty), output);
-    masm.jump(done);
-
-    // Use slow path for ropes.
-    masm.bind(&nonZero);
-    static_assert(JSString::ROPE_FLAGS == 0,
-                  "rope flags must be zero for (flags & TYPE_FLAGS_MASK) == 0 "
-                  "to be a valid is-rope check");
-    masm.branchTest32(Assembler::Zero, stringFlags, Imm32(JSString::TYPE_FLAGS_MASK), slowPath);
-
-    // Handle inlined strings by creating a FatInlineString.
-    masm.branchTest32(Assembler::Zero, stringFlags, Imm32(JSString::INLINE_CHARS_BIT), &notInline);
-    masm.newGCFatInlineString(output, temp, slowPath);
-    masm.store32(length, Address(output, JSString::offsetOfLength()));
-    Address stringStorage(string, JSInlineString::offsetOfInlineStorage());
-    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);
-        BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
-        masm.computeEffectiveAddress(chars, begin);
-        masm.computeEffectiveAddress(outputStorage, temp);
-        CopyStringChars(masm, temp, begin, length, string, sizeof(char16_t), sizeof(char16_t));
-        masm.store16(Imm32(0), Address(temp, 0));
-        masm.jump(done);
-    }
-    masm.bind(&isInlinedLatin1);
-    {
-        masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
-                     Address(output, JSString::offsetOfFlags()));
-        masm.computeEffectiveAddress(stringStorage, temp);
-        static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
-        masm.addPtr(temp, begin);
-        masm.computeEffectiveAddress(outputStorage, temp);
-        CopyStringChars(masm, temp, begin, length, string, sizeof(char), sizeof(char));
-        masm.store8(Imm32(0), Address(temp, 0));
-        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()));
-
-    masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
-    {
-        masm.store32(Imm32(JSString::DEPENDENT_FLAGS), Address(output, JSString::offsetOfFlags()));
-        masm.loadPtr(Address(string, JSString::offsetOfNonInlineChars()), temp);
-        BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
-        masm.computeEffectiveAddress(chars, temp);
-        masm.storePtr(temp, Address(output, JSString::offsetOfNonInlineChars()));
-        masm.jump(done);
-    }
-    masm.bind(&isLatin1);
-    {
-        masm.store32(Imm32(JSString::DEPENDENT_FLAGS | JSString::LATIN1_CHARS_BIT),
-                     Address(output, JSString::offsetOfFlags()));
-        masm.loadPtr(Address(string, JSString::offsetOfNonInlineChars()), temp);
-        static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
-        masm.addPtr(begin, temp);
-        masm.storePtr(temp, Address(output, JSString::offsetOfNonInlineChars()));
-        masm.jump(done);
-    }
-
-    masm.bind(done);
-    return true;
-}
-
 JitCode *
 JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
 {
     MacroAssembler masm(cx);
 
     Register lhs = CallTempReg0;
     Register rhs = CallTempReg1;
     Register temp1 = CallTempReg2;
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -183,17 +183,16 @@ class CodeGenerator : public CodeGenerat
     bool visitSetArrayLength(LSetArrayLength *lir);
     bool visitTypedArrayLength(LTypedArrayLength *lir);
     bool visitTypedArrayElements(LTypedArrayElements *lir);
     bool visitTypedObjectElements(LTypedObjectElements *lir);
     bool visitSetTypedObjectOffset(LSetTypedObjectOffset *lir);
     bool visitTypedObjectProto(LTypedObjectProto *ins);
     bool visitTypedObjectUnsizedLength(LTypedObjectUnsizedLength *ins);
     bool visitStringLength(LStringLength *lir);
-    bool visitSubstr(LSubstr *lir);
     bool visitInitializedLength(LInitializedLength *lir);
     bool visitSetInitializedLength(LSetInitializedLength *lir);
     bool visitNotO(LNotO *ins);
     bool visitNotV(LNotV *ins);
     bool visitBoundsCheck(LBoundsCheck *lir);
     bool visitBoundsCheckRange(LBoundsCheckRange *lir);
     bool visitBoundsCheckLower(LBoundsCheckLower *lir);
     bool visitLoadFixedSlotV(LLoadFixedSlotV *ins);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -776,17 +776,16 @@ class IonBuilder
     InliningStatus inlineToInteger(CallInfo &callInfo);
     InliningStatus inlineToString(CallInfo &callInfo);
     InliningStatus inlineDump(CallInfo &callInfo);
     InliningStatus inlineHasClass(CallInfo &callInfo, const Class *clasp,
                                   const Class *clasp2 = nullptr,
                                   const Class *clasp3 = nullptr,
                                   const Class *clasp4 = nullptr);
     InliningStatus inlineIsConstructing(CallInfo &callInfo);
-    InliningStatus inlineSubstringKernel(CallInfo &callInfo);
 
     // Testing functions.
     InliningStatus inlineForceSequentialOrInParallelSection(CallInfo &callInfo);
     InliningStatus inlineBailout(CallInfo &callInfo);
     InliningStatus inlineAssertFloat32(CallInfo &callInfo);
 
     // Bind function.
     InliningStatus inlineBoundFunction(CallInfo &callInfo, JSFunction *target);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3405,46 +3405,16 @@ class LStringSplit : public LCallInstruc
     const LAllocation *separator() {
         return getOperand(1);
     }
     const MStringSplit *mir() const {
         return mir_->toStringSplit();
     }
 };
 
-class LSubstr : public LInstructionHelper<1, 3, 1>
-{
-  public:
-    LIR_HEADER(Substr)
-
-    LSubstr(const LAllocation &string, const LAllocation &begin, const LAllocation &length,
-            const LDefinition &temp)
-    {
-        setOperand(0, string);
-        setOperand(1, begin);
-        setOperand(2, length);
-        setTemp(0, temp);
-    }
-    const LAllocation *string() {
-        return getOperand(0);
-    }
-    const LAllocation *begin() {
-        return getOperand(1);
-    }
-    const LAllocation *length() {
-        return getOperand(2);
-    }
-    const LDefinition *temp() {
-        return getTemp(0);
-    }
-    const MStringSplit *mir() const {
-        return mir_->toStringSplit();
-    }
-};
-
 // Convert a 32-bit integer to a double.
 class LInt32ToDouble : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(Int32ToDouble)
 
     explicit LInt32ToDouble(const LAllocation &input) {
         setOperand(0, input);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -182,17 +182,16 @@
     _(OsrScopeChain)                \
     _(OsrReturnValue)               \
     _(OsrArgumentsObject)           \
     _(RegExp)                       \
     _(RegExpExec)                   \
     _(RegExpTest)                   \
     _(RegExpReplace)                \
     _(StringReplace)                \
-    _(Substr)                       \
     _(Lambda)                       \
     _(LambdaArrow)                  \
     _(LambdaForSingleton)           \
     _(LambdaPar)                    \
     _(Slots)                        \
     _(Elements)                     \
     _(ConvertElementsToDoubles)     \
     _(MaybeToDoubleElement)         \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2167,26 +2167,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)
-{
-    LSubstr *lir = new (alloc()) LSubstr(useFixed(ins->string(), CallTempReg1),
-                                         useRegister(ins->begin()),
-                                         useRegister(ins->length()),
-                                         temp());
-    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
@@ -140,17 +140,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/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -192,18 +192,16 @@ IonBuilder::inlineNativeCall(CallInfo &c
     if (native == intrinsic_IsObject)
         return inlineIsObject(callInfo);
     if (native == intrinsic_ToInteger)
         return inlineToInteger(callInfo);
     if (native == intrinsic_ToString)
         return inlineToString(callInfo);
     if (native == intrinsic_IsConstructing)
         return inlineIsConstructing(callInfo);
-    if (native == intrinsic_SubstringKernel)
-        return inlineSubstringKernel(callInfo);
 
     // TypedObject intrinsics.
     if (native == intrinsic_ObjectIsTypedObject)
         return inlineHasClass(callInfo,
                               &OutlineTransparentTypedObject::class_,
                               &OutlineOpaqueTypedObject::class_,
                               &InlineTransparentTypedObject::class_,
                               &InlineOpaqueTypedObject::class_);
@@ -1548,48 +1546,16 @@ IonBuilder::inlineStrReplace(CallInfo &c
     current->add(cte);
     current->push(cte);
     if (cte->isEffectful() && !resumeAfter(cte))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineSubstringKernel(CallInfo &callInfo)
-{
-    MOZ_ASSERT(callInfo.argc() == 3);
-    MOZ_ASSERT(!callInfo.constructing());
-
-    // Return: String.
-    if (getInlineReturnType() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    // Arg 0: String.
-    if (callInfo.getArg(0)->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    // Arg 1: Int.
-    if (callInfo.getArg(1)->type() != MIRType_Int32)
-        return InliningStatus_NotInlined;
-
-    // Arg 2: Int.
-    if (callInfo.getArg(2)->type() != MIRType_Int32)
-        return InliningStatus_NotInlined;
-
-    callInfo.setImplicitlyUsedUnchecked();
-
-    MSubstr *substr = MSubstr::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
-                                            callInfo.getArg(2));
-    current->add(substr);
-    current->push(substr);
-
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
 IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
 {
     uint32_t argc = callInfo.argc();
     if (argc < 3 || (argc % 3) != 0 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     /* Important:
      *
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6920,57 +6920,16 @@ class MStringReplace
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
-class MSubstr
-  : public MTernaryInstruction,
-    public Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>
-{
-  private:
-
-    MSubstr(MDefinition *string, MDefinition *begin, MDefinition *length)
-      : MTernaryInstruction(string, begin, length)
-    {
-        setResultType(MIRType_String);
-    }
-
-  public:
-    INSTRUCTION_HEADER(Substr);
-
-    static MSubstr *New(TempAllocator &alloc, MDefinition *string, MDefinition *begin,
-                        MDefinition *length)
-    {
-        return new(alloc) MSubstr(string, begin, length);
-    }
-
-    MDefinition *string() {
-        return getOperand(0);
-    }
-
-    MDefinition *begin() {
-        return getOperand(1);
-    }
-
-    MDefinition *length() {
-        return getOperand(2);
-    }
-
-    bool congruentTo(const MDefinition *ins) const {
-        return congruentIfOperandsEqual(ins);
-    }
-    AliasSet getAliasSet() const {
-        return AliasSet::None();
-    }
-};
-
 struct LambdaFunctionInfo
 {
     // The functions used in lambdas are the canonical original function in
     // the script, and are immutable except for delazification. Record this
     // information while still on the main thread to avoid races.
     AlwaysTenuredFunction fun;
     uint16_t flags;
     gc::Cell *scriptOrLazyScript;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -90,17 +90,16 @@ namespace jit {
     _(Mul)                                                                  \
     _(Div)                                                                  \
     _(Mod)                                                                  \
     _(Concat)                                                               \
     _(ConcatPar)                                                            \
     _(CharCodeAt)                                                           \
     _(FromCharCode)                                                         \
     _(StringSplit)                                                          \
-    _(Substr)                                                               \
     _(Return)                                                               \
     _(Throw)                                                                \
     _(Box)                                                                  \
     _(Unbox)                                                                \
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -319,17 +319,16 @@ class ParallelSafetyVisitor : public MDe
     SAFE_OP(PowHalf)
     UNSAFE_OP(RegExpTest)
     UNSAFE_OP(RegExpExec)
     UNSAFE_OP(RegExpReplace)
     UNSAFE_OP(StringReplace)
     UNSAFE_OP(CallInstanceOf)
     UNSAFE_OP(ProfilerStackOp)
     UNSAFE_OP(GuardString)
-    UNSAFE_OP(Substr)
     UNSAFE_OP(NewDeclEnvObject)
     UNSAFE_OP(In)
     UNSAFE_OP(InArray)
     SAFE_OP(GuardThreadExclusive)
     SAFE_OP(InterruptCheckPar)
     SAFE_OP(CheckOverRecursedPar)
     SAFE_OP(FunctionDispatch)
     SAFE_OP(TypeObjectDispatch)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -972,17 +972,16 @@ class AutoAssertNoException
 bool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_IsObject(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_ToString(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_IsConstructing(JSContext *cx, unsigned argc, Value *vp);
-bool intrinsic_SubstringKernel(JSContext *cx, unsigned argc, Value *vp);
 
 bool intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_DefineDataProperty(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp);
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -570,47 +570,42 @@ ValueToIntegerRange(JSContext *cx, Handl
             *out = INT32_MIN;
         else
             *out = int32_t(d);
     }
 
     return true;
 }
 
-JSString *
-js::SubstringKernel(JSContext *cx, HandleString str, int32_t beginInt, int32_t lengthInt)
+static JSString *
+DoSubstr(JSContext *cx, JSString *str, size_t begin, size_t len)
 {
-    MOZ_ASSERT(0 <= beginInt);
-    MOZ_ASSERT(0 <= lengthInt);
-    MOZ_ASSERT(beginInt <= str->length());
-    MOZ_ASSERT(lengthInt <= str->length() - beginInt);
-
-    uint32_t begin = beginInt;
-    uint32_t len = lengthInt;
-
     /*
      * Optimization for one level deep ropes.
      * This is common for the following pattern:
      *
      * while() {
      *   text = text.substr(0, x) + "bla" + text.substr(x)
      *   test.charCodeAt(x + 1)
      * }
      */
     if (str->isRope()) {
         JSRope *rope = &str->asRope();
 
         /* Substring is totally in leftChild of rope. */
-        if (begin + len <= rope->leftChild()->length())
-            return NewDependentString(cx, rope->leftChild(), begin, len);
+        if (begin + len <= rope->leftChild()->length()) {
+            str = rope->leftChild();
+            return NewDependentString(cx, str, begin, len);
+        }
 
         /* Substring is totally in rightChild of rope. */
         if (begin >= rope->leftChild()->length()) {
+            str = rope->rightChild();
             begin -= rope->leftChild()->length();
-            return NewDependentString(cx, rope->rightChild(), begin, len);
+            return NewDependentString(cx, str, begin, len);
         }
 
         /*
          * Requested substring is partly in the left and partly in right child.
          * Create a rope of substrings for both childs.
          */
         MOZ_ASSERT(begin < rope->leftChild()->length() &&
                    begin + len > rope->leftChild()->length());
@@ -628,16 +623,75 @@ js::SubstringKernel(JSContext *cx, Handl
             return nullptr;
 
         return JSRope::new_<CanGC>(cx, lhs, rhs, len);
     }
 
     return NewDependentString(cx, str, begin, len);
 }
 
+bool
+js::str_substring(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    JSString *str = ThisToStringForStringProto(cx, args);
+    if (!str)
+        return false;
+
+    int32_t length, begin, end;
+    if (args.length() > 0) {
+        end = length = int32_t(str->length());
+
+        if (args[0].isInt32()) {
+            begin = args[0].toInt32();
+        } else {
+            RootedString strRoot(cx, str);
+            if (!ValueToIntegerRange(cx, args[0], &begin))
+                return false;
+            str = strRoot;
+        }
+
+        if (begin < 0)
+            begin = 0;
+        else if (begin > length)
+            begin = length;
+
+        if (args.hasDefined(1)) {
+            if (args[1].isInt32()) {
+                end = args[1].toInt32();
+            } else {
+                RootedString strRoot(cx, str);
+                if (!ValueToIntegerRange(cx, args[1], &end))
+                    return false;
+                str = strRoot;
+            }
+
+            if (end > length) {
+                end = length;
+            } else {
+                if (end < 0)
+                    end = 0;
+                if (end < begin) {
+                    int32_t tmp = begin;
+                    begin = end;
+                    end = tmp;
+                }
+            }
+        }
+
+        str = DoSubstr(cx, str, size_t(begin), size_t(end - begin));
+        if (!str)
+            return false;
+    }
+
+    args.rval().setString(str);
+    return true;
+}
+
 template <typename CharT>
 static JSString *
 ToLowerCase(JSContext *cx, JSLinearString *str)
 {
     // Unlike toUpperCase, toLowerCase has the nice invariant that if the input
     // is a Latin1 string, the output is also a Latin1 string.
     UniquePtr<CharT[], JS::FreePolicy> newChars;
     size_t length = str->length();
@@ -3933,16 +3987,64 @@ js::str_split_string(JSContext *cx, Hand
 
     if (!aobj)
         return nullptr;
 
     aobj->setType(type);
     return aobj;
 }
 
+static bool
+str_substr(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedString str(cx, ThisToStringForStringProto(cx, args));
+    if (!str)
+        return false;
+
+    int32_t length, len, begin;
+    if (args.length() > 0) {
+        length = int32_t(str->length());
+        if (!ValueToIntegerRange(cx, args[0], &begin))
+            return false;
+
+        if (begin >= length) {
+            args.rval().setString(cx->runtime()->emptyString);
+            return true;
+        }
+        if (begin < 0) {
+            begin += length; /* length + INT_MIN will always be less than 0 */
+            if (begin < 0)
+                begin = 0;
+        }
+
+        if (args.hasDefined(1)) {
+            if (!ValueToIntegerRange(cx, args[1], &len))
+                return false;
+
+            if (len <= 0) {
+                args.rval().setString(cx->runtime()->emptyString);
+                return true;
+            }
+
+            if (uint32_t(length) < uint32_t(begin + len))
+                len = length - begin;
+        } else {
+            len = length - begin;
+        }
+
+        str = DoSubstr(cx, str, size_t(begin), size_t(len));
+        if (!str)
+            return false;
+    }
+
+    args.rval().setString(str);
+    return true;
+}
+
 /*
  * Python-esque sequence operations.
  */
 static bool
 str_concat(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSString *str = ThisToStringForStringProto(cx, args);
@@ -3969,30 +4071,97 @@ str_concat(JSContext *cx, unsigned argc,
                 return false;
         }
     }
 
     args.rval().setString(str);
     return true;
 }
 
+static bool
+str_slice(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() == 1 && args.thisv().isString() && args[0].isInt32()) {
+        JSString *str = args.thisv().toString();
+        size_t begin = args[0].toInt32();
+        size_t end = str->length();
+        if (begin <= end) {
+            size_t length = end - begin;
+            if (length == 0) {
+                str = cx->runtime()->emptyString;
+            } else {
+                str = (length == 1)
+                      ? cx->staticStrings().getUnitStringForElement(cx, str, begin)
+                      : NewDependentString(cx, str, begin, length);
+                if (!str)
+                    return false;
+            }
+            args.rval().setString(str);
+            return true;
+        }
+    }
+
+    RootedString str(cx, ThisToStringForStringProto(cx, args));
+    if (!str)
+        return false;
+
+    if (args.length() != 0) {
+        double begin, end, length;
+
+        if (!ToInteger(cx, args[0], &begin))
+            return false;
+        length = str->length();
+        if (begin < 0) {
+            begin += length;
+            if (begin < 0)
+                begin = 0;
+        } else if (begin > length) {
+            begin = length;
+        }
+
+        if (args.hasDefined(1)) {
+            if (!ToInteger(cx, args[1], &end))
+                return false;
+            if (end < 0) {
+                end += length;
+                if (end < 0)
+                    end = 0;
+            } else if (end > length) {
+                end = length;
+            }
+            if (end < begin)
+                end = begin;
+        } else {
+            end = length;
+        }
+
+        str = NewDependentString(cx, str, size_t(begin), size_t(end - begin));
+        if (!str)
+            return false;
+    }
+    args.rval().setString(str);
+    return true;
+}
+
 static const JSFunctionSpec string_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN("quote",             str_quote,             0,JSFUN_GENERIC_NATIVE),
     JS_FN(js_toSource_str,     str_toSource,          0,0),
 #endif
 
     /* Java-like methods. */
     JS_FN(js_toString_str,     js_str_toString,       0,0),
     JS_FN(js_valueOf_str,      js_str_toString,       0,0),
+    JS_FN("substring",         str_substring,         2,JSFUN_GENERIC_NATIVE),
     JS_FN("toLowerCase",       str_toLowerCase,       0,JSFUN_GENERIC_NATIVE),
     JS_FN("toUpperCase",       str_toUpperCase,       0,JSFUN_GENERIC_NATIVE),
     JS_FN("charAt",            js_str_charAt,         1,JSFUN_GENERIC_NATIVE),
     JS_FN("charCodeAt",        js_str_charCodeAt,     1,JSFUN_GENERIC_NATIVE),
-    JS_SELF_HOSTED_FN("substring", "String_substring", 2,0),
     JS_SELF_HOSTED_FN("codePointAt", "String_codePointAt", 1,0),
     JS_FN("contains",          str_contains,          1,JSFUN_GENERIC_NATIVE),
     JS_FN("indexOf",           str_indexOf,           1,JSFUN_GENERIC_NATIVE),
     JS_FN("lastIndexOf",       str_lastIndexOf,       1,JSFUN_GENERIC_NATIVE),
     JS_FN("startsWith",        str_startsWith,        1,JSFUN_GENERIC_NATIVE),
     JS_FN("endsWith",          str_endsWith,          1,JSFUN_GENERIC_NATIVE),
     JS_FN("trim",              str_trim,              0,JSFUN_GENERIC_NATIVE),
     JS_FN("trimLeft",          str_trimLeft,          0,JSFUN_GENERIC_NATIVE),
@@ -4009,21 +4178,21 @@ static const JSFunctionSpec string_metho
     JS_FN("normalize",         str_normalize,         0,JSFUN_GENERIC_NATIVE),
 #endif
 
     /* Perl-ish methods (search is actually Python-esque). */
     JS_FN("match",             str_match,             1,JSFUN_GENERIC_NATIVE),
     JS_FN("search",            str_search,            1,JSFUN_GENERIC_NATIVE),
     JS_FN("replace",           str_replace,           2,JSFUN_GENERIC_NATIVE),
     JS_FN("split",             str_split,             2,JSFUN_GENERIC_NATIVE),
-    JS_SELF_HOSTED_FN("substr", "String_substr",      2,0),
+    JS_FN("substr",            str_substr,            2,JSFUN_GENERIC_NATIVE),
 
     /* Python-esque sequence methods. */
     JS_FN("concat",            str_concat,            1,JSFUN_GENERIC_NATIVE),
-    JS_SELF_HOSTED_FN("slice", "String_slice",        2,0),
+    JS_FN("slice",             str_slice,             2,JSFUN_GENERIC_NATIVE),
 
     /* HTML string methods. */
     JS_SELF_HOSTED_FN("bold",     "String_bold",       0,0),
     JS_SELF_HOSTED_FN("italics",  "String_italics",    0,0),
     JS_SELF_HOSTED_FN("fixed",    "String_fixed",      0,0),
     JS_SELF_HOSTED_FN("strike",   "String_strike",     0,0),
     JS_SELF_HOSTED_FN("small",    "String_small",      0,0),
     JS_SELF_HOSTED_FN("big",      "String_big",        0,0),
@@ -4119,27 +4288,23 @@ js::str_fromCharCode_one_arg(JSContext *
         return false;
 
     rval.setString(str);
     return true;
 }
 
 static const JSFunctionSpec string_static_methods[] = {
     JS_FN("fromCharCode", js::str_fromCharCode, 1, 0),
-
-    JS_SELF_HOSTED_FN("fromCodePoint",   "String_static_fromCodePoint", 1,0),
-    JS_SELF_HOSTED_FN("raw",             "String_static_raw",           2,0),
-    JS_SELF_HOSTED_FN("substring",       "String_static_substring",     3,0),
-    JS_SELF_HOSTED_FN("substr",          "String_static_substr",        3,0),
-    JS_SELF_HOSTED_FN("slice",           "String_static_slice",         3,0),
+    JS_SELF_HOSTED_FN("fromCodePoint", "String_static_fromCodePoint", 1, 0),
+    JS_SELF_HOSTED_FN("raw", "String_static_raw", 2, 0),
 
     // This must be at the end because of bug 853075: functions listed after
     // self-hosted methods aren't available in self-hosted code.
 #if EXPOSE_INTL_API
-    JS_SELF_HOSTED_FN("localeCompare",   "String_static_localeCompare", 2,0),
+    JS_SELF_HOSTED_FN("localeCompare", "String_static_localeCompare", 2,0),
 #endif
     JS_FS_END
 };
 
 /* static */ Shape *
 StringObject::assignInitialShape(ExclusiveContext *cx, Handle<StringObject*> obj)
 {
     MOZ_ASSERT(obj->empty());
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -245,24 +245,16 @@ EqualChars(const Char1 *s1, const Char2 
     for (const Char1 *s1end = s1 + len; s1 < s1end; s1++, s2++) {
         if (*s1 != *s2)
             return false;
     }
     return true;
 }
 
 /*
- * Computes |str|'s substring for the range [beginInt, beginInt + lengthInt).
- * Negative, overlarge, swapped, etc. |beginInt| and |lengthInt| are forbidden
- * and constitute API misuse.
- */
-JSString *
-SubstringKernel(JSContext *cx, HandleString str, int32_t beginInt, int32_t lengthInt);
-
-/*
  * Inflate bytes in ASCII encoding to char16_t code units. Return null on error,
  * otherwise return the char16_t buffer that was malloc'ed. length is updated to
  * the length of the new string (in char16_t code units). A null char is
  * appended, but it is not included in the length.
  */
 extern char16_t *
 InflateString(ThreadSafeContext *cx, const char *bytes, size_t *length);
 
@@ -315,16 +307,19 @@ str_indexOf(JSContext *cx, unsigned argc
 
 extern bool
 str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp);
 
 extern bool
 str_startsWith(JSContext *cx, unsigned argc, Value *vp);
 
 extern bool
+str_substring(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
 str_toLowerCase(JSContext *cx, unsigned argc, Value *vp);
 
 extern bool
 str_toUpperCase(JSContext *cx, unsigned argc, Value *vp);
 
 } /* namespace js */
 
 extern bool
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -115,36 +115,16 @@ js::intrinsic_IsCallable(JSContext *cx, 
 static bool
 intrinsic_IsConstructor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(IsConstructor(args[0]));
     return true;
 }
 
-bool
-js::intrinsic_SubstringKernel(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args[0].isString());
-    MOZ_ASSERT(args[1].isInt32());
-    MOZ_ASSERT(args[2].isInt32());
-
-    RootedString str(cx, args[0].toString());
-    int32_t begin = args[1].toInt32();
-    int32_t length = args[2].toInt32();
-
-    JSString *substr = SubstringKernel(cx, str, begin, length);
-    if (!substr)
-        return false;
-
-    args.rval().setString(substr);
-    return true;
-}
-
 static bool
 intrinsic_OwnPropertyKeys(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return GetOwnPropertyKeys(cx, args,
                               JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
 }
 
@@ -1031,16 +1011,17 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("std_String_fromCharCode",             str_fromCharCode,             1,0),
     JS_FN("std_String_charCodeAt",               js_str_charCodeAt,            1,0),
     JS_FN("std_String_indexOf",                  str_indexOf,                  1,0),
     JS_FN("std_String_lastIndexOf",              str_lastIndexOf,              1,0),
     JS_FN("std_String_match",                    str_match,                    1,0),
     JS_FN("std_String_replace",                  str_replace,                  2,0),
     JS_FN("std_String_split",                    str_split,                    2,0),
     JS_FN("std_String_startsWith",               str_startsWith,               1,0),
+    JS_FN("std_String_substring",                str_substring,                2,0),
     JS_FN("std_String_toLowerCase",              str_toLowerCase,              0,0),
     JS_FN("std_String_toUpperCase",              str_toUpperCase,              0,0),
 
     JS_FN("std_WeakMap_has",                     WeakMap_has,                  1,0),
     JS_FN("std_WeakMap_get",                     WeakMap_get,                  2,0),
     JS_FN("std_WeakMap_set",                     WeakMap_set,                  2,0),
     JS_FN("std_WeakMap_delete",                  WeakMap_delete,               1,0),
     JS_FN("std_WeakMap_clear",                   WeakMap_clear,                0,0),
@@ -1055,17 +1036,16 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("OwnPropertyKeys",         intrinsic_OwnPropertyKeys,         1,0),
     JS_FN("ThrowError",              intrinsic_ThrowError,              4,0),
     JS_FN("AssertionFailed",         intrinsic_AssertionFailed,         1,0),
     JS_FN("SetScriptHints",          intrinsic_SetScriptHints,          2,0),
     JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       2,0),
     JS_FN("_IsConstructing",         intrinsic_IsConstructing,          0,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
-    JS_FN("SubstringKernel",         intrinsic_SubstringKernel,         3,0),
 
     JS_FN("UnsafePutElements",       intrinsic_UnsafePutElements,       3,0),
     JS_FN("_DefineDataProperty",     intrinsic_DefineDataProperty,      4,0),
     JS_FN("UnsafeSetReservedSlot",   intrinsic_UnsafeSetReservedSlot,   3,0),
     JS_FN("UnsafeGetReservedSlot",   intrinsic_UnsafeGetReservedSlot,   2,0),
     JS_FN("HaveSameClass",           intrinsic_HaveSameClass,           2,0),
     JS_FN("IsPackedArray",           intrinsic_IsPackedArray,           1,0),
 
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -470,19 +470,16 @@ class JSString : public js::gc::TenuredC
     static size_t offsetOfLength() {
         return offsetof(JSString, d.u1.length);
     }
     static size_t offsetOfFlags() {
         return offsetof(JSString, d.u1.flags);
     }
 
     static size_t offsetOfNonInlineChars() {
-        static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) ==
-                      offsetof(JSString, d.s.u2.nonInlineCharsLatin1),
-                      "nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");
         return offsetof(JSString, d.s.u2.nonInlineCharsTwoByte);
     }
 
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_STRING; }
 
 #ifdef DEBUG
     void dump();
     void dumpCharsNoNewline(FILE *fp=stderr);