MIPS32: Refactor implicit null checks in array/field get/set.

Rationale: on MIPS32 64-bit loads and stores may be performed
as pairs of 32-bit loads/stores. Implicit null checks must be
associated with the first 32-bit load/store in a pair and not
the last. This change ensures proper association of said checks
(a few were done after the last 32-bit load/store in a pair)
and lays ground for further improvements in array/field get/set.

Test: booted MIPS32 in QEMU
Test: test-art-host-gtest
Test: test-art-target-run-test-optimizing in QEMU

Change-Id: I3674947c00bb17930790a7a47c9b7aadc0c030b8
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index a7fbc84..8a2f90d 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1833,11 +1833,19 @@
   }
 }
 
+auto InstructionCodeGeneratorMIPS::GetImplicitNullChecker(HInstruction* instruction) {
+  auto null_checker = [this, instruction]() {
+    this->codegen_->MaybeRecordImplicitNullCheck(instruction);
+  };
+  return null_checker;
+}
+
 void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsRegister<Register>();
   Location index = locations->InAt(1);
   uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
+  auto null_checker = GetImplicitNullChecker(instruction);
 
   Primitive::Type type = instruction->GetType();
   switch (type) {
@@ -1846,10 +1854,10 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
-        __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
+        __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset, null_checker);
       } else {
         __ Addu(TMP, obj, index.AsRegister<Register>());
-        __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
+        __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -1859,10 +1867,10 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
-        __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
+        __ LoadFromOffset(kLoadSignedByte, out, obj, offset, null_checker);
       } else {
         __ Addu(TMP, obj, index.AsRegister<Register>());
-        __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset);
+        __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -1872,11 +1880,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
-        __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
+        __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_2);
         __ Addu(TMP, obj, TMP);
-        __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset);
+        __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -1886,11 +1894,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
-        __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
+        __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_2);
         __ Addu(TMP, obj, TMP);
-        __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+        __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -1902,11 +1910,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-        __ LoadFromOffset(kLoadWord, out, obj, offset);
+        __ LoadFromOffset(kLoadWord, out, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_4);
         __ Addu(TMP, obj, TMP);
-        __ LoadFromOffset(kLoadWord, out, TMP, data_offset);
+        __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -1916,11 +1924,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ LoadFromOffset(kLoadDoubleword, out, obj, offset);
+        __ LoadFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_8);
         __ Addu(TMP, obj, TMP);
-        __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset);
+        __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -1930,11 +1938,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-        __ LoadSFromOffset(out, obj, offset);
+        __ LoadSFromOffset(out, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_4);
         __ Addu(TMP, obj, TMP);
