ARM: Merge data-processing instructions and shifts/(un)signed extensions

This commit mirrors the work that has already been done for ARM64.

Test: m test-art-target-run-test-551-checker-shifter-operand
Change-Id: Iec8c1563b035f40f0e18dcffde28d91dc21922f8
diff --git a/compiler/Android.bp b/compiler/Android.bp
index f6a4db4..f5589cd 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -111,6 +111,7 @@
                 "optimizing/instruction_simplifier_shared.cc",
                 "optimizing/intrinsics_arm.cc",
                 "optimizing/intrinsics_arm_vixl.cc",
+                "optimizing/nodes_shared.cc",
                 "utils/arm/assembler_arm.cc",
                 "utils/arm/assembler_arm_vixl.cc",
                 "utils/arm/assembler_thumb2.cc",
@@ -127,7 +128,6 @@
                 "optimizing/scheduler_arm64.cc",
                 "optimizing/instruction_simplifier_arm64.cc",
                 "optimizing/intrinsics_arm64.cc",
-                "optimizing/nodes_arm64.cc",
                 "utils/arm64/assembler_arm64.cc",
                 "utils/arm64/jni_macro_assembler_arm64.cc",
                 "utils/arm64/managed_register_arm64.cc",
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 759a951..7b84ef8 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -19,6 +19,7 @@
 #include "arch/arm/instruction_set_features_arm.h"
 #include "art_method.h"
 #include "code_generator_utils.h"
+#include "common_arm.h"
 #include "compiled_method.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/accounting/card_table.h"
@@ -1132,10 +1133,6 @@
   DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM);
 };
 
-#undef __
-// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
-#define __ down_cast<ArmAssembler*>(GetAssembler())->  // NOLINT
-
 inline Condition ARMCondition(IfCondition cond) {
   switch (cond) {
     case kCondEQ: return EQ;
@@ -1191,6 +1188,197 @@
   }
 }
 
+inline Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
+  switch (op_kind) {
+    case HDataProcWithShifterOp::kASR: return ASR;
+    case HDataProcWithShifterOp::kLSL: return LSL;
+    case HDataProcWithShifterOp::kLSR: return LSR;
+    default:
+      LOG(FATAL) << "Unexpected op kind " << op_kind;
+      UNREACHABLE();
+  }
+}
+
+static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
+                                        Register out,
+                                        Register first,
+                                        const ShifterOperand& second,
+                                        CodeGeneratorARM* codegen) {
+  if (second.IsImmediate() && second.GetImmediate() == 0) {
+    const ShifterOperand in = kind == HInstruction::kAnd
+        ? ShifterOperand(0)
+        : ShifterOperand(first);
+
+    __ mov(out, in);
+  } else {
+    switch (kind) {
+      case HInstruction::kAdd:
+        __ add(out, first, second);
+        break;
+      case HInstruction::kAnd:
+        __ and_(out, first, second);
+        break;
+      case HInstruction::kOr:
+        __ orr(out, first, second);
+        break;
+      case HInstruction::kSub:
+        __ sub(out, first, second);
+        break;
+      case HInstruction::kXor:
+        __ eor(out, first, second);
+        break;
+      default:
+        LOG(FATAL) << "Unexpected instruction kind: " << kind;
+        UNREACHABLE();
+    }
+  }
+}
+
+static void GenerateDataProc(HInstruction::InstructionKind kind,
+                             const Location& out,
+                             const Location& first,
+                             const ShifterOperand& second_lo,
+                             const ShifterOperand& second_hi,
+                             CodeGeneratorARM* codegen) {
+  const Register first_hi = first.AsRegisterPairHigh<Register>();
+  const Register first_lo = first.AsRegisterPairLow<Register>();
+  const Register out_hi = out.AsRegisterPairHigh<Register>();
+  const Register out_lo = out.AsRegisterPairLow<Register>();
+
+  if (kind == HInstruction::kAdd) {
+    __ adds(out_lo, first_lo, second_lo);
+    __ adc(out_hi, first_hi, second_hi);
+  } else if (kind == HInstruction::kSub) {
+    __ subs(out_lo, first_lo, second_lo);
+    __ sbc(out_hi, first_hi, second_hi);
+  } else {
+    GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
+    GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
+  }
+}
+
+static ShifterOperand GetShifterOperand(Register rm, Shift shift, uint32_t shift_imm) {
+  return shift_imm == 0 ? ShifterOperand(rm) : ShifterOperand(rm, shift, shift_imm);
+}
+
+static void GenerateLongDataProc(HDataProcWithShifterOp* instruction, CodeGeneratorARM* codegen) {
+  DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
+  DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
+
+  const LocationSummary* const locations = instruction->GetLocations();
+  const uint32_t shift_value = instruction->GetShiftAmount();
+  const HInstruction::InstructionKind kind = instruction->GetInstrKind();
+  const Location first = locations->InAt(0);
+  const Location second = locations->InAt(1);
+  const Location out = locations->Out();
+  const Register first_hi = first.AsRegisterPairHigh<Register>();
+  const Register first_lo = first.AsRegisterPairLow<Register>();
+  const Register out_hi = out.AsRegisterPairHigh<Register>();
+  const Register out_lo = out.AsRegisterPairLow<Register>();
+  const Register second_hi = second.AsRegisterPairHigh<Register>();
+  const Register second_lo = second.AsRegisterPairLow<Register>();
+  const Shift shift = ShiftFromOpKind(instruction->GetOpKind());
+
+  if (shift_value >= 32) {
+    if (shift == LSL) {
+      GenerateDataProcInstruction(kind,
+                                  out_hi,
+                                  first_hi,
+                                  ShifterOperand(second_lo, LSL, shift_value - 32),
+                                  codegen);
+      GenerateDataProcInstruction(kind,
+                                  out_lo,
+                                  first_lo,
+                                  ShifterOperand(0),
+                                  codegen);
+    } else if (shift == ASR) {
+      GenerateDataProc(kind,
+                       out,
+                       first,
+                       GetShifterOperand(second_hi, ASR, shift_value - 32),
+                       ShifterOperand(second_hi, ASR, 31),
+                       codegen);
+    } else {
+      DCHECK_EQ(shift, LSR);
+      GenerateDataProc(kind,
+                       out,
+                       first,
+                       GetShifterOperand(second_hi, LSR, shift_value - 32),
+                       ShifterOperand(0),
+                       codegen);
+    }
+  } else {
+    DCHECK_GT(shift_value, 1U);
+    DCHECK_LT(shift_value, 32U);
+
+    if (shift == LSL) {
+      // We are not doing this for HInstruction::kAdd because the output will require
+      // Location::kOutputOverlap; not applicable to other cases.
+      if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
+        GenerateDataProcInstruction(kind,
+                                    out_hi,
+                                    first_hi,
+                                    ShifterOperand(second_hi, LSL, shift_value),
+                                    codegen);
+        GenerateDataProcInstruction(kind,
+                                    out_hi,
+                                    out_hi,
+                                    ShifterOperand(second_lo, LSR, 32 - shift_value),
+                                    codegen);
+        GenerateDataProcInstruction(kind,
+                                    out_lo,
+                                    first_lo,
+                                    ShifterOperand(second_lo, LSL, shift_value),
+                                    codegen);
+      } else {
+        __ Lsl(IP, second_hi, shift_value);
+        __ orr(IP, IP, ShifterOperand(second_lo, LSR, 32 - shift_value));
+        GenerateDataProc(kind,
+                         out,
+                         first,
+                         ShifterOperand(second_lo, LSL, shift_value),
+                         ShifterOperand(IP),
+                         codegen);
+      }
+    } else {
+      DCHECK(shift == ASR || shift == LSR);
+
+      // We are not doing this for HInstruction::kAdd because the output will require
+      // Location::kOutputOverlap; not applicable to other cases.
+      if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
+        GenerateDataProcInstruction(kind,
+                                    out_lo,
+                                    first_lo,
+                                    ShifterOperand(second_lo, LSR, shift_value),
+                                    codegen);
+        GenerateDataProcInstruction(kind,
+                                    out_lo,
+                                    out_lo,
+                                    ShifterOperand(second_hi, LSL, 32 - shift_value),
+                                    codegen);
+        GenerateDataProcInstruction(kind,
+                                    out_hi,
+                                    first_hi,
+                                    ShifterOperand(second_hi, shift, shift_value),
+                                    codegen);
+      } else {
+        __ Lsr(IP, second_lo, shift_value);
+        __ orr(IP, IP, ShifterOperand(second_hi, LSL, 32 - shift_value));
+        GenerateDataProc(kind,
+                         out,
+                         first,
+                         ShifterOperand(IP),
+                         ShifterOperand(second_hi, shift, shift_value),
+                         codegen);
+      }
+    }
+  }
+}
+
+#undef __
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<ArmAssembler*>(GetAssembler())->  // NOLINT
+
 void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
   stream << Register(reg);
 }
