Commit fe909b47 authored by plind44@gmail.com's avatar plind44@gmail.com

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 <kilvadyb@homejinni.com>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19774 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a199ba80
...@@ -1132,108 +1132,6 @@ void LCodeGen::DoModI(LModI* instr) { ...@@ -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
// <divisor with magic numbers> * <power of 2>
// but not
// <divisor with magic numbers> * <other divisor with magic numbers>
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) { void LCodeGen::DoDivI(LDivI* instr) {
const Register left = ToRegister(instr->left()); const Register left = ToRegister(instr->left());
const Register right = ToRegister(instr->right()); const Register right = ToRegister(instr->right());
...@@ -1284,33 +1182,66 @@ void LCodeGen::DoMultiplyAddD(LMultiplyAddD* instr) { ...@@ -1284,33 +1182,66 @@ void LCodeGen::DoMultiplyAddD(LMultiplyAddD* instr) {
} }
void LCodeGen::DoFlooringDivByConstI(LFlooringDivByConstI* instr) { void LCodeGen::DoFlooringDivByPowerOf2I(LFlooringDivByPowerOf2I* instr) {
Register left = ToRegister(instr->dividend()); Register dividend = ToRegister(instr->dividend());
Register remainder = ToRegister(instr->temp()); int32_t divisor = instr->divisor();
ASSERT(dividend.is(ToRegister(instr->result())));
Register scratch = scratch0(); Register scratch = scratch0();
Register result = ToRegister(instr->result());
ASSERT(instr->divisor()->IsConstantOperand()); // If the divisor is positive, things are easy: There can be no deopts and we
Label done; // can simply do an arithmetic right shift.
int32_t divisor = ToInteger32(LConstantOperand::cast(instr->divisor())); if (divisor == 1) return;
if (divisor < 0) { uint16_t shift = WhichPowerOf2Abs(divisor);
DeoptimizeIf(eq, instr->environment(), left, Operand(zero_reg)); if (divisor > 1) {
__ sra(dividend, dividend, shift);
return;
} }
EmitSignedIntegerDivisionByConstant(result,
left, // If the divisor is negative, we have to negate and handle edge cases.
divisor, Label not_kmin_int, done;
remainder, __ Subu(scratch, zero_reg, dividend);
scratch, if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
instr->environment()); DeoptimizeIf(eq, instr->environment(), scratch, Operand(zero_reg));
// We performed a truncating division. Correct the result if necessary. }
__ Branch(&done, eq, remainder, Operand(zero_reg), USE_DELAY_SLOT); if (instr->hydrogen()->left()->RangeCanInclude(kMinInt)) {
__ Xor(scratch , remainder, Operand(divisor)); // Note that we could emit branch-free code, but that would need one more
__ Branch(&done, ge, scratch, Operand(zero_reg)); // register.
__ Subu(result, result, Operand(1)); __ Branch(&not_kmin_int, ne, dividend, Operand(kMinInt));
if (divisor == -1) {
DeoptimizeIf(al, instr->environment());
} else {
__ li(dividend, Operand(kMinInt / divisor));
__ Branch(&done);
}
}
__ bind(&not_kmin_int);
__ sra(dividend, scratch, shift);
__ bind(&done); __ 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) { void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
const Register result = ToRegister(instr->result()); const Register result = ToRegister(instr->result());
const Register left = ToRegister(instr->left()); const Register left = ToRegister(instr->left());
......
...@@ -1266,42 +1266,39 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { ...@@ -1266,42 +1266,39 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
} }
bool LChunkBuilder::HasMagicNumberForDivisor(int32_t divisor) { LInstruction* LChunkBuilder::DoFlooringDivByPowerOf2I(HMathFloorOfDiv* instr) {
uint32_t divisor_abs = abs(divisor); LOperand* dividend = UseRegisterAtStart(instr->left());
// Dividing by 0 or powers of 2 is easy. int32_t divisor = instr->right()->GetInteger32Constant();
if (divisor == 0 || IsPowerOf2(divisor_abs)) return true; LInstruction* result =
DefineSameAsFirst(
// We have magic numbers for a few specific divisors. new(zone()) LFlooringDivByPowerOf2I(dividend, divisor));
// Details and proofs can be found in: bool can_deopt =
// - Hacker's Delight, Henry S. Warren, Jr. (instr->CheckFlag(HValue::kBailoutOnMinusZero) && divisor < 0) ||
// - The PowerPC Compiler Writer's Guide (instr->left()->RangeCanInclude(kMinInt) && divisor == -1);
// and probably many others. return can_deopt ? AssignEnvironment(result) : result;
//
// We handle
// <divisor with magic numbers> * <power of 2>
// but not
// <divisor with magic numbers> * <other divisor with magic numbers>
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::DoFlooringDivByConstI(HMathFloorOfDiv* instr) { 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* dividend = UseRegister(instr->left());
LOperand* divisor = UseOrConstant(instr->right()); int32_t divisor = instr->right()->GetInteger32Constant();
LOperand* remainder = TempRegister();
LInstruction* result = LInstruction* result =
DefineAsRegister( DefineAsRegister(new(zone()) LFlooringDivByConstI(dividend, divisor));
new(zone()) LFlooringDivByConstI(dividend, divisor, remainder)); bool can_deopt =
return AssignEnvironment(result); divisor == 0 ||
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->RangeCanInclude(0) && divisor < 0);
return can_deopt ? AssignEnvironment(result) : result;
} }
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
if (instr->right()->IsConstant()) { if (instr->RightIsPowerOf2()) {
return DoFlooringDivByPowerOf2I(instr);
} else if (instr->right()->IsConstant()) {
return DoFlooringDivByConstI(instr); return DoFlooringDivByConstI(instr);
} else { } else {
HValue* right = instr->right(); HValue* right = instr->right();
......
...@@ -94,6 +94,7 @@ class LCodeGen; ...@@ -94,6 +94,7 @@ class LCodeGen;
V(Dummy) \ V(Dummy) \
V(DummyUse) \ V(DummyUse) \
V(FlooringDivByConstI) \ V(FlooringDivByConstI) \
V(FlooringDivByPowerOf2I) \
V(ForInCacheArray) \ V(ForInCacheArray) \
V(ForInPrepareMap) \ V(ForInPrepareMap) \
V(FunctionLiteral) \ V(FunctionLiteral) \
...@@ -667,20 +668,41 @@ class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 0> { ...@@ -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: public:
LFlooringDivByConstI(LOperand* dividend, LOperand* divisor, LOperand* temp) { LFlooringDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend; inputs_[0] = dividend;
inputs_[1] = divisor; divisor_ = divisor;
temps_[0] = temp;
} }
LOperand* dividend() { return inputs_[0]; } LOperand* dividend() { return inputs_[0]; }
LOperand* divisor() { return inputs_[1]; } int32_t divisor() { return divisor_; }
LOperand* temp() { return temps_[0]; }
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_CONCRETE_INSTRUCTION(FlooringDivByConstI, "flooring-div-by-const-i")
DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv) DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
private:
int32_t divisor_;
}; };
...@@ -2649,6 +2671,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase { ...@@ -2649,6 +2671,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
LInstruction* DoDivI(HBinaryOperation* instr); LInstruction* DoDivI(HBinaryOperation* instr);
LInstruction* DoModByPowerOf2I(HMod* instr); LInstruction* DoModByPowerOf2I(HMod* instr);
LInstruction* DoModI(HMod* instr); LInstruction* DoModI(HMod* instr);
LInstruction* DoFlooringDivByPowerOf2I(HMathFloorOfDiv* instr);
LInstruction* DoFlooringDivByConstI(HMathFloorOfDiv* instr); LInstruction* DoFlooringDivByConstI(HMathFloorOfDiv* instr);
private: private:
......
...@@ -5706,6 +5706,28 @@ void CodePatcher::ChangeBranchCondition(Condition cond) { ...@@ -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 } } // namespace v8::internal
#endif // V8_TARGET_ARCH_MIPS #endif // V8_TARGET_ARCH_MIPS
...@@ -1304,6 +1304,10 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT ...@@ -1304,6 +1304,10 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT
return code_object_; 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. // StatsCounter support.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment