in compiler/optimizing/code_generator_x86.cc [5311:5564]
void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location array_loc = locations->InAt(0);
Register array = array_loc.AsRegister<Register>();
Location index = locations->InAt(1);
Location value = locations->InAt(2);
Primitive::Type value_type = instruction->GetComponentType();
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_for_type_check = instruction->NeedsTypeCheck();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
switch (value_type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte: {
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 {
__ movb(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
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 {
__ movw(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
case Primitive::kPrimNot: {
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);
DCHECK(!needs_write_barrier);
DCHECK(!may_need_runtime_call_for_type_check);
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_for_type_check) {
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);
}
if (kEmitCompilerReadBarrier) {
// When read barriers are enabled, the type checking
// instrumentation requires two read barriers:
//
// __ movl(temp2, temp);
// // /* HeapReference<Class> */ temp = temp->component_type_
// __ movl(temp, Address(temp, component_offset));
// codegen_->GenerateReadBarrierSlow(
// instruction, temp_loc, temp_loc, temp2_loc, component_offset);
//
// // /* HeapReference<Class> */ temp2 = register_value->klass_
// __ movl(temp2, Address(register_value, class_offset));
// codegen_->GenerateReadBarrierSlow(
// instruction, temp2_loc, temp2_loc, value, class_offset, temp_loc);
//
// __ cmpl(temp, temp2);
//
// However, the second read barrier may trash `temp`, as it
// is a temporary register, and as such would not be saved
// along with live registers before calling the runtime (nor
// restored afterwards). So in this case, we bail out and
// delegate the work to the array set slow path.
//
// TODO: Extend the register allocator to support a new
// "(locally) live temp" location so as to avoid always
// going into the slow path when read barriers are enabled.
__ jmp(slow_path->GetEntryLabel());
} else {
// /* HeapReference<Class> */ temp = array->klass_
__ movl(temp, Address(array, class_offset));
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ MaybeUnpoisonHeapReference(temp);
// /* HeapReference<Class> */ temp = temp->component_type_
__ movl(temp, Address(temp, component_offset));
// If heap poisoning is enabled, no need to unpoison `temp`
// nor the object reference in `register_value->klass`, as
// we are comparing two poisoned references.
__ cmpl(temp, Address(register_value, class_offset));
if (instruction->StaticTypeOfArrayIsObjectArray()) {
__ j(kEqual, &do_put);
// If heap poisoning is enabled, the `temp` reference has
// not been unpoisoned yet; unpoison it now.
__ MaybeUnpoisonHeapReference(temp);
// /* HeapReference<Class> */ temp = temp->super_class_
__ movl(temp, Address(temp, super_offset));
// If heap poisoning is enabled, no need to unpoison
// `temp`, as we are comparing against null below.
__ 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_for_type_check) {
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;
}
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.IsRegisterPair()) {
__ movl(Address(array, offset), value.AsRegisterPairLow<Register>());
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(Address(array, offset + kX86WordSize), value.AsRegisterPairHigh<Register>());
} else {
DCHECK(value.IsConstant());
int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
__ movl(Address(array, offset), Immediate(Low32Bits(val)));
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(Address(array, offset + kX86WordSize), Immediate(High32Bits(val)));
}
} else {
if (value.IsRegisterPair()) {
__ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset),
value.AsRegisterPairLow<Register>());
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ 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(array, index.AsRegister<Register>(), TIMES_8, data_offset),
Immediate(Low32Bits(val)));
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
Immediate(High32Bits(val)));
}
}
break;
}
case Primitive::kPrimFloat: {
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);
if (value.IsFpuRegister()) {
__ movss(address, value.AsFpuRegister<XmmRegister>());
} else {
DCHECK(value.IsConstant());
int32_t v = bit_cast<int32_t, float>(value.GetConstant()->AsFloatConstant()->GetValue());
__ movl(address, Immediate(v));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
case Primitive::kPrimDouble: {
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);
if (value.IsFpuRegister()) {
__ movsd(address, value.AsFpuRegister<XmmRegister>());
} else {
DCHECK(value.IsConstant());
Address address_hi = index.IsConstant() ?
Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) +
offset + kX86WordSize) :
Address(array, index.AsRegister<Register>(), TIMES_8, offset + kX86WordSize);
int64_t v = bit_cast<int64_t, double>(value.GetConstant()->AsDoubleConstant()->GetValue());
__ movl(address, Immediate(Low32Bits(v)));
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(address_hi, Immediate(High32Bits(v)));
}
break;
}
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
}
}