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?
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 63a0345..46810d6 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -257,6 +257,7 @@
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
+ auto GetImplicitNullChecker(HInstruction* instruction);
MipsAssembler* const assembler_;
CodeGeneratorMIPS* const codegen_;
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 8b7da3f..bfc63d1 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -1407,44 +1407,6 @@
}
}
-void MipsAssembler::StoreConst32ToOffset(int32_t value,
- Register base,
- int32_t offset,
- Register temp) {
- CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
- AdjustBaseAndOffset(base, offset, /* is_doubleword */ false);
- if (value == 0) {
- temp = ZERO;
- } else {
- LoadConst32(temp, value);
- }
- Sw(temp, base, offset);
-}
-
-void MipsAssembler::StoreConst64ToOffset(int64_t value,
- Register base,
- int32_t offset,
- Register temp) {
- CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
- AdjustBaseAndOffset(base, offset, /* is_doubleword */ true);
- uint32_t low = Low32Bits(value);
- uint32_t high = High32Bits(value);
- if (low == 0) {
- Sw(ZERO, base, offset);
- } else {
- LoadConst32(temp, low);
- Sw(temp, base, offset);
- }
- if (high == 0) {
- Sw(ZERO, base, offset + kMipsWordSize);
- } else {
- if (high != low) {
- LoadConst32(temp, high);
- }
- Sw(temp, base, offset + kMipsWordSize);
- }
-}
-
void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
if (value == 0) {
temp = ZERO;
@@ -2533,61 +2495,19 @@
CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
}
-void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base,
+void MipsAssembler::LoadFromOffset(LoadOperandType type,
+ Register reg,
+ Register base,
int32_t offset) {
- AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kLoadDoubleword));
- switch (type) {
- case kLoadSignedByte:
- Lb(reg, base, offset);
- break;
- case kLoadUnsignedByte:
- Lbu(reg, base, offset);
- break;
- case kLoadSignedHalfword:
- Lh(reg, base, offset);
- break;
- case kLoadUnsignedHalfword:
- Lhu(reg, base, offset);
- break;
- case kLoadWord:
- Lw(reg, base, offset);
- break;
- case kLoadDoubleword:
- if (reg == base) {
- // This will clobber the base when loading the lower register. Since we have to load the
- // higher register as well, this will fail. Solution: reverse the order.
- Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
- Lw(reg, base, offset);
- } else {
- Lw(reg, base, offset);
- Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
- }
- break;
- default:
- LOG(FATAL) << "UNREACHABLE";
- }
+ LoadFromOffset<>(type, reg, base, offset);
}
void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
- AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
- Lwc1(reg, base, offset);
+ LoadSFromOffset<>(reg, base, offset);
}
void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
- AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
- if (offset & 0x7) {
- if (Is32BitFPU()) {
- Lwc1(reg, base, offset);
- Lwc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
- } else {
- // 64-bit FPU.
- Lwc1(reg, base, offset);
- Lw(T8, base, offset + kMipsWordSize);
- Mthc1(T8, reg);
- }
- } else {
- Ldc1(reg, base, offset);
- }
+ LoadDFromOffset<>(reg, base, offset);
}
void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
@@ -2611,53 +2531,19 @@
}
}
-void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register base,
+void MipsAssembler::StoreToOffset(StoreOperandType type,
+ Register reg,
+ Register base,
int32_t offset) {
- // Must not use AT as `reg`, so as not to overwrite the value being stored
- // with the adjusted `base`.
- CHECK_NE(reg, AT);
- AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kStoreDoubleword));
- switch (type) {
- case kStoreByte:
- Sb(reg, base, offset);
- break;
- case kStoreHalfword:
- Sh(reg, base, offset);
- break;
- case kStoreWord:
- Sw(reg, base, offset);
- break;
- case kStoreDoubleword:
- CHECK_NE(reg, base);
- CHECK_NE(static_cast<Register>(reg + 1), base);
- Sw(reg, base, offset);
- Sw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
- break;
- default:
- LOG(FATAL) << "UNREACHABLE";
- }
+ StoreToOffset<>(type, reg, base, offset);
}
void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
- AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
- Swc1(reg, base, offset);
+ StoreSToOffset<>(reg, base, offset);
}
void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
- AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
- if (offset & 0x7) {
- if (Is32BitFPU()) {
- Swc1(reg, base, offset);
- Swc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
- } else {
- // 64-bit FPU.
- Mfhc1(T8, reg);
- Swc1(reg, base, offset);
- Sw(T8, base, offset + kMipsWordSize);
- }
- } else {
- Sdc1(reg, base, offset);
- }
+ StoreDToOffset<>(reg, base, offset);
}
static dwarf::Reg DWARFReg(Register reg) {
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 41b6c6b..434ca67 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -412,8 +412,6 @@
void LoadConst64(Register reg_hi, Register reg_lo, int64_t value);
void LoadDConst64(FRegister rd, int64_t value, Register temp);
void LoadSConst32(FRegister r, int32_t value, Register temp);
- void StoreConst32ToOffset(int32_t value, Register base, int32_t offset, Register temp);
- void StoreConst64ToOffset(int64_t value, Register base, int32_t offset, Register temp);
void Addiu32(Register rt, Register rs, int32_t value, Register rtmp = AT);
// These will generate R2 branches or R6 branches as appropriate.
@@ -444,6 +442,204 @@
int32_t& offset,
bool is_doubleword,
bool is_float = false);
+
+ private:
+ struct NoImplicitNullChecker {
+ void operator()() {}
+ };
+
+ public:
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreConst32ToOffset(int32_t value,
+ Register base,
+ int32_t offset,
+ Register temp,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ false);
+ if (value == 0) {
+ temp = ZERO;
+ } else {
+ LoadConst32(temp, value);
+ }
+ Sw(temp, base, offset);
+ null_checker();
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreConst64ToOffset(int64_t value,
+ Register base,
+ int32_t offset,
+ Register temp,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ true);
+ uint32_t low = Low32Bits(value);
+ uint32_t high = High32Bits(value);
+ if (low == 0) {
+ Sw(ZERO, base, offset);
+ } else {
+ LoadConst32(temp, low);
+ Sw(temp, base, offset);
+ }
+ null_checker();
+ if (high == 0) {
+ Sw(ZERO, base, offset + kMipsWordSize);
+ } else {
+ if (high != low) {
+ LoadConst32(temp, high);
+ }
+ Sw(temp, base, offset + kMipsWordSize);
+ }
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void LoadFromOffset(LoadOperandType type,
+ Register reg,
+ Register base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kLoadDoubleword));
+ switch (type) {
+ case kLoadSignedByte:
+ Lb(reg, base, offset);
+ break;
+ case kLoadUnsignedByte:
+ Lbu(reg, base, offset);
+ break;
+ case kLoadSignedHalfword:
+ Lh(reg, base, offset);
+ break;
+ case kLoadUnsignedHalfword:
+ Lhu(reg, base, offset);
+ break;
+ case kLoadWord:
+ Lw(reg, base, offset);
+ break;
+ case kLoadDoubleword:
+ if (reg == base) {
+ // This will clobber the base when loading the lower register. Since we have to load the
+ // higher register as well, this will fail. Solution: reverse the order.
+ Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
+ null_checker();
+ Lw(reg, base, offset);
+ } else {
+ Lw(reg, base, offset);
+ null_checker();
+ Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
+ }
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ if (type != kLoadDoubleword) {
+ null_checker();
+ }
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void LoadSFromOffset(FRegister reg,
+ Register base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
+ Lwc1(reg, base, offset);
+ null_checker();
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void LoadDFromOffset(FRegister reg,
+ Register base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
+ if (IsAligned<kMipsDoublewordSize>(offset)) {
+ Ldc1(reg, base, offset);
+ null_checker();
+ } else {
+ if (Is32BitFPU()) {
+ Lwc1(reg, base, offset);
+ null_checker();
+ Lwc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
+ } else {
+ // 64-bit FPU.
+ Lwc1(reg, base, offset);
+ null_checker();
+ Lw(T8, base, offset + kMipsWordSize);
+ Mthc1(T8, reg);
+ }
+ }
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreToOffset(StoreOperandType type,
+ Register reg,
+ Register base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ // Must not use AT as `reg`, so as not to overwrite the value being stored
+ // with the adjusted `base`.
+ CHECK_NE(reg, AT);
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kStoreDoubleword));
+ switch (type) {
+ case kStoreByte:
+ Sb(reg, base, offset);
+ break;
+ case kStoreHalfword:
+ Sh(reg, base, offset);
+ break;
+ case kStoreWord:
+ Sw(reg, base, offset);
+ break;
+ case kStoreDoubleword:
+ CHECK_NE(reg, base);
+ CHECK_NE(static_cast<Register>(reg + 1), base);
+ Sw(reg, base, offset);
+ null_checker();
+ Sw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ if (type != kStoreDoubleword) {
+ null_checker();
+ }
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreSToOffset(FRegister reg,
+ Register base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
+ Swc1(reg, base, offset);
+ null_checker();
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreDToOffset(FRegister reg,
+ Register base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
+ if (IsAligned<kMipsDoublewordSize>(offset)) {
+ Sdc1(reg, base, offset);
+ null_checker();
+ } else {
+ if (Is32BitFPU()) {
+ Swc1(reg, base, offset);
+ null_checker();
+ Swc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
+ } else {
+ // 64-bit FPU.
+ Mfhc1(T8, reg);
+ Swc1(reg, base, offset);
+ null_checker();
+ Sw(T8, base, offset + kMipsWordSize);
+ }
+ }
+ }
+
void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset);
void LoadSFromOffset(FRegister reg, Register base, int32_t offset);
void LoadDFromOffset(FRegister reg, Register base, int32_t offset);
diff --git a/test/551-implicit-null-checks/expected.txt b/test/551-implicit-null-checks/expected.txt
index e69de29..49b3771 100644
--- a/test/551-implicit-null-checks/expected.txt
+++ b/test/551-implicit-null-checks/expected.txt
@@ -0,0 +1,4 @@
+NPE from GetLong
+NPE from PutLong
+NPE from GetDouble
+NPE from PutDouble
diff --git a/test/551-implicit-null-checks/info.txt b/test/551-implicit-null-checks/info.txt
index bdd066b..bd3ecfd 100644
--- a/test/551-implicit-null-checks/info.txt
+++ b/test/551-implicit-null-checks/info.txt
@@ -1 +1 @@
-Test that implicit null checks are recorded correctly for longs.
\ No newline at end of file
+Test that implicit null checks are recorded correctly for longs and doubles.
diff --git a/test/551-implicit-null-checks/src/Main.java b/test/551-implicit-null-checks/src/Main.java
index 677e8d3..3586a29 100644
--- a/test/551-implicit-null-checks/src/Main.java
+++ b/test/551-implicit-null-checks/src/Main.java
@@ -18,6 +18,7 @@
private class Inner {
private long i1;
+ private double i2;
}
private Inner inst;
@@ -26,12 +27,22 @@
try {
m.$opt$noinline$testGetLong();
} catch (NullPointerException ex) {
- // good
+ System.out.println("NPE from GetLong");
}
try {
m.$opt$noinline$testPutLong(778899112233L);
} catch (NullPointerException ex) {
- // good
+ System.out.println("NPE from PutLong");
+ }
+ try {
+ m.$opt$noinline$testGetDouble();
+ } catch (NullPointerException ex) {
+ System.out.println("NPE from GetDouble");
+ }
+ try {
+ m.$opt$noinline$testPutDouble(1.0);
+ } catch (NullPointerException ex) {
+ System.out.println("NPE from PutDouble");
}
}
@@ -44,4 +55,14 @@
inst.i1 = a;
throw new Exception(); // prevent inline
}
+
+ public void $opt$noinline$testGetDouble() throws Exception {
+ double result = inst.i2;
+ throw new Exception(); // prevent inline
+ }
+
+ public void $opt$noinline$testPutDouble(double a) throws Exception {
+ inst.i2 = a;
+ throw new Exception(); // prevent inline
+ }
}