Commit e536abb7 authored by yangguo@chromium.org's avatar yangguo@chromium.org

Handle non-constant divisor in MathFloorOfDiv, on ia32/x64

Zheng Liu
zheng.z.liu@intel.com

Review URL: https://chromiumcodereview.appspot.com/11624022
Patch from Zheng Liu <zheng.z.liu@intel.com>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13289 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent eb27eb03
...@@ -1212,7 +1212,7 @@ void LCodeGen::DoModI(LModI* instr) { ...@@ -1212,7 +1212,7 @@ void LCodeGen::DoModI(LModI* instr) {
void LCodeGen::DoDivI(LDivI* instr) { void LCodeGen::DoDivI(LDivI* instr) {
if (instr->hydrogen()->HasPowerOf2Divisor()) { if (!instr->is_flooring() && instr->hydrogen()->HasPowerOf2Divisor()) {
Register dividend = ToRegister(instr->left()); Register dividend = ToRegister(instr->left());
int32_t divisor = int32_t divisor =
HConstant::cast(instr->hydrogen()->right())->Integer32Value(); HConstant::cast(instr->hydrogen()->right())->Integer32Value();
...@@ -1259,13 +1259,13 @@ void LCodeGen::DoDivI(LDivI* instr) { ...@@ -1259,13 +1259,13 @@ void LCodeGen::DoDivI(LDivI* instr) {
// Check for x / 0. // Check for x / 0.
Register right_reg = ToRegister(right); Register right_reg = ToRegister(right);
if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { if (instr->hydrogen_value()->CheckFlag(HValue::kCanBeDivByZero)) {
__ test(right_reg, ToOperand(right)); __ test(right_reg, ToOperand(right));
DeoptimizeIf(zero, instr->environment()); DeoptimizeIf(zero, instr->environment());
} }
// Check for (0 / -x) that will produce negative zero. // Check for (0 / -x) that will produce negative zero.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { if (instr->hydrogen_value()->CheckFlag(HValue::kBailoutOnMinusZero)) {
Label left_not_zero; Label left_not_zero;
__ test(left_reg, Operand(left_reg)); __ test(left_reg, Operand(left_reg));
__ j(not_zero, &left_not_zero, Label::kNear); __ j(not_zero, &left_not_zero, Label::kNear);
...@@ -1275,7 +1275,7 @@ void LCodeGen::DoDivI(LDivI* instr) { ...@@ -1275,7 +1275,7 @@ void LCodeGen::DoDivI(LDivI* instr) {
} }
// Check for (kMinInt / -1). // Check for (kMinInt / -1).
if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) { if (instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow)) {
Label left_not_min_int; Label left_not_min_int;
__ cmp(left_reg, kMinInt); __ cmp(left_reg, kMinInt);
__ j(not_zero, &left_not_min_int, Label::kNear); __ j(not_zero, &left_not_min_int, Label::kNear);
...@@ -1288,9 +1288,19 @@ void LCodeGen::DoDivI(LDivI* instr) { ...@@ -1288,9 +1288,19 @@ void LCodeGen::DoDivI(LDivI* instr) {
__ cdq(); __ cdq();
__ idiv(right_reg); __ idiv(right_reg);
// Deoptimize if remainder is not 0. if (!instr->is_flooring()) {
__ test(edx, Operand(edx)); // Deoptimize if remainder is not 0.
DeoptimizeIf(not_zero, instr->environment()); __ test(edx, Operand(edx));
DeoptimizeIf(not_zero, instr->environment());
} else {
Label done;
__ test(edx, edx);
__ j(zero, &done, Label::kNear);
__ xor_(edx, right_reg);
__ sar(edx, 31);
__ add(eax, edx);
__ bind(&done);
}
} }
......
...@@ -1305,12 +1305,31 @@ HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) { ...@@ -1305,12 +1305,31 @@ HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
return constant_val->CopyToRepresentation(Representation::Integer32(), return constant_val->CopyToRepresentation(Representation::Integer32(),
divisor->block()->zone()); divisor->block()->zone());
} }
// A value with an integer representation does not need to be transformed.
if (divisor->representation().IsInteger32()) {
return divisor;
// A change from an integer32 can be replaced by the integer32 value.
} else if (divisor->IsChange() &&
HChange::cast(divisor)->from().IsInteger32()) {
return HChange::cast(divisor)->value();
}
return NULL; return NULL;
} }
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
HValue* right = instr->right(); 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()); ASSERT(right->IsConstant() && HConstant::cast(right)->HasInteger32Value());
LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right)); LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right));
int32_t divisor_si = HConstant::cast(right)->Integer32Value(); int32_t divisor_si = HConstant::cast(right)->Integer32Value();
......
...@@ -565,6 +565,8 @@ class LDivI: public LTemplateInstruction<1, 2, 1> { ...@@ -565,6 +565,8 @@ class LDivI: public LTemplateInstruction<1, 2, 1> {
LOperand* left() { return inputs_[0]; } LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; } LOperand* right() { return inputs_[1]; }
bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); }
DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i") DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
DECLARE_HYDROGEN_ACCESSOR(Div) DECLARE_HYDROGEN_ACCESSOR(Div)
}; };
......
...@@ -1144,7 +1144,7 @@ void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) { ...@@ -1144,7 +1144,7 @@ void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
void LCodeGen::DoDivI(LDivI* instr) { void LCodeGen::DoDivI(LDivI* instr) {
if (instr->hydrogen()->HasPowerOf2Divisor()) { if (!instr->is_flooring() && instr->hydrogen()->HasPowerOf2Divisor()) {
Register dividend = ToRegister(instr->left()); Register dividend = ToRegister(instr->left());
int32_t divisor = int32_t divisor =
HConstant::cast(instr->hydrogen()->right())->Integer32Value(); HConstant::cast(instr->hydrogen()->right())->Integer32Value();
...@@ -1191,13 +1191,13 @@ void LCodeGen::DoDivI(LDivI* instr) { ...@@ -1191,13 +1191,13 @@ void LCodeGen::DoDivI(LDivI* instr) {
// Check for x / 0. // Check for x / 0.
Register right_reg = ToRegister(right); Register right_reg = ToRegister(right);
if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { if (instr->hydrogen_value()->CheckFlag(HValue::kCanBeDivByZero)) {
__ testl(right_reg, right_reg); __ testl(right_reg, right_reg);
DeoptimizeIf(zero, instr->environment()); DeoptimizeIf(zero, instr->environment());
} }
// Check for (0 / -x) that will produce negative zero. // Check for (0 / -x) that will produce negative zero.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { if (instr->hydrogen_value()->CheckFlag(HValue::kBailoutOnMinusZero)) {
Label left_not_zero; Label left_not_zero;
__ testl(left_reg, left_reg); __ testl(left_reg, left_reg);
__ j(not_zero, &left_not_zero, Label::kNear); __ j(not_zero, &left_not_zero, Label::kNear);
...@@ -1207,7 +1207,7 @@ void LCodeGen::DoDivI(LDivI* instr) { ...@@ -1207,7 +1207,7 @@ void LCodeGen::DoDivI(LDivI* instr) {
} }
// Check for (kMinInt / -1). // Check for (kMinInt / -1).
if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) { if (instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow)) {
Label left_not_min_int; Label left_not_min_int;
__ cmpl(left_reg, Immediate(kMinInt)); __ cmpl(left_reg, Immediate(kMinInt));
__ j(not_zero, &left_not_min_int, Label::kNear); __ j(not_zero, &left_not_min_int, Label::kNear);
...@@ -1220,9 +1220,19 @@ void LCodeGen::DoDivI(LDivI* instr) { ...@@ -1220,9 +1220,19 @@ void LCodeGen::DoDivI(LDivI* instr) {
__ cdq(); __ cdq();
__ idivl(right_reg); __ idivl(right_reg);
// Deoptimize if remainder is not 0. if (!instr->is_flooring()) {
__ testl(rdx, rdx); // Deoptimize if remainder is not 0.
DeoptimizeIf(not_zero, instr->environment()); __ testl(rdx, rdx);
DeoptimizeIf(not_zero, instr->environment());
} else {
Label done;
__ testl(rdx, rdx);
__ j(zero, &done, Label::kNear);
__ xorl(rdx, right_reg);
__ sarl(rdx, Immediate(31));
__ addl(rax, rdx);
__ bind(&done);
}
} }
...@@ -1712,6 +1722,7 @@ void LCodeGen::DoArithmeticD(LArithmeticD* instr) { ...@@ -1712,6 +1722,7 @@ void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
break; break;
case Token::DIV: case Token::DIV:
__ divsd(left, right); __ divsd(left, right);
__ movaps(left, left);
break; break;
case Token::MOD: case Token::MOD:
__ PrepareCallCFunction(2); __ PrepareCallCFunction(2);
......
...@@ -1228,12 +1228,31 @@ HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) { ...@@ -1228,12 +1228,31 @@ HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
return constant_val->CopyToRepresentation(Representation::Integer32(), return constant_val->CopyToRepresentation(Representation::Integer32(),
divisor->block()->zone()); divisor->block()->zone());
} }
// A value with an integer representation does not need to be transformed.
if (divisor->representation().IsInteger32()) {
return divisor;
// A change from an integer32 can be replaced by the integer32 value.
} else if (divisor->IsChange() &&
HChange::cast(divisor)->from().IsInteger32()) {
return HChange::cast(divisor)->value();
}
return NULL; return NULL;
} }
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
HValue* right = instr->right(); 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()); ASSERT(right->IsConstant() && HConstant::cast(right)->HasInteger32Value());
LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right)); LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right));
int32_t divisor_si = HConstant::cast(right)->Integer32Value(); int32_t divisor_si = HConstant::cast(right)->Integer32Value();
......
...@@ -573,6 +573,8 @@ class LDivI: public LTemplateInstruction<1, 2, 1> { ...@@ -573,6 +573,8 @@ class LDivI: public LTemplateInstruction<1, 2, 1> {
LOperand* right() { return inputs_[1]; } LOperand* right() { return inputs_[1]; }
LOperand* temp() { return temps_[0]; } LOperand* temp() { return temps_[0]; }
bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); }
DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i") DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
DECLARE_HYDROGEN_ACCESSOR(Div) DECLARE_HYDROGEN_ACCESSOR(Div)
}; };
......
...@@ -184,10 +184,38 @@ test_div(); ...@@ -184,10 +184,38 @@ test_div();
%OptimizeFunctionOnNextCall(test_div); %OptimizeFunctionOnNextCall(test_div);
test_div(); test_div();
// Test for ia32/x64 flooring correctness.
var values2 = [1, 3, 10, 99, 100, 101, 0x7fffffff];
function test_div2() {
for (var i = 0; i < values2.length; i++) {
for (var j = 0; j < values2.length; j++) {
assertEquals(Math.floor(div((values2[i] | 0), (values2[j] | 0))),
Math.floor((values2[i] | 0) / (values2[j] | 0)));
assertEquals(Math.floor(div(-(values2[i] | 0), (values2[j] | 0))),
Math.floor(-(values2[i] | 0) / (values2[j] | 0)));
assertEquals(Math.floor(div((values2[i] | 0), -(values2[j] | 0))),
Math.floor((values2[i] | 0) / -(values2[j] | 0)));
assertEquals(Math.floor(div(-(values2[i] | 0), -(values2[j] | 0))),
Math.floor(-(values2[i] | 0) / -(values2[j] | 0)));
}
}
}
test_div2();
%OptimizeFunctionOnNextCall(test_div2);
test_div2();
// Test for negative zero, overflow and division by 0. // Test for negative zero, overflow and division by 0.
// Separate the tests to prevent deoptimizations from making the other optimized // Separate the tests to prevent deoptimizations from making the other optimized
// test unreachable. // test unreachable.
// We box the value in an array to avoid constant propagation.
var neg_one_in_array = [-1];
var zero_in_array = [0];
var min_int_in_array = [-2147483648];
// Test for dividing by constant.
function IsNegativeZero(x) { function IsNegativeZero(x) {
assertTrue(x == 0); // Is 0 or -0. assertTrue(x == 0); // Is 0 or -0.
var y = 1 / x; var y = 1 / x;
...@@ -196,15 +224,12 @@ function IsNegativeZero(x) { ...@@ -196,15 +224,12 @@ function IsNegativeZero(x) {
} }
function test_div_deopt_minus_zero() { function test_div_deopt_minus_zero() {
var zero_in_array = [0];
for (var i = 0; i < 2; ++i) { for (var i = 0; i < 2; ++i) {
assertTrue(IsNegativeZero(Math.floor((zero_in_array[0] | 0) / -1))); assertTrue(IsNegativeZero(Math.floor((zero_in_array[0] | 0) / -1)));
} }
} }
function test_div_deopt_overflow() { function test_div_deopt_overflow() {
// We box the value in an array to avoid constant propagation.
var min_int_in_array = [-2147483648];
for (var i = 0; i < 2; ++i) { for (var i = 0; i < 2; ++i) {
// We use '| 0' to force the representation to int32. // We use '| 0' to force the representation to int32.
assertEquals(-min_int_in_array[0], assertEquals(-min_int_in_array[0],
...@@ -228,3 +253,36 @@ test_div_deopt_div_by_zero(); ...@@ -228,3 +253,36 @@ test_div_deopt_div_by_zero();
test_div_deopt_minus_zero(); test_div_deopt_minus_zero();
test_div_deopt_overflow(); test_div_deopt_overflow();
test_div_deopt_div_by_zero(); test_div_deopt_div_by_zero();
// Test for dividing by variable.
function test_div_deopt_minus_zero_v() {
for (var i = 0; i < 2; ++i) {
assertTrue(IsNegativeZero(Math.floor((zero_in_array[0] | 0) /
neg_one_in_array[0])));
}
}
function test_div_deopt_overflow_v() {
for (var i = 0; i < 2; ++i) {
// We use '| 0' to force the representation to int32.
assertEquals(-min_int_in_array[0],
Math.floor((min_int_in_array[0] | 0) / neg_one_in_array[0]));
}
}
function test_div_deopt_div_by_zero_v() {
for (var i = 0; i < 2; ++i) {
assertEquals(div(i, 0),
Math.floor(i / zero_in_array[0]));
}
}
test_div_deopt_minus_zero_v();
test_div_deopt_overflow_v();
test_div_deopt_div_by_zero_v();
%OptimizeFunctionOnNextCall(test_div_deopt_minus_zero_v);
%OptimizeFunctionOnNextCall(test_div_deopt_overflow_v);
%OptimizeFunctionOnNextCall(test_div_deopt_div_by_zero_v);
test_div_deopt_minus_zero_v();
test_div_deopt_overflow_v();
test_div_deopt_div_by_zero_v();
...@@ -184,10 +184,38 @@ test_div(); ...@@ -184,10 +184,38 @@ test_div();
%OptimizeFunctionOnNextCall(test_div); %OptimizeFunctionOnNextCall(test_div);
test_div(); test_div();
// Test for ia32/x64 flooring correctness.
var values2 = [1, 3, 10, 99, 100, 101, 0x7fffffff];
function test_div2() {
for (var i = 0; i < values2.length; i++) {
for (var j = 0; j < values2.length; j++) {
assertEquals(Math.floor(div((values2[i] | 0), (values2[j] | 0))),
Math.floor((values2[i] | 0) / (values2[j] | 0)));
assertEquals(Math.floor(div(-(values2[i] | 0), (values2[j] | 0))),
Math.floor(-(values2[i] | 0) / (values2[j] | 0)));
assertEquals(Math.floor(div((values2[i] | 0), -(values2[j] | 0))),
Math.floor((values2[i] | 0) / -(values2[j] | 0)));
assertEquals(Math.floor(div(-(values2[i] | 0), -(values2[j] | 0))),
Math.floor(-(values2[i] | 0) / -(values2[j] | 0)));
}
}
}
test_div2();
%OptimizeFunctionOnNextCall(test_div2);
test_div2();
// Test for negative zero, overflow and division by 0. // Test for negative zero, overflow and division by 0.
// Separate the tests to prevent deoptimizations from making the other optimized // Separate the tests to prevent deoptimizations from making the other optimized
// test unreachable. // test unreachable.
// We box the value in an array to avoid constant propagation.
var neg_one_in_array = [-1];
var zero_in_array = [0];
var min_int_in_array = [-2147483648];
// Test for dividing by constant.
function IsNegativeZero(x) { function IsNegativeZero(x) {
assertTrue(x == 0); // Is 0 or -0. assertTrue(x == 0); // Is 0 or -0.
var y = 1 / x; var y = 1 / x;
...@@ -196,15 +224,12 @@ function IsNegativeZero(x) { ...@@ -196,15 +224,12 @@ function IsNegativeZero(x) {
} }
function test_div_deopt_minus_zero() { function test_div_deopt_minus_zero() {
var zero_in_array = [0];
for (var i = 0; i < 2; ++i) { for (var i = 0; i < 2; ++i) {
assertTrue(IsNegativeZero(Math.floor((zero_in_array[0] | 0) / -1))); assertTrue(IsNegativeZero(Math.floor((zero_in_array[0] | 0) / -1)));
} }
} }
function test_div_deopt_overflow() { function test_div_deopt_overflow() {
// We box the value in an array to avoid constant propagation.
var min_int_in_array = [-2147483648];
for (var i = 0; i < 2; ++i) { for (var i = 0; i < 2; ++i) {
// We use '| 0' to force the representation to int32. // We use '| 0' to force the representation to int32.
assertEquals(-min_int_in_array[0], assertEquals(-min_int_in_array[0],
...@@ -228,3 +253,36 @@ test_div_deopt_div_by_zero(); ...@@ -228,3 +253,36 @@ test_div_deopt_div_by_zero();
test_div_deopt_minus_zero(); test_div_deopt_minus_zero();
test_div_deopt_overflow(); test_div_deopt_overflow();
test_div_deopt_div_by_zero(); test_div_deopt_div_by_zero();
// Test for dividing by variable.
function test_div_deopt_minus_zero_v() {
for (var i = 0; i < 2; ++i) {
assertTrue(IsNegativeZero(Math.floor((zero_in_array[0] | 0) /
neg_one_in_array[0])));
}
}
function test_div_deopt_overflow_v() {
for (var i = 0; i < 2; ++i) {
// We use '| 0' to force the representation to int32.
assertEquals(-min_int_in_array[0],
Math.floor((min_int_in_array[0] | 0) / neg_one_in_array[0]));
}
}
function test_div_deopt_div_by_zero_v() {
for (var i = 0; i < 2; ++i) {
assertEquals(div(i, 0),
Math.floor(i / zero_in_array[0]));
}
}
test_div_deopt_minus_zero_v();
test_div_deopt_overflow_v();
test_div_deopt_div_by_zero_v();
%OptimizeFunctionOnNextCall(test_div_deopt_minus_zero_v);
%OptimizeFunctionOnNextCall(test_div_deopt_overflow_v);
%OptimizeFunctionOnNextCall(test_div_deopt_div_by_zero_v);
test_div_deopt_minus_zero_v();
test_div_deopt_overflow_v();
test_div_deopt_div_by_zero_v();
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