-        __ LoadSFromOffset(out, TMP, data_offset);
+        __ LoadSFromOffset(out, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -1944,11 +1952,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ LoadDFromOffset(out, obj, offset);
+        __ LoadDFromOffset(out, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_8);
         __ Addu(TMP, obj, TMP);
-        __ LoadDFromOffset(out, TMP, data_offset);
+        __ LoadDFromOffset(out, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -1957,7 +1965,6 @@
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
       UNREACHABLE();
   }
-  codegen_->MaybeRecordImplicitNullCheck(instruction);
 }
 
 void LocationsBuilderMIPS::VisitArrayLength(HArrayLength* instruction) {
@@ -2004,6 +2011,7 @@
   bool needs_runtime_call = locations->WillCall();
   bool needs_write_barrier =
       CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
+  auto null_checker = GetImplicitNullChecker(instruction);
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
@@ -2013,10 +2021,10 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
-        __ StoreToOffset(kStoreByte, value, obj, offset);
+        __ StoreToOffset(kStoreByte, value, obj, offset, null_checker);
       } else {
         __ Addu(TMP, obj, index.AsRegister<Register>());
-        __ StoreToOffset(kStoreByte, value, TMP, data_offset);
+        __ StoreToOffset(kStoreByte, value, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -2028,11 +2036,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
-        __ StoreToOffset(kStoreHalfword, value, obj, offset);
+        __ StoreToOffset(kStoreHalfword, value, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_2);
         __ Addu(TMP, obj, TMP);
-        __ StoreToOffset(kStoreHalfword, value, TMP, data_offset);
+        __ StoreToOffset(kStoreHalfword, value, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -2045,14 +2053,13 @@
         if (index.IsConstant()) {
           size_t offset =
               (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-          __ StoreToOffset(kStoreWord, value, obj, offset);
+          __ StoreToOffset(kStoreWord, value, obj, offset, null_checker);
         } else {
           DCHECK(index.IsRegister()) << index;
           __ Sll(TMP, index.AsRegister<Register>(), TIMES_4);
           __ Addu(TMP, obj, TMP);
-          __ StoreToOffset(kStoreWord, value, TMP, data_offset);
+          __ StoreToOffset(kStoreWord, value, TMP, data_offset, null_checker);
         }
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
         if (needs_write_barrier) {
           DCHECK_EQ(value_type, Primitive::kPrimNot);
           codegen_->MarkGCCard(obj, value);
@@ -2075,11 +2082,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ StoreToOffset(kStoreDoubleword, value, obj, offset);
+        __ StoreToOffset(kStoreDoubleword, value, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_8);
         __ Addu(TMP, obj, TMP);
-        __ StoreToOffset(kStoreDoubleword, value, TMP, data_offset);
+        __ StoreToOffset(kStoreDoubleword, value, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -2091,11 +2098,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
-        __ StoreSToOffset(value, obj, offset);
+        __ StoreSToOffset(value, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_4);
         __ Addu(TMP, obj, TMP);
-        __ StoreSToOffset(value, TMP, data_offset);
+        __ StoreSToOffset(value, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -2107,11 +2114,11 @@
       if (index.IsConstant()) {
         size_t offset =
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
-        __ StoreDToOffset(value, obj, offset);
+        __ StoreDToOffset(value, obj, offset, null_checker);
       } else {
         __ Sll(TMP, index.AsRegister<Register>(), TIMES_8);
         __ Addu(TMP, obj, TMP);
-        __ StoreDToOffset(value, TMP, data_offset);
+        __ StoreDToOffset(value, TMP, data_offset, null_checker);
       }
       break;
     }
@@ -2120,11 +2127,6 @@
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
       UNREACHABLE();
   }
-
-  // Ints and objects are handled in the switch.
-  if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) {
-    codegen_->MaybeRecordImplicitNullCheck(instruction);
-  }
 }
 
 void LocationsBuilderMIPS::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -3589,6 +3591,7 @@
   LoadOperandType load_type = kLoadUnsignedByte;
   bool is_volatile = field_info.IsVolatile();
   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+  auto null_checker = GetImplicitNullChecker(instruction);
 
   switch (type) {
     case Primitive::kPrimBoolean:
@@ -3654,34 +3657,20 @@
       if (type == Primitive::kPrimLong) {
         DCHECK(locations->Out().IsRegisterPair());
         dst = locations->Out().AsRegisterPairLow<Register>();
-        Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
-        if (obj == dst) {
-          __ LoadFromOffset(kLoadWord, dst_high, obj, offset + kMipsWordSize);
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
-          __ LoadFromOffset(kLoadWord, dst, obj, offset);
-        } else {
-          __ LoadFromOffset(kLoadWord, dst, obj, offset);
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
-          __ LoadFromOffset(kLoadWord, dst_high, obj, offset + kMipsWordSize);
-        }
       } else {
         DCHECK(locations->Out().IsRegister());
         dst = locations->Out().AsRegister<Register>();
-        __ LoadFromOffset(load_type, dst, obj, offset);
       }
+      __ LoadFromOffset(load_type, dst, obj, offset, null_checker);
     } else {
       DCHECK(locations->Out().IsFpuRegister());
       FRegister dst = locations->Out().AsFpuRegister<FRegister>();
       if (type == Primitive::kPrimFloat) {
-        __ LoadSFromOffset(dst, obj, offset);
+        __ LoadSFromOffset(dst, obj, offset, null_checker);
       } else {
-        __ LoadDFromOffset(dst, obj, offset);
+        __ LoadDFromOffset(dst, obj, offset, null_checker);
       }
     }
-    // Longs are handled earlier.
-    if (type != Primitive::kPrimLong) {
-      codegen_->MaybeRecordImplicitNullCheck(instruction);
-    }
   }
 
   if (is_volatile) {
@@ -3729,6 +3718,7 @@
   StoreOperandType store_type = kStoreByte;
   bool is_volatile = field_info.IsVolatile();
   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+  auto null_checker = GetImplicitNullChecker(instruction);
 
   switch (type) {
     case Primitive::kPrimBoolean:
@@ -3800,28 +3790,20 @@
       if (type == Primitive::kPrimLong) {
         DCHECK(locations->InAt(1).IsRegisterPair());
         src = locations->InAt(1).AsRegisterPairLow<Register>();
-        Register src_high = locations->InAt(1).AsRegisterPairHigh<Register>();
-        __ StoreToOffset(kStoreWord, src, obj, offset);
-        codegen_->MaybeRecordImplicitNullCheck(instruction);
-        __ StoreToOffset(kStoreWord, src_high, obj, offset + kMipsWordSize);
       } else {
         DCHECK(locations->InAt(1).IsRegister());
         src = locations->InAt(1).AsRegister<Register>();
-        __ StoreToOffset(store_type, src, obj, offset);
       }
+      __ StoreToOffset(store_type, src, obj, offset, null_checker);
     } else {
       DCHECK(locations->InAt(1).IsFpuRegister());
       FRegister src = locations->InAt(1).AsFpuRegister<FRegister>();
       if (type == Primitive::kPrimFloat) {
-        __ StoreSToOffset(src, obj, offset);
+        __ StoreSToOffset(src, obj, offset, null_checker);
       } else {
-        __ StoreDToOffset(src, obj, offset);
+        __ StoreDToOffset(src, obj, offset, null_checker);
       }
     }
-    // Longs are handled earlier.
-    if (type != Primitive::kPrimLong) {
-      codegen_->MaybeRecordImplicitNullCheck(instruction);
-    }
   }
 
   // TODO: memory barriers?