Refactor Baker CC read barrier link-time thunk generation.
Do not keep any knowledge of Baker thunk type in the
ArmBaseRelativePatcher, keep it strictly within the
derived classes.
Test: Nexus 6P boots.
Test: testrunner.py --target on Nexus 6P
Bug: 29516974
Bug: 30126666
Bug: 36141117
Change-Id: I7d9efcd2df1439bb51985467c5e3e53ddeb5a293
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index f55d5a6..e9d579d 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -249,7 +249,7 @@
// All remaining method call patches will be handled by this thunk.
DCHECK(!unprocessed_method_call_patches_.empty());
DCHECK_LE(thunk_offset - unprocessed_method_call_patches_.front().GetPatchOffset(),
- MaxPositiveDisplacement(ThunkType::kMethodCall));
+ MaxPositiveDisplacement(GetMethodCallKey()));
unprocessed_method_call_patches_.clear();
}
}
@@ -271,8 +271,8 @@
DCHECK(method_call_thunk_ != nullptr);
// Unsigned arithmetic with its well-defined overflow behavior is just fine here.
uint32_t displacement = target_offset - patch_offset;
- uint32_t max_positive_displacement = MaxPositiveDisplacement(ThunkType::kMethodCall);
- uint32_t max_negative_displacement = MaxNegativeDisplacement(ThunkType::kMethodCall);
+ uint32_t max_positive_displacement = MaxPositiveDisplacement(GetMethodCallKey());
+ uint32_t max_negative_displacement = MaxNegativeDisplacement(GetMethodCallKey());
// NOTE: With unsigned arithmetic we do mean to use && rather than || below.
if (displacement > max_positive_displacement && displacement < -max_negative_displacement) {
// Unwritten thunks have higher offsets, check if it's within range.
@@ -299,29 +299,42 @@
if (data.HasWrittenOffset()) {
uint32_t offset = data.LastWrittenOffset();
DCHECK_LT(offset, patch_offset);
- if (patch_offset - offset <= MaxNegativeDisplacement(key.GetType())) {
+ if (patch_offset - offset <= MaxNegativeDisplacement(key)) {
return offset;
}
}
DCHECK(data.HasPendingOffset());
uint32_t offset = data.GetPendingOffset();
DCHECK_GT(offset, patch_offset);
- DCHECK_LE(offset - patch_offset, MaxPositiveDisplacement(key.GetType()));
+ DCHECK_LE(offset - patch_offset, MaxPositiveDisplacement(key));
return offset;
}
+ArmBaseRelativePatcher::ThunkKey ArmBaseRelativePatcher::GetMethodCallKey() {
+ return ThunkKey(ThunkType::kMethodCall, ThunkParams{{ 0u, 0u }}); // NOLINT(whitespace/braces)
+}
+
+ArmBaseRelativePatcher::ThunkKey ArmBaseRelativePatcher::GetBakerThunkKey(
+ const LinkerPatch& patch) {
+ DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kBakerReadBarrierBranch);
+ ThunkParams params;
+ params.baker_params.custom_value1 = patch.GetBakerCustomValue1();
+ params.baker_params.custom_value2 = patch.GetBakerCustomValue2();
+ ThunkKey key(ThunkType::kBakerReadBarrier, params);
+ return key;
+}
+
void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_method,
uint32_t code_offset) {
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
uint32_t patch_offset = code_offset + patch.LiteralOffset();
- ThunkType key_type = static_cast<ThunkType>(-1);
+ ThunkKey key(static_cast<ThunkType>(-1), ThunkParams{{ 0u, 0u }}); // NOLINT(whitespace/braces)
ThunkData* old_data = nullptr;
if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
- key_type = ThunkType::kMethodCall;
+ key = GetMethodCallKey();
unprocessed_method_call_patches_.emplace_back(patch_offset, patch.TargetMethod());
if (method_call_thunk_ == nullptr) {
- ThunkKey key(key_type, ThunkParams{{ 0u, 0u }}); // NOLINT(whitespace/braces)
- uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key_type);
+ uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key);
auto it = thunks_.Put(key, ThunkData(CompileThunk(key), max_next_offset));
method_call_thunk_ = &it->second;
AddUnreservedThunk(method_call_thunk_);
@@ -329,11 +342,10 @@
old_data = method_call_thunk_;
}
} else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) {
- ThunkKey key = GetBakerReadBarrierKey(patch);
- key_type = key.GetType();
+ key = GetBakerThunkKey(patch);
auto lb = thunks_.lower_bound(key);
if (lb == thunks_.end() || thunks_.key_comp()(key, lb->first)) {
- uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key_type);
+ uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key);
auto it = thunks_.PutBefore(lb, key, ThunkData(CompileThunk(key), max_next_offset));
AddUnreservedThunk(&it->second);
} else {
@@ -342,16 +354,16 @@
}
if (old_data != nullptr) {
// Shared path where an old thunk may need an update.
- DCHECK(key_type != static_cast<ThunkType>(-1));
+ DCHECK(key.GetType() != static_cast<ThunkType>(-1));
DCHECK(!old_data->HasReservedOffset() || old_data->LastReservedOffset() < patch_offset);
if (old_data->NeedsNextThunk()) {
// Patches for a method are ordered by literal offset, so if we still need to place
// this thunk for a previous patch, that thunk shall be in range for this patch.
- DCHECK_LE(old_data->MaxNextOffset(), CalculateMaxNextOffset(patch_offset, key_type));
+ DCHECK_LE(old_data->MaxNextOffset(), CalculateMaxNextOffset(patch_offset, key));
} else {
if (!old_data->HasReservedOffset() ||
- patch_offset - old_data->LastReservedOffset() > MaxNegativeDisplacement(key_type)) {
- old_data->SetMaxNextOffset(CalculateMaxNextOffset(patch_offset, key_type));
+ patch_offset - old_data->LastReservedOffset() > MaxNegativeDisplacement(key)) {
+ old_data->SetMaxNextOffset(CalculateMaxNextOffset(patch_offset, key));
AddUnreservedThunk(old_data);
}
}
@@ -385,8 +397,8 @@
DCHECK(!unreserved_thunks_.empty());
DCHECK(!unprocessed_method_call_patches_.empty());
DCHECK(method_call_thunk_ != nullptr);
- uint32_t max_positive_displacement = MaxPositiveDisplacement(ThunkType::kMethodCall);
- uint32_t max_negative_displacement = MaxNegativeDisplacement(ThunkType::kMethodCall);
+ uint32_t max_positive_displacement = MaxPositiveDisplacement(GetMethodCallKey());
+ uint32_t max_negative_displacement = MaxNegativeDisplacement(GetMethodCallKey());
// Process as many patches as possible, stop only on unresolved targets or calls too far back.
while (!unprocessed_method_call_patches_.empty()) {
MethodReference target_method = unprocessed_method_call_patches_.front().GetTargetMethod();
@@ -439,8 +451,8 @@
}
inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_offset,
- ThunkType type) {
- return RoundDown(patch_offset + MaxPositiveDisplacement(type),
+ const ThunkKey& key) {
+ return RoundDown(patch_offset + MaxPositiveDisplacement(key),
GetInstructionSetAlignment(instruction_set_));
}
diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h
index 47f840f..fd204c0 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.h
+++ b/compiler/linker/arm/relative_patcher_arm_base.h
@@ -42,29 +42,12 @@
enum class ThunkType {
kMethodCall, // Method call thunk.
- kBakerReadBarrierField, // Baker read barrier, load field or array element at known offset.
- kBakerReadBarrierArray, // Baker read barrier, array load with index in register.
- kBakerReadBarrierRoot, // Baker read barrier, GC root load.
+ kBakerReadBarrier, // Baker read barrier.
};
- struct BakerReadBarrierFieldParams {
- uint32_t holder_reg; // Holder object for reading lock word.
- uint32_t base_reg; // Base register, different from holder for large offset.
- // If base differs from holder, it should be a pre-defined
- // register to limit the number of thunks we need to emit.
- // The offset is retrieved using introspection.
- };
-
- struct BakerReadBarrierArrayParams {
- uint32_t base_reg; // Reference to the start of the data.
- uint32_t dummy; // Dummy field.
- // The index register is retrieved using introspection
- // to limit the number of thunks we need to emit.
- };
-
- struct BakerReadBarrierRootParams {
- uint32_t root_reg; // The register holding the GC root.
- uint32_t dummy; // Dummy field.
+ struct BakerReadBarrierParams {
+ uint32_t custom_value1;
+ uint32_t custom_value2;
};
struct RawThunkParams {
@@ -74,12 +57,8 @@
union ThunkParams {
RawThunkParams raw_params;
- BakerReadBarrierFieldParams field_params;
- BakerReadBarrierArrayParams array_params;
- BakerReadBarrierRootParams root_params;
- static_assert(sizeof(raw_params) == sizeof(field_params), "field_params size check");
- static_assert(sizeof(raw_params) == sizeof(array_params), "array_params size check");
- static_assert(sizeof(raw_params) == sizeof(root_params), "root_params size check");
+ BakerReadBarrierParams baker_params;
+ static_assert(sizeof(raw_params) == sizeof(baker_params), "baker_params size check");
};
class ThunkKey {
@@ -90,19 +69,9 @@
return type_;
}
- BakerReadBarrierFieldParams GetFieldParams() const {
- DCHECK(type_ == ThunkType::kBakerReadBarrierField);
- return params_.field_params;
- }
-
- BakerReadBarrierArrayParams GetArrayParams() const {
- DCHECK(type_ == ThunkType::kBakerReadBarrierArray);
- return params_.array_params;
- }
-
- BakerReadBarrierRootParams GetRootParams() const {
- DCHECK(type_ == ThunkType::kBakerReadBarrierRoot);
- return params_.root_params;
+ BakerReadBarrierParams GetBakerReadBarrierParams() const {
+ DCHECK(type_ == ThunkType::kBakerReadBarrier);
+ return params_.baker_params;
}
RawThunkParams GetRawParams() const {
@@ -127,6 +96,9 @@
}
};
+ static ThunkKey GetMethodCallKey();
+ static ThunkKey GetBakerThunkKey(const LinkerPatch& patch);
+
uint32_t ReserveSpaceInternal(uint32_t offset,
const CompiledMethod* compiled_method,
MethodReference method_ref,
@@ -136,10 +108,9 @@
uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset,
uint32_t target_offset);
- virtual ThunkKey GetBakerReadBarrierKey(const LinkerPatch& patch) = 0;
virtual std::vector<uint8_t> CompileThunk(const ThunkKey& key) = 0;
- virtual uint32_t MaxPositiveDisplacement(ThunkType type) = 0;
- virtual uint32_t MaxNegativeDisplacement(ThunkType type) = 0;
+ virtual uint32_t MaxPositiveDisplacement(const ThunkKey& key) = 0;
+ virtual uint32_t MaxNegativeDisplacement(const ThunkKey& key) = 0;
private:
class ThunkData;
@@ -149,7 +120,7 @@
void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref);
- uint32_t CalculateMaxNextOffset(uint32_t patch_offset, ThunkType type);
+ uint32_t CalculateMaxNextOffset(uint32_t patch_offset, const ThunkKey& key);
RelativePatcherTargetProvider* const provider_;
const InstructionSet instruction_set_;
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index f2ccc4b..ced52ff 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -105,32 +105,37 @@
DCHECK_LT(literal_offset, code->size());
uint32_t insn = GetInsn32(code, literal_offset);
DCHECK_EQ(insn, 0xf0408000); // BNE +0 (unpatched)
- ThunkKey key = GetBakerReadBarrierKey(patch);
+ ThunkKey key = GetBakerThunkKey(patch);
if (kIsDebugBuild) {
+ const uint32_t encoded_data = key.GetBakerReadBarrierParams().custom_value1;
+ BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
// Check that the next instruction matches the expected LDR.
- switch (key.GetType()) {
- case ThunkType::kBakerReadBarrierField: {
+ switch (kind) {
+ case BakerReadBarrierKind::kField: {
DCHECK_GE(code->size() - literal_offset, 8u);
uint32_t next_insn = GetInsn32(code, literal_offset + 4u);
// LDR (immediate) with correct base_reg.
CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
- CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (key.GetFieldParams().base_reg << 16));
+ const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
+ CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16));
break;
}
- case ThunkType::kBakerReadBarrierArray: {
+ case BakerReadBarrierKind::kArray: {
DCHECK_GE(code->size() - literal_offset, 8u);
uint32_t next_insn = GetInsn32(code, literal_offset + 4u);
// LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]).
CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
- CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (key.GetArrayParams().base_reg << 16));
+ const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
+ CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16));
CheckValidReg(next_insn & 0xf); // Check index register
break;
}
- case ThunkType::kBakerReadBarrierRoot: {
+ case BakerReadBarrierKind::kGcRoot: {
DCHECK_GE(literal_offset, 4u);
uint32_t prev_insn = GetInsn32(code, literal_offset - 4u);
// LDR (immediate) with correct root_reg.
- CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (key.GetRootParams().root_reg << 12));
+ const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
+ CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12));
break;
}
default:
@@ -150,49 +155,6 @@
SetInsn32(code, literal_offset, insn);
}
-ArmBaseRelativePatcher::ThunkKey Thumb2RelativePatcher::GetBakerReadBarrierKey(
- const LinkerPatch& patch) {
- DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kBakerReadBarrierBranch);
- uint32_t value = patch.GetBakerCustomValue1();
- BakerReadBarrierKind type = BakerReadBarrierKindField::Decode(value);
- ThunkParams params;
- switch (type) {
- case BakerReadBarrierKind::kField:
- params.field_params.base_reg = BakerReadBarrierFirstRegField::Decode(value);
- CheckValidReg(params.field_params.base_reg);
- params.field_params.holder_reg = BakerReadBarrierSecondRegField::Decode(value);
- CheckValidReg(params.field_params.holder_reg);
- break;
- case BakerReadBarrierKind::kArray:
- params.array_params.base_reg = BakerReadBarrierFirstRegField::Decode(value);
- CheckValidReg(params.array_params.base_reg);
- params.array_params.dummy = 0u;
- DCHECK_EQ(BakerReadBarrierSecondRegField::Decode(value), kInvalidEncodedReg);
- break;
- case BakerReadBarrierKind::kGcRoot:
- params.root_params.root_reg = BakerReadBarrierFirstRegField::Decode(value);
- CheckValidReg(params.root_params.root_reg);
- params.root_params.dummy = 0u;
- DCHECK_EQ(BakerReadBarrierSecondRegField::Decode(value), kInvalidEncodedReg);
- break;
- default:
- LOG(FATAL) << "Unexpected type: " << static_cast<uint32_t>(type);
- UNREACHABLE();
- }
- constexpr uint8_t kTypeTranslationOffset = 1u;
- static_assert(static_cast<uint32_t>(BakerReadBarrierKind::kField) + kTypeTranslationOffset ==
- static_cast<uint32_t>(ThunkType::kBakerReadBarrierField),
- "Thunk type translation check.");
- static_assert(static_cast<uint32_t>(BakerReadBarrierKind::kArray) + kTypeTranslationOffset ==
- static_cast<uint32_t>(ThunkType::kBakerReadBarrierArray),
- "Thunk type translation check.");
- static_assert(static_cast<uint32_t>(BakerReadBarrierKind::kGcRoot) + kTypeTranslationOffset ==
- static_cast<uint32_t>(ThunkType::kBakerReadBarrierRoot),
- "Thunk type translation check.");
- return ThunkKey(static_cast<ThunkType>(static_cast<uint32_t>(type) + kTypeTranslationOffset),
- params);
-}
-
#define __ assembler.GetVIXLAssembler()->
static void EmitGrayCheckAndFastPath(arm::ArmVIXLAssembler& assembler,
@@ -223,30 +185,20 @@
// Note: The fake dependency is unnecessary for the slow path.
}
-std::vector<uint8_t> Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) {
+void Thumb2RelativePatcher::CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler,
+ uint32_t encoded_data) {
using namespace vixl::aarch32; // NOLINT(build/namespaces)
- ArenaPool pool;
- ArenaAllocator arena(&pool);
- arm::ArmVIXLAssembler assembler(&arena);
-
- switch (key.GetType()) {
- case ThunkType::kMethodCall:
- // The thunk just uses the entry point in the ArtMethod. This works even for calls
- // to the generic JNI and interpreter trampolines.
- assembler.LoadFromOffset(
- arm::kLoadWord,
- vixl::aarch32::pc,
- vixl::aarch32::r0,
- ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
- __ Bkpt(0);
- break;
- case ThunkType::kBakerReadBarrierField: {
+ BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
+ switch (kind) {
+ case BakerReadBarrierKind::kField: {
// Check if the holder is gray and, if not, add fake dependency to the base register
// and return to the LDR instruction to load the reference. Otherwise, use introspection
// to load the reference and call the entrypoint (in kBakerCcEntrypointRegister)
// that performs further checks on the reference and marks it if needed.
- Register holder_reg(key.GetFieldParams().holder_reg);
- Register base_reg(key.GetFieldParams().base_reg);
+ Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
+ CheckValidReg(base_reg.GetCode());
+ Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data));
+ CheckValidReg(holder_reg.GetCode());
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip);
// If base_reg differs from holder_reg, the offset was too large and we must have
@@ -277,8 +229,10 @@
}
break;
}
- case ThunkType::kBakerReadBarrierArray: {
- Register base_reg(key.GetArrayParams().base_reg);
+ case BakerReadBarrierKind::kArray: {
+ Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
+ CheckValidReg(base_reg.GetCode());
+ DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip);
vixl::aarch32::Label slow_path;
@@ -299,12 +253,14 @@
__ Bx(ep_reg); // Jump to the entrypoint's array switch case.
break;
}
- case ThunkType::kBakerReadBarrierRoot: {
+ case BakerReadBarrierKind::kGcRoot: {
// Check if the reference needs to be marked and if so (i.e. not null, not marked yet
// and it does not have a forwarding address), call the correct introspection entrypoint;
// otherwise return the reference (or the extracted forwarding address).
// There is no gray bit check for GC roots.
- Register root_reg(key.GetRootParams().root_reg);
+ Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
+ CheckValidReg(root_reg.GetCode());
+ DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip);
vixl::aarch32::Label return_label, not_marked, forwarding_address;
@@ -332,6 +288,31 @@
__ Bx(lr);
break;
}
+ default:
+ LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
+ UNREACHABLE();
+ }
+}
+
+std::vector<uint8_t> Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ arm::ArmVIXLAssembler assembler(&arena);
+
+ switch (key.GetType()) {
+ case ThunkType::kMethodCall:
+ // The thunk just uses the entry point in the ArtMethod. This works even for calls
+ // to the generic JNI and interpreter trampolines.
+ assembler.LoadFromOffset(
+ arm::kLoadWord,
+ vixl::aarch32::pc,
+ vixl::aarch32::r0,
+ ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
+ __ Bkpt(0);
+ break;
+ case ThunkType::kBakerReadBarrier:
+ CompileBakerReadBarrierThunk(assembler, key.GetBakerReadBarrierParams().custom_value1);
+ break;
}
assembler.FinalizeCode();
@@ -343,24 +324,20 @@
#undef __
-uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(ThunkType type) {
- switch (type) {
+uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) {
+ switch (key.GetType()) {
case ThunkType::kMethodCall:
return kMaxMethodCallPositiveDisplacement;
- case ThunkType::kBakerReadBarrierField:
- case ThunkType::kBakerReadBarrierArray:
- case ThunkType::kBakerReadBarrierRoot:
+ case ThunkType::kBakerReadBarrier:
return kMaxBcondPositiveDisplacement;
}
}
-uint32_t Thumb2RelativePatcher::MaxNegativeDisplacement(ThunkType type) {
- switch (type) {
+uint32_t Thumb2RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) {
+ switch (key.GetType()) {
case ThunkType::kMethodCall:
return kMaxMethodCallNegativeDisplacement;
- case ThunkType::kBakerReadBarrierField:
- case ThunkType::kBakerReadBarrierArray:
- case ThunkType::kBakerReadBarrierRoot:
+ case ThunkType::kBakerReadBarrier:
return kMaxBcondNegativeDisplacement;
}
}
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
index 9eb0689..7fad245 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -24,19 +24,17 @@
#include "linker/arm/relative_patcher_arm_base.h"
namespace art {
+
+namespace arm {
+class ArmVIXLAssembler;
+} // namespace arm
+
namespace linker {
class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher {
public:
static constexpr uint32_t kBakerCcEntrypointRegister = 4u;
- enum class BakerReadBarrierKind : uint8_t {
- kField, // Field get or array get with constant offset (i.e. constant index).
- kArray, // Array get with index in register.
- kGcRoot, // GC root load.
- kLast
- };
-
static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) {
CheckValidReg(base_reg);
CheckValidReg(holder_reg);
@@ -74,14 +72,20 @@
uint32_t patch_offset) OVERRIDE;
protected:
- ThunkKey GetBakerReadBarrierKey(const LinkerPatch& patch) OVERRIDE;
std::vector<uint8_t> CompileThunk(const ThunkKey& key) OVERRIDE;
- uint32_t MaxPositiveDisplacement(ThunkType type) OVERRIDE;
- uint32_t MaxNegativeDisplacement(ThunkType type) OVERRIDE;
+ uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE;
+ uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE;
private:
static constexpr uint32_t kInvalidEncodedReg = /* pc is invalid */ 15u;
+ enum class BakerReadBarrierKind : uint8_t {
+ kField, // Field get or array get with constant offset (i.e. constant index).
+ kArray, // Array get with index in register.
+ kGcRoot, // GC root load.
+ kLast
+ };
+
static constexpr size_t kBitsForBakerReadBarrierKind =
MinimumBitsToStore(static_cast<size_t>(BakerReadBarrierKind::kLast));
static constexpr size_t kBitsForRegister = 4u;
@@ -96,6 +100,8 @@
DCHECK(reg < 12u && reg != kBakerCcEntrypointRegister);
}
+ void CompileBakerReadBarrierThunk(arm::ArmVIXLAssembler& assembler, uint32_t encoded_data);
+
void SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value);
static uint32_t GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset);
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
index 8bc3eb4..2e28349 100644
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc
@@ -185,9 +185,7 @@
}
std::vector<uint8_t> CompileMethodCallThunk() {
- ArmBaseRelativePatcher::ThunkKey key(
- ArmBaseRelativePatcher::ThunkType::kMethodCall,
- ArmBaseRelativePatcher::ThunkParams{{ 0, 0 }}); // NOLINT(whitespace/braces)
+ ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey();
return static_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
}
@@ -228,25 +226,22 @@
std::vector<uint8_t> CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) {
const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
0u, Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg));
- auto* patcher = down_cast<Thumb2RelativePatcher*>(patcher_.get());
- ArmBaseRelativePatcher::ThunkKey key = patcher->GetBakerReadBarrierKey(patch);
- return patcher->CompileThunk(key);
+ ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
+ return down_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
}
std::vector<uint8_t> CompileBakerArrayThunk(uint32_t base_reg) {
LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
0u, Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg));
- auto* patcher = down_cast<Thumb2RelativePatcher*>(patcher_.get());
- ArmBaseRelativePatcher::ThunkKey key = patcher->GetBakerReadBarrierKey(patch);
- return patcher->CompileThunk(key);
+ ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
+ return down_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
}
std::vector<uint8_t> CompileBakerGcRootThunk(uint32_t root_reg) {
LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
0u, Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg));
- auto* patcher = down_cast<Thumb2RelativePatcher*>(patcher_.get());
- ArmBaseRelativePatcher::ThunkKey key = patcher->GetBakerReadBarrierKey(patch);
- return patcher->CompileThunk(key);
+ ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
+ return down_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
}
uint32_t GetOutputInsn32(uint32_t offset) {
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 5c6fb50..2b06e3f 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -305,37 +305,42 @@
DCHECK_LT(literal_offset, code->size());
uint32_t insn = GetInsn(code, literal_offset);
DCHECK_EQ(insn & 0xffffffe0u, 0xb5000000); // CBNZ Xt, +0 (unpatched)
- ThunkKey key = GetBakerReadBarrierKey(patch);
+ ThunkKey key = GetBakerThunkKey(patch);
if (kIsDebugBuild) {
+ const uint32_t encoded_data = key.GetBakerReadBarrierParams().custom_value1;
+ BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
// Check that the next instruction matches the expected LDR.
- switch (key.GetType()) {
- case ThunkType::kBakerReadBarrierField: {
+ switch (kind) {
+ case BakerReadBarrierKind::kField: {
DCHECK_GE(code->size() - literal_offset, 8u);
uint32_t next_insn = GetInsn(code, literal_offset + 4u);
// LDR (immediate) with correct base_reg.
CheckValidReg(next_insn & 0x1fu); // Check destination register.
- CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (key.GetFieldParams().base_reg << 5));
+ const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
+ CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5));
break;
}
- case ThunkType::kBakerReadBarrierArray: {
+ case BakerReadBarrierKind::kArray: {
DCHECK_GE(code->size() - literal_offset, 8u);
uint32_t next_insn = GetInsn(code, literal_offset + 4u);
// LDR (register) with the correct base_reg, size=10 (32-bit), option=011 (extend = LSL),
// and S=1 (shift amount = 2 for 32-bit version), i.e. LDR Wt, [Xn, Xm, LSL #2].
CheckValidReg(next_insn & 0x1fu); // Check destination register.
- CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (key.GetArrayParams().base_reg << 5));
+ const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
+ CHECK_EQ(next_insn & 0xffe0ffe0u, 0xb8607800u | (base_reg << 5));
CheckValidReg((next_insn >> 16) & 0x1f); // Check index register
break;
}
- case ThunkType::kBakerReadBarrierRoot: {
+ case BakerReadBarrierKind::kGcRoot: {
DCHECK_GE(literal_offset, 4u);
uint32_t prev_insn = GetInsn(code, literal_offset - 4u);
// LDR (immediate) with correct root_reg.
- CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | key.GetRootParams().root_reg);
+ const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
+ CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg);
break;
}
default:
- LOG(FATAL) << "Unexpected type: " << static_cast<uint32_t>(key.GetType());
+ LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
UNREACHABLE();
}
}
@@ -347,49 +352,6 @@
SetInsn(code, literal_offset, insn);
}
-ArmBaseRelativePatcher::ThunkKey Arm64RelativePatcher::GetBakerReadBarrierKey(
- const LinkerPatch& patch) {
- DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kBakerReadBarrierBranch);
- uint32_t value = patch.GetBakerCustomValue1();
- BakerReadBarrierKind type = BakerReadBarrierKindField::Decode(value);
- ThunkParams params;
- switch (type) {
- case BakerReadBarrierKind::kField:
- params.field_params.base_reg = BakerReadBarrierFirstRegField::Decode(value);
- CheckValidReg(params.field_params.base_reg);
- params.field_params.holder_reg = BakerReadBarrierSecondRegField::Decode(value);
- CheckValidReg(params.field_params.holder_reg);
- break;
- case BakerReadBarrierKind::kArray:
- params.array_params.base_reg = BakerReadBarrierFirstRegField::Decode(value);
- CheckValidReg(params.array_params.base_reg);
- params.array_params.dummy = 0u;
- DCHECK_EQ(BakerReadBarrierSecondRegField::Decode(value), kInvalidEncodedReg);
- break;
- case BakerReadBarrierKind::kGcRoot:
- params.root_params.root_reg = BakerReadBarrierFirstRegField::Decode(value);
- CheckValidReg(params.root_params.root_reg);
- params.root_params.dummy = 0u;
- DCHECK_EQ(BakerReadBarrierSecondRegField::Decode(value), kInvalidEncodedReg);
- break;
- default:
- LOG(FATAL) << "Unexpected type: " << static_cast<uint32_t>(type);
- UNREACHABLE();
- }
- constexpr uint8_t kTypeTranslationOffset = 1u;
- static_assert(static_cast<uint32_t>(BakerReadBarrierKind::kField) + kTypeTranslationOffset ==
- static_cast<uint32_t>(ThunkType::kBakerReadBarrierField),
- "Thunk type translation check.");
- static_assert(static_cast<uint32_t>(BakerReadBarrierKind::kArray) + kTypeTranslationOffset ==
- static_cast<uint32_t>(ThunkType::kBakerReadBarrierArray),
- "Thunk type translation check.");
- static_assert(static_cast<uint32_t>(BakerReadBarrierKind::kGcRoot) + kTypeTranslationOffset ==
- static_cast<uint32_t>(ThunkType::kBakerReadBarrierRoot),
- "Thunk type translation check.");
- return ThunkKey(static_cast<ThunkType>(static_cast<uint32_t>(type) + kTypeTranslationOffset),
- params);
-}
-
#define __ assembler.GetVIXLAssembler()->
static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler,
@@ -419,28 +381,22 @@
// Note: The fake dependency is unnecessary for the slow path.
}
-std::vector<uint8_t> Arm64RelativePatcher::CompileThunk(const ThunkKey& key) {
+void Arm64RelativePatcher::CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler,
+ uint32_t encoded_data) {
using namespace vixl::aarch64; // NOLINT(build/namespaces)
- ArenaPool pool;
- ArenaAllocator arena(&pool);
- arm64::Arm64Assembler assembler(&arena);
-
- switch (key.GetType()) {
- case ThunkType::kMethodCall: {
- // The thunk just uses the entry point in the ArtMethod. This works even for calls
- // to the generic JNI and interpreter trampolines.
- Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kArm64PointerSize).Int32Value());
- assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0));
- break;
- }
- case ThunkType::kBakerReadBarrierField: {
+ BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
+ switch (kind) {
+ case BakerReadBarrierKind::kField: {
// Check if the holder is gray and, if not, add fake dependency to the base register
// and return to the LDR instruction to load the reference. Otherwise, use introspection
// to load the reference and call the entrypoint (in IP1) that performs further checks
// on the reference and marks it if needed.
- auto holder_reg = Register::GetXRegFromCode(key.GetFieldParams().holder_reg);
- auto base_reg = Register::GetXRegFromCode(key.GetFieldParams().base_reg);
+ auto base_reg =
+ Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data));
+ CheckValidReg(base_reg.GetCode());
+ auto holder_reg =
+ Register::GetXRegFromCode(BakerReadBarrierSecondRegField::Decode(encoded_data));
+ CheckValidReg(holder_reg.GetCode());
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip0, ip1);
// If base_reg differs from holder_reg, the offset was too large and we must have
@@ -469,8 +425,11 @@
}
break;
}
- case ThunkType::kBakerReadBarrierArray: {
- auto base_reg = Register::GetXRegFromCode(key.GetArrayParams().base_reg);
+ case BakerReadBarrierKind::kArray: {
+ auto base_reg =
+ Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data));
+ CheckValidReg(base_reg.GetCode());
+ DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip0, ip1);
vixl::aarch64::Label slow_path;
@@ -489,12 +448,15 @@
__ Br(ip1); // Jump to the entrypoint's array switch case.
break;
}
- case ThunkType::kBakerReadBarrierRoot: {
+ case BakerReadBarrierKind::kGcRoot: {
// Check if the reference needs to be marked and if so (i.e. not null, not marked yet
// and it does not have a forwarding address), call the correct introspection entrypoint;
// otherwise return the reference (or the extracted forwarding address).
// There is no gray bit check for GC roots.
- auto root_reg = Register::GetWRegFromCode(key.GetRootParams().root_reg);
+ auto root_reg =
+ Register::GetWRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data));
+ CheckValidReg(root_reg.GetCode());
+ DCHECK_EQ(kInvalidEncodedReg, BakerReadBarrierSecondRegField::Decode(encoded_data));
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip0, ip1);
vixl::aarch64::Label return_label, not_marked, forwarding_address;
@@ -517,6 +479,30 @@
__ Br(lr);
break;
}
+ default:
+ LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
+ UNREACHABLE();
+ }
+}
+
+std::vector<uint8_t> Arm64RelativePatcher::CompileThunk(const ThunkKey& key) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ arm64::Arm64Assembler assembler(&arena);
+
+ switch (key.GetType()) {
+ case ThunkType::kMethodCall: {
+ // The thunk just uses the entry point in the ArtMethod. This works even for calls
+ // to the generic JNI and interpreter trampolines.
+ Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArm64PointerSize).Int32Value());
+ assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0));
+ break;
+ }
+ case ThunkType::kBakerReadBarrier: {
+ CompileBakerReadBarrierThunk(assembler, key.GetBakerReadBarrierParams().custom_value1);
+ break;
+ }
}
// Ensure we emit the literal pool.
@@ -529,24 +515,20 @@
#undef __
-uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(ThunkType type) {
- switch (type) {
+uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) {
+ switch (key.GetType()) {
case ThunkType::kMethodCall:
return kMaxMethodCallPositiveDisplacement;
- case ThunkType::kBakerReadBarrierField:
- case ThunkType::kBakerReadBarrierArray:
- case ThunkType::kBakerReadBarrierRoot:
+ case ThunkType::kBakerReadBarrier:
return kMaxBcondPositiveDisplacement;
}
}
-uint32_t Arm64RelativePatcher::MaxNegativeDisplacement(ThunkType type) {
- switch (type) {
+uint32_t Arm64RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) {
+ switch (key.GetType()) {
case ThunkType::kMethodCall:
return kMaxMethodCallNegativeDisplacement;
- case ThunkType::kBakerReadBarrierField:
- case ThunkType::kBakerReadBarrierArray:
- case ThunkType::kBakerReadBarrierRoot:
+ case ThunkType::kBakerReadBarrier:
return kMaxBcondNegativeDisplacement;
}
}
diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h
index 71ab70e..d1ab410 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.h
+++ b/compiler/linker/arm64/relative_patcher_arm64.h
@@ -23,17 +23,15 @@
#include "linker/arm/relative_patcher_arm_base.h"
namespace art {
+
+namespace arm64 {
+class Arm64Assembler;
+} // namespace arm64
+
namespace linker {
class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher {
public:
- enum class BakerReadBarrierKind : uint8_t {
- kField, // Field get or array get with constant offset (i.e. constant index).
- kArray, // Array get with index in register.
- kGcRoot, // GC root load.
- kLast
- };
-
static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) {
CheckValidReg(base_reg);
CheckValidReg(holder_reg);
@@ -77,14 +75,20 @@
uint32_t patch_offset) OVERRIDE;
protected:
- ThunkKey GetBakerReadBarrierKey(const LinkerPatch& patch) OVERRIDE;
std::vector<uint8_t> CompileThunk(const ThunkKey& key) OVERRIDE;
- uint32_t MaxPositiveDisplacement(ThunkType type) OVERRIDE;
- uint32_t MaxNegativeDisplacement(ThunkType type) OVERRIDE;
+ uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE;
+ uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE;
private:
static constexpr uint32_t kInvalidEncodedReg = /* sp/zr is invalid */ 31u;
+ enum class BakerReadBarrierKind : uint8_t {
+ kField, // Field get or array get with constant offset (i.e. constant index).
+ kArray, // Array get with index in register.
+ kGcRoot, // GC root load.
+ kLast
+ };
+
static constexpr size_t kBitsForBakerReadBarrierKind =
MinimumBitsToStore(static_cast<size_t>(BakerReadBarrierKind::kLast));
static constexpr size_t kBitsForRegister = 5u;
@@ -99,6 +103,8 @@
DCHECK(reg < 30u && reg != 16u && reg != 17u);
}
+ void CompileBakerReadBarrierThunk(arm64::Arm64Assembler& assembler, uint32_t encoded_data);
+
static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp);
static bool NeedsErratum843419Thunk(ArrayRef<const uint8_t> code, uint32_t literal_offset,
diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc
index 57ea886..b6549ee 100644
--- a/compiler/linker/arm64/relative_patcher_arm64_test.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc
@@ -167,9 +167,7 @@
}
std::vector<uint8_t> CompileMethodCallThunk() {
- ArmBaseRelativePatcher::ThunkKey key(
- ArmBaseRelativePatcher::ThunkType::kMethodCall,
- ArmBaseRelativePatcher::ThunkParams{{ 0, 0 }}); // NOLINT(whitespace/braces)
+ ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetMethodCallKey();
return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key);
}
@@ -473,25 +471,22 @@
std::vector<uint8_t> CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) {
const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
0u, Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg));
- auto* patcher = down_cast<Arm64RelativePatcher*>(patcher_.get());
- ArmBaseRelativePatcher::ThunkKey key = patcher->GetBakerReadBarrierKey(patch);
- return patcher->CompileThunk(key);
+ ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
+ return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key);
}
std::vector<uint8_t> CompileBakerArrayThunk(uint32_t base_reg) {
LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
0u, Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(base_reg));
- auto* patcher = down_cast<Arm64RelativePatcher*>(patcher_.get());
- ArmBaseRelativePatcher::ThunkKey key = patcher->GetBakerReadBarrierKey(patch);
- return patcher->CompileThunk(key);
+ ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
+ return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key);
}
std::vector<uint8_t> CompileBakerGcRootThunk(uint32_t root_reg) {
LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
0u, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg));
- auto* patcher = down_cast<Arm64RelativePatcher*>(patcher_.get());
- ArmBaseRelativePatcher::ThunkKey key = patcher->GetBakerReadBarrierKey(patch);
- return patcher->CompileThunk(key);
+ ArmBaseRelativePatcher::ThunkKey key = ArmBaseRelativePatcher::GetBakerThunkKey(patch);
+ return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key);
}
uint32_t GetOutputInsn(uint32_t offset) {