From: plind44@gmail.com Date: Mon, 10 Mar 2014 19:50:36 +0000 (+0000) Subject: MIPS: Reland "Handle non-power-of-2 divisors in division-like operations". X-Git-Tag: upstream/4.7.83~10404 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fe909b47ea49c78bd94e0b2c56ade3eaf4afd983;p=platform%2Fupstream%2Fv8.git MIPS: Reland "Handle non-power-of-2 divisors in division-like operations". Port r19749 (4880ed9) Original commit message: Fixed the flooring div bug and added a test case. BUG= R=plind44@gmail.com Review URL: https://codereview.chromium.org/192743006 Patch from Balazs Kilvady . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19774 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index c7e2a43af..b5102a4a1 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -1132,108 +1132,6 @@ void LCodeGen::DoModI(LModI* instr) { } -void LCodeGen::EmitSignedIntegerDivisionByConstant( - Register result, - Register dividend, - int32_t divisor, - Register remainder, - Register scratch, - LEnvironment* environment) { - ASSERT(!AreAliased(dividend, scratch, at, no_reg)); - - uint32_t divisor_abs = abs(divisor); - - int32_t power_of_2_factor = - CompilerIntrinsics::CountTrailingZeros(divisor_abs); - - switch (divisor_abs) { - case 0: - DeoptimizeIf(al, environment); - return; - - case 1: - if (divisor > 0) { - __ Move(result, dividend); - } else { - __ SubuAndCheckForOverflow(result, zero_reg, dividend, scratch); - DeoptimizeIf(lt, environment, scratch, Operand(zero_reg)); - } - // Compute the remainder. - __ Move(remainder, zero_reg); - return; - - default: - if (IsPowerOf2(divisor_abs)) { - // Branch and condition free code for integer division by a power - // of two. - int32_t power = WhichPowerOf2(divisor_abs); - if (power > 1) { - __ sra(scratch, dividend, power - 1); - } - __ srl(scratch, scratch, 32 - power); - __ Addu(scratch, dividend, Operand(scratch)); - __ sra(result, scratch, power); - // Negate if necessary. - // We don't need to check for overflow because the case '-1' is - // handled separately. - if (divisor < 0) { - ASSERT(divisor != -1); - __ Subu(result, zero_reg, Operand(result)); - } - // Compute the remainder. - if (divisor > 0) { - __ sll(scratch, result, power); - __ Subu(remainder, dividend, Operand(scratch)); - } else { - __ sll(scratch, result, power); - __ Addu(remainder, dividend, Operand(scratch)); - } - return; - } else if (LChunkBuilder::HasMagicNumberForDivisor(divisor)) { - // Use magic numbers for a few specific divisors. - // Details and proofs can be found in: - // - Hacker's Delight, Henry S. Warren, Jr. - // - The PowerPC Compiler Writer's Guide - // and probably many others. - // - // We handle - // * - // but not - // * - DivMagicNumbers magic_numbers = - DivMagicNumberFor(divisor_abs >> power_of_2_factor); - // Branch and condition free code for integer division by a power - // of two. - const int32_t M = magic_numbers.M; - const int32_t s = magic_numbers.s + power_of_2_factor; - - __ li(scratch, Operand(M)); - __ mult(dividend, scratch); - __ mfhi(scratch); - if (M < 0) { - __ Addu(scratch, scratch, Operand(dividend)); - } - if (s > 0) { - __ sra(scratch, scratch, s); - __ mov(scratch, scratch); - } - __ srl(at, dividend, 31); - __ Addu(result, scratch, Operand(at)); - if (divisor < 0) __ Subu(result, zero_reg, Operand(result)); - // Compute the remainder. - __ li(scratch, Operand(divisor)); - __ Mul(scratch, result, Operand(scratch)); - __ Subu(remainder, dividend, Operand(scratch)); - } else { - __ li(scratch, Operand(divisor)); - __ div(dividend, scratch); - __ mfhi(remainder); - __ mflo(result); - } - } -} - - void LCodeGen::DoDivI(LDivI* instr) { const Register left = ToRegister(instr->left()); const Register right = ToRegister(instr->right()); @@ -1284,33 +1182,66 @@ void LCodeGen::DoMultiplyAddD(LMultiplyAddD* instr) { } -void LCodeGen::DoFlooringDivByConstI(LFlooringDivByConstI* instr) { - Register left = ToRegister(instr->dividend()); - Register remainder = ToRegister(instr->temp()); +void LCodeGen::DoFlooringDivByPowerOf2I(LFlooringDivByPowerOf2I* instr) { + Register dividend = ToRegister(instr->dividend()); + int32_t divisor = instr->divisor(); + ASSERT(dividend.is(ToRegister(instr->result()))); Register scratch = scratch0(); - Register result = ToRegister(instr->result()); - ASSERT(instr->divisor()->IsConstantOperand()); - Label done; - int32_t divisor = ToInteger32(LConstantOperand::cast(instr->divisor())); - if (divisor < 0) { - DeoptimizeIf(eq, instr->environment(), left, Operand(zero_reg)); - } - EmitSignedIntegerDivisionByConstant(result, - left, - divisor, - remainder, - scratch, - instr->environment()); - // We performed a truncating division. Correct the result if necessary. - __ Branch(&done, eq, remainder, Operand(zero_reg), USE_DELAY_SLOT); - __ Xor(scratch , remainder, Operand(divisor)); - __ Branch(&done, ge, scratch, Operand(zero_reg)); - __ Subu(result, result, Operand(1)); + // If the divisor is positive, things are easy: There can be no deopts and we + // can simply do an arithmetic right shift. + if (divisor == 1) return; + uint16_t shift = WhichPowerOf2Abs(divisor); + if (divisor > 1) { + __ sra(dividend, dividend, shift); + return; + } + + // If the divisor is negative, we have to negate and handle edge cases. + Label not_kmin_int, done; + __ Subu(scratch, zero_reg, dividend); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + DeoptimizeIf(eq, instr->environment(), scratch, Operand(zero_reg)); + } + if (instr->hydrogen()->left()->RangeCanInclude(kMinInt)) { + // Note that we could emit branch-free code, but that would need one more + // register. + __ Branch(¬_kmin_int, ne, dividend, Operand(kMinInt)); + if (divisor == -1) { + DeoptimizeIf(al, instr->environment()); + } else { + __ li(dividend, Operand(kMinInt / divisor)); + __ Branch(&done); + } + } + __ bind(¬_kmin_int); + __ sra(dividend, scratch, shift); __ bind(&done); } +void LCodeGen::DoFlooringDivByConstI(LFlooringDivByConstI* instr) { + Register dividend = ToRegister(instr->dividend()); + int32_t divisor = instr->divisor(); + Register result = ToRegister(instr->result()); + ASSERT(!dividend.is(result)); + + if (divisor == 0) { + DeoptimizeIf(al, instr->environment()); + return; + } + + // Check for (0 / -x) that will produce negative zero. + HMathFloorOfDiv* hdiv = instr->hydrogen(); + if (hdiv->CheckFlag(HValue::kBailoutOnMinusZero) && + hdiv->left()->RangeCanInclude(0) && divisor < 0) { + DeoptimizeIf(eq, instr->environment(), dividend, Operand(zero_reg)); + } + + __ FlooringDiv(result, dividend, divisor); +} + + void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) { const Register result = ToRegister(instr->result()); const Register left = ToRegister(instr->left()); diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index d4f4e22cf..d2cc39be9 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -1266,42 +1266,39 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { } -bool LChunkBuilder::HasMagicNumberForDivisor(int32_t divisor) { - uint32_t divisor_abs = abs(divisor); - // Dividing by 0 or powers of 2 is easy. - if (divisor == 0 || IsPowerOf2(divisor_abs)) return true; - - // We have magic numbers for a few specific divisors. - // Details and proofs can be found in: - // - Hacker's Delight, Henry S. Warren, Jr. - // - The PowerPC Compiler Writer's Guide - // and probably many others. - // - // We handle - // * - // but not - // * - int32_t power_of_2_factor = - CompilerIntrinsics::CountTrailingZeros(divisor_abs); - DivMagicNumbers magic_numbers = - DivMagicNumberFor(divisor_abs >> power_of_2_factor); - return magic_numbers.M != InvalidDivMagicNumber.M; +LInstruction* LChunkBuilder::DoFlooringDivByPowerOf2I(HMathFloorOfDiv* instr) { + LOperand* dividend = UseRegisterAtStart(instr->left()); + int32_t divisor = instr->right()->GetInteger32Constant(); + LInstruction* result = + DefineSameAsFirst( + new(zone()) LFlooringDivByPowerOf2I(dividend, divisor)); + bool can_deopt = + (instr->CheckFlag(HValue::kBailoutOnMinusZero) && divisor < 0) || + (instr->left()->RangeCanInclude(kMinInt) && divisor == -1); + return can_deopt ? AssignEnvironment(result) : result; } LInstruction* LChunkBuilder::DoFlooringDivByConstI(HMathFloorOfDiv* instr) { + ASSERT(instr->representation().IsInteger32()); + ASSERT(instr->left()->representation().Equals(instr->representation())); + ASSERT(instr->right()->representation().Equals(instr->representation())); LOperand* dividend = UseRegister(instr->left()); - LOperand* divisor = UseOrConstant(instr->right()); - LOperand* remainder = TempRegister(); + int32_t divisor = instr->right()->GetInteger32Constant(); LInstruction* result = - DefineAsRegister( - new(zone()) LFlooringDivByConstI(dividend, divisor, remainder)); - return AssignEnvironment(result); + DefineAsRegister(new(zone()) LFlooringDivByConstI(dividend, divisor)); + bool can_deopt = + divisor == 0 || + (instr->CheckFlag(HValue::kBailoutOnMinusZero) && + instr->left()->RangeCanInclude(0) && divisor < 0); + return can_deopt ? AssignEnvironment(result) : result; } LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { - if (instr->right()->IsConstant()) { + if (instr->RightIsPowerOf2()) { + return DoFlooringDivByPowerOf2I(instr); + } else if (instr->right()->IsConstant()) { return DoFlooringDivByConstI(instr); } else { HValue* right = instr->right(); diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h index 43ce740a7..45b6cb574 100644 --- a/src/mips/lithium-mips.h +++ b/src/mips/lithium-mips.h @@ -94,6 +94,7 @@ class LCodeGen; V(Dummy) \ V(DummyUse) \ V(FlooringDivByConstI) \ + V(FlooringDivByPowerOf2I) \ V(ForInCacheArray) \ V(ForInPrepareMap) \ V(FunctionLiteral) \ @@ -667,20 +668,41 @@ class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 0> { }; -class LFlooringDivByConstI V8_FINAL : public LTemplateInstruction<1, 2, 1> { +class LFlooringDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> { public: - LFlooringDivByConstI(LOperand* dividend, LOperand* divisor, LOperand* temp) { + LFlooringDivByPowerOf2I(LOperand* dividend, int32_t divisor) { inputs_[0] = dividend; - inputs_[1] = divisor; - temps_[0] = temp; + divisor_ = divisor; } LOperand* dividend() { return inputs_[0]; } - LOperand* divisor() { return inputs_[1]; } - LOperand* temp() { return temps_[0]; } + int32_t divisor() { return divisor_; } + + DECLARE_CONCRETE_INSTRUCTION(FlooringDivByPowerOf2I, + "flooring-div-by-power-of-2-i") + DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv) + + private: + int32_t divisor_; +}; + + +class LFlooringDivByConstI V8_FINAL : public LTemplateInstruction<1, 1, 0> { + public: + LFlooringDivByConstI(LOperand* dividend, int32_t divisor) { + inputs_[0] = dividend; + divisor_ = divisor; + } + + LOperand* dividend() { return inputs_[0]; } + int32_t divisor() const { return divisor_; } + LOperand* temp1() { return temps_[0]; } DECLARE_CONCRETE_INSTRUCTION(FlooringDivByConstI, "flooring-div-by-const-i") DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv) + + private: + int32_t divisor_; }; @@ -2649,6 +2671,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase { LInstruction* DoDivI(HBinaryOperation* instr); LInstruction* DoModByPowerOf2I(HMod* instr); LInstruction* DoModI(HMod* instr); + LInstruction* DoFlooringDivByPowerOf2I(HMathFloorOfDiv* instr); LInstruction* DoFlooringDivByConstI(HMathFloorOfDiv* instr); private: diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc index 1c22a89e1..0538cd407 100644 --- a/src/mips/macro-assembler-mips.cc +++ b/src/mips/macro-assembler-mips.cc @@ -5706,6 +5706,28 @@ void CodePatcher::ChangeBranchCondition(Condition cond) { } +void MacroAssembler::FlooringDiv(Register result, + Register dividend, + int32_t divisor) { + ASSERT(!dividend.is(result)); + ASSERT(!dividend.is(at)); + ASSERT(!result.is(at)); + MultiplierAndShift ms(divisor); + li(at, Operand(ms.multiplier())); + Mult(dividend, Operand(at)); + mfhi(result); + if (divisor > 0 && ms.multiplier() < 0) { + Addu(result, result, Operand(dividend)); + } + if (divisor < 0 && ms.multiplier() > 0) { + Subu(result, result, Operand(dividend)); + } + if (ms.shift() > 0) { + sra(result, result, ms.shift()); + } +} + + } } // namespace v8::internal #endif // V8_TARGET_ARCH_MIPS diff --git a/src/mips/macro-assembler-mips.h b/src/mips/macro-assembler-mips.h index 1b64f2568..c22056fb7 100644 --- a/src/mips/macro-assembler-mips.h +++ b/src/mips/macro-assembler-mips.h @@ -1304,6 +1304,10 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT return code_object_; } + // Emit code for a flooring division by a constant. The dividend register is + // unchanged and at gets clobbered. Dividend and result must be different. + void FlooringDiv(Register result, Register dividend, int32_t divisor); + // ------------------------------------------------------------------------- // StatsCounter support.