Implement MToDouble (bug 677339, r=sstangl).
authorDavid Anderson <danderson@mozilla.com>
Tue, 16 Aug 2011 14:02:48 -0700
changeset 108723 6b30297a73661294f6cb93b5454ced28faa0e3e0
parent 108722 d8e8d950cfbae8b700e9cbfb134ab373b2908e75
child 108724 e029a3cf6a641f6cc083f3633db92622d2993cfb
push id2248
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 19:23:44 +0000
treeherdermozilla-aurora@118a3b748323 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssstangl
bugs677339
milestone8.0a1
Implement MToDouble (bug 677339, r=sstangl).
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/shared/Assembler-x86-shared.h
js/src/ion/shared/MacroAssembler-x86-shared.h
js/src/ion/x64/CodeGenerator-x64.cpp
js/src/ion/x64/MacroAssembler-x64.h
js/src/ion/x86/Assembler-x86.h
js/src/ion/x86/MacroAssembler-x86.h
js/src/jsgc.cpp
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -38,16 +38,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "CodeGenerator.h"
 #include "IonLinker.h"
 #include "MIRGenerator.h"
 #include "shared/CodeGenerator-shared-inl.h"
+#include "jsnum.h"
 
 using namespace js;
 using namespace js::ion;
 
 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph &graph)
   : CodeGeneratorSpecific(gen, graph)
 {
 }
@@ -98,16 +99,69 @@ CodeGenerator::visitValueToInt32(LValueT
     masm.bind(&isInt32);
     masm.unboxInt32(operand, output);
 
     masm.bind(&done);
 
     return true;
 }
 
+static const double DoubleZero = 0.0;
+
+bool
+CodeGenerator::visitValueToDouble(LValueToDouble *lir)
+{
+    ValueOperand operand = ToValue(lir, LValueToDouble::Input);
+    FloatRegister output = ToFloatRegister(lir->output());
+
+    Assembler::Condition cond;
+    Label isDouble, isInt32, isBool, isNull, done;
+
+    // Type-check switch.
+    cond = masm.testDouble(Assembler::Equal, operand);
+    masm.j(cond, &isDouble);
+    cond = masm.testInt32(Assembler::Equal, operand);
+    masm.j(cond, &isInt32);
+    cond = masm.testBoolean(Assembler::Equal, operand);
+    masm.j(cond, &isBool);
+    cond = masm.testNull(Assembler::Equal, operand);
+    masm.j(cond, &isNull);
+
+    cond = masm.testUndefined(Assembler::NotEqual, operand);
+    if (!bailoutIf(cond, lir->snapshot()))
+        return false;
+    masm.loadStaticDouble(&js_NaN, output);
+    masm.jump(&done);
+
+    masm.bind(&isNull);
+    masm.loadStaticDouble(&DoubleZero, output);
+    masm.jump(&done);
+
+    masm.bind(&isBool);
+    masm.boolValueToDouble(operand, output);
+    masm.jump(&done);
+
+    masm.bind(&isInt32);
+    masm.int32ValueToDouble(operand, output);
+    masm.jump(&done);
+
+    masm.bind(&isDouble);
+    masm.unboxDouble(operand, output);
+    masm.bind(&done);
+
+    return true;
+}
+
+bool
+CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
+{
+    masm.convertInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
+    return true;
+}
+
 bool
 CodeGenerator::generateBody()
 {
     for (size_t i = 0; i < graph.numBlocks(); i++) {
         current = graph.getBlock(i);
         masm.bind(current->label());
         for (LInstructionIterator iter = current->begin(); iter != current->end(); iter++) {
             if (!iter->accept(this))
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -57,15 +57,17 @@ class CodeGenerator : public CodeGenerat
 
   public:
     CodeGenerator(MIRGenerator *gen, LIRGraph &graph);
 
   public:
     bool generate();
 
     virtual bool visitValueToInt32(LValueToInt32 *lir);
+    virtual bool visitValueToDouble(LValueToDouble *lir);
+    virtual bool visitInt32ToDouble(LInt32ToDouble *lir);
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_codegen_h__
 
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -296,22 +296,42 @@ class LMathD : public LInstructionHelper
       : jsop_(jsop)
     { }
 
     JSOp jsop() const {
         return jsop_;
     }
 };
 
+// Convert a 32-bit integer to a double.
+class LInt32ToDouble : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(Int32ToDouble);
+
+    LInt32ToDouble(const LAllocation &input) {
+        setOperand(0, input);
+    }
+
+    const LAllocation *input() {
+        return getOperand(0);
+    }
+    const LDefinition *output() {
+        return getDef(0);
+    }
+};
+
 // Convert a value to a double.
 class LValueToDouble : public LInstructionHelper<1, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(ValueToDouble);
 
+    static const size_t Input = 0;
+
     const LDefinition *output() {
         return getDef(0);
     }
 };
 
 // Convert a value to an int32.
 //   Input: components of a Value
 //   Output: 32-bit integer
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -55,16 +55,17 @@
     _(BitOp)                        \
     _(Return)                       \
     _(Phi)                          \
     _(TestIAndBranch)               \
     _(TestDAndBranch)               \
     _(TestVAndBranch)               \
     _(AddI)                         \
     _(MathD)                        \
+    _(Int32ToDouble)                \
     _(ValueToDouble)                \
     _(ValueToInt32)
 
 #if defined(JS_CPU_X86)
 # include "x86/LOpcodes-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/LOpcodes-x64.h"
 #elif defined(JS_CPU_ARM)
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -236,17 +236,48 @@ LIRGenerator::visitStart(MStart *start)
 {
     // This is a no-op.
     return true;
 }
 
 bool
 LIRGenerator::visitToDouble(MToDouble *convert)
 {
-    JS_NOT_REACHED("NYI");
+    MDefinition *opd = convert->input();
+
+    switch (opd->type()) {
+      case MIRType_Value:
+      {
+        LValueToDouble *lir = new LValueToDouble();
+        if (!useBox(lir, LValueToDouble::Input, opd))
+            return false;
+        return define(lir, convert) && assignSnapshot(lir);
+      }
+
+      case MIRType_Null:
+        return lowerConstantDouble(0, convert);
+
+      case MIRType_Undefined:
+        return lowerConstantDouble(js_NaN, convert);
+
+      case MIRType_Int32:
+      case MIRType_Boolean:
+      {
+        LInt32ToDouble *lir = new LInt32ToDouble(useRegister(opd));
+        return define(lir, convert);
+      }
+
+      case MIRType_Double:
+        return redefine(convert, opd);
+
+      default:
+        // Objects might not be idempotent.
+        // Strings are complicated - we don't handle them yet.
+        JS_NOT_REACHED("unexpected type");
+    }
     return false;
 }
 
 bool
 LIRGenerator::visitToInt32(MToInt32 *convert)
 {
     MDefinition *opd = convert->input();
 
--- a/js/src/ion/shared/Assembler-x86-shared.h
+++ b/js/src/ion/shared/Assembler-x86-shared.h
@@ -547,16 +547,29 @@ class AssemblerX86Shared
             break;
           default:
             JS_NOT_REACHED("unexpected operand kind");
         }
     }
     void psrlq(Imm32 shift, const FloatRegister &dest) {
         masm.psrldq_rr(dest.code(), shift.value);
     }