@@ -6709,6 +6897,63 @@
   }
 }
 
+void LocationsBuilderARM::VisitDataProcWithShifterOp(
+    HDataProcWithShifterOp* instruction) {
+  DCHECK(instruction->GetType() == Primitive::kPrimInt ||
+         instruction->GetType() == Primitive::kPrimLong);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  const bool overlap = instruction->GetType() == Primitive::kPrimLong &&
+                       HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
+
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(),
+                    overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM::VisitDataProcWithShifterOp(
+    HDataProcWithShifterOp* instruction) {
+  const LocationSummary* const locations = instruction->GetLocations();
+  const HInstruction::InstructionKind kind = instruction->GetInstrKind();
+  const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
+  const Location left = locations->InAt(0);
+  const Location right = locations->InAt(1);
+  const Location out = locations->Out();
+
+  if (instruction->GetType() == Primitive::kPrimInt) {
+    DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind));
+
+    const Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong
+        ? right.AsRegisterPairLow<Register>()
+        : right.AsRegister<Register>();
+
+    GenerateDataProcInstruction(kind,
+                                out.AsRegister<Register>(),
+                                left.AsRegister<Register>(),
+                                ShifterOperand(second,
+                                               ShiftFromOpKind(op_kind),
+                                               instruction->GetShiftAmount()),
+                                codegen_);
+  } else {
+    DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
+
+    if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
+      const Register second = right.AsRegister<Register>();
+
+      DCHECK_NE(out.AsRegisterPairLow<Register>(), second);
+      GenerateDataProc(kind,
+                       out,
+                       left,
+                       ShifterOperand(second),
+                       ShifterOperand(second, ASR, 31),
+                       codegen_);
+    } else {
+      GenerateLongDataProc(instruction, codegen_);
+    }
+  }
+}
+
 void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) {
   // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
   if (value == 0xffffffffu) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index e6032d2..edccbd4 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2277,8 +2277,8 @@
   }
 }
 
-void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp(
-    HArm64DataProcWithShifterOp* instruction) {
+void LocationsBuilderARM64::VisitDataProcWithShifterOp(
+    HDataProcWithShifterOp* instruction) {
   DCHECK(instruction->GetType() == Primitive::kPrimInt ||
          instruction->GetType() == Primitive::kPrimLong);
   LocationSummary* locations =
@@ -2292,8 +2292,8 @@
   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
-void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp(
-    HArm64DataProcWithShifterOp* instruction) {
+void InstructionCodeGeneratorARM64::VisitDataProcWithShifterOp(
+    HDataProcWithShifterOp* instruction) {
   Primitive::Type type = instruction->GetType();
   HInstruction::InstructionKind kind = instruction->GetInstrKind();
   DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
@@ -2302,21 +2302,20 @@
   if (kind != HInstruction::kNeg) {
     left = InputRegisterAt(instruction, 0);
   }
-  // If this `HArm64DataProcWithShifterOp` was created by merging a type conversion as the
+  // If this `HDataProcWithShifterOp` was created by merging a type conversion as the
   // shifter operand operation, the IR generating `right_reg` (input to the type
   // conversion) can have a different type from the current instruction's type,
   // so we manually indicate the type.
   Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type);
-  int64_t shift_amount = instruction->GetShiftAmount() &
-      (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance);
-
   Operand right_operand(0);
 
-  HArm64DataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
-  if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind)) {
+  HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
+  if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
     right_operand = Operand(right_reg, helpers::ExtendFromOpKind(op_kind));
   } else {
-    right_operand = Operand(right_reg, helpers::ShiftFromOpKind(op_kind), shift_amount);
+    right_operand = Operand(right_reg,
+                            helpers::ShiftFromOpKind(op_kind),
+                            instruction->GetShiftAmount());
   }
 
   // Logical binary operations do not support extension operations in the
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 5c4ca5b..6bfbe4a 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -1216,6 +1216,17 @@
   }
 }
 
+inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
+  switch (op_kind) {
+    case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
+    case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
+    case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
+    default:
+      LOG(FATAL) << "Unexpected op kind " << op_kind;
+      UNREACHABLE();
+  }
+}
+
 void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
   stream << vixl32::Register(reg);
 }
@@ -1260,6 +1271,185 @@
   return 0;
 }
 
