ARM64: Improve BoundsCheck for constant inputs.
This is a port of 2dd053d to ARM64.
Original author: Georgia Kouveli <georgia.kouveli@linaro.org>
Committed by: David Horstmann <david.horstmann@linaro.org>
Test: test-art-target, test-art-host
Test: 1960-checker-bounds-codegen
Test: 449-checker-bce
Change-Id: I6564e4d147a0f40665b37c604487159a9d9aeae5
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 492fe4d..a299ece 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2718,16 +2718,59 @@
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1).GetCode()));
LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
+
+ // If both index and length are constant, we can check the bounds statically and
+ // generate code accordingly. We want to make sure we generate constant locations
+ // in that case, regardless of whether they are encodable in the comparison or not.
+ HInstruction* index = instruction->InputAt(0);
+ HInstruction* length = instruction->InputAt(1);
+ bool both_const = index->IsConstant() && length->IsConstant();
+ locations->SetInAt(0, both_const
+ ? Location::ConstantLocation(index->AsConstant())
+ : ARM64EncodableConstantOrRegister(index, instruction));
+ locations->SetInAt(1, both_const
+ ? Location::ConstantLocation(length->AsConstant())
+ : ARM64EncodableConstantOrRegister(length, instruction));
}
void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location index_loc = locations->InAt(0);
+ Location length_loc = locations->InAt(1);
+
+ int cmp_first_input = 0;
+ int cmp_second_input = 1;
+ Condition cond = hs;
+
+ if (index_loc.IsConstant()) {
+ int64_t index = Int64FromLocation(index_loc);
+ if (length_loc.IsConstant()) {
+ int64_t length = Int64FromLocation(length_loc);
+ if (index < 0 || index >= length) {
+ BoundsCheckSlowPathARM64* slow_path =
+ new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARM64(instruction);
+ codegen_->AddSlowPath(slow_path);
+ __ B(slow_path->GetEntryLabel());
+ } else {
+ // BCE will remove the bounds check if we are guaranteed to pass.
+ // However, some optimization after BCE may have generated this, and we should not
+ // generate a bounds check if it is a valid range.
+ }
+ return;
+ }
+ // Only the index is constant: change the order of the operands and commute the condition
+ // so we can use an immediate constant for the index (only the second input to a cmp
+ // instruction can be an immediate).
+ cmp_first_input = 1;
+ cmp_second_input = 0;
+ cond = ls;
+ }
BoundsCheckSlowPathARM64* slow_path =
new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARM64(instruction);
+ __ Cmp(InputRegisterAt(instruction, cmp_first_input),
+ InputOperandAt(instruction, cmp_second_input));
codegen_->AddSlowPath(slow_path);
- __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
- __ B(slow_path->GetEntryLabel(), hs);
+ __ B(slow_path->GetEntryLabel(), cond);
}
void LocationsBuilderARM64::VisitClinitCheck(HClinitCheck* check) {
diff --git a/test/1960-checker-bounds-codegen/expected.txt b/test/1960-checker-bounds-codegen/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/1960-checker-bounds-codegen/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/1960-checker-bounds-codegen/info.txt b/test/1960-checker-bounds-codegen/info.txt
new file mode 100644
index 0000000..1a4f84f
--- /dev/null
+++ b/test/1960-checker-bounds-codegen/info.txt
@@ -0,0 +1 @@
+Test code generation for BoundsCheck.
diff --git a/test/1960-checker-bounds-codegen/src/Main.java b/test/1960-checker-bounds-codegen/src/Main.java
new file mode 100644
index 0000000..a84d67f
--- /dev/null
+++ b/test/1960-checker-bounds-codegen/src/Main.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/**
+ * Test code generation for BoundsCheck.
+ */
+public class Main {
+ // Constant index, variable length.
+ /// CHECK-START-ARM64: int Main.constantIndex(int[]) disassembly (after)
+ /// CHECK: BoundsCheck
+ /// CHECK: cmp {{w\d+}}, #0x0
+ /// CHECK: b.ls #+0x{{[0-9a-f]+}} (addr 0x<<SLOW:[0-9a-f]+>>)
+ /// CHECK: BoundsCheckSlowPathARM64
+ /// CHECK-NEXT: 0x{{0*}}<<SLOW>>:
+ /// CHECK-START-ARM: int Main.constantIndex(int[]) disassembly (after)
+ /// CHECK: BoundsCheck
+ /// CHECK: cmp {{r\d+}}, #0
+ /// CHECK: bls.w <<SLOW:0x[0-9a-f]+>>
+ /// CHECK: BoundsCheckSlowPathARMVIXL
+ /// CHECK-NEXT: <<SLOW>>:
+ public static int constantIndex(int[] a) {
+ try {
+ a[0] = 42;
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ return -1;
+ }
+ return a.length;
+ }
+
+ // Constant length, variable index.
+ /// CHECK-START-ARM64: int Main.constantLength(int) disassembly (after)
+ /// CHECK: BoundsCheck
+ /// CHECK: cmp {{w\d+}}, #0xa
+ /// CHECK: b.hs #+0x{{[0-9a-f]+}} (addr 0x<<SLOW:[0-9a-f]+>>)
+ /// CHECK: BoundsCheckSlowPathARM64
+ /// CHECK-NEXT: 0x{{0*}}<<SLOW>>:
+ /// CHECK-START-ARM: int Main.constantLength(int) disassembly (after)
+ /// CHECK: BoundsCheck
+ /// CHECK: cmp {{r\d+}}, #10
+ /// CHECK: bcs.w <<SLOW:0x[0-9a-f]+>>
+ /// CHECK: BoundsCheckSlowPathARMVIXL
+ /// CHECK-NEXT: <<SLOW>>:
+ public static int constantLength(int index) {
+ int[] a = new int[10];
+ try {
+ a[index] = 1;
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ return -1;
+ }
+ return index;
+ }
+
+ // Constant index and length, out of bounds access. Check that we only have
+ // the slow path.
+ /// CHECK-START-ARM64: int Main.constantIndexAndLength() disassembly (after)
+ /// CHECK: BoundsCheck
+ /// CHECK-NOT: cmp
+ /// CHECK: b #+0x{{[0-9a-f]+}} (addr 0x<<SLOW:[0-9a-f]+>>)
+ /// CHECK: BoundsCheckSlowPathARM64
+ /// CHECK-NEXT: 0x{{0*}}<<SLOW>>:
+ /// CHECK-START-ARM: int Main.constantIndexAndLength() disassembly (after)
+ /// CHECK: BoundsCheck
+ /// CHECK-NOT: cmp
+ /// CHECK: b <<SLOW:0x[0-9a-f]+>>
+ /// CHECK: BoundsCheckSlowPathARMVIXL
+ /// CHECK-NEXT: <<SLOW>>:
+ public static int constantIndexAndLength() {
+ try {
+ int[] a = new int[5];
+ a[10] = 42;
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ return -1;
+ }
+ return 0;
+ }
+
+ public static void main(String[] args) {
+ int[] a = new int[10];
+ int[] b = new int[0];
+ expectEquals(a.length, constantIndex(a));
+ expectEquals(-1, constantIndex(b));
+ expectEquals(0, constantLength(0));
+ expectEquals(9, constantLength(9));
+ expectEquals(-1, constantLength(10));
+ expectEquals(-1, constantLength(-2));
+ expectEquals(-1, constantIndexAndLength());
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}