From: sgjesse@chromium.org Date: Tue, 22 Mar 2011 10:00:43 +0000 (+0000) Subject: Improved modulo operation in lithium as well as bailout on -0. X-Git-Tag: upstream/4.7.83~19856 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4a2639e7ef2d4ab8d262c3215e0ddf6816b9153b;p=platform%2Fupstream%2Fv8.git Improved modulo operation in lithium as well as bailout on -0. TEST=none BUG=none Patch by Rodolph Perfetta from ARM Ltd. Review URL: http://codereview.chromium.org/6612017 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7300 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 153e795..3446995 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1345,29 +1345,25 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { LInstruction* LChunkBuilder::DoMod(HMod* instr) { if (instr->representation().IsInteger32()) { - // TODO(1042) The fixed register allocation - // is needed because we call GenericBinaryOpStub from - // the generated code, which requires registers r0 - // and r1 to be used. We should remove that - // when we provide a native implementation. ASSERT(instr->left()->representation().IsInteger32()); ASSERT(instr->right()->representation().IsInteger32()); - LInstruction* result; + LModI* mod; 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); + mod = new LModI(value, UseOrConstant(instr->right())); } else { - LOperand* value = UseFixed(instr->left(), r0); - LOperand* divisor = UseFixed(instr->right(), r1); - result = DefineFixed(new LModI(value, divisor), r0); - result = AssignEnvironment(AssignPointerMap(result)); + LOperand* dividend = UseRegister(instr->left()); + LOperand* divisor = UseRegisterAtStart(instr->right()); + mod = new LModI(dividend, + divisor, + TempRegister(), + FixedTemp(d1), + FixedTemp(d2)); } - return result; + return AssignEnvironment(DefineSameAsFirst(mod)); } else if (instr->representation().IsTagged()) { return DoArithmeticT(Token::MOD, instr); } else { diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index 18b70e1..50650c1 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -523,11 +523,29 @@ class LArgumentsElements: public LTemplateInstruction<1, 0, 0> { }; -class LModI: public LTemplateInstruction<1, 2, 0> { +class LModI: public LTemplateInstruction<1, 2, 3> { public: - LModI(LOperand* left, LOperand* right) { + // Used when the right hand is a constant power of 2. + LModI(LOperand* left, + LOperand* right) { inputs_[0] = left; inputs_[1] = right; + temps_[0] = NULL; + temps_[1] = NULL; + temps_[2] = NULL; + } + + // Used for the standard case. + LModI(LOperand* left, + LOperand* right, + LOperand* temp1, + LOperand* temp2, + LOperand* temp3) { + inputs_[0] = left; + inputs_[1] = right; + temps_[0] = temp1; + temps_[1] = temp2; + temps_[2] = temp3; } DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i") diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 6a16067..d02b862 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -802,7 +802,7 @@ void LCodeGen::DoModI(LModI* instr) { if (divisor < 0) divisor = -divisor; Label positive_dividend, done; - __ tst(dividend, Operand(dividend)); + __ cmp(dividend, Operand(0)); __ b(pl, &positive_dividend); __ rsb(dividend, dividend, Operand(0)); __ and_(dividend, dividend, Operand(divisor - 1)); @@ -817,55 +817,67 @@ void LCodeGen::DoModI(LModI* instr) { return; } - class DeferredModI: public LDeferredCode { - public: - DeferredModI(LCodeGen* codegen, LModI* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { - codegen()->DoDeferredBinaryOpStub(instr_, Token::MOD); - } - private: - LModI* instr_; - }; // These registers hold untagged 32 bit values. Register left = ToRegister(instr->InputAt(0)); Register right = ToRegister(instr->InputAt(1)); Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + Register scratch2 = ToRegister(instr->TempAt(0)); + DwVfpRegister dividend = ToDoubleRegister(instr->TempAt(1)); + DwVfpRegister divisor = ToDoubleRegister(instr->TempAt(2)); + DwVfpRegister quotient = double_scratch0(); + + ASSERT(result.is(left)); + + ASSERT(!dividend.is(divisor)); + ASSERT(!dividend.is(quotient)); + ASSERT(!divisor.is(quotient)); + ASSERT(!scratch.is(left)); + ASSERT(!scratch.is(right)); + ASSERT(!scratch.is(result)); + + Label done, vfp_modulo, both_positive, right_negative; - Label deoptimize, done; // Check for x % 0. if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { - __ tst(right, Operand(right)); - __ b(eq, &deoptimize); + __ cmp(right, Operand(0)); + DeoptimizeIf(eq, instr->environment()); } - // Check for (0 % -x) that will produce negative zero. - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - Label ok; - __ tst(left, Operand(left)); - __ b(ne, &ok); - __ tst(right, Operand(right)); - __ b(pl, &ok); - __ b(al, &deoptimize); - __ bind(&ok); - } + // (0 % x) must yield 0 (if x is finite, which is the case here). + __ cmp(left, Operand(0)); + __ b(eq, &done); + // Preload right in a vfp register. + __ vmov(divisor.low(), right); + __ b(lt, &vfp_modulo); + + __ cmp(left, Operand(right)); + __ b(lt, &done); - // Try a few common cases before using the stub. - Label call_stub; + // Check for (positive) power of two on the right hand side. + __ JumpIfNotPowerOfTwoOrZeroAndNeg(right, + scratch, + &right_negative, + &both_positive); + // Perform modulo operation (scratch contains right - 1). + __ and_(result, scratch, Operand(left)); + __ b(&done); + + __ bind(&right_negative); + // Negate right. The sign of the divisor does not matter. + __ rsb(right, right, Operand(0)); + + __ bind(&both_positive); const int kUnfolds = 3; - // Skip if either side is negative. - __ cmp(left, Operand(0)); - __ cmp(right, Operand(0), NegateCondition(mi)); - __ b(mi, &call_stub); // If the right hand side is smaller than the (nonnegative) - // left hand side, it is the result. Else try a few subtractions - // of the left hand side. + // left hand side, the left hand side is the result. + // Else try a few subtractions of the left hand side. __ mov(scratch, left); for (int i = 0; i < kUnfolds; i++) { // Check if the left hand side is less or equal than the // the right hand side. - __ cmp(scratch, right); + __ cmp(scratch, Operand(right)); __ mov(result, scratch, LeaveCC, lt); __ b(lt, &done); // If not, reduce the left hand side by the right hand @@ -873,29 +885,45 @@ void LCodeGen::DoModI(LModI* instr) { if (i < kUnfolds - 1) __ sub(scratch, scratch, right); } - // Check for power of two on the right hand side. - __ 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 - // to be tagged to Smis. If that is not possible, deoptimize. - DeferredModI* deferred = new DeferredModI(this, instr); - __ TrySmiTag(left, &deoptimize, scratch); - __ TrySmiTag(right, &deoptimize, scratch); - - __ b(al, deferred->entry()); - __ bind(deferred->exit()); - - // If the result in r0 is a Smi, untag it, else deoptimize. - __ JumpIfNotSmi(result, &deoptimize); - __ SmiUntag(result); + __ bind(&vfp_modulo); + // Load the arguments in VFP registers. + // The divisor value is preloaded before. Be careful that 'right' is only live + // on entry. + __ vmov(dividend.low(), left); + // From here on don't use right as it may have been reallocated (for example + // to scratch2). + right = no_reg; + + __ vcvt_f64_s32(dividend, dividend.low()); + __ vcvt_f64_s32(divisor, divisor.low()); + + // We do not care about the sign of the divisor. + __ vabs(divisor, divisor); + // Compute the quotient and round it to a 32bit integer. + __ vdiv(quotient, dividend, divisor); + __ vcvt_s32_f64(quotient.low(), quotient); + __ vcvt_f64_s32(quotient, quotient.low()); + + // Compute the remainder in result. + DwVfpRegister double_scratch = dividend; + __ vmul(double_scratch, divisor, quotient); + __ vcvt_s32_f64(double_scratch.low(), double_scratch); + __ vmov(scratch, double_scratch.low()); + + if (!instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + __ sub(result, left, scratch); + } else { + Label ok; + // Check for -0. + __ sub(scratch2, left, scratch, SetCC); + __ b(ne, &ok); + __ cmp(left, Operand(0)); + DeoptimizeIf(mi, instr->environment()); + __ bind(&ok); + // Load the result and we are done. + __ mov(result, scratch2); + } - __ b(al, &done); - __ bind(&deoptimize); - DeoptimizeIf(al, instr->environment()); __ bind(&done); } @@ -919,16 +947,16 @@ void LCodeGen::DoDivI(LDivI* instr) { // Check for x / 0. if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) { - __ tst(right, right); + __ cmp(right, Operand(0)); DeoptimizeIf(eq, instr->environment()); } // Check for (0 / -x) that will produce negative zero. if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { Label left_not_zero; - __ tst(left, Operand(left)); + __ cmp(left, Operand(0)); __ b(ne, &left_not_zero); - __ tst(right, Operand(right)); + __ cmp(right, Operand(0)); DeoptimizeIf(mi, instr->environment()); __ bind(&left_not_zero); } @@ -1035,7 +1063,7 @@ void LCodeGen::DoMulI(LMulI* instr) { if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { // Bail out if the result is supposed to be negative zero. Label done; - __ tst(left, Operand(left)); + __ cmp(left, Operand(0)); __ b(ne, &done); if (instr->InputAt(1)->IsConstantOperand()) { if (ToInteger32(LConstantOperand::cast(instr->InputAt(1))) <= 0) { @@ -1900,8 +1928,7 @@ void LCodeGen::DoInstanceOf(LInstanceOf* instr) { InstanceofStub stub(InstanceofStub::kArgsInRegisters); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); - Label true_value, done; - __ tst(r0, r0); + __ cmp(r0, Operand(0)); __ mov(r0, Operand(factory()->false_value()), LeaveCC, ne); __ mov(r0, Operand(factory()->true_value()), LeaveCC, eq); } @@ -1916,7 +1943,7 @@ void LCodeGen::DoInstanceOfAndBranch(LInstanceOfAndBranch* instr) { InstanceofStub stub(InstanceofStub::kArgsInRegisters); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); - __ tst(r0, Operand(r0)); + __ cmp(r0, Operand(0)); EmitBranch(true_block, false_block, eq); } @@ -2406,7 +2433,7 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { // stack. Label invoke, loop; // length is a small non-negative integer, due to the test above. - __ tst(length, Operand(length)); + __ cmp(length, Operand(0)); __ b(eq, &invoke); __ bind(&loop); __ ldr(scratch, MemOperand(elements, length, LSL, 2)); @@ -2642,14 +2669,16 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { // Move the result back to general purpose register r0. __ vmov(result, single_scratch); - // Test for -0. - Label done; - __ cmp(result, Operand(0)); - __ b(ne, &done); - __ vmov(scratch1, input.high()); - __ tst(scratch1, Operand(HeapNumber::kSignMask)); - DeoptimizeIf(ne, instr->environment()); - __ bind(&done); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // Test for -0. + Label done; + __ cmp(result, Operand(0)); + __ b(ne, &done); + __ vmov(scratch1, input.high()); + __ tst(scratch1, Operand(HeapNumber::kSignMask)); + DeoptimizeIf(ne, instr->environment()); + __ bind(&done); + } } @@ -2666,14 +2695,16 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { DeoptimizeIf(ne, instr->environment()); __ vmov(result, double_scratch0().low()); - // Test for -0. - Label done; - __ cmp(result, Operand(0)); - __ b(ne, &done); - __ vmov(scratch1, input.high()); - __ tst(scratch1, Operand(HeapNumber::kSignMask)); - DeoptimizeIf(ne, instr->environment()); - __ bind(&done); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + // Test for -0. + Label done; + __ cmp(result, Operand(0)); + __ b(ne, &done); + __ vmov(scratch1, input.high()); + __ tst(scratch1, Operand(HeapNumber::kSignMask)); + DeoptimizeIf(ne, instr->environment()); + __ bind(&done); + } } diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index ce653ea..8cdbf3b 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -2489,6 +2489,18 @@ void MacroAssembler::JumpIfNotPowerOfTwoOrZero( } +void MacroAssembler::JumpIfNotPowerOfTwoOrZeroAndNeg( + Register reg, + Register scratch, + Label* zero_and_neg, + Label* not_power_of_two) { + sub(scratch, reg, Operand(1), SetCC); + b(mi, zero_and_neg); + tst(scratch, reg); + b(ne, not_power_of_two); +} + + void MacroAssembler::JumpIfNotBothSmi(Register reg1, Register reg2, Label* on_not_both_smi) { diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index a2a7e2f..2b81c08 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -826,6 +826,16 @@ class MacroAssembler: public Assembler { void JumpIfNotPowerOfTwoOrZero(Register reg, Register scratch, Label* not_power_of_two_or_zero); + // Check whether the value of reg is a power of two and not zero. + // Control falls through if it is, with scratch containing the mask + // value (reg - 1). + // Otherwise control jumps to the 'zero_and_neg' label if the value of reg is + // zero or negative, or jumps to the 'not_power_of_two' label if the value is + // strictly positive but not a power of two. + void JumpIfNotPowerOfTwoOrZeroAndNeg(Register reg, + Register scratch, + Label* zero_and_neg, + Label* not_power_of_two); // --------------------------------------------------------------------------- // Smi utilities