+static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
+                                        vixl32::Register out,
+                                        vixl32::Register first,
+                                        const Operand& second,
+                                        CodeGeneratorARMVIXL* codegen) {
+  if (second.IsImmediate() && second.GetImmediate() == 0) {
+    const Operand in = kind == HInstruction::kAnd
+        ? Operand(0)
+        : Operand(first);
+
+    __ Mov(out, in);
+  } else {
+    switch (kind) {
+      case HInstruction::kAdd:
+        __ Add(out, first, second);
+        break;
+      case HInstruction::kAnd:
+        __ And(out, first, second);
+        break;
+      case HInstruction::kOr:
+        __ Orr(out, first, second);
+        break;
+      case HInstruction::kSub:
+        __ Sub(out, first, second);
+        break;
+      case HInstruction::kXor:
+        __ Eor(out, first, second);
+        break;
+      default:
+        LOG(FATAL) << "Unexpected instruction kind: " << kind;
+        UNREACHABLE();
+    }
+  }
+}
+
+static void GenerateDataProc(HInstruction::InstructionKind kind,
+                             const Location& out,
+                             const Location& first,
+                             const Operand& second_lo,
+                             const Operand& second_hi,
+                             CodeGeneratorARMVIXL* codegen) {
+  const vixl32::Register first_hi = HighRegisterFrom(first);
+  const vixl32::Register first_lo = LowRegisterFrom(first);
+  const vixl32::Register out_hi = HighRegisterFrom(out);
+  const vixl32::Register out_lo = LowRegisterFrom(out);
+
+  if (kind == HInstruction::kAdd) {
+    __ Adds(out_lo, first_lo, second_lo);
+    __ Adc(out_hi, first_hi, second_hi);
+  } else if (kind == HInstruction::kSub) {
+    __ Subs(out_lo, first_lo, second_lo);
+    __ Sbc(out_hi, first_hi, second_hi);
+  } else {
+    GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
+    GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
+  }
+}
+
+static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
+  return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
+}
+
+static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
+                                 CodeGeneratorARMVIXL* codegen) {
+  DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
+  DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
+
+  const LocationSummary* const locations = instruction->GetLocations();
+  const uint32_t shift_value = instruction->GetShiftAmount();
+  const HInstruction::InstructionKind kind = instruction->GetInstrKind();
+  const Location first = locations->InAt(0);
+  const Location second = locations->InAt(1);
+  const Location out = locations->Out();
+  const vixl32::Register first_hi = HighRegisterFrom(first);
+  const vixl32::Register first_lo = LowRegisterFrom(first);
+  const vixl32::Register out_hi = HighRegisterFrom(out);
+  const vixl32::Register out_lo = LowRegisterFrom(out);
+  const vixl32::Register second_hi = HighRegisterFrom(second);
+  const vixl32::Register second_lo = LowRegisterFrom(second);
+  const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
+
+  if (shift_value >= 32) {
+    if (shift == ShiftType::LSL) {
+      GenerateDataProcInstruction(kind,
+                                  out_hi,
+                                  first_hi,
+                                  Operand(second_lo, ShiftType::LSL, shift_value - 32),
+                                  codegen);
+      GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
+    } else if (shift == ShiftType::ASR) {
+      GenerateDataProc(kind,
+                       out,
+                       first,
+                       GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
+                       Operand(second_hi, ShiftType::ASR, 31),
+                       codegen);
+    } else {
+      DCHECK_EQ(shift, ShiftType::LSR);
+      GenerateDataProc(kind,
+                       out,
+                       first,
+                       GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
+                       0,
+                       codegen);
+    }
+  } else {
+    DCHECK_GT(shift_value, 1U);
+    DCHECK_LT(shift_value, 32U);
+
+    UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
+
+    if (shift == ShiftType::LSL) {
+      // We are not doing this for HInstruction::kAdd because the output will require
+      // Location::kOutputOverlap; not applicable to other cases.
+      if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
+        GenerateDataProcInstruction(kind,
+                                    out_hi,
+                                    first_hi,
+                                    Operand(second_hi, ShiftType::LSL, shift_value),
+                                    codegen);
+        GenerateDataProcInstruction(kind,
+                                    out_hi,
+                                    out_hi,
+                                    Operand(second_lo, ShiftType::LSR, 32 - shift_value),
+                                    codegen);
+        GenerateDataProcInstruction(kind,
+                                    out_lo,
+                                    first_lo,
+                                    Operand(second_lo, ShiftType::LSL, shift_value),
+                                    codegen);
+      } else {
+        const vixl32::Register temp = temps.Acquire();
+
+        __ Lsl(temp, second_hi, shift_value);
+        __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
+        GenerateDataProc(kind,
+                         out,
+                         first,
+                         Operand(second_lo, ShiftType::LSL, shift_value),
+                         temp,
+                         codegen);
+      }
+    } else {
+      DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
+
+      // We are not doing this for HInstruction::kAdd because the output will require
+      // Location::kOutputOverlap; not applicable to other cases.
+      if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
+        GenerateDataProcInstruction(kind,
+                                    out_lo,
+                                    first_lo,
+                                    Operand(second_lo, ShiftType::LSR, shift_value),
+                                    codegen);
+        GenerateDataProcInstruction(kind,
+                                    out_lo,
+                                    out_lo,
+                                    Operand(second_hi, ShiftType::LSL, 32 - shift_value),
+                                    codegen);
+        GenerateDataProcInstruction(kind,
+                                    out_hi,
+                                    first_hi,
+                                    Operand(second_hi, shift, shift_value),
+                                    codegen);
+      } else {
+        const vixl32::Register temp = temps.Acquire();
+
+        __ Lsr(temp, second_lo, shift_value);
+        __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
+        GenerateDataProc(kind,
+                         out,
+                         first,
+                         temp,
+                         Operand(second_hi, shift, shift_value),
+                         codegen);
+      }
+    }
+  }
+}
+
 #undef __
 
 CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
@@ -6781,6 +6971,60 @@
   }
 }
 
+void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
+    HDataProcWithShifterOp* instruction) {
+  DCHECK(instruction->GetType() == Primitive::kPrimInt ||
+         instruction->GetType() == Primitive::kPrimLong);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  const bool overlap = instruction->GetType() == Primitive::kPrimLong &&
+                       HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
+
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(),
+                    overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
+    HDataProcWithShifterOp* instruction) {
+  const LocationSummary* const locations = instruction->GetLocations();
+  const HInstruction::InstructionKind kind = instruction->GetInstrKind();
+  const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
+
+  if (instruction->GetType() == Primitive::kPrimInt) {
+    DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind));
+
+    const vixl32::Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong
+        ? LowRegisterFrom(locations->InAt(1))
+        : InputRegisterAt(instruction, 1);
+
+    GenerateDataProcInstruction(kind,
+                                OutputRegister(instruction),
+                                InputRegisterAt(instruction, 0),
+                                Operand(second,
+                                        ShiftFromOpKind(op_kind),
+                                        instruction->GetShiftAmount()),
+                                codegen_);
+  } else {
+    DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
+
+    if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
+      const vixl32::Register second = InputRegisterAt(instruction, 1);
+
+      DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
+      GenerateDataProc(kind,
+                       locations->Out(),
+                       locations->InAt(0),
+                       second,
+                       Operand(second, ShiftType::ASR, 31),
+                       codegen_);
+    } else {
+      GenerateLongDataProc(instruction, codegen_);
+    }
+  }
+}
+
 // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
 void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
                                                        vixl32::Register first,
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index ecb8687..e184745 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
 #define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
 
+#include "instruction_simplifier_shared.h"
 #include "debug/dwarf/register.h"
 #include "locations.h"
 #include "nodes.h"
