Consistenly handle power-of-2 divisors in division-like operations

Lithium currently supports 3 division-like operations on integral operands: "Normal" division (rounding towards zero), flooring division (rounding towards -Infinity) and modulus calculation (the counterpart for the "normal" division). For divisors which are a power of 2, one can efficiently use some bit fiddling to avoid the actual division for such operations. This CL cleanly splits off these operations into separate Lithium instructions, making the code much more maintainable and more consistent across platforms.

There are 2 basic variations of these bit fiddling algorithms: One involving branches and a seemingly more clever one without branches. Choosing between the two is not as easy as it seems: Benchmarks (and probably real-world) programs seem to favor positive dividends, registers and shifting units are sometimes scarce resources, and branch prediction is quite good in modern processors. Therefore only the "normal" division by a power of 2 is implemented in a branch-free manner, this seems to be the best approach in practice. If this turns out to be wrong, we can easily and locally change this.

R=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/175143002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19715 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a1e59cca
......@@ -1368,23 +1368,44 @@ LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
}
LInstruction* LChunkBuilder::DoDivByPowerOf2I(HDiv* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegister(instr->left());
int32_t divisor = instr->right()->GetInteger32Constant();
LInstruction* result =
DefineAsRegister(new(zone()) LDivByPowerOf2I(dividend, divisor));
bool can_deopt =
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->RangeCanInclude(0) && divisor < 0) ||
(instr->CheckFlag(HValue::kCanOverflow) &&
instr->left()->RangeCanInclude(kMinInt) && divisor == -1) ||
(!instr->CheckFlag(HInstruction::kAllUsesTruncatingToInt32) &&
divisor != 1 && divisor != -1);
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoDivI(HBinaryOperation* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegister(instr->left());
LOperand* divisor = UseRegister(instr->right());
LOperand* temp = instr->CheckFlag(HInstruction::kAllUsesTruncatingToInt32)
? NULL : TempRegister();
LDivI* div = new(zone()) LDivI(dividend, divisor, temp);
return AssignEnvironment(DefineAsRegister(div));
}
LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
if (instr->representation().IsInteger32()) {
// TODO(all): Update this case to support smi inputs.
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
if (instr->RightIsPowerOf2()) {
ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
LOperand* value = UseRegister(instr->left());
LDivI* div = new(zone()) LDivI(value, UseConstant(instr->right()), NULL);
return AssignEnvironment(DefineAsRegister(div));
}
LOperand* dividend = UseRegister(instr->left());
LOperand* divisor = UseRegister(instr->right());
LOperand* temp = instr->CheckFlag(HInstruction::kAllUsesTruncatingToInt32)
? NULL : TempRegister();
LDivI* div = new(zone()) LDivI(dividend, divisor, temp);
return AssignEnvironment(DefineAsRegister(div));
if (instr->representation().IsSmiOrInteger32()) {
// TODO(all): Add Smi support to DoDivI and turn this into a ternary.
if (instr->RightIsPowerOf2()) return DoDivByPowerOf2I(instr);
if (instr->representation().IsInteger32()) return DoDivI(instr);
return DoArithmeticT(Token::DIV, instr);
} else if (instr->representation().IsDouble()) {
return DoArithmeticD(Token::DIV, instr);
} else {
......@@ -1692,13 +1713,37 @@ LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
}
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
HValue* right = instr->right();
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::DoFlooringDivI(HMathFloorOfDiv* instr) {
LOperand* dividend = UseRegister(instr->left());
LOperand* divisor = UseRegister(right);
LOperand* divisor = UseRegister(instr->right());
LOperand* remainder = TempRegister();
return AssignEnvironment(DefineAsRegister(
new(zone()) LMathFloorOfDiv(dividend, divisor, remainder)));
LInstruction* result =
DefineAsRegister(new(zone()) LFlooringDivI(dividend, divisor, remainder));
return AssignEnvironment(result);
}
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
if (instr->RightIsPowerOf2()) {
return DoFlooringDivByPowerOf2I(instr);
} else if (instr->right()->IsConstant()) {
// TODO(svenpanne) Do something more efficient in this case.
return DoFlooringDivI(instr);
} else {
return DoFlooringDivI(instr);
}
}
......@@ -1721,38 +1766,46 @@ LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) {
}
LInstruction* LChunkBuilder::DoMod(HMod* hmod) {
HValue* hleft = hmod->left();
HValue* hright = hmod->right();
// TODO(jbramley): Add smi support.
if (hmod->representation().IsInteger32()) {
ASSERT(hleft->representation().IsInteger32());
ASSERT(hleft->representation().IsInteger32());
LOperand* left_op;
LOperand* right_op;
LInstruction* LChunkBuilder::DoModByPowerOf2I(HMod* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegisterAtStart(instr->left());
int32_t divisor = instr->right()->GetInteger32Constant();
LInstruction* result =
DefineSameAsFirst(new(zone()) LModByPowerOf2I(dividend, divisor));
bool can_deopt =
instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->CanBeNegative();
return can_deopt ? AssignEnvironment(result) : result;
}
if (hmod->RightIsPowerOf2()) {
left_op = UseRegisterAtStart(hleft);
right_op = UseConstant(hright);
} else {
right_op = UseRegister(hright);
left_op = UseRegister(hleft);
}
LModI* lmod = new(zone()) LModI(left_op, right_op);
LInstruction* LChunkBuilder::DoModI(HMod* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegister(instr->left());
LOperand* divisor = UseRegister(instr->right());
LInstruction* result =
DefineAsRegister(new(zone()) LModI(dividend, divisor));
bool can_deopt = (instr->right()->CanBeZero() ||
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->CanBeNegative() && instr->CanBeZero()));
return can_deopt ? AssignEnvironment(result) : result;
}
if (hmod->right()->CanBeZero() ||
(hmod->CheckFlag(HValue::kBailoutOnMinusZero) &&
hmod->left()->CanBeNegative() && hmod->CanBeZero())) {
AssignEnvironment(lmod);
}
return DefineAsRegister(lmod);
} else if (hmod->representation().IsSmiOrTagged()) {
return DoArithmeticT(Token::MOD, hmod);
LInstruction* LChunkBuilder::DoMod(HMod* instr) {
if (instr->representation().IsSmiOrInteger32()) {
// TODO(all): Add Smi support to DoDivI and turn this into a ternary.
if (instr->RightIsPowerOf2()) return DoModByPowerOf2I(instr);
if (instr->representation().IsInteger32()) return DoModI(instr);
return DoArithmeticT(Token::MOD, instr);
} else if (instr->representation().IsDouble()) {
return DoArithmeticD(Token::MOD, instr);
} else {
return DoArithmeticD(Token::MOD, hmod);
return DoArithmeticT(Token::MOD, instr);
}
}
......
......@@ -89,11 +89,14 @@ class LCodeGen;
V(DebugBreak) \
V(DeclareGlobals) \
V(Deoptimize) \
V(DivByPowerOf2I) \
V(DivI) \
V(DoubleToIntOrSmi) \
V(Drop) \
V(Dummy) \
V(DummyUse) \
V(FlooringDivByPowerOf2I) \
V(FlooringDivI) \
V(ForInCacheArray) \
V(ForInPrepareMap) \
V(FunctionLiteral) \
......@@ -133,12 +136,12 @@ class LCodeGen;
V(MathClz32) \
V(MathExp) \
V(MathFloor) \
V(MathFloorOfDiv) \
V(MathLog) \
V(MathMinMax) \
V(MathPowHalf) \
V(MathRound) \
V(MathSqrt) \
V(ModByPowerOf2I) \
V(ModI) \
V(MulConstIS) \
V(MulI) \
......@@ -1248,6 +1251,24 @@ class LDeoptimize V8_FINAL : public LTemplateInstruction<0, 0, 0> {
};
class LDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(DivByPowerOf2I, "div-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
private:
int32_t divisor_;
};
class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
public:
LDivI(LOperand* left, LOperand* right, LOperand* temp) {
......@@ -1263,7 +1284,7 @@ class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); }
DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
DECLARE_HYDROGEN_ACCESSOR(BinaryOperation)
};
......@@ -1878,21 +1899,38 @@ class LMathFloor V8_FINAL : public LUnaryMathOperation<0> {
};
class LMathFloorOfDiv V8_FINAL : public LTemplateInstruction<1, 2, 1> {
class LFlooringDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LMathFloorOfDiv(LOperand* left,
LOperand* right,
LOperand* temp = NULL) {
inputs_[0] = left;
inputs_[1] = right;
LFlooringDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(FlooringDivByPowerOf2I,
"flooring-div-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
private:
int32_t divisor_;
};
class LFlooringDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
public:
LFlooringDivI(LOperand* dividend, LOperand* divisor, LOperand* temp) {
inputs_[0] = dividend;
inputs_[1] = divisor;
temps_[0] = temp;
}
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
LOperand* dividend() { return inputs_[0]; }
LOperand* divisor() { return inputs_[1]; }
LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
DECLARE_CONCRETE_INSTRUCTION(FlooringDivI, "flooring-div-i")
DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
};
......@@ -1953,6 +1991,24 @@ class LMathSqrt V8_FINAL : public LUnaryMathOperation<0> {
};
class LModByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LModByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(ModByPowerOf2I, "mod-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(Mod)
private:
int32_t divisor_;
};
class LModI V8_FINAL : public LTemplateInstruction<1, 2, 0> {
public:
LModI(LOperand* left, LOperand* right) {
......@@ -2843,6 +2899,13 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
#undef DECLARE_DO
LInstruction* DoDivByPowerOf2I(HDiv* instr);
LInstruction* DoDivI(HBinaryOperation* instr);
LInstruction* DoModByPowerOf2I(HMod* instr);
LInstruction* DoModI(HMod* instr);
LInstruction* DoFlooringDivByPowerOf2I(HMathFloorOfDiv* instr);
LInstruction* DoFlooringDivI(HMathFloorOfDiv* instr);
static bool HasMagicNumberForDivision(int32_t divisor);
private:
......
This diff is collapsed.
This diff is collapsed.
......@@ -85,12 +85,15 @@ class LCodeGen;
V(DebugBreak) \
V(DeclareGlobals) \
V(Deoptimize) \
V(DivByPowerOf2I) \
V(DivI) \
V(DoubleToI) \
V(DoubleToSmi) \
V(Drop) \
V(Dummy) \
V(DummyUse) \
V(FlooringDivByConstI) \
V(FlooringDivByPowerOf2I) \
V(ForInCacheArray) \
V(ForInPrepareMap) \
V(FunctionLiteral) \
......@@ -127,12 +130,12 @@ class LCodeGen;
V(MathClz32) \
V(MathExp) \
V(MathFloor) \
V(MathFloorOfDiv) \
V(MathLog) \
V(MathMinMax) \
V(MathPowHalf) \
V(MathRound) \
V(MathSqrt) \
V(ModByPowerOf2I) \
V(ModI) \
V(MulI) \
V(MultiplyAddD) \
......@@ -615,12 +618,27 @@ class LArgumentsElements V8_FINAL : public LTemplateInstruction<1, 0, 0> {
};
class LModByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LModByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(ModByPowerOf2I, "mod-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(Mod)
private:
int32_t divisor_;
};
class LModI V8_FINAL : public LTemplateInstruction<1, 2, 2> {
public:
LModI(LOperand* left,
LOperand* right,
LOperand* temp = NULL,
LOperand* temp2 = NULL) {
LModI(LOperand* left, LOperand* right, LOperand* temp, LOperand* temp2) {
inputs_[0] = left;
inputs_[1] = right;
temps_[0] = temp;
......@@ -637,6 +655,24 @@ class LModI V8_FINAL : public LTemplateInstruction<1, 2, 2> {
};
class LDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(DivByPowerOf2I, "div-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
private:
int32_t divisor_;
};
class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
public:
LDivI(LOperand* left, LOperand* right, LOperand* temp) {
......@@ -652,25 +688,42 @@ class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); }
DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
DECLARE_HYDROGEN_ACCESSOR(BinaryOperation)
};
class LMathFloorOfDiv V8_FINAL : public LTemplateInstruction<1, 2, 1> {
class LFlooringDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LMathFloorOfDiv(LOperand* left,
LOperand* right,
LOperand* temp = NULL) {
inputs_[0] = left;
inputs_[1] = right;
LFlooringDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[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, 2, 1> {
public:
LFlooringDivByConstI(LOperand* dividend, LOperand* divisor, LOperand* temp) {
inputs_[0] = dividend;
inputs_[1] = divisor;
temps_[0] = temp;
}
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
LOperand* dividend() { return inputs_[0]; }
LOperand* divisor() { return inputs_[1]; }
LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
DECLARE_CONCRETE_INSTRUCTION(FlooringDivByConstI, "flooring-div-by-const-i")
DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
};
......@@ -2628,6 +2681,12 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
LInstruction* DoMathSqrt(HUnaryMathOperation* instr);
LInstruction* DoMathPowHalf(HUnaryMathOperation* instr);
LInstruction* DoMathClz32(HUnaryMathOperation* instr);
LInstruction* DoDivByPowerOf2I(HDiv* instr);
LInstruction* DoDivI(HBinaryOperation* instr);
LInstruction* DoModByPowerOf2I(HMod* instr);
LInstruction* DoModI(HMod* instr);
LInstruction* DoFlooringDivByPowerOf2I(HMathFloorOfDiv* instr);
LInstruction* DoFlooringDivByConstI(HMathFloorOfDiv* instr);
private:
enum Status {
......
......@@ -1111,36 +1111,44 @@ void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) {
}
void LCodeGen::DoModI(LModI* instr) {
void LCodeGen::DoModByPowerOf2I(LModByPowerOf2I* instr) {
Register dividend = ToRegister(instr->dividend());
int32_t divisor = instr->divisor();
ASSERT(dividend.is(ToRegister(instr->result())));
// Theoretically, a variation of the branch-free code for integer division by
// a power of 2 (calculating the remainder via an additional multiplication
// (which gets simplified to an 'and') and subtraction) should be faster, and
// this is exactly what GCC and clang emit. Nevertheless, benchmarks seem to
// indicate that positive dividends are heavily favored, so the branching
// version performs better.
HMod* hmod = instr->hydrogen();
HValue* left = hmod->left();
HValue* right = hmod->right();
if (hmod->RightIsPowerOf2()) {
// TODO(svenpanne) We should really do the strength reduction on the
// Hydrogen level.
Register left_reg = ToRegister(instr->left());
Register result_reg = ToRegister(instr->result());
int32_t mask = divisor < 0 ? -(divisor + 1) : (divisor - 1);
Label dividend_is_not_negative, done;
if (hmod->left()->CanBeNegative()) {
__ cmp(dividend, Operand::Zero());
__ b(pl, &dividend_is_not_negative);
// Note that this is correct even for kMinInt operands.
__ rsb(dividend, dividend, Operand::Zero());
__ and_(dividend, dividend, Operand(mask));
__ rsb(dividend, dividend, Operand::Zero(), SetCC);
if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
DeoptimizeIf(eq, instr->environment());
}
__ b(&done);
}
// Note: The code below even works when right contains kMinInt.
int32_t divisor = Abs(right->GetInteger32Constant());
__ bind(&dividend_is_not_negative);
__ and_(dividend, dividend, Operand(mask));
__ bind(&done);
}
Label left_is_not_negative, done;
if (left->CanBeNegative()) {
__ cmp(left_reg, Operand::Zero());
__ b(pl, &left_is_not_negative);
__ rsb(result_reg, left_reg, Operand::Zero());
__ and_(result_reg, result_reg, Operand(divisor - 1));
__ rsb(result_reg, result_reg, Operand::Zero(), SetCC);
if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
DeoptimizeIf(eq, instr->environment());
}
__ b(&done);
}
__ bind(&left_is_not_negative);
__ and_(result_reg, left_reg, Operand(divisor - 1));
__ bind(&done);
} else if (CpuFeatures::IsSupported(SUDIV)) {
void LCodeGen::DoModI(LModI* instr) {
HMod* hmod = instr->hydrogen();
HValue* left = hmod->left();
HValue* right = hmod->right();
if (CpuFeatures::IsSupported(SUDIV)) {
CpuFeatureScope scope(masm(), SUDIV);
Register left_reg = ToRegister(instr->left());
......@@ -1344,50 +1352,53 @@ void LCodeGen::EmitSignedIntegerDivisionByConstant(
}
void LCodeGen::DoDivI(LDivI* instr) {
if (!instr->is_flooring() && instr->hydrogen()->RightIsPowerOf2()) {
Register dividend = ToRegister(instr->left());
HDiv* hdiv = instr->hydrogen();
int32_t divisor = hdiv->right()->GetInteger32Constant();
Register result = ToRegister(instr->result());
ASSERT(!result.is(dividend));
void LCodeGen::DoDivByPowerOf2I(LDivByPowerOf2I* instr) {
Register dividend = ToRegister(instr->dividend());
int32_t divisor = instr->divisor();
Register result = ToRegister(instr->result());
ASSERT(divisor == kMinInt || (divisor != 0 && IsPowerOf2(Abs(divisor))));
ASSERT(!result.is(dividend));
// Check for (0 / -x) that will produce negative zero.
if (hdiv->left()->RangeCanInclude(0) && divisor < 0 &&
hdiv->CheckFlag(HValue::kBailoutOnMinusZero)) {
__ cmp(dividend, Operand::Zero());
DeoptimizeIf(eq, instr->environment());
}
// Check for (kMinInt / -1).
if (hdiv->left()->RangeCanInclude(kMinInt) && divisor == -1 &&
hdiv->CheckFlag(HValue::kCanOverflow)) {
__ cmp(dividend, Operand(kMinInt));
DeoptimizeIf(eq, instr->environment());
}
// Deoptimize if remainder will not be 0.
if (!hdiv->CheckFlag(HInstruction::kAllUsesTruncatingToInt32) &&
Abs(divisor) != 1) {
__ tst(dividend, Operand(Abs(divisor) - 1));
DeoptimizeIf(ne, instr->environment());
}
if (divisor == -1) { // Nice shortcut, not needed for correctness.
__ rsb(result, dividend, Operand(0));
return;
}
int32_t shift = WhichPowerOf2(Abs(divisor));
if (shift == 0) {
__ mov(result, dividend);
} else if (shift == 1) {
__ add(result, dividend, Operand(dividend, LSR, 31));
} else {
__ mov(result, Operand(dividend, ASR, 31));
__ add(result, dividend, Operand(result, LSR, 32 - shift));
}
if (shift > 0) __ mov(result, Operand(result, ASR, shift));
if (divisor < 0) __ rsb(result, result, Operand(0));
// Check for (0 / -x) that will produce negative zero.
HDiv* hdiv = instr->hydrogen();
if (hdiv->CheckFlag(HValue::kBailoutOnMinusZero) &&
hdiv->left()->RangeCanInclude(0) && divisor < 0) {
__ cmp(dividend, Operand::Zero());
DeoptimizeIf(eq, instr->environment());
}
// Check for (kMinInt / -1).
if (hdiv->CheckFlag(HValue::kCanOverflow) &&
hdiv->left()->RangeCanInclude(kMinInt) && divisor == -1) {
__ cmp(dividend, Operand(kMinInt));
DeoptimizeIf(eq, instr->environment());
}
// Deoptimize if remainder will not be 0.
if (!hdiv->CheckFlag(HInstruction::kAllUsesTruncatingToInt32) &&
divisor != 1 && divisor != -1) {
int32_t mask = divisor < 0 ? -(divisor + 1) : (divisor - 1);
__ tst(dividend, Operand(mask));
DeoptimizeIf(ne, instr->environment());
}
if (divisor == -1) { // Nice shortcut, not needed for correctness.
__ rsb(result, dividend, Operand(0));
return;
}
int32_t shift = WhichPowerOf2Abs(divisor);
if (shift == 0) {
__ mov(result, dividend);
} else if (shift == 1) {
__ add(result, dividend, Operand(dividend, LSR, 31));
} else {
__ mov(result, Operand(dividend, ASR, 31));
__ add(result, dividend, Operand(result, LSR, 32 - shift));
}
if (shift > 0) __ mov(result, Operand(result, ASR, shift));
if (divisor < 0) __ rsb(result, result, Operand(0));
}
void LCodeGen::DoDivI(LDivI* instr) {
const Register left = ToRegister(instr->left());
const Register right = ToRegister(instr->right());
const Register result = ToRegister(instr->result());
......@@ -1482,18 +1493,55 @@ void LCodeGen::DoMultiplySubD(LMultiplySubD* instr) {
}
void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
const Register result = ToRegister(instr->result());
const Register left = ToRegister(instr->left());
const Register remainder = ToRegister(instr->temp());
const Register scratch = scratch0();
void LCodeGen::DoFlooringDivByPowerOf2I(LFlooringDivByPowerOf2I* instr) {
Register dividend = ToRegister(instr->dividend());
int32_t divisor = instr->divisor();
ASSERT(dividend.is(ToRegister(instr->result())));
// 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;
int32_t shift = WhichPowerOf2Abs(divisor);
if (divisor > 1) {
__ mov(dividend, Operand(dividend, ASR, shift));
return;
}
// If the divisor is negative, we have to negate and handle edge cases.
Label not_kmin_int, done;
__ rsb(dividend, dividend, Operand::Zero(), SetCC);
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
DeoptimizeIf(eq, instr->environment());
}
if (instr->hydrogen()->left()->RangeCanInclude(kMinInt)) {
// Note that we could emit branch-free code, but that would need one more
// register.
__ b(vc, &not_kmin_int);
if (divisor == -1) {
DeoptimizeIf(al, instr->environment());
} else {
__ mov(dividend, Operand(kMinInt / divisor));
__ b(&done);
}
}
__ bind(&not_kmin_int);
__ mov(dividend, Operand(dividend, ASR, shift));
__ bind(&done);
}
void LCodeGen::DoFlooringDivByConstI(LFlooringDivByConstI* instr) {
Register left = ToRegister(instr->dividend());
Register remainder = ToRegister(instr->temp());
Register scratch = scratch0();
Register result = ToRegister(instr->result());
if (!CpuFeatures::IsSupported(SUDIV)) {
// If the CPU doesn't support sdiv instruction, we only optimize when we
// have magic numbers for the divisor. The standard integer division routine
// is usually slower than transitionning to VFP.
ASSERT(instr->right()->IsConstantOperand());
int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right()));
ASSERT(instr->divisor()->IsConstantOperand());
int32_t divisor = ToInteger32(LConstantOperand::cast(instr->divisor()));
ASSERT(LChunkBuilder::HasMagicNumberForDivisor(divisor));
if (divisor < 0) {
__ cmp(left, Operand::Zero());
......@@ -1511,7 +1559,8 @@ void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
__ sub(result, result, Operand(1), LeaveCC, mi);
} else {
CpuFeatureScope scope(masm(), SUDIV);
const Register right = ToRegister(instr->right());
// TODO(svenpanne) We *statically* know the divisor, use that fact!
Register right = ToRegister(instr->divisor());
// Check for x / 0.
__ cmp(right, Operand::Zero());
......
......@@ -3713,6 +3713,12 @@ class HBinaryOperation : public HTemplateInstruction<3> {
set_operand_position(zone, 2, right_pos);
}
bool RightIsPowerOf2() {
if (!right()->IsInteger32Constant()) return false;
int32_t value = right()->GetInteger32Constant();
return value != 0 && (IsPowerOf2(value) || IsPowerOf2(-value));
}
DECLARE_ABSTRACT_INSTRUCTION(BinaryOperation)
private:
......@@ -4089,12 +4095,6 @@ class HArithmeticBinaryOperation : public HBinaryOperation {
}
}
bool RightIsPowerOf2() {
if (!right()->IsInteger32Constant()) return false;
int32_t value = right()->GetInteger32Constant();
return value != 0 && (IsPowerOf2(value) || IsPowerOf2(-value));
}
DECLARE_ABSTRACT_INSTRUCTION(ArithmeticBinaryOperation)
private:
......
This diff is collapsed.
......@@ -1321,24 +1321,40 @@ LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) {
}
LInstruction* LChunkBuilder::DoDivByPowerOf2I(HDiv* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegister(instr->left());
int32_t divisor = instr->right()->GetInteger32Constant();
LInstruction* result =
DefineAsRegister(new(zone()) LDivByPowerOf2I(dividend, divisor));
bool can_deopt =
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->RangeCanInclude(0) && divisor < 0) ||
(instr->CheckFlag(HValue::kCanOverflow) &&
instr->left()->RangeCanInclude(kMinInt) && divisor == -1) ||
(!instr->CheckFlag(HInstruction::kAllUsesTruncatingToInt32) &&
divisor != 1 && divisor != -1);
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoDivI(HBinaryOperation* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseFixed(instr->left(), eax);
LOperand* divisor = UseRegister(instr->right());
LOperand* temp = FixedTemp(edx);
LDivI* result = new(zone()) LDivI(dividend, divisor, temp);
return AssignEnvironment(DefineFixed(result, eax));
}
LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
if (instr->representation().IsSmiOrInteger32()) {
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
if (instr->RightIsPowerOf2()) {
ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
LOperand* value = UseRegister(instr->left());
LDivI* div =
new(zone()) LDivI(value, UseOrConstant(instr->right()), NULL);
return AssignEnvironment(DefineAsRegister(div));
}
// The temporary operand is necessary to ensure that right is not allocated
// into edx.
LOperand* temp = FixedTemp(edx);
LOperand* dividend = UseFixed(instr->left(), eax);
LOperand* divisor = UseRegister(instr->right());
LDivI* result = new(zone()) LDivI(dividend, divisor, temp);
return AssignEnvironment(DefineFixed(result, eax));
return instr->RightIsPowerOf2() ? DoDivByPowerOf2I(instr) : DoDivI(instr);
} else if (instr->representation().IsDouble()) {
return DoArithmeticD(Token::DIV, instr);
} else {
......@@ -1347,79 +1363,79 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
}
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) {
LOperand* dividend = UseFixed(instr->left(), eax);
int32_t divisor = instr->right()->GetInteger32Constant();
LOperand* temp = TempRegister();
LInstruction* result =
DefineFixed(
new(zone()) LFlooringDivByConstI(dividend, divisor, temp), edx);
bool can_deopt = divisor <= 0;
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
HValue* right = instr->right();
if (!right->IsConstant()) {
ASSERT(right->representation().IsInteger32());
// The temporary operand is necessary to ensure that right is not allocated
// into edx.
LOperand* temp = FixedTemp(edx);
LOperand* dividend = UseFixed(instr->left(), eax);
LOperand* divisor = UseRegister(instr->right());
LDivI* flooring_div = new(zone()) LDivI(dividend, divisor, temp);
return AssignEnvironment(DefineFixed(flooring_div, eax));
}
ASSERT(right->IsConstant() && HConstant::cast(right)->HasInteger32Value());
LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right));
int32_t divisor_si = HConstant::cast(right)->Integer32Value();
if (divisor_si == 0) {
LOperand* dividend = UseRegister(instr->left());
return AssignEnvironment(DefineAsRegister(
new(zone()) LMathFloorOfDiv(dividend, divisor, NULL)));
} else if (IsPowerOf2(abs(divisor_si))) {
// use dividend as temp if divisor < 0 && divisor != -1
LOperand* dividend = divisor_si < -1 ? UseTempRegister(instr->left()) :
UseRegisterAtStart(instr->left());
LInstruction* result = DefineAsRegister(
new(zone()) LMathFloorOfDiv(dividend, divisor, NULL));
return divisor_si < 0 ? AssignEnvironment(result) : result;
if (instr->RightIsPowerOf2()) {
return DoFlooringDivByPowerOf2I(instr);
} else if (instr->right()->IsConstant()) {
return DoFlooringDivByConstI(instr);
} else {
// needs edx:eax, plus a temp
LOperand* dividend = UseFixed(instr->left(), eax);
LOperand* temp = TempRegister();
LInstruction* result = DefineFixed(
new(zone()) LMathFloorOfDiv(dividend, divisor, temp), edx);
return divisor_si < 0 ? AssignEnvironment(result) : result;
return DoDivI(instr);
}
}
LInstruction* LChunkBuilder::DoModByPowerOf2I(HMod* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegisterAtStart(instr->left());
int32_t divisor = instr->right()->GetInteger32Constant();
LInstruction* result =
DefineSameAsFirst(new(zone()) LModByPowerOf2I(dividend, divisor));
bool can_deopt =
instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->CanBeNegative();
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoModI(HMod* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseFixed(instr->left(), eax);
LOperand* divisor = UseRegister(instr->right());
LOperand* temp = FixedTemp(edx);
LInstruction* result =
DefineFixed(new(zone()) LModI(dividend, divisor, temp), edx);
bool can_deopt = (instr->right()->CanBeZero() ||
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->RangeCanInclude(kMinInt) &&
instr->right()->RangeCanInclude(-1)) ||
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->CanBeNegative() &&
instr->CanBeZero()));
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoMod(HMod* instr) {
HValue* left = instr->left();
HValue* right = instr->right();
if (instr->representation().IsSmiOrInteger32()) {
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
if (instr->RightIsPowerOf2()) {
ASSERT(!right->CanBeZero());
LModI* mod = new(zone()) LModI(UseRegisterAtStart(left),
UseOrConstant(right),
NULL);
LInstruction* result = DefineSameAsFirst(mod);
return (left->CanBeNegative() &&
instr->CheckFlag(HValue::kBailoutOnMinusZero))
? AssignEnvironment(result)
: result;
return AssignEnvironment(DefineSameAsFirst(mod));
} else {
// The temporary operand is necessary to ensure that right is not
// allocated into edx.
LModI* mod = new(zone()) LModI(UseFixed(left, eax),
UseRegister(right),
FixedTemp(edx));
LInstruction* result = DefineFixed(mod, edx);
return (right->CanBeZero() ||
(left->RangeCanInclude(kMinInt) &&
right->RangeCanInclude(-1) &&
instr->CheckFlag(HValue::kBailoutOnMinusZero)) ||
(left->CanBeNegative() &&
instr->CanBeZero() &&
instr->CheckFlag(HValue::kBailoutOnMinusZero)))
? AssignEnvironment(result)
: result;
}
return instr->RightIsPowerOf2() ? DoModByPowerOf2I(instr) : DoModI(instr);
} else if (instr->representation().IsDouble()) {
return DoArithmeticD(Token::MOD, instr);
} else {
......
......@@ -87,12 +87,15 @@ class LCodeGen;
V(DebugBreak) \
V(DeclareGlobals) \
V(Deoptimize) \
V(DivByPowerOf2I) \
V(DivI) \
V(DoubleToI) \
V(DoubleToSmi) \
V(Drop) \
V(Dummy) \
V(DummyUse) \
V(FlooringDivByConstI) \
V(FlooringDivByPowerOf2I) \
V(ForInCacheArray) \
V(ForInPrepareMap) \
V(FunctionLiteral) \
......@@ -129,12 +132,12 @@ class LCodeGen;
V(MathClz32) \
V(MathExp) \
V(MathFloor) \
V(MathFloorOfDiv) \
V(MathLog) \
V(MathMinMax) \
V(MathPowHalf) \
V(MathRound) \
V(MathSqrt) \
V(ModByPowerOf2I) \
V(ModI) \
V(MulI) \
V(NumberTagD) \
......@@ -634,6 +637,24 @@ class LDebugBreak V8_FINAL : public LTemplateInstruction<0, 0, 0> {
};
class LModByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LModByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(ModByPowerOf2I, "mod-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(Mod)
private:
int32_t divisor_;
};
class LModI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
public:
LModI(LOperand* left, LOperand* right, LOperand* temp) {
......@@ -651,6 +672,24 @@ class LModI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
};
class LDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(DivByPowerOf2I, "div-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
private:
int32_t divisor_;
};
class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
public:
LDivI(LOperand* left, LOperand* right, LOperand* temp) {
......@@ -661,30 +700,51 @@ class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
LOperand* temp() { return temps_[0]; }
bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); }
DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
DECLARE_HYDROGEN_ACCESSOR(BinaryOperation)
};
class LMathFloorOfDiv V8_FINAL : public LTemplateInstruction<1, 2, 1> {
class LFlooringDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LMathFloorOfDiv(LOperand* left,
LOperand* right,
LOperand* temp = NULL) {
inputs_[0] = left;
inputs_[1] = right;
LFlooringDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { 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, 1> {
public:
LFlooringDivByConstI(LOperand* dividend, int32_t divisor, LOperand* temp) {
inputs_[0] = dividend;
divisor_ = divisor;
temps_[0] = temp;
}
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
DECLARE_CONCRETE_INSTRUCTION(FlooringDivByConstI, "flooring-div-by-const-i")
DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
private:
int32_t divisor_;
};
......@@ -2630,6 +2690,12 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
LInstruction* DoMathSqrt(HUnaryMathOperation* instr);
LInstruction* DoMathPowHalf(HUnaryMathOperation* instr);
LInstruction* DoMathClz32(HUnaryMathOperation* instr);
LInstruction* DoDivByPowerOf2I(HDiv* instr);
LInstruction* DoDivI(HBinaryOperation* instr);
LInstruction* DoModByPowerOf2I(HMod* instr);
LInstruction* DoModI(HMod* instr);
LInstruction* DoFlooringDivByPowerOf2I(HMathFloorOfDiv* instr);
LInstruction* DoFlooringDivByConstI(HMathFloorOfDiv* instr);
private:
enum Status {
......
......@@ -283,6 +283,12 @@ inline int StrLength(const char* string) {
}
// TODO(svenpanne) Clean up the whole power-of-2 mess.
inline int32_t WhichPowerOf2Abs(int32_t x) {
return (x == kMinInt) ? 31 : WhichPowerOf2(Abs(x));
}
// ----------------------------------------------------------------------------
// BitField is a help template for encoding and decode bitfield with
// unsigned content.
......
This diff is collapsed.
......@@ -1242,24 +1242,41 @@ LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) {
}
LInstruction* LChunkBuilder::DoDivByPowerOf2I(HDiv* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegister(instr->left());
int32_t divisor = instr->right()->GetInteger32Constant();
LInstruction* result =
DefineAsRegister(new(zone()) LDivByPowerOf2I(dividend, divisor));
bool can_deopt =
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->RangeCanInclude(0) && divisor < 0) ||
(instr->CheckFlag(HValue::kCanOverflow) &&
instr->left()->RangeCanInclude(kMinInt) && divisor == -1) ||
(!instr->CheckFlag(HInstruction::kAllUsesTruncatingToInt32) &&
divisor != 1 && divisor != -1);
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoDivI(HBinaryOperation* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseFixed(instr->left(), rax);
LOperand* divisor = UseRegister(instr->right());
LOperand* temp = FixedTemp(rdx);
LInstruction* result =
DefineFixed(new(zone()) LDivI(dividend, divisor, temp), rax);
return AssignEnvironment(result);
}
LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
if (instr->representation().IsSmiOrInteger32()) {
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
if (instr->RightIsPowerOf2()) {
ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
LOperand* value = UseRegister(instr->left());
LDivI* div =
new(zone()) LDivI(value, UseOrConstant(instr->right()), NULL);
return AssignEnvironment(DefineAsRegister(div));
}
// The temporary operand is necessary to ensure that right is not allocated
// into rdx.
LOperand* temp = FixedTemp(rdx);
LOperand* dividend = UseFixed(instr->left(), rax);
LOperand* divisor = UseRegister(instr->right());
LDivI* result = new(zone()) LDivI(dividend, divisor, temp);
return AssignEnvironment(DefineFixed(result, rax));
return instr->RightIsPowerOf2() ? DoDivByPowerOf2I(instr) : DoDivI(instr);
} else if (instr->representation().IsDouble()) {
return DoArithmeticD(Token::DIV, instr);
} else {
......@@ -1268,75 +1285,79 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
}
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) {
LOperand* dividend = UseRegisterAtStart(instr->left());
int32_t divisor = instr->right()->GetInteger32Constant();
LOperand* temp = TempRegister();
LInstruction* result =
DefineAsRegister(
new(zone()) LFlooringDivByConstI(dividend, divisor, temp));
bool can_deopt = divisor <= 0;
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
HValue* right = instr->right();
if (!right->IsConstant()) {
ASSERT(right->representation().IsInteger32());
// The temporary operand is necessary to ensure that right is not allocated
// into rdx.
LOperand* temp = FixedTemp(rdx);
LOperand* dividend = UseFixed(instr->left(), rax);
LOperand* divisor = UseRegister(instr->right());
LDivI* flooring_div = new(zone()) LDivI(dividend, divisor, temp);
return AssignEnvironment(DefineFixed(flooring_div, rax));
}
ASSERT(right->IsConstant() && HConstant::cast(right)->HasInteger32Value());
LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right));
int32_t divisor_si = HConstant::cast(right)->Integer32Value();
if (divisor_si == 0) {
LOperand* dividend = UseRegister(instr->left());
return AssignEnvironment(DefineAsRegister(
new(zone()) LMathFloorOfDiv(dividend, divisor, NULL)));
} else if (IsPowerOf2(abs(divisor_si))) {
LOperand* dividend = UseRegisterAtStart(instr->left());
LInstruction* result = DefineAsRegister(
new(zone()) LMathFloorOfDiv(dividend, divisor, NULL));
return divisor_si < 0 ? AssignEnvironment(result) : result;
if (instr->RightIsPowerOf2()) {
return DoFlooringDivByPowerOf2I(instr);
} else if (instr->right()->IsConstant()) {
return DoFlooringDivByConstI(instr);
} else {
// use two r64
LOperand* dividend = UseRegisterAtStart(instr->left());
LOperand* temp = TempRegister();
LInstruction* result = DefineAsRegister(
new(zone()) LMathFloorOfDiv(dividend, divisor, temp));
return divisor_si < 0 ? AssignEnvironment(result) : result;
return DoDivI(instr);
}
}
LInstruction* LChunkBuilder::DoModByPowerOf2I(HMod* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegisterAtStart(instr->left());
int32_t divisor = instr->right()->GetInteger32Constant();
LInstruction* result =
DefineSameAsFirst(new(zone()) LModByPowerOf2I(dividend, divisor));
bool can_deopt =
instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->CanBeNegative();
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoModI(HMod* instr) {
ASSERT(instr->representation().IsSmiOrInteger32());
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseFixed(instr->left(), rax);
LOperand* divisor = UseRegister(instr->right());
LOperand* temp = FixedTemp(rdx);
LInstruction* result =
DefineFixed(new(zone()) LModI(dividend, divisor, temp), rdx);
bool can_deopt = (instr->right()->CanBeZero() ||
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->RangeCanInclude(kMinInt) &&
instr->right()->RangeCanInclude(-1)) ||
(instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
instr->left()->CanBeNegative() &&
instr->CanBeZero()));
return can_deopt ? AssignEnvironment(result) : result;
}
LInstruction* LChunkBuilder::DoMod(HMod* instr) {
HValue* left = instr->left();
HValue* right = instr->right();
if (instr->representation().IsSmiOrInteger32()) {
ASSERT(left->representation().Equals(instr->representation()));
ASSERT(right->representation().Equals(instr->representation()));
if (instr->RightIsPowerOf2()) {
ASSERT(!right->CanBeZero());
LModI* mod = new(zone()) LModI(UseRegisterAtStart(left),
UseOrConstant(right),
NULL);
LInstruction* result = DefineSameAsFirst(mod);
return (left->CanBeNegative() &&
instr->CheckFlag(HValue::kBailoutOnMinusZero))
? AssignEnvironment(result)
: result;
} else {
// The temporary operand is necessary to ensure that right is not
// allocated into edx.
LModI* mod = new(zone()) LModI(UseFixed(left, rax),
UseRegister(right),
FixedTemp(rdx));
LInstruction* result = DefineFixed(mod, rdx);
return (right->CanBeZero() ||
(left->RangeCanInclude(kMinInt) &&
right->RangeCanInclude(-1) &&
instr->CheckFlag(HValue::kBailoutOnMinusZero)) ||
(left->CanBeNegative() &&
instr->CanBeZero() &&
instr->CheckFlag(HValue::kBailoutOnMinusZero)))
? AssignEnvironment(result)
: result;
}
return instr->RightIsPowerOf2() ? DoModByPowerOf2I(instr) : DoModI(instr);
} else if (instr->representation().IsDouble()) {
return DoArithmeticD(Token::MOD, instr);
} else {
......
......@@ -85,12 +85,15 @@ class LCodeGen;
V(DebugBreak) \
V(DeclareGlobals) \
V(Deoptimize) \
V(DivByPowerOf2I) \
V(DivI) \
V(DoubleToI) \
V(DoubleToSmi) \
V(Drop) \
V(DummyUse) \
V(Dummy) \
V(FlooringDivByConstI) \
V(FlooringDivByPowerOf2I) \
V(ForInCacheArray) \
V(ForInPrepareMap) \
V(FunctionLiteral) \
......@@ -127,12 +130,12 @@ class LCodeGen;
V(MathClz32) \
V(MathExp) \
V(MathFloor) \
V(MathFloorOfDiv) \
V(MathLog) \
V(MathMinMax) \
V(MathPowHalf) \
V(MathRound) \
V(MathSqrt) \
V(ModByPowerOf2I) \
V(ModI) \
V(MulI) \
V(NumberTagD) \
......@@ -615,6 +618,24 @@ class LArgumentsElements V8_FINAL : public LTemplateInstruction<1, 0, 0> {
};
class LModByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LModByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(ModByPowerOf2I, "mod-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(Mod)
private:
int32_t divisor_;
};
class LModI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
public:
LModI(LOperand* left, LOperand* right, LOperand* temp) {
......@@ -632,6 +653,24 @@ class LModI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
};
class LDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
DECLARE_CONCRETE_INSTRUCTION(DivByPowerOf2I, "div-by-power-of-2-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
private:
int32_t divisor_;
};
class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
public:
LDivI(LOperand* left, LOperand* right, LOperand* temp) {
......@@ -647,26 +686,46 @@ class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); }
DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
DECLARE_HYDROGEN_ACCESSOR(BinaryOperation)
};
class LMathFloorOfDiv V8_FINAL : public LTemplateInstruction<1, 2, 1> {
class LFlooringDivByPowerOf2I V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
LMathFloorOfDiv(LOperand* left,
LOperand* right,
LOperand* temp = NULL) {
inputs_[0] = left;
inputs_[1] = right;
LFlooringDivByPowerOf2I(LOperand* dividend, int32_t divisor) {
inputs_[0] = dividend;
divisor_ = divisor;
}
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { 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, 1> {
public:
LFlooringDivByConstI(LOperand* dividend, int32_t divisor, LOperand* temp) {
inputs_[0] = dividend;
divisor_ = divisor;
temps_[0] = temp;
}
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
LOperand* dividend() { return inputs_[0]; }
int32_t divisor() const { return divisor_; }
LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
DECLARE_CONCRETE_INSTRUCTION(FlooringDivByConstI, "flooring-div-by-const-i")
DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
private:
int32_t divisor_;
};
......@@ -2545,6 +2604,12 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
LInstruction* DoMathSqrt(HUnaryMathOperation* instr);
LInstruction* DoMathPowHalf(HUnaryMathOperation* instr);
LInstruction* DoMathClz32(HUnaryMathOperation* instr);
LInstruction* DoDivByPowerOf2I(HDiv* instr);
LInstruction* DoDivI(HBinaryOperation* instr);
LInstruction* DoModByPowerOf2I(HMod* instr);
LInstruction* DoModI(HMod* instr);
LInstruction* DoFlooringDivByPowerOf2I(HMathFloorOfDiv* instr);
LInstruction* DoFlooringDivByConstI(HMathFloorOfDiv* instr);
private:
enum Status {
......
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