Bug 1010747 - Part 2: Implement Ceil (floating-point) -> int32 in Ion. r=sunfish, r=mjrosenb, a=sledru
💩💩 backed out by a89aa1e3e367 💩 💩
authorBenjamin Bouvier <benj@benj.me>
Thu, 22 May 2014 12:03:08 +0200
changeset 192365 80950d72bd71
parent 192364 586ed41fa2d1
child 192366 0db12290df12
push id3584
push userryanvm@gmail.com
push date2014-05-22 13:22 +0000
treeherdermozilla-beta@0db12290df12 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish, mjrosenb, sledru
bugs1010747
milestone30.0
Bug 1010747 - Part 2: Implement Ceil (floating-point) -> int32 in Ion. r=sunfish, r=mjrosenb, a=sledru
js/src/jit-test/tests/ion/ceil.js
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.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/ceil.js
@@ -0,0 +1,59 @@
+setJitCompilerOption("baseline.usecount.trigger", 10);
+setJitCompilerOption("ion.usecount.trigger", 20);
+
+//var log = print;
+var log = function(x){}
+
+function ceil(x) {
+    // A nice but not always efficient polyfill.
+    return -Math.floor(-x);
+}
+
+function doubleCheck(g) {
+    for (var j = 0; j < 200; j++) {
+        var i = j;
+        assertEq(g(i), i);
+        assertEq(g(i+.5), i+1);
+        assertEq(g(-i), -i);
+        assertEq(g(-i-.5), -i);
+    }
+}
+
+function floatCheck(g, val) {
+    for (var j = 0; j < 200; j++) {
+        var i = Math.fround(j);
+        assertEq(g(i), i);
+        assertEq(g(i+.5), i+1);
+        assertEq(g(-i), -i);
+        assertEq(g(-i-.5), -i);
+    }
+}
+
+function testBailout(value) {
+    var dceil = eval('(function(x) { return Math.ceil(x) })');
+    log('start double');
+    doubleCheck(dceil);
+    log('bailout');
+    // At this point, the compiled code should bailout, if 'value' is in the
+    // edge case set.
+    assertEq(dceil(value), ceil(value));
+
+    var fceil = eval('(function(x) { return Math.ceil(Math.fround(x)) })');
+    log('start float');
+    floatCheck(fceil, value);
+    log('bailout');
+    assertEq(fceil(Math.fround(value)), ceil(Math.fround(value)));
+}
+
+var INT_MAX = Math.pow(2, 31) - 1;
+var INT_MIN = INT_MAX + 1 | 0;
+
+// Values in ]-1; -0]
+testBailout(-0);
+testBailout(-.5);
+// Values outside the INT32 range, when represented in either double or
+// single precision
+testBailout(INT_MAX + .5);
+testBailout(INT_MIN - 129);
+// BatNaN
+testBailout(NaN);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -4771,16 +4771,38 @@ class LFloorF : public LInstructionHelpe
   public:
     LIR_HEADER(FloorF)
 
     LFloorF(const LAllocation &num) {
         setOperand(0, num);
     }
 };
 
+// Take the ceiling of a double precision number. Implements Math.ceil().
+class LCeil : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(Ceil)
+
+    LCeil(const LAllocation &num) {
+        setOperand(0, num);
+    }
+};
+
+// Take the ceiling of a single precision number. Implements Math.ceil().
+class LCeilF : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(CeilF)
+
+    LCeilF(const LAllocation &num) {
+        setOperand(0, num);
+    }
+};
+
 // Round a double precision number. Implements Math.round().
 class LRound : public LInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(Round)
 
     LRound(const LAllocation &num, const LDefinition &temp) {
         setOperand(0, num);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -255,16 +255,18 @@
     _(SetFrameArgumentV)            \
     _(RunOncePrologue)              \
     _(Rest)                         \
     _(RestPar)                      \
     _(TypeOfV)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(FloorF)                       \
+    _(Ceil)                         \
+    _(CeilF)                        \
     _(Round)                        \
     _(RoundF)                       \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1172,16 +1172,35 @@ LIRGenerator::visitFloor(MFloor *ins)
 
     LFloorF *lir = new(alloc()) LFloorF(useRegister(ins->num()));
     if (!assignSnapshot(lir))
         return false;
     return define(lir, ins);
 }
 
 bool
