content: /*
   * 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.
   */
  
  public class Main {
  
    // A dummy value to defeat inlining of these routines.
    static boolean doThrow = false;
  
    public static void assertByteEquals(byte expected, byte result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    public static void assertCharEquals(char expected, char result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    public static void assertShortEquals(short expected, short result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    public static void assertIntEquals(int expected, int result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    public static void assertLongEquals(long expected, long result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    // Non-inlinable type-casting helpers.
    static  char $noinline$byteToChar   (byte v) { if (doThrow) throw new Error(); return  (char)v; }
    static short $noinline$byteToShort  (byte v) { if (doThrow) throw new Error(); return (short)v; }
    static   int $noinline$byteToInt    (byte v) { if (doThrow) throw new Error(); return   (int)v; }
    static  long $noinline$byteToLong   (byte v) { if (doThrow) throw new Error(); return  (long)v; }
    static  byte $noinline$charToByte   (char v) { if (doThrow) throw new Error(); return  (byte)v; }
    static short $noinline$charToShort  (char v) { if (doThrow) throw new Error(); return (short)v; }
    static   int $noinline$charToInt    (char v) { if (doThrow) throw new Error(); return   (int)v; }
    static  long $noinline$charToLong   (char v) { if (doThrow) throw new Error(); return  (long)v; }
    static  byte $noinline$shortToByte (short v) { if (doThrow) throw new Error(); return  (byte)v; }
    static  char $noinline$shortToChar (short v) { if (doThrow) throw new Error(); return  (char)v; }
    static   int $noinline$shortToInt  (short v) { if (doThrow) throw new Error(); return   (int)v; }
    static  long $noinline$shortToLong (short v) { if (doThrow) throw new Error(); return  (long)v; }
    static  byte $noinline$intToByte     (int v) { if (doThrow) throw new Error(); return  (byte)v; }
    static  char $noinline$intToChar     (int v) { if (doThrow) throw new Error(); return  (char)v; }
    static short $noinline$intToShort    (int v) { if (doThrow) throw new Error(); return (short)v; }
    static  long $noinline$intToLong     (int v) { if (doThrow) throw new Error(); return  (long)v; }
    static  byte $noinline$longToByte   (long v) { if (doThrow) throw new Error(); return  (byte)v; }
    static  char $noinline$longToChar   (long v) { if (doThrow) throw new Error(); return  (char)v; }
    static short $noinline$longToShort  (long v) { if (doThrow) throw new Error(); return (short)v; }
    static   int $noinline$longToInt    (long v) { if (doThrow) throw new Error(); return   (int)v; }
  
    /**
     * Basic test merging a bitfield move operation (here a type conversion) into
     * the shifter operand.
     */
  
    /// 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
    /// CHECK:       <<tmp:j\d+>>         TypeConversion [<<b>>]
    /// CHECK:                            Sub [<<l>>,<<tmp>>]
  
    /// 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-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        TypeConversion
    /// CHECK-NOT:                        Sub
  
    /// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) disassembly (after)
    /// CHECK:                            sub x{{\d+}}, x{{\d+}}, w{{\d+}}, sxtb
  
    public static long $opt$noinline$translate(long l, byte b) {
      if (doThrow) throw new Error();
      long tmp = (long)b;
      return l - tmp;
    }
  
  
    /**
     * Test that we do not merge into the shifter operand when the left and right
     * inputs are the the IR.
     */
  
    /// CHECK-START-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (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-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (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-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    public static int $opt$noinline$sameInput(int a) {
      if (doThrow) throw new Error();
      int tmp = a << 2;
      return tmp + tmp;
    }
  
    /**
     * Check that we perform the merge for multiple uses.
     */
  
    /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (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-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-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        Shl
    /// CHECK-NOT:                        Add
  
    public static int $opt$noinline$multipleUses(int arg) {
      if (doThrow) throw new Error();
      int tmp = arg << 23;
      switch (arg) {
        case 1:  return (arg | 1) + tmp;
        case 2:  return (arg | 2) + tmp;
        case 3:  return (arg | 3) + tmp;
        case 4:  return (arg | 4) + tmp;
        case (1 << 20):  return (arg | 5) + tmp;
        default: return 0;
      }
    }
  
    /**
     * Logical instructions cannot take 'extend' operations into the shift
     * operand, so test that only the shifts are merged.
     */
  
    /// CHECK-START-ARM64: void Main.$opt$noinline$testAnd(long, long) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$noinline$testAnd(long, long) disassembly (after)
    /// CHECK:                            and lsl
    /// CHECK:                            sxtb
    /// CHECK:                            and
  
    static void $opt$noinline$testAnd(long a, long b) {
      if (doThrow) throw new Error();
      assertLongEquals((a & $noinline$LongShl(b, 5)) | (a & $noinline$longToByte(b)),
                       (a & (b << 5)) | (a & (byte)b));
    }
  
    /// CHECK-START-ARM64: void Main.$opt$noinline$testOr(int, int) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$noinline$testOr(int, int) disassembly (after)
    /// CHECK:                            orr asr
    /// CHECK:                            uxth
    /// CHECK:                            orr
  
    static void $opt$noinline$testOr(int a, int b) {
      if (doThrow) throw new Error();
      assertIntEquals((a | $noinline$IntShr(b, 6)) | (a | $noinline$intToChar(b)),
                      (a | (b >> 6)) | (a | (char)b));
    }
  
    /// CHECK-START-ARM64: void Main.$opt$noinline$testXor(long, long) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$noinline$testXor(long, long) disassembly (after)
    /// CHECK:                            eor lsr
    /// CHECK:                            sxtw
    /// CHECK:                            eor
  
    static void $opt$noinline$testXor(long a, long b) {
      if (doThrow) throw new Error();
      assertLongEquals((a ^ $noinline$LongUshr(b, 7)) | (a ^ $noinline$longToInt(b)),
                       (a ^ (b >>> 7)) | (a ^ (int)b));
    }
  
    /// CHECK-START-ARM64: void Main.$opt$noinline$testNeg(int) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$noinline$testNeg(int) disassembly (after)
    /// CHECK:                            neg lsl
    /// CHECK:                            sxth
    /// CHECK:                            neg
  
    static void $opt$noinline$testNeg(int a) {
      if (doThrow) throw new Error();
      assertIntEquals(-$noinline$IntShl(a, 8) | -$noinline$intToShort(a),
                      (-(a << 8)) | (-(short)a));
    }
  
    /**
     * The functions below are used to compare the result of optimized operations
     * to non-optimized operations.
     * On the left-hand side we use a non-inlined function call to ensure the
     * optimization does not occur. The checker tests ensure that the optimization
     * does occur on the right-hand.
     */
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        TypeConversion
  
    public static void $opt$validateExtendByteInt1(int a, byte b) {
      assertIntEquals(a + $noinline$byteToChar (b), a +  (char)b);
      // Conversions byte->short and short->int are implicit; nothing to merge.
      assertIntEquals(a + $noinline$byteToShort(b), a + (short)b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt2(int, byte) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    public static void $opt$validateExtendByteInt2(int a, byte b) {
      // The conversion to `int` has been optimized away, so there is nothing to merge.
      assertIntEquals (a + $noinline$byteToInt (b), a +  (int)b);
      // There is an environment use for `(long)b`, preventing the merge.
      assertLongEquals(a + $noinline$byteToLong(b), a + (long)b);
    }
  
    /// 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-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after)
    /// CHECK:                            TypeConversion
    /// CHECK-NOT:                        TypeConversion
  
    public static void $opt$validateExtendByteLong(long a, byte b) {
      // In each of the following tests, there will be a merge on the LHS.
  
      // The first test has an explicit byte->char conversion on RHS,
      // followed by a conversion that is merged with the Add.
      assertLongEquals(a + $noinline$byteToChar (b), a +  (char)b);
      // Since conversions byte->short and byte->int are implicit, the RHS
      // for the two tests below is the same and one is eliminated by GVN.
      // The other is then merged to a shifter operand instruction.
      assertLongEquals(a + $noinline$byteToShort(b), a + (short)b);
      assertLongEquals(a + $noinline$byteToInt  (b), a +  (int)b);
    }
  
    public static void $opt$validateExtendByte(long a, byte b) {
      $opt$validateExtendByteInt1((int)a, b);
      $opt$validateExtendByteInt2((int)a, b);
      $opt$validateExtendByteLong(a, b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        TypeConversion
  
    public static void $opt$validateExtendCharInt1(int a, char b) {
      assertIntEquals(a + $noinline$charToByte (b), a +  (byte)b);
      assertIntEquals(a + $noinline$charToShort(b), a + (short)b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt2(int, char) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    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.
      assertLongEquals(a + $noinline$charToLong(b), a + (long)b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm64 (after)
    /// CHECK:                            TypeConversion
    /// CHECK:                            TypeConversion
    /// CHECK-NOT:                        TypeConversion
  
    public static void $opt$validateExtendCharLong(long a, char b) {
      // 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.
      assertLongEquals(a + $noinline$charToInt  (b), a +   (int)b);
    }
  
    public static void $opt$validateExtendChar(long a, char b) {
      $opt$validateExtendCharInt1((int)a, b);
      $opt$validateExtendCharInt2((int)a, b);
      $opt$validateExtendCharLong(a, b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        TypeConversion
  
    public static void $opt$validateExtendShortInt1(int a, short b) {
      assertIntEquals(a + $noinline$shortToByte (b), a + (byte)b);
      assertIntEquals(a + $noinline$shortToChar (b), a + (char)b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt2(int, short) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
    /// CHECK-NOT:                        Arm64DataProcWithShifterOp
  
    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.
      assertLongEquals(a + $noinline$shortToLong (b), a + (long)b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm64 (after)
    /// CHECK:                            TypeConversion
    /// CHECK:                            TypeConversion
    /// CHECK-NOT:                        TypeConversion
  
    public static void $opt$validateExtendShortLong(long a, short b) {
      // 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.
      assertLongEquals(a + $noinline$shortToInt (b), a +  (int)b);
    }
  
    public static void $opt$validateExtendShort(long a, short b) {
      $opt$validateExtendShortInt1((int)a, b);
      $opt$validateExtendShortInt2((int)a, b);
      $opt$validateExtendShortLong(a, b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm64 (after)
    /// CHECK:                            TypeConversion
    /// CHECK:                            TypeConversion
    /// CHECK:                            TypeConversion
    /// CHECK-NOT:                        TypeConversion
  
    public static void $opt$validateExtendInt(long a, int b) {
      // All tests have a conversion to `long`. The first three tests also have a
      // conversion from `int` to the specified type. For each test the conversion
      // to `long` is merged into the shifter operand.
      assertLongEquals(a + $noinline$intToByte (b), a +  (byte)b);
      assertLongEquals(a + $noinline$intToChar (b), a +  (char)b);
      assertLongEquals(a + $noinline$intToShort(b), a + (short)b);
      assertLongEquals(a + $noinline$intToLong (b), a +  (long)b);
    }
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm64 (after)
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
    /// CHECK:                            Arm64DataProcWithShifterOp
  
    /// CHECK-START-ARM64: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm64 (after)
    /// CHECK:                            TypeConversion
    /// CHECK:                            TypeConversion
    /// CHECK:                            TypeConversion
    /// CHECK:                            TypeConversion
    /// CHECK-NOT:                        TypeConversion
  
    public static void $opt$validateExtendLong(long a, long b) {
      // Each test has two conversions, from `long` and then back to `long`. The
      // conversions to `long` are merged.
      assertLongEquals(a + $noinline$longToByte (b), a +  (byte)b);
      assertLongEquals(a + $noinline$longToChar (b), a +  (char)b);
      assertLongEquals(a + $noinline$longToShort(b), a + (short)b);
      assertLongEquals(a + $noinline$longToInt  (b), a +   (int)b);
    }
  
  
    static int $noinline$IntShl(int b, int c) {
      if (doThrow) throw new Error();
      return b << c;
    }
    static int $noinline$IntShr(int b, int c) {
      if (doThrow) throw new Error();
      return b >> c;
    }
    static int $noinline$IntUshr(int b, int c) {
      if (doThrow) throw new Error();
      return b >>> c;
    }
  
  
    // Each test line below should see one merge.
    /// 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
    // 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)
    /// CHECK-NOT:                        Shl
    /// CHECK-NOT:                        Shr
    /// CHECK-NOT:                        UShr
  
    public static void $opt$validateShiftInt(int a, int b) {
      assertIntEquals(a + $noinline$IntShl(b, 1),   a + (b <<  1));
      assertIntEquals(a + $noinline$IntShl(b, 6),   a + (b <<  6));
      assertIntEquals(a + $noinline$IntShl(b, 7),   a + (b <<  7));
      assertIntEquals(a + $noinline$IntShl(b, 8),   a + (b <<  8));
      assertIntEquals(a + $noinline$IntShl(b, 14),  a + (b << 14));
      assertIntEquals(a + $noinline$IntShl(b, 15),  a + (b << 15));
      assertIntEquals(a + $noinline$IntShl(b, 16),  a + (b << 16));
      assertIntEquals(a + $noinline$IntShl(b, 30),  a + (b << 30));
      assertIntEquals(a + $noinline$IntShl(b, 31),  a + (b << 31));
      assertIntEquals(a + $noinline$IntShl(b, 32),  a + (b << $opt$inline$IntConstant32()));
      assertIntEquals(a + $noinline$IntShl(b, 62),  a + (b << $opt$inline$IntConstant62()));
      assertIntEquals(a + $noinline$IntShl(b, 63),  a + (b << $opt$inline$IntConstant63()));
  
      assertIntEquals(a - $noinline$IntShr(b, 1),   a - (b >>  1));
      assertIntEquals(a - $noinline$IntShr(b, 6),   a - (b >>  6));
      assertIntEquals(a - $noinline$IntShr(b, 7),   a - (b >>  7));
      assertIntEquals(a - $noinline$IntShr(b, 8),   a - (b >>  8));
      assertIntEquals(a - $noinline$IntShr(b, 14),  a - (b >> 14));
      assertIntEquals(a - $noinline$IntShr(b, 15),  a - (b >> 15));
      assertIntEquals(a - $noinline$IntShr(b, 16),  a - (b >> 16));
      assertIntEquals(a - $noinline$IntShr(b, 30),  a - (b >> 30));
      assertIntEquals(a - $noinline$IntShr(b, 31),  a - (b >> 31));
      assertIntEquals(a - $noinline$IntShr(b, 32),  a - (b >> $opt$inline$IntConstant32()));
      assertIntEquals(a - $noinline$IntShr(b, 62),  a - (b >> $opt$inline$IntConstant62()));
      assertIntEquals(a - $noinline$IntShr(b, 63),  a - (b >> $opt$inline$IntConstant63()));
  
      assertIntEquals(a ^ $noinline$IntUshr(b, 1),   a ^ (b >>>  1));
      assertIntEquals(a ^ $noinline$IntUshr(b, 6),   a ^ (b >>>  6));
      assertIntEquals(a ^ $noinline$IntUshr(b, 7),   a ^ (b >>>  7));
      assertIntEquals(a ^ $noinline$IntUshr(b, 8),   a ^ (b >>>  8));
      assertIntEquals(a ^ $noinline$IntUshr(b, 14),  a ^ (b >>> 14));
      assertIntEquals(a ^ $noinline$IntUshr(b, 15),  a ^ (b >>> 15));
      assertIntEquals(a ^ $noinline$IntUshr(b, 16),  a ^ (b >>> 16));
      assertIntEquals(a ^ $noinline$IntUshr(b, 30),  a ^ (b >>> 30));
      assertIntEquals(a ^ $noinline$IntUshr(b, 31),  a ^ (b >>> 31));
      assertIntEquals(a ^ $noinline$IntUshr(b, 32),  a ^ (b >>> $opt$inline$IntConstant32()));
      assertIntEquals(a ^ $noinline$IntUshr(b, 62),  a ^ (b >>> $opt$inline$IntConstant62()));
      assertIntEquals(a ^ $noinline$IntUshr(b, 63),  a ^ (b >>> $opt$inline$IntConstant63()));
    }
  
    // Hiding constants outside the range [0, 32) used for int shifts from Jack.
    // (Jack extracts only the low 5 bits.)
    public static int $opt$inline$IntConstant32() { return 32; }
    public static int $opt$inline$IntConstant62() { return 62; }
    public static int $opt$inline$IntConstant63() { return 63; }
  
  
    static long $noinline$LongShl(long b, long c) {
      if (doThrow) throw new Error();
      return b << c;
    }
    static long $noinline$LongShr(long b, long c) {
      if (doThrow) throw new Error();
      return b >> c;
    }
    static long $noinline$LongUshr(long b, long c) {
      if (doThrow) throw new Error();
      return b >>> c;
    }
  
    // Each test line below should see one merge.
    /// 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-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
    /// CHECK-NOT:                        Shl
    /// CHECK-NOT:                        Shr
    /// CHECK-NOT:                        UShr
  
    public static void $opt$validateShiftLong(long a, long b) {
      assertLongEquals(a + $noinline$LongShl(b, 1),   a + (b <<  1));
      assertLongEquals(a + $noinline$LongShl(b, 6),   a + (b <<  6));
      assertLongEquals(a + $noinline$LongShl(b, 7),   a + (b <<  7));
      assertLongEquals(a + $noinline$LongShl(b, 8),   a + (b <<  8));
      assertLongEquals(a + $noinline$LongShl(b, 14),  a + (b << 14));
      assertLongEquals(a + $noinline$LongShl(b, 15),  a + (b << 15));
      assertLongEquals(a + $noinline$LongShl(b, 16),  a + (b << 16));
      assertLongEquals(a + $noinline$LongShl(b, 30),  a + (b << 30));
      assertLongEquals(a + $noinline$LongShl(b, 31),  a + (b << 31));
      assertLongEquals(a + $noinline$LongShl(b, 32),  a + (b << 32));
      assertLongEquals(a + $noinline$LongShl(b, 62),  a + (b << 62));
      assertLongEquals(a + $noinline$LongShl(b, 63),  a + (b << 63));
  
      assertLongEquals(a - $noinline$LongShr(b, 1),   a - (b >>  1));
      assertLongEquals(a - $noinline$LongShr(b, 6),   a - (b >>  6));
      assertLongEquals(a - $noinline$LongShr(b, 7),   a - (b >>  7));
      assertLongEquals(a - $noinline$LongShr(b, 8),   a - (b >>  8));
      assertLongEquals(a - $noinline$LongShr(b, 14),  a - (b >> 14));
      assertLongEquals(a - $noinline$LongShr(b, 15),  a - (b >> 15));
      assertLongEquals(a - $noinline$LongShr(b, 16),  a - (b >> 16));
      assertLongEquals(a - $noinline$LongShr(b, 30),  a - (b >> 30));
      assertLongEquals(a - $noinline$LongShr(b, 31),  a - (b >> 31));
      assertLongEquals(a - $noinline$LongShr(b, 32),  a - (b >> 32));
      assertLongEquals(a - $noinline$LongShr(b, 62),  a - (b >> 62));
      assertLongEquals(a - $noinline$LongShr(b, 63),  a - (b >> 63));
  
      assertLongEquals(a ^ $noinline$LongUshr(b, 1),   a ^ (b >>>  1));
      assertLongEquals(a ^ $noinline$LongUshr(b, 6),   a ^ (b >>>  6));
      assertLongEquals(a ^ $noinline$LongUshr(b, 7),   a ^ (b >>>  7));
      assertLongEquals(a ^ $noinline$LongUshr(b, 8),   a ^ (b >>>  8));
      assertLongEquals(a ^ $noinline$LongUshr(b, 14),  a ^ (b >>> 14));
      assertLongEquals(a ^ $noinline$LongUshr(b, 15),  a ^ (b >>> 15));
      assertLongEquals(a ^ $noinline$LongUshr(b, 16),  a ^ (b >>> 16));
      assertLongEquals(a ^ $noinline$LongUshr(b, 30),  a ^ (b >>> 30));
      assertLongEquals(a ^ $noinline$LongUshr(b, 31),  a ^ (b >>> 31));
      assertLongEquals(a ^ $noinline$LongUshr(b, 32),  a ^ (b >>> 32));
      assertLongEquals(a ^ $noinline$LongUshr(b, 62),  a ^ (b >>> 62));
      assertLongEquals(a ^ $noinline$LongUshr(b, 63),  a ^ (b >>> 63));
    }
  
  
    public static void main(String[] args) {
      assertLongEquals(10000L - 3L, $opt$noinline$translate(10000L, (byte)3));
      assertLongEquals(-10000L - -3L, $opt$noinline$translate(-10000L, (byte)-3));
  
      assertIntEquals(4096, $opt$noinline$sameInput(512));
      assertIntEquals(-8192, $opt$noinline$sameInput(-1024));
  
      assertIntEquals(((1 << 23) | 1), $opt$noinline$multipleUses(1));
      assertIntEquals(((1 << 20) | 5), $opt$noinline$multipleUses(1 << 20));
  
      long inputs[] = {
        -((1L <<  7) - 1L), -((1L <<  7)), -((1L <<  7) + 1L),
        -((1L << 15) - 1L), -((1L << 15)), -((1L << 15) + 1L),
        -((1L << 16) - 1L), -((1L << 16)), -((1L << 16) + 1L),
        -((1L << 31) - 1L), -((1L << 31)), -((1L << 31) + 1L),
        -((1L << 32) - 1L), -((1L << 32)), -((1L << 32) + 1L),
        -((1L << 63) - 1L), -((1L << 63)), -((1L << 63) + 1L),
        -42L, -314L, -2718281828L, -0x123456789L, -0x987654321L,
        -1L, -20L, -300L, -4000L, -50000L, -600000L, -7000000L, -80000000L,
        0L,
        1L, 20L, 300L, 4000L, 50000L, 600000L, 7000000L, 80000000L,
        42L,  314L,  2718281828L,  0x123456789L,  0x987654321L,
        (1L <<  7) - 1L, (1L <<  7), (1L <<  7) + 1L,
        (1L <<  8) - 1L, (1L <<  8), (1L <<  8) + 1L,
        (1L << 15) - 1L, (1L << 15), (1L << 15) + 1L,
        (1L << 16) - 1L, (1L << 16), (1L << 16) + 1L,
        (1L << 31) - 1L, (1L << 31), (1L << 31) + 1L,
        (1L << 32) - 1L, (1L << 32), (1L << 32) + 1L,
        (1L << 63) - 1L, (1L << 63), (1L << 63) + 1L,
        Long.MIN_VALUE, Long.MAX_VALUE
      };
      for (int i = 0; i < inputs.length; i++) {
        $opt$noinline$testNeg((int)inputs[i]);
        for (int j = 0; j < inputs.length; j++) {
          $opt$noinline$testAnd(inputs[i], inputs[j]);
          $opt$noinline$testOr((int)inputs[i], (int)inputs[j]);
          $opt$noinline$testXor(inputs[i], inputs[j]);
  
          $opt$validateExtendByte(inputs[i], (byte)inputs[j]);
          $opt$validateExtendChar(inputs[i], (char)inputs[j]);
          $opt$validateExtendShort(inputs[i], (short)inputs[j]);
          $opt$validateExtendInt(inputs[i], (int)inputs[j]);
          $opt$validateExtendLong(inputs[i], inputs[j]);
  
          $opt$validateShiftInt((int)inputs[i], (int)inputs[j]);
          $opt$validateShiftLong(inputs[i], inputs[j]);
        }
      }
  
    }
  }
  