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