+LIRGenerator::visitCeil(MCeil *ins)
+{
+    MIRType type = ins->num()->type();
+    JS_ASSERT(IsFloatingPointType(type));
+
+    if (type == MIRType_Double) {
+        LCeil *lir = new(alloc()) LCeil(useRegister(ins->num()));
+        if (!assignSnapshot(lir))
+            return false;
+        return define(lir, ins);
+    }
+
+    LCeilF *lir = new(alloc()) LCeilF(useRegister(ins->num()));
+    if (!assignSnapshot(lir))
+        return false;
+    return define(lir, ins);
+}
+
+bool
 LIRGenerator::visitRound(MRound *ins)
 {
     MIRType type = ins->num()->type();
     JS_ASSERT(IsFloatingPointType(type));
 
     if (type == MIRType_Double) {
         LRound *lir = new (alloc()) LRound(useRegister(ins->num()), tempDouble());
         if (!assignSnapshot(lir))
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -110,16 +110,17 @@ class LIRGenerator : public LIRGenerator
     bool visitBitNot(MBitNot *ins);
     bool visitBitAnd(MBitAnd *ins);
     bool visitBitOr(MBitOr *ins);
     bool visitBitXor(MBitXor *ins);
     bool visitLsh(MLsh *ins);
     bool visitRsh(MRsh *ins);
     bool visitUrsh(MUrsh *ins);
     bool visitFloor(MFloor *ins);
+    bool visitCeil(MCeil *ins);
     bool visitRound(MRound *ins);
     bool visitMinMax(MMinMax *ins);
     bool visitAbs(MAbs *ins);
     bool visitSqrt(MSqrt *ins);
     bool visitAtan2(MAtan2 *ins);
     bool visitHypot(MHypot *ins);
     bool visitPow(MPow *ins);
     bool visitRandom(MRandom *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -650,16 +650,24 @@ IonBuilder::inlineMathCeil(CallInfo &cal
         MFloor *floor = MFloor::New(alloc(), mul);
         current->add(floor);
         MMul *result = MMul::New(alloc(), floor, minusOne, MIRType_Int32);
         current->add(result);
         current->push(result);
         return InliningStatus_Inlined;
     }
 
+    if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
+        callInfo.setImplicitlyUsedUnchecked();
+        MCeil *ins = MCeil::New(alloc(), callInfo.getArg(0));
+        current->add(ins);
+        current->push(ins);
+        return InliningStatus_Inlined;
+    }
+
     if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
         callInfo.setImplicitlyUsedUnchecked();
         MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, nullptr);
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
     }
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -842,16 +842,24 @@ void
 MFloor::trySpecializeFloat32(TempAllocator &alloc)
 {
     JS_ASSERT(type() == MIRType_Int32);
     if (EnsureFloatInputOrConvert(this, alloc))
         setPolicyType(MIRType_Float32);
 }
 
 void
+MCeil::trySpecializeFloat32(TempAllocator &alloc)
+{
+    JS_ASSERT(type() == MIRType_Int32);
+    if (EnsureFloatInputOrConvert(this, alloc))
+        setPolicyType(MIRType_Float32);
+}
+
+void
 MRound::trySpecializeFloat32(TempAllocator &alloc)
 {
     JS_ASSERT(type() == MIRType_Int32);
     if (EnsureFloatInputOrConvert(this, alloc))
         setPolicyType(MIRType_Float32);
 }
 
 MCompare *
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8330,16 +8330,59 @@ class MFloor
     void trySpecializeFloat32(TempAllocator &alloc);
 #ifdef DEBUG
     bool isConsistentFloat32Use(MUse *use) const {
         return true;
     }
 #endif
 };
 