@@ -29,6 +30,9 @@
 #pragma GCC diagnostic pop
 
 namespace art {
+
+using helpers::HasShifterOperand;
+
 namespace arm {
 namespace helpers {
 
@@ -218,6 +222,14 @@
   return Location::FpuRegisterPairLocation(low.GetCode(), high.GetCode());
 }
 
+inline bool ShifterOperandSupportsExtension(HInstruction* instruction) {
+  DCHECK(HasShifterOperand(instruction, kArm));
+  // TODO: HAdd applied to the other integral types could make use of
+  // the SXTAB, SXTAH, UXTAB and UXTAH instructions.
+  return instruction->GetType() == Primitive::kPrimLong &&
+         (instruction->IsAdd() || instruction->IsSub());
+}
+
 }  // namespace helpers
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 93ea090..d3f431e 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
 
 #include "code_generator.h"
+#include "instruction_simplifier_shared.h"
 #include "locations.h"
 #include "nodes.h"
 #include "utils/arm64/assembler_arm64.h"
@@ -31,6 +32,10 @@
 #pragma GCC diagnostic pop
 
 namespace art {
+
+using helpers::CanFitInShifterOperand;
+using helpers::HasShifterOperand;
+
 namespace arm64 {
 namespace helpers {
 
@@ -290,11 +295,11 @@
   return true;
 }
 
-inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) {
+inline vixl::aarch64::Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
   switch (op_kind) {
-    case HArm64DataProcWithShifterOp::kASR: return vixl::aarch64::ASR;
-    case HArm64DataProcWithShifterOp::kLSL: return vixl::aarch64::LSL;
-    case HArm64DataProcWithShifterOp::kLSR: return vixl::aarch64::LSR;
+    case HDataProcWithShifterOp::kASR: return vixl::aarch64::ASR;
+    case HDataProcWithShifterOp::kLSL: return vixl::aarch64::LSL;
+    case HDataProcWithShifterOp::kLSR: return vixl::aarch64::LSR;
     default:
       LOG(FATAL) << "Unexpected op kind " << op_kind;
       UNREACHABLE();
@@ -302,14 +307,14 @@
   }
 }
 
-inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) {
+inline vixl::aarch64::Extend ExtendFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
   switch (op_kind) {
-    case HArm64DataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB;
-    case HArm64DataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH;
-    case HArm64DataProcWithShifterOp::kUXTW: return vixl::aarch64::UXTW;
-    case HArm64DataProcWithShifterOp::kSXTB: return vixl::aarch64::SXTB;
-    case HArm64DataProcWithShifterOp::kSXTH: return vixl::aarch64::SXTH;
-    case HArm64DataProcWithShifterOp::kSXTW: return vixl::aarch64::SXTW;
+    case HDataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB;
+    case HDataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH;
+    case HDataProcWithShifterOp::kUXTW: return vixl::aarch64::UXTW;
+    case HDataProcWithShifterOp::kSXTB: return vixl::aarch64::SXTB;
+    case HDataProcWithShifterOp::kSXTH: return vixl::aarch64::SXTH;
+    case HDataProcWithShifterOp::kSXTW: return vixl::aarch64::SXTW;
     default:
       LOG(FATAL) << "Unexpected op kind " << op_kind;
       UNREACHABLE();
@@ -317,31 +322,8 @@
   }
 }
 
-inline bool CanFitInShifterOperand(HInstruction* instruction) {
-  if (instruction->IsTypeConversion()) {
-    HTypeConversion* conversion = instruction->AsTypeConversion();
-    Primitive::Type result_type = conversion->GetResultType();
-    Primitive::Type input_type = conversion->GetInputType();
-    // We don't expect to see the same type as input and result.
-    return Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type) &&
-        (result_type != input_type);
-  } else {
-    return (instruction->IsShl() && instruction->AsShl()->InputAt(1)->IsIntConstant()) ||
-        (instruction->IsShr() && instruction->AsShr()->InputAt(1)->IsIntConstant()) ||
-        (instruction->IsUShr() && instruction->AsUShr()->InputAt(1)->IsIntConstant());
-  }
-}
-
-inline bool HasShifterOperand(HInstruction* instr) {
-  // `neg` instructions are an alias of `sub` using the zero register as the
-  // first register input.
-  bool res = instr->IsAdd() || instr->IsAnd() || instr->IsNeg() ||
-      instr->IsOr() || instr->IsSub() || instr->IsXor();
-  return res;
-}
-
 inline bool ShifterOperandSupportsExtension(HInstruction* instruction) {
-  DCHECK(HasShifterOperand(instruction));
+  DCHECK(HasShifterOperand(instruction, kArm64));
   // Although the `neg` instruction is an alias of the `sub` instruction, `HNeg`
   // does *not* support extension. This is because the `extended register` form
   // of the `sub` instruction interprets the left register with code 31 as the
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index f6fba88..2bf5c53 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -511,12 +511,10 @@
   void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE {
     StartAttributeStream("kind") << instruction->GetOpKind();
   }
-#endif
 
-#ifdef ART_ENABLE_CODEGEN_arm64
-  void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE {
+  void VisitDataProcWithShifterOp(HDataProcWithShifterOp* instruction) OVERRIDE {
     StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind();
-    if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) {
+    if (HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) {
       StartAttributeStream("shift") << instruction->GetShiftAmount();
     }
   }
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 56e4c7a..5f5e29b 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -15,23 +15,124 @@
  */
 
 #include "code_generator.h"
+#include "common_arm.h"
 #include "instruction_simplifier_arm.h"
 #include "instruction_simplifier_shared.h"
 #include "mirror/array-inl.h"
+#include "nodes.h"
 
 namespace art {
+
+using helpers::CanFitInShifterOperand;
+using helpers::HasShifterOperand;
+
 namespace arm {
 
-void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) {
-  if (TryCombineMultiplyAccumulate(instruction, kArm)) {
+using helpers::ShifterOperandSupportsExtension;
+
+bool InstructionSimplifierArmVisitor::TryMergeIntoShifterOperand(HInstruction* use,
+                                                                 HInstruction* bitfield_op,
+                                                                 bool do_merge) {
+  DCHECK(HasShifterOperand(use, kArm));
+  DCHECK(use->IsBinaryOperation());
+  DCHECK(CanFitInShifterOperand(bitfield_op));
+  DCHECK(!bitfield_op->HasEnvironmentUses());
+
+  Primitive::Type type = use->GetType();
+  if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
+    return false;
+  }
+
+  HInstruction* left = use->InputAt(0);
+  HInstruction* right = use->InputAt(1);
+  DCHECK(left == bitfield_op || right == bitfield_op);
+
+  if (left == right) {
+    // TODO: Handle special transformations in this situation?
+    // For example should we transform `(x << 1) + (x << 1)` into `(x << 2)`?
+    // Or should this be part of a separate transformation logic?
+    return false;
+  }
+
+  bool is_commutative = use->AsBinaryOperation()->IsCommutative();
+  HInstruction* other_input;
+  if (bitfield_op == right) {
+    other_input = left;
+  } else {
+    if (is_commutative) {
+      other_input = right;
+    } else {
+      return false;
+    }
+  }
+
+  HDataProcWithShifterOp::OpKind op_kind;
+  int shift_amount = 0;
+
+  HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
+  shift_amount &= use->GetType() == Primitive::kPrimInt
+      ? kMaxIntShiftDistance
+      : kMaxLongShiftDistance;
+
+  if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
+    if (!ShifterOperandSupportsExtension(use)) {
+      return false;
+    }
+  // Shift by 1 is a special case that results in the same number and type of instructions
+  // as this simplification, but potentially shorter code.
+  } else if (type == Primitive::kPrimLong && shift_amount == 1) {
+    return false;
+  }
+
+  if (do_merge) {
+    HDataProcWithShifterOp* alu_with_op =
+        new (GetGraph()->GetArena()) HDataProcWithShifterOp(use,
+                                                            other_input,
+                                                            bitfield_op->InputAt(0),
+                                                            op_kind,
+                                                            shift_amount,
+                                                            use->GetDexPc());
+    use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
+    if (bitfield_op->GetUses().empty()) {
+      bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
+    }
     RecordSimplification();
   }
+
+  return true;
 }
 
-void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) {
-  if (TryMergeNegatedInput(instruction)) {
-    RecordSimplification();
+// Merge a bitfield move instruction into its uses if it can be merged in all of them.
+bool InstructionSimplifierArmVisitor::TryMergeIntoUsersShifterOperand(HInstruction* bitfield_op) {
+  DCHECK(CanFitInShifterOperand(bitfield_op));
+
+  if (bitfield_op->HasEnvironmentUses()) {
+    return false;
   }
+
+  const HUseList<HInstruction*>& uses = bitfield_op->GetUses();
+
+  // Check whether we can merge the instruction in all its users' shifter operand.
+  for (const HUseListNode<HInstruction*>& use : uses) {
+    HInstruction* user = use.GetUser();
+    if (!HasShifterOperand(user, kArm)) {
+      return false;
+    }
+    if (!CanMergeIntoShifterOperand(user, bitfield_op)) {
+      return false;
+    }
+  }
+
+  // Merge the instruction into its uses.
+  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+    HInstruction* user = it->GetUser();
+    // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand().
+    ++it;
+    bool merged = MergeIntoShifterOperand(user, bitfield_op);
+    DCHECK(merged);
+  }
+
+  return true;
 }
 
 void InstructionSimplifierArmVisitor::VisitAnd(HAnd* instruction) {
@@ -89,5 +190,49 @@
   }
 }
 
+void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) {
+  if (TryCombineMultiplyAccumulate(instruction, kArm)) {
+    RecordSimplification();
+  }
+}
+
+void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) {
+  if (TryMergeNegatedInput(instruction)) {
+    RecordSimplification();
+  }
+}
+
+void InstructionSimplifierArmVisitor::VisitShl(HShl* instruction) {
+  if (instruction->InputAt(1)->IsConstant()) {
+    TryMergeIntoUsersShifterOperand(instruction);
+  }
+}
+
+void InstructionSimplifierArmVisitor::VisitShr(HShr* instruction) {
+  if (instruction->InputAt(1)->IsConstant()) {
+    TryMergeIntoUsersShifterOperand(instruction);
+  }
+}
+
+void InstructionSimplifierArmVisitor::VisitTypeConversion(HTypeConversion* instruction) {
+  Primitive::Type result_type = instruction->GetResultType();
+  Primitive::Type input_type = instruction->GetInputType();
+
+  if (input_type == result_type) {
+    // We let the arch-independent code handle this.
+    return;
+  }
+
+  if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
+    TryMergeIntoUsersShifterOperand(instruction);
+  }
+}
+
+void InstructionSimplifierArmVisitor::VisitUShr(HUShr* instruction) {
+  if (instruction->InputAt(1)->IsConstant()) {
+    TryMergeIntoUsersShifterOperand(instruction);
+  }
+}
+
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h
index 9b54511..e2ed257 100644
--- a/compiler/optimizing/instruction_simplifier_arm.h
+++ b/compiler/optimizing/instruction_simplifier_arm.h
@@ -35,11 +35,41 @@
     }
   }
 
