Bug 899139 - Part 1: Refactor value-to-int logic into IonMacroAssembler. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Mon, 09 Sep 2013 18:55:52 -0700
changeset 159212 e9370865aae020dd07f00216f17787d7b6288d85
parent 159211 afa6f48ffe9cad472abf4d40d498576152ed2345
child 159213 12657c2feb9c73ab8a0a8e3e75014c301b9ca4e2
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs899139
milestone26.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 899139 - Part 1: Refactor value-to-int logic into IonMacroAssembler. (r=jandem)
js/src/jit/AsmJS.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/LIR-Common.h
js/src/jit/Lowering.cpp
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/shared/MacroAssembler-x86-shared.h
js/src/jit/x86/MacroAssembler-x86.h
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -5976,17 +5976,18 @@ GenerateFFIIonExit(ModuleCompiler &m, co
 #else
     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
 #endif
 
     switch (exit.sig().retType().which()) {
       case RetType::Void:
         break;
       case RetType::Signed:
-        masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert);
+        masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert,
+                                 /* -0 check */ false);
         break;
       case RetType::Double:
         masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert);
 #if defined(JS_CPU_ARM) && !defined(JS_CPU_ARM_HARDFP)
         masm.boxDouble(ReturnFloatReg, softfpReturnOperand);
 #endif
         break;
     }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -152,99 +152,46 @@ static const VMFunctionsModal StringToNu
     FunctionInfo<StringToNumberFn>(StringToNumber),
     FunctionInfo<StringToNumberParFn>(StringToNumberPar));
 
 bool
 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
 {
     ValueOperand operand = ToValue(lir, LValueToInt32::Input);
     Register output = ToRegister(lir->output());
-
-    Register tag = masm.splitTagForTest(operand);
-
-    Label done, simple, isInt32, isBool, isString, notDouble;
-    // Type-check switch.
+    FloatRegister temp = ToFloatRegister(lir->tempFloat());
+
     MDefinition *input;
     if (lir->mode() == LValueToInt32::NORMAL)
         input = lir->mirNormal()->input();
     else
         input = lir->mirTruncate()->input();
-    masm.branchEqualTypeIfNeeded(MIRType_Int32, input, tag, &isInt32);
-    masm.branchEqualTypeIfNeeded(MIRType_Boolean, input, tag, &isBool);
-    // Only convert strings to int if we are in a truncation context, like
-    // bitwise operations.
-    if (lir->mode() == LValueToInt32::TRUNCATE)
-        masm.branchEqualTypeIfNeeded(MIRType_String, input, tag, &isString);
-    masm.branchTestDouble(Assembler::NotEqual, tag, &notDouble);
-
-    // If the value is a double, see if it fits in a 32-bit int. We need to ask
-    // the platform-specific codegenerator to do this.
-    FloatRegister temp = ToFloatRegister(lir->tempFloat());
-    masm.unboxDouble(operand, temp);
-
-    Label fails, isDouble;
-    masm.bind(&isDouble);
+
+    Label fails;
     if (lir->mode() == LValueToInt32::TRUNCATE) {
-        if (!emitTruncateDouble(temp, output))
-            return false;
-    } else {
-        masm.convertDoubleToInt32(temp, output, &fails, lir->mirNormal()->canBeNegativeZero());
-    }
-    masm.jump(&done);
-
-    masm.bind(&notDouble);
-
-    if (lir->mode() == LValueToInt32::NORMAL) {
-        // If the value is not null, it's a string, object, or undefined,
-        // which we can't handle here.
-        masm.branchTestNull(Assembler::NotEqual, tag, &fails);
+        // We can only handle strings in truncation contexts, like bitwise
+        // operations.
+        if (input->mightBeType(MIRType_String)) {
+            Register stringReg = ToRegister(lir->temp());
+            OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), stringReg),
+                                           StoreFloatRegisterTo(temp));
+            if (!ool)
+                return false;
+
+            masm.truncateValueToInt32(operand, input, ool->entry(), ool->rejoin(), stringReg,
+                                      temp, output, &fails);
+        } else {
+            masm.truncateValueToInt32(operand, input, temp, output, &fails);
+        }
     } else {
-        // Test for object - then fallthrough to null, which will also handle
-        // undefined.
-        masm.branchEqualTypeIfNeeded(MIRType_Object, input, tag, &fails);
-    }
-
-    if (fails.used() && !bailoutFrom(&fails, lir->snapshot()))
-        return false;
-
-    // The value is null - just emit 0.
-    masm.mov(Imm32(0), output);
-    masm.jump(&done);
-
-    // Unbox a string, call StringToNumber to get a double back, and jump back
-    // to the snippet generated above about dealing with doubles.
-    if (isString.used()) {
-        masm.bind(&isString);
-        Register str = masm.extractString(operand, ToRegister(lir->temp()));
-        OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), str),
-                                       StoreFloatRegisterTo(temp));
-        if (!ool)
-            return false;
-
-        masm.jump(ool->entry());
-        masm.bind(ool->rejoin());
-        masm.jump(&isDouble);
-    }
-
-    // Just unbox a bool, the result is 0 or 1.
-    if (isBool.used()) {
-        masm.bind(&isBool);
-        masm.unboxBoolean(operand, output);
-        masm.jump(&done);
-    }
-
-    // Integers can be unboxed.
-    if (isInt32.used()) {
-        masm.bind(&isInt32);
-        masm.unboxInt32(operand, output);
-    }
-
-    masm.bind(&done);
-
-    return true;
+        masm.convertValueToInt32(operand, input, temp, output, &fails,
+                                 lir->mirNormal()->canBeNegativeZero());
+    }
+
+    return bailoutFrom(&fails, lir->snapshot());
 }
 
 static const double DoubleZero = 0.0;
 
 bool
 CodeGenerator::visitValueToDouble(LValueToDouble *lir)
 {
     MToDouble *mir = lir->mir();
@@ -6785,56 +6732,34 @@ CodeGenerator::visitClampDToUint8(LClamp
     Register output = ToRegister(lir->output());
     masm.clampDoubleToUint8(input, output);
     return true;
 }
 
 bool
 CodeGenerator::visitClampVToUint8(LClampVToUint8 *lir)
 {
-    ValueOperand input = ToValue(lir, LClampVToUint8::Input);
+    ValueOperand operand = ToValue(lir, LClampVToUint8::Input);
     FloatRegister tempFloat = ToFloatRegister(lir->tempFloat());
     Register output = ToRegister(lir->output());
-
-    Register tag = masm.splitTagForTest(input);
-
-    Label done;
-    Label isInt32, isDouble, isBoolean;
-    masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
-    masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
-    masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean);
-
-    // Undefined, null and objects are always 0.
-    Label isZero;
-    masm.branchTestUndefined(Assembler::Equal, tag, &isZero);
-    masm.branchTestNull(Assembler::Equal, tag, &isZero);
-    masm.branchTestObject(Assembler::Equal, tag, &isZero);
-
-    // Bailout for everything else (strings).
-    if (!bailout(lir->snapshot()))
+    MDefinition *input = lir->mir()->input();
+
+    Label fails;
+    if (input->mightBeType(MIRType_String)) {
+        OutOfLineCode *ool = oolCallVM(StringToNumberInfo, lir, (ArgList(), output),
+                                       StoreFloatRegisterTo(tempFloat));
+        masm.clampValueToUint8(operand, input, ool->entry(), ool->rejoin(), output,
+                               tempFloat, output, &fails);
+
+    } else {
+        masm.clampValueToUint8(operand, input, tempFloat, output, &fails);
+    }
+    if (!bailoutFrom(&fails, lir->snapshot()))
         return false;
 
-    masm.bind(&isInt32);
-    masm.unboxInt32(input, output);
-    masm.clampIntToUint8(output, output);
-    masm.jump(&done);
-
-    masm.bind(&isDouble);
-    masm.unboxDouble(input, tempFloat);
-    masm.clampDoubleToUint8(tempFloat, output);
-    masm.jump(&done);
-
-    masm.bind(&isBoolean);
-    masm.unboxBoolean(input, output);
-    masm.jump(&done);
-
-    masm.bind(&isZero);
-    masm.move32(Imm32(0), output);
-
-    masm.bind(&done);
     return true;
 }
 
 typedef bool (*OperatorInFn)(JSContext *, HandleValue, HandleObject, bool *);
 static const VMFunction OperatorInInfo = FunctionInfo<OperatorInFn>(OperatorIn);
 
 bool
 CodeGenerator::visitIn(LIn *ins)
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1371,16 +1371,17 @@ MacroAssembler::convertInt32ValueToDoubl
 {
     branchTestInt32(Assembler::NotEqual, address, done);
     unboxInt32(address, scratch);
     convertInt32ToDouble(scratch, ScratchFloatReg);
     storeDouble(ScratchFloatReg, address);
 }
 
 static const double DoubleZero = 0.0;
