| 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)) { | 
|  |