-  void VisitMul(HMul* instruction) OVERRIDE;
-  void VisitOr(HOr* instruction) OVERRIDE;
+  bool TryMergeIntoUsersShifterOperand(HInstruction* instruction);
+  bool TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge);
+  bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) {
+    return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ false);
+  }
+  bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) {
+    DCHECK(CanMergeIntoShifterOperand(use, bitfield_op));
+    return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ true);
+  }
+
+  /**
+   * This simplifier uses a special-purpose BB visitor.
+   * (1) No need to visit Phi nodes.
+   * (2) Since statements can be removed in a "forward" fashion,
+   *     the visitor should test if each statement is still there.
+   */
+  void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
+    // TODO: fragile iteration, provide more robust iterators?
+    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+      HInstruction* instruction = it.Current();
+      if (instruction->IsInBlock()) {
+        instruction->Accept(this);
+      }
+    }
+  }
+
   void VisitAnd(HAnd* instruction) OVERRIDE;
   void VisitArrayGet(HArrayGet* instruction) OVERRIDE;
   void VisitArraySet(HArraySet* instruction) OVERRIDE;
+  void VisitMul(HMul* instruction) OVERRIDE;
+  void VisitOr(HOr* instruction) OVERRIDE;
+  void VisitShl(HShl* instruction) OVERRIDE;
+  void VisitShr(HShr* instruction) OVERRIDE;
+  void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
+  void VisitUShr(HUShr* instruction) OVERRIDE;
 
   OptimizingCompilerStats* stats_;
 };
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 6d107d5..73b7b2b 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -22,16 +22,18 @@
 #include "mirror/string.h"
 
 namespace art {
-namespace arm64 {
 
 using helpers::CanFitInShifterOperand;
 using helpers::HasShifterOperand;
+
+namespace arm64 {
+
 using helpers::ShifterOperandSupportsExtension;
 
 bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use,
                                                                    HInstruction* bitfield_op,
                                                                    bool do_merge) {
-  DCHECK(HasShifterOperand(use));
+  DCHECK(HasShifterOperand(use, kArm64));
   DCHECK(use->IsBinaryOperation() || use->IsNeg());
   DCHECK(CanFitInShifterOperand(bitfield_op));
   DCHECK(!bitfield_op->HasEnvironmentUses());
@@ -72,23 +74,22 @@
     }
   }
 
-  HArm64DataProcWithShifterOp::OpKind op_kind;
+  HDataProcWithShifterOp::OpKind op_kind;
   int shift_amount = 0;
-  HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
+  HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
 
-  if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind) &&
-      !ShifterOperandSupportsExtension(use)) {
+  if (HDataProcWithShifterOp::IsExtensionOp(op_kind) && !ShifterOperandSupportsExtension(use)) {
     return false;
   }
 
   if (do_merge) {
-    HArm64DataProcWithShifterOp* alu_with_op =
-        new (GetGraph()->GetArena()) HArm64DataProcWithShifterOp(use,
-                                                                 other_input,
-                                                                 bitfield_op->InputAt(0),
-                                                                 op_kind,
-                                                                 shift_amount,
-                                                                 use->GetDexPc());
+    HDataProcWithShifterOp* alu_with_op =
+        new (GetGraph()->GetArena()) HDataProcWithShifterOp(use,
+                                                            other_input,
+                                                            bitfield_op->InputAt(0),
+                                                            op_kind,
+                                                            shift_amount,
+                                                            use->GetDexPc());
     use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
     if (bitfield_op->GetUses().empty()) {
       bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
@@ -112,7 +113,7 @@
   // Check whether we can merge the instruction in all its users' shifter operand.
   for (const HUseListNode<HInstruction*>& use : uses) {
     HInstruction* user = use.GetUser();
-    if (!HasShifterOperand(user)) {
+    if (!HasShifterOperand(user, kArm64)) {
       return false;
     }
     if (!CanMergeIntoShifterOperand(user, bitfield_op)) {
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index d4cb1f1..65654f5 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -40,11 +40,11 @@
                                   HInstruction* bitfield_op,
                                   bool do_merge);
   bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) {
-    return TryMergeIntoShifterOperand(use, bitfield_op, false);
+    return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ false);
   }
   bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) {
     DCHECK(CanMergeIntoShifterOperand(use, bitfield_op));
-    return TryMergeIntoShifterOperand(use, bitfield_op, true);
+    return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ true);
   }
 
   /**
diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h
index 56804f5..83e3ffc 100644
--- a/compiler/optimizing/instruction_simplifier_shared.h
+++ b/compiler/optimizing/instruction_simplifier_shared.h
@@ -21,6 +21,33 @@
 
 namespace art {
 
+namespace helpers {
+
+inline bool CanFitInShifterOperand(HInstruction* instruction) {
+  if (instruction->IsTypeConversion()) {
+    HTypeConversion* conversion = instruction->AsTypeConversion();
+    Primitive::Type result_type = conversion->GetResultType();
+    Primitive::Type input_type = conversion->GetInputType();
+    // We don't expect to see the same type as input and result.
+    return Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type) &&
+        (result_type != input_type);
+  } else {
+    return (instruction->IsShl() && instruction->AsShl()->InputAt(1)->IsIntConstant()) ||
+        (instruction->IsShr() && instruction->AsShr()->InputAt(1)->IsIntConstant()) ||
+        (instruction->IsUShr() && instruction->AsUShr()->InputAt(1)->IsIntConstant());
+  }
+}
+
+inline bool HasShifterOperand(HInstruction* instr, InstructionSet isa) {
+  // On ARM64 `neg` instructions are an alias of `sub` using the zero register
+  // as the first register input.
+  bool res = instr->IsAdd() || instr->IsAnd() || (isa == kArm64 && instr->IsNeg()) ||
+      instr->IsOr() || instr->IsSub() || instr->IsXor();
+  return res;
+}
+
+}  // namespace helpers
+
 bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa);
 // For bitwise operations (And/Or/Xor) with a negated input, try to use
 // a negated bitwise instruction.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 96f9aba..56ca84a 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1346,6 +1346,7 @@
 #else
 #define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M)                         \
   M(BitwiseNegatedRight, Instruction)                                   \
+  M(DataProcWithShifterOp, Instruction)                                 \
   M(MultiplyAccumulate, Instruction)                                    \
   M(IntermediateAddress, Instruction)
 #endif
@@ -1357,12 +1358,7 @@
   M(ArmDexCacheArraysBase, Instruction)
 #endif
 
-#ifndef ART_ENABLE_CODEGEN_arm64
 #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M)
-#else
-#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M)                          \
-  M(Arm64DataProcWithShifterOp, Instruction)
-#endif
 
 #ifndef ART_ENABLE_CODEGEN_mips
 #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M)
@@ -6603,9 +6599,6 @@
 #ifdef ART_ENABLE_CODEGEN_arm
 #include "nodes_arm.h"
 #endif
-#ifdef ART_ENABLE_CODEGEN_arm64
-#include "nodes_arm64.h"
-#endif
 #ifdef ART_ENABLE_CODEGEN_mips
 #include "nodes_mips.h"
 #endif
diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h
deleted file mode 100644
index 3f88717..0000000
--- a/compiler/optimizing/nodes_arm64.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_OPTIMIZING_NODES_ARM64_H_
-#define ART_COMPILER_OPTIMIZING_NODES_ARM64_H_
-
-#include "nodes.h"
-
-namespace art {
-
-class HArm64DataProcWithShifterOp FINAL : public HExpression<2> {
- public:
-  enum OpKind {
-    kLSL,   // Logical shift left.
-    kLSR,   // Logical shift right.
-    kASR,   // Arithmetic shift right.
-    kUXTB,  // Unsigned extend byte.
-    kUXTH,  // Unsigned extend half-word.
-    kUXTW,  // Unsigned extend word.
-    kSXTB,  // Signed extend byte.
-    kSXTH,  // Signed extend half-word.
-    kSXTW,  // Signed extend word.
-
-    // Aliases.
-    kFirstShiftOp = kLSL,
-    kLastShiftOp = kASR,
-    kFirstExtensionOp = kUXTB,
-    kLastExtensionOp = kSXTW
-  };
-  HArm64DataProcWithShifterOp(HInstruction* instr,
-                              HInstruction* left,
-                              HInstruction* right,
-                              OpKind op,
-                              // The shift argument is unused if the operation
-                              // is an extension.
-                              int shift = 0,
-                              uint32_t dex_pc = kNoDexPc)
-      : HExpression(instr->GetType(), SideEffects::None(), dex_pc),
-        instr_kind_(instr->GetKind()), op_kind_(op), shift_amount_(shift) {
-    DCHECK(!instr->HasSideEffects());
-    SetRawInputAt(0, left);
-    SetRawInputAt(1, right);
-  }
-
-  bool CanBeMoved() const OVERRIDE { return true; }
-  bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE {
-    const HArm64DataProcWithShifterOp* other = other_instr->AsArm64DataProcWithShifterOp();
-    return instr_kind_ == other->instr_kind_ &&
-        op_kind_ == other->op_kind_ &&
-        shift_amount_ == other->shift_amount_;
-  }
-
-  static bool IsShiftOp(OpKind op_kind) {
-    return kFirstShiftOp <= op_kind && op_kind <= kLastShiftOp;
-  }
-
-  static bool IsExtensionOp(OpKind op_kind) {
-    return kFirstExtensionOp <= op_kind && op_kind <= kLastExtensionOp;
-  }
-
-  // Find the operation kind and shift amount from a bitfield move instruction.
-  static void GetOpInfoFromInstruction(HInstruction* bitfield_op,
-                                       /*out*/OpKind* op_kind,
-                                       /*out*/int* shift_amount);
-
-  InstructionKind GetInstrKind() const { return instr_kind_; }
-  OpKind GetOpKind() const { return op_kind_; }
-  int GetShiftAmount() const { return shift_amount_; }
-
-  DECLARE_INSTRUCTION(Arm64DataProcWithShifterOp);
-
- private:
-  InstructionKind instr_kind_;
-  OpKind op_kind_;
-  int shift_amount_;
-
-  friend std::ostream& operator<<(std::ostream& os, OpKind op);
-
-  DISALLOW_COPY_AND_ASSIGN(HArm64DataProcWithShifterOp);
-};
-
-std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op);
-
-}  // namespace art
-
-#endif  // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_
diff --git a/compiler/optimizing/nodes_arm64.cc b/compiler/optimizing/nodes_shared.cc
similarity index 63%
rename from compiler/optimizing/nodes_arm64.cc
rename to compiler/optimizing/nodes_shared.cc
index ac2f093..f145bf9 100644
--- a/compiler/optimizing/nodes_arm64.cc
+++ b/compiler/optimizing/nodes_shared.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,15 +15,15 @@
  */
 
 #include "common_arm64.h"