+static const double DoubleOne  = 1.0;
 
 void
 MacroAssembler::convertValueToDouble(ValueOperand value, FloatRegister output, Label *fail)
 {
     Register tag = splitTagForTest(value);
 
     Label isDouble, isInt32, isBool, isNull, done;
 
@@ -1406,49 +1407,55 @@ MacroAssembler::convertValueToDouble(Val
     int32ValueToDouble(value, output);
     jump(&done);
 
     bind(&isDouble);
     unboxDouble(value, output);
     bind(&done);
 }
 
-void
-MacroAssembler::convertValueToInt32(ValueOperand value, FloatRegister temp,
-                                    Register output, Label *fail)
+bool
+MacroAssembler::convertValueToDouble(JSContext *cx, const Value &v, FloatRegister output, Label *fail)
 {
-    Register tag = splitTagForTest(value);
-
-    Label done, simple, isInt32, isBool, isDouble;
+    if (v.isNumber() || v.isString()) {
+        double d;
+        if (v.isNumber())
+            d = v.toNumber();
+        else if (!StringToNumber(cx, v.toString(), &d))
+            return false;
 
-    branchTestInt32(Assembler::Equal, tag, &isInt32);
-    branchTestBoolean(Assembler::Equal, tag, &isBool);
-    branchTestDouble(Assembler::Equal, tag, &isDouble);
-    branchTestNull(Assembler::NotEqual, tag, fail);
+        if (d == js_NaN)
+            loadStaticDouble(&js_NaN, output);
+        else
+            loadConstantDouble(d, output);
 
-    // The value is null - just emit 0.
-    mov(Imm32(0), output);
-    jump(&done);
+        return true;
+    }
 
-    // Try converting double into integer
-    bind(&isDouble);
-    unboxDouble(value, temp);
-    convertDoubleToInt32(temp, output, fail, /* -0 check */ false);
-    jump(&done);
+    if (v.isBoolean()) {
+        if (v.toBoolean())
+            loadStaticDouble(&DoubleOne, output);
+        else
+            loadStaticDouble(&DoubleZero, output);
+        return true;
+    }
 
-    // Just unbox a bool, the result is 0 or 1.
-    bind(&isBool);
-    unboxBoolean(value, output);
-    jump(&done);
+    if (v.isNull()) {
+        loadStaticDouble(&DoubleZero, output);
+        return true;
+    }
 
-    // Integers can be unboxed.
-    bind(&isInt32);
-    unboxInt32(value, output);
+    if (v.isUndefined()) {
+        loadStaticDouble(&js_NaN, output);
+        return true;
+    }
 
-    bind(&done);
+    JS_ASSERT(v.isObject());
+    jump(fail);
+    return true;
 }
 
 void
 MacroAssembler::PushEmptyRooted(VMFunction::RootType rootType)
 {
     switch (rootType) {
       case VMFunction::RootNone:
         MOZ_ASSUME_UNREACHABLE("Handle must have root type");
@@ -1480,16 +1487,261 @@ MacroAssembler::popRooted(VMFunction::Ro
         Pop(cellReg);
         break;
       case VMFunction::RootValue:
         Pop(valueReg);
         break;
     }
 }
 
+bool
+MacroAssembler::convertConstantOrRegisterToDouble(JSContext *cx, ConstantOrRegister src,
+                                                  FloatRegister output, Label *fail)
+{
+    if (src.constant())
+        return convertValueToDouble(cx, src.value(), output, fail);
+
+    convertTypedOrValueToDouble(src.reg(), output, fail);
+    return true;
+}
+
+void
+MacroAssembler::convertTypedOrValueToDouble(TypedOrValueRegister src, FloatRegister output,
+                                            Label *fail)
+{
+    if (src.hasValue()) {
+        convertValueToDouble(src.valueReg(), output, fail);
+        return;
+    }
+
+    switch (src.type()) {
+      case MIRType_Null:
+        loadStaticDouble(&DoubleZero, output);
+        break;
+      case MIRType_Boolean:
+      case MIRType_Int32:
+        convertInt32ToDouble(src.typedReg().gpr(), output);
+        break;
+      case MIRType_Double:
+        if (src.typedReg().fpu() != output)
+            moveDouble(src.typedReg().fpu(), output);
+        break;
+      case MIRType_Object:
+      case MIRType_String:
+        jump(fail);
+        break;
+      case MIRType_Undefined:
+        loadStaticDouble(&js_NaN, output);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Bad MIRType");
+    }
+}
+
+void
+MacroAssembler::convertDoubleToInt(FloatRegister src, Register output, Label *fail,
+                                   IntConversionBehavior behavior)
+{
+    switch (behavior) {
+      case IntConversion_Normal:
+      case IntConversion_NegativeZeroCheck:
+        convertDoubleToInt32(src, output, fail, behavior == IntConversion_NegativeZeroCheck);
+        break;
+      case IntConversion_Truncate:
+        branchTruncateDouble(src, output, fail);
+        break;
+      case IntConversion_ClampToUint8:
+        clampDoubleToUint8(src, output);
+        break;
+    }
+}
+
+void
+MacroAssembler::convertValueToInt(ValueOperand value, MDefinition *maybeInput,
+                                  Label *handleStringEntry, Label *handleStringRejoin,
+                                  Register stringReg, FloatRegister temp, Register output,
+                                  Label *fail, IntConversionBehavior behavior)
+{
+    Register tag = splitTagForTest(value);
+    bool handleStrings = (behavior == IntConversion_Truncate ||
+                          behavior == IntConversion_ClampToUint8) &&
+                         handleStringEntry &&
+                         handleStringRejoin;
+    bool zeroObjects = behavior == IntConversion_ClampToUint8;
+
+    Label done, isInt32, isBool, isDouble, isNull, isString;
+
+    branchEqualTypeIfNeeded(MIRType_Int32, maybeInput, tag, &isInt32);
+    branchEqualTypeIfNeeded(MIRType_Boolean, maybeInput, tag, &isBool);
+    branchEqualTypeIfNeeded(MIRType_Double, maybeInput, tag, &isDouble);
+
+    // If we are not truncating, we fail for anything that's not
+    // null. Otherwise we might be able to handle strings and objects.
+    switch (behavior) {
+      case IntConversion_Normal:
+      case IntConversion_NegativeZeroCheck:
+        branchTestNull(Assembler::NotEqual, tag, fail);
+        break;
+
+      case IntConversion_Truncate:
+      case IntConversion_ClampToUint8:
+        branchEqualTypeIfNeeded(MIRType_Null, maybeInput, tag, &isNull);
+        if (handleStrings)
+            branchEqualTypeIfNeeded(MIRType_String, maybeInput, tag, &isString);
+        if (zeroObjects)
+            branchEqualTypeIfNeeded(MIRType_Object, maybeInput, tag, &isNull);
+        branchTestUndefined(Assembler::NotEqual, tag, fail);
+        break;
+    }
+
+    // The value is null or undefined in truncation contexts - just emit 0.
+    if (isNull.used())
+        bind(&isNull);
+    mov(Imm32(0), output);
+    jump(&done);
+
+    // Try converting a string into a double, then jump to the double case.
+    if (handleStrings) {
+        bind(&isString);
+        unboxString(value, stringReg);
+        jump(handleStringEntry);
+    }
+
+    // Try converting double into integer.
+    if (isDouble.used() || handleStrings) {
+        if (isDouble.used()) {
+            bind(&isDouble);
+            unboxDouble(value, temp);
+        }
+
+        if (handleStrings)
+            bind(handleStringRejoin);
+
+        convertDoubleToInt(temp, output, fail, behavior);
+        jump(&done);
+    }
+
+    // Just unbox a bool, the result is 0 or 1.
+    if (isBool.used()) {
+        bind(&isBool);
+        unboxBoolean(value, output);
+        jump(&done);
+    }
+
+    // Integers can be unboxed.
+    if (isInt32.used()) {
+        bind(&isInt32);
+        unboxInt32(value, output);
+        if (behavior == IntConversion_ClampToUint8)
+            clampIntToUint8(output, output);
+    }
+
+    bind(&done);
+}
+
+bool
+MacroAssembler::convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail,
+                                  IntConversionBehavior behavior)
+{
+    bool handleStrings = (behavior == IntConversion_Truncate ||
+                          behavior == IntConversion_ClampToUint8);
+    bool zeroObjects = behavior == IntConversion_ClampToUint8;
+
+    if (v.isNumber() || (handleStrings && v.isString())) {
+        double d;
+        if (v.isNumber())
+            d = v.toNumber();
+        else if (!StringToNumber(cx, v.toString(), &d))
+            return false;
+
+        switch (behavior) {
+          case IntConversion_Normal:
+          case IntConversion_NegativeZeroCheck: {
+            // -0 is checked anyways if we have a constant value.
+            int i;
+            if (mozilla::DoubleIsInt32(d, &i))
+                move32(Imm32(i), output);
+            else
+                jump(fail);
+            break;
+          }
+          case IntConversion_Truncate:
+            move32(Imm32(ToInt32(d)), output);
+            break;
+          case IntConversion_ClampToUint8:
+            move32(Imm32(ClampDoubleToUint8(d)), output);
+            break;
+        }
+
+        return true;
+    }
+
+    if (v.isBoolean()) {
+        move32(Imm32(v.toBoolean() ? 1 : 0), output);
+        return true;
+    }
+
+    if (v.isNull() || v.isUndefined()) {
+        move32(Imm32(0), output);
+        return true;
+    }
+
+    JS_ASSERT(v.isObject());
+
+    if (zeroObjects)
+        move32(Imm32(0), output);
+    else
+        jump(fail);
+    return true;
+}
+
+bool
+MacroAssembler::convertConstantOrRegisterToInt(JSContext *cx, ConstantOrRegister src,
+                                               FloatRegister temp, Register output,
+                                               Label *fail, IntConversionBehavior behavior)
+{
+    if (src.constant())
+        return convertValueToInt(cx, src.value(), output, fail, behavior);
+
+    convertTypedOrValueToInt(src.reg(), temp, output, fail, behavior);
+    return true;
+}
+
+void
+MacroAssembler::convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp,
+                                         Register output, Label *fail,
+                                         IntConversionBehavior behavior)
+{
+    if (src.hasValue()) {
+        convertValueToInt(src.valueReg(), temp, output, fail, behavior);
+        return;
+    }
+
+    switch (src.type()) {
+      case MIRType_Undefined:
+      case MIRType_Null:
+        move32(Imm32(0), output);
+        break;
+      case MIRType_Boolean:
+      case MIRType_Int32:
+        if (src.typedReg().gpr() != output)
+            move32(src.typedReg().gpr(), output);
+        break;
+      case MIRType_Double:
+        convertDoubleToInt(src.typedReg().fpu(), output, fail, behavior);
+        break;
+      case MIRType_String:
+      case MIRType_Object:
+        jump(fail);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Bad MIRType");
+    }
+}
+
 void
 MacroAssembler::finish()
 {
     if (sequentialFailureLabel_.used()) {
         bind(&sequentialFailureLabel_);
         handleFailure(SequentialExecution);
     }
     if (parallelFailureLabel_.used()) {
@@ -1537,20 +1789,20 @@ MacroAssembler::branchIfNotInterpretedCo
         branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::IS_FUN_PROTO << 16), &done);
         breakpoint();
 #endif
     }
     bind(&done);
 }
 
 void
-MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *def, const Register &tag,
+MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
                                         Label *label)
 {
-    if (def->mightBeType(type)) {
+    if (!maybeDef || maybeDef->mightBeType(type)) {
         switch (type) {
           case MIRType_Null:
             branchTestNull(Equal, tag, label);
             break;
           case MIRType_Boolean:
             branchTestBoolean(Equal, tag, label);
             break;
           case MIRType_Int32:
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -625,33 +625,34 @@ class MacroAssembler : public MacroAssem
     }
 
     // Inline version of js_TypedArray_uint8_clamp_double.
     // This function clobbers the input register.
     void clampDoubleToUint8(FloatRegister input, Register output);
 
     using MacroAssemblerSpecific::ensureDouble;
 
-    void ensureDouble(const Address &source, FloatRegister dest, Label *failure) {
+    template <typename S>
+    void ensureDouble(const S &source, FloatRegister dest, Label *failure) {
         Label isDouble, done;
         branchTestDouble(Assembler::Equal, source, &isDouble);
         branchTestInt32(Assembler::NotEqual, source, failure);
 
         convertInt32ToDouble(source, dest);
         jump(&done);
 
         bind(&isDouble);
         unboxDouble(source, dest);
 
         bind(&done);
     }
 
     // Emit type case branch on tag matching if the type tag in the definition
     // might actually be that type.
-    void branchEqualTypeIfNeeded(MIRType type, MDefinition *def, const Register &tag,
+    void branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
                                  Label *label);
 
     // Inline allocation.
     void newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail);
     void newGCThing(const Register &result, JSObject *templateObject, Label *fail);
     void newGCString(const Register &result, Label *fail);
     void newGCShortString(const Register &result, Label *fail);
 
@@ -992,17 +993,161 @@ class MacroAssembler : public MacroAssem
 #if JS_TRACE_LOGGING
     void tracelogStart(JSScript *script);
     void tracelogStop();
     void tracelogLog(TraceLogging::Type type);
 #endif
 
     void convertInt32ValueToDouble(const Address &address, Register scratch, Label *done);
     void convertValueToDouble(ValueOperand value, FloatRegister output, Label *fail);
-    void convertValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail);
+    bool convertValueToDouble(JSContext *cx, const Value &v, FloatRegister output, Label *fail);
+    bool convertConstantOrRegisterToDouble(JSContext *cx, ConstantOrRegister src,
+                                           FloatRegister output, Label *fail);
+    void convertTypedOrValueToDouble(TypedOrValueRegister src, FloatRegister output, Label *fail);
+
+    enum IntConversionBehavior {
+        IntConversion_Normal,
+        IntConversion_NegativeZeroCheck,
+        IntConversion_Truncate,
+        IntConversion_ClampToUint8,
+    };
+
+    //
+    // Functions for converting values to int.
+    //
+    void convertDoubleToInt(FloatRegister src, Register output, Label *fail,
+                            IntConversionBehavior behavior);
+
+    // Strings may be handled by providing labels to jump to when the behavior
+    // is truncation or clamping. The subroutine, usually an OOL call, is
+    // passed the unboxed string in |stringReg| and should convert it to a
+    // double store into |temp|.
+    void convertValueToInt(ValueOperand value, MDefinition *input,
+                           Label *handleStringEntry, Label *handleStringRejoin,
+                           Register stringReg, FloatRegister temp, Register output,
+                           Label *fail, IntConversionBehavior behavior);
+    void convertValueToInt(ValueOperand value, FloatRegister temp, Register output, Label *fail,
+                           IntConversionBehavior behavior)
+    {
+        convertValueToInt(value, NULL, NULL, NULL, InvalidReg, temp, output, fail, behavior);
+    }
+    bool convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail,
+                           IntConversionBehavior behavior);
+    bool convertConstantOrRegisterToInt(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
+                                        Register output, Label *fail, IntConversionBehavior behavior);
+    void convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp, Register output,
+                                  Label *fail, IntConversionBehavior behavior);
+
+    //
+    // Convenience functions for converting values to int32.
+    //
+    void convertValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail,
+                             bool negativeZeroCheck)
+    {
+        convertValueToInt(value, temp, output, fail, negativeZeroCheck
+                          ? IntConversion_NegativeZeroCheck
+                          : IntConversion_Normal);
+    }
+    void convertValueToInt32(ValueOperand value, MDefinition *input,
+                             FloatRegister temp, Register output, Label *fail,
+                             bool negativeZeroCheck)
+    {
+        convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
+                          negativeZeroCheck
+                          ? IntConversion_NegativeZeroCheck
+                          : IntConversion_Normal);
+    }
+    bool convertValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail,
+                             bool negativeZeroCheck)
+    {
+        return convertValueToInt(cx, v, output, fail, negativeZeroCheck
+                                 ? IntConversion_NegativeZeroCheck
+                                 : IntConversion_Normal);
+    }
+    bool convertConstantOrRegisterToInt32(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
+                                          Register output, Label *fail, bool negativeZeroCheck)
+    {
+        return convertConstantOrRegisterToInt(cx, src, temp, output, fail, negativeZeroCheck
+                                              ? IntConversion_NegativeZeroCheck
+                                              : IntConversion_Normal);
+    }
+    void convertTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
+                                    Label *fail, bool negativeZeroCheck)
+    {
+        convertTypedOrValueToInt(src, temp, output, fail, negativeZeroCheck
+                                 ? IntConversion_NegativeZeroCheck
+                                 : IntConversion_Normal);
+    }
+
+    //
+    // Convenience functions for truncating values to int32.
+    //
+    void truncateValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label *fail) {
+        convertValueToInt(value, temp, output, fail, IntConversion_Truncate);
+    }
+    void truncateValueToInt32(ValueOperand value, MDefinition *input,
+                              Label *handleStringEntry, Label *handleStringRejoin,
+                              Register stringReg, FloatRegister temp, Register output,
+                              Label *fail)
+    {
+        convertValueToInt(value, input, handleStringEntry, handleStringRejoin,
+                          stringReg, temp, output, fail, IntConversion_Truncate);
+    }
+    void truncateValueToInt32(ValueOperand value, MDefinition *input,
+                              FloatRegister temp, Register output, Label *fail)
+    {
+        convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
+                          IntConversion_Truncate);
+    }
+    bool truncateValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail) {
+        return convertValueToInt(cx, v, output, fail, IntConversion_Truncate);
+    }
+    bool truncateConstantOrRegisterToInt32(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
+                                           Register output, Label *fail)
+    {
+        return convertConstantOrRegisterToInt(cx, src, temp, output, fail, IntConversion_Truncate);
+    }
+    void truncateTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
+                                     Label *fail)
+    {
+        convertTypedOrValueToInt(src, temp, output, fail, IntConversion_Truncate);
+    }
+
+    // Convenience functions for clamping values to uint8.
+    void clampValueToUint8(ValueOperand value, FloatRegister temp, Register output, Label *fail) {
+        convertValueToInt(value, temp, output, fail, IntConversion_ClampToUint8);
+    }
+    void clampValueToUint8(ValueOperand value, MDefinition *input,
+                           Label *handleStringEntry, Label *handleStringRejoin,
+                           Register stringReg, FloatRegister temp, Register output,
+                           Label *fail)
+    {
+        convertValueToInt(value, input, handleStringEntry, handleStringRejoin,
+                          stringReg, temp, output, fail, IntConversion_ClampToUint8);
+    }
+    void clampValueToUint8(ValueOperand value, MDefinition *input,
+                           FloatRegister temp, Register output, Label *fail)
+    {
+        convertValueToInt(value, input, NULL, NULL, InvalidReg, temp, output, fail,
+                          IntConversion_ClampToUint8);
+    }
+    bool clampValueToUint8(JSContext *cx, const Value &v, Register output, Label *fail) {
+        return convertValueToInt(cx, v, output, fail, IntConversion_ClampToUint8);
+    }
+    bool clampConstantOrRegisterToUint8(JSContext *cx, ConstantOrRegister src, FloatRegister temp,
+                                        Register output, Label *fail)
+    {
+        return convertConstantOrRegisterToInt(cx, src, temp, output, fail,
+                                              IntConversion_ClampToUint8);
+    }
+    void clampTypedOrValueToUint8(TypedOrValueRegister src, FloatRegister temp, Register output,
+                                  Label *fail)
+    {
+        convertTypedOrValueToInt(src, temp, output, fail, IntConversion_ClampToUint8);
+    }
 };
 
 static inline Assembler::DoubleCondition
 JSOpToDoubleCondition(JSOp op)
 {
     switch (op) {
       case JSOP_EQ:
       case JSOP_STRICTEQ:
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3642,16 +3642,19 @@ class LClampVToUint8 : public LInstructi
         setTemp(0, tempFloat);
     }
 
     static const size_t Input = 0;
 
     const LDefinition *tempFloat() {
         return getTemp(0);
     }
+    const MClampToUint8 *mir() const {
+        return mir_->toClampToUint8();
+    }
 };
 
 // Load a boxed value from an object's fixed slot.
 class LLoadFixedSlotV : public LInstructionHelper<BOX_PIECES, 1, 0>
 {
   public:
     LIR_HEADER(LoadFixedSlotV)
     BOX_OUTPUT_ACCESSORS()
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2361,17 +2361,17 @@ LIRGenerator::visitClampToUint8(MClampTo
       case MIRType_Double:
         return define(new LClampDToUint8(useRegisterAtStart(in), tempCopy(in, 0)), ins);
 
       case MIRType_Value:
       {
         LClampVToUint8 *lir = new LClampVToUint8(tempFloat());
         if (!useBox(lir, LClampVToUint8::Input, in))
             return false;
-        return assignSnapshot(lir) && define(lir, ins);
+        return assignSnapshot(lir) && define(lir, ins) && assignSafepoint(lir, ins);
       }
 
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 bool
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1729,16 +1729,22 @@ MacroAssemblerARMCompat::andPtr(Register
     ma_and(src, dest);
 }
 
 void
 MacroAssemblerARMCompat::move32(const Imm32 &imm, const Register &dest)
 {
     ma_mov(imm, dest);
 }
+
+void
+MacroAssemblerARMCompat::move32(const Register &src, const Register &dest) {
+    ma_mov(src, dest);
+}
+
 void
 MacroAssemblerARMCompat::movePtr(const Register &src, const Register &dest)
 {
     ma_mov(src, dest);
 }
 void
 MacroAssemblerARMCompat::movePtr(const ImmWord &imm, const Register &dest)
 {
@@ -2578,16 +2584,26 @@ MacroAssemblerARMCompat::unboxDouble(con
 
 void
 MacroAssemblerARMCompat::unboxDouble(const Address &src, const FloatRegister &dest)
 {
     ma_vldr(Operand(src), dest);
 }
 
 void
+MacroAssemblerARMCompat::unboxString(const ValueOperand &operand, const Register &dest) {
+    ma_mov(operand.payloadReg(), dest);
+}
+
+void
+MacroAssemblerARMCompat::unboxString(const Address &src, const Register &dest) {
+    ma_ldr(payloadOf(src), dest);
+}
+
+void
 MacroAssemblerARMCompat::unboxValue(const ValueOperand &src, AnyRegister dest)
 {
     if (dest.isFloat()) {
         Label notInt32, end;
         branchTestInt32(Assembler::NotEqual, src, &notInt32);
         convertInt32ToDouble(src.payloadReg(), dest.fpu());
         ma_b(&end);
         bind(&notInt32);
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -713,16 +713,18 @@ class MacroAssemblerARMCompat : public M
 
     // unboxing code
     void unboxInt32(const ValueOperand &operand, const Register &dest);
     void unboxInt32(const Address &src, const Register &dest);
     void unboxBoolean(const ValueOperand &operand, const Register &dest);
     void unboxBoolean(const Address &src, const Register &dest);
     void unboxDouble(const ValueOperand &operand, const FloatRegister &dest);
     void unboxDouble(const Address &src, const FloatRegister &dest);
+    void unboxString(const ValueOperand &operand, const Register &dest);
+    void unboxString(const Address &src, const Register &dest);
     void unboxValue(const ValueOperand &src, AnyRegister dest);
     void unboxPrivate(const ValueOperand &src, Register dest);
 
     void notBoolean(const ValueOperand &val) {
         ma_eor(Imm32(1), val.payloadReg());
     }
 
     // boxing code
@@ -1171,16 +1173,17 @@ class MacroAssemblerARMCompat : public M
     void orPtr(Imm32 imm, Register dest);
     void orPtr(Register src, Register dest);
     void andPtr(Imm32 imm, Register dest);
     void andPtr(Register src, Register dest);
     void addPtr(Register src, Register dest);
     void addPtr(const Address &src, Register dest);
 
     void move32(const Imm32 &imm, const Register &dest);
+    void move32(const Register &src, const Register &dest);
 
     void movePtr(const Register &src, const Register &dest);
     void movePtr(const ImmWord &imm, const Register &dest);
     void movePtr(const ImmGCPtr &imm, const Register &dest);
 
     void load8SignExtend(const Address &address, const Register &dest);
     void load8SignExtend(const BaseIndex &src, const Register &dest);
 
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -94,16 +94,19 @@ class MacroAssemblerX86Shared : public A
         if (imm.value == 0)
             xorl(dest, dest);
         else
             movl(imm, dest);
     }
     void move32(const Imm32 &imm, const Operand &dest) {
         movl(imm, dest);
     }
+    void move32(const Register &src, const Register &dest) {
+        movl(src, dest);
+    }
     void and32(const Imm32 &imm, const Register &dest) {
         andl(imm, dest);
     }
     void and32(const Imm32 &imm, const Address &dest) {
         andl(imm, Operand(dest));
     }
     void or32(const Imm32 &imm, const Register &dest) {
         orl(imm, dest);
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -769,16 +769,22 @@ class MacroAssemblerX86 : public MacroAs
         } else {
             movl(payload, scratch);
             movd(scratch, dest);
             movl(type, scratch);
             movd(scratch, ScratchFloatReg);
             unpcklps(ScratchFloatReg, dest);
         }
     }
+    void unboxString(const ValueOperand &src, const Register &dest) {
+        movl(src.payloadReg(), dest);
+    }
+    void unboxString(const Address &src, const Register &dest) {
+        movl(payloadOf(src), dest);
+    }
     void unboxValue(const ValueOperand &src, AnyRegister dest) {
         if (dest.isFloat()) {
             Label notInt32, end;
             branchTestInt32(Assembler::NotEqual, src, &notInt32);
             cvtsi2sd(src.payloadReg(), dest.fpu());
             jump(&end);
             bind(&notInt32);
             unboxDouble(src, dest.fpu());