MIPS32R2: Share address computation

For array accesses the element address has the following structure:
Address = CONST_OFFSET + base_addr + index << ELEM_SHIFT

The address part (index << ELEM_SHIFT) can be shared across array
accesses with the same data type and index.

For example, in 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: Id09fa782934aad4ee47669275e7e1a4d7d23b0fa
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 70c8a5b..3c592e7 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2665,6 +2665,9 @@
           __ ShiftAndAdd(TMP, index_reg, obj, TIMES_2, TMP);
           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
           __ Bind(&done);
+        } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+          __ Addu(TMP, index_reg, obj);
+          __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
         } else {
           __ ShiftAndAdd(TMP, index_reg, obj, TIMES_2, TMP);
           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
@@ -2679,6 +2682,9 @@
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
         __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset, null_checker);
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(TMP, index.AsRegister<Register>(), obj);
+        __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
       } else {
         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_2, TMP);
         __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
@@ -2693,6 +2699,9 @@
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
         __ LoadFromOffset(kLoadWord, out, obj, offset, null_checker);
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(TMP, index.AsRegister<Register>(), obj);
+        __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
       } else {
         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
         __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
@@ -2766,6 +2775,9 @@
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
         __ LoadFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(TMP, index.AsRegister<Register>(), obj);
+        __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
       } else {
         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_8, TMP);
         __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
@@ -2779,6 +2791,9 @@
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
         __ LoadSFromOffset(out, obj, offset, null_checker);
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(TMP, index.AsRegister<Register>(), obj);
+        __ LoadSFromOffset(out, TMP, data_offset, null_checker);
       } else {
         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
         __ LoadSFromOffset(out, TMP, data_offset, null_checker);
@@ -2792,6 +2807,9 @@
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
         __ LoadDFromOffset(out, obj, offset, null_checker);
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(TMP, index.AsRegister<Register>(), obj);
+        __ LoadDFromOffset(out, TMP, data_offset, null_checker);
       } else {
         __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_8, TMP);
         __ LoadDFromOffset(out, TMP, data_offset, null_checker);
@@ -2906,6 +2924,8 @@
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2;
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(base_reg, index.AsRegister<Register>(), obj);
       } else {
         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_2, base_reg);
       }
@@ -2923,6 +2943,8 @@
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(base_reg, index.AsRegister<Register>(), obj);
       } else {
         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
       }
@@ -2972,6 +2994,8 @@
           uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
           if (index.IsConstant()) {
             data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
+          } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+            __ Addu(base_reg, index.AsRegister<Register>(), obj);
           } else {
             __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
           }
@@ -3055,6 +3079,8 @@
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(base_reg, index.AsRegister<Register>(), obj);
       } else {
         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_8, base_reg);
       }
@@ -3072,6 +3098,8 @@
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(base_reg, index.AsRegister<Register>(), obj);
       } else {
         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
       }
@@ -3089,6 +3117,8 @@
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
+      } else if (instruction->InputAt(1)->IsIntermediateArrayAddressIndex()) {
+        __ Addu(base_reg, index.AsRegister<Register>(), obj);
       } else {
         __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_8, base_reg);
       }
@@ -3108,6 +3138,26 @@
   }
 }
 
+void LocationsBuilderMIPS::VisitIntermediateArrayAddressIndex(
+    HIntermediateArrayAddressIndex* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+
+  HIntConstant* shift = instruction->GetShift()->AsIntConstant();
+
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::ConstantLocation(shift));
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorMIPS::VisitIntermediateArrayAddressIndex(
+    HIntermediateArrayAddressIndex* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register index_reg = locations->InAt(0).AsRegister<Register>();
+  uint32_t shift = instruction->GetShift()->AsIntConstant()->GetValue();
+  __ Sll(locations->Out().AsRegister<Register>(), index_reg, shift);
+}
+
 void LocationsBuilderMIPS::VisitBoundsCheck(HBoundsCheck* instruction) {
   RegisterSet caller_saves = RegisterSet::Empty();
   InvokeRuntimeCallingConvention calling_convention;