-#include "nodes.h"
+#include "nodes_shared.h"
 
 namespace art {
 
-using arm64::helpers::CanFitInShifterOperand;
+using helpers::CanFitInShifterOperand;
 
-void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruction,
-                                                           /*out*/OpKind* op_kind,
-                                                           /*out*/int* shift_amount) {
+void HDataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruction,
+                                                      /*out*/OpKind* op_kind,
+                                                      /*out*/int* shift_amount) {
   DCHECK(CanFitInShifterOperand(instruction));
   if (instruction->IsShl()) {
     *op_kind = kLSL;
@@ -41,12 +41,11 @@
     int result_size = Primitive::ComponentSize(result_type);
     int input_size = Primitive::ComponentSize(input_type);
     int min_size = std::min(result_size, input_size);
-    // This follows the logic in
-    // `InstructionCodeGeneratorARM64::VisitTypeConversion()`.
     if (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimLong) {
-      // There is actually nothing to do. The register will be used as a W
-      // register, discarding the top bits. This is represented by the default
-      // encoding 'LSL 0'.
+      // There is actually nothing to do. On ARM the high register from the
+      // pair will be ignored. On ARM64 the register will be used as a W
+      // register, discarding the top bits. This is represented by the
+      // default encoding 'LSL 0'.
       *op_kind = kLSL;
       *shift_amount = 0;
     } else if (result_type == Primitive::kPrimChar ||
@@ -64,17 +63,17 @@
   }
 }
 
-std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op) {
+std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op) {
   switch (op) {
-    case HArm64DataProcWithShifterOp::kLSL:  return os << "LSL";
-    case HArm64DataProcWithShifterOp::kLSR:  return os << "LSR";
-    case HArm64DataProcWithShifterOp::kASR:  return os << "ASR";
-    case HArm64DataProcWithShifterOp::kUXTB: return os << "UXTB";
-    case HArm64DataProcWithShifterOp::kUXTH: return os << "UXTH";
-    case HArm64DataProcWithShifterOp::kUXTW: return os << "UXTW";
-    case HArm64DataProcWithShifterOp::kSXTB: return os << "SXTB";
-    case HArm64DataProcWithShifterOp::kSXTH: return os << "SXTH";
-    case HArm64DataProcWithShifterOp::kSXTW: return os << "SXTW";
+    case HDataProcWithShifterOp::kLSL:  return os << "LSL";
+    case HDataProcWithShifterOp::kLSR:  return os << "LSR";
+    case HDataProcWithShifterOp::kASR:  return os << "ASR";
+    case HDataProcWithShifterOp::kUXTB: return os << "UXTB";
+    case HDataProcWithShifterOp::kUXTH: return os << "UXTH";
+    case HDataProcWithShifterOp::kUXTW: return os << "UXTW";
+    case HDataProcWithShifterOp::kSXTB: return os << "SXTB";
+    case HDataProcWithShifterOp::kSXTH: return os << "SXTH";
+    case HDataProcWithShifterOp::kSXTW: return os << "SXTW";
     default:
       LOG(FATAL) << "Invalid OpKind " << static_cast<int>(op);
       UNREACHABLE();
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index 814202e..c6bfbcc 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -150,6 +150,81 @@
   DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress);
 };
 
+class HDataProcWithShifterOp FINAL : public HExpression<2> {
+ public:
+  enum OpKind {
+    kLSL,   // Logical shift left.
+    kLSR,   // Logical shift right.
+    kASR,   // Arithmetic shift right.
+    kUXTB,  // Unsigned extend byte.
+    kUXTH,  // Unsigned extend half-word.
+    kUXTW,  // Unsigned extend word.
+    kSXTB,  // Signed extend byte.
+    kSXTH,  // Signed extend half-word.
+    kSXTW,  // Signed extend word.
+
+    // Aliases.
+    kFirstShiftOp = kLSL,
+    kLastShiftOp = kASR,
+    kFirstExtensionOp = kUXTB,
+    kLastExtensionOp = kSXTW
+  };
+  HDataProcWithShifterOp(HInstruction* instr,
+                         HInstruction* left,
+                         HInstruction* right,
+                         OpKind op,
+                         // The shift argument is unused if the operation
+                         // is an extension.
+                         int shift = 0,
+                         uint32_t dex_pc = kNoDexPc)
+      : HExpression(instr->GetType(), SideEffects::None(), dex_pc),
+        instr_kind_(instr->GetKind()), op_kind_(op),
+        shift_amount_(shift & (instr->GetType() == Primitive::kPrimInt
+            ? kMaxIntShiftDistance
+            : kMaxLongShiftDistance)) {
+    DCHECK(!instr->HasSideEffects());
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+
+  bool CanBeMoved() const OVERRIDE { return true; }
+  bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE {
+    const HDataProcWithShifterOp* other = other_instr->AsDataProcWithShifterOp();
+    return instr_kind_ == other->instr_kind_ &&
+        op_kind_ == other->op_kind_ &&
+        shift_amount_ == other->shift_amount_;
+  }
+
+  static bool IsShiftOp(OpKind op_kind) {
+    return kFirstShiftOp <= op_kind && op_kind <= kLastShiftOp;
+  }
+
+  static bool IsExtensionOp(OpKind op_kind) {
+    return kFirstExtensionOp <= op_kind && op_kind <= kLastExtensionOp;
+  }
+
+  // Find the operation kind and shift amount from a bitfield move instruction.
+  static void GetOpInfoFromInstruction(HInstruction* bitfield_op,
+                                       /*out*/OpKind* op_kind,
+                                       /*out*/int* shift_amount);
+
+  InstructionKind GetInstrKind() const { return instr_kind_; }
+  OpKind GetOpKind() const { return op_kind_; }
+  int GetShiftAmount() const { return shift_amount_; }
+
+  DECLARE_INSTRUCTION(DataProcWithShifterOp);
+
+ private:
+  InstructionKind instr_kind_;
+  OpKind op_kind_;
+  int shift_amount_;
+
+  friend std::ostream& operator<<(std::ostream& os, OpKind op);
+
+  DISALLOW_COPY_AND_ASSIGN(HDataProcWithShifterOp);
+};
+
+std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op);
 
 }  // namespace art
 
diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc
index e3701fb..558dcc4 100644
--- a/compiler/optimizing/scheduler_arm64.cc
+++ b/compiler/optimizing/scheduler_arm64.cc
@@ -31,8 +31,8 @@
   last_visited_latency_ = kArm64IntegerOpLatency;
 }
 
-void SchedulingLatencyVisitorARM64::VisitArm64DataProcWithShifterOp(
-    HArm64DataProcWithShifterOp* ATTRIBUTE_UNUSED) {
+void SchedulingLatencyVisitorARM64::VisitDataProcWithShifterOp(
+    HDataProcWithShifterOp* ATTRIBUTE_UNUSED) {
   last_visited_latency_ = kArm64DataProcWithShifterOpLatency;
 }
 
diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h
index 702027c..7a33720 100644
--- a/compiler/optimizing/scheduler_arm64.h
+++ b/compiler/optimizing/scheduler_arm64.h
@@ -74,7 +74,8 @@
 #define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \
   M(BitwiseNegatedRight, unused)                 \
   M(MultiplyAccumulate, unused)                  \
-  M(IntermediateAddress, unused)
+  M(IntermediateAddress, unused)                 \
+  M(DataProcWithShifterOp, unused)
 
 #define DECLARE_VISIT_INSTRUCTION(type, unused)  \
   void Visit##type(H##type* instruction) OVERRIDE;
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index a4561b8..e967398 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -76,6 +76,25 @@
    * the shifter operand.
    */
 
+  /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (before)
+  /// CHECK-DAG:   <<l:j\d+>>           ParameterValue
+  /// CHECK-DAG:   <<b:b\d+>>           ParameterValue
+  /// CHECK:       <<tmp:j\d+>>         TypeConversion [<<b>>]
+  /// CHECK:                            Sub [<<l>>,<<tmp>>]
+
+  /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (after)
+  /// CHECK-DAG:   <<l:j\d+>>           ParameterValue
+  /// CHECK-DAG:   <<b:b\d+>>           ParameterValue
+  /// CHECK:                            DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB
+
+  /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        TypeConversion
+  /// CHECK-NOT:                        Sub
+
+  /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) disassembly (after)
+  /// CHECK:                            subs r{{\d+}}, r{{\d+}}, r{{\d+}}
+  /// CHECK:                            sbc r{{\d+}}, r{{\d+}}, r{{\d+}}, asr #31
+
   /// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (before)
   /// CHECK-DAG:   <<l:j\d+>>           ParameterValue
   /// CHECK-DAG:   <<b:b\d+>>           ParameterValue
@@ -85,7 +104,7 @@
   /// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (after)
   /// CHECK-DAG:   <<l:j\d+>>           ParameterValue
   /// CHECK-DAG:   <<b:b\d+>>           ParameterValue
-  /// CHECK:                            Arm64DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB
+  /// CHECK:                            DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB
 
   /// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (after)
   /// CHECK-NOT:                        TypeConversion
@@ -106,6 +125,21 @@
    * inputs are the the IR.
    */
 
+  /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (before)
+  /// CHECK:       <<a:i\d+>>           ParameterValue
+  /// CHECK:       <<Const2:i\d+>>      IntConstant 2
+  /// CHECK:       <<tmp:i\d+>>         Shl [<<a>>,<<Const2>>]
+  /// CHECK:                            Add [<<tmp>>,<<tmp>>]
+
+  /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (after)
+  /// CHECK-DAG:   <<a:i\d+>>           ParameterValue
+  /// CHECK-DAG:   <<Const2:i\d+>>      IntConstant 2
+  /// CHECK:       <<Shl:i\d+>>         Shl [<<a>>,<<Const2>>]
+  /// CHECK:                            Add [<<Shl>>,<<Shl>>]
+
+  /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
   /// CHECK-START-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (before)
   /// CHECK:       <<a:i\d+>>           ParameterValue
   /// CHECK:       <<Const2:i\d+>>      IntConstant 2
@@ -119,7 +153,7 @@
   /// CHECK:                            Add [<<Shl>>,<<Shl>>]
 
   /// CHECK-START-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (after)
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   public static int $opt$noinline$sameInput(int a) {
     if (doThrow) throw new Error();
@@ -131,6 +165,28 @@
    * Check that we perform the merge for multiple uses.
    */
 
+  /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (before)
+  /// CHECK:       <<arg:i\d+>>         ParameterValue
+  /// CHECK:       <<Const23:i\d+>>     IntConstant 23
+  /// CHECK:       <<tmp:i\d+>>         Shl [<<arg>>,<<Const23>>]
+  /// CHECK:                            Add [<<tmp>>,{{i\d+}}]
+  /// CHECK:                            Add [<<tmp>>,{{i\d+}}]
+  /// CHECK:                            Add [<<tmp>>,{{i\d+}}]
+  /// CHECK:                            Add [<<tmp>>,{{i\d+}}]
+  /// CHECK:                            Add [<<tmp>>,{{i\d+}}]
+
+  /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (after)
+  /// CHECK:       <<arg:i\d+>>         ParameterValue
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+
+  /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        Shl
+  /// CHECK-NOT:                        Add
+
   /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (before)
   /// CHECK:       <<arg:i\d+>>         ParameterValue
   /// CHECK:       <<Const23:i\d+>>     IntConstant 23
@@ -143,11 +199,11 @@
 
   /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (after)
   /// CHECK:       <<arg:i\d+>>         ParameterValue
-  /// CHECK:                            Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
-  /// CHECK:                            Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
-  /// CHECK:                            Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
-  /// CHECK:                            Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
-  /// CHECK:                            Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+  /// CHECK:                            DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
 
   /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (after)
   /// CHECK-NOT:                        Shl
@@ -171,9 +227,19 @@
    * operand, so test that only the shifts are merged.
    */
 
+  /// CHECK-START-ARM: void Main.$opt$noinline$testAnd(long, long) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  /// CHECK-START-ARM: void Main.$opt$noinline$testAnd(long, long) disassembly (after)
+  /// CHECK:                            and lsl
+  /// CHECK:                            sbfx
+  /// CHECK:                            asr
+  /// CHECK:                            and
+
   /// CHECK-START-ARM64: void Main.$opt$noinline$testAnd(long, long) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$noinline$testAnd(long, long) disassembly (after)
   /// CHECK:                            and lsl
@@ -186,9 +252,18 @@
                      (a & (b << 5)) | (a & (byte)b));
   }
 
+  /// CHECK-START-ARM: void Main.$opt$noinline$testOr(int, int) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  /// CHECK-START-ARM: void Main.$opt$noinline$testOr(int, int) disassembly (after)
+  /// CHECK:                            orr asr
+  /// CHECK:                            ubfx
+  /// CHECK:                            orr
+
   /// CHECK-START-ARM64: void Main.$opt$noinline$testOr(int, int) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$noinline$testOr(int, int) disassembly (after)
   /// CHECK:                            orr asr
@@ -201,9 +276,19 @@
                     (a | (b >> 6)) | (a | (char)b));
   }
 
