summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/optimizing/code_generator_mips.cc14
-rw-r--r--compiler/optimizing/code_generator_mips.h1
-rw-r--r--compiler/optimizing/code_generator_mips64.cc94
-rw-r--r--compiler/optimizing/induction_var_range.cc24
-rw-r--r--compiler/optimizing/load_store_elimination.cc109
-rw-r--r--compiler/optimizing/loop_optimization.cc3
-rw-r--r--compiler/optimizing/loop_optimization.h9
-rw-r--r--compiler/optimizing/loop_optimization_test.cc2
-rw-r--r--compiler/optimizing/optimizing_compiler.cc20
-rw-r--r--compiler/optimizing/side_effects_analysis.h6
-rw-r--r--compiler/utils/mips/assembler_mips.h4
-rw-r--r--compiler/utils/mips64/assembler_mips64.cc148
-rw-r--r--compiler/utils/mips64/assembler_mips64.h185
13 files changed, 382 insertions, 237 deletions
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 791e63265e..51dd898a81 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1899,9 +1899,9 @@ void LocationsBuilderMIPS::VisitArrayGet(HArrayGet* instruction) {
}
}
-auto InstructionCodeGeneratorMIPS::GetImplicitNullChecker(HInstruction* instruction) {
- auto null_checker = [this, instruction]() {
- this->codegen_->MaybeRecordImplicitNullCheck(instruction);
+static auto GetImplicitNullChecker(HInstruction* instruction, CodeGeneratorMIPS* codegen) {
+ auto null_checker = [codegen, instruction]() {
+ codegen->MaybeRecordImplicitNullCheck(instruction);
};
return null_checker;
}
@@ -1911,7 +1911,7 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
Register obj = locations->InAt(0).AsRegister<Register>();
Location index = locations->InAt(1);
uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
- auto null_checker = GetImplicitNullChecker(instruction);
+ auto null_checker = GetImplicitNullChecker(instruction, codegen_);
Primitive::Type type = instruction->GetType();
const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
@@ -2148,7 +2148,7 @@ void InstructionCodeGeneratorMIPS::VisitArraySet(HArraySet* instruction) {
bool needs_runtime_call = locations->WillCall();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
- auto null_checker = GetImplicitNullChecker(instruction);
+ auto null_checker = GetImplicitNullChecker(instruction, codegen_);
Register base_reg = index.IsConstant() ? obj : TMP;
switch (value_type) {
@@ -4923,7 +4923,7 @@ void InstructionCodeGeneratorMIPS::HandleFieldGet(HInstruction* instruction,
LoadOperandType load_type = kLoadUnsignedByte;
bool is_volatile = field_info.IsVolatile();
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
- auto null_checker = GetImplicitNullChecker(instruction);
+ auto null_checker = GetImplicitNullChecker(instruction, codegen_);
switch (type) {
case Primitive::kPrimBoolean:
@@ -5052,7 +5052,7 @@ void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction,
bool is_volatile = field_info.IsVolatile();
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1));
- auto null_checker = GetImplicitNullChecker(instruction);
+ auto null_checker = GetImplicitNullChecker(instruction, codegen_);
switch (type) {
case Primitive::kPrimBoolean:
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 47eba50248..0ccd80ab93 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -297,7 +297,6 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator {
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
- auto GetImplicitNullChecker(HInstruction* instruction);
void GenPackedSwitchWithCompares(Register value_reg,
int32_t lower_bound,
uint32_t num_entries,
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 817854b507..138ebe6a25 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1483,11 +1483,19 @@ void LocationsBuilderMIPS64::VisitArrayGet(HArrayGet* instruction) {
}
}
+static auto GetImplicitNullChecker(HInstruction* instruction, CodeGeneratorMIPS64* codegen) {
+ auto null_checker = [codegen, instruction]() {
+ codegen->MaybeRecordImplicitNullCheck(instruction);
+ };
+ return null_checker;
+}
+
void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
Location index = locations->InAt(1);
uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
+ auto null_checker = GetImplicitNullChecker(instruction, codegen_);
Primitive::Type type = instruction->GetType();
const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
@@ -1498,10 +1506,10 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
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 {
__ Daddu(TMP, obj, index.AsRegister<GpuRegister>());
- __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
+ __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset, null_checker);
}
break;
}
@@ -1511,10 +1519,10 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
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 {
__ Daddu(TMP, obj, index.AsRegister<GpuRegister>());
- __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset);
+ __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset, null_checker);
}
break;
}
@@ -1524,11 +1532,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
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 {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2);
__ Daddu(TMP, obj, TMP);
- __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset);
+ __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
}
break;
}
@@ -1537,8 +1545,7 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (maybe_compressed_char_at) {
uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
- __ LoadFromOffset(kLoadWord, TMP, obj, count_offset);
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ LoadFromOffset(kLoadWord, TMP, obj, count_offset, null_checker);
__ Dext(TMP, TMP, 0, 1);
static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
"Expecting 0=compressed, 1=uncompressed");
@@ -1563,7 +1570,8 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
__ LoadFromOffset(kLoadUnsignedHalfword,
out,
obj,
- data_offset + (const_index << TIMES_2));
+ data_offset + (const_index << TIMES_2),
+ null_checker);
}
} else {
GpuRegister index_reg = index.AsRegister<GpuRegister>();
@@ -1581,7 +1589,7 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
} else {
__ Dsll(TMP, index_reg, TIMES_2);
__ Daddu(TMP, obj, TMP);
- __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+ __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
}
}
break;
@@ -1595,11 +1603,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ LoadFromOffset(load_type, out, obj, offset);
+ __ LoadFromOffset(load_type, out, obj, offset, null_checker);
} else {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4);
__ Daddu(TMP, obj, TMP);
- __ LoadFromOffset(load_type, out, TMP, data_offset);
+ __ LoadFromOffset(load_type, out, TMP, data_offset, null_checker);
}
break;
}
@@ -1609,11 +1617,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
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 {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8);
__ Daddu(TMP, obj, TMP);
- __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset);
+ __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
}
break;
}
@@ -1623,11 +1631,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ LoadFpuFromOffset(kLoadWord, out, obj, offset);
+ __ LoadFpuFromOffset(kLoadWord, out, obj, offset, null_checker);
} else {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4);
__ Daddu(TMP, obj, TMP);
- __ LoadFpuFromOffset(kLoadWord, out, TMP, data_offset);
+ __ LoadFpuFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
}
break;
}
@@ -1637,11 +1645,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ LoadFpuFromOffset(kLoadDoubleword, out, obj, offset);
+ __ LoadFpuFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
} else {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8);
__ Daddu(TMP, obj, TMP);
- __ LoadFpuFromOffset(kLoadDoubleword, out, TMP, data_offset);
+ __ LoadFpuFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
}
break;
}
@@ -1650,9 +1658,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
}
- if (!maybe_compressed_char_at) {
- codegen_->MaybeRecordImplicitNullCheck(instruction);
- }
if (type == Primitive::kPrimNot) {
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
@@ -1708,6 +1713,7 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
bool needs_runtime_call = locations->WillCall();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
+ auto null_checker = GetImplicitNullChecker(instruction, codegen_);
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -1717,10 +1723,10 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
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 {
__ Daddu(TMP, obj, index.AsRegister<GpuRegister>());
- __ StoreToOffset(kStoreByte, value, TMP, data_offset);
+ __ StoreToOffset(kStoreByte, value, TMP, data_offset, null_checker);
}
break;
}
@@ -1732,11 +1738,11 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
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 {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2);
__ Daddu(TMP, obj, TMP);
- __ StoreToOffset(kStoreHalfword, value, TMP, data_offset);
+ __ StoreToOffset(kStoreHalfword, value, TMP, data_offset, null_checker);
}
break;
}
@@ -1786,10 +1792,10 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
}
__ PoisonHeapReference(AT, value);
__ Sw(AT, base_reg, data_offset);
+ null_checker();
} else {
- __ StoreToOffset(kStoreWord, value, base_reg, data_offset);
+ __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
if (needs_write_barrier) {
DCHECK_EQ(value_type, Primitive::kPrimNot);
codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull());
@@ -1810,11 +1816,11 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
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 {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8);
__ Daddu(TMP, obj, TMP);
- __ StoreToOffset(kStoreDoubleword, value, TMP, data_offset);
+ __ StoreToOffset(kStoreDoubleword, value, TMP, data_offset, null_checker);
}
break;
}
@@ -1826,11 +1832,11 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ StoreFpuToOffset(kStoreWord, value, obj, offset);
+ __ StoreFpuToOffset(kStoreWord, value, obj, offset, null_checker);
} else {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4);
__ Daddu(TMP, obj, TMP);
- __ StoreFpuToOffset(kStoreWord, value, TMP, data_offset);
+ __ StoreFpuToOffset(kStoreWord, value, TMP, data_offset, null_checker);
}
break;
}
@@ -1842,11 +1848,11 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ StoreFpuToOffset(kStoreDoubleword, value, obj, offset);
+ __ StoreFpuToOffset(kStoreDoubleword, value, obj, offset, null_checker);
} else {
__ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8);
__ Daddu(TMP, obj, TMP);
- __ StoreFpuToOffset(kStoreDoubleword, value, TMP, data_offset);
+ __ StoreFpuToOffset(kStoreDoubleword, value, TMP, data_offset, null_checker);
}
break;
}
@@ -1855,11 +1861,6 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
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 LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -3128,6 +3129,8 @@ void InstructionCodeGeneratorMIPS64::HandleFieldGet(HInstruction* instruction,
GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
LoadOperandType load_type = kLoadUnsignedByte;
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+ auto null_checker = GetImplicitNullChecker(instruction, codegen_);
+
switch (type) {
case Primitive::kPrimBoolean:
load_type = kLoadUnsignedByte;
@@ -3159,14 +3162,12 @@ void InstructionCodeGeneratorMIPS64::HandleFieldGet(HInstruction* instruction,
if (!Primitive::IsFloatingPointType(type)) {
DCHECK(locations->Out().IsRegister());
GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
- __ LoadFromOffset(load_type, dst, obj, offset);
+ __ LoadFromOffset(load_type, dst, obj, offset, null_checker);
} else {
DCHECK(locations->Out().IsFpuRegister());
FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>();
- __ LoadFpuFromOffset(load_type, dst, obj, offset);
+ __ LoadFpuFromOffset(load_type, dst, obj, offset, null_checker);
}
-
- codegen_->MaybeRecordImplicitNullCheck(instruction);
// TODO: memory barrier?
if (type == Primitive::kPrimNot) {
@@ -3196,6 +3197,8 @@ void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction,
StoreOperandType store_type = kStoreByte;
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1));
+ auto null_checker = GetImplicitNullChecker(instruction, codegen_);
+
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -3227,17 +3230,16 @@ void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction,
// need poisoning.
DCHECK_EQ(type, Primitive::kPrimNot);
__ PoisonHeapReference(TMP, src);
- __ StoreToOffset(store_type, TMP, obj, offset);
+ __ StoreToOffset(store_type, TMP, obj, offset, null_checker);
} else {
- __ StoreToOffset(store_type, src, obj, offset);
+ __ StoreToOffset(store_type, src, obj, offset, null_checker);
}
} else {
DCHECK(locations->InAt(1).IsFpuRegister());
FpuRegister src = locations->InAt(1).AsFpuRegister<FpuRegister>();
- __ StoreFpuToOffset(store_type, src, obj, offset);
+ __ StoreFpuToOffset(store_type, src, obj, offset, null_checker);
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
// TODO: memory barriers?
if (needs_write_barrier) {
DCHECK(locations->InAt(1).IsRegister());
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 1cd65c1c66..3dfe17647c 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -57,21 +57,27 @@ static bool IsIntAndGet(HInstruction* instruction, int64_t* value) {
return false;
}
-/** Returns b^e for b,e >= 1. Sets overflow if arithmetic wrap-around occurred. */
+/** Computes a * b for a,b > 0 (at least until first overflow happens). */
+static int64_t SafeMul(int64_t a, int64_t b, /*out*/ bool* overflow) {
+ if (a > 0 && b > 0 && a > (std::numeric_limits<int64_t>::max() / b)) {
+ *overflow = true;
+ }
+ return a * b;
+}
+
+/** Returns b^e for b,e > 0. Sets overflow if arithmetic wrap-around occurred. */
static int64_t IntPow(int64_t b, int64_t e, /*out*/ bool* overflow) {
- DCHECK_GE(b, 1);
- DCHECK_GE(e, 1);
+ DCHECK_LT(0, b);
+ DCHECK_LT(0, e);
int64_t pow = 1;
while (e) {
if (e & 1) {
- int64_t oldpow = pow;
- pow *= b;
- if (pow < oldpow) {
- *overflow = true;
- }
+ pow = SafeMul(pow, b, overflow);
}
e >>= 1;
- b *= b;
+ if (e) {
+ b = SafeMul(b, b, overflow);
+ }
}
return pow;
}
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 2d3c00fb97..46ba048738 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -38,7 +38,8 @@ class ReferenceInfo : public ArenaObject<kArenaAllocMisc> {
position_(pos),
is_singleton_(true),
is_singleton_and_not_returned_(true),
- is_singleton_and_not_deopt_visible_(true) {
+ is_singleton_and_not_deopt_visible_(true),
+ has_index_aliasing_(false) {
CalculateEscape(reference_,
nullptr,
&is_singleton_,
@@ -68,13 +69,29 @@ class ReferenceInfo : public ArenaObject<kArenaAllocMisc> {
return is_singleton_and_not_returned_ && is_singleton_and_not_deopt_visible_;
}
+ bool HasIndexAliasing() {
+ return has_index_aliasing_;
+ }
+
+ void SetHasIndexAliasing(bool has_index_aliasing) {
+ // Only allow setting to true.
+ DCHECK(has_index_aliasing);
+ has_index_aliasing_ = has_index_aliasing;
+ }
+
private:
HInstruction* const reference_;
const size_t position_; // position in HeapLocationCollector's ref_info_array_.
- bool is_singleton_; // can only be referred to by a single name in the method,
- bool is_singleton_and_not_returned_; // and not returned to caller,
- bool is_singleton_and_not_deopt_visible_; // and not used as an environment local of HDeoptimize.
+ // Can only be referred to by a single name in the method.
+ bool is_singleton_;
+ // Is singleton and not returned to caller.
+ bool is_singleton_and_not_returned_;
+ // Is singleton and not used as an environment local of HDeoptimize.
+ bool is_singleton_and_not_deopt_visible_;
+ // Some heap locations with reference_ have array index aliasing,
+ // e.g. arr[i] and arr[j] may be the same location.
+ bool has_index_aliasing_;
DISALLOW_COPY_AND_ASSIGN(ReferenceInfo);
};
@@ -321,6 +338,12 @@ class HeapLocationCollector : public HGraphVisitor {
// Different constant indices do not alias.
return false;
}
+ ReferenceInfo* ref_info = loc1->GetReferenceInfo();
+ if (ref_info->IsSingleton()) {
+ // This is guaranteed by the CanReferencesAlias() test above.
+ DCHECK_EQ(ref_info, loc2->GetReferenceInfo());
+ ref_info->SetHasIndexAliasing(true);
+ }
}
return true;
}
@@ -497,7 +520,8 @@ class LSEVisitor : public HGraphVisitor {
removed_loads_(graph->GetArena()->Adapter(kArenaAllocLSE)),
substitute_instructions_for_loads_(graph->GetArena()->Adapter(kArenaAllocLSE)),
possibly_removed_stores_(graph->GetArena()->Adapter(kArenaAllocLSE)),
- singleton_new_instances_(graph->GetArena()->Adapter(kArenaAllocLSE)) {
+ singleton_new_instances_(graph->GetArena()->Adapter(kArenaAllocLSE)),
+ singleton_new_arrays_(graph->GetArena()->Adapter(kArenaAllocLSE)) {
}
void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
@@ -534,20 +558,24 @@ class LSEVisitor : public HGraphVisitor {
}
// At this point, stores in possibly_removed_stores_ can be safely removed.
- for (size_t i = 0, e = possibly_removed_stores_.size(); i < e; i++) {
- HInstruction* store = possibly_removed_stores_[i];
+ for (HInstruction* store : possibly_removed_stores_) {
DCHECK(store->IsInstanceFieldSet() || store->IsStaticFieldSet() || store->IsArraySet());
store->GetBlock()->RemoveInstruction(store);
}
// Eliminate allocations that are not used.
- for (size_t i = 0, e = singleton_new_instances_.size(); i < e; i++) {
- HInstruction* new_instance = singleton_new_instances_[i];
+ for (HInstruction* new_instance : singleton_new_instances_) {
if (!new_instance->HasNonEnvironmentUses()) {
new_instance->RemoveEnvironmentUsers();
new_instance->GetBlock()->RemoveInstruction(new_instance);
}
}
+ for (HInstruction* new_array : singleton_new_arrays_) {
+ if (!new_array->HasNonEnvironmentUses()) {
+ new_array->RemoveEnvironmentUsers();
+ new_array->GetBlock()->RemoveInstruction(new_array);
+ }
+ }
}
private:
@@ -558,7 +586,7 @@ class LSEVisitor : public HGraphVisitor {
void KeepIfIsStore(HInstruction* heap_value) {
if (heap_value == kDefaultHeapValue ||
heap_value == kUnknownHeapValue ||
- !heap_value->IsInstanceFieldSet()) {
+ !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) {
return;
}
auto idx = std::find(possibly_removed_stores_.begin(),
@@ -734,13 +762,16 @@ class LSEVisitor : public HGraphVisitor {
heap_values[idx] = constant;
return;
}
- if (heap_value != kUnknownHeapValue && heap_value->IsInstanceFieldSet()) {
- HInstruction* store = heap_value;
- // This load must be from a singleton since it's from the same field
- // that a "removed" store puts the value. That store must be to a singleton's field.
- DCHECK(ref_info->IsSingleton());
- // Get the real heap value of the store.
- heap_value = store->InputAt(1);
+ if (heap_value != kUnknownHeapValue) {
+ if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ HInstruction* store = heap_value;
+ // This load must be from a singleton since it's from the same
+ // field/element that a "removed" store puts the value. That store
+ // must be to a singleton's field/element.
+ DCHECK(ref_info->IsSingleton());
+ // Get the real heap value of the store.
+ heap_value = heap_value->IsInstanceFieldSet() ? store->InputAt(1) : store->InputAt(2);
+ }
}
if (heap_value == kUnknownHeapValue) {
// Load isn't eliminated. Put the load as the value into the HeapLocation.
@@ -796,19 +827,19 @@ class LSEVisitor : public HGraphVisitor {
if (Equal(heap_value, value)) {
// Store into the heap location with the same value.
same_value = true;
- } else if (index != nullptr) {
- // For array element, don't eliminate stores since it can be easily aliased
- // with non-constant index.
+ } else if (index != nullptr && ref_info->HasIndexAliasing()) {
+ // For array element, don't eliminate stores if the index can be
+ // aliased.
} else if (ref_info->IsSingletonAndRemovable()) {
- // Store into a field of a singleton that's not returned. The value cannot be
- // killed due to aliasing/invocation. It can be redundant since future loads can
- // directly get the value set by this instruction. The value can still be killed due to
- // merging or loop side effects. Stores whose values are killed due to merging/loop side
- // effects later will be removed from possibly_removed_stores_ when that is detected.
+ // Store into a field/element of a singleton instance/array that's not returned.
+ // The value cannot be killed due to aliasing/invocation. It can be redundant since
+ // future loads can directly get the value set by this instruction. The value can
+ // still be killed due to merging or loop side effects. Stores whose values are
+ // killed due to merging/loop side effects later will be removed from
+ // possibly_removed_stores_ when that is detected.
possibly_redundant = true;
HNewInstance* new_instance = ref_info->GetReference()->AsNewInstance();
- DCHECK(new_instance != nullptr);
- if (new_instance->IsFinalizable()) {
+ if (new_instance != nullptr && new_instance->IsFinalizable()) {
// Finalizable objects escape globally. Need to keep the store.
possibly_redundant = false;
} else {
@@ -834,7 +865,7 @@ class LSEVisitor : public HGraphVisitor {
if (!same_value) {
if (possibly_redundant) {
- DCHECK(instruction->IsInstanceFieldSet());
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet());
// Put the store as the heap value. If the value is loaded from heap
// by a load later, this store isn't really redundant.
heap_values[idx] = instruction;
@@ -995,6 +1026,27 @@ class LSEVisitor : public HGraphVisitor {
}
}
+ void VisitNewArray(HNewArray* new_array) OVERRIDE {
+ ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_array);
+ if (ref_info == nullptr) {
+ // new_array isn't used for array accesses. No need to process it.
+ return;
+ }
+ if (ref_info->IsSingletonAndRemovable()) {
+ singleton_new_arrays_.push_back(new_array);
+ }
+ ArenaVector<HInstruction*>& heap_values =
+ heap_values_for_[new_array->GetBlock()->GetBlockId()];
+ for (size_t i = 0; i < heap_values.size(); i++) {
+ HeapLocation* location = heap_location_collector_.GetHeapLocation(i);
+ HInstruction* ref = location->GetReferenceInfo()->GetReference();
+ if (ref == new_array && location->GetIndex() != nullptr) {
+ // Array elements are set to default heap values.
+ heap_values[i] = kDefaultHeapValue;
+ }
+ }
+ }
+
// Find an instruction's substitute if it should be removed.
// Return the same instruction if it should not be removed.
HInstruction* FindSubstitute(HInstruction* instruction) {
@@ -1023,6 +1075,7 @@ class LSEVisitor : public HGraphVisitor {
ArenaVector<HInstruction*> possibly_removed_stores_;
ArenaVector<HInstruction*> singleton_new_instances_;
+ ArenaVector<HInstruction*> singleton_new_arrays_;
DISALLOW_COPY_AND_ASSIGN(LSEVisitor);
};
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 26c9ab83c2..318d83bf40 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -16,6 +16,7 @@
#include "loop_optimization.h"
+#include "driver/compiler_driver.h"
#include "linear_order.h"
namespace art {
@@ -57,8 +58,10 @@ static bool IsEarlyExit(HLoopInformation* loop_info) {
//
HLoopOptimization::HLoopOptimization(HGraph* graph,
+ CompilerDriver* compiler_driver,
HInductionVarAnalysis* induction_analysis)
: HOptimization(graph, kLoopOptimizationPassName),
+ compiler_driver_(compiler_driver),
induction_range_(induction_analysis),
loop_allocator_(nullptr),
top_loop_(nullptr),
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 9ddab4150c..0b798fc7a9 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -23,13 +23,17 @@
namespace art {
+class CompilerDriver;
+
/**
* Loop optimizations. Builds a loop hierarchy and applies optimizations to
* the detected nested loops, such as removal of dead induction and empty loops.
*/
class HLoopOptimization : public HOptimization {
public:
- HLoopOptimization(HGraph* graph, HInductionVarAnalysis* induction_analysis);
+ HLoopOptimization(HGraph* graph,
+ CompilerDriver* compiler_driver,
+ HInductionVarAnalysis* induction_analysis);
void Run() OVERRIDE;
@@ -76,6 +80,9 @@ class HLoopOptimization : public HOptimization {
bool TryReplaceWithLastValue(HInstruction* instruction, HBasicBlock* block);
void RemoveDeadInstructions(const HInstructionList& list);
+ // Compiler driver (to query ISA features).
+ const CompilerDriver* compiler_driver_;
+
// Range information based on prior induction variable analysis.
InductionVarRange induction_range_;
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index 9a6b4935b2..5b9350689e 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -31,7 +31,7 @@ class LoopOptimizationTest : public CommonCompilerTest {
allocator_(&pool_),
graph_(CreateGraph(&allocator_)),
iva_(new (&allocator_) HInductionVarAnalysis(graph_)),
- loop_opt_(new (&allocator_) HLoopOptimization(graph_, iva_)) {
+ loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_)) {
BuildGraph();
}
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3842ef98da..607b9433ae 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -519,7 +519,7 @@ static HOptimization* BuildOptimization(
} else if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) {
return new (arena) SideEffectsAnalysis(graph);
} else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) {
- return new (arena) HLoopOptimization(graph, most_recent_induction);
+ return new (arena) HLoopOptimization(graph, driver, most_recent_induction);
} else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) {
return new (arena) CHAGuardOptimization(graph);
} else if (opt_name == CodeSinking::kCodeSinkingPassName) {
@@ -773,13 +773,16 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph,
HConstantFolding* fold2 = new (arena) HConstantFolding(
graph, "constant_folding$after_inlining");
HConstantFolding* fold3 = new (arena) HConstantFolding(graph, "constant_folding$after_bce");
- SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
- GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects);
- LICM* licm = new (arena) LICM(graph, *side_effects, stats);
- LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects);
+ SideEffectsAnalysis* side_effects1 = new (arena) SideEffectsAnalysis(
+ graph, "side_effects$before_gvn");
+ SideEffectsAnalysis* side_effects2 = new (arena) SideEffectsAnalysis(
+ graph, "side_effects$before_lse");
+ GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects1);
+ LICM* licm = new (arena) LICM(graph, *side_effects1, stats);
HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
- BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects, induction);
- HLoopOptimization* loop = new (arena) HLoopOptimization(graph, induction);
+ BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects1, induction);
+ HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction);
+ LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects2);
HSharpening* sharpening = new (arena) HSharpening(
graph, codegen, dex_compilation_unit, driver, handles);
InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier(
@@ -810,7 +813,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph,
fold2, // TODO: if we don't inline we can also skip fold2.
simplify2,
dce2,
- side_effects,
+ side_effects1,
gvn,
licm,
induction,
@@ -818,6 +821,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph,
loop,
fold3, // evaluates code generated by dynamic bce
simplify3,
+ side_effects2,
lse,
cha_guard,
dce3,
diff --git a/compiler/optimizing/side_effects_analysis.h b/compiler/optimizing/side_effects_analysis.h
index bac6088bf7..fea47e66d9 100644
--- a/compiler/optimizing/side_effects_analysis.h
+++ b/compiler/optimizing/side_effects_analysis.h
@@ -25,8 +25,8 @@ namespace art {
class SideEffectsAnalysis : public HOptimization {
public:
- explicit SideEffectsAnalysis(HGraph* graph)
- : HOptimization(graph, kSideEffectsAnalysisPassName),
+ SideEffectsAnalysis(HGraph* graph, const char* pass_name = kSideEffectsAnalysisPassName)
+ : HOptimization(graph, pass_name),
graph_(graph),
block_effects_(graph->GetBlocks().size(),
graph->GetArena()->Adapter(kArenaAllocSideEffectsAnalysis)),
@@ -41,7 +41,7 @@ class SideEffectsAnalysis : public HOptimization {
bool HasRun() const { return has_run_; }
- static constexpr const char* kSideEffectsAnalysisPassName = "SideEffects";
+ static constexpr const char* kSideEffectsAnalysisPassName = "side_effects";
private:
void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 47ddf2547a..1a5a23d10b 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -501,8 +501,10 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi
bool is_float = false);
private:
+ // This will be used as an argument for loads/stores
+ // when there is no need for implicit null checks.
struct NoImplicitNullChecker {
- void operator()() {}
+ void operator()() const {}
};
public:
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 0f86f8843d..39eb5893d8 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -2020,80 +2020,18 @@ void Mips64Assembler::Bc1nez(FpuRegister ft, Mips64Label* label) {
Bcond(label, kCondT, static_cast<GpuRegister>(ft), ZERO);
}
-void Mips64Assembler::LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base,
+void Mips64Assembler::LoadFromOffset(LoadOperandType type,
+ GpuRegister reg,
+ GpuRegister base,
int32_t offset) {
- if (!IsInt<16>(offset) ||
- (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
- !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
- LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
- Daddu(AT, AT, base);
- base = AT;
- offset &= (kMips64DoublewordSize - 1);
- }
-
- 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:
- CHECK_ALIGNED(offset, kMips64WordSize);
- Lw(reg, base, offset);
- break;
- case kLoadUnsignedWord:
- CHECK_ALIGNED(offset, kMips64WordSize);
- Lwu(reg, base, offset);
- break;
- case kLoadDoubleword:
- if (!IsAligned<kMips64DoublewordSize>(offset)) {
- CHECK_ALIGNED(offset, kMips64WordSize);
- Lwu(reg, base, offset);
- Lwu(TMP2, base, offset + kMips64WordSize);
- Dinsu(reg, TMP2, 32, 32);
- } else {
- Ld(reg, base, offset);
- }
- break;
- }
+ LoadFromOffset<>(type, reg, base, offset);
}
-void Mips64Assembler::LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base,
+void Mips64Assembler::LoadFpuFromOffset(LoadOperandType type,
+ FpuRegister reg,
+ GpuRegister base,
int32_t offset) {
- if (!IsInt<16>(offset) ||
- (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
- !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
- LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
- Daddu(AT, AT, base);
- base = AT;
- offset &= (kMips64DoublewordSize - 1);
- }
-
- switch (type) {
- case kLoadWord:
- CHECK_ALIGNED(offset, kMips64WordSize);
- Lwc1(reg, base, offset);
- break;
- case kLoadDoubleword:
- if (!IsAligned<kMips64DoublewordSize>(offset)) {
- CHECK_ALIGNED(offset, kMips64WordSize);
- Lwc1(reg, base, offset);
- Lw(TMP2, base, offset + kMips64WordSize);
- Mthc1(TMP2, reg);
- } else {
- Ldc1(reg, base, offset);
- }
- break;
- default:
- LOG(FATAL) << "UNREACHABLE";
- }
+ LoadFpuFromOffset<>(type, reg, base, offset);
}
void Mips64Assembler::EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset,
@@ -2123,72 +2061,18 @@ void Mips64Assembler::EmitLoad(ManagedRegister m_dst, GpuRegister src_register,
}
}
-void Mips64Assembler::StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base,
+void Mips64Assembler::StoreToOffset(StoreOperandType type,
+ GpuRegister reg,
+ GpuRegister base,
int32_t offset) {
- if (!IsInt<16>(offset) ||
- (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
- !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
- LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
- Daddu(AT, AT, base);
- base = AT;
- offset &= (kMips64DoublewordSize - 1);
- }
-
- switch (type) {
- case kStoreByte:
- Sb(reg, base, offset);
- break;
- case kStoreHalfword:
- Sh(reg, base, offset);
- break;
- case kStoreWord:
- CHECK_ALIGNED(offset, kMips64WordSize);
- Sw(reg, base, offset);
- break;
- case kStoreDoubleword:
- if (!IsAligned<kMips64DoublewordSize>(offset)) {
- CHECK_ALIGNED(offset, kMips64WordSize);
- Sw(reg, base, offset);
- Dsrl32(TMP2, reg, 0);
- Sw(TMP2, base, offset + kMips64WordSize);
- } else {
- Sd(reg, base, offset);
- }
- break;
- default:
- LOG(FATAL) << "UNREACHABLE";
- }
+ StoreToOffset<>(type, reg, base, offset);
}
-void Mips64Assembler::StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base,
+void Mips64Assembler::StoreFpuToOffset(StoreOperandType type,
+ FpuRegister reg,
+ GpuRegister base,
int32_t offset) {
- if (!IsInt<16>(offset) ||
- (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
- !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
- LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
- Daddu(AT, AT, base);
- base = AT;
- offset &= (kMips64DoublewordSize - 1);
- }
-
- switch (type) {
- case kStoreWord:
- CHECK_ALIGNED(offset, kMips64WordSize);
- Swc1(reg, base, offset);
- break;
- case kStoreDoubleword:
- if (!IsAligned<kMips64DoublewordSize>(offset)) {
- CHECK_ALIGNED(offset, kMips64WordSize);
- Mfhc1(TMP2, reg);
- Swc1(reg, base, offset);
- Sw(TMP2, base, offset + kMips64WordSize);
- } else {
- Sdc1(reg, base, offset);
- }
- break;
- default:
- LOG(FATAL) << "UNREACHABLE";
- }
+ StoreFpuToOffset<>(type, reg, base, offset);
}
static dwarf::Reg DWARFReg(GpuRegister reg) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index ee15c6da80..8bbe862d19 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -772,6 +772,191 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer
void Bc1nez(FpuRegister ft, Mips64Label* label);
void EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset, size_t size);
+
+ private:
+ // This will be used as an argument for loads/stores
+ // when there is no need for implicit null checks.
+ struct NoImplicitNullChecker {
+ void operator()() const {}
+ };
+
+ public:
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void LoadFromOffset(LoadOperandType type,
+ GpuRegister reg,
+ GpuRegister base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ if (!IsInt<16>(offset) ||
+ (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
+ Daddu(AT, AT, base);
+ base = AT;
+ offset &= (kMips64DoublewordSize - 1);
+ }
+
+ 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:
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Lw(reg, base, offset);
+ break;
+ case kLoadUnsignedWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Lwu(reg, base, offset);
+ break;
+ case kLoadDoubleword:
+ if (!IsAligned<kMips64DoublewordSize>(offset)) {
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Lwu(reg, base, offset);
+ null_checker();
+ Lwu(TMP2, base, offset + kMips64WordSize);
+ Dinsu(reg, TMP2, 32, 32);
+ } else {
+ Ld(reg, base, offset);
+ null_checker();
+ }
+ break;
+ }
+ if (type != kLoadDoubleword) {
+ null_checker();
+ }
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void LoadFpuFromOffset(LoadOperandType type,
+ FpuRegister reg,
+ GpuRegister base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ if (!IsInt<16>(offset) ||
+ (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
+ Daddu(AT, AT, base);
+ base = AT;
+ offset &= (kMips64DoublewordSize - 1);
+ }
+
+ switch (type) {
+ case kLoadWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Lwc1(reg, base, offset);
+ null_checker();
+ break;
+ case kLoadDoubleword:
+ if (!IsAligned<kMips64DoublewordSize>(offset)) {
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Lwc1(reg, base, offset);
+ null_checker();
+ Lw(TMP2, base, offset + kMips64WordSize);
+ Mthc1(TMP2, reg);
+ } else {
+ Ldc1(reg, base, offset);
+ null_checker();
+ }
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreToOffset(StoreOperandType type,
+ GpuRegister reg,
+ GpuRegister base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ if (!IsInt<16>(offset) ||
+ (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
+ Daddu(AT, AT, base);
+ base = AT;
+ offset &= (kMips64DoublewordSize - 1);
+ }
+
+ switch (type) {
+ case kStoreByte:
+ Sb(reg, base, offset);
+ break;
+ case kStoreHalfword:
+ Sh(reg, base, offset);
+ break;
+ case kStoreWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Sw(reg, base, offset);
+ break;
+ case kStoreDoubleword:
+ if (!IsAligned<kMips64DoublewordSize>(offset)) {
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Sw(reg, base, offset);
+ null_checker();
+ Dsrl32(TMP2, reg, 0);
+ Sw(TMP2, base, offset + kMips64WordSize);
+ } else {
+ Sd(reg, base, offset);
+ null_checker();
+ }
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ if (type != kStoreDoubleword) {
+ null_checker();
+ }
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreFpuToOffset(StoreOperandType type,
+ FpuRegister reg,
+ GpuRegister base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ if (!IsInt<16>(offset) ||
+ (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
+ Daddu(AT, AT, base);
+ base = AT;
+ offset &= (kMips64DoublewordSize - 1);
+ }
+
+ switch (type) {
+ case kStoreWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Swc1(reg, base, offset);
+ null_checker();
+ break;
+ case kStoreDoubleword:
+ if (!IsAligned<kMips64DoublewordSize>(offset)) {
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Mfhc1(TMP2, reg);
+ Swc1(reg, base, offset);
+ null_checker();
+ Sw(TMP2, base, offset + kMips64WordSize);
+ } else {
+ Sdc1(reg, base, offset);
+ null_checker();
+ }
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ }
+
void LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base, int32_t offset);
void LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base, int32_t offset);
void StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base, int32_t offset);