/*
 * 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.
 * 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 {

  public static void assertIntEquals(int expected, int result) {
    if (expected != result) {
      throw new Error("Expected: " + expected + ", found: " + result);
    }
  }

  /// CHECK-START-ARM64: void Main.checkIntCase(int[]) instruction_simplifier_arm64 (before)
  /// CHECK-DAG:             <<Array:l\d+>>         ParameterValue
  /// CHECK-DAG:             <<Const5:i\d+>>        IntConstant 5
  /// CHECK-DAG:             <<Repl:d\d+>>          VecReplicateScalar [<<Const5>>]
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array>>,<<Index>>]
  /// CHECK-DAG:             <<Add:d\d+>>           VecAdd [<<Load>>,<<Repl>>]
  /// CHECK-DAG:                                    VecStore [<<Array>>,<<Index>>,<<Add>>]

  /// CHECK-START-ARM64: void Main.checkIntCase(int[]) instruction_simplifier_arm64 (after)
  /// CHECK-DAG:             <<Array:l\d+>>         ParameterValue
  /// CHECK-DAG:             <<Const5:i\d+>>        IntConstant 5
  /// CHECK-DAG:             <<DataOffset:i\d+>>    IntConstant 12
  /// CHECK-DAG:             <<Const2:i\d+>>        IntConstant 2
  /// CHECK-DAG:             <<Repl:d\d+>>          VecReplicateScalar [<<Const5>>]
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Address1:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const2>>]
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array>>,<<Address1>>]
  /// CHECK-DAG:             <<Add:d\d+>>           VecAdd [<<Load>>,<<Repl>>]
  /// CHECK-DAG:             <<Address2:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const2>>]
  /// CHECK-DAG:                                    VecStore [<<Array>>,<<Address2>>,<<Add>>]

  /// CHECK-START-ARM64: void Main.checkIntCase(int[]) GVN$after_arch (after)
  /// CHECK-DAG:             <<Array:l\d+>>         ParameterValue
  /// CHECK-DAG:             <<Const5:i\d+>>        IntConstant 5
  /// CHECK-DAG:             <<DataOffset:i\d+>>    IntConstant 12
  /// CHECK-DAG:             <<Const2:i\d+>>        IntConstant 2
  /// CHECK-DAG:             <<Repl:d\d+>>          VecReplicateScalar [<<Const5>>]
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Address1:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const2>>]
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array>>,<<Address1>>]
  /// CHECK-DAG:             <<Add:d\d+>>           VecAdd [<<Load>>,<<Repl>>]
  /// CHECK-NOT:                                    IntermediateAddress
  /// CHECK-DAG:                                    VecStore [<<Array>>,<<Address1>>,<<Add>>]

  /// CHECK-START-ARM64: void Main.checkIntCase(int[]) disassembly (after)
  /// CHECK:                                        IntermediateAddressIndex
  /// CHECK-NEXT:                                   add w{{[0-9]+}}, w{{[0-9]+}}, w{{[0-9]+}}, lsl #2
  public static void checkIntCase(int[] a) {
    for (int i = 0; i < 128; i++) {
      a[i] += 5;
    }
  }

  /// CHECK-START-ARM64: void Main.checkByteCase(byte[]) instruction_simplifier_arm64 (before)
  /// CHECK-DAG:             <<Array:l\d+>>         ParameterValue
  /// CHECK-DAG:             <<Const5:i\d+>>        IntConstant 5
  /// CHECK-DAG:             <<Repl:d\d+>>          VecReplicateScalar [<<Const5>>]
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array>>,<<Index>>]
  /// CHECK-DAG:             <<Add:d\d+>>           VecAdd [<<Load>>,<<Repl>>]
  /// CHECK-DAG:                                    VecStore [<<Array>>,<<Index>>,<<Add>>]

  /// CHECK-START-ARM64: void Main.checkByteCase(byte[]) instruction_simplifier_arm64 (after)
  /// CHECK-DAG:             <<Array:l\d+>>         ParameterValue
  /// CHECK-DAG:             <<Const0:i\d+>>        IntConstant 0
  /// CHECK-DAG:             <<Const5:i\d+>>        IntConstant 5
  /// CHECK-DAG:             <<DataOffset:i\d+>>    IntConstant 12
  /// CHECK-DAG:             <<Repl:d\d+>>          VecReplicateScalar [<<Const5>>]
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Address1:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const0>>]
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array>>,<<Address1>>]
  /// CHECK-DAG:             <<Add:d\d+>>           VecAdd [<<Load>>,<<Repl>>]
  /// CHECK-DAG:             <<Address2:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const0>>]
  /// CHECK-DAG:                                    VecStore [<<Array>>,<<Address2>>,<<Add>>]

  /// CHECK-START-ARM64: void Main.checkByteCase(byte[]) GVN$after_arch (after)
  /// CHECK-DAG:             <<Array:l\d+>>         ParameterValue
  /// CHECK-DAG:             <<Const0:i\d+>>        IntConstant 0
  /// CHECK-DAG:             <<Const5:i\d+>>        IntConstant 5
  /// CHECK-DAG:             <<DataOffset:i\d+>>    IntConstant 12
  /// CHECK-DAG:             <<Repl:d\d+>>          VecReplicateScalar [<<Const5>>]
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Address1:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const0>>]
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array>>,<<Address1>>]
  /// CHECK-DAG:             <<Add:d\d+>>           VecAdd [<<Load>>,<<Repl>>]
  /// CHECK-NOT:                                    IntermediateAddress
  /// CHECK-DAG:                                    VecStore [<<Array>>,<<Address1>>,<<Add>>]

  /// CHECK-START-ARM64: void Main.checkByteCase(byte[]) disassembly (after)
  /// CHECK:                                        IntermediateAddressIndex
  /// CHECK-NEXT:                                   add w{{[0-9]+}}, w{{[0-9]+}}, #0x{{[0-9a-fA-F]+}}
  /// CHECK:                                        VecLoad
  /// CHECK-NEXT:                                   ldr q{{[0-9]+}}, [x{{[0-9]+}}, x{{[0-9]+}}]
  /// CHECK:                                        VecStore
  /// CHECK-NEXT:                                   str q{{[0-9]+}}, [x{{[0-9]+}}, x{{[0-9]+}}]
  public static void checkByteCase(byte[] a) {
    for (int i = 0; i < 128; i++) {
      a[i] += 5;
    }
  }

  /// CHECK-START-ARM64: void Main.checkSingleAccess(int[]) instruction_simplifier_arm64 (before)
  /// CHECK-DAG:             <<Array:l\d+>>         ParameterValue
  /// CHECK-DAG:             <<Const5:i\d+>>        IntConstant 5
  /// CHECK-DAG:             <<Repl:d\d+>>          VecReplicateScalar [<<Const5>>]
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:                                    VecStore [<<Array>>,<<Index>>,<<Repl>>]

  /// CHECK-START-ARM64: void Main.checkSingleAccess(int[]) instruction_simplifier_arm64 (after)
  /// CHECK-DAG:             <<Array:l\d+>>         ParameterValue
  /// CHECK-DAG:             <<Const0:i\d+>>        IntConstant 0
  /// CHECK-DAG:             <<Const5:i\d+>>        IntConstant 5
  /// CHECK-DAG:             <<Repl:d\d+>>          VecReplicateScalar [<<Const5>>]
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:                                    VecStore [<<Array>>,<<Index>>,<<Repl>>]
  /// CHECK-NOT:                                    IntermediateAddress
  public static void checkSingleAccess(int[] a) {
    for (int i = 0; i < 128; i++) {
      a[i] = 5;
    }
  }

  /// CHECK-START-ARM64: void Main.checkInt2Float(int[], float[]) instruction_simplifier_arm64 (before)
  /// CHECK-DAG:             <<Array1:l\d+>>        ParameterValue
  /// CHECK-DAG:             <<Array2:l\d+>>        ParameterValue
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array1>>,<<Index>>]
  /// CHECK-DAG:             <<Cnv:d\d+>>           VecCnv [<<Load>>]
  /// CHECK-DAG:                                    VecStore [<<Array2>>,<<Index>>,<<Cnv>>]

  /// CHECK-START-ARM64: void Main.checkInt2Float(int[], float[]) instruction_simplifier_arm64 (after)
  /// CHECK-DAG:             <<Array1:l\d+>>        ParameterValue
  /// CHECK-DAG:             <<Array2:l\d+>>        ParameterValue
  /// CHECK-DAG:             <<DataOffset:i\d+>>    IntConstant 12
  /// CHECK-DAG:             <<Const2:i\d+>>        IntConstant 2
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Address1:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const2>>]
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array1>>,<<Address1>>]
  /// CHECK-DAG:             <<Cnv:d\d+>>           VecCnv [<<Load>>]
  /// CHECK-DAG:             <<Address2:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const2>>]
  /// CHECK-DAG:                                    VecStore [<<Array2>>,<<Address2>>,<<Cnv>>]

  /// CHECK-START-ARM64: void Main.checkInt2Float(int[], float[]) GVN$after_arch (after)
  /// CHECK-DAG:             <<Array1:l\d+>>        ParameterValue
  /// CHECK-DAG:             <<Array2:l\d+>>        ParameterValue
  /// CHECK-DAG:             <<DataOffset:i\d+>>    IntConstant 12
  /// CHECK-DAG:             <<Const2:i\d+>>        IntConstant 2
  //  -------------- Loop
  /// CHECK-DAG:             <<Index:i\d+>>         Phi
  /// CHECK-DAG:                                    If
  /// CHECK-DAG:             <<Address1:i\d+>>      IntermediateAddressIndex [<<Index>>,<<DataOffset>>,<<Const2>>]
  /// CHECK-DAG:             <<Load:d\d+>>          VecLoad [<<Array1>>,<<Address1>>]
  /// CHECK-DAG:             <<Cnv:d\d+>>           VecCnv [<<Load>>]
  /// CHECK-NOT:                                    IntermediateAddress
  /// CHECK-DAG:                                    VecStore [<<Array2>>,<<Address1>>,<<Cnv>>]

  /// CHECK-START-ARM64: void Main.checkInt2Float(int[], float[]) disassembly (after)
  /// CHECK:                                        IntermediateAddressIndex
  /// CHECK-NEXT:                                   add w{{[0-9]+}}, w{{[0-9]+}}, w{{[0-9]+}}, lsl #2
  public static void checkInt2Float(int[] a, float[] b) {
    for (int i = 0; i < 128; i++) {
      b[i] = (float) a[i];
    }
  }

  public static final int ARRAY_SIZE = 1024;

  public static int calcArraySum(int[] a, byte[] b, float[] c) {
    int sum = 0;
    for (int i = 0; i < 128; i++) {
      sum += a[i] + b[i] + (int) c[i];
    }
    return sum;
  }

  public static void main(String[] args) {
    byte[] ba = new byte[ARRAY_SIZE];
    int[] ia = new int[ARRAY_SIZE];
    float[] fa = new float[ARRAY_SIZE];

    checkSingleAccess(ia);
    checkIntCase(ia);
    checkByteCase(ba);
    checkInt2Float(ia, fa);

    assertIntEquals(3200, calcArraySum(ia, ba, fa));
  }
}