From e6dbf48d7a549e58a3d798bbbdc391e4d091b432 Mon Sep 17 00:00:00 2001 From: Alexandre Rames Date: Mon, 19 Oct 2015 10:10:41 +0100 Subject: ARM64: Instruction simplification for array accesses. HArrayGet and HArraySet with variable indexes generate two instructions on arm64, like add temp, obj, #data_offset ldr out, [temp, index LSL #shift_amount] When we have multiple accesses to the same array, the initial `add` instruction is redundant. This patch introduces the first instruction simplification in the arm64-specific instruction simplification pass. It splits HArrayGet and HArraySet using the new arm64-specific IR HIntermediateAddress. After that we run GVN again to squash the multiple occurrences of HIntermediateAddress. Change-Id: I2e3d12fbb07fed07b2cb2f3f47f99f5a032f8312 --- test/527-checker-array-access-split/src/Main.java | 341 ++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 test/527-checker-array-access-split/src/Main.java (limited to 'test/527-checker-array-access-split/src') diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java new file mode 100644 index 0000000000..ead94464bf --- /dev/null +++ b/test/527-checker-array-access-split/src/Main.java @@ -0,0 +1,341 @@ +/* + * 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 { + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + /** + * Test that HArrayGet with a constant index is not split. + */ + + /// CHECK-START-ARM64: int Main.constantIndexGet(int[]) instruction_simplifier_arm64 (before) + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: ArrayGet [<>,<>] + + /// CHECK-START-ARM64: int Main.constantIndexGet(int[]) instruction_simplifier_arm64 (after) + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK: ArrayGet [<>,<>] + + public static int constantIndexGet(int array[]) { + return array[1]; + } + + /** + * Test that HArraySet with a constant index is not split. + */ + + /// CHECK-START-ARM64: void Main.constantIndexSet(int[]) instruction_simplifier_arm64 (before) + /// CHECK: <> IntConstant 2 + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: ArraySet [<>,<>,<>] + + /// CHECK-START-ARM64: void Main.constantIndexSet(int[]) instruction_simplifier_arm64 (after) + /// CHECK: <> IntConstant 2 + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK: ArraySet [<>,<>,<>] + + + public static void constantIndexSet(int array[]) { + array[1] = 2; + } + + /** + * Test basic splitting of HArrayGet. + */ + + /// CHECK-START-ARM64: int Main.get(int[], int) instruction_simplifier_arm64 (before) + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: ArrayGet [<>,<>] + + /// CHECK-START-ARM64: int Main.get(int[], int) instruction_simplifier_arm64 (after) + /// CHECK: <> IntConstant + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-NEXT: ArrayGet [<
>,<>] + + public static int get(int array[], int index) { + return array[index]; + } + + /** + * Test basic splitting of HArraySet. + */ + + /// CHECK-START-ARM64: void Main.set(int[], int, int) instruction_simplifier_arm64 (before) + /// CHECK: ParameterValue + /// CHECK: ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: ArraySet [<>,<>,<>] + + /// CHECK-START-ARM64: void Main.set(int[], int, int) instruction_simplifier_arm64 (after) + /// CHECK: ParameterValue + /// CHECK: ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-NEXT: ArraySet [<
>,<>,<>] + + public static void set(int array[], int index, int value) { + array[index] = value; + } + + /** + * Check that the intermediate address can be shared after GVN. + */ + + /// CHECK-START-ARM64: void Main.getSet(int[], int) instruction_simplifier_arm64 (before) + /// CHECK: <> IntConstant 1 + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: ArraySet [<>,<>,<>] + + /// CHECK-START-ARM64: void Main.getSet(int[], int) instruction_simplifier_arm64 (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-NEXT: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-NEXT: ArraySet [<>,<>,<>] + + /// CHECK-START-ARM64: void Main.getSet(int[], int) GVN_after_arch (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK: <> ArrayGet [<
>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK: ArraySet [<
>,<>,<>] + + public static void getSet(int array[], int index) { + array[index] = array[index] + 1; + } + + /** + * Check that the intermediate address computation is not reordered or merged + * across IRs that can trigger GC. + */ + + /// CHECK-START-ARM64: int[] Main.accrossGC(int[], int) instruction_simplifier_arm64 (before) + /// CHECK: <> IntConstant 1 + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: NewArray + /// CHECK: ArraySet [<>,<>,<>] + + /// CHECK-START-ARM64: int[] Main.accrossGC(int[], int) instruction_simplifier_arm64 (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-NEXT: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: NewArray + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-NEXT: ArraySet [<>,<>,<>] + + /// CHECK-START-ARM64: int[] Main.accrossGC(int[], int) GVN_after_arch (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant + /// CHECK: <> NullCheck + /// CHECK: <> BoundsCheck + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: NewArray + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK: ArraySet [<>,<>,<>] + + public static int[] accrossGC(int array[], int index) { + int tmp = array[index] + 1; + int[] new_array = new int[1]; + array[index] = tmp; + return new_array; + } + + /** + * Test that the intermediate address is shared between array accesses after + * the bounds check have been removed by BCE. + */ + + /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() instruction_simplifier_arm64 (before) + /// CHECK: <> IntConstant 1 + /// CHECK: <> NewArray + /// CHECK: <> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: ArraySet [<>,<>,<>] + + // By the time we reach the architecture-specific instruction simplifier, BCE + // has removed the bounds checks in the loop. + + // Note that we do not care that the `DataOffset` is `12`. But if we do not + // specify it and any other `IntConstant` appears before that instruction, + // checker will match the previous `IntConstant`, and we will thus fail the + // check. + + /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() instruction_simplifier_arm64 (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 12 + /// CHECK: <> NewArray + /// CHECK: <> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-NEXT: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-NEXT: ArraySet [<>,<>,<>] + + /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() GVN_after_arch (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 12 + /// CHECK: <> NewArray + /// CHECK: <> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK: <> ArrayGet [<
>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK: ArraySet [<
>,<>,<>] + + public static int canMergeAfterBCE1() { + int[] array = {0, 1, 2, 3}; + for (int i = 0; i < array.length; i++) { + array[i] = array[i] + 1; + } + return array[array.length - 1]; + } + + /** + * This test case is similar to `canMergeAfterBCE1`, but with different + * indexes for the accesses. + */ + + /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() instruction_simplifier_arm64 (before) + /// CHECK: <> IntConstant 1 + /// CHECK: <> NewArray + /// CHECK: <> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: ArraySet [<>,<>,<>] + + // Note that we do not care that the `DataOffset` is `12`. But if we do not + // specify it and any other `IntConstant` appears before that instruction, + // checker will match the previous `IntConstant`, and we will thus fail the + // check. + + /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() instruction_simplifier_arm64 (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 12 + /// CHECK: <> NewArray + /// CHECK: <> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK-DAG: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-DAG: <> ArrayGet [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Arm64IntermediateAddress [<>,<>] + /// CHECK: ArraySet [<>,<>,<>] + + /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() GVN_after_arch (after) + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 12 + /// CHECK: <> NewArray + /// CHECK: <> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Arm64IntermediateAddress [<>,<>] + /// CHECK-DAG: <> ArrayGet [<
>,<>] + /// CHECK-DAG: <> ArrayGet [<
>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: ArraySet [<
>,<>,<>] + + // There should be only one intermediate address computation in the loop. + + /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() GVN_after_arch (after) + /// CHECK: Arm64IntermediateAddress + /// CHECK-NOT: Arm64IntermediateAddress + + public static int canMergeAfterBCE2() { + int[] array = {0, 1, 2, 3}; + for (int i = 0; i < array.length - 1; i++) { + array[i + 1] = array[i] + array[i + 1]; + } + return array[array.length - 1]; + } + + + public static void main(String[] args) { + int[] array = {123, 456, 789}; + + assertIntEquals(456, constantIndexGet(array)); + + constantIndexSet(array); + assertIntEquals(2, array[1]); + + assertIntEquals(789, get(array, 2)); + + set(array, 1, 456); + assertIntEquals(456, array[1]); + + getSet(array, 0); + assertIntEquals(124, array[0]); + + accrossGC(array, 0); + assertIntEquals(125, array[0]); + + assertIntEquals(4, canMergeAfterBCE1()); + assertIntEquals(6, canMergeAfterBCE2()); + } +} -- cgit v1.2.3-59-g8ed1b