diff --git a/js/src/ion/arm/CodeGenerator-arm.cpp b/js/src/ion/arm/CodeGenerator-arm.cpp index 8dcc09d4e48..bacf2e23ff7 100644 --- a/js/src/ion/arm/CodeGenerator-arm.cpp +++ b/js/src/ion/arm/CodeGenerator-arm.cpp @@ -641,37 +641,9 @@ CodeGeneratorARM::visitDivPowTwoI(LDivPowTwoI *ins) } bool -CodeGeneratorARM::visitModI(LModI *ins) +CodeGeneratorARM::modICommon(MMod *mir, Register lhs, Register rhs, Register output, + LSnapshot *snapshot, Label &done) { - // Extract the registers from this instruction - Register lhs = ToRegister(ins->lhs()); - Register rhs = ToRegister(ins->rhs()); - Register callTemp = ToRegister(ins->getTemp(2)); - MMod *mir = ins->mir(); - Label done; - // save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0. - JS_ASSERT(callTemp.code() > r3.code() && callTemp.code() < r12.code()); - masm.ma_mov(lhs, callTemp); - - // Prevent INT_MIN % -1; - // The integer division will give INT_MIN, but we want -(double)INT_MIN. - if (mir->canBeNegativeDividend()) { - masm.ma_cmp(lhs, Imm32(INT_MIN)); // sets EQ if lhs == INT_MIN - masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal); // if EQ (LHS == INT_MIN), sets EQ if rhs == -1 - if (mir->isTruncated()) { - // (INT_MIN % -1)|0 == 0 - Label skip; - masm.ma_b(&skip, Assembler::NotEqual); - masm.ma_mov(Imm32(0), r1); - masm.ma_b(&done); - masm.bind(&skip); - } else { - JS_ASSERT(mir->fallible()); - if (!bailoutIf(Assembler::Equal, ins->snapshot())) - return false; - } - } - // 0/X (with X < 0) is bad because both of these values *should* be doubles, and // the result should be -0.0, which cannot be represented in integers. // X/0 is bad because it will give garbage (or abort), when it should give @@ -691,25 +663,101 @@ CodeGeneratorARM::visitModI(LModI *ins) // NaN|0 == 0 and (0 % -X)|0 == 0 Label skip; masm.ma_b(&skip, Assembler::NotEqual); - masm.ma_mov(Imm32(0), r1); + masm.ma_mov(Imm32(0), output); masm.ma_b(&done); masm.bind(&skip); } else { JS_ASSERT(mir->fallible()); - if (!bailoutIf(Assembler::Equal, ins->snapshot())) + if (!bailoutIf(Assembler::Equal, snapshot)) return false; } - masm.setupAlignedABICall(2); - masm.passABIArg(lhs); - masm.passABIArg(rhs); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, __aeabi_idivmod)); + + return true; +} + +bool +CodeGeneratorARM::visitModI(LModI *ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register output = ToRegister(ins->output()); + Register callTemp = ToRegister(ins->getTemp(0)); + MMod *mir = ins->mir(); + + // save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0. + masm.ma_mov(lhs, callTemp); + + Label done; + if (!modICommon(mir, lhs, rhs, output, ins->snapshot(), done)) + return false; + + masm.ma_smod(lhs, rhs, output); + // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 - // See if X < 0 - masm.ma_cmp(r1, Imm32(0)); if (mir->isTruncated()) { // -0.0|0 == 0 } else { JS_ASSERT(mir->fallible()); + // See if X < 0 + masm.ma_cmp(output, Imm32(0)); + masm.ma_b(&done, Assembler::NotEqual); + masm.ma_cmp(callTemp, Imm32(0)); + if (!bailoutIf(Assembler::Signed, ins->snapshot())) + return false; + } + + masm.bind(&done); + return true; +} + +bool +CodeGeneratorARM::visitSoftModI(LSoftModI *ins) +{ + // Extract the registers from this instruction + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register output = ToRegister(ins->output()); + Register callTemp = ToRegister(ins->getTemp(2)); + MMod *mir = ins->mir(); + Label done; + // save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0. + JS_ASSERT(callTemp.code() > r3.code() && callTemp.code() < r12.code()); + masm.ma_mov(lhs, callTemp); + + // Prevent INT_MIN % -1; + // The integer division will give INT_MIN, but we want -(double)INT_MIN. + if (mir->canBeNegativeDividend()) { + masm.ma_cmp(lhs, Imm32(INT_MIN)); // sets EQ if lhs == INT_MIN + masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal); // if EQ (LHS == INT_MIN), sets EQ if rhs == -1 + if (mir->isTruncated()) { + // (INT_MIN % -1)|0 == 0 + Label skip; + masm.ma_b(&skip, Assembler::NotEqual); + masm.ma_mov(Imm32(0), output); + masm.ma_b(&done); + masm.bind(&skip); + } else { + JS_ASSERT(mir->fallible()); + if (!bailoutIf(Assembler::Equal, ins->snapshot())) + return false; + } + } + + if (!modICommon(mir, lhs, rhs, output, ins->snapshot(), done)) + return false; + + masm.setupAlignedABICall(2); + masm.passABIArg(lhs); + masm.passABIArg(rhs); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, __aeabi_idivmod)); + + // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 + if (mir->isTruncated()) { + // -0.0|0 == 0 + } else { + JS_ASSERT(mir->fallible()); + // See if X < 0 + masm.ma_cmp(r1, Imm32(0)); masm.ma_b(&done, Assembler::NotEqual); masm.ma_cmp(callTemp, Imm32(0)); if (!bailoutIf(Assembler::Signed, ins->snapshot())) diff --git a/js/src/ion/arm/CodeGenerator-arm.h b/js/src/ion/arm/CodeGenerator-arm.h index 4b7c2c4cbd3..0329612ed33 100644 --- a/js/src/ion/arm/CodeGenerator-arm.h +++ b/js/src/ion/arm/CodeGenerator-arm.h @@ -78,6 +78,7 @@ class CodeGeneratorARM : public CodeGeneratorShared virtual bool visitSoftDivI(LSoftDivI *ins); virtual bool visitDivPowTwoI(LDivPowTwoI *ins); virtual bool visitModI(LModI *ins); + virtual bool visitSoftModI(LSoftModI *ins); virtual bool visitModPowTwoI(LModPowTwoI *ins); virtual bool visitModMaskI(LModMaskI *ins); virtual bool visitPowHalfD(LPowHalfD *ins); @@ -120,6 +121,8 @@ class CodeGeneratorARM : public CodeGeneratorShared bool divICommon(MDiv *mir, Register lhs, Register rhs, Register output, LSnapshot *snapshot, Label &done); + bool modICommon(MMod *mir, Register lhs, Register rhs, Register output, LSnapshot *snapshot, + Label &done); public: CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); diff --git a/js/src/ion/arm/LIR-arm.h b/js/src/ion/arm/LIR-arm.h index 73244cb8218..4734b35eda3 100644 --- a/js/src/ion/arm/LIR-arm.h +++ b/js/src/ion/arm/LIR-arm.h @@ -150,14 +150,32 @@ class LDivPowTwoI : public LInstructionHelper<1, 1, 0> } }; -class LModI : public LBinaryMath<3> +class LModI : public LBinaryMath<1> { public: LIR_HEADER(ModI); LModI(const LAllocation &lhs, const LAllocation &rhs, - const LDefinition &temp1, const LDefinition &temp2, const LDefinition &callTemp) + { + setOperand(0, lhs); + setOperand(1, rhs); + setTemp(0, callTemp); + } + + MMod *mir() const { + return mir_->toMod(); + } +}; + +class LSoftModI : public LBinaryMath<3> +{ + public: + LIR_HEADER(SoftModI); + + LSoftModI(const LAllocation &lhs, const LAllocation &rhs, + const LDefinition &temp1, const LDefinition &temp2, + const LDefinition &callTemp) { setOperand(0, lhs); setOperand(1, rhs); diff --git a/js/src/ion/arm/LOpcodes-arm.h b/js/src/ion/arm/LOpcodes-arm.h index a7f2cc12e4c..89e3e97d21f 100644 --- a/js/src/ion/arm/LOpcodes-arm.h +++ b/js/src/ion/arm/LOpcodes-arm.h @@ -16,6 +16,7 @@ _(SoftDivI) \ _(DivPowTwoI) \ _(ModI) \ + _(SoftModI) \ _(ModPowTwoI) \ _(ModMaskI) \ _(PowHalfD) \ diff --git a/js/src/ion/arm/Lowering-arm.cpp b/js/src/ion/arm/Lowering-arm.cpp index 765fd84a1c9..42b43a35ea4 100644 --- a/js/src/ion/arm/Lowering-arm.cpp +++ b/js/src/ion/arm/Lowering-arm.cpp @@ -297,12 +297,19 @@ LIRGeneratorARM::lowerModI(MMod *mod) return define(lir, mod); } } - LModI *lir = new LModI(useFixed(mod->lhs(), r0), use(mod->rhs(), r1), - tempFixed(r2), tempFixed(r3), temp(LDefinition::GENERAL)); - if (mod->fallible() && !assignSnapshot(lir)) - return false; - return defineFixed(lir, mod, LAllocation(AnyRegister(r1))); + if (hasIDIV()) { + LModI *lir = new LModI(useRegister(mod->lhs()), useRegister(mod->rhs()), temp()); + if (mod->fallible() && !assignSnapshot(lir)) + return false; + return define(lir, mod); + } else { + LSoftModI *lir = new LSoftModI(useFixed(mod->lhs(), r0), use(mod->rhs(), r1), + tempFixed(r2), tempFixed(r3), temp(LDefinition::GENERAL)); + if (mod->fallible() && !assignSnapshot(lir)) + return false; + return defineFixed(lir, mod, LAllocation(AnyRegister(r1))); + } } bool diff --git a/js/src/ion/arm/MacroAssembler-arm.cpp b/js/src/ion/arm/MacroAssembler-arm.cpp index 368d6ce233c..1781b51df80 100644 --- a/js/src/ion/arm/MacroAssembler-arm.cpp +++ b/js/src/ion/arm/MacroAssembler-arm.cpp @@ -882,6 +882,13 @@ MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, int32 } +void +MacroAssemblerARM::ma_smod(Register num, Register div, Register dest) +{ + as_sdiv(ScratchRegister, num, div); + as_mls(dest, num, ScratchRegister, div); +} + // division void MacroAssemblerARM::ma_sdiv(Register num, Register div, Register dest, Condition cond) diff --git a/js/src/ion/arm/MacroAssembler-arm.h b/js/src/ion/arm/MacroAssembler-arm.h index 9ed38ab4c81..a8cdb241368 100644 --- a/js/src/ion/arm/MacroAssembler-arm.h +++ b/js/src/ion/arm/MacroAssembler-arm.h @@ -238,6 +238,9 @@ class MacroAssemblerARM : public Assembler // implicitly assumes that we can overwrite dest at the beginning of the sequence void ma_mod_mask(Register src, Register dest, Register hold, int32_t shift); + // mod, depends on sdiv being supported + void ma_smod(Register num, Register div, Register dest); + // division void ma_sdiv(Register num, Register div, Register dest, Condition cond = Always);