Commit 653a66f5 authored by danno@chromium.org's avatar danno@chromium.org

ARM: Use division instructions in lithium and stubs

BUG=none
TEST=Added to test/mjsunit/math-floor-of-div.js, math-floor-of-div-nosudiv.js

Review URL: https://codereview.chromium.org/11316105
Patch from Martyn Capewell <m.m.capewell@googlemail.com>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13257 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a3f16f8e
...@@ -2441,33 +2441,112 @@ void BinaryOpStub_GenerateSmiSmiOperation(MacroAssembler* masm, ...@@ -2441,33 +2441,112 @@ void BinaryOpStub_GenerateSmiSmiOperation(MacroAssembler* masm,
// We fall through here if we multiplied a negative number with 0, because // We fall through here if we multiplied a negative number with 0, because
// that would mean we should produce -0. // that would mean we should produce -0.
break; break;
case Token::DIV: case Token::DIV: {
Label div_with_sdiv;
// Check for 0 divisor.
__ cmp(right, Operand(0));
__ b(eq, &not_smi_result);
// Check for power of two on the right hand side. // Check for power of two on the right hand side.
__ JumpIfNotPowerOfTwoOrZero(right, scratch1, &not_smi_result); __ sub(scratch1, right, Operand(1));
// Check for positive and no remainder (scratch1 contains right - 1). __ tst(scratch1, right);
__ orr(scratch2, scratch1, Operand(0x80000000u)); if (CpuFeatures::IsSupported(SUDIV)) {
__ tst(left, scratch2); __ b(ne, &div_with_sdiv);
__ b(ne, &not_smi_result); // Check for no remainder.
__ tst(left, scratch1);
__ b(ne, &not_smi_result);
// Check for positive left hand side.
__ cmp(left, Operand(0));
__ b(mi, &div_with_sdiv);
} else {
__ b(ne, &not_smi_result);
// Check for positive and no remainder.
__ orr(scratch2, scratch1, Operand(0x80000000u));
__ tst(left, scratch2);
__ b(ne, &not_smi_result);
}
// Perform division by shifting. // Perform division by shifting.
__ CountLeadingZeros(scratch1, scratch1, scratch2); __ CountLeadingZeros(scratch1, scratch1, scratch2);
__ rsb(scratch1, scratch1, Operand(31)); __ rsb(scratch1, scratch1, Operand(31));
__ mov(right, Operand(left, LSR, scratch1)); __ mov(right, Operand(left, LSR, scratch1));
__ Ret(); __ Ret();
if (CpuFeatures::IsSupported(SUDIV)) {
Label result_not_zero;
__ bind(&div_with_sdiv);
// Do division.
__ sdiv(scratch1, left, right);
// Check that the remainder is zero.
__ mls(scratch2, scratch1, right, left);
__ cmp(scratch2, Operand(0));
__ b(ne, &not_smi_result);
// Check for negative zero result.
__ cmp(scratch1, Operand(0));
__ b(ne, &result_not_zero);
__ cmp(right, Operand(0));
__ b(lt, &not_smi_result);
__ bind(&result_not_zero);
// Check for the corner case of dividing the most negative smi by -1.
__ cmp(scratch1, Operand(0x40000000));
__ b(eq, &not_smi_result);
// Tag and return the result.
__ SmiTag(right, scratch1);
__ Ret();
}
break; break;
case Token::MOD: }
// Check for two positive smis. case Token::MOD: {
__ orr(scratch1, left, Operand(right)); Label modulo_with_sdiv;
__ tst(scratch1, Operand(0x80000000u | kSmiTagMask));
__ b(ne, &not_smi_result); if (CpuFeatures::IsSupported(SUDIV)) {
// Check for x % 0.
__ cmp(right, Operand(0));
__ b(eq, &not_smi_result);
// Check for two positive smis.
__ orr(scratch1, left, Operand(right));
__ tst(scratch1, Operand(0x80000000u));
__ b(ne, &modulo_with_sdiv);
// Check for power of two on the right hand side.
__ sub(scratch1, right, Operand(1));
__ tst(scratch1, right);
__ b(ne, &modulo_with_sdiv);
} else {
// Check for two positive smis.
__ orr(scratch1, left, Operand(right));
__ tst(scratch1, Operand(0x80000000u));
__ b(ne, &not_smi_result);
// Check for power of two on the right hand side. // Check for power of two on the right hand side.
__ JumpIfNotPowerOfTwoOrZero(right, scratch1, &not_smi_result); __ JumpIfNotPowerOfTwoOrZero(right, scratch1, &not_smi_result);
}
// Perform modulus by masking. // Perform modulus by masking (scratch1 contains right - 1).
__ and_(right, left, Operand(scratch1)); __ and_(right, left, Operand(scratch1));
__ Ret(); __ Ret();
if (CpuFeatures::IsSupported(SUDIV)) {
__ bind(&modulo_with_sdiv);
__ mov(scratch2, right);
// Perform modulus with sdiv and mls.
__ sdiv(scratch1, left, right);
__ mls(right, scratch1, right, left);
// Return if the result is not 0.
__ cmp(right, Operand(0));
__ Ret(ne);
// The result is 0, check for -0 case.
__ cmp(left, Operand(0));
__ Ret(pl);
// This is a -0 case, restore the value of right.
__ mov(right, scratch2);
// We fall through here to not_smi_result to produce -0.
}
break; break;
}
case Token::BIT_OR: case Token::BIT_OR:
__ orr(right, left, Operand(right)); __ orr(right, left, Operand(right));
__ Ret(); __ Ret();
......
...@@ -1241,31 +1241,43 @@ HValue* LChunkBuilder::SimplifiedDividendForMathFloorOfDiv(HValue* dividend) { ...@@ -1241,31 +1241,43 @@ HValue* LChunkBuilder::SimplifiedDividendForMathFloorOfDiv(HValue* dividend) {
HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) { HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
// Only optimize when we have magic numbers for the divisor. if (CpuFeatures::IsSupported(SUDIV)) {
// The standard integer division routine is usually slower than transitionning // A value with an integer representation does not need to be transformed.
// to VFP. if (divisor->representation().IsInteger32()) {
if (divisor->IsConstant() && return divisor;
HConstant::cast(divisor)->HasInteger32Value()) { // 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();
}
}
if (divisor->IsConstant() && HConstant::cast(divisor)->HasInteger32Value()) {
HConstant* constant_val = HConstant::cast(divisor); HConstant* constant_val = HConstant::cast(divisor);
int32_t int32_val = constant_val->Integer32Value(); int32_t int32_val = constant_val->Integer32Value();
if (LChunkBuilder::HasMagicNumberForDivisor(int32_val)) { if (LChunkBuilder::HasMagicNumberForDivisor(int32_val) ||
CpuFeatures::IsSupported(SUDIV)) {
return constant_val->CopyToRepresentation(Representation::Integer32(), return constant_val->CopyToRepresentation(Representation::Integer32(),
divisor->block()->zone()); divisor->block()->zone());
} }
} }
return NULL; return NULL;
} }
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
HValue* right = instr->right(); HValue* right = instr->right();
LOperand* dividend = UseRegister(instr->left()); LOperand* dividend = UseRegister(instr->left());
LOperand* divisor = UseRegisterOrConstant(right); LOperand* divisor = CpuFeatures::IsSupported(SUDIV)
LOperand* remainder = TempRegister(); ? UseRegister(right)
ASSERT(right->IsConstant() && : UseOrConstant(right);
HConstant::cast(right)->HasInteger32Value() && LOperand* remainder = TempRegister();
HasMagicNumberForDivisor(HConstant::cast(right)->Integer32Value())); ASSERT(CpuFeatures::IsSupported(SUDIV) ||
return AssignEnvironment(DefineAsRegister( (right->IsConstant() &&
HConstant::cast(right)->HasInteger32Value() &&
HasMagicNumberForDivisor(HConstant::cast(right)->Integer32Value())));
return AssignEnvironment(DefineAsRegister(
new(zone()) LMathFloorOfDiv(dividend, divisor, remainder))); new(zone()) LMathFloorOfDiv(dividend, divisor, remainder)));
} }
......
...@@ -1433,25 +1433,68 @@ void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) { ...@@ -1433,25 +1433,68 @@ void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
const Register remainder = ToRegister(instr->temp()); const Register remainder = ToRegister(instr->temp());
const Register scratch = scratch0(); const Register scratch = scratch0();
// We only optimize this for division by constants, because the standard if (!CpuFeatures::IsSupported(SUDIV)) {
// integer division routine is usually slower than transitionning to VFP. // If the CPU doesn't support sdiv instruction, we only optimize when we
// This could be optimized on processors with SDIV available. // have magic numbers for the divisor. The standard integer division routine
ASSERT(instr->right()->IsConstantOperand()); // is usually slower than transitionning to VFP.
int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right())); ASSERT(instr->right()->IsConstantOperand());
if (divisor < 0) { int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right()));
__ cmp(left, Operand(0)); ASSERT(LChunkBuilder::HasMagicNumberForDivisor(divisor));
if (divisor < 0) {
__ cmp(left, Operand(0));
DeoptimizeIf(eq, instr->environment());
}
EmitSignedIntegerDivisionByConstant(result,
left,
divisor,
remainder,
scratch,
instr->environment());
// We performed a truncating division. Correct the result if necessary.
__ cmp(remainder, Operand(0));
__ teq(remainder, Operand(divisor), ne);
__ sub(result, result, Operand(1), LeaveCC, mi);
} else {
CpuFeatures::Scope scope(SUDIV);
const Register right = ToRegister(instr->right());
// Check for x / 0.
__ cmp(right, Operand(0));
DeoptimizeIf(eq, instr->environment()); DeoptimizeIf(eq, instr->environment());
// Check for (kMinInt / -1).
if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
Label left_not_min_int;
__ cmp(left, Operand(kMinInt));
__ b(ne, &left_not_min_int);
__ cmp(right, Operand(-1));
DeoptimizeIf(eq, instr->environment());
__ bind(&left_not_min_int);
}
// Check for (0 / -x) that will produce negative zero.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
__ cmp(right, Operand(0));
__ cmp(left, Operand(0), mi);
// "right" can't be null because the code would have already been
// deoptimized. The Z flag is set only if (right < 0) and (left == 0).
// In this case we need to deoptimize to produce a -0.
DeoptimizeIf(eq, instr->environment());
}
Label done;
__ sdiv(result, left, right);
// If both operands have the same sign then we are done.
__ eor(remainder, left, Operand(right), SetCC);
__ b(pl, &done);
// Check if the result needs to be corrected.
__ mls(remainder, result, right, left);
__ cmp(remainder, Operand(0));
__ sub(result, result, Operand(1), LeaveCC, ne);
__ bind(&done);
} }
EmitSignedIntegerDivisionByConstant(result,
left,
divisor,
remainder,
scratch,
instr->environment());
// We operated a truncating division. Correct the result if necessary.
__ cmp(remainder, Operand(0));
__ teq(remainder, Operand(divisor), ne);
__ sub(result, result, Operand(1), LeaveCC, mi);
} }
......
This diff is collapsed.
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --nouse_inlining // Flags: --allow-natives-syntax --nouse_inlining --enable_sudiv
// Use this function as reference. Make sure it is not inlined. // Use this function as reference. Make sure it is not inlined.
function div(a, b) { function div(a, b) {
...@@ -184,7 +184,7 @@ test_div(); ...@@ -184,7 +184,7 @@ test_div();
%OptimizeFunctionOnNextCall(test_div); %OptimizeFunctionOnNextCall(test_div);
test_div(); test_div();
// Test for negative zero and overflow. // 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.
...@@ -197,20 +197,34 @@ function IsNegativeZero(x) { ...@@ -197,20 +197,34 @@ function IsNegativeZero(x) {
function test_div_deopt_minus_zero() { function test_div_deopt_minus_zero() {
var zero_in_array = [0]; var zero_in_array = [0];
assertTrue(IsNegativeZero(Math.floor((zero_in_array[0] | 0) / -1))); for (var i = 0; i < 2; ++i) {
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. // We box the value in an array to avoid constant propagation.
var min_int_in_array = [-2147483648]; var min_int_in_array = [-2147483648];
// We use '| 0' to force the representation to int32. for (var i = 0; i < 2; ++i) {
assertEquals(-min_int_in_array[0], // We use '| 0' to force the representation to int32.
Math.floor((min_int_in_array[0] | 0) / -1)); assertEquals(-min_int_in_array[0],
Math.floor((min_int_in_array[0] | 0) / -1));
}
}
function test_div_deopt_div_by_zero() {
for (var i = 0; i < 2; ++i) {
assertEquals(div(i, 0),
Math.floor(i / 0));
}
} }
test_div_deopt_minus_zero(); test_div_deopt_minus_zero();
test_div_deopt_overflow(); test_div_deopt_overflow();
test_div_deopt_div_by_zero();
%OptimizeFunctionOnNextCall(test_div_deopt_minus_zero); %OptimizeFunctionOnNextCall(test_div_deopt_minus_zero);
%OptimizeFunctionOnNextCall(test_div_deopt_overflow); %OptimizeFunctionOnNextCall(test_div_deopt_overflow);
%OptimizeFunctionOnNextCall(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();
...@@ -76,6 +76,10 @@ regress/regress-crbug-160010: PASS, SKIP if $mode == debug ...@@ -76,6 +76,10 @@ regress/regress-crbug-160010: PASS, SKIP if $mode == debug
d8-os: PASS, SKIP if ($isolates || $arch == android_arm || $arch == android_ia32) d8-os: PASS, SKIP if ($isolates || $arch == android_arm || $arch == android_ia32)
tools/tickprocessor: PASS, SKIP if ($arch == android_arm || $arch == android_ia32) tools/tickprocessor: PASS, SKIP if ($arch == android_arm || $arch == android_ia32)
##############################################################################
# This test is the same as math-floor-of-div for non ARM architectures.
math-floor-of-div-nosudiv: PASS, SKIP if ($arch != arm && $arch != android_arm)
############################################################################## ##############################################################################
[ $arch == arm || $arch == android_arm ] [ $arch == arm || $arch == android_arm ]
......
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