Optimize ArraySet for x86/x64/arm/arm64.
Change-Id: I5bc8c6adf7f82f3b211f0c21067f5bb54dd0c040
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index da7a675..1b849a2 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -361,6 +361,51 @@
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM);
};
+class ArraySetSlowPathARM : public SlowPathCode {
+ public:
+ explicit ArraySetSlowPathARM(HInstruction* instruction) : instruction_(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, locations);
+
+ InvokeRuntimeCallingConvention calling_convention;
+ HParallelMove parallel_move(codegen->GetGraph()->GetArena());
+ parallel_move.AddMove(
+ locations->InAt(0),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Primitive::kPrimNot,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(1),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
+ Primitive::kPrimInt,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(2),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
+ Primitive::kPrimNot,
+ nullptr);
+ codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
+
+ CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
+ arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject),
+ instruction_,
+ instruction_->GetDexPc(),
+ this);
+ RestoreLiveRegisters(codegen, locations);
+ __ b(GetExitLabel());
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; }
+
+ private:
+ HInstruction* const instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM);
+};
+
#undef __
#define __ down_cast<ArmAssembler*>(GetAssembler())->
@@ -3744,38 +3789,32 @@
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
- bool needs_runtime_call = instruction->NeedsTypeCheck();
+ bool may_need_runtime_call = instruction->NeedsTypeCheck();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
- instruction, needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall);
- if (needs_runtime_call) {
- InvokeRuntimeCallingConvention calling_convention;
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ instruction,
+ may_need_runtime_call ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ if (Primitive::IsFloatingPointType(value_type)) {
+ locations->SetInAt(2, Location::RequiresFpuRegister());
} else {
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
- if (Primitive::IsFloatingPointType(value_type)) {
- locations->SetInAt(2, Location::RequiresFpuRegister());
- } else {
- locations->SetInAt(2, Location::RequiresRegister());
- }
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
- if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
- locations->AddTemp(Location::RequiresRegister());
- }
+ if (needs_write_barrier) {
+ // Temporary registers for the write barrier.
+ locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
+ locations->AddTemp(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
- Register obj = locations->InAt(0).AsRegister<Register>();
+ Register array = locations->InAt(0).AsRegister<Register>();
Location index = locations->InAt(1);
Primitive::Type value_type = instruction->GetComponentType();
- bool needs_runtime_call = locations->WillCall();
+ bool may_need_runtime_call = locations->CanCall();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
@@ -3787,9 +3826,9 @@
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
- __ StoreToOffset(kStoreByte, value, obj, offset);
+ __ StoreToOffset(kStoreByte, value, array, offset);
} else {
- __ add(IP, obj, ShifterOperand(index.AsRegister<Register>()));
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>()));
__ StoreToOffset(kStoreByte, value, IP, data_offset);
}
break;
@@ -3802,55 +3841,133 @@
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
- __ StoreToOffset(kStoreHalfword, value, obj, offset);
+ __ StoreToOffset(kStoreHalfword, value, array, offset);
} else {
- __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2));
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2));
__ StoreToOffset(kStoreHalfword, value, IP, data_offset);
}
break;
}
- case Primitive::kPrimInt:
case Primitive::kPrimNot: {
- if (!needs_runtime_call) {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- Register value = locations->InAt(2).AsRegister<Register>();
- Register source = value;
- if (kPoisonHeapReferences && needs_write_barrier) {
- // Note that in the case where `value` is a null reference,
- // we do not enter this block, as a null reference does not
- // need poisoning.
- DCHECK_EQ(value_type, Primitive::kPrimNot);
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- __ Mov(temp, value);
- __ PoisonHeapReference(temp);
- source = temp;
- }
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Register value = locations->InAt(2).AsRegister<Register>();
+ Register source = value;
+
+ if (instruction->InputAt(2)->IsNullConstant()) {
+ // Just setting null.
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ StoreToOffset(kStoreWord, source, obj, offset);
+ __ StoreToOffset(kStoreWord, source, array, offset);
} else {
DCHECK(index.IsRegister()) << index;
- __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
__ StoreToOffset(kStoreWord, source, IP, data_offset);
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
- if (needs_write_barrier) {
- DCHECK_EQ(value_type, Primitive::kPrimNot);
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, obj, value, instruction->GetValueCanBeNull());
- }
- } else {
- DCHECK_EQ(value_type, Primitive::kPrimNot);
- // Note: if heap poisoning is enabled, pAputObject takes cares
- // of poisoning the reference.
- codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ break;
}
+
+ DCHECK(needs_write_barrier);
+ Register temp1 = locations->GetTemp(0).AsRegister<Register>();
+ Register temp2 = locations->GetTemp(1).AsRegister<Register>();
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
+ uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
+ Label done;
+ SlowPathCode* slow_path = nullptr;
+
+ if (may_need_runtime_call) {
+ slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction);
+ codegen_->AddSlowPath(slow_path);
+ if (instruction->GetValueCanBeNull()) {
+ Label non_zero;
+ __ CompareAndBranchIfNonZero(value, &non_zero);
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ StoreToOffset(kStoreWord, value, array, offset);
+ } else {
+ DCHECK(index.IsRegister()) << index;
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ StoreToOffset(kStoreWord, value, IP, data_offset);
+ }
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ b(&done);
+ __ Bind(&non_zero);
+ }
+
+ __ LoadFromOffset(kLoadWord, temp1, array, class_offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ MaybeUnpoisonHeapReference(temp1);
+ __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
+ __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
+ // No need to poison/unpoison, we're comparing two poisoined references.
+ __ cmp(temp1, ShifterOperand(temp2));
+ if (instruction->StaticTypeOfArrayIsObjectArray()) {
+ Label do_put;
+ __ b(&do_put, EQ);
+ __ MaybeUnpoisonHeapReference(temp1);
+ __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
+ // No need to poison/unpoison, we're comparing against null.
+ __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
+ __ Bind(&do_put);
+ } else {
+ __ b(slow_path->GetEntryLabel(), NE);
+ }
+ }
+
+ if (kPoisonHeapReferences) {
+ // Note that in the case where `value` is a null reference,
+ // we do not enter this block, as a null reference does not
+ // need poisoning.
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ __ Mov(temp1, value);
+ __ PoisonHeapReference(temp1);
+ source = temp1;
+ }
+
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ StoreToOffset(kStoreWord, source, array, offset);
+ } else {
+ DCHECK(index.IsRegister()) << index;
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ StoreToOffset(kStoreWord, source, IP, data_offset);
+ }
+
+ if (!may_need_runtime_call) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
+
+ if (done.IsLinked()) {
+ __ Bind(&done);
+ }
+
+ if (slow_path != nullptr) {
+ __ Bind(slow_path->GetExitLabel());
+ }
+
+ break;
+ }
+
+ case Primitive::kPrimInt: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Register value = locations->InAt(2).AsRegister<Register>();
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ StoreToOffset(kStoreWord, value, array, offset);
+ } else {
+ DCHECK(index.IsRegister()) << index;
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ StoreToOffset(kStoreWord, value, IP, data_offset);
+ }
+
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -3860,9 +3977,9 @@
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), obj, offset);
+ __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset);
} else {
- __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
__ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset);
}
break;
@@ -3874,9 +3991,9 @@
DCHECK(value.IsFpuRegister());
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ StoreSToOffset(value.AsFpuRegister<SRegister>(), obj, offset);
+ __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset);
} else {
- __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
__ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset);
}
break;
@@ -3888,9 +4005,9 @@
DCHECK(value.IsFpuRegisterPair());
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), obj, offset);
+ __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset);
} else {
- __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
+ __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
__ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
}
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 31900d5..87f502b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -477,7 +477,7 @@
class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 {
public:
explicit DeoptimizationSlowPathARM64(HInstruction* instruction)
- : instruction_(instruction) {}
+ : instruction_(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
@@ -496,6 +496,52 @@
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM64);
};
+class ArraySetSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+ explicit ArraySetSlowPathARM64(HInstruction* instruction) : instruction_(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, locations);
+
+ InvokeRuntimeCallingConvention calling_convention;
+ HParallelMove parallel_move(codegen->GetGraph()->GetArena());
+ parallel_move.AddMove(
+ locations->InAt(0),
+ LocationFrom(calling_convention.GetRegisterAt(0)),
+ Primitive::kPrimNot,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(1),
+ LocationFrom(calling_convention.GetRegisterAt(1)),
+ Primitive::kPrimInt,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(2),
+ LocationFrom(calling_convention.GetRegisterAt(2)),
+ Primitive::kPrimNot,
+ nullptr);
+ codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
+
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+ arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject),
+ instruction_,
+ instruction_->GetDexPc(),
+ this);
+ CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
+ RestoreLiveRegisters(codegen, locations);
+ __ B(GetExitLabel());
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM64"; }
+
+ private:
+ HInstruction* const instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM64);
+};
+
#undef __
Location InvokeDexCallingConventionVisitorARM64::GetNextLocation(Primitive::Type type) {
@@ -1552,76 +1598,134 @@
}
void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) {
- if (instruction->NeedsTypeCheck()) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
- InvokeRuntimeCallingConvention calling_convention;
- locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
- locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2)));
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction,
+ instruction->NeedsTypeCheck() ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ if (Primitive::IsFloatingPointType(instruction->InputAt(2)->GetType())) {
+ locations->SetInAt(2, Location::RequiresFpuRegister());
} else {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
- if (Primitive::IsFloatingPointType(instruction->InputAt(2)->GetType())) {
- locations->SetInAt(2, Location::RequiresFpuRegister());
- } else {
- locations->SetInAt(2, Location::RequiresRegister());
- }
+ locations->SetInAt(2, Location::RequiresRegister());
}
}
void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
Primitive::Type value_type = instruction->GetComponentType();
LocationSummary* locations = instruction->GetLocations();
- bool needs_runtime_call = locations->WillCall();
+ bool may_need_runtime_call = locations->CanCall();
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
- if (needs_runtime_call) {
- // Note: if heap poisoning is enabled, pAputObject takes cares
- // of poisoning the reference.
- codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc(), nullptr);
- CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
+ Register array = InputRegisterAt(instruction, 0);
+ CPURegister value = InputCPURegisterAt(instruction, 2);
+ CPURegister source = value;
+ Location index = locations->InAt(1);
+ size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
+ MemOperand destination = HeapOperand(array);
+ MacroAssembler* masm = GetVIXLAssembler();
+ BlockPoolsScope block_pools(masm);
+
+ if (!needs_write_barrier) {
+ DCHECK(!may_need_runtime_call);
+ if (index.IsConstant()) {
+ offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type);
+ destination = HeapOperand(array, offset);
+ } else {
+ UseScratchRegisterScope temps(masm);
+ Register temp = temps.AcquireSameSizeAs(array);
+ __ Add(temp, array, offset);
+ destination = HeapOperand(temp,
+ XRegisterFrom(index),
+ LSL,
+ Primitive::ComponentSizeShift(value_type));
+ }
+ codegen_->Store(value_type, value, destination);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
} else {
- Register obj = InputRegisterAt(instruction, 0);
- CPURegister value = InputCPURegisterAt(instruction, 2);
- CPURegister source = value;
- Location index = locations->InAt(1);
- size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
- MemOperand destination = HeapOperand(obj);
- MacroAssembler* masm = GetVIXLAssembler();
- BlockPoolsScope block_pools(masm);
+ DCHECK(needs_write_barrier);
+ vixl::Label done;
+ SlowPathCodeARM64* slow_path = nullptr;
{
// We use a block to end the scratch scope before the write barrier, thus
// freeing the temporary registers so they can be used in `MarkGCCard`.
UseScratchRegisterScope temps(masm);
-
- if (kPoisonHeapReferences && value_type == Primitive::kPrimNot) {
- DCHECK(value.IsW());
- Register temp = temps.AcquireW();
- __ Mov(temp, value.W());
- GetAssembler()->PoisonHeapReference(temp.W());
- source = temp;
- }
-
+ Register temp = temps.AcquireSameSizeAs(array);
if (index.IsConstant()) {
offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type);
- destination = HeapOperand(obj, offset);
+ destination = HeapOperand(array, offset);
} else {
- Register temp = temps.AcquireSameSizeAs(obj);
- __ Add(temp, obj, offset);
destination = HeapOperand(temp,
XRegisterFrom(index),
LSL,
Primitive::ComponentSizeShift(value_type));
}
- codegen_->Store(value_type, source, destination);
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
+ uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
+
+ if (may_need_runtime_call) {
+ slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM64(instruction);
+ codegen_->AddSlowPath(slow_path);
+ if (instruction->GetValueCanBeNull()) {
+ vixl::Label non_zero;
+ __ Cbnz(Register(value), &non_zero);
+ if (!index.IsConstant()) {
+ __ Add(temp, array, offset);
+ }
+ __ Str(wzr, destination);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ B(&done);
+ __ Bind(&non_zero);
+ }
+
+ Register temp2 = temps.AcquireSameSizeAs(array);
+ __ Ldr(temp, HeapOperand(array, class_offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ GetAssembler()->MaybeUnpoisonHeapReference(temp);
+ __ Ldr(temp, HeapOperand(temp, component_offset));
+ __ Ldr(temp2, HeapOperand(Register(value), class_offset));
+ // No need to poison/unpoison, we're comparing two poisoned references.
+ __ Cmp(temp, temp2);
+ if (instruction->StaticTypeOfArrayIsObjectArray()) {
+ vixl::Label do_put;
+ __ B(eq, &do_put);
+ GetAssembler()->MaybeUnpoisonHeapReference(temp);
+ __ Ldr(temp, HeapOperand(temp, super_offset));
+ // No need to unpoison, we're comparing against null.
+ __ Cbnz(temp, slow_path->GetEntryLabel());
+ __ Bind(&do_put);
+ } else {
+ __ B(ne, slow_path->GetEntryLabel());
+ }
+ }
+
+ if (kPoisonHeapReferences) {
+ DCHECK(value.IsW());
+ __ Mov(temp, value.W());
+ GetAssembler()->PoisonHeapReference(temp);
+ source = temp;
+ }
+
+ if (!index.IsConstant()) {
+ __ Add(temp, array, offset);
+ }
+ __ Str(value, destination);
+
+ if (!may_need_runtime_call) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
}
- if (CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue())) {
- codegen_->MarkGCCard(obj, value.W(), instruction->GetValueCanBeNull());
+
+ codegen_->MarkGCCard(array, value.W(), instruction->GetValueCanBeNull());
+
+ if (done.IsLinked()) {
+ __ Bind(&done);
+ }
+
+ if (slow_path != nullptr) {
+ __ Bind(slow_path->GetExitLabel());
}
}
}
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 277f6b4..db4a854 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -380,6 +380,51 @@
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86);
};
+class ArraySetSlowPathX86 : public SlowPathCode {
+ public:
+ explicit ArraySetSlowPathX86(HInstruction* instruction) : instruction_(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, locations);
+
+ InvokeRuntimeCallingConvention calling_convention;
+ HParallelMove parallel_move(codegen->GetGraph()->GetArena());
+ parallel_move.AddMove(
+ locations->InAt(0),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Primitive::kPrimNot,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(1),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
+ Primitive::kPrimInt,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(2),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
+ Primitive::kPrimNot,
+ nullptr);
+ codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
+
+ CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
+ x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject),
+ instruction_,
+ instruction_->GetDexPc(),
+ this);
+ RestoreLiveRegisters(codegen, locations);
+ __ jmp(GetExitLabel());
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathX86"; }
+
+ private:
+ HInstruction* const instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86);
+};
+
#undef __
#define __ down_cast<X86Assembler*>(GetAssembler())->
@@ -4245,72 +4290,59 @@
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
- bool needs_runtime_call = instruction->NeedsTypeCheck();
+ bool may_need_runtime_call = instruction->NeedsTypeCheck();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
instruction,
- needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall);
+ may_need_runtime_call ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
- if (needs_runtime_call) {
- InvokeRuntimeCallingConvention calling_convention;
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ bool is_byte_type = (value_type == Primitive::kPrimBoolean)
+ || (value_type == Primitive::kPrimByte);
+ // We need the inputs to be different than the output in case of long operation.
+ // In case of a byte operation, the register allocator does not support multiple
+ // inputs that die at entry with one in a specific register.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ if (is_byte_type) {
+ // Ensure the value is in a byte register.
+ locations->SetInAt(2, Location::ByteRegisterOrConstant(EAX, instruction->InputAt(2)));
+ } else if (Primitive::IsFloatingPointType(value_type)) {
+ locations->SetInAt(2, Location::RequiresFpuRegister());
} else {
- bool is_byte_type = (value_type == Primitive::kPrimBoolean)
- || (value_type == Primitive::kPrimByte);
- // We need the inputs to be different than the output in case of long operation.
- // In case of a byte operation, the register allocator does not support multiple
- // inputs that die at entry with one in a specific register.
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
- if (is_byte_type) {
- // Ensure the value is in a byte register.
- locations->SetInAt(2, Location::ByteRegisterOrConstant(EAX, instruction->InputAt(2)));
- } else if (Primitive::IsFloatingPointType(value_type)) {
- locations->SetInAt(2, Location::RequiresFpuRegister());
- } else {
- locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
- }
- if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
- // Ensure the card is in a byte register.
- locations->AddTemp(Location::RegisterLocation(ECX));
- }
+ locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
+ }
+ if (needs_write_barrier) {
+ // Temporary registers for the write barrier.
+ locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
+ // Ensure the card is in a byte register.
+ locations->AddTemp(Location::RegisterLocation(ECX));
}
}
void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
- Register obj = locations->InAt(0).AsRegister<Register>();
+ Register array = locations->InAt(0).AsRegister<Register>();
Location index = locations->InAt(1);
Location value = locations->InAt(2);
Primitive::Type value_type = instruction->GetComponentType();
- bool needs_runtime_call = locations->WillCall();
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
+ uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
+ bool may_need_runtime_call = locations->CanCall();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
switch (value_type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
- if (value.IsRegister()) {
- __ movb(Address(obj, offset), value.AsRegister<ByteRegister>());
- } else {
- __ movb(Address(obj, offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + offset)
+ : Address(array, index.AsRegister<Register>(), TIMES_1, offset);
+ if (value.IsRegister()) {
+ __ movb(address, value.AsRegister<ByteRegister>());
} else {
- if (value.IsRegister()) {
- __ movb(Address(obj, index.AsRegister<Register>(), TIMES_1, data_offset),
- value.AsRegister<ByteRegister>());
- } else {
- __ movb(Address(obj, index.AsRegister<Register>(), TIMES_1, data_offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
- }
+ __ movb(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
@@ -4318,93 +4350,106 @@
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
- if (value.IsRegister()) {
- __ movw(Address(obj, offset), value.AsRegister<Register>());
- } else {
- __ movw(Address(obj, offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + offset)
+ : Address(array, index.AsRegister<Register>(), TIMES_2, offset);
+ if (value.IsRegister()) {
+ __ movw(address, value.AsRegister<Register>());
} else {
- if (value.IsRegister()) {
- __ movw(Address(obj, index.AsRegister<Register>(), TIMES_2, data_offset),
- value.AsRegister<Register>());
- } else {
- __ movw(Address(obj, index.AsRegister<Register>(), TIMES_2, data_offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
- }
+ __ movw(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
- case Primitive::kPrimInt:
case Primitive::kPrimNot: {
- if (!needs_runtime_call) {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- if (value.IsRegister()) {
- if (kPoisonHeapReferences && value_type == Primitive::kPrimNot) {
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- __ movl(temp, value.AsRegister<Register>());
- __ PoisonHeapReference(temp);
- __ movl(Address(obj, offset), temp);
- } else {
- __ movl(Address(obj, offset), value.AsRegister<Register>());
- }
- } else {
- DCHECK(value.IsConstant()) << value;
- int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
- // `value_type == Primitive::kPrimNot` implies `v == 0`.
- DCHECK((value_type != Primitive::kPrimNot) || (v == 0));
- // Note: if heap poisoning is enabled, no need to poison
- // (negate) `v` if it is a reference, as it would be null.
- __ movl(Address(obj, offset), Immediate(v));
- }
- } else {
- DCHECK(index.IsRegister()) << index;
- if (value.IsRegister()) {
- if (kPoisonHeapReferences && value_type == Primitive::kPrimNot) {
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- __ movl(temp, value.AsRegister<Register>());
- __ PoisonHeapReference(temp);
- __ movl(Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset), temp);
- } else {
- __ movl(Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset),
- value.AsRegister<Register>());
- }
- } else {
- DCHECK(value.IsConstant()) << value;
- int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
- // `value_type == Primitive::kPrimNot` implies `v == 0`.
- DCHECK((value_type != Primitive::kPrimNot) || (v == 0));
- // Note: if heap poisoning is enabled, no need to poison
- // (negate) `v` if it is a reference, as it would be null.
- __ movl(Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset), Immediate(v));
- }
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
+ : Address(array, index.AsRegister<Register>(), TIMES_4, offset);
+ if (!value.IsRegister()) {
+ // Just setting null.
+ DCHECK(instruction->InputAt(2)->IsNullConstant());
+ DCHECK(value.IsConstant()) << value;
+ __ movl(address, Immediate(0));
codegen_->MaybeRecordImplicitNullCheck(instruction);
-
- if (needs_write_barrier) {
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(
- temp, card, obj, value.AsRegister<Register>(), instruction->GetValueCanBeNull());
- }
- } else {
- DCHECK_EQ(value_type, Primitive::kPrimNot);
- DCHECK(!codegen_->IsLeafMethod());
- // Note: if heap poisoning is enabled, pAputObject takes cares
- // of poisoning the reference.
- codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ DCHECK(!needs_write_barrier);
+ DCHECK(!may_need_runtime_call);
+ break;
}
+
+ DCHECK(needs_write_barrier);
+ Register register_value = value.AsRegister<Register>();
+ NearLabel done, not_null, do_put;
+ SlowPathCode* slow_path = nullptr;
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ if (may_need_runtime_call) {
+ slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathX86(instruction);
+ codegen_->AddSlowPath(slow_path);
+ if (instruction->GetValueCanBeNull()) {
+ __ testl(register_value, register_value);
+ __ j(kNotEqual, ¬_null);
+ __ movl(address, Immediate(0));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ jmp(&done);
+ __ Bind(¬_null);
+ }
+
+ __ movl(temp, Address(array, class_offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ MaybeUnpoisonHeapReference(temp);
+ __ movl(temp, Address(temp, component_offset));
+ // No need to poison/unpoison, we're comparing two poisoned references.
+ __ cmpl(temp, Address(register_value, class_offset));
+ if (instruction->StaticTypeOfArrayIsObjectArray()) {
+ __ j(kEqual, &do_put);
+ __ MaybeUnpoisonHeapReference(temp);
+ __ movl(temp, Address(temp, super_offset));
+ // No need to unpoison, we're comparing against null..
+ __ testl(temp, temp);
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ __ Bind(&do_put);
+ } else {
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ }
+ }
+
+ if (kPoisonHeapReferences) {
+ __ movl(temp, register_value);
+ __ PoisonHeapReference(temp);
+ __ movl(address, temp);
+ } else {
+ __ movl(address, register_value);
+ }
+ if (!may_need_runtime_call) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ Register card = locations->GetTemp(1).AsRegister<Register>();
+ codegen_->MarkGCCard(
+ temp, card, array, value.AsRegister<Register>(), instruction->GetValueCanBeNull());
+ __ Bind(&done);
+
+ if (slow_path != nullptr) {
+ __ Bind(slow_path->GetExitLabel());
+ }
+
+ break;
+ }
+ case Primitive::kPrimInt: {
+ uint32_t offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
+ : Address(array, index.AsRegister<Register>(), TIMES_4, offset);
+ if (value.IsRegister()) {
+ __ movl(address, value.AsRegister<Register>());
+ } else {
+ DCHECK(value.IsConstant()) << value;
+ int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
+ __ movl(address, Immediate(v));
+ }
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -4413,30 +4458,30 @@
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
if (value.IsRegisterPair()) {
- __ movl(Address(obj, offset), value.AsRegisterPairLow<Register>());
+ __ movl(Address(array, offset), value.AsRegisterPairLow<Register>());
codegen_->MaybeRecordImplicitNullCheck(instruction);
- __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh<Register>());
+ __ movl(Address(array, offset + kX86WordSize), value.AsRegisterPairHigh<Register>());
} else {
DCHECK(value.IsConstant());
int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
- __ movl(Address(obj, offset), Immediate(Low32Bits(val)));
+ __ movl(Address(array, offset), Immediate(Low32Bits(val)));
codegen_->MaybeRecordImplicitNullCheck(instruction);
- __ movl(Address(obj, offset + kX86WordSize), Immediate(High32Bits(val)));
+ __ movl(Address(array, offset + kX86WordSize), Immediate(High32Bits(val)));
}
} else {
if (value.IsRegisterPair()) {
- __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
+ __ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset),
value.AsRegisterPairLow<Register>());
codegen_->MaybeRecordImplicitNullCheck(instruction);
- __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
+ __ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
value.AsRegisterPairHigh<Register>());
} else {
DCHECK(value.IsConstant());
int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
- __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
+ __ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset),
Immediate(Low32Bits(val)));
codegen_->MaybeRecordImplicitNullCheck(instruction);
- __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
+ __ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
Immediate(High32Bits(val)));
}
}
@@ -4444,28 +4489,22 @@
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+ uint32_t offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
+ : Address(array, index.AsRegister<Register>(), TIMES_4, offset);
DCHECK(value.IsFpuRegister());
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ movss(Address(obj, offset), value.AsFpuRegister<XmmRegister>());
- } else {
- __ movss(Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset),
- value.AsFpuRegister<XmmRegister>());
- }
+ __ movss(address, value.AsFpuRegister<XmmRegister>());
break;
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+ uint32_t offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + offset)
+ : Address(array, index.AsRegister<Register>(), TIMES_8, offset);
DCHECK(value.IsFpuRegister());
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ movsd(Address(obj, offset), value.AsFpuRegister<XmmRegister>());
- } else {
- __ movsd(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
- value.AsFpuRegister<XmmRegister>());
- }
+ __ movsd(address, value.AsFpuRegister<XmmRegister>());
break;
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 453c6fd..3299db4 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -396,6 +396,51 @@
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86_64);
};
+class ArraySetSlowPathX86_64 : public SlowPathCode {
+ public:
+ explicit ArraySetSlowPathX86_64(HInstruction* instruction) : instruction_(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, locations);
+
+ InvokeRuntimeCallingConvention calling_convention;
+ HParallelMove parallel_move(codegen->GetGraph()->GetArena());
+ parallel_move.AddMove(
+ locations->InAt(0),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Primitive::kPrimNot,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(1),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
+ Primitive::kPrimInt,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(2),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
+ Primitive::kPrimNot,
+ nullptr);
+ codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
+
+ CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+ x64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject),
+ instruction_,
+ instruction_->GetDexPc(),
+ this);
+ RestoreLiveRegisters(codegen, locations);
+ __ jmp(GetExitLabel());
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathX86_64"; }
+
+ private:
+ HInstruction* const instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86_64);
+};
+
#undef __
#define __ down_cast<X86_64Assembler*>(GetAssembler())->
@@ -3992,66 +4037,55 @@
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
- bool needs_runtime_call = instruction->NeedsTypeCheck();
+ bool may_need_runtime_call = instruction->NeedsTypeCheck();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
- instruction, needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall);
- if (needs_runtime_call) {
- InvokeRuntimeCallingConvention calling_convention;
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
- } else {
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(
- 1, Location::RegisterOrConstant(instruction->InputAt(1)));
- locations->SetInAt(2, Location::RequiresRegister());
- if (value_type == Primitive::kPrimLong) {
- locations->SetInAt(2, Location::RegisterOrInt32LongConstant(instruction->InputAt(2)));
- } else if (value_type == Primitive::kPrimFloat || value_type == Primitive::kPrimDouble) {
- locations->SetInAt(2, Location::RequiresFpuRegister());
- } else {
- locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
- }
+ instruction,
+ may_need_runtime_call ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
- if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
- locations->AddTemp(Location::RequiresRegister());
- }
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(
+ 1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ locations->SetInAt(2, Location::RequiresRegister());
+ if (value_type == Primitive::kPrimLong) {
+ locations->SetInAt(2, Location::RegisterOrInt32LongConstant(instruction->InputAt(2)));
+ } else if (value_type == Primitive::kPrimFloat || value_type == Primitive::kPrimDouble) {
+ locations->SetInAt(2, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
+ }
+
+ if (needs_write_barrier) {
+ // Temporary registers for the write barrier.
+ locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
+ locations->AddTemp(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
- CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister array = locations->InAt(0).AsRegister<CpuRegister>();
Location index = locations->InAt(1);
Location value = locations->InAt(2);
Primitive::Type value_type = instruction->GetComponentType();
- bool needs_runtime_call = locations->WillCall();
+ bool may_need_runtime_call = locations->CanCall();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
+ uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
switch (value_type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
- if (value.IsRegister()) {
- __ movb(Address(obj, offset), value.AsRegister<CpuRegister>());
- } else {
- __ movb(Address(obj, offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + offset)
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_1, offset);
+ if (value.IsRegister()) {
+ __ movb(address, value.AsRegister<CpuRegister>());
} else {
- if (value.IsRegister()) {
- __ movb(Address(obj, index.AsRegister<CpuRegister>(), TIMES_1, data_offset),
- value.AsRegister<CpuRegister>());
- } else {
- __ movb(Address(obj, index.AsRegister<CpuRegister>(), TIMES_1, data_offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
- }
+ __ movb(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
@@ -4059,154 +4093,145 @@
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
- if (value.IsRegister()) {
- __ movw(Address(obj, offset), value.AsRegister<CpuRegister>());
- } else {
- DCHECK(value.IsConstant()) << value;
- __ movw(Address(obj, offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + offset)
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_2, offset);
+ if (value.IsRegister()) {
+ __ movw(address, value.AsRegister<CpuRegister>());
} else {
- DCHECK(index.IsRegister()) << index;
- if (value.IsRegister()) {
- __ movw(Address(obj, index.AsRegister<CpuRegister>(), TIMES_2, data_offset),
- value.AsRegister<CpuRegister>());
- } else {
- DCHECK(value.IsConstant()) << value;
- __ movw(Address(obj, index.AsRegister<CpuRegister>(), TIMES_2, data_offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
- }
+ DCHECK(value.IsConstant()) << value;
+ __ movw(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
- case Primitive::kPrimInt:
case Primitive::kPrimNot: {
- if (!needs_runtime_call) {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- if (value.IsRegister()) {
- if (kPoisonHeapReferences && value_type == Primitive::kPrimNot) {
- CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
- __ movl(temp, value.AsRegister<CpuRegister>());
- __ PoisonHeapReference(temp);
- __ movl(Address(obj, offset), temp);
- } else {
- __ movl(Address(obj, offset), value.AsRegister<CpuRegister>());
- }
- } else {
- DCHECK(value.IsConstant()) << value;
- int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
- // `value_type == Primitive::kPrimNot` implies `v == 0`.
- DCHECK((value_type != Primitive::kPrimNot) || (v == 0));
- // Note: if heap poisoning is enabled, no need to poison
- // (negate) `v` if it is a reference, as it would be null.
- __ movl(Address(obj, offset), Immediate(v));
- }
- } else {
- DCHECK(index.IsRegister()) << index;
- if (value.IsRegister()) {
- if (kPoisonHeapReferences && value_type == Primitive::kPrimNot) {
- CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
- __ movl(temp, value.AsRegister<CpuRegister>());
- __ PoisonHeapReference(temp);
- __ movl(Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset), temp);
- } else {
- __ movl(Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset),
- value.AsRegister<CpuRegister>());
- }
- } else {
- DCHECK(value.IsConstant()) << value;
- int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
- // `value_type == Primitive::kPrimNot` implies `v == 0`.
- DCHECK((value_type != Primitive::kPrimNot) || (v == 0));
- // Note: if heap poisoning is enabled, no need to poison
- // (negate) `v` if it is a reference, as it would be null.
- __ movl(Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset),
- Immediate(v));
- }
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_4, offset);
+ if (!value.IsRegister()) {
+ // Just setting null.
+ DCHECK(instruction->InputAt(2)->IsNullConstant());
+ DCHECK(value.IsConstant()) << value;
+ __ movl(address, Immediate(0));
codegen_->MaybeRecordImplicitNullCheck(instruction);
- if (needs_write_barrier) {
- DCHECK_EQ(value_type, Primitive::kPrimNot);
- CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
- CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(
- temp, card, obj, value.AsRegister<CpuRegister>(), instruction->GetValueCanBeNull());
- }
- } else {
- DCHECK_EQ(value_type, Primitive::kPrimNot);
- // Note: if heap poisoning is enabled, pAputObject takes cares
- // of poisoning the reference.
- codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject),
- instruction,
- instruction->GetDexPc(),
- nullptr);
- DCHECK(!codegen_->IsLeafMethod());
+ DCHECK(!needs_write_barrier);
+ DCHECK(!may_need_runtime_call);
+ break;
}
+
+ DCHECK(needs_write_barrier);
+ CpuRegister register_value = value.AsRegister<CpuRegister>();
+ NearLabel done, not_null, do_put;
+ SlowPathCode* slow_path = nullptr;
+ CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+ if (may_need_runtime_call) {
+ slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathX86_64(instruction);
+ codegen_->AddSlowPath(slow_path);
+ if (instruction->GetValueCanBeNull()) {
+ __ testl(register_value, register_value);
+ __ j(kNotEqual, ¬_null);
+ __ movl(address, Immediate(0));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ jmp(&done);
+ __ Bind(¬_null);
+ }
+
+ __ movl(temp, Address(array, class_offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ MaybeUnpoisonHeapReference(temp);
+ __ movl(temp, Address(temp, component_offset));
+ // No need to poison/unpoison, we're comparing two poisoned references.
+ __ cmpl(temp, Address(register_value, class_offset));
+ if (instruction->StaticTypeOfArrayIsObjectArray()) {
+ __ j(kEqual, &do_put);
+ __ MaybeUnpoisonHeapReference(temp);
+ __ movl(temp, Address(temp, super_offset));
+ // No need to unpoison the result, we're comparing against null.
+ __ testl(temp, temp);
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ __ Bind(&do_put);
+ } else {
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ }
+ }
+
+ if (kPoisonHeapReferences) {
+ __ movl(temp, register_value);
+ __ PoisonHeapReference(temp);
+ __ movl(address, temp);
+ } else {
+ __ movl(address, register_value);
+ }
+ if (!may_need_runtime_call) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
+ codegen_->MarkGCCard(
+ temp, card, array, value.AsRegister<CpuRegister>(), instruction->GetValueCanBeNull());
+ __ Bind(&done);
+
+ if (slow_path != nullptr) {
+ __ Bind(slow_path->GetExitLabel());
+ }
+
+ break;
+ }
+ case Primitive::kPrimInt: {
+ uint32_t offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_4, offset);
+ if (value.IsRegister()) {
+ __ movl(address, value.AsRegister<CpuRegister>());
+ } else {
+ DCHECK(value.IsConstant()) << value;
+ int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
+ __ movl(address, Immediate(v));
+ }
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- if (value.IsRegister()) {
- __ movq(Address(obj, offset), value.AsRegister<CpuRegister>());
- } else {
- int64_t v = value.GetConstant()->AsLongConstant()->GetValue();
- DCHECK(IsInt<32>(v));
- int32_t v_32 = v;
- __ movq(Address(obj, offset), Immediate(v_32));
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + offset)
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_8, offset);
+ if (value.IsRegister()) {
+ __ movq(address, value.AsRegister<CpuRegister>());
} else {
- if (value.IsRegister()) {
- __ movq(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset),
- value.AsRegister<CpuRegister>());
- } else {
- int64_t v = value.GetConstant()->AsLongConstant()->GetValue();
- DCHECK(IsInt<32>(v));
- int32_t v_32 = v;
- __ movq(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset),
- Immediate(v_32));
- }
+ int64_t v = value.GetConstant()->AsLongConstant()->GetValue();
+ DCHECK(IsInt<32>(v));
+ int32_t v_32 = v;
+ __ movq(address, Immediate(v_32));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- DCHECK(value.IsFpuRegister());
- __ movss(Address(obj, offset), value.AsFpuRegister<XmmRegister>());
- } else {
- DCHECK(value.IsFpuRegister());
- __ movss(Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset),
- value.AsFpuRegister<XmmRegister>());
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_4, offset);
+ DCHECK(value.IsFpuRegister());
+ __ movss(address, value.AsFpuRegister<XmmRegister>());
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- DCHECK(value.IsFpuRegister());
- __ movsd(Address(obj, offset), value.AsFpuRegister<XmmRegister>());
- } else {
- DCHECK(value.IsFpuRegister());
- __ movsd(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset),
- value.AsFpuRegister<XmmRegister>());
- }
+ uint32_t offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+ Address address = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + offset)
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_8, offset);
+ DCHECK(value.IsFpuRegister());
+ __ movsd(address, value.AsFpuRegister<XmmRegister>());
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -4250,7 +4275,7 @@
Location index_loc = locations->InAt(0);
Location length_loc = locations->InAt(1);
SlowPathCode* slow_path =
- new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(instruction);
+ new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(instruction);
if (length_loc.IsConstant()) {
int32_t length = CodeGenerator::GetInt32ValueOf(length_loc.GetConstant());
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 22bca2f..3287a0a 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -431,19 +431,41 @@
HInstruction* value = instruction->GetValue();
if (value->GetType() != Primitive::kPrimNot) return;
+ if (CanEnsureNotNullAt(value, instruction)) {
+ instruction->ClearValueCanBeNull();
+ }
+
if (value->IsArrayGet()) {
if (value->AsArrayGet()->GetArray() == instruction->GetArray()) {
// If the code is just swapping elements in the array, no need for a type check.
instruction->ClearNeedsTypeCheck();
+ return;
}
}
if (value->IsNullConstant()) {
instruction->ClearNeedsTypeCheck();
+ return;
}
- if (CanEnsureNotNullAt(value, instruction)) {
- instruction->ClearValueCanBeNull();
+ ScopedObjectAccess soa(Thread::Current());
+ ReferenceTypeInfo array_rti = instruction->GetArray()->GetReferenceTypeInfo();
+ ReferenceTypeInfo value_rti = value->GetReferenceTypeInfo();
+ if (!array_rti.IsValid()) {
+ return;
+ }
+
+ if (value_rti.IsValid() && array_rti.CanArrayHold(value_rti)) {
+ instruction->ClearNeedsTypeCheck();
+ return;
+ }
+
+ if (array_rti.IsObjectArray()) {
+ if (array_rti.IsExact()) {
+ instruction->ClearNeedsTypeCheck();
+ return;
+ }
+ instruction->SetStaticTypeOfArrayIsObjectArray();
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 486968c..84bde8e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1644,17 +1644,34 @@
bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) {
return IsValidHandle(type_handle_);
}
+
bool IsExact() const { return is_exact_; }
bool IsObjectClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(IsValid());
return GetTypeHandle()->IsObjectClass();
}
+
+ bool IsObjectArray() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return IsArrayClass() && GetTypeHandle()->GetComponentType()->IsObjectClass();
+ }
+
bool IsInterface() const SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(IsValid());
return GetTypeHandle()->IsInterface();
}
+ bool IsArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetTypeHandle()->IsArrayClass();
+ }
+
+ bool CanArrayHold(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (!IsExact()) return false;
+ if (!IsArrayClass()) return false;
+ return GetTypeHandle()->GetComponentType()->IsAssignableFrom(rti.GetTypeHandle().Get());
+ }
+
Handle<mirror::Class> GetTypeHandle() const { return type_handle_; }
bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -4312,7 +4329,8 @@
SideEffectsForArchRuntimeCalls(value->GetType())), dex_pc),
expected_component_type_(expected_component_type),
needs_type_check_(value->GetType() == Primitive::kPrimNot),
- value_can_be_null_(true) {
+ value_can_be_null_(true),
+ static_type_of_array_is_object_array_(false) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
@@ -4341,8 +4359,13 @@
value_can_be_null_ = false;
}
+ void SetStaticTypeOfArrayIsObjectArray() {
+ static_type_of_array_is_object_array_ = true;
+ }
+
bool GetValueCanBeNull() const { return value_can_be_null_; }
bool NeedsTypeCheck() const { return needs_type_check_; }
+ bool StaticTypeOfArrayIsObjectArray() const { return static_type_of_array_is_object_array_; }
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
@@ -4369,6 +4392,9 @@
const Primitive::Type expected_component_type_;
bool needs_type_check_;
bool value_can_be_null_;
+ // Cached information for the reference_type_info_ so that codegen
+ // does not need to inspect the static type.
+ bool static_type_of_array_is_object_array_;
DISALLOW_COPY_AND_ASSIGN(HArraySet);
};