+// Inlined version of Math.ceil().
+class MCeil
+  : public MUnaryInstruction,
+    public FloatingPointPolicy<0>
+{
+    MCeil(MDefinition *num)
+      : MUnaryInstruction(num)
+    {
+        setResultType(MIRType_Int32);
+        setPolicyType(MIRType_Double);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(Ceil)
+
+    static MCeil *New(TempAllocator &alloc, MDefinition *num) {
+        return new(alloc) MCeil(num);
+    }
+
+    MDefinition *num() const {
+        return getOperand(0);
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+    TypePolicy *typePolicy() {
+        return this;
+    }
+    bool isFloat32Commutative() const {
+        return true;
+    }
+    void trySpecializeFloat32(TempAllocator &alloc);
+#ifdef DEBUG
+    bool isConsistentFloat32Use(MUse *use) const {
+        return true;
+    }
+#endif
+    bool congruentTo(const MDefinition *ins) const {
+        return congruentIfOperandsEqual(ins);
+    }
+};
+
 // Inlined version of Math.round().
 class MRound
   : public MUnaryInstruction,
     public FloatingPointPolicy<0>
 {
     MRound(MDefinition *num)
       : MUnaryInstruction(num)
     {
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -174,16 +174,17 @@ namespace jit {
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
     _(GetFrameArgument)                                                     \
     _(SetFrameArgument)                                                     \
     _(RunOncePrologue)                                                      \
     _(Rest)                                                                 \
     _(Floor)                                                                \
+    _(Ceil)                                                                 \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
     _(FunctionBoundary)                                                     \
     _(GetDOMProperty)                                                       \
     _(GetDOMMember)                                                         \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -260,16 +260,17 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(StringLength)
     SAFE_OP(ArgumentsLength)
     SAFE_OP(GetFrameArgument)
     UNSAFE_OP(SetFrameArgument)
     UNSAFE_OP(RunOncePrologue)
     CUSTOM_OP(Rest)
     SAFE_OP(RestPar)
     SAFE_OP(Floor)
+    SAFE_OP(Ceil)
     SAFE_OP(Round)
     UNSAFE_OP(InstanceOf)
     CUSTOM_OP(InterruptCheck)
     SAFE_OP(ForkJoinContext)
     SAFE_OP(ForkJoinGetSlice)
     SAFE_OP(NewPar)
     SAFE_OP(NewDenseArrayPar)
     SAFE_OP(NewCallObjectPar)
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1209,16 +1209,40 @@ CodeGeneratorARM::visitFloorF(LFloorF *l
     Label bail;
     masm.floorf(input, output, &bail);
     if (!bailoutFrom(&bail, lir->snapshot()))
         return false;
     return true;
 }
 
 bool
+CodeGeneratorARM::visitCeil(LCeil *lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+    Label bail;
+    masm.ceil(input, output, &bail);
+    if (!bailoutFrom(&bail, lir->snapshot()))
+        return false;
+    return true;
+}
+
+bool
+CodeGeneratorARM::visitCeilF(LCeilF *lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+    Label bail;
+    masm.ceilf(input, output, &bail);
+    if (!bailoutFrom(&bail, lir->snapshot()))
+        return false;
+    return true;
+}
+
+bool
 CodeGeneratorARM::visitRound(LRound *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     Register output = ToRegister(lir->output());
     FloatRegister tmp = ToFloatRegister(lir->temp());
     Label bail;
     // Output is either correct, or clamped.  All -0 cases have been translated to a clamped
     // case.a
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -106,16 +106,18 @@ class CodeGeneratorARM : public CodeGene
     virtual bool visitNotI(LNotI *ins);
     virtual bool visitNotD(LNotD *ins);
     virtual bool visitNotF(LNotF *ins);
 
     virtual bool visitMathD(LMathD *math);
     virtual bool visitMathF(LMathF *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitFloorF(LFloorF *lir);
+    virtual bool visitCeil(LCeil *lir);
+    virtual bool visitCeilF(LCeilF *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitRoundF(LRoundF *lir);
     virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
     virtual bool visitTruncateFToInt32(LTruncateFToInt32 *ins);
 
     // Out of line visitors.
     bool visitOutOfLineBailout(OutOfLineBailout *ool);
     bool visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool);
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4044,16 +4044,118 @@ MacroAssemblerARMCompat::floorf(FloatReg
     // the int range, and special handling is required.
     // zero is also caught by this case, but floor of a negative number
     // should never be zero.
     ma_b(bail, NotSigned);
 
     bind(&fin);
 }
 
+void
+MacroAssemblerARMCompat::ceil(FloatRegister input, Register output, Label *bail)
+{
+    Label handleZero;
+    Label handlePos;
+    Label fin;
+
+    compareDouble(input, InvalidFloatReg);
+    // NaN is always a bail condition, just bail directly.
+    ma_b(bail, Assembler::Overflow);
+    ma_b(&handleZero, Assembler::Equal);
+    ma_b(&handlePos, Assembler::NotSigned);
+
+    // We are in the ]-Inf; 0[ range
+    // If we are in the ]-1; 0[ range => bailout
+    ma_vimm(-1.0, ScratchFloatReg);
+    compareDouble(input, ScratchFloatReg);
+    ma_b(bail, Assembler::GreaterThan);
+
+    // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can
+    // be computed with direct truncation here (x > 0).
+    ma_vneg(input, ScratchFloatReg);
+    ma_vcvt_F64_U32(ScratchFloatReg, ScratchFloatReg);
+    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_neg(output, output, SetCond);
+    ma_b(bail, NotSigned);
+    ma_b(&fin);
+
+    // Test for 0.0 / -0.0: if the top word of the input double is not zero,
+    // then it was -0 and we need to bail out.
+    bind(&handleZero);
+    as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
+    ma_cmp(output, Imm32(0));
+    ma_b(bail, NonZero);
+    ma_b(&fin);
+
+    // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
+    // non integer values, maybe bail if overflow.
+    bind(&handlePos);
+    ma_vcvt_F64_U32(input, ScratchFloatReg);
+    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vcvt_U32_F64(ScratchFloatReg, ScratchFloatReg);
+    compareDouble(ScratchFloatReg, input);
+    ma_add(output, Imm32(1), output, NoSetCond, NotEqual);
+    // Bail out if the add overflowed or the result is negative
+    ma_mov(output, output, SetCond);
+    ma_b(bail, Signed);
+
+    bind(&fin);
+}
+
+void
+MacroAssemblerARMCompat::ceilf(FloatRegister input, Register output, Label *bail)
+{
+    Label handleZero;
+    Label handlePos;
+    Label fin;
+
+    compareFloat(input, InvalidFloatReg);
+    // NaN is always a bail condition, just bail directly.
+    ma_b(bail, Assembler::Overflow);
+    ma_b(&handleZero, Assembler::Equal);
+    ma_b(&handlePos, Assembler::NotSigned);
+
+    // We are in the ]-Inf; 0[ range
+    // If we are in the ]-1; 0[ range => bailout
+    ma_vimm_f32(-1.f, ScratchFloatReg);
+    compareFloat(input, ScratchFloatReg);
+    ma_b(bail, Assembler::GreaterThan);
+
+    // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can
+    // be computed with direct truncation here (x > 0).
+    ma_vneg_f32(input, ScratchFloatReg);
+    ma_vcvt_F32_U32(ScratchFloatReg, ScratchFloatReg);
+    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_neg(output, output, SetCond);
+    ma_b(bail, NotSigned);
+    ma_b(&fin);
+
+    // Test for 0.0 / -0.0: if the top word of the input double is not zero,
+    // then it was -0 and we need to bail out.
+    bind(&handleZero);
+    as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
+    ma_cmp(output, Imm32(0));
+    ma_b(bail, NonZero);
+    ma_b(&fin);
+
+    // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
+    // non integer values, maybe bail if overflow.
+    bind(&handlePos);
+    ma_vcvt_F32_U32(input, ScratchFloatReg);
+    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vcvt_U32_F32(ScratchFloatReg, ScratchFloatReg);
+    compareFloat(ScratchFloatReg, input);
+    ma_add(output, Imm32(1), output, NoSetCond, NotEqual);
+    // Bail out if the add overflowed or the result is negative
+    ma_mov(output, output, SetCond);
+    ma_b(bail, Signed);
+
+    bind(&fin);
+}
+
 CodeOffsetLabel
 MacroAssemblerARMCompat::toggledJump(Label *label)
 {
     // Emit a B that can be toggled to a CMP. See ToggleToJmp(), ToggleToCmp().
     CodeOffsetLabel ret(nextOffset().getOffset());
     ma_b(label, Always, true);
     return ret;
 }
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1449,16 +1449,18 @@ class MacroAssemblerARMCompat : public M
     }
     void computeEffectiveAddress(const BaseIndex &address, Register dest) {
         ma_alu(address.base, lsl(address.index, address.scale), dest, op_add, NoSetCond);
         if (address.offset)
             ma_add(dest, Imm32(address.offset), dest, NoSetCond);
     }
     void floor(FloatRegister input, Register output, Label *handleNotAnInt);
     void floorf(FloatRegister input, Register output, Label *handleNotAnInt);
+    void ceil(FloatRegister input, Register output, Label *handleNotAnInt);
+    void ceilf(FloatRegister input, Register output, Label *handleNotAnInt);
     void round(FloatRegister input, Register output, Label *handleNotAnInt, FloatRegister tmp);
     void roundf(FloatRegister input, Register output, Label *handleNotAnInt, FloatRegister tmp);
 
     void clampCheck(Register r, Label *handleNotAnInt) {
         // check explicitly for r == INT_MIN || r == INT_MAX
         // this is the instruction sequence that gcc generated for this
         // operation.
         ma_sub(r, Imm32(0x80000001), ScratchRegister);
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -1573,16 +1573,146 @@ CodeGeneratorX86Shared::visitFloorF(LFlo
         }
 
         masm.bind(&end);
     }
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitCeil(LCeil *lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    FloatRegister scratch = ScratchFloatReg;
+    Register output = ToRegister(lir->output());
+
+    Label bailout, lessThanMinusOne;
+
+    // Bail on ]-1; -0] range
+    masm.loadConstantDouble(-1, scratch);
+    masm.branchDouble(Assembler::DoubleLessThanOrEqualOrUnordered, input,
+                      scratch, &lessThanMinusOne);
+
+    // Test for remaining values with the sign bit set, i.e. ]-1; -0]
+    masm.movmskpd(input, output);
+    masm.branchTest32(Assembler::NonZero, output, Imm32(1), &bailout);
+    if (!bailoutFrom(&bailout, lir->snapshot()))
+        return false;
+
+    if (AssemblerX86Shared::HasSSE41()) {
+        // x <= -1 or x > -0
+        masm.bind(&lessThanMinusOne);
+        // Round toward +Infinity.
+        masm.roundsd(input, scratch, JSC::X86Assembler::RoundUp);
+
+        masm.cvttsd2si(scratch, output);
+        masm.cmp32(output, Imm32(1));
+        if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
+            return false;
+        return true;
+    }
+
+    // No SSE4.1
+    Label end;
+
+    // x >= 0 and x is not -0.0, we can truncate (resp. truncate and add 1) for
+    // integer (resp. non-integer) values.
+    // Will also work for values >= INT_MAX + 1, as the truncate
+    // operation will return INT_MIN and there'll be a bailout.
+    masm.cvttsd2si(input, output);
+    masm.cmp32(output, Imm32(1));
+    if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
+        return false;
+    masm.convertInt32ToDouble(output, scratch);
+    masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, &end);
+
+    // Input is not integer-valued, add 1 to obtain the ceiling value
+    masm.addl(Imm32(1), output);
+    // if input > INT_MAX, output == INT_MAX so adding 1 will overflow.
+    if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
+        return false;
+    masm.jump(&end);
+
+    // x <= -1, truncation is the way to go.
+    masm.bind(&lessThanMinusOne);
+    masm.cvttsd2si(input, output);
+    masm.cmp32(output, Imm32(1));
+    if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
+        return false;
+
+    masm.bind(&end);
+    return true;
+}
+
+bool
+CodeGeneratorX86Shared::visitCeilF(LCeilF *lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    FloatRegister scratch = ScratchFloatReg;
+    Register output = ToRegister(lir->output());
+
+    Label bailout, lessThanMinusOne;
+
+    // Bail on ]-1; -0] range
+    masm.loadConstantFloat32(-1.f, scratch);
+    masm.branchFloat(Assembler::DoubleLessThanOrEqualOrUnordered, input,
+                     scratch, &lessThanMinusOne);
+
+    // Test for remaining values with the sign bit set, i.e. ]-1; -0]
+    masm.movmskps(input, output);
+    masm.branchTest32(Assembler::NonZero, output, Imm32(1), &bailout);
+    if (!bailoutFrom(&bailout, lir->snapshot()))
+        return false;
+
+    if (AssemblerX86Shared::HasSSE41()) {
+        // x <= -1 or x > -0
+        masm.bind(&lessThanMinusOne);
+        // Round toward +Infinity.
+        masm.roundss(input, scratch, JSC::X86Assembler::RoundUp);
+
+        masm.cvttss2si(scratch, output);
+        masm.cmp32(output, Imm32(1));
+        if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
+            return false;
+        return true;
+    }
+
+    // No SSE4.1
+    Label end;
+
+    // x >= 0 and x is not -0.0, we can truncate (resp. truncate and add 1) for
+    // integer (resp. non-integer) values.
+    // Will also work for values >= INT_MAX + 1, as the truncate
+    // operation will return INT_MIN and there'll be a bailout.
+    masm.cvttss2si(input, output);
+    masm.cmp32(output, Imm32(1));
+    if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
+        return false;
+    masm.convertInt32ToFloat32(output, scratch);
+    masm.branchFloat(Assembler::DoubleEqualOrUnordered, input, scratch, &end);
+
+    // Input is not integer-valued, add 1 to obtain the ceiling value
+    masm.addl(Imm32(1), output);
+    // if input > INT_MAX, output == INT_MAX so adding 1 will overflow.
+    if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
+        return false;
+    masm.jump(&end);
+
+    // x <= -1, truncation is the way to go.
+    masm.bind(&lessThanMinusOne);
+    masm.cvttss2si(input, output);
+    masm.cmp32(output, Imm32(1));
+    if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
+        return false;
+
+    masm.bind(&end);
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitRound(LRound *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister temp = ToFloatRegister(lir->temp());
     FloatRegister scratch = ScratchFloatReg;
     Register output = ToRegister(lir->output());
 
     Label negative, end;
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.h
@@ -109,16 +109,18 @@ class CodeGeneratorX86Shared : public Co
     virtual bool visitBitAndAndBranch(LBitAndAndBranch *baab);
     virtual bool visitNotI(LNotI *comp);
     virtual bool visitNotD(LNotD *comp);
     virtual bool visitNotF(LNotF *comp);
     virtual bool visitMathD(LMathD *math);
     virtual bool visitMathF(LMathF *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitFloorF(LFloorF *lir);
+    virtual bool visitCeil(LCeil *lir);
+    virtual bool visitCeilF(LCeilF *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitRoundF(LRoundF *lir);
     virtual bool visitGuardShape(LGuardShape *guard);
     virtual bool visitGuardObjectType(LGuardObjectType *guard);
     virtual bool visitGuardClass(LGuardClass *guard);
     virtual bool visitEffectiveAddress(LEffectiveAddress *ins);
     virtual bool visitUDivOrMod(LUDivOrMod *ins);
     virtual bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins);