in compiler/optimizing/code_generator_arm.cc [4549:4806]
void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location array_loc = locations->InAt(0);
Register array = array_loc.AsRegister<Register>();
Location index = locations->InAt(1);
Primitive::Type value_type = instruction->GetComponentType();
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 data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Register value = locations->InAt(2).AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
__ StoreToOffset(kStoreByte, value, array, offset);
} else {
__ add(IP, array, ShifterOperand(index.AsRegister<Register>()));
__ StoreToOffset(kStoreByte, value, IP, data_offset);
}
break;
}
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
Register value = locations->InAt(2).AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
__ StoreToOffset(kStoreHalfword, value, array, offset);
} else {
__ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2));
__ StoreToOffset(kStoreHalfword, value, IP, data_offset);
}
break;
}
case Primitive::kPrimNot: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
Location value_loc = locations->InAt(2);
Register value = value_loc.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, array, offset);
} else {
DCHECK(index.IsRegister()) << index;
__ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
__ StoreToOffset(kStoreWord, source, IP, data_offset);
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
DCHECK(!needs_write_barrier);
DCHECK(!may_need_runtime_call_for_type_check);
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_for_type_check) {
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);
}
if (kEmitCompilerReadBarrier) {
// When read barriers are enabled, the type checking
// instrumentation requires two read barriers:
//
// __ Mov(temp2, temp1);
// // /* HeapReference<Class> */ temp1 = temp1->component_type_
// __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
// codegen_->GenerateReadBarrierSlow(
// instruction, temp1_loc, temp1_loc, temp2_loc, component_offset);
//
// // /* HeapReference<Class> */ temp2 = value->klass_
// __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
// codegen_->GenerateReadBarrierSlow(
// instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc);
//
// __ cmp(temp1, ShifterOperand(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.
__ b(slow_path->GetEntryLabel());
} else {
// /* HeapReference<Class> */ temp1 = array->klass_
__ LoadFromOffset(kLoadWord, temp1, array, class_offset);
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ MaybeUnpoisonHeapReference(temp1);
// /* HeapReference<Class> */ temp1 = temp1->component_type_
__ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
// /* HeapReference<Class> */ temp2 = value->klass_
__ LoadFromOffset(kLoadWord, temp2, value, class_offset);
// If heap poisoning is enabled, no need to unpoison `temp1`
// nor `temp2`, as we are comparing two poisoned references.
__ cmp(temp1, ShifterOperand(temp2));
if (instruction->StaticTypeOfArrayIsObjectArray()) {
Label do_put;
__ b(&do_put, EQ);
// If heap poisoning is enabled, the `temp1` reference has
// not been unpoisoned yet; unpoison it now.
__ MaybeUnpoisonHeapReference(temp1);
// /* HeapReference<Class> */ temp1 = temp1->super_class_
__ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
// If heap poisoning is enabled, no need to unpoison
// `temp1`, as we are comparing against null below.
__ 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_for_type_check) {
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);
}
break;
}
case Primitive::kPrimLong: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
Location value = locations->InAt(2);
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
__ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset);
} else {
__ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
__ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset);
}
break;
}
case Primitive::kPrimFloat: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
Location value = locations->InAt(2);
DCHECK(value.IsFpuRegister());
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
__ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset);
} else {
__ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
__ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset);
}
break;
}
case Primitive::kPrimDouble: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
Location value = locations->InAt(2);
DCHECK(value.IsFpuRegisterPair());
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
__ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset);
} else {
__ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
__ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
}
break;
}
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable type " << value_type;
UNREACHABLE();
}
// Objects are handled in the switch.
if (value_type != Primitive::kPrimNot) {
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
}