ARM64: Share address computation across SIMD LDRs/STRs.
For array accesses the element address has the following structure:
Address = CONST_OFFSET + base_addr + index << ELEM_SHIFT
Taking into account ARM64 LDR/STR addressing modes address part
(CONST_OFFSET + index << ELEM_SHIFT) can be shared across array
access with the same data type and index.
For example, for the following loop 5 accesses can share address
computation:
void foo(int[] a, int[] b, int[] c) {
for (i...) {
a[i] = a[i] + 5;
b[i] = b[i] + c[i];
}
}
Test: test-art-host, test-art-target
Change-Id: I46af3b4e4a55004336672cdba3296b7622d815ca
diff --git a/test/527-checker-array-access-simd/src/Main.java b/test/527-checker-array-access-simd/src/Main.java
new file mode 100644
index 0000000..8af5465
--- /dev/null
+++ b/test/527-checker-array-access-simd/src/Main.java
@@ -0,0 +1,223 @@
+/*
+ * 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));
+ }
+}