diff --git a/js/src/assembler/assembler/X86Assembler.h b/js/src/assembler/assembler/X86Assembler.h index ab98e0dd059..dc4a149c50d 100644 --- a/js/src/assembler/assembler/X86Assembler.h +++ b/js/src/assembler/assembler/X86Assembler.h @@ -269,7 +269,8 @@ private: OP_INT3 = 0xCC, OP_GROUP2_Ev1 = 0xD1, OP_GROUP2_EvCL = 0xD3, - OP_FPU6 = 0xDD, + OP_FPU6 = 0xDD, + OP_FLD32 = 0xD9, OP_CALL_rel32 = 0xE8, OP_JMP_rel32 = 0xE9, PRE_SSE_F2 = 0xF2, @@ -723,6 +724,11 @@ public: spew("fld %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base)); m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FLD, base, offset); } + void fld32_m(int offset, RegisterID base) + { + spew("fld %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base)); + m_formatter.oneByteOp(OP_FLD32, FPU6_OP_FLD, base, offset); + } void fisttp_m(int offset, RegisterID base) { spew("fisttp %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base)); @@ -2408,14 +2414,19 @@ public: #if WTF_CPU_X86_64 void cvttsd2sq_rr(XMMRegisterID src, RegisterID dst) { - // We call this instruction cvttsd2sq to differentiate the 64-bit - // version from the 32-bit version, but in assembler it's just - // called cvttsd2si and it's disambiguated by the register name. spew("cvttsd2si %s, %s", nameFPReg(src), nameIReg(dst)); m_formatter.prefix(PRE_SSE_F2); m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); } + + void cvttss2sq_rr(XMMRegisterID src, RegisterID dst) + { + spew("cvttss2si %s, %s", + nameFPReg(src), nameIReg(dst)); + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); + } #endif void unpcklps_rr(XMMRegisterID src, XMMRegisterID dst) diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 5e17d5d567d..977427a2214 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -2774,6 +2774,24 @@ class LTruncateDToInt32 : public LInstructionHelper<1, 1, 1> } }; +// Convert a float32 to a truncated int32. +// Input: floating-point register +// Output: 32-bit integer +class LTruncateFToInt32 : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(TruncateFToInt32) + + LTruncateFToInt32(const LAllocation &in, const LDefinition &temp) { + setOperand(0, in); + setTemp(0, temp); + } + + const LDefinition *tempFloat() { + return getTemp(0); + } +}; + // Convert an integer hosted on one definition to a string with a function call. class LIntToString : public LInstructionHelper<1, 1, 0> { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index cf04e783802..ded3a1d720e 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -130,6 +130,7 @@ _(DoubleToInt32) \ _(Float32ToInt32) \ _(TruncateDToInt32) \ + _(TruncateFToInt32) \ _(IntToString) \ _(DoubleToString) \ _(Start) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index d3fd89d5105..dff437527f6 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1736,6 +1736,9 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32 *truncate) case MIRType_Double: return lowerTruncateDToInt32(truncate); + case MIRType_Float32: + return lowerTruncateFToInt32(truncate); + default: // Objects might be effectful. // Strings are complicated - we don't handle them yet. diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 2b6ab170855..fb4ea0d679e 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -800,9 +800,9 @@ IonBuilder::inlineMathImul(CallInfo &callInfo) if (returnType != MIRType_Int32) return InliningStatus_NotInlined; - if (!IsNumberType(callInfo.getArg(0)->type()) || callInfo.getArg(0)->type() == MIRType_Float32) + if (!IsNumberType(callInfo.getArg(0)->type())) return InliningStatus_NotInlined; - if (!IsNumberType(callInfo.getArg(1)->type()) || callInfo.getArg(1)->type() == MIRType_Float32) + if (!IsNumberType(callInfo.getArg(1)->type())) return InliningStatus_NotInlined; callInfo.unwrapArgs(); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 4b2ea4f9412..25f6da7007d 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2953,6 +2953,11 @@ class MTruncateToInt32 : public MUnaryInstruction void computeRange(); bool isOperandTruncated(size_t index) const; +# ifdef DEBUG + bool isConsistentFloat32Use() const { + return true; + } +#endif }; // Converts any type to a string diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index 1a0cbad14fc..15cc3350ae8 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -340,13 +340,6 @@ BitwisePolicy::adjustInputs(MInstruction *ins) if (in->type() == MIRType_Object || in->type() == MIRType_String) in = boxAt(ins, in); - if (in->type() == MIRType_Float32) { - MToDouble *replace = MToDouble::New(in); - ins->block()->insertBefore(ins, replace); - ins->replaceOperand(i, replace); - in = replace; - } - MInstruction *replace = MTruncateToInt32::New(in); ins->block()->insertBefore(ins, replace); ins->replaceOperand(i, replace); @@ -652,11 +645,6 @@ StoreTypedArrayPolicy::adjustValueInput(MInstruction *ins, int arrayType, case ScalarTypeRepresentation::TYPE_INT32: case ScalarTypeRepresentation::TYPE_UINT32: if (value->type() != MIRType_Int32) { - // Workaround for bug 915903 - if (value->type() == MIRType_Float32) { - value = MToDouble::New(value); - ins->block()->insertBefore(ins, value->toInstruction()); - } value = MTruncateToInt32::New(value); ins->block()->insertBefore(ins, value->toInstruction()); } diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index b5e5754cdb6..1c5427aecb9 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1213,6 +1213,12 @@ CodeGeneratorARM::visitTruncateDToInt32(LTruncateDToInt32 *ins) return emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output())); } +bool +CodeGeneratorARM::visitTruncateFToInt32(LTruncateFToInt32 *ins) +{ + return emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output())); +} + static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 }; FrameSizeClass diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h index 689da43e9fa..b6fd3bcc8fc 100644 --- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -105,6 +105,7 @@ class CodeGeneratorARM : public CodeGeneratorShared virtual bool visitFloor(LFloor *lir); virtual bool visitRound(LRound *lir); virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins); + virtual bool visitTruncateFToInt32(LTruncateFToInt32 *ins); // Out of line visitors. bool visitOutOfLineBailout(OutOfLineBailout *ool); diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 44704f9e28f..8dc985e42f4 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -521,6 +521,15 @@ LIRGeneratorARM::lowerTruncateDToInt32(MTruncateToInt32 *ins) return define(new LTruncateDToInt32(useRegister(opd), LDefinition::BogusTemp()), ins); } +bool +LIRGeneratorARM::lowerTruncateFToInt32(MTruncateToInt32 *ins) +{ + MDefinition *opd = ins->input(); + JS_ASSERT(opd->type() == MIRType_Float32); + + return define(new LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins); +} + bool LIRGeneratorARM::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins) { diff --git a/js/src/jit/arm/Lowering-arm.h b/js/src/jit/arm/Lowering-arm.h index 6a6651f55ff..21fd2ea6719 100644 --- a/js/src/jit/arm/Lowering-arm.h +++ b/js/src/jit/arm/Lowering-arm.h @@ -61,6 +61,7 @@ class LIRGeneratorARM : public LIRGeneratorShared bool lowerConstantDouble(double d, MInstruction *ins); bool lowerConstantFloat32(float d, MInstruction *ins); bool lowerTruncateDToInt32(MTruncateToInt32 *ins); + bool lowerTruncateFToInt32(MTruncateToInt32 *ins); bool lowerDivI(MDiv *div); bool lowerModI(MMod *mod); bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs); diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 8705d44db35..2ddf29a3d5e 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -485,7 +485,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM enum Result { GENERAL, - DOUBLE + DOUBLE, + FLOAT }; MacroAssemblerARMCompat() @@ -605,6 +606,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void push(const Register ®) { ma_push(reg); } + void push(const FloatRegister ®) { + ma_vpush(VFPRegister(reg)); + } void pushWithPadding(const Register ®, const Imm32 extraSpace) { Imm32 totSpace = Imm32(extraSpace.value + 4); ma_dtr(IsStore, sp, totSpace, reg, PreIndex); @@ -620,6 +624,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void pop(const Register ®) { ma_pop(reg); } + void pop(const FloatRegister ®) { + ma_vpop(VFPRegister(reg)); + } void popN(const Register ®, Imm32 extraSpace) { Imm32 totSpace = Imm32(extraSpace.value + 4); diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 28731e8417e..1c5823d4ac0 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -676,10 +676,11 @@ class OutOfLineTruncateSlow : public OutOfLineCodeBase { FloatRegister src_; Register dest_; + bool needFloat32Conversion_; public: - OutOfLineTruncateSlow(FloatRegister src, Register dest) - : src_(src), dest_(dest) + OutOfLineTruncateSlow(FloatRegister src, Register dest, bool needFloat32Conversion = false) + : src_(src), dest_(dest), needFloat32Conversion_(needFloat32Conversion) { } bool accept(CodeGeneratorShared *codegen) { @@ -691,6 +692,10 @@ class OutOfLineTruncateSlow : public OutOfLineCodeBase Register dest() const { return dest_; } + bool needFloat32Conversion() const { + return needFloat32Conversion_; + } + }; OutOfLineCode * @@ -714,6 +719,18 @@ CodeGeneratorShared::emitTruncateDouble(const FloatRegister &src, const Register return true; } +bool +CodeGeneratorShared::emitTruncateFloat32(const FloatRegister &src, const Register &dest) +{ + OutOfLineTruncateSlow *ool = new OutOfLineTruncateSlow(src, dest, true); + if (!addOutOfLineCode(ool)) + return false; + + masm.branchTruncateFloat32(src, dest, ool->entry()); + masm.bind(ool->rejoin()); + return true; +} + bool CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool) { @@ -722,6 +739,11 @@ CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool) saveVolatile(dest); + if (ool->needFloat32Conversion()) { + masm.push(src); + masm.convertFloatToDouble(src, src); + } + masm.setupUnalignedABICall(1, dest); masm.passABIArg(src); if (gen->compilingAsmJS()) @@ -732,6 +754,9 @@ CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool) restoreVolatile(dest); + if (ool->needFloat32Conversion()) + masm.pop(src); + masm.jump(ool->rejoin()); return true; } diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index 8845e0d802d..2addfa5d2e0 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -285,6 +285,7 @@ class CodeGeneratorShared : public LInstructionVisitor OutOfLineCode *oolTruncateDouble(const FloatRegister &src, const Register &dest); bool emitTruncateDouble(const FloatRegister &src, const Register &dest); + bool emitTruncateFloat32(const FloatRegister &src, const Register &dest); void emitPreBarrier(Register base, const LAllocation *index, MIRType type); void emitPreBarrier(Address address, MIRType type); diff --git a/js/src/jit/shared/Lowering-x86-shared.cpp b/js/src/jit/shared/Lowering-x86-shared.cpp index 7f2e8eb86c5..4f081e647f0 100644 --- a/js/src/jit/shared/Lowering-x86-shared.cpp +++ b/js/src/jit/shared/Lowering-x86-shared.cpp @@ -294,3 +294,13 @@ LIRGeneratorX86Shared::lowerTruncateDToInt32(MTruncateToInt32 *ins) LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat(); return define(new LTruncateDToInt32(useRegister(opd), maybeTemp), ins); } + +bool +LIRGeneratorX86Shared::lowerTruncateFToInt32(MTruncateToInt32 *ins) +{ + MDefinition *opd = ins->input(); + JS_ASSERT(opd->type() == MIRType_Float32); + + LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat(); + return define(new LTruncateFToInt32(useRegister(opd), maybeTemp), ins); +} diff --git a/js/src/jit/shared/Lowering-x86-shared.h b/js/src/jit/shared/Lowering-x86-shared.h index c2bf3cdaf3e..4f36026f8f1 100644 --- a/js/src/jit/shared/Lowering-x86-shared.h +++ b/js/src/jit/shared/Lowering-x86-shared.h @@ -48,6 +48,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared bool lowerConstantDouble(double d, MInstruction *ins); bool lowerConstantFloat32(float d, MInstruction *ins); bool lowerTruncateDToInt32(MTruncateToInt32 *ins); + bool lowerTruncateFToInt32(MTruncateToInt32 *ins); }; } // namespace jit diff --git a/js/src/jit/x64/Assembler-x64.h b/js/src/jit/x64/Assembler-x64.h index 060a21d353d..a7d50a19fc4 100644 --- a/js/src/jit/x64/Assembler-x64.h +++ b/js/src/jit/x64/Assembler-x64.h @@ -665,6 +665,9 @@ class Assembler : public AssemblerX86Shared void cvttsd2sq(const FloatRegister &src, const Register &dest) { masm.cvttsd2sq_rr(src.code(), dest.code()); } + void cvttss2sq(const FloatRegister &src, const Register &dest) { + masm.cvttss2sq_rr(src.code(), dest.code()); + } void cvtsq2sd(const Register &src, const FloatRegister &dest) { masm.cvtsq2sd_rr(src.code(), dest.code()); } diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 4415c574999..c9c68927448 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -568,3 +568,15 @@ CodeGeneratorX64::visitTruncateDToInt32(LTruncateDToInt32 *ins) // call a stub if it fails. return emitTruncateDouble(input, output); } + +bool +CodeGeneratorX64::visitTruncateFToInt32(LTruncateFToInt32 *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + Register output = ToRegister(ins->output()); + + // On x64, branchTruncateFloat32 uses cvttss2sq. Unlike the x86 + // implementation, this should handle most floats and we can just + // call a stub if it fails. + return emitTruncateFloat32(input, output); +} diff --git a/js/src/jit/x64/CodeGenerator-x64.h b/js/src/jit/x64/CodeGenerator-x64.h index 86abf4f522f..a00b639f229 100644 --- a/js/src/jit/x64/CodeGenerator-x64.h +++ b/js/src/jit/x64/CodeGenerator-x64.h @@ -50,6 +50,7 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared bool visitCompareV(LCompareV *lir); bool visitCompareVAndBranch(LCompareVAndBranch *lir); bool visitTruncateDToInt32(LTruncateDToInt32 *ins); + bool visitTruncateFToInt32(LTruncateFToInt32 *ins); bool visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins); bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins); bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins); diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index 383fdf15a7b..15b8cb243bb 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -1050,6 +1050,15 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared movl(dest, dest); // Zero upper 32-bits. } + void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail) { + cvttss2sq(src, dest); + + // Same trick as for Doubles + cmpq(dest, Imm32(1)); + j(Assembler::Overflow, fail); + + movl(dest, dest); // Zero upper 32-bits. + } Condition testInt32Truthy(bool truthy, const ValueOperand &operand) { testl(operand.valueReg(), operand.valueReg()); diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index d8e93854b6f..199d7acdeb7 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -268,6 +268,16 @@ class Assembler : public AssemblerX86Shared return leal(src, dest); } + void fld32(const Operand &dest) { + switch (dest.kind()) { + case Operand::MEM_REG_DISP: + masm.fld32_m(dest.disp(), dest.base()); + break; + default: + MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); + } + } + void cmpl(const Register src, ImmWord ptr) { masm.cmpl_ir(ptr.value, src.code()); } diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 9d037dcfb33..53960eac5ee 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -26,6 +26,9 @@ using namespace js::jit; using mozilla::DebugOnly; using mozilla::DoubleExponentBias; using mozilla::DoubleExponentShift; +using mozilla::FloatExponentBias; +using mozilla::FloatExponentShift; +using mozilla::FloatExponentBits; using JS::GenericNaN; CodeGeneratorX86::CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) @@ -790,6 +793,23 @@ class OutOfLineTruncate : public OutOfLineCodeBase } }; +class OutOfLineTruncateFloat32 : public OutOfLineCodeBase +{ + LTruncateFToInt32 *ins_; + + public: + OutOfLineTruncateFloat32(LTruncateFToInt32 *ins) + : ins_(ins) + { } + + bool accept(CodeGeneratorX86 *codegen) { + return codegen->visitOutOfLineTruncateFloat32(this); + } + LTruncateFToInt32 *ins() const { + return ins_; + } +}; + } // namespace jit } // namespace js @@ -808,6 +828,21 @@ CodeGeneratorX86::visitTruncateDToInt32(LTruncateDToInt32 *ins) return true; } +bool +CodeGeneratorX86::visitTruncateFToInt32(LTruncateFToInt32 *ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + Register output = ToRegister(ins->output()); + + OutOfLineTruncateFloat32 *ool = new OutOfLineTruncateFloat32(ins); + if (!addOutOfLineCode(ool)) + return false; + + masm.branchTruncateFloat32(input, output, ool->entry()); + masm.bind(ool->rejoin()); + return true; +} + bool CodeGeneratorX86::visitOutOfLineTruncate(OutOfLineTruncate *ool) { @@ -895,3 +930,92 @@ CodeGeneratorX86::visitOutOfLineTruncate(OutOfLineTruncate *ool) masm.jump(ool->rejoin()); return true; } + +bool +CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool) +{ + LTruncateFToInt32 *ins = ool->ins(); + FloatRegister input = ToFloatRegister(ins->input()); + Register output = ToRegister(ins->output()); + + Label fail; + + if (Assembler::HasSSE3()) { + // Push float32, but subtracts 64 bits so that the value popped by fisttp fits + masm.subl(Imm32(sizeof(uint64_t)), esp); + masm.storeFloat(input, Operand(esp, 0)); + + static const uint32_t EXPONENT_MASK = FloatExponentBits; + static const uint32_t EXPONENT_SHIFT = FloatExponentShift; + // Integers are still 64 bits long, so we can still test for an exponent > 63. + static const uint32_t TOO_BIG_EXPONENT = (FloatExponentBias + 63) << EXPONENT_SHIFT; + + // Check exponent to avoid fp exceptions. + Label failPopFloat; + masm.movl(Operand(esp, 0), output); + masm.and32(Imm32(EXPONENT_MASK), output); + masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(TOO_BIG_EXPONENT), &failPopFloat); + + // Load float, perform 32-bit truncation. + masm.fld32(Operand(esp, 0)); + masm.fisttp(Operand(esp, 0)); + + // Load low word, pop 64bits and jump back. + masm.movl(Operand(esp, 0), output); + masm.addl(Imm32(sizeof(uint64_t)), esp); + masm.jump(ool->rejoin()); + + masm.bind(&failPopFloat); + masm.addl(Imm32(sizeof(uint64_t)), esp); + masm.jump(&fail); + } else { + FloatRegister temp = ToFloatRegister(ins->tempFloat()); + + // Try to convert float32 representing integers within 2^32 of a signed + // integer, by adding/subtracting 2^32 and then trying to convert to int32. + // This has to be an exact conversion, as otherwise the truncation works + // incorrectly on the modified value. + masm.xorps(ScratchFloatReg, ScratchFloatReg); + masm.ucomiss(input, ScratchFloatReg); + masm.j(Assembler::Parity, &fail); + + { + Label positive; + masm.j(Assembler::Above, &positive); + + masm.loadConstantFloat32(4294967296.f, temp); + Label skip; + masm.jmp(&skip); + + masm.bind(&positive); + masm.loadConstantFloat32(-4294967296.f, temp); + masm.bind(&skip); + } + + masm.addss(input, temp); + masm.cvttss2si(temp, output); + masm.cvtsi2ss(output, ScratchFloatReg); + + masm.ucomiss(temp, ScratchFloatReg); + masm.j(Assembler::Parity, &fail); + masm.j(Assembler::Equal, ool->rejoin()); + } + + masm.bind(&fail); + { + saveVolatile(output); + + masm.push(input); + masm.setupUnalignedABICall(1, output); + masm.cvtss2sd(input, input); + masm.passABIArg(input); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); + masm.storeCallResult(output); + masm.pop(input); + + restoreVolatile(output); + } + + masm.jump(ool->rejoin()); + return true; +} diff --git a/js/src/jit/x86/CodeGenerator-x86.h b/js/src/jit/x86/CodeGenerator-x86.h index ee8fec03bdf..a422479a19a 100644 --- a/js/src/jit/x86/CodeGenerator-x86.h +++ b/js/src/jit/x86/CodeGenerator-x86.h @@ -15,6 +15,7 @@ namespace jit { class OutOfLineLoadTypedArrayOutOfBounds; class OutOfLineTruncate; +class OutOfLineTruncateFloat32; class CodeGeneratorX86 : public CodeGeneratorX86Shared { @@ -64,6 +65,7 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared bool visitCompareVAndBranch(LCompareVAndBranch *lir); bool visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir); bool visitTruncateDToInt32(LTruncateDToInt32 *ins); + bool visitTruncateFToInt32(LTruncateFToInt32 *ins); bool visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins); bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins); bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins); @@ -75,6 +77,7 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared bool visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool); bool visitOutOfLineTruncate(OutOfLineTruncate *ool); + bool visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool); void postAsmJSCall(LAsmJSCall *lir); };