+  /// CHECK-START-ARM: void Main.$opt$noinline$testXor(long, long) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  /// CHECK-START-ARM: void Main.$opt$noinline$testXor(long, long) disassembly (after)
+  /// CHECK:                            eor lsr
+  /// CHECK:                            mov
+  /// CHECK:                            asr
+  /// CHECK:                            eor
+
   /// CHECK-START-ARM64: void Main.$opt$noinline$testXor(long, long) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$noinline$testXor(long, long) disassembly (after)
   /// CHECK:                            eor lsr
@@ -216,9 +301,12 @@
                      (a ^ (b >>> 7)) | (a ^ (int)b));
   }
 
+  /// CHECK-START-ARM: void Main.$opt$noinline$testNeg(int) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                            DataProcWithShifterOp
+
   /// CHECK-START-ARM64: void Main.$opt$noinline$testNeg(int) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$noinline$testNeg(int) disassembly (after)
   /// CHECK:                            neg lsl
@@ -239,9 +327,12 @@
    * does occur on the right-hand.
    */
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
   /// CHECK-NOT:                        TypeConversion
@@ -252,9 +343,11 @@
     assertIntEquals(a + $noinline$byteToShort(b), a + (short)b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendByteInt2(int, byte) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt2(int, byte) instruction_simplifier_arm64 (after)
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   public static void $opt$validateExtendByteInt2(int a, byte b) {
     // The conversion to `int` has been optimized away, so there is nothing to merge.
@@ -263,13 +356,25 @@
     assertLongEquals(a + $noinline$byteToLong(b), a + (long)b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  /// CHECK-START-ARM: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm (after)
+  /// CHECK:                            TypeConversion
+  /// CHECK-NOT:                        TypeConversion
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after)
   /// CHECK:                            TypeConversion
@@ -294,9 +399,12 @@
     $opt$validateExtendByteLong(a, b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after)
   /// CHECK-NOT:                        TypeConversion
@@ -306,22 +414,41 @@
     assertIntEquals(a + $noinline$charToShort(b), a + (short)b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendCharInt2(int, char) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt2(int, char) instruction_simplifier_arm64 (after)
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   public static void $opt$validateExtendCharInt2(int a, char b) {
     // The conversion to `int` has been optimized away, so there is nothing to merge.
     assertIntEquals (a + $noinline$charToInt (b), a +  (int)b);
-    // There is an environment use for `(long)b`, preventing the merge.
+    // There is an environment use for `(long)b` and the implicit `(long)a`, preventing the merge.
     assertLongEquals(a + $noinline$charToLong(b), a + (long)b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  /// CHECK-START-ARM: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm (after)
+  /// CHECK:                            TypeConversion
+  /// CHECK:                            TypeConversion
+  /// CHECK-NOT:                        TypeConversion
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm64 (after)
   /// CHECK:                            TypeConversion
@@ -332,7 +459,7 @@
     // The first two tests have a type conversion.
     assertLongEquals(a + $noinline$charToByte (b), a +  (byte)b);
     assertLongEquals(a + $noinline$charToShort(b), a + (short)b);
-    // This test does not because the conversion to `int` is optimized away.
+    // On ARM64 this test does not because the conversion to `int` is optimized away.
     assertLongEquals(a + $noinline$charToInt  (b), a +   (int)b);
   }
 
@@ -342,9 +469,12 @@
     $opt$validateExtendCharLong(a, b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after)
   /// CHECK-NOT:                        TypeConversion
@@ -354,21 +484,41 @@
     assertIntEquals(a + $noinline$shortToChar (b), a + (char)b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendShortInt2(int, short) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt2(int, short) instruction_simplifier_arm64 (after)
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
-  /// CHECK-NOT:                        Arm64DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   public static void $opt$validateExtendShortInt2(int a, short b) {
     // The conversion to `int` has been optimized away, so there is nothing to merge.
     assertIntEquals (a + $noinline$shortToInt  (b), a +  (int)b);
-    // There is an environment use for `(long)b`, preventing the merge.
+    // There is an environment use for `(long)b` and the implicit `(long)a`, preventing the merge.
     assertLongEquals(a + $noinline$shortToLong (b), a + (long)b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  /// CHECK-START-ARM: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm (after)
+  /// CHECK:                            TypeConversion
+  /// CHECK:                            TypeConversion
+  /// CHECK-NOT:                        TypeConversion
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm64 (after)
   /// CHECK:                            TypeConversion
@@ -379,7 +529,7 @@
     // The first two tests have a type conversion.
     assertLongEquals(a + $noinline$shortToByte(b), a + (byte)b);
     assertLongEquals(a + $noinline$shortToChar(b), a + (char)b);
-    // This test does not because the conversion to `int` is optimized away.
+    // On ARM64 this test does not because the conversion to `int` is optimized away.
     assertLongEquals(a + $noinline$shortToInt (b), a +  (int)b);
   }
 
@@ -389,11 +539,31 @@
     $opt$validateExtendShortLong(a, b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  /// CHECK-START-ARM: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm (after)
+  /// CHECK:                            TypeConversion
+  /// CHECK:                            TypeConversion
+  /// CHECK:                            TypeConversion
+  /// CHECK-NOT:                        TypeConversion
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm64 (after)
   /// CHECK:                            TypeConversion
@@ -411,11 +581,34 @@
     assertLongEquals(a + $noinline$intToLong (b), a +  (long)b);
   }
 
+  /// CHECK-START-ARM: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  /// CHECK-START-ARM: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm (after)
+  /// CHECK:                            TypeConversion
+  /// CHECK:                            TypeConversion
+  /// CHECK:                            TypeConversion
+  /// CHECK:                            TypeConversion
+  /// CHECK-NOT:                        TypeConversion
+
   /// CHECK-START-ARM64: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm64 (after)
   /// CHECK:                            TypeConversion
@@ -449,40 +642,83 @@
 
 
   // Each test line below should see one merge.
+  /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+  // Note: `b << 32`, `b >> 32` and `b >>> 32` are optimized away by generic simplifier.
+
+  /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after)
+  /// CHECK-NOT:                        Shl
+  /// CHECK-NOT:                        Shr
+  /// CHECK-NOT:                        UShr
+
   /// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
   // Note: `b << 32`, `b >> 32` and `b >>> 32` are optimized away by generic simplifier.
 
   /// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after)
@@ -552,43 +788,89 @@
   }
 
   // Each test line below should see one merge.
+  /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after)
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
+
+  // On ARM shifts by 1 are not merged.
+  /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after)
+  /// CHECK:                            Shl
+  /// CHECK-NOT:                        Shl
+  /// CHECK:                            Shr
+  /// CHECK-NOT:                        Shr
+  /// CHECK:                            UShr
+  /// CHECK-NOT:                        UShr
+
   /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
-  /// CHECK:                            Arm64DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK:                            DataProcWithShifterOp
+  /// CHECK-NOT:                        DataProcWithShifterOp
 
   /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
   /// CHECK-NOT:                        Shl
diff --git a/test/593-checker-shift-and-simplifier/src/Main.java b/test/593-checker-shift-and-simplifier/src/Main.java
index 65e809a..c9826bc 100644
--- a/test/593-checker-shift-and-simplifier/src/Main.java
+++ b/test/593-checker-shift-and-simplifier/src/Main.java
@@ -21,6 +21,17 @@
   // A very particular set of operations that caused a double removal by the
   // ARM64 simplifier doing "forward" removals (b/27851582).
 
+  /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (before)
+  /// CHECK-DAG: <<Get:i\d+>> ArrayGet
+  /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+  /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
+  /// CHECK-DAG:              And [<<Not>>,<<Shl>>]
+  //
+  /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (after)
+  /// CHECK-DAG: <<Get:i\d+>> ArrayGet
+  /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+  /// CHECK-DAG:              DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+
   /// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (before)
   /// CHECK-DAG: <<Get:i\d+>> ArrayGet
   /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
@@ -30,7 +41,7 @@
   /// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (after)
   /// CHECK-DAG: <<Get:i\d+>> ArrayGet
   /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
-  /// CHECK-DAG:              Arm64DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+  /// CHECK-DAG:              DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
   private static int operations() {
      int r = a[0];
      int n = ~r;