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);
+    }
+  }
+}