From d0219737478fe5a41917eb2ba34c5d7c2d418c9a Mon Sep 17 00:00:00 2001 From: "alexandre.rames@arm.com" Date: Tue, 25 Mar 2014 13:35:19 +0000 Subject: [PATCH] ARM64: Add overflow checking support for multiplications by constant powers of 2. R=ulan@chromium.org Review URL: https://codereview.chromium.org/210253002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20246 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm64/lithium-arm64.cc | 23 ++++++++++++------- src/arm64/lithium-codegen-arm64.cc | 47 ++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/arm64/lithium-arm64.cc b/src/arm64/lithium-arm64.cc index c6f547f..3d3ae97 100644 --- a/src/arm64/lithium-arm64.cc +++ b/src/arm64/lithium-arm64.cc @@ -1891,29 +1891,36 @@ LInstruction* LChunkBuilder::DoMul(HMul* instr) { HValue* least_const = instr->BetterLeftOperand(); HValue* most_const = instr->BetterRightOperand(); - LOperand* left = UseRegisterAtStart(least_const); + LOperand* left; // LMulConstI can handle a subset of constants: // With support for overflow detection: // -1, 0, 1, 2 - // Without support for overflow detection: // 2^n, -(2^n) + // Without support for overflow detection: // 2^n + 1, -(2^n - 1) if (most_const->IsConstant()) { int32_t constant = HConstant::cast(most_const)->Integer32Value(); - int32_t constant_abs = (constant >= 0) ? constant : -constant; - - if (((constant >= -1) && (constant <= 2)) || - (!can_overflow && (IsPowerOf2(constant_abs) || - IsPowerOf2(constant_abs + 1) || - IsPowerOf2(constant_abs - 1)))) { + bool small_constant = (constant >= -1) && (constant <= 2); + bool end_range_constant = (constant <= -kMaxInt) || (constant == kMaxInt); + int32_t constant_abs = Abs(constant); + + if (!end_range_constant && + (small_constant || + (IsPowerOf2(constant_abs)) || + (!can_overflow && (IsPowerOf2(constant_abs + 1) || + IsPowerOf2(constant_abs - 1))))) { LConstantOperand* right = UseConstant(most_const); + bool need_register = IsPowerOf2(constant_abs) && !small_constant; + left = need_register ? UseRegister(least_const) + : UseRegisterAtStart(least_const); LMulConstIS* mul = new(zone()) LMulConstIS(left, right); if (needs_environment) AssignEnvironment(mul); return DefineAsRegister(mul); } } + left = UseRegisterAtStart(least_const); // LMulI/S can handle all cases, but it requires that a register is // allocated for the second operand. LInstruction* result; diff --git a/src/arm64/lithium-codegen-arm64.cc b/src/arm64/lithium-codegen-arm64.cc index 7fdd94f..2f344e5 100644 --- a/src/arm64/lithium-codegen-arm64.cc +++ b/src/arm64/lithium-codegen-arm64.cc @@ -4238,6 +4238,7 @@ void LCodeGen::DoMulConstIS(LMulConstIS* instr) { Register left = is_smi ? ToRegister(instr->left()) : ToRegister32(instr->left()) ; int32_t right = ToInteger32(instr->right()); + ASSERT((right > -kMaxInt) || (right < kMaxInt)); bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); bool bailout_on_minus_zero = @@ -4281,20 +4282,40 @@ void LCodeGen::DoMulConstIS(LMulConstIS* instr) { } break; - // All other cases cannot detect overflow, because it would probably be no - // faster than using the smull method in LMulI. - // TODO(jbramley): Investigate this, and add overflow support if it would - // be useful. default: - ASSERT(!can_overflow); - // Multiplication by constant powers of two (and some related values) // can be done efficiently with shifted operands. - if (right >= 0) { - if (IsPowerOf2(right)) { + int32_t right_abs = Abs(right); + + if (IsPowerOf2(right_abs)) { + int right_log2 = WhichPowerOf2(right_abs); + + if (can_overflow) { + Register scratch = result; + ASSERT(!AreAliased(scratch, left)); + __ Cls(scratch, left); + __ Cmp(scratch, right_log2); + DeoptimizeIf(lt, instr->environment()); + } + + if (right >= 0) { // result = left << log2(right) - __ Lsl(result, left, WhichPowerOf2(right)); - } else if (IsPowerOf2(right - 1)) { + __ Lsl(result, left, right_log2); + } else { + // result = -left << log2(-right) + __ Neg(result, Operand(left, LSL, right_log2)); + } + return; + } + + + // For the following cases, we could perform a conservative overflow check + // with CLS as above. However the few cycles saved are likely not worth + // the risk of deoptimizing more often than required. + ASSERT(!can_overflow); + + if (right >= 0) { + if (IsPowerOf2(right - 1)) { // result = left + left << log2(right - 1) __ Add(result, left, Operand(left, LSL, WhichPowerOf2(right - 1))); } else if (IsPowerOf2(right + 1)) { @@ -4305,10 +4326,7 @@ void LCodeGen::DoMulConstIS(LMulConstIS* instr) { UNREACHABLE(); } } else { - if (IsPowerOf2(-right)) { - // result = -left << log2(-right) - __ Neg(result, Operand(left, LSL, WhichPowerOf2(-right))); - } else if (IsPowerOf2(-right + 1)) { + if (IsPowerOf2(-right + 1)) { // result = left - left << log2(-right + 1) __ Sub(result, left, Operand(left, LSL, WhichPowerOf2(-right + 1))); } else if (IsPowerOf2(-right - 1)) { @@ -4319,7 +4337,6 @@ void LCodeGen::DoMulConstIS(LMulConstIS* instr) { UNREACHABLE(); } } - break; } } -- 2.7.4