+
+    void cvtsi2sd(const Operand &src, const FloatRegister &dest) {
+        switch (src.kind()) {
+          case Operand::REG:
+            masm.cvtsi2sd_rr(src.reg(), dest.code());
+            break;
+          case Operand::REG_DISP:
+            masm.cvtsi2sd_mr(src.disp(), src.base(), dest.code());
+            break;
+          default:
+            JS_NOT_REACHED("unexpected operand kind");
+        }
+    }
     void cvttsd2si(const FloatRegister &src, const Register &dest) {
         masm.cvttsd2si_rr(src.code(), dest.code());
     }
     void cvtsi2sd(const Register &src, const FloatRegister &dest) {
         masm.cvtsi2sd_rr(src.code(), dest.code());
     }
     void movmskpd(const FloatRegister &src, const Register &dest) {
         masm.movmskpd_rr(src.code(), dest.code());
--- a/js/src/ion/shared/MacroAssembler-x86-shared.h
+++ b/js/src/ion/shared/MacroAssembler-x86-shared.h
@@ -66,16 +66,19 @@ class MacroAssemblerX86Shared : public A
       : framePushed_(0)
     { }
 
     void Push(const Register &reg) {
         push(reg);
         framePushed_ += STACK_SLOT_SIZE;
     }
 
+    void convertInt32ToDouble(const Register &src, const FloatRegister &dest) {
+        cvtsi2sd(Operand(src), dest);
+    }
     void jump(Label *label) {
         jmp(label);
     }
 
     uint32 framePushed() const {
         return framePushed_;
     }
 };
--- a/js/src/ion/x64/CodeGenerator-x64.cpp
+++ b/js/src/ion/x64/CodeGenerator-x64.cpp
@@ -57,27 +57,17 @@ CodeGeneratorX64::ToValue(LInstruction *
 {
     return ValueOperand(ToRegister(ins->getOperand(pos)));
 }
 
 bool
 CodeGeneratorX64::visitDouble(LDouble *ins)
 {
     const LDefinition *out = ins->output();
-
-    jsdpun dpun;
-    dpun.d = ins->getDouble();
-
-    if (dpun.u64 == 0) {
-        masm.xorpd(ToFloatRegister(out), ToFloatRegister(out));
-        return true;
-    }
-
-    masm.movq(ImmWord(dpun.u64), ScratchReg);
-    masm.movqsd(ScratchReg, ToFloatRegister(out));
+    masm.loadDouble(ins->getDouble(), ToFloatRegister(out));
     return true;
 }
 
 FrameSizeClass
 FrameSizeClass::FromDepth(uint32 frameDepth)
 {
     return FrameSizeClass::None();
 }
--- a/js/src/ion/x64/MacroAssembler-x64.h
+++ b/js/src/ion/x64/MacroAssembler-x64.h
@@ -38,16 +38,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jsion_macro_assembler_x64_h__
 #define jsion_macro_assembler_x64_h__
 
 #include "ion/shared/MacroAssembler-x86-shared.h"
+#include "jsnum.h"
 
 namespace js {
 namespace ion {
 
 struct ImmShiftedTag : public ImmWord
 {
     ImmShiftedTag(JSValueShiftedTag shtag)
       : ImmWord((uintptr_t)shtag)
@@ -144,26 +145,53 @@ class MacroAssemblerX64 : public MacroAs
         cmpq(src.value(), ScratchReg);
         return (cond == NotEqual) ? Above : BelowOrEqual;
     }
     Condition testNull(Condition cond, const ValueOperand &src) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpTag(src, ImmTag(JSVAL_TAG_NULL));
         return cond;
     }
+    Condition testUndefined(Condition cond, const ValueOperand &src) {
+        JS_ASSERT(cond == Equal || cond == NotEqual);
+        cmpTag(src, ImmTag(JSVAL_TAG_UNDEFINED));
+        return cond;
+    }
 
     void unboxInt32(const ValueOperand &src, const Register &dest) {
         movl(src.value(), dest);
     }
     void unboxBoolean(const ValueOperand &src, const Register &dest) {
         movl(src.value(), dest);
     }
     void unboxDouble(const ValueOperand &src, const FloatRegister &dest) {
         movqsd(src.valueReg(), dest);
     }
+
+    // These two functions use the low 32-bits of the full value register.
+    void boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
+        cvtsi2sd(operand.value(), dest);
+    }
+    void int32ValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
+        cvtsi2sd(operand.value(), dest);
+    }
+
+    void loadDouble(double d, const FloatRegister &dest) {
+        jsdpun dpun;
+        dpun.d = d;
+        if (dpun.u64 == 0) {
+            xorpd(dest, dest);
+        } else {
+            movq(ImmWord(dpun.u64), ScratchReg);
+            movqsd(ScratchReg, dest);
+        }
+    }
+    void loadStaticDouble(const double *dp, const FloatRegister &dest) {
+        loadDouble(*dp, dest);
+    }
 };
 
 typedef MacroAssemblerX64 MacroAssemblerSpecific;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_macro_assembler_x64_h__
