diff options
author | 2020-09-07 15:24:36 +0000 | |
---|---|---|
committer | 2020-09-11 10:33:12 +0000 | |
commit | a41ea2708d143b5982f1969864513b62706d11d4 (patch) | |
tree | dfe5d58ffb7dd6d9220e87430416b8ea2deef617 | |
parent | 1e2d5679e56364b090c989427f43ad6273359904 (diff) |
arm64: VarHandle.get/set intrinsic for fields.
Test: testrunner.py --target --64 --optimizing -t 712-varhandle-invocation
Test: Repeat with ART_USE_READ_BARRIER=false ART_HEAP_POISONING=true.
Test: Repeat with ART_READ_BARRIER_TYPE=TABLELOOKUP.
Bug: 65872996
Change-Id: Ic0ec2eba7b493ef3b1d15291b67e0214e6e2de0e
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 11 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_arm64.cc | 419 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_utils.h | 21 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_x86.cc | 32 |
4 files changed, 381 insertions, 102 deletions
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 9b9a878ad6..95e2eeaddf 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -642,7 +642,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { instruction_->IsArrayGet() || instruction_->IsInstanceOf() || instruction_->IsCheckCast() || - (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified())) + (instruction_->IsInvoke() && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); // The read barrier instrumentation of object ArrayGet @@ -708,14 +708,15 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ Add(index_reg, index_reg, Operand(offset_)); } else { - // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile/VarHandleGet // intrinsics, `index_` is not shifted by a scale factor of 2 // (as in the case of ArrayGet), as it is actually an offset // to an object field within an object. DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || - (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) + (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile || + (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kVarHandleGet))) << instruction_->AsInvoke()->GetIntrinsic(); DCHECK_EQ(offset_, 0u); DCHECK(index_.IsRegister()); @@ -802,7 +803,9 @@ class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 { DataType::Type type = DataType::Type::kReference; DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg())); - DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString()) + DCHECK(instruction_->IsLoadClass() || + instruction_->IsLoadString() || + (instruction_->IsInvoke() && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for GC root slow path: " << instruction_->DebugName(); diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 9c98f8066b..29c9ee53ea 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -16,8 +16,10 @@ #include "intrinsics_arm64.h" +#include "arch/arm64/callee_save_frame_arm64.h" #include "arch/arm64/instruction_set_features_arm64.h" #include "art_method.h" +#include "base/bit_utils.h" #include "code_generator_arm64.h" #include "common_arm64.h" #include "data_type-inl.h" @@ -51,6 +53,8 @@ namespace arm64 { using helpers::DRegisterFrom; using helpers::HeapOperand; using helpers::LocationFrom; +using helpers::InputCPURegisterOrZeroRegAt; +using helpers::IsConstantZeroBitPattern; using helpers::OperandFrom; using helpers::RegisterFrom; using helpers::SRegisterFrom; @@ -3329,97 +3333,367 @@ void IntrinsicCodeGeneratorARM64::VisitFP16LessEquals(HInvoke* invoke) { GenerateFP16Compare(invoke, codegen_, masm, ls); } +// Check access mode and the primitive type from VarHandle.varType. +// The `var_type_no_rb`, if valid, shall be filled with VarHandle.varType read without read barrier. +static void GenerateVarHandleAccessModeAndVarTypeChecks(HInvoke* invoke, + CodeGeneratorARM64* codegen, + SlowPathCodeARM64* slow_path, + DataType::Type type, + Register var_type_no_rb = Register()) { + mirror::VarHandle::AccessMode access_mode = + mirror::VarHandle::GetAccessModeByIntrinsic(invoke->GetIntrinsic()); + Primitive::Type primitive_type = DataTypeToPrimitive(type); + + MacroAssembler* masm = codegen->GetVIXLAssembler(); + Register varhandle = InputRegisterAt(invoke, 0); + + const MemberOffset var_type_offset = mirror::VarHandle::VarTypeOffset(); + const MemberOffset access_mode_bit_mask_offset = mirror::VarHandle::AccessModesBitMaskOffset(); + const MemberOffset primitive_type_offset = mirror::Class::PrimitiveTypeOffset(); + + UseScratchRegisterScope temps(masm); + if (!var_type_no_rb.IsValid()) { + var_type_no_rb = temps.AcquireW(); + } + Register temp2 = temps.AcquireW(); + + // Check that the operation is permitted and the primitive type of varhandle.varType. + // We do not need a read barrier when loading a reference only for loading constant + // primitive field through the reference. Use LDP to load the fields together. + DCHECK_EQ(var_type_offset.Int32Value() + 4, access_mode_bit_mask_offset.Int32Value()); + __ Ldp(var_type_no_rb, temp2, HeapOperand(varhandle, var_type_offset.Int32Value())); + codegen->GetAssembler()->MaybeUnpoisonHeapReference(var_type_no_rb); + __ Tbz(temp2, static_cast<uint32_t>(access_mode), slow_path->GetEntryLabel()); + __ Ldrh(temp2, HeapOperand(var_type_no_rb, primitive_type_offset.Int32Value())); + __ Cmp(temp2, static_cast<uint16_t>(primitive_type)); + __ B(slow_path->GetEntryLabel(), ne); +} + +// Generate subtype check without read barriers. +static void GenerateSubTypeObjectCheckNoReadBarrier(CodeGeneratorARM64* codegen, + SlowPathCodeARM64* slow_path, + Register object, + Register type, + bool object_can_be_null = true) { + MacroAssembler* masm = codegen->GetVIXLAssembler(); + + const MemberOffset class_offset = mirror::Object::ClassOffset(); + const MemberOffset super_class_offset = mirror::Class::SuperClassOffset(); + + vixl::aarch64::Label success; + if (object_can_be_null) { + __ Cbz(object, &success); + } + + UseScratchRegisterScope temps(masm); + Register temp = temps.AcquireW(); + + __ Ldr(temp, HeapOperand(object, class_offset.Int32Value())); + codegen->GetAssembler()->MaybeUnpoisonHeapReference(temp); + vixl::aarch64::Label loop; + __ Bind(&loop); + __ Cmp(type, temp); // For heap poisoning, compare poisoned references. + __ B(&success, eq); + __ Ldr(temp, HeapOperand(temp, super_class_offset.Int32Value())); + codegen->GetAssembler()->MaybeUnpoisonHeapReference(temp); + __ Cbz(temp, slow_path->GetEntryLabel()); + __ B(&loop); + __ Bind(&success); +} + +static void GenerateVarHandleStaticFieldCheck(HInvoke* invoke, + CodeGeneratorARM64* codegen, + SlowPathCodeARM64* slow_path) { + MacroAssembler* masm = codegen->GetVIXLAssembler(); + Register varhandle = InputRegisterAt(invoke, 0); + + const MemberOffset coordinate_type0_offset = mirror::VarHandle::CoordinateType0Offset(); + + UseScratchRegisterScope temps(masm); + Register temp = temps.AcquireW(); + + // Check that the VarHandle references a static field by checking that coordinateType0 == null. + // Do not emit read barrier (or unpoison the reference) for comparing to null. + __ Ldr(temp, HeapOperand(varhandle, coordinate_type0_offset.Int32Value())); + __ Cbnz(temp, slow_path->GetEntryLabel()); +} + +static void GenerateVarHandleInstanceFieldCheck(HInvoke* invoke, + CodeGeneratorARM64* codegen, + SlowPathCodeARM64* slow_path) { + MacroAssembler* masm = codegen->GetVIXLAssembler(); + Register varhandle = InputRegisterAt(invoke, 0); + Register object = InputRegisterAt(invoke, 1); + + const MemberOffset coordinate_type0_offset = mirror::VarHandle::CoordinateType0Offset(); + const MemberOffset coordinate_type1_offset = mirror::VarHandle::CoordinateType1Offset(); + + UseScratchRegisterScope temps(masm); + Register temp = temps.AcquireW(); + Register temp2 = temps.AcquireW(); + + // Null-check the object. + __ Cbz(object, slow_path->GetEntryLabel()); + + // Check that the VarHandle references an instance field by checking that + // coordinateType1 == null. coordinateType0 should not be null, but this is handled by the + // type compatibility check with the source object's type, which will fail for null. + DCHECK_EQ(coordinate_type0_offset.Int32Value() + 4, coordinate_type1_offset.Int32Value()); + __ Ldp(temp, temp2, HeapOperand(varhandle, coordinate_type0_offset.Int32Value())); + __ Cbnz(temp2, slow_path->GetEntryLabel()); + + // Check that the object has the correct type. + // We deliberately avoid the read barrier, letting the slow path handle the false negatives. + temps.Release(temp2); // Needed by GenerateSubTypeObjectCheckNoReadBarrier(). + GenerateSubTypeObjectCheckNoReadBarrier( + codegen, slow_path, object, temp, /*object_can_be_null=*/ false); +} + +static void GenerateVarHandleFieldCheck(HInvoke* invoke, + CodeGeneratorARM64* codegen, + SlowPathCodeARM64* slow_path) { + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + DCHECK_LE(expected_coordinates_count, 1u); + if (expected_coordinates_count == 0u) { + GenerateVarHandleStaticFieldCheck(invoke, codegen, slow_path); + } else { + GenerateVarHandleInstanceFieldCheck(invoke, codegen, slow_path); + } +} + +static void GenerateVarHandleFieldReference(HInvoke* invoke, + CodeGeneratorARM64* codegen, + Register object, + Register offset) { + MacroAssembler* masm = codegen->GetVIXLAssembler(); + Register varhandle = InputRegisterAt(invoke, 0); + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + DCHECK_LE(expected_coordinates_count, 1u); + + // For static fields, we need to fill the `object` with the declaring class, so + // we can use `object` as temporary for the `ArtMethod*`. For instance fields, + // we do not need the declaring class, so we can forget the `ArtMethod*` when + // we load the `offset`, so use the `offset` to hold the `ArtMethod*`. + Register method = (expected_coordinates_count == 0) ? object : offset; + + const MemberOffset art_field_offset = mirror::FieldVarHandle::ArtFieldOffset(); + const MemberOffset offset_offset = ArtField::OffsetOffset(); + + // Load the ArtField, the offset and, if needed, declaring class. + __ Ldr(method.X(), HeapOperand(varhandle, art_field_offset.Int32Value())); + __ Ldr(offset, MemOperand(method.X(), offset_offset.Int32Value())); + if (expected_coordinates_count == 0u) { + codegen->GenerateGcRootFieldLoad(invoke, + LocationFrom(object), + method.X(), + ArtField::DeclaringClassOffset().Int32Value(), + /*fixup_label=*/ nullptr, + kCompilerReadBarrierOption); + } +} + void IntrinsicLocationsBuilderARM64::VisitVarHandleGet(HInvoke* invoke) { - if (invoke->GetNumberOfArguments() == 1u) { - // Static field Get. - switch (invoke->GetType()) { - case DataType::Type::kBool: - case DataType::Type::kInt8: - case DataType::Type::kUint16: - case DataType::Type::kInt16: - case DataType::Type::kInt32: - case DataType::Type::kInt64: { - ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator(); - LocationSummary* locations = new (allocator) LocationSummary( - invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); - break; - } - case DataType::Type::kFloat32: - case DataType::Type::kFloat64: { - ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator(); - LocationSummary* locations = new (allocator) LocationSummary( - invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); - break; - } - default: - // TODO: Implement for kReference. - break; - } + DataType::Type type = invoke->GetType(); + if (type == DataType::Type::kVoid) { + // Return type should not be void for get. + return; + } + + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + if (expected_coordinates_count > 1u) { + // Only field VarHandle is currently supported. + return; + } + if (expected_coordinates_count == 1u && + invoke->InputAt(1)->GetType() != DataType::Type::kReference) { + // For an instance field, the object must be a reference. + return; + } + + ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator(); + LocationSummary* locations = + new (allocator) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + if (expected_coordinates_count == 1u) { + // For instance fields, this is the source object. + locations->SetInAt(1, Location::RequiresRegister()); + } else { + // Add a temporary to hold the declaring class. + locations->AddTemp(Location::RequiresRegister()); + } + if (DataType::IsFloatingPointType(type)) { + locations->SetOut(Location::RequiresFpuRegister()); + } else { + locations->SetOut(Location::RequiresRegister()); + } + // Add a temporary for offset if we cannot use the output register. + if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) { + // To preserve the offset value across the non-Baker read barrier + // slow path, use a fixed callee-save register. + locations->AddTemp(Location::RegisterLocation(CTZ(kArm64CalleeSaveRefSpills))); + } else if (DataType::IsFloatingPointType(type)) { + locations->AddTemp(Location::RequiresRegister()); } - // TODO: Support instance fields, arrays, etc. } void IntrinsicCodeGeneratorARM64::VisitVarHandleGet(HInvoke* invoke) { - // Implemented only for primitive static fields. - DCHECK_EQ(invoke->GetNumberOfArguments(), 1u); + // Implemented only for fields. + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + DCHECK_LE(expected_coordinates_count, 1u); DataType::Type type = invoke->GetType(); DCHECK_NE(type, DataType::Type::kVoid); - DCHECK_NE(type, DataType::Type::kReference); - Primitive::Type primitive_type = DataTypeToPrimitive(type); + LocationSummary* locations = invoke->GetLocations(); MacroAssembler* masm = GetVIXLAssembler(); - Register varhandle = InputRegisterAt(invoke, 0).X(); CPURegister out = helpers::OutputCPURegister(invoke); - Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0)); SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke); codegen_->AddSlowPath(slow_path); - // Check that the operation is permitted. For static field Get, this - // should be the same as checking that the field is not volatile. - mirror::VarHandle::AccessMode access_mode = - mirror::VarHandle::GetAccessModeByIntrinsic(invoke->GetIntrinsic()); - __ Ldr(temp, MemOperand(varhandle, mirror::VarHandle::AccessModesBitMaskOffset().Int32Value())); - __ Tbz(temp, static_cast<uint32_t>(access_mode), slow_path->GetEntryLabel()); - - // Check the varType.primitiveType against the type we're trying to retrieve. We do not need a - // read barrier when loading a reference only for loading constant field through the reference. - __ Ldr(temp, MemOperand(varhandle, mirror::VarHandle::VarTypeOffset().Int32Value())); - codegen_->GetAssembler()->MaybeUnpoisonHeapReference(temp); - __ Ldrh(temp, MemOperand(temp.X(), mirror::Class::PrimitiveTypeOffset().Int32Value())); - __ Cmp(temp, static_cast<uint16_t>(primitive_type)); - __ B(slow_path->GetEntryLabel(), ne); + GenerateVarHandleFieldCheck(invoke, codegen_, slow_path); + GenerateVarHandleAccessModeAndVarTypeChecks(invoke, codegen_, slow_path, type); - // Check that the VarHandle references a static field by checking that coordinateType0 == null. - // Do not emit read barrier (or unpoison the reference) for comparing to null. - __ Ldr(temp, MemOperand(varhandle, mirror::VarHandle::CoordinateType0Offset().Int32Value())); - __ Cbnz(temp, slow_path->GetEntryLabel()); + // Use `out` for offset if it is a core register, except for non-Baker read barrier. + Register offset = + ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) || DataType::IsFloatingPointType(type)) + ? WRegisterFrom(locations->GetTemp(expected_coordinates_count == 0u ? 1u : 0u)) + : out.W(); + // The object reference from which to load the field. + Register object = (expected_coordinates_count == 0u) + ? WRegisterFrom(locations->GetTemp(0u)) + : InputRegisterAt(invoke, 1).W(); - // Use `out` for offset if it is a core register. - Register offset = DataType::IsFloatingPointType(type) - ? WRegisterFrom(invoke->GetLocations()->GetTemp(1)) - : out.W(); - - // Load the ArtField, the offset and declaring class. - __ Ldr(temp.X(), MemOperand(varhandle, mirror::FieldVarHandle::ArtFieldOffset().Int32Value())); - __ Ldr(offset, MemOperand(temp.X(), ArtField::OffsetOffset().Int32Value())); - codegen_->GenerateGcRootFieldLoad(invoke, - LocationFrom(temp), - temp.X(), - ArtField::DeclaringClassOffset().Int32Value(), - /*fixup_label=*/ nullptr, - kCompilerReadBarrierOption); + GenerateVarHandleFieldReference(invoke, codegen_, object, offset); // Load the value from the field. - codegen_->Load(type, out, MemOperand(temp.X(), offset.X())); + if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { + // Piggy-back on the field load path using introspection for the Baker read barrier. + // The `offset` is either the `out` or a temporary, use it for field address. + __ Add(offset.X(), object.X(), offset.X()); + codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke, + locations->Out(), + object, + MemOperand(offset.X()), + /*needs_null_check=*/ false, + /*is_volatile=*/ false); + } else { + codegen_->Load(type, out, MemOperand(object.X(), offset.X())); + if (type == DataType::Type::kReference) { + DCHECK(out.IsW()); + Location out_loc = locations->Out(); + Location object_loc = LocationFrom(object); + Location offset_loc = LocationFrom(offset); + codegen_->MaybeGenerateReadBarrierSlow(invoke, out_loc, out_loc, object_loc, 0u, offset_loc); + } + } + + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderARM64::VisitVarHandleSet(HInvoke* invoke) { + if (invoke->GetType() != DataType::Type::kVoid) { + // Return type should be void for set. + return; + } + + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + if (expected_coordinates_count > 1u) { + // Only field VarHandle is currently supported. + return; + } + if (expected_coordinates_count == 1u && + invoke->InputAt(1)->GetType() != DataType::Type::kReference) { + // For an instance field, the object must be a reference. + return; + } + + // The last argument should be the value we intend to set. + uint32_t value_index = invoke->GetNumberOfArguments() - 1; + HInstruction* value = invoke->InputAt(value_index); + DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index); + + ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator(); + LocationSummary* locations = + new (allocator) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) { + // To preserve the offset value across the non-Baker read barrier + // slow path, use a fixed callee-save register. + locations->AddTemp(Location::RegisterLocation(CTZ(kArm64CalleeSaveRefSpills))); + } else { + locations->AddTemp(Location::RequiresRegister()); // For offset. + } + if (expected_coordinates_count == 1u) { + // For instance fields, this is the source object. + locations->SetInAt(1, Location::RequiresRegister()); + } else { + // Add a temporary to hold the declaring class. + locations->AddTemp(Location::RequiresRegister()); + } + if (IsConstantZeroBitPattern(value)) { + locations->SetInAt(value_index, Location::ConstantLocation(value->AsConstant())); + } else if (DataType::IsFloatingPointType(value_type)) { + locations->SetInAt(value_index, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(value_index, Location::RequiresRegister()); + } +} + +void IntrinsicCodeGeneratorARM64::VisitVarHandleSet(HInvoke* invoke) { + // Implemented only for fields. + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + DCHECK_LE(expected_coordinates_count, 1u); + uint32_t value_index = invoke->GetNumberOfArguments() - 1; + DataType::Type value_type = GetDataTypeFromShorty(invoke, value_index); + + MacroAssembler* masm = GetVIXLAssembler(); + CPURegister value = InputCPURegisterOrZeroRegAt(invoke, value_index); + + SlowPathCodeARM64* slow_path = + new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + + GenerateVarHandleFieldCheck(invoke, codegen_, slow_path); + { + UseScratchRegisterScope temps(masm); + Register var_type_no_rb = temps.AcquireW(); + GenerateVarHandleAccessModeAndVarTypeChecks( + invoke, codegen_, slow_path, value_type, var_type_no_rb); + if (value_type == DataType::Type::kReference && !value.IsZero()) { + // If the value type is a reference, check it against the varType. + // False negatives due to varType being an interface or array type + // or due to the missing read barrier are handled by the slow path. + GenerateSubTypeObjectCheckNoReadBarrier(codegen_, slow_path, value.W(), var_type_no_rb); + } + } + + // The temporary allocated for loading `offset`. + Register offset = WRegisterFrom(invoke->GetLocations()->GetTemp(0u)); + // The object reference from which to load the field. + Register object = (expected_coordinates_count == 0u) + ? WRegisterFrom(invoke->GetLocations()->GetTemp(1u)) + : InputRegisterAt(invoke, 1).W(); + + GenerateVarHandleFieldReference(invoke, codegen_, object, offset); + + // Store the value to the field. + { + CPURegister source = value; + UseScratchRegisterScope temps(masm); + if (kPoisonHeapReferences && value_type == DataType::Type::kReference) { + DCHECK(value.IsW()); + Register temp = temps.AcquireW(); + __ Mov(temp, value.W()); + codegen_->GetAssembler()->PoisonHeapReference(temp); + source = temp; + } + codegen_->Store(value_type, source, MemOperand(object.X(), offset.X())); + } + + if (CodeGenerator::StoreNeedsWriteBarrier(value_type, invoke->InputAt(value_index))) { + codegen_->MarkGCCard(object, Register(value), /*value_can_be_null=*/ true); + } __ Bind(slow_path->GetExitLabel()); } @@ -3481,7 +3755,6 @@ UNIMPLEMENTED_INTRINSIC(ARM64, VarHandleGetAndSetAcquire) UNIMPLEMENTED_INTRINSIC(ARM64, VarHandleGetAndSetRelease) UNIMPLEMENTED_INTRINSIC(ARM64, VarHandleGetOpaque) UNIMPLEMENTED_INTRINSIC(ARM64, VarHandleGetVolatile) -UNIMPLEMENTED_INTRINSIC(ARM64, VarHandleSet) UNIMPLEMENTED_INTRINSIC(ARM64, VarHandleSetOpaque) UNIMPLEMENTED_INTRINSIC(ARM64, VarHandleSetRelease) UNIMPLEMENTED_INTRINSIC(ARM64, VarHandleSetVolatile) diff --git a/compiler/optimizing/intrinsics_utils.h b/compiler/optimizing/intrinsics_utils.h index 4b8a8e743d..d900b21073 100644 --- a/compiler/optimizing/intrinsics_utils.h +++ b/compiler/optimizing/intrinsics_utils.h @@ -20,7 +20,10 @@ #include "base/casts.h" #include "base/macros.h" #include "code_generator.h" +#include "data_type-inl.h" +#include "dex/dex_file-inl.h" #include "locations.h" +#include "mirror/var_handle.h" #include "nodes.h" #include "utils/assembler.h" #include "utils/label.h" @@ -93,6 +96,24 @@ class IntrinsicSlowPath : public TSlowPathCode { DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPath); }; +static inline size_t GetExpectedVarHandleCoordinatesCount(HInvoke *invoke) { + mirror::VarHandle::AccessModeTemplate access_mode_template = + mirror::VarHandle::GetAccessModeTemplateByIntrinsic(invoke->GetIntrinsic()); + size_t var_type_count = mirror::VarHandle::GetNumberOfVarTypeParameters(access_mode_template); + size_t accessor_argument_count = invoke->GetNumberOfArguments() - 1; + + return accessor_argument_count - var_type_count; +} + +static inline DataType::Type GetDataTypeFromShorty(HInvoke* invoke, uint32_t index) { + DCHECK(invoke->IsInvokePolymorphic()); + const DexFile& dex_file = invoke->GetBlock()->GetGraph()->GetDexFile(); + const char* shorty = dex_file.GetShorty(invoke->AsInvokePolymorphic()->GetProtoIndex()); + DCHECK_LT(index, strlen(shorty)); + + return DataType::FromShorty(shorty[index]); +} + } // namespace art #endif // ART_COMPILER_OPTIMIZING_INTRINSICS_UTILS_H_ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 47e5864d32..c01782a01c 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -3066,26 +3066,8 @@ void IntrinsicCodeGeneratorX86::VisitIntegerDivideUnsigned(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } -static uint32_t GetExpectedVarHandleCoordinatesCount(HInvoke *invoke) { - mirror::VarHandle::AccessModeTemplate access_mode_template = - mirror::VarHandle::GetAccessModeTemplateByIntrinsic(invoke->GetIntrinsic()); - uint32_t var_type_count = mirror::VarHandle::GetNumberOfVarTypeParameters(access_mode_template); - uint32_t accessor_argument_count = invoke->GetNumberOfArguments() - 1; - - return accessor_argument_count - var_type_count; -} - -static DataType::Type GetDataTypeFromShorty(HInvoke* invoke, uint32_t index) { - DCHECK(invoke->IsInvokePolymorphic()); - const DexFile& dex_file = invoke->GetBlock()->GetGraph()->GetDexFile(); - const char* shorty = dex_file.GetShorty(invoke->AsInvokePolymorphic()->GetProtoIndex()); - DCHECK_LT(index, strlen(shorty)); - - return DataType::FromShorty(shorty[index]); -} - static bool IsValidFieldVarHandleExpected(HInvoke* invoke) { - uint32_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); if (expected_coordinates_count > 1u) { // Only static and instance fields VarHandle are supported now. return false; @@ -3234,7 +3216,7 @@ static void GenerateVarHandleCommonChecks(HInvoke *invoke, slow_path, assembler); - uint32_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); switch (expected_coordinates_count) { case 0u: GenerateVarHandleStaticFieldCheck(vh_object, slow_path, assembler); @@ -3285,7 +3267,7 @@ static Register GenerateVarHandleFieldReference(HInvoke* invoke, // Load the ArtField and the offset __ movl(temp, Address(varhandle_object, artfield_offset)); __ movl(offset, Address(temp, offset_offset)); - uint32_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); if (expected_coordinates_count == 0) { // For static fields, load the declaring class InstructionCodeGeneratorX86* instr_codegen = @@ -3319,7 +3301,7 @@ void IntrinsicLocationsBuilderX86::VisitVarHandleGet(HInvoke* invoke) { LocationSummary* locations = new (allocator) LocationSummary( invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); - uint32_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); if (expected_coordinates_count == 1u) { // For instance fields, this is the source object. locations->SetInAt(1, Location::RequiresRegister()); @@ -3422,7 +3404,7 @@ void IntrinsicLocationsBuilderX86::VisitVarHandleSet(HInvoke* invoke) { // This temporary register is also used for card for MarkGCCard. Make sure it's a byte register locations->AddTemp(Location::RegisterLocation(EAX)); locations->SetInAt(0, Location::RequiresRegister()); - uint32_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); if (expected_coordinates_count == 1u) { // For instance fields, this is the source object locations->SetInAt(1, Location::RequiresRegister()); @@ -3501,7 +3483,7 @@ void IntrinsicCodeGeneratorX86::VisitVarHandleSet(HInvoke* invoke) { // Store the value to the field CodeGeneratorX86* codegen_x86 = down_cast<CodeGeneratorX86*>(codegen_); if (value_type == DataType::Type::kReference) { - uint32_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(value_type, invoke->InputAt(value_index)); Register value_reg = value.AsRegister<Register>(); @@ -3678,7 +3660,7 @@ void IntrinsicCodeGeneratorX86::VisitVarHandleCompareAndSet(HInvoke* invoke) { Register new_value = new_value_loc.AsRegister<Register>(); bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(new_value_type, invoke->InputAt(new_value_index)); - uint32_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); + size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke); temp = expected_coordinates_count == 1u ? temp : locations->GetTemp(2).AsRegister<Register>(); if (kCompilerReadBarrierOption == kWithReadBarrier) { |