Bug 892634 - Part 3: Supporting strings in LValueToInt32 in truncation contexts. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Fri, 02 Aug 2013 08:24:58 -0700
changeset 153428 701af9ee77c8720d305e037fb4b2af55bdeb1728
parent 153427 aba98905fc13482fa3c649f9ea54138185b9b517
child 153429 e21faadc79429c6bb06f213851bcdd804a8d3ed1
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs892634
milestone25.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 892634 - Part 3: Supporting strings in LValueToInt32 in truncation contexts. (r=jandem)
js/src/ion/CodeGenerator.cpp
js/src/ion/IonMacroAssembler.cpp
js/src/ion/IonMacroAssembler.h
js/src/ion/LIR-Common.h
js/src/ion/Lowering.cpp
js/src/ion/ParallelFunctions.cpp
js/src/ion/ParallelFunctions.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -139,76 +139,106 @@ CodeGenerator::CodeGenerator(MIRGenerato
 {
 }
 
 CodeGenerator::~CodeGenerator()
 {
     js_delete(unassociatedScriptCounts_);
 }
 
+typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *);
+typedef ParallelResult (*StringToNumberParFn)(ForkJoinSlice *, JSString *, double *);
+static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal(
+    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, notDouble;
+    Label done, simple, isInt32, isBool, isString, notDouble;
     // Type-check switch.
-    masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
-    masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
+    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;
-    switch (lir->mode()) {
-      case LValueToInt32::TRUNCATE:
+    Label fails, isDouble;
+    masm.bind(&isDouble);
+    if (lir->mode() == LValueToInt32::TRUNCATE) {
         if (!emitTruncateDouble(temp, output))
             return false;
-        break;
-      default:
-        JS_ASSERT(lir->mode() == LValueToInt32::NORMAL);
-        masm.convertDoubleToInt32(temp, output, &fails, lir->mir()->canBeNegativeZero());
-        break;
+    } 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);
     } else {
-        // Test for string or object - then fallthrough to null, which will
-        // also handle undefined.
-        masm.branchTestObject(Assembler::Equal, tag, &fails);
-        masm.branchTestString(Assembler::Equal, tag, &fails);
+        // 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.
-    masm.bind(&isBool);
-    masm.unboxBoolean(operand, output);
-    masm.jump(&done);
+    if (isBool.used()) {
+        masm.bind(&isBool);
+        masm.unboxBoolean(operand, output);
+        masm.jump(&done);
+    }
 
     // Integers can be unboxed.
-    masm.bind(&isInt32);
-    masm.unboxInt32(operand, output);
+    if (isInt32.used()) {
+        masm.bind(&isInt32);
+        masm.unboxInt32(operand, output);
+    }
 
     masm.bind(&done);
 
     return true;
 }
 
 static const double DoubleZero = 0.0;
 
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -1400,8 +1400,38 @@ MacroAssembler::popRooted(VMFunction::Ro
       case VMFunction::RootCell:
         Pop(cellReg);
         break;
       case VMFunction::RootValue:
         Pop(valueReg);
         break;
     }
 }
+
+void
+MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *def, const Register &tag,
+                                        Label *label)
+{
+    if (def->mightBeType(type)) {
+        switch (type) {
+          case MIRType_Null:
+            branchTestNull(Equal, tag, label);
+            break;
+          case MIRType_Boolean:
+            branchTestBoolean(Equal, tag, label);
+            break;
+          case MIRType_Int32:
+            branchTestInt32(Equal, tag, label);
+            break;
+          case MIRType_Double:
+            branchTestDouble(Equal, tag, label);
+            break;
+          case MIRType_String:
+            branchTestString(Equal, tag, label);
+            break;
+          case MIRType_Object:
+            branchTestObject(Equal, tag, label);
+            break;
+          default:
+            MOZ_ASSUME_UNREACHABLE("Unsupported type");
+        }
+    }
+}
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -611,16 +611,21 @@ class MacroAssembler : public MacroAssem
         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,
+                                 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);
 
     void newGCThingPar(const Register &result, const Register &slice,
                        const Register &tempReg1, const Register &tempReg2,
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -2482,54 +2482,63 @@ class LValueToDouble : public LInstructi
         return mir_->toToDouble();
     }
 };
 
 // Convert a value to an int32.
 //   Input: components of a Value
 //   Output: 32-bit integer
 //   Bailout: undefined, string, object, or non-int32 double
-//   Temps: one float register
+//   Temps: one float register, one GP register
 //
 // This instruction requires a temporary float register.