--- a/js/src/ion/x86/Assembler-x86.h
+++ b/js/src/ion/x86/Assembler-x86.h
@@ -253,16 +253,19 @@ class Assembler : public AssemblerX86Sha
         JmpSrc src = masm.jmp();
         addPendingJump(src, target, reloc);
     }
     void j(Condition cond, void *target, Relocation::Kind reloc) {
         JmpSrc src = masm.jCC(static_cast<JSC::X86Assembler::Condition>(cond));
         addPendingJump(src, target, reloc);
     }
 
+    void movsd(const double *dp, const FloatRegister &dest) {
+        masm.movsd_mr((const void *)dp, dest.code());
+    }
     void movsd(AbsoluteLabel *label, const FloatRegister &dest) {
         JS_ASSERT(!label->bound());
         // Thread the patch list through the unpatched address word in the
         // instruction stream.
         masm.movsd_mr(reinterpret_cast<void *>(label->prev()), dest.code());
         label->setPrev(masm.size());
     }
 };
--- a/js/src/ion/x86/MacroAssembler-x86.h
+++ b/js/src/ion/x86/MacroAssembler-x86.h
@@ -120,16 +120,21 @@ class MacroAssemblerX86 : public MacroAs
         cmpl(ImmTag(JSVAL_TAG_CLEAR), value.typeReg());
         return actual;
     }
     Condition testNull(Condition cond, const ValueOperand &value) {
         JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
         cmpl(ImmType(JSVAL_TYPE_NULL), value.typeReg());
         return cond;
     }
+    Condition testUndefined(Condition cond, const ValueOperand &value) {
+        JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
+        cmpl(ImmType(JSVAL_TYPE_UNDEFINED), value.typeReg());
+        return cond;
+    }
 
     void unboxInt32(const ValueOperand &operand, const Register &dest) {
         movl(operand.payloadReg(), dest);
     }
     void unboxBoolean(const ValueOperand &operand, const Register &dest) {
         movl(operand.payloadReg(), dest);
     }
     void unboxDouble(const ValueOperand &operand, const FloatRegister &dest) {
@@ -138,16 +143,27 @@ class MacroAssemblerX86 : public MacroAs
             movd(operand.payloadReg(), dest);
             pinsrd(operand.typeReg(), dest);
         } else {
             movd(operand.payloadReg(), dest);
             movd(operand.typeReg(), ScratchFloatReg);
             unpcklps(ScratchFloatReg, dest);
         }
     }
+
+    void boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
+        cvtsi2sd(operand.payloadReg(), dest);
+    }
+    void int32ValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
+        cvtsi2sd(operand.payloadReg(), dest);
+    }
+
+    void loadStaticDouble(const double *dp, const FloatRegister &dest) {
+        movsd(dp, dest);
+    }
 };
 
 typedef MacroAssemblerX86 MacroAssemblerSpecific;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_macro_assembler_x86_h__
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -236,17 +236,17 @@ Arena::finalize(JSContext *cx)
 #ifdef DEBUG
                 nmarked++;
 #endif
                 if (newFreeSpanStart) {
                     JS_ASSERT(thing >= thingsStart(sizeof(T)) + sizeof(T));
                     newListTail->first = newFreeSpanStart;
                     newListTail->last = thing - sizeof(T);
                     newListTail = newListTail->nextSpanUnchecked(sizeof(T));
-                    newFreeSpanStart = NULL;
+                    newFreeSpanStart = 0;
                 }
             } else {
                 if (!newFreeSpanStart)
                     newFreeSpanStart = thing;
                 t->finalize(cx);
                 memset(t, JS_FREE_PATTERN, sizeof(T));
             }
         }