MIPS32: Record missing implicit null checks
ImplicitNullChecks are recorded for HandleFieldGet and HandleFieldSet
instructions for longs after accessing first word.
This fixes following CTS tests:
* dot.junit.opcodes.iget_wide.JUnit_Test_iget_wide#testE2
* dot.junit.opcodes.iput_wide.JUnit_Test_iput_wide#testE2
Change-Id: I0a93370d4a78d36805a8d5a6c304ca0329da2363
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index b42c053..3711c43 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2637,6 +2637,7 @@
Register obj = locations->InAt(0).AsRegister<Register>();
LoadOperandType load_type = kLoadUnsignedByte;
bool is_volatile = field_info.IsVolatile();
+ uint32_t offset = field_info.GetFieldOffset().Uint32Value();
switch (type) {
case Primitive::kPrimBoolean:
@@ -2667,8 +2668,7 @@
if (is_volatile && load_type == kLoadDoubleword) {
InvokeRuntimeCallingConvention calling_convention;
- __ Addiu32(locations->GetTemp(0).AsRegister<Register>(),
- obj, field_info.GetFieldOffset().Uint32Value());
+ __ Addiu32(locations->GetTemp(0).AsRegister<Register>(), obj, offset);
// Do implicit Null check
__ Lw(ZERO, locations->GetTemp(0).AsRegister<Register>(), 0);
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2691,21 +2691,34 @@
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, field_info.GetFieldOffset().Uint32Value());
} else {
DCHECK(locations->Out().IsFpuRegister());
FRegister dst = locations->Out().AsFpuRegister<FRegister>();
if (type == Primitive::kPrimFloat) {
- __ LoadSFromOffset(dst, obj, field_info.GetFieldOffset().Uint32Value());
+ __ LoadSFromOffset(dst, obj, offset);
} else {
- __ LoadDFromOffset(dst, obj, field_info.GetFieldOffset().Uint32Value());
+ __ LoadDFromOffset(dst, obj, offset);
}
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // Longs are handled earlier.
+ if (type != Primitive::kPrimLong) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
}
if (is_volatile) {
@@ -2751,6 +2764,7 @@
Register obj = locations->InAt(0).AsRegister<Register>();
StoreOperandType store_type = kStoreByte;
bool is_volatile = field_info.IsVolatile();
+ uint32_t offset = field_info.GetFieldOffset().Uint32Value();
switch (type) {
case Primitive::kPrimBoolean:
@@ -2781,8 +2795,7 @@
if (is_volatile && store_type == kStoreDoubleword) {
InvokeRuntimeCallingConvention calling_convention;
- __ Addiu32(locations->GetTemp(0).AsRegister<Register>(),
- obj, field_info.GetFieldOffset().Uint32Value());
+ __ Addiu32(locations->GetTemp(0).AsRegister<Register>(), obj, offset);
// Do implicit Null check.
__ Lw(ZERO, locations->GetTemp(0).AsRegister<Register>(), 0);
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2805,21 +2818,28 @@
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, field_info.GetFieldOffset().Uint32Value());
} else {
DCHECK(locations->InAt(1).IsFpuRegister());
FRegister src = locations->InAt(1).AsFpuRegister<FRegister>();
if (type == Primitive::kPrimFloat) {
- __ StoreSToOffset(src, obj, field_info.GetFieldOffset().Uint32Value());
+ __ StoreSToOffset(src, obj, offset);
} else {
- __ StoreDToOffset(src, obj, field_info.GetFieldOffset().Uint32Value());
+ __ StoreDToOffset(src, obj, offset);
}
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // Longs are handled earlier.
+ if (type != Primitive::kPrimLong) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
}
// TODO: memory barriers?
diff --git a/test/551-implicit-null-checks/expected.txt b/test/551-implicit-null-checks/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/551-implicit-null-checks/expected.txt
diff --git a/test/551-implicit-null-checks/info.txt b/test/551-implicit-null-checks/info.txt
new file mode 100644
index 0000000..bdd066b
--- /dev/null
+++ b/test/551-implicit-null-checks/info.txt
@@ -0,0 +1 @@
+Test that implicit null checks are recorded correctly for longs.
\ No newline at end of file
diff --git a/test/551-implicit-null-checks/src/Main.java b/test/551-implicit-null-checks/src/Main.java
new file mode 100644
index 0000000..677e8d3
--- /dev/null
+++ b/test/551-implicit-null-checks/src/Main.java
@@ -0,0 +1,47 @@
+/*
+ * 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 {
+
+ private class Inner {
+ private long i1;
+ }
+ private Inner inst;
+
+ public static void main(String args[]) throws Exception {
+ Main m = new Main();
+ try {
+ m.$opt$noinline$testGetLong();
+ } catch (NullPointerException ex) {
+ // good
+ }
+ try {
+ m.$opt$noinline$testPutLong(778899112233L);
+ } catch (NullPointerException ex) {
+ // good
+ }
+ }
+
+ public void $opt$noinline$testGetLong() throws Exception {
+ long result = inst.i1;
+ throw new Exception(); // prevent inline
+ }
+
+ public void $opt$noinline$testPutLong(long a) throws Exception {
+ inst.i1 = a;
+ throw new Exception(); // prevent inline
+ }
+}