-class LValueToInt32 : public LInstructionHelper<1, BOX_PIECES, 1>
+class LValueToInt32 : public LInstructionHelper<1, BOX_PIECES, 2>
 {
   public:
     enum Mode {
         NORMAL,
         TRUNCATE
     };
 
   private:
     Mode mode_;
 
   public:
     LIR_HEADER(ValueToInt32)
 
-    LValueToInt32(const LDefinition &temp, Mode mode)
+    LValueToInt32(const LDefinition &temp0, const LDefinition &temp1, Mode mode)
       : mode_(mode)
     {
-        setTemp(0, temp);
+        setTemp(0, temp0);
+        setTemp(1, temp1);
     }
 
     const char *extraName() const {
         return mode() == NORMAL ? "Normal" : "Truncate";
     }
 
     static const size_t Input = 0;
 
     Mode mode() const {
         return mode_;
     }
     const LDefinition *tempFloat() {
         return getTemp(0);
     }
-    MToInt32 *mir() const {
+    const LDefinition *temp() {
+        return getTemp(1);
+    }
+    MToInt32 *mirNormal() const {
+        JS_ASSERT(mode_ == NORMAL);
         return mir_->toToInt32();
     }
+    MTruncateToInt32 *mirTruncate() const {
+        JS_ASSERT(mode_ == TRUNCATE);
+        return mir_->toTruncateToInt32();
+    }
 };
 
 // Convert a double to an int32.
 //   Input: floating-point register
 //   Output: 32-bit integer
 //   Bailout: if the double cannot be converted to an integer.
 class LDoubleToInt32 : public LInstructionHelper<1, 1, 0>
 {
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -1536,20 +1536,20 @@ LIRGenerator::visitToDouble(MToDouble *c
 bool
 LIRGenerator::visitToInt32(MToInt32 *convert)
 {
     MDefinition *opd = convert->input();
 
     switch (opd->type()) {
       case MIRType_Value:
       {
-        LValueToInt32 *lir = new LValueToInt32(tempFloat(), LValueToInt32::NORMAL);
+        LValueToInt32 *lir = new LValueToInt32(tempFloat(), temp(), LValueToInt32::NORMAL);
         if (!useBox(lir, LValueToInt32::Input, opd))
             return false;
-        return assignSnapshot(lir) && define(lir, convert);
+        return assignSnapshot(lir) && define(lir, convert) && assignSafepoint(lir, convert);
       }
 
       case MIRType_Null:
         return define(new LInteger(0), convert);
 
       case MIRType_Int32:
       case MIRType_Boolean:
         return redefine(convert, opd);
@@ -1582,20 +1582,20 @@ LIRGenerator::visitToInt32(MToInt32 *con
 bool
 LIRGenerator::visitTruncateToInt32(MTruncateToInt32 *truncate)
 {
     MDefinition *opd = truncate->input();
 
     switch (opd->type()) {
       case MIRType_Value:
       {
-        LValueToInt32 *lir = new LValueToInt32(tempFloat(), LValueToInt32::TRUNCATE);
+        LValueToInt32 *lir = new LValueToInt32(tempFloat(), temp(), LValueToInt32::TRUNCATE);
         if (!useBox(lir, LValueToInt32::Input, opd))
             return false;
-        return assignSnapshot(lir) && define(lir, truncate);
+        return assignSnapshot(lir) && define(lir, truncate) && assignSafepoint(lir, truncate);
       }
 
       case MIRType_Null:
       case MIRType_Undefined:
         return define(new LInteger(0), truncate);
 
       case MIRType_Int32:
       case MIRType_Boolean:
--- a/js/src/ion/ParallelFunctions.cpp
+++ b/js/src/ion/ParallelFunctions.cpp
@@ -213,16 +213,22 @@ ion::DoubleToStringPar(ForkJoinSlice *sl
 {
     JSString *str = js_NumberToString<NoGC>(slice, d);
     if (!str)
         return TP_RETRY_SEQUENTIALLY;
     out.set(str);
     return TP_SUCCESS;
 }
 
+ParallelResult
+ion::StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out)
+{
+    return StringToNumber(slice, str, out) ? TP_SUCCESS : TP_FATAL;
+}
+
 #define PAR_RELATIONAL_OP(OP, EXPECTED)                                         \
 do {                                                                            \
     /* Optimize for two int-tagged operands (typical loop control). */          \
     if (lhs.isInt32() && rhs.isInt32()) {                                       \
         *res = (lhs.toInt32() OP rhs.toInt32()) == EXPECTED;                    \
     } else if (lhs.isNumber() && rhs.isNumber()) {                              \
         double l = lhs.toNumber(), r = rhs.toNumber();                          \
         *res = (l OP r) == EXPECTED;                                            \
--- a/js/src/ion/ParallelFunctions.h
+++ b/js/src/ion/ParallelFunctions.h
@@ -39,16 +39,17 @@ JSObject *PushPar(PushParArgs *args);
 JSObject *ExtendArrayPar(ForkJoinSlice *slice, JSObject *array, uint32_t length);
 
 // String related parallel functions. These tend to call existing VM functions
 // that take a ThreadSafeContext.
 ParallelResult ConcatStringsPar(ForkJoinSlice *slice, HandleString left, HandleString right,
                                 MutableHandleString out);
 ParallelResult IntToStringPar(ForkJoinSlice *slice, int i, MutableHandleString out);
 ParallelResult DoubleToStringPar(ForkJoinSlice *slice, double d, MutableHandleString out);
+ParallelResult StringToNumberPar(ForkJoinSlice *slice, JSString *str, double *out);
 
 // Binary and unary operator functions on values. These tend to return
 // RETRY_SEQUENTIALLY if the values are objects.
 ParallelResult StrictlyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
 ParallelResult StrictlyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
 ParallelResult LooselyEqualPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
 ParallelResult LooselyUnequalPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);
 ParallelResult LessThanPar(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, bool *);