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,
// We fall through here if we multiplied a negative number with 0, because
// that would mean we should produce -0.
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.
__ JumpIfNotPowerOfTwoOrZero(right, scratch1, &not_smi_result);
// Check for positive and no remainder (scratch1 contains right - 1).
__ orr(scratch2, scratch1, Operand(0x80000000u));
__ tst(left, scratch2);
__ b(ne, &not_smi_result);
__ sub(scratch1, right, Operand(1));
__ tst(scratch1, right);
if (CpuFeatures::IsSupported(SUDIV)) {
__ b(ne, &div_with_sdiv);
// 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.
__ CountLeadingZeros(scratch1, scratch1, scratch2);
__ rsb(scratch1, scratch1, Operand(31));
__ mov(right, Operand(left, LSR, scratch1));
__ 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;
case Token::MOD:
// Check for two positive smis.
__ orr(scratch1, left, Operand(right));
__ tst(scratch1, Operand(0x80000000u | kSmiTagMask));
__ b(ne, &not_smi_result);
}
case Token::MOD: {
Label modulo_with_sdiv;
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.
__ JumpIfNotPowerOfTwoOrZero(right, scratch1, &not_smi_result);
// Check for power of two on the right hand side.
__ JumpIfNotPowerOfTwoOrZero(right, scratch1, &not_smi_result);
}
// Perform modulus by masking.
// Perform modulus by masking (scratch1 contains right - 1).
__ and_(right, left, Operand(scratch1));
__ 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;
}
case Token::BIT_OR:
__ orr(right, left, Operand(right));
__ Ret();
......
......@@ -1241,31 +1241,43 @@ HValue* LChunkBuilder::SimplifiedDividendForMathFloorOfDiv(HValue* dividend) {
HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
// Only optimize when we have magic numbers for the divisor.
// The standard integer division routine is usually slower than transitionning
// to VFP.
if (divisor->IsConstant() &&
HConstant::cast(divisor)->HasInteger32Value()) {
if (CpuFeatures::IsSupported(SUDIV)) {
// 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();
}
}
if (divisor->IsConstant() && HConstant::cast(divisor)->HasInteger32Value()) {
HConstant* constant_val = HConstant::cast(divisor);
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(),
divisor->block()->zone());
}
}
return NULL;
}
LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
HValue* right = instr->right();
LOperand* dividend = UseRegister(instr->left());
LOperand* divisor = UseRegisterOrConstant(right);
LOperand* remainder = TempRegister();
ASSERT(right->IsConstant() &&
HConstant::cast(right)->HasInteger32Value() &&
HasMagicNumberForDivisor(HConstant::cast(right)->Integer32Value()));
return AssignEnvironment(DefineAsRegister(
HValue* right = instr->right();
LOperand* dividend = UseRegister(instr->left());
LOperand* divisor = CpuFeatures::IsSupported(SUDIV)
? UseRegister(right)
: UseOrConstant(right);
LOperand* remainder = TempRegister();
ASSERT(CpuFeatures::IsSupported(SUDIV) ||
(right->IsConstant() &&
HConstant::cast(right)->HasInteger32Value() &&
HasMagicNumberForDivisor(HConstant::cast(right)->Integer32Value())));
return AssignEnvironment(DefineAsRegister(
new(zone()) LMathFloorOfDiv(dividend, divisor, remainder)));
}
......
......@@ -1433,25 +1433,68 @@ void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
const Register remainder = ToRegister(instr->temp());
const Register scratch = scratch0();
// We only optimize this for division by constants, because the standard
// integer division routine is usually slower than transitionning to VFP.
// This could be optimized on processors with SDIV available.
ASSERT(instr->right()->IsConstantOperand());
int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right()));
if (divisor < 0) {
__ cmp(left, Operand(0));
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(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());
// 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 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// 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.
function div(a, b) {
......@@ -184,7 +184,7 @@ test_div();
%OptimizeFunctionOnNextCall(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
// test unreachable.
......@@ -197,20 +197,34 @@ function IsNegativeZero(x) {
function test_div_deopt_minus_zero() {
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() {
// We box the value in an array to avoid constant propagation.
var min_int_in_array = [-2147483648];
// We use '| 0' to force the representation to int32.
assertEquals(-min_int_in_array[0],
Math.floor((min_int_in_array[0] | 0) / -1));
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) / -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_overflow();
test_div_deopt_div_by_zero();
%OptimizeFunctionOnNextCall(test_div_deopt_minus_zero);
%OptimizeFunctionOnNextCall(test_div_deopt_overflow);
%OptimizeFunctionOnNextCall(test_div_deopt_div_by_zero);
test_div_deopt_minus_zero();
test_div_deopt_overflow();
test_div_deopt_div_by_zero();
......@@ -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)
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 ]
......
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