From 9b311c21b1cb56254013161d9665ed850adbad53 Mon Sep 17 00:00:00 2001 From: "vegorov@chromium.org" Date: Mon, 14 Mar 2011 14:42:14 +0000 Subject: [PATCH] Reduce strength of ModI for power-of-2 divisor. This is ia32 only. I will port it to other platforms if this looks good to you. Review URL: http://codereview.chromium.org/6665006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7158 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/lithium-arm.cc | 19 ++++++++-- src/arm/lithium-codegen-arm.cc | 25 +++++++++++++ src/hydrogen-instructions.h | 10 +++++ src/ia32/lithium-codegen-ia32.cc | 81 ++++++++++++++++++++++++++-------------- src/ia32/lithium-ia32.cc | 24 ++++++++---- src/x64/lithium-codegen-x64.cc | 81 ++++++++++++++++++++++++++-------------- src/x64/lithium-x64.cc | 24 ++++++++---- 7 files changed, 188 insertions(+), 76 deletions(-) diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index c5b0c1d..fb7c103 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1350,10 +1350,21 @@ LInstruction* LChunkBuilder::DoMod(HMod* instr) { // when we provide a native implementation. ASSERT(instr->left()->representation().IsInteger32()); ASSERT(instr->right()->representation().IsInteger32()); - LOperand* value = UseFixed(instr->left(), r0); - LOperand* divisor = UseFixed(instr->right(), r1); - LInstruction* result = DefineFixed(new LModI(value, divisor), r0); - result = AssignEnvironment(AssignPointerMap(result)); + + LInstruction* result; + if (instr->HasPowerOf2Divisor()) { + ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero)); + LOperand* value = UseRegisterAtStart(instr->left()); + LModI* mod = new LModI(value, UseOrConstant(instr->right())); + result = DefineSameAsFirst(mod); + result = AssignEnvironment(result); + } else { + LOperand* value = UseFixed(instr->left(), r0); + LOperand* divisor = UseFixed(instr->right(), r1); + result = DefineFixed(new LModI(value, divisor), r0); + result = AssignEnvironment(AssignPointerMap(result)); + } + return result; } else if (instr->representation().IsTagged()) { return DoArithmeticT(Token::MOD, instr); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 8dd35b6..34a826e 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -796,6 +796,30 @@ void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { void LCodeGen::DoModI(LModI* instr) { + if (instr->hydrogen()->HasPowerOf2Divisor()) { + Register dividend = ToRegister(instr->InputAt(0)); + + int32_t divisor = + HConstant::cast(instr->hydrogen()->right())->Integer32Value(); + + if (divisor < 0) divisor = -divisor; + + Label positive_dividend, done; + __ tst(dividend, Operand(dividend)); + __ b(pl, &positive_dividend); + __ rsb(dividend, dividend, Operand(0)); + __ and_(dividend, dividend, Operand(divisor - 1)); + __ rsb(dividend, dividend, Operand(0), SetCC); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + __ b(ne, &done); + DeoptimizeIf(al, instr->environment()); + } + __ bind(&positive_dividend); + __ and_(dividend, dividend, Operand(divisor - 1)); + __ bind(&done); + return; + } + class DeferredModI: public LDeferredCode { public: DeferredModI(LCodeGen* codegen, LModI* instr) @@ -856,6 +880,7 @@ void LCodeGen::DoModI(LModI* instr) { __ JumpIfNotPowerOfTwoOrZero(right, scratch, &call_stub); // Perform modulo operation (scratch contains right - 1). __ and_(result, scratch, Operand(left)); + __ b(&done); __ bind(&call_stub); // Call the stub. The numbers in r0 and r1 have diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 6c516f2..2178184 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -2567,6 +2567,16 @@ class HMod: public HArithmeticBinaryOperation { SetFlag(kCanBeDivByZero); } + bool HasPowerOf2Divisor() { + if (right()->IsConstant() && + HConstant::cast(right())->HasInteger32Value()) { + int32_t value = HConstant::cast(right())->Integer32Value(); + return value != 0 && (IsPowerOf2(value) || IsPowerOf2(-value)); + } + + return false; + } + virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); DECLARE_CONCRETE_INSTRUCTION(Mod, "mod") diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 28f4679..5e7f528 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -781,41 +781,64 @@ void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { void LCodeGen::DoModI(LModI* instr) { - LOperand* right = instr->InputAt(1); - ASSERT(ToRegister(instr->result()).is(edx)); - ASSERT(ToRegister(instr->InputAt(0)).is(eax)); - ASSERT(!ToRegister(instr->InputAt(1)).is(eax)); - ASSERT(!ToRegister(instr->InputAt(1)).is(edx)); + if (instr->hydrogen()->HasPowerOf2Divisor()) { + Register dividend = ToRegister(instr->InputAt(0)); - Register right_reg = ToRegister(right); + int32_t divisor = + HConstant::cast(instr->hydrogen()->right())->Integer32Value(); - // Check for x % 0. - if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { - __ test(right_reg, ToOperand(right)); - DeoptimizeIf(zero, instr->environment()); - } + if (divisor < 0) divisor = -divisor; - // Sign extend to edx. - __ cdq(); + NearLabel positive_dividend, done; + __ test(dividend, Operand(dividend)); + __ j(not_sign, &positive_dividend); + __ neg(dividend); + __ and_(dividend, divisor - 1); + __ neg(dividend); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + __ j(not_zero, &done); + DeoptimizeIf(no_condition, instr->environment()); + } + __ bind(&positive_dividend); + __ and_(dividend, divisor - 1); + __ bind(&done); + } else { + LOperand* right = instr->InputAt(1); + ASSERT(ToRegister(instr->InputAt(0)).is(eax)); + ASSERT(ToRegister(instr->result()).is(edx)); - // Check for (0 % -x) that will produce negative zero. - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - NearLabel positive_left; - NearLabel done; - __ test(eax, Operand(eax)); - __ j(not_sign, &positive_left); - __ idiv(right_reg); + Register right_reg = ToRegister(right); + ASSERT(!right_reg.is(eax)); + ASSERT(!right_reg.is(edx)); - // Test the remainder for 0, because then the result would be -0. - __ test(edx, Operand(edx)); - __ j(not_zero, &done); + // Check for x % 0. + if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { + __ test(right_reg, ToOperand(right)); + DeoptimizeIf(zero, instr->environment()); + } - DeoptimizeIf(no_condition, instr->environment()); - __ bind(&positive_left); - __ idiv(right_reg); - __ bind(&done); - } else { - __ idiv(right_reg); + // Sign extend to edx. + __ cdq(); + + // Check for (0 % -x) that will produce negative zero. + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + NearLabel positive_left; + NearLabel done; + __ test(eax, Operand(eax)); + __ j(not_sign, &positive_left); + __ idiv(right_reg); + + // Test the remainder for 0, because then the result would be -0. + __ test(edx, Operand(edx)); + __ j(not_zero, &done); + + DeoptimizeIf(no_condition, instr->environment()); + __ bind(&positive_left); + __ idiv(right_reg); + __ bind(&done); + } else { + __ idiv(right_reg); + } } } diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 0ade55f..80fd71d 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1366,13 +1366,23 @@ LInstruction* LChunkBuilder::DoMod(HMod* instr) { if (instr->representation().IsInteger32()) { ASSERT(instr->left()->representation().IsInteger32()); ASSERT(instr->right()->representation().IsInteger32()); - // The temporary operand is necessary to ensure that right is not allocated - // into edx. - LOperand* temp = FixedTemp(edx); - LOperand* value = UseFixed(instr->left(), eax); - LOperand* divisor = UseRegister(instr->right()); - LModI* mod = new LModI(value, divisor, temp); - LInstruction* result = DefineFixed(mod, edx); + + LInstruction* result; + if (instr->HasPowerOf2Divisor()) { + ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero)); + LOperand* value = UseRegisterAtStart(instr->left()); + LModI* mod = new LModI(value, UseOrConstant(instr->right()), NULL); + result = DefineSameAsFirst(mod); + } else { + // The temporary operand is necessary to ensure that right is + // not allocated into edx. + LOperand* temp = FixedTemp(edx); + LOperand* value = UseFixed(instr->left(), eax); + LOperand* divisor = UseRegister(instr->right()); + LModI* mod = new LModI(value, divisor, temp); + result = DefineFixed(mod, edx); + } + return (instr->CheckFlag(HValue::kBailoutOnMinusZero) || instr->CheckFlag(HValue::kCanBeDivByZero)) ? AssignEnvironment(result) diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index dbbbaa7..1c1addb 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -752,41 +752,64 @@ void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { void LCodeGen::DoModI(LModI* instr) { - LOperand* right = instr->InputAt(1); - ASSERT(ToRegister(instr->result()).is(rdx)); - ASSERT(ToRegister(instr->InputAt(0)).is(rax)); - ASSERT(!ToRegister(instr->InputAt(1)).is(rax)); - ASSERT(!ToRegister(instr->InputAt(1)).is(rdx)); + if (instr->hydrogen()->HasPowerOf2Divisor()) { + Register dividend = ToRegister(instr->InputAt(0)); - Register right_reg = ToRegister(right); + int32_t divisor = + HConstant::cast(instr->hydrogen()->right())->Integer32Value(); - // Check for x % 0. - if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { - __ testl(right_reg, right_reg); - DeoptimizeIf(zero, instr->environment()); - } + if (divisor < 0) divisor = -divisor; - // Sign extend eax to edx. (We are using only the low 32 bits of the values.) - __ cdq(); + NearLabel positive_dividend, done; + __ testl(dividend, dividend); + __ j(not_sign, &positive_dividend); + __ negl(dividend); + __ andl(dividend, Immediate(divisor - 1)); + __ negl(dividend); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + __ j(not_zero, &done); + DeoptimizeIf(no_condition, instr->environment()); + } + __ bind(&positive_dividend); + __ andl(dividend, Immediate(divisor - 1)); + __ bind(&done); + } else { + LOperand* right = instr->InputAt(1); + Register right_reg = ToRegister(right); - // Check for (0 % -x) that will produce negative zero. - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - NearLabel positive_left; - NearLabel done; - __ testl(rax, rax); - __ j(not_sign, &positive_left); - __ idivl(right_reg); + ASSERT(ToRegister(instr->result()).is(rdx)); + ASSERT(ToRegister(instr->InputAt(0)).is(rax)); + ASSERT(!right_reg.is(rax)); + ASSERT(!right_reg.is(rdx)); - // Test the remainder for 0, because then the result would be -0. - __ testl(rdx, rdx); - __ j(not_zero, &done); + // Check for x % 0. + if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { + __ testl(right_reg, right_reg); + DeoptimizeIf(zero, instr->environment()); + } - DeoptimizeIf(no_condition, instr->environment()); - __ bind(&positive_left); - __ idivl(right_reg); - __ bind(&done); - } else { - __ idivl(right_reg); + // Sign extend eax to edx. (We are using only the low 32 bits of the values.) + __ cdq(); + + // Check for (0 % -x) that will produce negative zero. + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + NearLabel positive_left; + NearLabel done; + __ testl(rax, rax); + __ j(not_sign, &positive_left); + __ idivl(right_reg); + + // Test the remainder for 0, because then the result would be -0. + __ testl(rdx, rdx); + __ j(not_zero, &done); + + DeoptimizeIf(no_condition, instr->environment()); + __ bind(&positive_left); + __ idivl(right_reg); + __ bind(&done); + } else { + __ idivl(right_reg); + } } } diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index a29a337..640d58e 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1347,13 +1347,23 @@ LInstruction* LChunkBuilder::DoMod(HMod* instr) { if (instr->representation().IsInteger32()) { ASSERT(instr->left()->representation().IsInteger32()); ASSERT(instr->right()->representation().IsInteger32()); - // The temporary operand is necessary to ensure that right is not allocated - // into edx. - LOperand* temp = FixedTemp(rdx); - LOperand* value = UseFixed(instr->left(), rax); - LOperand* divisor = UseRegister(instr->right()); - LModI* mod = new LModI(value, divisor, temp); - LInstruction* result = DefineFixed(mod, rdx); + + LInstruction* result; + if (instr->HasPowerOf2Divisor()) { + ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero)); + LOperand* value = UseRegisterAtStart(instr->left()); + LModI* mod = new LModI(value, UseOrConstant(instr->right()), NULL); + result = DefineSameAsFirst(mod); + } else { + // The temporary operand is necessary to ensure that right is not allocated + // into edx. + LOperand* temp = FixedTemp(rdx); + LOperand* value = UseFixed(instr->left(), rax); + LOperand* divisor = UseRegister(instr->right()); + LModI* mod = new LModI(value, divisor, temp); + result = DefineFixed(mod, rdx); + } + return (instr->CheckFlag(HValue::kBailoutOnMinusZero) || instr->CheckFlag(HValue::kCanBeDivByZero)) ? AssignEnvironment(result) -- 2.7.4