Index: src/arm64/lithium-arm64.cc |
diff --git a/src/arm64/lithium-arm64.cc b/src/arm64/lithium-arm64.cc |
index a2c3a3212073a47118dee65cf5367a540857298c..b178b7f104590ecc30ef8388fcd03b6d4c94f250 100644 |
--- a/src/arm64/lithium-arm64.cc |
+++ b/src/arm64/lithium-arm64.cc |
@@ -826,6 +826,12 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { |
if (instr->representation().IsSmiOrInteger32()) { |
ASSERT(instr->left()->representation().Equals(instr->representation())); |
ASSERT(instr->right()->representation().Equals(instr->representation())); |
+ |
+ LInstruction* shifted_operation = TryDoOpWithShiftedRightOperand(instr); |
+ if (shifted_operation != NULL) { |
+ return shifted_operation; |
+ } |
+ |
LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand()); |
LOperand* right = |
UseRegisterOrConstantAtStart(instr->BetterRightOperand()); |
@@ -906,6 +912,11 @@ LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) { |
ASSERT(instr->right()->representation().Equals(instr->representation())); |
ASSERT(instr->CheckFlag(HValue::kTruncatingToInt32)); |
+ LInstruction* shifted_operation = TryDoOpWithShiftedRightOperand(instr); |
+ if (shifted_operation != NULL) { |
+ return shifted_operation; |
+ } |
+ |
LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand()); |
LOperand* right = |
UseRegisterOrConstantAtStart(instr->BetterRightOperand()); |
@@ -2031,6 +2042,117 @@ LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) { |
} |
+HBitwiseBinaryOperation* LChunkBuilder::CanTransformToShiftedOp(HValue* val, |
+ HValue** left) { |
+ if (!val->representation().IsInteger32()) return NULL; |
+ if (!(val->IsBitwise() || val->IsAdd() || val->IsSub())) return NULL; |
+ |
+ HBinaryOperation* hinstr = HBinaryOperation::cast(val); |
+ HValue* hleft = hinstr->left(); |
+ HValue* hright = hinstr->right(); |
+ ASSERT(hleft->representation().Equals(hinstr->representation())); |
+ ASSERT(hright->representation().Equals(hinstr->representation())); |
+ |
+ if ((hright->IsConstant() && |
+ LikelyFitsImmField(hinstr, HConstant::cast(hright)->Integer32Value())) || |
+ (hinstr->IsCommutative() && hleft->IsConstant() && |
+ LikelyFitsImmField(hinstr, HConstant::cast(hleft)->Integer32Value()))) { |
+ // The constant operand will likely fit in the immediate field. We are |
+ // better off with |
+ // lsl x8, x9, #imm |
+ // add x0, x8, #imm2 |
+ // than with |
+ // mov x16, #imm2 |
+ // add x0, x16, x9 LSL #imm |
+ return NULL; |
+ } |
+ |
+ HBitwiseBinaryOperation* shift = NULL; |
+ // TODO(aleram): We will miss situations where a shift operation is used by |
+ // different instructions both as a left and right operands. |
+ if (hright->IsBitwiseBinaryShift() && |
+ HBitwiseBinaryOperation::cast(hright)->right()->IsConstant()) { |
+ shift = HBitwiseBinaryOperation::cast(hright); |
+ if (left != NULL) { |
+ *left = hleft; |
+ } |
+ } else if (hinstr->IsCommutative() && |
+ hleft->IsBitwiseBinaryShift() && |
+ HBitwiseBinaryOperation::cast(hleft)->right()->IsConstant()) { |
+ shift = HBitwiseBinaryOperation::cast(hleft); |
+ if (left != NULL) { |
+ *left = hright; |
+ } |
+ } else { |
+ return NULL; |
+ } |
+ |
+ if ((JSShiftAmountFromHConstant(shift->right()) == 0) && shift->IsShr()) { |
+ // Shifts right by zero can deoptimize. |
+ return NULL; |
+ } |
+ |
+ return shift; |
+} |
+ |
+ |
+bool LChunkBuilder::ShiftCanBeOptimizedAway(HBitwiseBinaryOperation* shift) { |
+ if (!shift->representation().IsInteger32()) { |
+ return false; |
+ } |
+ for (HUseIterator it(shift->uses()); !it.Done(); it.Advance()) { |
+ if (shift != CanTransformToShiftedOp(it.value())) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+ |
+LInstruction* LChunkBuilder::TryDoOpWithShiftedRightOperand( |
+ HBinaryOperation* instr) { |
+ HValue* left; |
+ HBitwiseBinaryOperation* shift = CanTransformToShiftedOp(instr, &left); |
+ |
+ if ((shift != NULL) && ShiftCanBeOptimizedAway(shift)) { |
+ return DoShiftedBinaryOp(instr, left, shift); |
+ } |
+ return NULL; |
+} |
+ |
+ |
+LInstruction* LChunkBuilder::DoShiftedBinaryOp( |
+ HBinaryOperation* hinstr, HValue* hleft, HBitwiseBinaryOperation* hshift) { |
+ ASSERT(hshift->IsBitwiseBinaryShift()); |
+ ASSERT(!hshift->IsShr() || (JSShiftAmountFromHConstant(hshift->right()) > 0)); |
+ |
+ LTemplateResultInstruction<1>* res; |
+ LOperand* left = UseRegisterAtStart(hleft); |
+ LOperand* right = UseRegisterAtStart(hshift->left()); |
+ LOperand* shift_amount = UseConstant(hshift->right()); |
+ Shift shift_op; |
+ switch (hshift->opcode()) { |
+ case HValue::kShl: shift_op = LSL; break; |
+ case HValue::kShr: shift_op = LSR; break; |
+ case HValue::kSar: shift_op = ASR; break; |
+ default: UNREACHABLE(); shift_op = NO_SHIFT; |
+ } |
+ |
+ if (hinstr->IsBitwise()) { |
+ res = new(zone()) LBitI(left, right, shift_op, shift_amount); |
+ } else if (hinstr->IsAdd()) { |
+ res = new(zone()) LAddI(left, right, shift_op, shift_amount); |
+ } else { |
+ ASSERT(hinstr->IsSub()); |
+ res = new(zone()) LSubI(left, right, shift_op, shift_amount); |
+ } |
+ if (hinstr->CheckFlag(HValue::kCanOverflow)) { |
+ AssignEnvironment(res); |
+ } |
+ return DefineAsRegister(res); |
+} |
+ |
+ |
LInstruction* LChunkBuilder::DoShift(Token::Value op, |
HBitwiseBinaryOperation* instr) { |
if (instr->representation().IsTagged()) { |
@@ -2042,6 +2164,10 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op, |
ASSERT(instr->left()->representation().Equals(instr->representation())); |
ASSERT(instr->right()->representation().Equals(instr->representation())); |
+ if (ShiftCanBeOptimizedAway(instr)) { |
+ return NULL; |
+ } |
+ |
LOperand* left = instr->representation().IsSmi() |
? UseRegister(instr->left()) |
: UseRegisterAtStart(instr->left()); |
@@ -2052,8 +2178,7 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op, |
int constant_value = 0; |
if (right_value->IsConstant()) { |
right = UseConstant(right_value); |
- HConstant* constant = HConstant::cast(right_value); |
- constant_value = constant->Integer32Value() & 0x1f; |
+ constant_value = JSShiftAmountFromHConstant(right_value); |
} else { |
right = UseRegisterAtStart(right_value); |
if (op == Token::ROR) { |
@@ -2315,6 +2440,12 @@ LInstruction* LChunkBuilder::DoSub(HSub* instr) { |
if (instr->representation().IsSmiOrInteger32()) { |
ASSERT(instr->left()->representation().Equals(instr->representation())); |
ASSERT(instr->right()->representation().Equals(instr->representation())); |
+ |
+ LInstruction* shifted_operation = TryDoOpWithShiftedRightOperand(instr); |
+ if (shifted_operation != NULL) { |
+ return shifted_operation; |
+ } |
+ |
LOperand *left; |
if (instr->left()->IsConstant() && |
(HConstant::cast(instr->left())->Integer32Value() == 0)) { |