summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2018-04-13 09:34:00 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2018-04-13 09:34:00 +0000
commit8a78f81a16a7c8225b48bf634b67f5697f72e15f (patch)
tree1e7e6ee76ec6dc88d55d8681bf5816108b18b51b /compiler
parentbe2d2ad255585f639d657022ab55a78b682298be (diff)
parent6d66fcf87dee9d2207ea745969b1f89836b0f8c5 (diff)
Merge changes I8687ab9e,I61f879f2
* changes: Move RelativePatcher from libart-compiler.so to dex2oat. Rewrite null checks in read barrier introspection thunks.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/Android.bp18
-rw-r--r--compiler/linker/arm/relative_patcher_arm_base.cc538
-rw-r--r--compiler/linker/arm/relative_patcher_arm_base.h158
-rw-r--r--compiler/linker/arm/relative_patcher_thumb2.cc186
-rw-r--r--compiler/linker/arm/relative_patcher_thumb2.h73
-rw-r--r--compiler/linker/arm/relative_patcher_thumb2_test.cc1329
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64.cc435
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64.h84
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64_test.cc1405
-rw-r--r--compiler/linker/mips/relative_patcher_mips.cc101
-rw-r--r--compiler/linker/mips/relative_patcher_mips.h58
-rw-r--r--compiler/linker/mips/relative_patcher_mips32r6_test.cc110
-rw-r--r--compiler/linker/mips/relative_patcher_mips_test.cc115
-rw-r--r--compiler/linker/mips64/relative_patcher_mips64.cc99
-rw-r--r--compiler/linker/mips64/relative_patcher_mips64.h54
-rw-r--r--compiler/linker/mips64/relative_patcher_mips64_test.cc97
-rw-r--r--compiler/linker/relative_patcher.cc164
-rw-r--r--compiler/linker/relative_patcher.h170
-rw-r--r--compiler/linker/relative_patcher_test.h359
-rw-r--r--compiler/linker/x86/relative_patcher_x86.cc67
-rw-r--r--compiler/linker/x86/relative_patcher_x86.h41
-rw-r--r--compiler/linker/x86/relative_patcher_x86_base.cc58
-rw-r--r--compiler/linker/x86/relative_patcher_x86_base.h53
-rw-r--r--compiler/linker/x86/relative_patcher_x86_test.cc177
-rw-r--r--compiler/linker/x86_64/relative_patcher_x86_64.cc45
-rw-r--r--compiler/linker/x86_64/relative_patcher_x86_64.h41
-rw-r--r--compiler/linker/x86_64/relative_patcher_x86_64_test.cc180
-rw-r--r--compiler/optimizing/code_generator_arm64.cc38
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc42
29 files changed, 42 insertions, 6253 deletions
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 6bed48e107..cde64b058c 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -39,7 +39,6 @@ art_cc_defaults {
"linker/file_output_stream.cc",
"linker/output_stream.cc",
"linker/vector_output_stream.cc",
- "linker/relative_patcher.cc",
"jit/jit_compiler.cc",
"jit/jit_logger.cc",
"jni/quick/calling_convention.cc",
@@ -102,8 +101,6 @@ art_cc_defaults {
arm: {
srcs: [
"jni/quick/arm/calling_convention_arm.cc",
- "linker/arm/relative_patcher_arm_base.cc",
- "linker/arm/relative_patcher_thumb2.cc",
"optimizing/code_generator_arm_vixl.cc",
"optimizing/code_generator_vector_arm_vixl.cc",
"optimizing/instruction_simplifier_arm.cc",
@@ -120,7 +117,6 @@ art_cc_defaults {
arm64: {
srcs: [
"jni/quick/arm64/calling_convention_arm64.cc",
- "linker/arm64/relative_patcher_arm64.cc",
"optimizing/code_generator_arm64.cc",
"optimizing/code_generator_vector_arm64.cc",
"optimizing/scheduler_arm64.cc",
@@ -134,7 +130,6 @@ art_cc_defaults {
mips: {
srcs: [
"jni/quick/mips/calling_convention_mips.cc",
- "linker/mips/relative_patcher_mips.cc",
"optimizing/code_generator_mips.cc",
"optimizing/code_generator_vector_mips.cc",
"optimizing/instruction_simplifier_mips.cc",
@@ -147,7 +142,6 @@ art_cc_defaults {
mips64: {
srcs: [
"jni/quick/mips64/calling_convention_mips64.cc",
- "linker/mips64/relative_patcher_mips64.cc",
"optimizing/code_generator_mips64.cc",
"optimizing/code_generator_vector_mips64.cc",
"optimizing/intrinsics_mips64.cc",
@@ -158,8 +152,6 @@ art_cc_defaults {
x86: {
srcs: [
"jni/quick/x86/calling_convention_x86.cc",
- "linker/x86/relative_patcher_x86.cc",
- "linker/x86/relative_patcher_x86_base.cc",
"optimizing/code_generator_x86.cc",
"optimizing/code_generator_vector_x86.cc",
"optimizing/intrinsics_x86.cc",
@@ -173,7 +165,6 @@ art_cc_defaults {
x86_64: {
srcs: [
"jni/quick/x86_64/calling_convention_x86_64.cc",
- "linker/x86_64/relative_patcher_x86_64.cc",
"optimizing/intrinsics_x86_64.cc",
"optimizing/code_generator_x86_64.cc",
"optimizing/code_generator_vector_x86_64.cc",
@@ -373,31 +364,25 @@ art_cc_test {
codegen: {
arm: {
srcs: [
- "linker/arm/relative_patcher_thumb2_test.cc",
"utils/arm/managed_register_arm_test.cc",
],
},
arm64: {
srcs: [
- "linker/arm64/relative_patcher_arm64_test.cc",
"utils/arm64/managed_register_arm64_test.cc",
],
},
mips: {
srcs: [
- "linker/mips/relative_patcher_mips_test.cc",
- "linker/mips/relative_patcher_mips32r6_test.cc",
],
},
mips64: {
srcs: [
- "linker/mips64/relative_patcher_mips64_test.cc",
"utils/mips64/managed_register_mips64_test.cc",
],
},
x86: {
srcs: [
- "linker/x86/relative_patcher_x86_test.cc",
"utils/x86/managed_register_x86_test.cc",
// These tests are testing architecture-independent
@@ -413,7 +398,8 @@ art_cc_test {
},
x86_64: {
srcs: [
- "linker/x86_64/relative_patcher_x86_64_test.cc",
+ // Is this test a bit-rotten copy of the x86 test? b/77951326
+ // "utils/x86_64/managed_register_x86_64_test.cc",
],
},
},
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
deleted file mode 100644
index 7cb8ae55c5..0000000000
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/arm/relative_patcher_arm_base.h"
-
-#include "base/stl_util.h"
-#include "compiled_method-inl.h"
-#include "debug/method_debug_info.h"
-#include "dex/dex_file_types.h"
-#include "linker/linker_patch.h"
-#include "linker/output_stream.h"
-#include "oat.h"
-#include "oat_quick_method_header.h"
-
-namespace art {
-namespace linker {
-
-class ArmBaseRelativePatcher::ThunkData {
- public:
- ThunkData(ArrayRef<const uint8_t> code, const std::string& debug_name, uint32_t max_next_offset)
- : code_(code),
- debug_name_(debug_name),
- offsets_(),
- max_next_offset_(max_next_offset),
- pending_offset_(0u) {
- DCHECK(NeedsNextThunk()); // The data is constructed only when we expect to need the thunk.
- }
-
- ThunkData(ThunkData&& src) = default;
-
- size_t CodeSize() const {
- return code_.size();
- }
-
- ArrayRef<const uint8_t> GetCode() const {
- return code_;
- }
-
- const std::string& GetDebugName() const {
- return debug_name_;
- }
-
- bool NeedsNextThunk() const {
- return max_next_offset_ != 0u;
- }
-
- uint32_t MaxNextOffset() const {
- DCHECK(NeedsNextThunk());
- return max_next_offset_;
- }
-
- void ClearMaxNextOffset() {
- DCHECK(NeedsNextThunk());
- max_next_offset_ = 0u;
- }
-
- void SetMaxNextOffset(uint32_t max_next_offset) {
- DCHECK(!NeedsNextThunk());
- max_next_offset_ = max_next_offset;
- }
-
- // Adjust the MaxNextOffset() down if needed to fit the code before the next thunk.
- // Returns true if it was adjusted, false if the old value was kept.
- bool MakeSpaceBefore(const ThunkData& next_thunk, size_t alignment) {
- DCHECK(NeedsNextThunk());
- DCHECK(next_thunk.NeedsNextThunk());
- DCHECK_ALIGNED_PARAM(MaxNextOffset(), alignment);
- DCHECK_ALIGNED_PARAM(next_thunk.MaxNextOffset(), alignment);
- if (next_thunk.MaxNextOffset() - CodeSize() < MaxNextOffset()) {
- max_next_offset_ = RoundDown(next_thunk.MaxNextOffset() - CodeSize(), alignment);
- return true;
- } else {
- return false;
- }
- }
-
- uint32_t ReserveOffset(size_t offset) {
- DCHECK(NeedsNextThunk());
- DCHECK_LE(offset, max_next_offset_);
- max_next_offset_ = 0u; // The reserved offset should satisfy all pending references.
- offsets_.push_back(offset);
- return offset + CodeSize();
- }
-
- bool HasReservedOffset() const {
- return !offsets_.empty();
- }
-
- uint32_t LastReservedOffset() const {
- DCHECK(HasReservedOffset());
- return offsets_.back();
- }
-
- bool HasPendingOffset() const {
- return pending_offset_ != offsets_.size();
- }
-
- uint32_t GetPendingOffset() const {
- DCHECK(HasPendingOffset());
- return offsets_[pending_offset_];
- }
-
- void MarkPendingOffsetAsWritten() {
- DCHECK(HasPendingOffset());
- ++pending_offset_;
- }
-
- bool HasWrittenOffset() const {
- return pending_offset_ != 0u;
- }
-
- uint32_t LastWrittenOffset() const {
- DCHECK(HasWrittenOffset());
- return offsets_[pending_offset_ - 1u];
- }
-
- size_t IndexOfFirstThunkAtOrAfter(uint32_t offset) const {
- size_t number_of_thunks = NumberOfThunks();
- for (size_t i = 0; i != number_of_thunks; ++i) {
- if (GetThunkOffset(i) >= offset) {
- return i;
- }
- }
- return number_of_thunks;
- }
-
- size_t NumberOfThunks() const {
- return offsets_.size();
- }
-
- uint32_t GetThunkOffset(size_t index) const {
- DCHECK_LT(index, NumberOfThunks());
- return offsets_[index];
- }
-
- private:
- const ArrayRef<const uint8_t> code_; // The code of the thunk.
- const std::string debug_name_; // The debug name of the thunk.
- std::vector<uint32_t> offsets_; // Offsets at which the thunk needs to be written.
- uint32_t max_next_offset_; // The maximum offset at which the next thunk can be placed.
- uint32_t pending_offset_; // The index of the next offset to write.
-};
-
-class ArmBaseRelativePatcher::PendingThunkComparator {
- public:
- bool operator()(const ThunkData* lhs, const ThunkData* rhs) const {
- DCHECK(lhs->HasPendingOffset());
- DCHECK(rhs->HasPendingOffset());
- // The top of the heap is defined to contain the highest element and we want to pick
- // the thunk with the smallest pending offset, so use the reverse ordering, i.e. ">".
- return lhs->GetPendingOffset() > rhs->GetPendingOffset();
- }
-};
-
-uint32_t ArmBaseRelativePatcher::ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) {
- return ReserveSpaceInternal(offset, compiled_method, method_ref, 0u);
-}
-
-uint32_t ArmBaseRelativePatcher::ReserveSpaceEnd(uint32_t offset) {
- // For multi-oat compilations (boot image), ReserveSpaceEnd() is called for each oat file.
- // Since we do not know here whether this is the last file or whether the next opportunity
- // to place thunk will be soon enough, we need to reserve all needed thunks now. Code for
- // subsequent oat files can still call back to them.
- if (!unprocessed_method_call_patches_.empty()) {
- ResolveMethodCalls(offset, MethodReference(nullptr, dex::kDexNoIndex));
- }
- for (ThunkData* data : unreserved_thunks_) {
- uint32_t thunk_offset = CompiledCode::AlignCode(offset, instruction_set_);
- offset = data->ReserveOffset(thunk_offset);
- }
- unreserved_thunks_.clear();
- // We also need to delay initiating the pending_thunks_ until the call to WriteThunks().
- // Check that the `pending_thunks_.capacity()` indicates that no WriteThunks() has taken place.
- DCHECK_EQ(pending_thunks_.capacity(), 0u);
- return offset;
-}
-
-uint32_t ArmBaseRelativePatcher::WriteThunks(OutputStream* out, uint32_t offset) {
- if (pending_thunks_.capacity() == 0u) {
- if (thunks_.empty()) {
- return offset;
- }
- // First call to WriteThunks(), prepare the thunks for writing.
- pending_thunks_.reserve(thunks_.size());
- for (auto& entry : thunks_) {
- ThunkData* data = &entry.second;
- if (data->HasPendingOffset()) {
- pending_thunks_.push_back(data);
- }
- }
- std::make_heap(pending_thunks_.begin(), pending_thunks_.end(), PendingThunkComparator());
- }
- uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
- while (!pending_thunks_.empty() &&
- pending_thunks_.front()->GetPendingOffset() == aligned_offset) {
- // Write alignment bytes and code.
- uint32_t aligned_code_delta = aligned_offset - offset;
- if (aligned_code_delta != 0u && UNLIKELY(!WriteCodeAlignment(out, aligned_code_delta))) {
- return 0u;
- }
- if (UNLIKELY(!WriteThunk(out, pending_thunks_.front()->GetCode()))) {
- return 0u;
- }
- offset = aligned_offset + pending_thunks_.front()->CodeSize();
- // Mark the thunk as written at the pending offset and update the `pending_thunks_` heap.
- std::pop_heap(pending_thunks_.begin(), pending_thunks_.end(), PendingThunkComparator());
- pending_thunks_.back()->MarkPendingOffsetAsWritten();
- if (pending_thunks_.back()->HasPendingOffset()) {
- std::push_heap(pending_thunks_.begin(), pending_thunks_.end(), PendingThunkComparator());
- } else {
- pending_thunks_.pop_back();
- }
- aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
- }
- DCHECK(pending_thunks_.empty() || pending_thunks_.front()->GetPendingOffset() > aligned_offset);
- return offset;
-}
-
-std::vector<debug::MethodDebugInfo> ArmBaseRelativePatcher::GenerateThunkDebugInfo(
- uint32_t executable_offset) {
- // For multi-oat compilation (boot image), `thunks_` records thunks for all oat files.
- // To return debug info for the current oat file, we must ignore thunks before the
- // `executable_offset` as they are in the previous oat files and this function must be
- // called before reserving thunk positions for subsequent oat files.
- size_t number_of_thunks = 0u;
- for (auto&& entry : thunks_) {
- const ThunkData& data = entry.second;
- number_of_thunks += data.NumberOfThunks() - data.IndexOfFirstThunkAtOrAfter(executable_offset);
- }
- std::vector<debug::MethodDebugInfo> result;
- result.reserve(number_of_thunks);
- for (auto&& entry : thunks_) {
- const ThunkData& data = entry.second;
- size_t start = data.IndexOfFirstThunkAtOrAfter(executable_offset);
- if (start == data.NumberOfThunks()) {
- continue;
- }
- // Get the base name to use for the first occurrence of the thunk.
- std::string base_name = data.GetDebugName();
- for (size_t i = start, num = data.NumberOfThunks(); i != num; ++i) {
- debug::MethodDebugInfo info = {};
- if (i == 0u) {
- info.custom_name = base_name;
- } else {
- // Add a disambiguating tag for subsequent identical thunks. Since the `thunks_`
- // keeps records also for thunks in previous oat files, names based on the thunk
- // index shall be unique across the whole multi-oat output.
- info.custom_name = base_name + "_" + std::to_string(i);
- }
- info.isa = instruction_set_;
- info.is_code_address_text_relative = true;
- info.code_address = data.GetThunkOffset(i) - executable_offset;
- info.code_size = data.CodeSize();
- result.push_back(std::move(info));
- }
- }
- return result;
-}
-
-ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider,
- RelativePatcherTargetProvider* target_provider,
- InstructionSet instruction_set)
- : thunk_provider_(thunk_provider),
- target_provider_(target_provider),
- instruction_set_(instruction_set),
- thunks_(),
- unprocessed_method_call_patches_(),
- method_call_thunk_(nullptr),
- pending_thunks_() {
-}
-
-ArmBaseRelativePatcher::~ArmBaseRelativePatcher() {
- // All work done by member destructors.
-}
-
-uint32_t ArmBaseRelativePatcher::ReserveSpaceInternal(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref,
- uint32_t max_extra_space) {
- // Adjust code size for extra space required by the subclass.
- uint32_t max_code_size = compiled_method->GetQuickCode().size() + max_extra_space;
- uint32_t code_offset;
- uint32_t next_aligned_offset;
- while (true) {
- code_offset = compiled_method->AlignCode(offset + sizeof(OatQuickMethodHeader));
- next_aligned_offset = compiled_method->AlignCode(code_offset + max_code_size);
- if (unreserved_thunks_.empty() ||
- unreserved_thunks_.front()->MaxNextOffset() >= next_aligned_offset) {
- break;
- }
- ThunkData* thunk = unreserved_thunks_.front();
- if (thunk == method_call_thunk_) {
- ResolveMethodCalls(code_offset, method_ref);
- // This may have changed `method_call_thunk_` data, so re-check if we need to reserve.
- if (unreserved_thunks_.empty() ||
- unreserved_thunks_.front()->MaxNextOffset() >= next_aligned_offset) {
- break;
- }
- // We need to process the new `front()` whether it's still the `method_call_thunk_` or not.
- thunk = unreserved_thunks_.front();
- }
- unreserved_thunks_.pop_front();
- uint32_t thunk_offset = CompiledCode::AlignCode(offset, instruction_set_);
- offset = thunk->ReserveOffset(thunk_offset);
- if (thunk == method_call_thunk_) {
- // 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(GetMethodCallKey()));
- unprocessed_method_call_patches_.clear();
- }
- }
-
- // Process patches and check that adding thunks for the current method did not push any
- // thunks (previously existing or newly added) before `next_aligned_offset`. This is
- // essentially a check that we never compile a method that's too big. The calls or branches
- // from the method should be able to reach beyond the end of the method and over any pending
- // thunks. (The number of different thunks should be relatively low and their code short.)
- ProcessPatches(compiled_method, code_offset);
- CHECK(unreserved_thunks_.empty() ||
- unreserved_thunks_.front()->MaxNextOffset() >= next_aligned_offset);
-
- return offset;
-}
-
-uint32_t ArmBaseRelativePatcher::CalculateMethodCallDisplacement(uint32_t patch_offset,
- uint32_t target_offset) {
- 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(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.
- DCHECK(!method_call_thunk_->HasPendingOffset() ||
- method_call_thunk_->GetPendingOffset() > patch_offset);
- if (method_call_thunk_->HasPendingOffset() &&
- method_call_thunk_->GetPendingOffset() - patch_offset <= max_positive_displacement) {
- displacement = method_call_thunk_->GetPendingOffset() - patch_offset;
- } else {
- // We must have a previous thunk then.
- DCHECK(method_call_thunk_->HasWrittenOffset());
- DCHECK_LT(method_call_thunk_->LastWrittenOffset(), patch_offset);
- displacement = method_call_thunk_->LastWrittenOffset() - patch_offset;
- DCHECK_GE(displacement, -max_negative_displacement);
- }
- }
- return displacement;
-}
-
-uint32_t ArmBaseRelativePatcher::GetThunkTargetOffset(const ThunkKey& key, uint32_t patch_offset) {
- auto it = thunks_.find(key);
- CHECK(it != thunks_.end());
- const ThunkData& data = it->second;
- if (data.HasWrittenOffset()) {
- uint32_t offset = data.LastWrittenOffset();
- DCHECK_LT(offset, patch_offset);
- 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));
- return offset;
-}
-
-ArmBaseRelativePatcher::ThunkKey ArmBaseRelativePatcher::GetMethodCallKey() {
- return ThunkKey(ThunkType::kMethodCall);
-}
-
-ArmBaseRelativePatcher::ThunkKey ArmBaseRelativePatcher::GetBakerThunkKey(
- const LinkerPatch& patch) {
- DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kBakerReadBarrierBranch);
- return ThunkKey(ThunkType::kBakerReadBarrier,
- patch.GetBakerCustomValue1(),
- patch.GetBakerCustomValue2());
-}
-
-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();
- ThunkKey key(static_cast<ThunkType>(-1));
- ThunkData* old_data = nullptr;
- if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
- key = GetMethodCallKey();
- unprocessed_method_call_patches_.emplace_back(patch_offset, patch.TargetMethod());
- if (method_call_thunk_ == nullptr) {
- uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key);
- auto it = thunks_.Put(key, ThunkDataForPatch(patch, max_next_offset));
- method_call_thunk_ = &it->second;
- AddUnreservedThunk(method_call_thunk_);
- } else {
- old_data = method_call_thunk_;
- }
- } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) {
- 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);
- auto it = thunks_.PutBefore(lb, key, ThunkDataForPatch(patch, max_next_offset));
- AddUnreservedThunk(&it->second);
- } else {
- old_data = &lb->second;
- }
- }
- if (old_data != nullptr) {
- // Shared path where an old thunk may need an update.
- 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));
- } else {
- if (!old_data->HasReservedOffset() ||
- patch_offset - old_data->LastReservedOffset() > MaxNegativeDisplacement(key)) {
- old_data->SetMaxNextOffset(CalculateMaxNextOffset(patch_offset, key));
- AddUnreservedThunk(old_data);
- }
- }
- }
- }
-}
-
-void ArmBaseRelativePatcher::AddUnreservedThunk(ThunkData* data) {
- DCHECK(data->NeedsNextThunk());
- size_t index = unreserved_thunks_.size();
- while (index != 0u && data->MaxNextOffset() < unreserved_thunks_[index - 1u]->MaxNextOffset()) {
- --index;
- }
- unreserved_thunks_.insert(unreserved_thunks_.begin() + index, data);
- // We may need to update the max next offset(s) if the thunk code would not fit.
- size_t alignment = GetInstructionSetAlignment(instruction_set_);
- if (index + 1u != unreserved_thunks_.size()) {
- // Note: Ignore the return value as we need to process previous thunks regardless.
- data->MakeSpaceBefore(*unreserved_thunks_[index + 1u], alignment);
- }
- // Make space for previous thunks. Once we find a pending thunk that does
- // not need an adjustment, we can stop.
- while (index != 0u && unreserved_thunks_[index - 1u]->MakeSpaceBefore(*data, alignment)) {
- --index;
- data = unreserved_thunks_[index];
- }
-}
-
-void ArmBaseRelativePatcher::ResolveMethodCalls(uint32_t quick_code_offset,
- MethodReference method_ref) {
- DCHECK(!unreserved_thunks_.empty());
- DCHECK(!unprocessed_method_call_patches_.empty());
- DCHECK(method_call_thunk_ != nullptr);
- 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();
- uint32_t patch_offset = unprocessed_method_call_patches_.front().GetPatchOffset();
- DCHECK(!method_call_thunk_->HasReservedOffset() ||
- method_call_thunk_->LastReservedOffset() <= patch_offset);
- if (!method_call_thunk_->HasReservedOffset() ||
- patch_offset - method_call_thunk_->LastReservedOffset() > max_negative_displacement) {
- // No previous thunk in range, check if we can reach the target directly.
- if (target_method == method_ref) {
- DCHECK_GT(quick_code_offset, patch_offset);
- if (quick_code_offset - patch_offset > max_positive_displacement) {
- break;
- }
- } else {
- auto result = target_provider_->FindMethodOffset(target_method);
- if (!result.first) {
- break;
- }
- uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_);
- if (target_offset >= patch_offset) {
- DCHECK_LE(target_offset - patch_offset, max_positive_displacement);
- } else if (patch_offset - target_offset > max_negative_displacement) {
- break;
- }
- }
- }
- unprocessed_method_call_patches_.pop_front();
- }
- if (!unprocessed_method_call_patches_.empty()) {
- // Try to adjust the max next offset in `method_call_thunk_`. Do this conservatively only if
- // the thunk shall be at the end of the `unreserved_thunks_` to avoid dealing with overlaps.
- uint32_t new_max_next_offset =
- unprocessed_method_call_patches_.front().GetPatchOffset() + max_positive_displacement;
- if (new_max_next_offset >
- unreserved_thunks_.back()->MaxNextOffset() + unreserved_thunks_.back()->CodeSize()) {
- method_call_thunk_->ClearMaxNextOffset();
- method_call_thunk_->SetMaxNextOffset(new_max_next_offset);
- if (method_call_thunk_ != unreserved_thunks_.back()) {
- RemoveElement(unreserved_thunks_, method_call_thunk_);
- unreserved_thunks_.push_back(method_call_thunk_);
- }
- }
- } else {
- // We have resolved all method calls, we do not need a new thunk anymore.
- method_call_thunk_->ClearMaxNextOffset();
- RemoveElement(unreserved_thunks_, method_call_thunk_);
- }
-}
-
-inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_offset,
- const ThunkKey& key) {
- return RoundDown(patch_offset + MaxPositiveDisplacement(key),
- GetInstructionSetAlignment(instruction_set_));
-}
-
-inline ArmBaseRelativePatcher::ThunkData ArmBaseRelativePatcher::ThunkDataForPatch(
- const LinkerPatch& patch, uint32_t max_next_offset) {
- ArrayRef<const uint8_t> code;
- std::string debug_name;
- thunk_provider_->GetThunkCode(patch, &code, &debug_name);
- DCHECK(!code.empty());
- return ThunkData(code, debug_name, max_next_offset);
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h
deleted file mode 100644
index 963d6690b0..0000000000
--- a/compiler/linker/arm/relative_patcher_arm_base.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
-#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
-
-#include <deque>
-#include <vector>
-
-#include "base/safe_map.h"
-#include "dex/method_reference.h"
-#include "linker/relative_patcher.h"
-
-namespace art {
-namespace linker {
-
-class ArmBaseRelativePatcher : public RelativePatcher {
- public:
- uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) OVERRIDE;
- uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
- uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE;
-
- protected:
- ArmBaseRelativePatcher(RelativePatcherThunkProvider* thunk_provider,
- RelativePatcherTargetProvider* target_provider,
- InstructionSet instruction_set);
- ~ArmBaseRelativePatcher();
-
- enum class ThunkType {
- kMethodCall, // Method call thunk.
- kBakerReadBarrier, // Baker read barrier.
- };
-
- class ThunkKey {
- public:
- explicit ThunkKey(ThunkType type, uint32_t custom_value1 = 0u, uint32_t custom_value2 = 0u)
- : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) { }
-
- ThunkType GetType() const {
- return type_;
- }
-
- uint32_t GetCustomValue1() const {
- return custom_value1_;
- }
-
- uint32_t GetCustomValue2() const {
- return custom_value2_;
- }
-
- private:
- ThunkType type_;
- uint32_t custom_value1_;
- uint32_t custom_value2_;
- };
-
- class ThunkKeyCompare {
- public:
- bool operator()(const ThunkKey& lhs, const ThunkKey& rhs) const {
- if (lhs.GetType() != rhs.GetType()) {
- return lhs.GetType() < rhs.GetType();
- }
- if (lhs.GetCustomValue1() != rhs.GetCustomValue1()) {
- return lhs.GetCustomValue1() < rhs.GetCustomValue1();
- }
- return lhs.GetCustomValue2() < rhs.GetCustomValue2();
- }
- };
-
- static ThunkKey GetMethodCallKey();
- static ThunkKey GetBakerThunkKey(const LinkerPatch& patch);
-
- uint32_t ReserveSpaceInternal(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref,
- uint32_t max_extra_space);
- uint32_t GetThunkTargetOffset(const ThunkKey& key, uint32_t patch_offset);
-
- uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset,
- uint32_t target_offset);
-
- virtual uint32_t MaxPositiveDisplacement(const ThunkKey& key) = 0;
- virtual uint32_t MaxNegativeDisplacement(const ThunkKey& key) = 0;
-
- private:
- class ThunkData;
-
- void ProcessPatches(const CompiledMethod* compiled_method, uint32_t code_offset);
- void AddUnreservedThunk(ThunkData* data);
-
- void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref);
-
- uint32_t CalculateMaxNextOffset(uint32_t patch_offset, const ThunkKey& key);
- ThunkData ThunkDataForPatch(const LinkerPatch& patch, uint32_t max_next_offset);
-
- RelativePatcherThunkProvider* const thunk_provider_;
- RelativePatcherTargetProvider* const target_provider_;
- const InstructionSet instruction_set_;
-
- // The data for all thunks.
- // SafeMap<> nodes don't move after being inserted, so we can use direct pointers to the data.
- using ThunkMap = SafeMap<ThunkKey, ThunkData, ThunkKeyCompare>;
- ThunkMap thunks_;
-
- // ReserveSpace() tracks unprocessed method call patches. These may be resolved later.
- class UnprocessedMethodCallPatch {
- public:
- UnprocessedMethodCallPatch(uint32_t patch_offset, MethodReference target_method)
- : patch_offset_(patch_offset), target_method_(target_method) { }
-
- uint32_t GetPatchOffset() const {
- return patch_offset_;
- }
-
- MethodReference GetTargetMethod() const {
- return target_method_;
- }
-
- private:
- uint32_t patch_offset_;
- MethodReference target_method_;
- };
- std::deque<UnprocessedMethodCallPatch> unprocessed_method_call_patches_;
- // Once we have compiled a method call thunk, cache pointer to the data.
- ThunkData* method_call_thunk_;
-
- // Thunks
- std::deque<ThunkData*> unreserved_thunks_;
-
- class PendingThunkComparator;
- std::vector<ThunkData*> pending_thunks_; // Heap with the PendingThunkComparator.
-
- friend class Arm64RelativePatcherTest;
- friend class Thumb2RelativePatcherTest;
-
- DISALLOW_COPY_AND_ASSIGN(ArmBaseRelativePatcher);
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
deleted file mode 100644
index 697fb09f73..0000000000
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/arm/relative_patcher_thumb2.h"
-
-#include <sstream>
-
-#include "arch/arm/asm_support_arm.h"
-#include "art_method.h"
-#include "base/bit_utils.h"
-#include "base/malloc_arena_pool.h"
-#include "compiled_method.h"
-#include "entrypoints/quick/quick_entrypoints_enum.h"
-#include "linker/linker_patch.h"
-#include "lock_word.h"
-#include "mirror/array-inl.h"
-#include "mirror/object.h"
-#include "read_barrier.h"
-#include "utils/arm/assembler_arm_vixl.h"
-
-namespace art {
-namespace linker {
-
-// PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
-static constexpr int32_t kPcDisplacement = 4;
-
-// Maximum positive and negative displacement for method call measured from the patch location.
-// (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from
-// the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.)
-constexpr uint32_t kMaxMethodCallPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
-constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 24) - kPcDisplacement;
-
-// Maximum positive and negative displacement for a conditional branch measured from the patch
-// location. (Signed 21 bit displacement with the last bit 0 has range [-2^20, 2^20-2] measured
-// from the Thumb2 PC pointing right after the B.cond, i.e. 4 bytes later than the patch location.)
-constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 2u + kPcDisplacement;
-constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20) - kPcDisplacement;
-
-Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider,
- RelativePatcherTargetProvider* target_provider)
- : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kThumb2) {
-}
-
-void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) {
- DCHECK_LE(literal_offset + 4u, code->size());
- DCHECK_EQ(literal_offset & 1u, 0u);
- DCHECK_EQ(patch_offset & 1u, 0u);
- DCHECK_EQ(target_offset & 1u, 1u); // Thumb2 mode bit.
- uint32_t displacement = CalculateMethodCallDisplacement(patch_offset, target_offset & ~1u);
- displacement -= kPcDisplacement; // The base PC is at the end of the 4-byte patch.
- DCHECK_EQ(displacement & 1u, 0u);
- DCHECK((displacement >> 24) == 0u || (displacement >> 24) == 255u); // 25-bit signed.
- uint32_t signbit = (displacement >> 31) & 0x1;
- uint32_t i1 = (displacement >> 23) & 0x1;
- uint32_t i2 = (displacement >> 22) & 0x1;
- uint32_t imm10 = (displacement >> 12) & 0x03ff;
- uint32_t imm11 = (displacement >> 1) & 0x07ff;
- uint32_t j1 = i1 ^ (signbit ^ 1);
- uint32_t j2 = i2 ^ (signbit ^ 1);
- uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11;
- value |= 0xf000d000; // BL
-
- // Check that we're just overwriting an existing BL.
- DCHECK_EQ(GetInsn32(code, literal_offset) & 0xf800d000, 0xf000d000);
- // Write the new BL.
- SetInsn32(code, literal_offset, value);
-}
-
-void Thumb2RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
- uint32_t literal_offset = patch.LiteralOffset();
- uint32_t pc_literal_offset = patch.PcInsnOffset();
- uint32_t pc_base = patch_offset + (pc_literal_offset - literal_offset) + 4u /* PC adjustment */;
- uint32_t diff = target_offset - pc_base;
-
- uint32_t insn = GetInsn32(code, literal_offset);
- DCHECK_EQ(insn & 0xff7ff0ffu, 0xf2400000u); // MOVW/MOVT, unpatched (imm16 == 0).
- uint32_t diff16 = ((insn & 0x00800000u) != 0u) ? (diff >> 16) : (diff & 0xffffu);
- uint32_t imm4 = (diff16 >> 12) & 0xfu;
- uint32_t imm = (diff16 >> 11) & 0x1u;
- uint32_t imm3 = (diff16 >> 8) & 0x7u;
- uint32_t imm8 = diff16 & 0xffu;
- insn = (insn & 0xfbf08f00u) | (imm << 26) | (imm4 << 16) | (imm3 << 12) | imm8;
- SetInsn32(code, literal_offset, insn);
-}
-
-void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) {
- DCHECK_ALIGNED(patch_offset, 2u);
- uint32_t literal_offset = patch.LiteralOffset();
- DCHECK_ALIGNED(literal_offset, 2u);
- DCHECK_LT(literal_offset, code->size());
- uint32_t insn = GetInsn32(code, literal_offset);
- DCHECK_EQ(insn, 0xf0408000); // BNE +0 (unpatched)
- ThunkKey key = GetBakerThunkKey(patch);
- uint32_t target_offset = GetThunkTargetOffset(key, patch_offset);
- DCHECK_ALIGNED(target_offset, 4u);
- uint32_t disp = target_offset - (patch_offset + kPcDisplacement);
- DCHECK((disp >> 20) == 0u || (disp >> 20) == 0xfffu); // 21-bit signed.
- insn |= ((disp << (26 - 20)) & 0x04000000u) | // Shift bit 20 to 26, "S".
- ((disp >> (19 - 11)) & 0x00000800u) | // Shift bit 19 to 13, "J1".
- ((disp >> (18 - 13)) & 0x00002000u) | // Shift bit 18 to 11, "J2".
- ((disp << (16 - 12)) & 0x003f0000u) | // Shift bits 12-17 to 16-25, "imm6".
- ((disp >> (1 - 0)) & 0x000007ffu); // Shift bits 1-12 to 0-11, "imm11".
- SetInsn32(code, literal_offset, insn);
-}
-
-uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) {
- switch (key.GetType()) {
- case ThunkType::kMethodCall:
- return kMaxMethodCallPositiveDisplacement;
- case ThunkType::kBakerReadBarrier:
- return kMaxBcondPositiveDisplacement;
- }
-}
-
-uint32_t Thumb2RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) {
- switch (key.GetType()) {
- case ThunkType::kMethodCall:
- return kMaxMethodCallNegativeDisplacement;
- case ThunkType::kBakerReadBarrier:
- return kMaxBcondNegativeDisplacement;
- }
-}
-
-void Thumb2RelativePatcher::SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
- DCHECK_LE(offset + 4u, code->size());
- DCHECK_ALIGNED(offset, 2u);
- uint8_t* addr = &(*code)[offset];
- addr[0] = (value >> 16) & 0xff;
- addr[1] = (value >> 24) & 0xff;
- addr[2] = (value >> 0) & 0xff;
- addr[3] = (value >> 8) & 0xff;
-}
-
-uint32_t Thumb2RelativePatcher::GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset) {
- DCHECK_LE(offset + 4u, code.size());
- DCHECK_ALIGNED(offset, 2u);
- const uint8_t* addr = &code[offset];
- return
- (static_cast<uint32_t>(addr[0]) << 16) +
- (static_cast<uint32_t>(addr[1]) << 24) +
- (static_cast<uint32_t>(addr[2]) << 0)+
- (static_cast<uint32_t>(addr[3]) << 8);
-}
-
-template <typename Vector>
-uint32_t Thumb2RelativePatcher::GetInsn32(Vector* code, uint32_t offset) {
- static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
- return GetInsn32(ArrayRef<const uint8_t>(*code), offset);
-}
-
-uint32_t Thumb2RelativePatcher::GetInsn16(ArrayRef<const uint8_t> code, uint32_t offset) {
- DCHECK_LE(offset + 2u, code.size());
- DCHECK_ALIGNED(offset, 2u);
- const uint8_t* addr = &code[offset];
- return (static_cast<uint32_t>(addr[0]) << 0) + (static_cast<uint32_t>(addr[1]) << 8);
-}
-
-template <typename Vector>
-uint32_t Thumb2RelativePatcher::GetInsn16(Vector* code, uint32_t offset) {
- static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
- return GetInsn16(ArrayRef<const uint8_t>(*code), offset);
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
deleted file mode 100644
index 68610d69e1..0000000000
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_
-#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_
-
-#include "arch/arm/registers_arm.h"
-#include "base/array_ref.h"
-#include "linker/arm/relative_patcher_arm_base.h"
-
-namespace art {
-
-namespace arm {
-class ArmVIXLAssembler;
-} // namespace arm
-
-namespace linker {
-
-class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher {
- public:
- explicit Thumb2RelativePatcher(RelativePatcherThunkProvider* thunk_provider,
- RelativePatcherTargetProvider* target_provider);
-
- void PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) OVERRIDE;
-
- protected:
- uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE;
- uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE;
-
- private:
- 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);
-
- template <typename Vector>
- static uint32_t GetInsn32(Vector* code, uint32_t offset);
-
- static uint32_t GetInsn16(ArrayRef<const uint8_t> code, uint32_t offset);
-
- template <typename Vector>
- static uint32_t GetInsn16(Vector* code, uint32_t offset);
-
- friend class Thumb2RelativePatcherTest;
-
- DISALLOW_COPY_AND_ASSIGN(Thumb2RelativePatcher);
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_THUMB2_H_
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
deleted file mode 100644
index e7b11bd16b..0000000000
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ /dev/null
@@ -1,1329 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/arm/relative_patcher_thumb2.h"
-
-#include "arch/arm/instruction_set_features_arm.h"
-#include "base/casts.h"
-#include "linker/relative_patcher_test.h"
-#include "lock_word.h"
-#include "mirror/array-inl.h"
-#include "mirror/object.h"
-#include "oat_quick_method_header.h"
-#include "optimizing/code_generator_arm_vixl.h"
-#include "optimizing/optimizing_unit_test.h"
-
-namespace art {
-namespace linker {
-
-class Thumb2RelativePatcherTest : public RelativePatcherTest {
- public:
- Thumb2RelativePatcherTest() : RelativePatcherTest(InstructionSet::kThumb2, "default") { }
-
- protected:
- static const uint8_t kCallRawCode[];
- static const ArrayRef<const uint8_t> kCallCode;
- static const uint8_t kNopRawCode[];
- static const ArrayRef<const uint8_t> kNopCode;
- static const uint8_t kUnpatchedPcRelativeRawCode[];
- static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
- static const uint32_t kPcInsnOffset;
-
- // The PC in Thumb mode is 4 bytes after the instruction location.
- static constexpr uint32_t kPcAdjustment = 4u;
-
- // Branches within range [-256, 256) can be created from these by adding the low 8 bits.
- static constexpr uint32_t kBlPlus0 = 0xf000f800u;
- static constexpr uint32_t kBlMinus256 = 0xf7ffff00u;
-
- // Special BL values.
- static constexpr uint32_t kBlPlusMax = 0xf3ffd7ffu;
- static constexpr uint32_t kBlMinusMax = 0xf400d000u;
-
- // BNE +0, 32-bit, encoding T3. Bits 0-10, 11, 13, 16-21, 26 are placeholder for target offset.
- static constexpr uint32_t kBneWPlus0 = 0xf0408000u;
-
- // LDR immediate, 16-bit, encoding T1. Bits 6-10 are imm5, 0-2 are Rt, 3-5 are Rn.
- static constexpr uint32_t kLdrInsn = 0x6800u;
-
- // LDR immediate, 32-bit, encoding T3. Bits 0-11 are offset, 12-15 are Rt, 16-20 are Rn.
- static constexpr uint32_t kLdrWInsn = 0xf8d00000u;
-
- // LDR immediate, negative offset, encoding T4. Bits 0-7 are the offset to subtract.
- static constexpr uint32_t kLdrNegativeOffset = 0xf8500c00u;
-
- // LDR register, lsl #2. Bits 4-5 are the imm2, i.e. the lsl shift.
- static constexpr uint32_t kLdrRegLsl2 = 0xf8500020u;
-
- // NOP instructions.
- static constexpr uint32_t kNopInsn = 0xbf00u;
- static constexpr uint32_t kNopWInsn = 0xf3af8000u;
-
- void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
- CHECK_LE(pos, code->size());
- if (IsUint<16>(insn)) {
- const uint8_t insn_code[] = {
- static_cast<uint8_t>(insn),
- static_cast<uint8_t>(insn >> 8),
- };
- static_assert(sizeof(insn_code) == 2u, "Invalid sizeof(insn_code).");
- code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
- } else {
- const uint8_t insn_code[] = {
- static_cast<uint8_t>(insn >> 16),
- static_cast<uint8_t>(insn >> 24),
- static_cast<uint8_t>(insn),
- static_cast<uint8_t>(insn >> 8),
- };
- static_assert(sizeof(insn_code) == 4u, "Invalid sizeof(insn_code).");
- code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
- }
- }
-
- void PushBackInsn(std::vector<uint8_t>* code, uint32_t insn) {
- InsertInsn(code, code->size(), insn);
- }
-
- std::vector<uint8_t> GenNops(size_t num_nops) {
- std::vector<uint8_t> result;
- result.reserve(num_nops * 2u);
- for (size_t i = 0; i != num_nops; ++i) {
- PushBackInsn(&result, kNopInsn);
- }
- return result;
- }
-
- std::vector<uint8_t> RawCode(std::initializer_list<uint32_t> insns) {
- std::vector<uint8_t> raw_code;
- size_t number_of_16_bit_insns =
- std::count_if(insns.begin(), insns.end(), [](uint32_t x) { return IsUint<16>(x); });
- raw_code.reserve(insns.size() * 4u - number_of_16_bit_insns * 2u);
- for (uint32_t insn : insns) {
- PushBackInsn(&raw_code, insn);
- }
- return raw_code;
- }
-
- uint32_t BneWWithOffset(uint32_t bne_offset, uint32_t target_offset) {
- if (!IsAligned<2u>(bne_offset)) {
- LOG(ERROR) << "Unaligned bne_offset: " << bne_offset;
- return 0xffffffffu; // Fails code diff later.
- }
- if (!IsAligned<2u>(target_offset)) {
- LOG(ERROR) << "Unaligned target_offset: " << target_offset;
- return 0xffffffffu; // Fails code diff later.
- }
- uint32_t diff = target_offset - bne_offset - kPcAdjustment;
- DCHECK_ALIGNED(diff, 2u);
- if ((diff >> 20) != 0 && (diff >> 20) != 0xfffu) {
- LOG(ERROR) << "Target out of range: " << diff;
- return 0xffffffffu; // Fails code diff later.
- }
- return kBneWPlus0 | ((diff >> 1) & 0x7ffu) // imm11
- | (((diff >> 12) & 0x3fu) << 16) // imm6
- | (((diff >> 18) & 1) << 13) // J1
- | (((diff >> 19) & 1) << 11) // J2
- | (((diff >> 20) & 1) << 26); // S
- }
-
- bool Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
- const ArrayRef<const LinkerPatch>& method1_patches,
- const ArrayRef<const uint8_t>& method3_code,
- const ArrayRef<const LinkerPatch>& method3_patches,
- uint32_t distance_without_thunks) {
- CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
- uint32_t method1_offset =
- kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
- AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
-
- // We want to put the method3 at a very precise offset.
- const uint32_t method3_offset = method1_offset + distance_without_thunks;
- CHECK_ALIGNED(method3_offset, kArmAlignment);
-
- // Calculate size of method2 so that we put method3 at the correct place.
- const uint32_t method1_end = method1_offset + method1_code.size();
- const uint32_t method2_offset =
- method1_end + CodeAlignmentSize(method1_end) + sizeof(OatQuickMethodHeader);
- const uint32_t method2_size = (method3_offset - sizeof(OatQuickMethodHeader) - method2_offset);
- std::vector<uint8_t> method2_raw_code(method2_size);
- ArrayRef<const uint8_t> method2_code(method2_raw_code);
- AddCompiledMethod(MethodRef(2u), method2_code);
-
- AddCompiledMethod(MethodRef(3u), method3_code, method3_patches);
-
- Link();
-
- // Check assumptions.
- CHECK_EQ(GetMethodOffset(1), method1_offset);
- CHECK_EQ(GetMethodOffset(2), method2_offset);
- auto result3 = method_offset_map_.FindMethodOffset(MethodRef(3));
- CHECK(result3.first);
- // There may be a thunk before method2.
- if (result3.second == method3_offset + 1 /* thumb mode */) {
- return false; // No thunk.
- } else {
- uint32_t thunk_end =
- CompiledCode::AlignCode(method3_offset - sizeof(OatQuickMethodHeader),
- InstructionSet::kThumb2) +
- MethodCallThunkSize();
- uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end);
- CHECK_EQ(result3.second, header_offset + sizeof(OatQuickMethodHeader) + 1 /* thumb mode */);
- return true; // Thunk present.
- }
- }
-
- uint32_t GetMethodOffset(uint32_t method_idx) {
- auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
- CHECK(result.first);
- CHECK_NE(result.second & 1u, 0u);
- return result.second - 1 /* thumb mode */;
- }
-
- std::vector<uint8_t> CompileThunk(const LinkerPatch& patch,
- /*out*/ std::string* debug_name = nullptr) {
- OptimizingUnitTestHelper helper;
- HGraph* graph = helper.CreateGraph();
- std::string error_msg;
- ArmFeaturesUniquePtr features =
- ArmInstructionSetFeatures::FromVariant("default", &error_msg);
- CompilerOptions options;
- arm::CodeGeneratorARMVIXL codegen(graph, *features, options);
- ArenaVector<uint8_t> code(helper.GetAllocator()->Adapter());
- codegen.EmitThunkCode(patch, &code, debug_name);
- return std::vector<uint8_t>(code.begin(), code.end());
- }
-
- void AddCompiledMethod(
- MethodReference method_ref,
- const ArrayRef<const uint8_t>& code,
- const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) {
- RelativePatcherTest::AddCompiledMethod(method_ref, code, patches);
-
- // Make sure the ThunkProvider has all the necessary thunks.
- for (const LinkerPatch& patch : patches) {
- if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch ||
- patch.GetType() == LinkerPatch::Type::kCallRelative) {
- std::string debug_name;
- std::vector<uint8_t> thunk_code = CompileThunk(patch, &debug_name);
- thunk_provider_.SetThunkCode(patch, ArrayRef<const uint8_t>(thunk_code), debug_name);
- }
- }
- }
-
- std::vector<uint8_t> CompileMethodCallThunk() {
- LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u,
- /* target_dex_file*/ nullptr,
- /* target_method_idx */ 0u);
- return CompileThunk(patch);
- }
-
- uint32_t MethodCallThunkSize() {
- return CompileMethodCallThunk().size();
- }
-
- bool CheckThunk(uint32_t thunk_offset) {
- const std::vector<uint8_t> expected_code = CompileMethodCallThunk();
- if (output_.size() < thunk_offset + expected_code.size()) {
- LOG(ERROR) << "output_.size() == " << output_.size() << " < "
- << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
- return false;
- }
- ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
- if (linked_code == ArrayRef<const uint8_t>(expected_code)) {
- return true;
- }
- // Log failure info.
- DumpDiff(ArrayRef<const uint8_t>(expected_code), linked_code);
- return false;
- }
-
- std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
- std::vector<uint8_t> result;
- result.reserve(num_nops * 2u + 4u);
- for (size_t i = 0; i != num_nops; ++i) {
- PushBackInsn(&result, kNopInsn);
- }
- PushBackInsn(&result, bl);
- return result;
- }
-
- void TestStringBssEntry(uint32_t bss_begin, uint32_t string_entry_offset);
- void TestStringReference(uint32_t string_offset);
- void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
-
- static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg,
- uint32_t holder_reg,
- bool narrow) {
- return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow);
- }
-
- static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) {
- return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierArrayData(base_reg);
- }
-
- static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg, bool narrow) {
- return arm::CodeGeneratorARMVIXL::EncodeBakerReadBarrierGcRootData(root_reg, narrow);
- }
-
- std::vector<uint8_t> CompileBakerOffsetThunk(uint32_t base_reg,
- uint32_t holder_reg,
- bool narrow) {
- const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
- /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg, narrow));
- return CompileThunk(patch);
- }
-
- std::vector<uint8_t> CompileBakerArrayThunk(uint32_t base_reg) {
- LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
- /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg));
- return CompileThunk(patch);
- }
-
- std::vector<uint8_t> CompileBakerGcRootThunk(uint32_t root_reg, bool narrow) {
- LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
- /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg, narrow));
- return CompileThunk(patch);
- }
-
- uint32_t GetOutputInsn32(uint32_t offset) {
- CHECK_LE(offset, output_.size());
- CHECK_GE(output_.size() - offset, 4u);
- return (static_cast<uint32_t>(output_[offset]) << 16) |
- (static_cast<uint32_t>(output_[offset + 1]) << 24) |
- (static_cast<uint32_t>(output_[offset + 2]) << 0) |
- (static_cast<uint32_t>(output_[offset + 3]) << 8);
- }
-
- uint16_t GetOutputInsn16(uint32_t offset) {
- CHECK_LE(offset, output_.size());
- CHECK_GE(output_.size() - offset, 2u);
- return (static_cast<uint32_t>(output_[offset]) << 0) |
- (static_cast<uint32_t>(output_[offset + 1]) << 8);
- }
-
- void TestBakerFieldWide(uint32_t offset, uint32_t ref_reg);
- void TestBakerFieldNarrow(uint32_t offset, uint32_t ref_reg);
-};
-
-const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = {
- 0x00, 0xf0, 0x00, 0xf8
-};
-
-const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kCallCode(kCallRawCode);
-
-const uint8_t Thumb2RelativePatcherTest::kNopRawCode[] = {
- 0x00, 0xbf
-};
-
-const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kNopCode(kNopRawCode);
-
-const uint8_t Thumb2RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
- 0x40, 0xf2, 0x00, 0x00, // MOVW r0, #0 (placeholder)
- 0xc0, 0xf2, 0x00, 0x00, // MOVT r0, #0 (placeholder)
- 0x78, 0x44, // ADD r0, pc
-};
-const ArrayRef<const uint8_t> Thumb2RelativePatcherTest::kUnpatchedPcRelativeCode(
- kUnpatchedPcRelativeRawCode);
-const uint32_t Thumb2RelativePatcherTest::kPcInsnOffset = 8u;
-
-void Thumb2RelativePatcherTest::TestStringBssEntry(uint32_t bss_begin,
- uint32_t string_entry_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
- bss_begin_ = bss_begin;
- const LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(0u, nullptr, kPcInsnOffset, kStringIndex),
- LinkerPatch::StringBssEntryPatch(4u, nullptr, kPcInsnOffset, kStringIndex),
- };
- CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
-}
-
-void Thumb2RelativePatcherTest::TestStringReference(uint32_t string_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_offset);
- const LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(0u, nullptr, kPcInsnOffset, kStringIndex),
- LinkerPatch::RelativeStringPatch(4u, nullptr, kPcInsnOffset, kStringIndex),
- };
- CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
-}
-
-void Thumb2RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
- uint32_t target_offset) {
- AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t pc_base_offset = method1_offset + kPcInsnOffset + 4u /* PC adjustment */;
- uint32_t diff = target_offset - pc_base_offset;
- // Distribute the bits of the diff between the MOVW and MOVT:
- uint32_t diffw = diff & 0xffffu;
- uint32_t difft = diff >> 16;
- uint32_t movw = 0xf2400000u | // MOVW r0, #0 (placeholder),
- ((diffw & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
- ((diffw & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
- ((diffw & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
- ((diffw & 0x00ffu)); // keep imm8 at bits 0-7.
- uint32_t movt = 0xf2c00000u | // MOVT r0, #0 (placeholder),
- ((difft & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
- ((difft & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
- ((difft & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
- ((difft & 0x00ffu)); // keep imm8 at bits 0-7.
- const uint8_t expected_code[] = {
- static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
- static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
- static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
- static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
- 0x78, 0x44,
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, CallSelf) {
- const LinkerPatch patches[] = {
- LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- static const uint8_t expected_code[] = {
- 0xff, 0xf7, 0xfe, 0xff
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, CallOther) {
- const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
- const LinkerPatch method2_patches[] = {
- LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t method2_offset = GetMethodOffset(2u);
- uint32_t diff_after = method2_offset - (method1_offset + 4u /* PC adjustment */);
- ASSERT_EQ(diff_after & 1u, 0u);
- ASSERT_LT(diff_after >> 1, 1u << 8); // Simple encoding, (diff_after >> 1) fits into 8 bits.
- static const uint8_t method1_expected_code[] = {
- 0x00, 0xf0, static_cast<uint8_t>(diff_after >> 1), 0xf8
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
- uint32_t diff_before = method1_offset - (method2_offset + 4u /* PC adjustment */);
- ASSERT_EQ(diff_before & 1u, 0u);
- ASSERT_GE(diff_before, -1u << 9); // Simple encoding, -256 <= (diff >> 1) < 0.
- auto method2_expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff_before >> 1) & 0xffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, CallTrampoline) {
- const LinkerPatch patches[] = {
- LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t diff = kTrampolineOffset - (method1_offset + 4u);
- ASSERT_EQ(diff & 1u, 0u);
- ASSERT_GE(diff, -1u << 9); // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned).
- auto expected_code = GenNopsAndBl(0u, kBlMinus256 | ((diff >> 1) & 0xffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, CallTrampolineTooFar) {
- constexpr uint32_t missing_method_index = 1024u;
- auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs.
- ArrayRef<const uint8_t> method3_code(method3_raw_code);
- ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
- const LinkerPatch method3_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, missing_method_index),
- };
-
- constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
- ArrayRef<const LinkerPatch>(),
- method3_code,
- ArrayRef<const LinkerPatch>(method3_patches),
- just_over_max_negative_disp - bl_offset_in_method3);
- ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
- ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
-
- // Check linked code.
- uint32_t method3_offset = GetMethodOffset(3u);
- uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(),
- InstructionSet::kThumb2);
- uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
- ASSERT_EQ(diff & 1u, 0u);
- ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits.
- auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
- EXPECT_TRUE(CheckThunk(thunk_offset));
-}
-
-TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarAfter) {
- auto method1_raw_code = GenNopsAndBl(3u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method1 = 3u * 2u; // After NOPs.
- ArrayRef<const uint8_t> method1_code(method1_raw_code);
- ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
- const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
- };
-
- constexpr uint32_t max_positive_disp = 16 * MB - 2u + 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(method1_code,
- ArrayRef<const LinkerPatch>(method1_patches),
- kNopCode,
- ArrayRef<const LinkerPatch>(),
- bl_offset_in_method1 + max_positive_disp);
- ASSERT_FALSE(thunk_in_gap); // There should be no thunk.
-
- // Check linked code.
- auto expected_code = GenNopsAndBl(3u, kBlPlusMax);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarBefore) {
- auto method3_raw_code = GenNopsAndBl(2u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method3 = 2u * 2u; // After NOPs.
- ArrayRef<const uint8_t> method3_code(method3_raw_code);
- ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
- const LinkerPatch method3_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
- };
-
- constexpr uint32_t just_over_max_negative_disp = 16 * MB - 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
- ArrayRef<const LinkerPatch>(),
- method3_code,
- ArrayRef<const LinkerPatch>(method3_patches),
- just_over_max_negative_disp - bl_offset_in_method3);
- ASSERT_FALSE(thunk_in_gap); // There should be no thunk.
-
- // Check linked code.
- auto expected_code = GenNopsAndBl(2u, kBlMinusMax);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarAfter) {
- auto method1_raw_code = GenNopsAndBl(2u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method1 = 2u * 2u; // After NOPs.
- ArrayRef<const uint8_t> method1_code(method1_raw_code);
- ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
- const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
- };
-
- constexpr uint32_t just_over_max_positive_disp = 16 * MB + 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(method1_code,
- ArrayRef<const LinkerPatch>(method1_patches),
- kNopCode,
- ArrayRef<const LinkerPatch>(),
- bl_offset_in_method1 + just_over_max_positive_disp);
- ASSERT_TRUE(thunk_in_gap);
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t method3_offset = GetMethodOffset(3u);
- ASSERT_TRUE(IsAligned<kArmAlignment>(method3_offset));
- uint32_t method3_header_offset = method3_offset - sizeof(OatQuickMethodHeader);
- uint32_t thunk_size = MethodCallThunkSize();
- uint32_t thunk_offset = RoundDown(method3_header_offset - thunk_size, kArmAlignment);
- DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
- method3_header_offset);
- ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
- uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
- ASSERT_EQ(diff & 1u, 0u);
- ASSERT_GE(diff, 16 * MB - (1u << 9)); // Simple encoding, unknown bits fit into the low 8 bits.
- auto expected_code = GenNopsAndBl(2u, 0xf3ffd700 | ((diff >> 1) & 0xffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
- CheckThunk(thunk_offset);
-}
-
-TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarBefore) {
- auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs.
- ArrayRef<const uint8_t> method3_code(method3_raw_code);
- ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
- const LinkerPatch method3_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
- };
-
- constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
- ArrayRef<const LinkerPatch>(),
- method3_code,
- ArrayRef<const LinkerPatch>(method3_patches),
- just_over_max_negative_disp - bl_offset_in_method3);
- ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
-
- // Check linked code.
- uint32_t method3_offset = GetMethodOffset(3u);
- uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(),
- InstructionSet::kThumb2);
- uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
- ASSERT_EQ(diff & 1u, 0u);
- ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits.
- auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
- EXPECT_TRUE(CheckThunk(thunk_offset));
-}
-
-TEST_F(Thumb2RelativePatcherTest, StringBssEntry1) {
- TestStringBssEntry(0x00ff0000u, 0x00fcu);
- ASSERT_LT(GetMethodOffset(1u), 0xfcu);
-}
-
-TEST_F(Thumb2RelativePatcherTest, StringBssEntry2) {
- TestStringBssEntry(0x02ff0000u, 0x05fcu);
- ASSERT_LT(GetMethodOffset(1u), 0xfcu);
-}
-
-TEST_F(Thumb2RelativePatcherTest, StringBssEntry3) {
- TestStringBssEntry(0x08ff0000u, 0x08fcu);
- ASSERT_LT(GetMethodOffset(1u), 0xfcu);
-}
-
-TEST_F(Thumb2RelativePatcherTest, StringBssEntry4) {
- TestStringBssEntry(0xd0ff0000u, 0x60fcu);
- ASSERT_LT(GetMethodOffset(1u), 0xfcu);
-}
-
-TEST_F(Thumb2RelativePatcherTest, StringReference1) {
- TestStringReference(0x00ff00fcu);
- ASSERT_LT(GetMethodOffset(1u), 0xfcu);
-}
-
-TEST_F(Thumb2RelativePatcherTest, StringReference2) {
- TestStringReference(0x02ff05fcu);
- ASSERT_LT(GetMethodOffset(1u), 0xfcu);
-}
-
-TEST_F(Thumb2RelativePatcherTest, StringReference3) {
- TestStringReference(0x08ff08fcu);
- ASSERT_LT(GetMethodOffset(1u), 0xfcu);
-}
-
-TEST_F(Thumb2RelativePatcherTest, StringReference4) {
- TestStringReference(0xd0ff60fcu);
- ASSERT_LT(GetMethodOffset(1u), 0xfcu);
-}
-
-void Thumb2RelativePatcherTest::TestBakerFieldWide(uint32_t offset, uint32_t ref_reg) {
- uint32_t valid_regs[] = {
- 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address.
- 8, 9, 10, 11, // IP, SP, LR and PC are reserved.
- };
- DCHECK_ALIGNED(offset, 4u);
- DCHECK_LT(offset, 4 * KB);
- constexpr size_t kMethodCodeSize = 8u;
- constexpr size_t kLiteralOffset = 0u;
- uint32_t method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- for (uint32_t holder_reg : valid_regs) {
- uint32_t ldr = kLdrWInsn | offset | (base_reg << 16) | (ref_reg << 12);
- const std::vector<uint8_t> raw_code = RawCode({kBneWPlus0, ldr});
- ASSERT_EQ(kMethodCodeSize, raw_code.size());
- ArrayRef<const uint8_t> code(raw_code);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(
- base_reg, holder_reg, /* narrow */ false);
- const LinkerPatch patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data),
- };
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
- }
- }
- Link();
-
- // All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
- method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- for (uint32_t holder_reg : valid_regs) {
- ++method_idx;
- uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
- uint32_t ldr = kLdrWInsn | offset | (base_reg << 16) | (ref_reg << 12);
- const std::vector<uint8_t> expected_code = RawCode({bne, ldr});
- ASSERT_EQ(kMethodCodeSize, expected_code.size()) << "bne=0x" << std::hex << bne;
- ASSERT_TRUE(
- CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
-
- std::vector<uint8_t> expected_thunk =
- CompileBakerOffsetThunk(base_reg, holder_reg, /* narrow */ false);
- ASSERT_GT(output_.size(), thunk_offset);
- ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
- ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
- expected_thunk.size());
- if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
- ASSERT_TRUE(false);
- }
-
- size_t gray_check_offset = thunk_offset;
- if (holder_reg == base_reg) {
- // Verify that the null-check uses the correct register, i.e. holder_reg.
- if (holder_reg < 8) {
- ASSERT_GE(output_.size() - gray_check_offset, 2u);
- ASSERT_EQ(0xb100 | holder_reg, GetOutputInsn16(thunk_offset) & 0xfd07u);
- gray_check_offset +=2u;
- } else {
- ASSERT_GE(output_.size() - gray_check_offset, 6u);
- ASSERT_EQ(0xf1b00f00u | (holder_reg << 16), GetOutputInsn32(thunk_offset) & 0xfbff8f00u);
- ASSERT_EQ(0xd000u, GetOutputInsn16(thunk_offset + 4u) & 0xff00u); // BEQ
- gray_check_offset += 6u;
- }
- }
- // Verify that the lock word for gray bit check is loaded from the holder address.
- ASSERT_GE(output_.size() - gray_check_offset,
- 4u * /* 32-bit instructions */ 4u + 2u * /* 16-bit instructions */ 2u);
- const uint32_t load_lock_word =
- kLdrWInsn |
- (holder_reg << 16) |
- (/* IP */ 12 << 12) |
- mirror::Object::MonitorOffset().Uint32Value();
- ASSERT_EQ(load_lock_word, GetOutputInsn32(gray_check_offset));
- // Verify the gray bit check.
- DCHECK_GE(LockWord::kReadBarrierStateShift, 8u); // ROR modified immediate.
- uint32_t ror_shift = 7 + (32 - LockWord::kReadBarrierStateShift);
- const uint32_t tst_gray_bit_without_offset =
- 0xf0100f00 | (/* IP */ 12 << 16)
- | (((ror_shift >> 4) & 1) << 26) // i
- | (((ror_shift >> 1) & 7) << 12) // imm3
- | ((ror_shift & 1) << 7); // imm8, ROR('1':imm8<7:0>, ror_shift).
- EXPECT_EQ(tst_gray_bit_without_offset, GetOutputInsn32(gray_check_offset + 4u));
- EXPECT_EQ(0xd100u, GetOutputInsn16(gray_check_offset + 8u) & 0xff00u); // BNE
- // Verify the fake dependency (skip "ADD LR, LR, #ldr_offset").
- const uint32_t fake_dependency =
- 0xeb000010 | // ADD Rd, Rn, Rm, LSR 32 (type=01, imm3=000, imm2=00)
- (/* IP */ 12) | // Rm = IP
- (base_reg << 16) | // Rn = base_reg
- (base_reg << 8); // Rd = base_reg
- EXPECT_EQ(fake_dependency, GetOutputInsn32(gray_check_offset + 14u));
- // Do not check the rest of the implementation.
-
- // The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
- }
- }
-}
-
-void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t ref_reg) {
- uint32_t valid_regs[] = {
- 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address.
- 8, 9, 10, 11, // IP, SP, LR and PC are reserved.
- };
- DCHECK_ALIGNED(offset, 4u);
- DCHECK_LT(offset, 32u);
- constexpr size_t kMethodCodeSize = 6u;
- constexpr size_t kLiteralOffset = 0u;
- uint32_t method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- if (base_reg >= 8u) {
- continue;
- }
- for (uint32_t holder_reg : valid_regs) {
- uint32_t ldr = kLdrInsn | (offset << (6 - 2)) | (base_reg << 3) | ref_reg;
- const std::vector<uint8_t> raw_code = RawCode({kBneWPlus0, ldr});
- ASSERT_EQ(kMethodCodeSize, raw_code.size());
- ArrayRef<const uint8_t> code(raw_code);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(
- base_reg, holder_reg, /* narrow */ true);
- const LinkerPatch patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data),
- };
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
- }
- }
- Link();
-
- // All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
- method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- if (base_reg >= 8u) {
- continue;
- }
- for (uint32_t holder_reg : valid_regs) {
- ++method_idx;
- uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
- uint32_t ldr = kLdrInsn | (offset << (6 - 2)) | (base_reg << 3) | ref_reg;
- const std::vector<uint8_t> expected_code = RawCode({bne, ldr});
- ASSERT_EQ(kMethodCodeSize, expected_code.size()) << "bne=0x" << std::hex << bne;
- ASSERT_TRUE(
- CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
-
- std::vector<uint8_t> expected_thunk =
- CompileBakerOffsetThunk(base_reg, holder_reg, /* narrow */ true);
- ASSERT_GT(output_.size(), thunk_offset);
- ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
- ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
- expected_thunk.size());
- if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
- ASSERT_TRUE(false);
- }
-
- size_t gray_check_offset = thunk_offset;
- if (holder_reg == base_reg) {
- // Verify that the null-check uses the correct register, i.e. holder_reg.
- if (holder_reg < 8) {
- ASSERT_GE(output_.size() - gray_check_offset, 2u);
- ASSERT_EQ(0xb100 | holder_reg, GetOutputInsn16(thunk_offset) & 0xfd07u);
- gray_check_offset +=2u;
- } else {
- ASSERT_GE(output_.size() - gray_check_offset, 6u);
- ASSERT_EQ(0xf1b00f00u | (holder_reg << 16), GetOutputInsn32(thunk_offset) & 0xfbff8f00u);
- ASSERT_EQ(0xd000u, GetOutputInsn16(thunk_offset + 4u) & 0xff00u); // BEQ
- gray_check_offset += 6u;
- }
- }
- // Verify that the lock word for gray bit check is loaded from the holder address.
- ASSERT_GE(output_.size() - gray_check_offset,
- 4u * /* 32-bit instructions */ 4u + 2u * /* 16-bit instructions */ 2u);
- const uint32_t load_lock_word =
- kLdrWInsn |
- (holder_reg << 16) |
- (/* IP */ 12 << 12) |
- mirror::Object::MonitorOffset().Uint32Value();
- ASSERT_EQ(load_lock_word, GetOutputInsn32(gray_check_offset));
- // Verify the gray bit check.
- DCHECK_GE(LockWord::kReadBarrierStateShift, 8u); // ROR modified immediate.
- uint32_t ror_shift = 7 + (32 - LockWord::kReadBarrierStateShift);
- const uint32_t tst_gray_bit_without_offset =
- 0xf0100f00 | (/* IP */ 12 << 16)
- | (((ror_shift >> 4) & 1) << 26) // i
- | (((ror_shift >> 1) & 7) << 12) // imm3
- | ((ror_shift & 1) << 7); // imm8, ROR('1':imm8<7:0>, ror_shift).
- EXPECT_EQ(tst_gray_bit_without_offset, GetOutputInsn32(gray_check_offset + 4u));
- EXPECT_EQ(0xd100u, GetOutputInsn16(gray_check_offset + 8u) & 0xff00u); // BNE
- // Verify the fake dependency (skip "ADD LR, LR, #ldr_offset").
- const uint32_t fake_dependency =
- 0xeb000010 | // ADD Rd, Rn, Rm, LSR 32 (type=01, imm3=000, imm2=00)
- (/* IP */ 12) | // Rm = IP
- (base_reg << 16) | // Rn = base_reg
- (base_reg << 8); // Rd = base_reg
- EXPECT_EQ(fake_dependency, GetOutputInsn32(gray_check_offset + 14u));
- // Do not check the rest of the implementation.
-
- // The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
- }
- }
-}
-
-#define TEST_BAKER_FIELD_WIDE(offset, ref_reg) \
- TEST_F(Thumb2RelativePatcherTest, \
- BakerOffsetWide##offset##_##ref_reg) { \
- TestBakerFieldWide(offset, ref_reg); \
- }
-
-TEST_BAKER_FIELD_WIDE(/* offset */ 0, /* ref_reg */ 0)
-TEST_BAKER_FIELD_WIDE(/* offset */ 8, /* ref_reg */ 3)
-TEST_BAKER_FIELD_WIDE(/* offset */ 28, /* ref_reg */ 7)
-TEST_BAKER_FIELD_WIDE(/* offset */ 0xffc, /* ref_reg */ 11)
-
-#define TEST_BAKER_FIELD_NARROW(offset, ref_reg) \
- TEST_F(Thumb2RelativePatcherTest, \
- BakerOffsetNarrow##offset##_##ref_reg) { \
- TestBakerFieldNarrow(offset, ref_reg); \
- }
-
-TEST_BAKER_FIELD_NARROW(/* offset */ 0, /* ref_reg */ 0)
-TEST_BAKER_FIELD_NARROW(/* offset */ 8, /* ref_reg */ 3)
-TEST_BAKER_FIELD_NARROW(/* offset */ 28, /* ref_reg */ 7)
-
-TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) {
- // One thunk in the middle with maximum distance branches to it from both sides.
- // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
- constexpr uint32_t kLiteralOffset1 = 6u;
- const std::vector<uint8_t> raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn});
- ArrayRef<const uint8_t> code1(raw_code1);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(
- /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false);
- const LinkerPatch patches1[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
- };
- AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
-
- constexpr uint32_t expected_thunk_offset =
- kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
- size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
- std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
- ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
- AddCompiledMethod(MethodRef(2u), filler1_code);
-
- // Enforce thunk reservation with a tiny method.
- AddCompiledMethod(MethodRef(3u), kNopCode);
-
- constexpr uint32_t kLiteralOffset2 = 4;
- static_assert(IsAligned<kArmAlignment>(kLiteralOffset2 + kPcAdjustment),
- "PC for BNE must be aligned.");
-
- // Allow reaching the thunk from the very beginning of a method almost 1MiB away. Backward branch
- // reaches the full 1MiB but we need to take PC adjustment into account. Things to subtract:
- // - thunk size and method 3 pre-header, rounded up (padding in between if needed)
- // - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
- // - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
- size_t thunk_size =
- CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size();
- size_t filler2_size =
- 1 * MB - (kLiteralOffset2 + kPcAdjustment)
- - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
- - sizeof(OatQuickMethodHeader);
- std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u);
- ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
- AddCompiledMethod(MethodRef(4u), filler2_code);
-
- const std::vector<uint8_t> raw_code2 = RawCode({kNopWInsn, kBneWPlus0, kLdrWInsn});
- ArrayRef<const uint8_t> code2(raw_code2);
- const LinkerPatch patches2[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
- };
- AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
-
- Link();
-
- uint32_t first_method_offset = GetMethodOffset(1u);
- uint32_t last_method_offset = GetMethodOffset(5u);
- EXPECT_EQ(2 * MB, last_method_offset - first_method_offset);
-
- const uint32_t bne_max_forward = kBneWPlus0 | 0x003f2fff;
- const uint32_t bne_max_backward = kBneWPlus0 | 0x04000000;
- const std::vector<uint8_t> expected_code1 =
- RawCode({kNopWInsn, kNopInsn, bne_max_forward, kLdrWInsn});
- const std::vector<uint8_t> expected_code2 = RawCode({kNopWInsn, bne_max_backward, kLdrWInsn});
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkBeforeFiller) {
- // Based on the first part of BakerOffsetThunkInTheMiddle but the BNE is one instruction
- // earlier, so the thunk is emitted before the filler.
- // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
- constexpr uint32_t kLiteralOffset1 = 4u;
- const std::vector<uint8_t> raw_code1 = RawCode({kNopWInsn, kBneWPlus0, kLdrWInsn, kNopInsn});
- ArrayRef<const uint8_t> code1(raw_code1);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(
- /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false);
- const LinkerPatch patches1[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
- };
- AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
-
- constexpr uint32_t expected_thunk_offset =
- kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement + 2 */ (1u << 20);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
- size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
- std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
- ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
- AddCompiledMethod(MethodRef(2u), filler1_code);
-
- Link();
-
- const uint32_t bne = BneWWithOffset(kLiteralOffset1, RoundUp(raw_code1.size(), kArmAlignment));
- const std::vector<uint8_t> expected_code1 = RawCode({kNopWInsn, bne, kLdrWInsn, kNopInsn});
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddleUnreachableFromLast) {
- // Based on the BakerOffsetThunkInTheMiddle but the BNE in the last method is preceded
- // by NOP and cannot reach the thunk in the middle, so we emit an extra thunk at the end.
- // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
- constexpr uint32_t kLiteralOffset1 = 6u;
- const std::vector<uint8_t> raw_code1 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn});
- ArrayRef<const uint8_t> code1(raw_code1);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(
- /* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false);
- const LinkerPatch patches1[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
- };
- AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
-
- constexpr uint32_t expected_thunk_offset =
- kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
- size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
- std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
- ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
- AddCompiledMethod(MethodRef(2u), filler1_code);
-
- // Enforce thunk reservation with a tiny method.
- AddCompiledMethod(MethodRef(3u), kNopCode);
-
- constexpr uint32_t kReachableFromOffset2 = 4;
- constexpr uint32_t kLiteralOffset2 = kReachableFromOffset2 + 2;
- static_assert(IsAligned<kArmAlignment>(kReachableFromOffset2 + kPcAdjustment),
- "PC for BNE must be aligned.");
-
- // If not for the extra NOP, this would allow reaching the thunk from the BNE
- // of a method 1MiB away. Backward branch reaches the full 1MiB but we need to take
- // PC adjustment into account. Things to subtract:
- // - thunk size and method 3 pre-header, rounded up (padding in between if needed)
- // - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
- // - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
- size_t thunk_size =
- CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size();
- size_t filler2_size =
- 1 * MB - (kReachableFromOffset2 + kPcAdjustment)
- - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
- - sizeof(OatQuickMethodHeader);
- std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u);
- ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
- AddCompiledMethod(MethodRef(4u), filler2_code);
-
- // Extra 16-bit NOP compared to BakerOffsetThunkInTheMiddle.
- const std::vector<uint8_t> raw_code2 = RawCode({kNopWInsn, kNopInsn, kBneWPlus0, kLdrWInsn});
- ArrayRef<const uint8_t> code2(raw_code2);
- const LinkerPatch patches2[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
- };
- AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
-
- Link();
-
- uint32_t first_method_offset = GetMethodOffset(1u);
- uint32_t last_method_offset = GetMethodOffset(5u);
- EXPECT_EQ(2 * MB, last_method_offset - first_method_offset);
-
- const uint32_t bne_max_forward = kBneWPlus0 | 0x003f2fff;
- const uint32_t bne_last =
- BneWWithOffset(kLiteralOffset2, RoundUp(raw_code2.size(), kArmAlignment));
- const std::vector<uint8_t> expected_code1 =
- RawCode({kNopWInsn, kNopInsn, bne_max_forward, kLdrWInsn});
- const std::vector<uint8_t> expected_code2 =
- RawCode({kNopWInsn, kNopInsn, bne_last, kLdrWInsn});
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, BakerArray) {
- uint32_t valid_regs[] = {
- 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address.
- 8, 9, 10, 11, // IP, SP, LR and PC are reserved.
- };
- auto ldr = [](uint32_t base_reg) {
- uint32_t index_reg = (base_reg == 0u) ? 1u : 0u;
- uint32_t ref_reg = (base_reg == 2) ? 3u : 2u;
- return kLdrRegLsl2 | index_reg | (base_reg << 16) | (ref_reg << 12);
- };
- constexpr size_t kMethodCodeSize = 8u;
- constexpr size_t kLiteralOffset = 0u;
- uint32_t method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- ++method_idx;
- const std::vector<uint8_t> raw_code = RawCode({kBneWPlus0, ldr(base_reg)});
- ASSERT_EQ(kMethodCodeSize, raw_code.size());
- ArrayRef<const uint8_t> code(raw_code);
- const LinkerPatch patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(
- kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)),
- };
- AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
- }
- Link();
-
- // All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
- method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- ++method_idx;
- uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
- const std::vector<uint8_t> expected_code = RawCode({bne, ldr(base_reg)});
- ASSERT_EQ(kMethodCodeSize, expected_code.size());
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
-
- std::vector<uint8_t> expected_thunk = CompileBakerArrayThunk(base_reg);
- ASSERT_GT(output_.size(), thunk_offset);
- ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
- ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
- expected_thunk.size());
- if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
- ASSERT_TRUE(false);
- }
-
- // Verify that the lock word for gray bit check is loaded from the correct address
- // before the base_reg which points to the array data.
- ASSERT_GE(output_.size() - thunk_offset,
- 4u * /* 32-bit instructions */ 4u + 2u * /* 16-bit instructions */ 2u);
- int32_t data_offset =
- mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
- int32_t offset = mirror::Object::MonitorOffset().Int32Value() - data_offset;
- ASSERT_LT(offset, 0);
- ASSERT_GT(offset, -256);
- const uint32_t load_lock_word =
- kLdrNegativeOffset |
- (-offset & 0xffu) |
- (base_reg << 16) |
- (/* IP */ 12 << 12);
- EXPECT_EQ(load_lock_word, GetOutputInsn32(thunk_offset));
- // Verify the gray bit check.
- DCHECK_GE(LockWord::kReadBarrierStateShift, 8u); // ROR modified immediate.
- uint32_t ror_shift = 7 + (32 - LockWord::kReadBarrierStateShift);
- const uint32_t tst_gray_bit_without_offset =
- 0xf0100f00 | (/* IP */ 12 << 16)
- | (((ror_shift >> 4) & 1) << 26) // i
- | (((ror_shift >> 1) & 7) << 12) // imm3
- | ((ror_shift & 1) << 7); // imm8, ROR('1':imm8<7:0>, ror_shift).
- EXPECT_EQ(tst_gray_bit_without_offset, GetOutputInsn32(thunk_offset + 4u));
- EXPECT_EQ(0xd100u, GetOutputInsn16(thunk_offset + 8u) & 0xff00u); // BNE
- // Verify the fake dependency.
- const uint32_t fake_dependency =
- 0xeb000010 | // ADD Rd, Rn, Rm, LSR 32 (type=01, imm3=000, imm2=00)
- (/* IP */ 12) | // Rm = IP
- (base_reg << 16) | // Rn = base_reg
- (base_reg << 8); // Rd = base_reg
- EXPECT_EQ(fake_dependency, GetOutputInsn32(thunk_offset + 14u));
- // Do not check the rest of the implementation.
-
- // The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
- }
-}
-
-TEST_F(Thumb2RelativePatcherTest, BakerGcRootWide) {
- uint32_t valid_regs[] = {
- 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address.
- 8, 9, 10, 11, // IP, SP, LR and PC are reserved.
- };
- constexpr size_t kMethodCodeSize = 8u;
- constexpr size_t kLiteralOffset = 4u;
- uint32_t method_idx = 0u;
- for (uint32_t root_reg : valid_regs) {
- ++method_idx;
- uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (root_reg << 12);
- const std::vector<uint8_t> raw_code = RawCode({ldr, kBneWPlus0});
- ASSERT_EQ(kMethodCodeSize, raw_code.size());
- ArrayRef<const uint8_t> code(raw_code);
- const LinkerPatch patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(
- kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ false)),
- };
- AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
- }
- Link();
-
- // All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
- method_idx = 0u;
- for (uint32_t root_reg : valid_regs) {
- ++method_idx;
- uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
- uint32_t ldr = kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (root_reg << 12);
- const std::vector<uint8_t> expected_code = RawCode({ldr, bne});
- ASSERT_EQ(kMethodCodeSize, expected_code.size());
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
-
- std::vector<uint8_t> expected_thunk = CompileBakerGcRootThunk(root_reg, /* narrow */ false);
- ASSERT_GT(output_.size(), thunk_offset);
- ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
- ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
- expected_thunk.size());
- if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
- ASSERT_TRUE(false);
- }
-
- // Verify that the fast-path null-check uses the correct register, i.e. root_reg.
- if (root_reg < 8) {
- ASSERT_GE(output_.size() - thunk_offset, 2u);
- ASSERT_EQ(0xb100 | root_reg, GetOutputInsn16(thunk_offset) & 0xfd07u);
- } else {
- ASSERT_GE(output_.size() - thunk_offset, 6u);
- ASSERT_EQ(0xf1b00f00u | (root_reg << 16), GetOutputInsn32(thunk_offset) & 0xfbff8f00u);
- ASSERT_EQ(0xd000u, GetOutputInsn16(thunk_offset + 4u) & 0xff00u); // BEQ
- }
- // Do not check the rest of the implementation.
-
- // The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
- }
-}
-
-TEST_F(Thumb2RelativePatcherTest, BakerGcRootNarrow) {
- uint32_t valid_regs[] = {
- 0, 1, 2, 3, 5, 6, 7, // R4 is reserved for entrypoint address.
- // Not appplicable to high registers.
- };
- constexpr size_t kMethodCodeSize = 6u;
- constexpr size_t kLiteralOffset = 2u;
- uint32_t method_idx = 0u;
- for (uint32_t root_reg : valid_regs) {
- ++method_idx;
- uint32_t ldr = kLdrInsn | (/* offset */ 8 << (6 - 2)) | (/* base_reg */ 0 << 3) | root_reg;
- const std::vector<uint8_t> raw_code = RawCode({ldr, kBneWPlus0});
- ASSERT_EQ(kMethodCodeSize, raw_code.size());
- ArrayRef<const uint8_t> code(raw_code);
- const LinkerPatch patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(
- kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg, /* narrow */ true)),
- };
- AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
- }
- Link();
-
- // All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
- method_idx = 0u;
- for (uint32_t root_reg : valid_regs) {
- ++method_idx;
- uint32_t bne = BneWWithOffset(GetMethodOffset(method_idx) + kLiteralOffset, thunk_offset);
- uint32_t ldr = kLdrInsn | (/* offset */ 8 << (6 - 2)) | (/* base_reg */ 0 << 3) | root_reg;
- const std::vector<uint8_t> expected_code = RawCode({ldr, bne});
- ASSERT_EQ(kMethodCodeSize, expected_code.size());
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
-
- std::vector<uint8_t> expected_thunk = CompileBakerGcRootThunk(root_reg, /* narrow */ true);
- ASSERT_GT(output_.size(), thunk_offset);
- ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
- ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
- expected_thunk.size());
- if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
- ASSERT_TRUE(false);
- }
-
- // Verify that the fast-path null-check CBZ uses the correct register, i.e. root_reg.
- ASSERT_GE(output_.size() - thunk_offset, 2u);
- ASSERT_EQ(0xb100 | root_reg, GetOutputInsn16(thunk_offset) & 0xfd07u);
- // Do not check the rest of the implementation.
-
- // The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
- }
-}
-
-TEST_F(Thumb2RelativePatcherTest, BakerGcRootOffsetBits) {
- // Test 1MiB of patches to the same thunk to stress-test different large offsets.
- // (The low bits are not that important but the location of the high bits is easy to get wrong.)
- std::vector<uint8_t> code;
- code.reserve(1 * MB);
- const size_t num_patches = 1 * MB / 8u;
- std::vector<LinkerPatch> patches;
- patches.reserve(num_patches);
- const uint32_t ldr =
- kLdrWInsn | (/* offset */ 8) | (/* base_reg */ 0 << 16) | (/* root_reg */ 0 << 12);
- uint32_t encoded_data = EncodeBakerReadBarrierGcRootData(/* root_reg */ 0, /* narrow */ false);
- for (size_t i = 0; i != num_patches; ++i) {
- PushBackInsn(&code, ldr);
- PushBackInsn(&code, kBneWPlus0);
- patches.push_back(LinkerPatch::BakerReadBarrierBranchPatch(8u * i + 4u, encoded_data));
- }
- ASSERT_EQ(1 * MB, code.size());
- ASSERT_EQ(num_patches, patches.size());
- AddCompiledMethod(MethodRef(1u),
- ArrayRef<const uint8_t>(code),
- ArrayRef<const LinkerPatch>(patches));
- Link();
-
- // The thunk is right after the method code.
- DCHECK_ALIGNED(1 * MB, kArmAlignment);
- std::vector<uint8_t> expected_code;
- for (size_t i = 0; i != num_patches; ++i) {
- PushBackInsn(&expected_code, ldr);
- PushBackInsn(&expected_code, BneWWithOffset(8u * i + 4u, 1 * MB));
- patches.push_back(LinkerPatch::BakerReadBarrierBranchPatch(8u * i + 4u, encoded_data));
- }
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Thumb2RelativePatcherTest, BakerAndMethodCallInteraction) {
- // During development, there was a `DCHECK_LE(MaxNextOffset(), next_thunk.MaxNextOffset());`
- // in `ArmBaseRelativePatcher::ThunkData::MakeSpaceBefore()` which does not necessarily
- // hold when we're reserving thunks of different sizes. This test exposes the situation
- // by using Baker thunks and a method call thunk.
-
- // Add a method call patch that can reach to method 1 offset + 16MiB.
- uint32_t method_idx = 0u;
- constexpr size_t kMethodCallLiteralOffset = 2u;
- constexpr uint32_t kMissingMethodIdx = 2u;
- const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kBlPlus0});
- const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(kMethodCallLiteralOffset, nullptr, 2u),
- };
- ArrayRef<const uint8_t> code1(raw_code1);
- ++method_idx;
- AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(method1_patches));
-
- // Skip kMissingMethodIdx.
- ++method_idx;
- ASSERT_EQ(kMissingMethodIdx, method_idx);
- // Add a method with the right size that the method code for the next one starts 1MiB
- // after code for method 1.
- size_t filler_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
- - sizeof(OatQuickMethodHeader);
- std::vector<uint8_t> filler_code = GenNops(filler_size / 2u);
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
- // Add 14 methods with 1MiB code+header, making the code for the next method start 1MiB
- // before the currently scheduled MaxNextOffset() for the method call thunk.
- for (uint32_t i = 0; i != 14; ++i) {
- filler_size = 1 * MB - sizeof(OatQuickMethodHeader);
- filler_code = GenNops(filler_size / 2u);
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
- }
-
- // Add 2 Baker GC root patches to the last method, one that would allow the thunk at
- // 1MiB + kArmAlignment, i.e. kArmAlignment after the method call thunk, and the
- // second that needs it kArmAlignment after that. Given the size of the GC root thunk
- // is more than the space required by the method call thunk plus kArmAlignment,
- // this pushes the first GC root thunk's pending MaxNextOffset() before the method call
- // thunk's pending MaxNextOffset() which needs to be adjusted.
- ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArmAlignment) + kArmAlignment,
- CompileBakerGcRootThunk(/* root_reg */ 0, /* narrow */ false).size());
- static_assert(kArmAlignment == 8, "Code below assumes kArmAlignment == 8");
- constexpr size_t kBakerLiteralOffset1 = kArmAlignment + 2u - kPcAdjustment;
- constexpr size_t kBakerLiteralOffset2 = kBakerLiteralOffset1 + kArmAlignment;
- // Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | (root_reg << 12)`.
- const uint32_t ldr1 = kLdrWInsn | (/* root_reg */ 1 << 12);
- const uint32_t ldr2 = kLdrWInsn | (/* root_reg */ 2 << 12);
- const std::vector<uint8_t> last_method_raw_code = RawCode({
- kNopInsn, // Padding before first GC root read barrier.
- ldr1, kBneWPlus0, // First GC root LDR with read barrier.
- ldr2, kBneWPlus0, // Second GC root LDR with read barrier.
- });
- uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1, /* narrow */ false);
- uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2, /* narrow */ false);
- const LinkerPatch last_method_patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1),
- LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2),
- };
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx),
- ArrayRef<const uint8_t>(last_method_raw_code),
- ArrayRef<const LinkerPatch>(last_method_patches));
-
- // The main purpose of the test is to check that Link() does not cause a crash.
- Link();
-
- ASSERT_EQ(15 * MB, GetMethodOffset(method_idx) - GetMethodOffset(1u));
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
deleted file mode 100644
index 71d1287c87..0000000000
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/arm64/relative_patcher_arm64.h"
-
-#include "arch/arm64/asm_support_arm64.h"
-#include "arch/arm64/instruction_set_features_arm64.h"
-#include "art_method.h"
-#include "base/bit_utils.h"
-#include "base/malloc_arena_pool.h"
-#include "compiled_method-inl.h"
-#include "driver/compiler_driver.h"
-#include "entrypoints/quick/quick_entrypoints_enum.h"
-#include "heap_poisoning.h"
-#include "linker/linker_patch.h"
-#include "linker/output_stream.h"
-#include "lock_word.h"
-#include "mirror/array-inl.h"
-#include "mirror/object.h"
-#include "oat.h"
-#include "oat_quick_method_header.h"
-#include "read_barrier.h"
-#include "utils/arm64/assembler_arm64.h"
-
-namespace art {
-namespace linker {
-
-namespace {
-
-// Maximum positive and negative displacement for method call measured from the patch location.
-// (Signed 28 bit displacement with the last two bits 0 has range [-2^27, 2^27-4] measured from
-// the ARM64 PC pointing to the BL.)
-constexpr uint32_t kMaxMethodCallPositiveDisplacement = (1u << 27) - 4u;
-constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 27);
-
-// Maximum positive and negative displacement for a conditional branch measured from the patch
-// location. (Signed 21 bit displacement with the last two bits 0 has range [-2^20, 2^20-4]
-// measured from the ARM64 PC pointing to the B.cond.)
-constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 4u;
-constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20);
-
-// The ADRP thunk for erratum 843419 is 2 instructions, i.e. 8 bytes.
-constexpr uint32_t kAdrpThunkSize = 8u;
-
-inline bool IsAdrpPatch(const LinkerPatch& patch) {
- switch (patch.GetType()) {
- case LinkerPatch::Type::kCall:
- case LinkerPatch::Type::kCallRelative:
- case LinkerPatch::Type::kBakerReadBarrierBranch:
- return false;
- case LinkerPatch::Type::kDataBimgRelRo:
- case LinkerPatch::Type::kMethodRelative:
- case LinkerPatch::Type::kMethodBssEntry:
- case LinkerPatch::Type::kTypeRelative:
- case LinkerPatch::Type::kTypeBssEntry:
- case LinkerPatch::Type::kStringRelative:
- case LinkerPatch::Type::kStringBssEntry:
- return patch.LiteralOffset() == patch.PcInsnOffset();
- }
-}
-
-inline uint32_t MaxExtraSpace(size_t num_adrp, size_t code_size) {
- if (num_adrp == 0u) {
- return 0u;
- }
- uint32_t alignment_bytes =
- CompiledMethod::AlignCode(code_size, InstructionSet::kArm64) - code_size;
- return kAdrpThunkSize * num_adrp + alignment_bytes;
-}
-
-} // anonymous namespace
-
-Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider,
- RelativePatcherTargetProvider* target_provider,
- const Arm64InstructionSetFeatures* features)
- : ArmBaseRelativePatcher(thunk_provider, target_provider, InstructionSet::kArm64),
- fix_cortex_a53_843419_(features->NeedFixCortexA53_843419()),
- reserved_adrp_thunks_(0u),
- processed_adrp_thunks_(0u) {
- if (fix_cortex_a53_843419_) {
- adrp_thunk_locations_.reserve(16u);
- current_method_thunks_.reserve(16u * kAdrpThunkSize);
- }
-}
-
-uint32_t Arm64RelativePatcher::ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) {
- if (!fix_cortex_a53_843419_) {
- DCHECK(adrp_thunk_locations_.empty());
- return ReserveSpaceInternal(offset, compiled_method, method_ref, 0u);
- }
-
- // Add thunks for previous method if any.
- if (reserved_adrp_thunks_ != adrp_thunk_locations_.size()) {
- size_t num_adrp_thunks = adrp_thunk_locations_.size() - reserved_adrp_thunks_;
- offset = CompiledMethod::AlignCode(offset, InstructionSet::kArm64) +
- kAdrpThunkSize * num_adrp_thunks;
- reserved_adrp_thunks_ = adrp_thunk_locations_.size();
- }
-
- // Count the number of ADRP insns as the upper bound on the number of thunks needed
- // and use it to reserve space for other linker patches.
- size_t num_adrp = 0u;
- DCHECK(compiled_method != nullptr);
- for (const LinkerPatch& patch : compiled_method->GetPatches()) {
- if (IsAdrpPatch(patch)) {
- ++num_adrp;
- }
- }
- ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
- uint32_t max_extra_space = MaxExtraSpace(num_adrp, code.size());
- offset = ReserveSpaceInternal(offset, compiled_method, method_ref, max_extra_space);
- if (num_adrp == 0u) {
- return offset;
- }
-
- // Now that we have the actual offset where the code will be placed, locate the ADRP insns
- // that actually require the thunk.
- uint32_t quick_code_offset = compiled_method->AlignCode(offset + sizeof(OatQuickMethodHeader));
- uint32_t thunk_offset = compiled_method->AlignCode(quick_code_offset + code.size());
- DCHECK(compiled_method != nullptr);
- for (const LinkerPatch& patch : compiled_method->GetPatches()) {
- if (IsAdrpPatch(patch)) {
- uint32_t patch_offset = quick_code_offset + patch.LiteralOffset();
- if (NeedsErratum843419Thunk(code, patch.LiteralOffset(), patch_offset)) {
- adrp_thunk_locations_.emplace_back(patch_offset, thunk_offset);
- thunk_offset += kAdrpThunkSize;
- }
- }
- }
- return offset;
-}
-
-uint32_t Arm64RelativePatcher::ReserveSpaceEnd(uint32_t offset) {
- if (!fix_cortex_a53_843419_) {
- DCHECK(adrp_thunk_locations_.empty());
- } else {
- // Add thunks for the last method if any.
- if (reserved_adrp_thunks_ != adrp_thunk_locations_.size()) {
- size_t num_adrp_thunks = adrp_thunk_locations_.size() - reserved_adrp_thunks_;
- offset = CompiledMethod::AlignCode(offset, InstructionSet::kArm64) +
- kAdrpThunkSize * num_adrp_thunks;
- reserved_adrp_thunks_ = adrp_thunk_locations_.size();
- }
- }
- return ArmBaseRelativePatcher::ReserveSpaceEnd(offset);
-}
-
-uint32_t Arm64RelativePatcher::WriteThunks(OutputStream* out, uint32_t offset) {
- if (fix_cortex_a53_843419_) {
- if (!current_method_thunks_.empty()) {
- uint32_t aligned_offset = CompiledMethod::AlignCode(offset, InstructionSet::kArm64);
- if (kIsDebugBuild) {
- CHECK_ALIGNED(current_method_thunks_.size(), kAdrpThunkSize);
- size_t num_thunks = current_method_thunks_.size() / kAdrpThunkSize;
- CHECK_LE(num_thunks, processed_adrp_thunks_);
- for (size_t i = 0u; i != num_thunks; ++i) {
- const auto& entry = adrp_thunk_locations_[processed_adrp_thunks_ - num_thunks + i];
- CHECK_EQ(entry.second, aligned_offset + i * kAdrpThunkSize);
- }
- }
- uint32_t aligned_code_delta = aligned_offset - offset;
- if (aligned_code_delta != 0u && !WriteCodeAlignment(out, aligned_code_delta)) {
- return 0u;
- }
- if (!WriteMiscThunk(out, ArrayRef<const uint8_t>(current_method_thunks_))) {
- return 0u;
- }
- offset = aligned_offset + current_method_thunks_.size();
- current_method_thunks_.clear();
- }
- }
- return ArmBaseRelativePatcher::WriteThunks(out, offset);
-}
-
-void Arm64RelativePatcher::PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset, uint32_t
- target_offset) {
- DCHECK_LE(literal_offset + 4u, code->size());
- DCHECK_EQ(literal_offset & 3u, 0u);
- DCHECK_EQ(patch_offset & 3u, 0u);
- DCHECK_EQ(target_offset & 3u, 0u);
- uint32_t displacement = CalculateMethodCallDisplacement(patch_offset, target_offset & ~1u);
- DCHECK_EQ(displacement & 3u, 0u);
- DCHECK((displacement >> 27) == 0u || (displacement >> 27) == 31u); // 28-bit signed.
- uint32_t insn = (displacement & 0x0fffffffu) >> 2;
- insn |= 0x94000000; // BL
-
- // Check that we're just overwriting an existing BL.
- DCHECK_EQ(GetInsn(code, literal_offset) & 0xfc000000u, 0x94000000u);
- // Write the new BL.
- SetInsn(code, literal_offset, insn);
-}
-
-void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
- DCHECK_EQ(patch_offset & 3u, 0u);
- DCHECK_EQ(target_offset & 3u, 0u);
- uint32_t literal_offset = patch.LiteralOffset();
- uint32_t insn = GetInsn(code, literal_offset);
- uint32_t pc_insn_offset = patch.PcInsnOffset();
- uint32_t disp = target_offset - ((patch_offset - literal_offset + pc_insn_offset) & ~0xfffu);
- bool wide = (insn & 0x40000000) != 0;
- uint32_t shift = wide ? 3u : 2u;
- if (literal_offset == pc_insn_offset) {
- // Check it's an ADRP with imm == 0 (unset).
- DCHECK_EQ((insn & 0xffffffe0u), 0x90000000u)
- << literal_offset << ", " << pc_insn_offset << ", 0x" << std::hex << insn;
- if (fix_cortex_a53_843419_ && processed_adrp_thunks_ != adrp_thunk_locations_.size() &&
- adrp_thunk_locations_[processed_adrp_thunks_].first == patch_offset) {
- DCHECK(NeedsErratum843419Thunk(ArrayRef<const uint8_t>(*code),
- literal_offset, patch_offset));
- uint32_t thunk_offset = adrp_thunk_locations_[processed_adrp_thunks_].second;
- uint32_t adrp_disp = target_offset - (thunk_offset & ~0xfffu);
- uint32_t adrp = PatchAdrp(insn, adrp_disp);
-
- uint32_t out_disp = thunk_offset - patch_offset;
- DCHECK_EQ(out_disp & 3u, 0u);
- DCHECK((out_disp >> 27) == 0u || (out_disp >> 27) == 31u); // 28-bit signed.
- insn = (out_disp & 0x0fffffffu) >> shift;
- insn |= 0x14000000; // B <thunk>
-
- uint32_t back_disp = -out_disp;
- DCHECK_EQ(back_disp & 3u, 0u);
- DCHECK((back_disp >> 27) == 0u || (back_disp >> 27) == 31u); // 28-bit signed.
- uint32_t b_back = (back_disp & 0x0fffffffu) >> 2;
- b_back |= 0x14000000; // B <back>
- size_t thunks_code_offset = current_method_thunks_.size();
- current_method_thunks_.resize(thunks_code_offset + kAdrpThunkSize);
- SetInsn(&current_method_thunks_, thunks_code_offset, adrp);
- SetInsn(&current_method_thunks_, thunks_code_offset + 4u, b_back);
- static_assert(kAdrpThunkSize == 2 * 4u, "thunk has 2 instructions");
-
- processed_adrp_thunks_ += 1u;
- } else {
- insn = PatchAdrp(insn, disp);
- }
- // Write the new ADRP (or B to the erratum 843419 thunk).
- SetInsn(code, literal_offset, insn);
- } else {
- if ((insn & 0xfffffc00) == 0x91000000) {
- // ADD immediate, 64-bit with imm12 == 0 (unset).
- if (!kEmitCompilerReadBarrier) {
- DCHECK(patch.GetType() == LinkerPatch::Type::kMethodRelative ||
- patch.GetType() == LinkerPatch::Type::kTypeRelative ||
- patch.GetType() == LinkerPatch::Type::kStringRelative) << patch.GetType();
- } else {
- // With the read barrier (non-Baker) enabled, it could be kStringBssEntry or kTypeBssEntry.
- DCHECK(patch.GetType() == LinkerPatch::Type::kMethodRelative ||
- patch.GetType() == LinkerPatch::Type::kTypeRelative ||
- patch.GetType() == LinkerPatch::Type::kStringRelative ||
- patch.GetType() == LinkerPatch::Type::kTypeBssEntry ||
- patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType();
- }
- shift = 0u; // No shift for ADD.
- } else {
- // LDR/STR 32-bit or 64-bit with imm12 == 0 (unset).
- DCHECK(patch.GetType() == LinkerPatch::Type::kDataBimgRelRo ||
- patch.GetType() == LinkerPatch::Type::kMethodBssEntry ||
- patch.GetType() == LinkerPatch::Type::kTypeBssEntry ||
- patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType();
- DCHECK_EQ(insn & 0xbfbffc00, 0xb9000000) << std::hex << insn;
- }
- if (kIsDebugBuild) {
- uint32_t adrp = GetInsn(code, pc_insn_offset);
- if ((adrp & 0x9f000000u) != 0x90000000u) {
- CHECK(fix_cortex_a53_843419_);
- CHECK_EQ(adrp & 0xfc000000u, 0x14000000u); // B <thunk>
- CHECK_ALIGNED(current_method_thunks_.size(), kAdrpThunkSize);
- size_t num_thunks = current_method_thunks_.size() / kAdrpThunkSize;
- CHECK_LE(num_thunks, processed_adrp_thunks_);
- uint32_t b_offset = patch_offset - literal_offset + pc_insn_offset;
- for (size_t i = processed_adrp_thunks_ - num_thunks; ; ++i) {
- CHECK_NE(i, processed_adrp_thunks_);
- if (adrp_thunk_locations_[i].first == b_offset) {
- size_t idx = num_thunks - (processed_adrp_thunks_ - i);
- adrp = GetInsn(&current_method_thunks_, idx * kAdrpThunkSize);
- break;
- }
- }
- }
- CHECK_EQ(adrp & 0x9f00001fu, // Check that pc_insn_offset points
- 0x90000000 | ((insn >> 5) & 0x1fu)); // to ADRP with matching register.
- }
- uint32_t imm12 = (disp & 0xfffu) >> shift;
- insn = (insn & ~(0xfffu << 10)) | (imm12 << 10);
- SetInsn(code, literal_offset, insn);
- }
-}
-
-void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) {
- DCHECK_ALIGNED(patch_offset, 4u);
- uint32_t literal_offset = patch.LiteralOffset();
- DCHECK_ALIGNED(literal_offset, 4u);
- DCHECK_LT(literal_offset, code->size());
- uint32_t insn = GetInsn(code, literal_offset);
- DCHECK_EQ(insn & 0xffffffe0u, 0xb5000000); // CBNZ Xt, +0 (unpatched)
- ThunkKey key = GetBakerThunkKey(patch);
- uint32_t target_offset = GetThunkTargetOffset(key, patch_offset);
- DCHECK_ALIGNED(target_offset, 4u);
- uint32_t disp = target_offset - patch_offset;
- DCHECK((disp >> 20) == 0u || (disp >> 20) == 4095u); // 21-bit signed.
- insn |= (disp << (5 - 2)) & 0x00ffffe0u; // Shift bits 2-20 to 5-23.
- SetInsn(code, literal_offset, insn);
-}
-
-uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) {
- switch (key.GetType()) {
- case ThunkType::kMethodCall:
- return kMaxMethodCallPositiveDisplacement;
- case ThunkType::kBakerReadBarrier:
- return kMaxBcondPositiveDisplacement;
- }
-}
-
-uint32_t Arm64RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) {
- switch (key.GetType()) {
- case ThunkType::kMethodCall:
- return kMaxMethodCallNegativeDisplacement;
- case ThunkType::kBakerReadBarrier:
- return kMaxBcondNegativeDisplacement;
- }
-}
-
-uint32_t Arm64RelativePatcher::PatchAdrp(uint32_t adrp, uint32_t disp) {
- return (adrp & 0x9f00001fu) | // Clear offset bits, keep ADRP with destination reg.
- // Bottom 12 bits are ignored, the next 2 lowest bits are encoded in bits 29-30.
- ((disp & 0x00003000u) << (29 - 12)) |
- // The next 16 bits are encoded in bits 5-22.
- ((disp & 0xffffc000u) >> (12 + 2 - 5)) |
- // Since the target_offset is based on the beginning of the oat file and the
- // image space precedes the oat file, the target_offset into image space will
- // be negative yet passed as uint32_t. Therefore we limit the displacement
- // to +-2GiB (rather than the maximim +-4GiB) and determine the sign bit from
- // the highest bit of the displacement. This is encoded in bit 23.
- ((disp & 0x80000000u) >> (31 - 23));
-}
-
-bool Arm64RelativePatcher::NeedsErratum843419Thunk(ArrayRef<const uint8_t> code,
- uint32_t literal_offset,
- uint32_t patch_offset) {
- DCHECK_EQ(patch_offset & 0x3u, 0u);
- if ((patch_offset & 0xff8) == 0xff8) { // ...ff8 or ...ffc
- uint32_t adrp = GetInsn(code, literal_offset);
- DCHECK_EQ(adrp & 0x9f000000, 0x90000000);
- uint32_t next_offset = patch_offset + 4u;
- uint32_t next_insn = GetInsn(code, literal_offset + 4u);
-
- // Below we avoid patching sequences where the adrp is followed by a load which can easily
- // be proved to be aligned.
-
- // First check if the next insn is the LDR using the result of the ADRP.
- // LDR <Wt>, [<Xn>, #pimm], where <Xn> == ADRP destination reg.
- if ((next_insn & 0xffc00000) == 0xb9400000 &&
- (((next_insn >> 5) ^ adrp) & 0x1f) == 0) {
- return false;
- }
-
- // And since LinkerPatch::Type::k{Method,Type,String}Relative is using the result
- // of the ADRP for an ADD immediate, check for that as well. We generalize a bit
- // to include ADD/ADDS/SUB/SUBS immediate that either uses the ADRP destination
- // or stores the result to a different register.
- if ((next_insn & 0x1f000000) == 0x11000000 &&
- ((((next_insn >> 5) ^ adrp) & 0x1f) == 0 || ((next_insn ^ adrp) & 0x1f) != 0)) {
- return false;
- }
-
- // LDR <Wt>, <label> is always aligned and thus it doesn't cause boundary crossing.
- if ((next_insn & 0xff000000) == 0x18000000) {
- return false;
- }
-
- // LDR <Xt>, <label> is aligned iff the pc + displacement is a multiple of 8.
- if ((next_insn & 0xff000000) == 0x58000000) {
- bool is_aligned_load = (((next_offset >> 2) ^ (next_insn >> 5)) & 1) == 0;
- return !is_aligned_load;
- }
-
- // LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned loads, as SP is
- // guaranteed to be 128-bits aligned and <pimm> is multiple of the load size.
- if ((next_insn & 0xbfc003e0) == 0xb94003e0) {
- return false;
- }
- return true;
- }
- return false;
-}
-
-void Arm64RelativePatcher::SetInsn(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
- DCHECK_LE(offset + 4u, code->size());
- DCHECK_EQ(offset & 3u, 0u);
- uint8_t* addr = &(*code)[offset];
- addr[0] = (value >> 0) & 0xff;
- addr[1] = (value >> 8) & 0xff;
- addr[2] = (value >> 16) & 0xff;
- addr[3] = (value >> 24) & 0xff;
-}
-
-uint32_t Arm64RelativePatcher::GetInsn(ArrayRef<const uint8_t> code, uint32_t offset) {
- DCHECK_LE(offset + 4u, code.size());
- DCHECK_EQ(offset & 3u, 0u);
- const uint8_t* addr = &code[offset];
- return
- (static_cast<uint32_t>(addr[0]) << 0) +
- (static_cast<uint32_t>(addr[1]) << 8) +
- (static_cast<uint32_t>(addr[2]) << 16)+
- (static_cast<uint32_t>(addr[3]) << 24);
-}
-
-template <typename Alloc>
-uint32_t Arm64RelativePatcher::GetInsn(std::vector<uint8_t, Alloc>* code, uint32_t offset) {
- return GetInsn(ArrayRef<const uint8_t>(*code), offset);
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h
deleted file mode 100644
index 9dc289da44..0000000000
--- a/compiler/linker/arm64/relative_patcher_arm64.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_
-#define ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_
-
-#include "base/array_ref.h"
-#include "linker/arm/relative_patcher_arm_base.h"
-
-namespace art {
-
-namespace arm64 {
-class Arm64Assembler;
-} // namespace arm64
-
-namespace linker {
-
-class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher {
- public:
- Arm64RelativePatcher(RelativePatcherThunkProvider* thunk_provider,
- RelativePatcherTargetProvider* target_provider,
- const Arm64InstructionSetFeatures* features);
-
- uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) OVERRIDE;
- uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
- uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- void PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) OVERRIDE;
-
- protected:
- uint32_t MaxPositiveDisplacement(const ThunkKey& key) OVERRIDE;
- uint32_t MaxNegativeDisplacement(const ThunkKey& key) OVERRIDE;
-
- private:
- static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp);
-
- static bool NeedsErratum843419Thunk(ArrayRef<const uint8_t> code, uint32_t literal_offset,
- uint32_t patch_offset);
- void SetInsn(std::vector<uint8_t>* code, uint32_t offset, uint32_t value);
- static uint32_t GetInsn(ArrayRef<const uint8_t> code, uint32_t offset);
-
- template <typename Alloc>
- static uint32_t GetInsn(std::vector<uint8_t, Alloc>* code, uint32_t offset);
-
- const bool fix_cortex_a53_843419_;
- // Map original patch_offset to thunk offset.
- std::vector<std::pair<uint32_t, uint32_t>> adrp_thunk_locations_;
- size_t reserved_adrp_thunks_;
- size_t processed_adrp_thunks_;
- std::vector<uint8_t> current_method_thunks_;
-
- friend class Arm64RelativePatcherTest;
-
- DISALLOW_COPY_AND_ASSIGN(Arm64RelativePatcher);
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_
diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc
deleted file mode 100644
index 393733dd0c..0000000000
--- a/compiler/linker/arm64/relative_patcher_arm64_test.cc
+++ /dev/null
@@ -1,1405 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/arm64/relative_patcher_arm64.h"
-
-#include "arch/arm64/instruction_set_features_arm64.h"
-#include "base/casts.h"
-#include "linker/relative_patcher_test.h"
-#include "lock_word.h"
-#include "mirror/array-inl.h"
-#include "mirror/object.h"
-#include "oat_quick_method_header.h"
-#include "optimizing/code_generator_arm64.h"
-#include "optimizing/optimizing_unit_test.h"
-
-namespace art {
-namespace linker {
-
-class Arm64RelativePatcherTest : public RelativePatcherTest {
- public:
- explicit Arm64RelativePatcherTest(const std::string& variant)
- : RelativePatcherTest(InstructionSet::kArm64, variant) { }
-
- protected:
- static const uint8_t kCallRawCode[];
- static const ArrayRef<const uint8_t> kCallCode;
- static const uint8_t kNopRawCode[];
- static const ArrayRef<const uint8_t> kNopCode;
-
- // NOP instruction.
- static constexpr uint32_t kNopInsn = 0xd503201f;
-
- // All branches can be created from kBlPlus0 or kBPlus0 by adding the low 26 bits.
- static constexpr uint32_t kBlPlus0 = 0x94000000u;
- static constexpr uint32_t kBPlus0 = 0x14000000u;
-
- // Special BL values.
- static constexpr uint32_t kBlPlusMax = 0x95ffffffu;
- static constexpr uint32_t kBlMinusMax = 0x96000000u;
-
- // LDR immediate, 32-bit, unsigned offset.
- static constexpr uint32_t kLdrWInsn = 0xb9400000u;
-
- // LDR register, 32-bit, LSL #2.
- static constexpr uint32_t kLdrWLsl2Insn = 0xb8607800u;
-
- // LDUR, 32-bit.
- static constexpr uint32_t kLdurWInsn = 0xb8400000u;
-
- // ADD/ADDS/SUB/SUBS immediate, 64-bit.
- static constexpr uint32_t kAddXInsn = 0x91000000u;
- static constexpr uint32_t kAddsXInsn = 0xb1000000u;
- static constexpr uint32_t kSubXInsn = 0xd1000000u;
- static constexpr uint32_t kSubsXInsn = 0xf1000000u;
-
- // LDUR x2, [sp, #4], i.e. unaligned load crossing 64-bit boundary (assuming aligned sp).
- static constexpr uint32_t kLdurInsn = 0xf840405fu;
-
- // LDR w12, <label> and LDR x12, <label>. Bits 5-23 contain label displacement in 4-byte units.
- static constexpr uint32_t kLdrWPcRelInsn = 0x1800000cu;
- static constexpr uint32_t kLdrXPcRelInsn = 0x5800000cu;
-
- // LDR w13, [SP, #<pimm>] and LDR x13, [SP, #<pimm>]. Bits 10-21 contain displacement from SP
- // in units of 4-bytes (for 32-bit load) or 8-bytes (for 64-bit load).
- static constexpr uint32_t kLdrWSpRelInsn = 0xb94003edu;
- static constexpr uint32_t kLdrXSpRelInsn = 0xf94003edu;
-
- // CBNZ x17, +0. Bits 5-23 are a placeholder for target offset from PC in units of 4-bytes.
- static constexpr uint32_t kCbnzIP1Plus0Insn = 0xb5000011u;
-
- void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
- CHECK_LE(pos, code->size());
- const uint8_t insn_code[] = {
- static_cast<uint8_t>(insn),
- static_cast<uint8_t>(insn >> 8),
- static_cast<uint8_t>(insn >> 16),
- static_cast<uint8_t>(insn >> 24),
- };
- static_assert(sizeof(insn_code) == 4u, "Invalid sizeof(insn_code).");
- code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
- }
-
- void PushBackInsn(std::vector<uint8_t>* code, uint32_t insn) {
- InsertInsn(code, code->size(), insn);
- }
-
- std::vector<uint8_t> RawCode(std::initializer_list<uint32_t> insns) {
- std::vector<uint8_t> raw_code;
- raw_code.reserve(insns.size() * 4u);
- for (uint32_t insn : insns) {
- PushBackInsn(&raw_code, insn);
- }
- return raw_code;
- }
-
- uint32_t Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
- const ArrayRef<const LinkerPatch>& method1_patches,
- const ArrayRef<const uint8_t>& last_method_code,
- const ArrayRef<const LinkerPatch>& last_method_patches,
- uint32_t distance_without_thunks) {
- CHECK_EQ(distance_without_thunks % kArm64Alignment, 0u);
- uint32_t method1_offset =
- kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
- AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
- const uint32_t gap_start = method1_offset + method1_code.size();
-
- // We want to put the method3 at a very precise offset.
- const uint32_t last_method_offset = method1_offset + distance_without_thunks;
- CHECK_ALIGNED(last_method_offset, kArm64Alignment);
- const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
-
- // Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB).
- // (This allows deduplicating the small chunks to avoid using 256MiB of memory for +-128MiB
- // offsets by this test. Making the first chunk bigger makes it easy to give all intermediate
- // methods the same alignment of the end, so the thunk insertion adds a predictable size as
- // long as it's after the first chunk.)
- uint32_t method_idx = 2u;
- constexpr uint32_t kSmallChunkSize = 2 * MB;
- std::vector<uint8_t> gap_code;
- uint32_t gap_size = gap_end - gap_start;
- uint32_t num_small_chunks = std::max(gap_size / kSmallChunkSize, 1u) - 1u;
- uint32_t chunk_start = gap_start;
- uint32_t chunk_size = gap_size - num_small_chunks * kSmallChunkSize;
- for (uint32_t i = 0; i <= num_small_chunks; ++i) { // num_small_chunks+1 iterations.
- uint32_t chunk_code_size =
- chunk_size - CodeAlignmentSize(chunk_start) - sizeof(OatQuickMethodHeader);
- gap_code.resize(chunk_code_size, 0u);
- AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code));
- method_idx += 1u;
- chunk_start += chunk_size;
- chunk_size = kSmallChunkSize; // For all but the first chunk.
- DCHECK_EQ(CodeAlignmentSize(gap_end), CodeAlignmentSize(chunk_start));
- }
-
- // Add the last method and link
- AddCompiledMethod(MethodRef(method_idx), last_method_code, last_method_patches);
- Link();
-
- // Check assumptions.
- CHECK_EQ(GetMethodOffset(1), method1_offset);
- auto last_result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
- CHECK(last_result.first);
- // There may be a thunk before method2.
- if (last_result.second != last_method_offset) {
- // Thunk present. Check that there's only one.
- uint32_t thunk_end =
- CompiledCode::AlignCode(gap_end, InstructionSet::kArm64) + MethodCallThunkSize();
- uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end);
- CHECK_EQ(last_result.second, header_offset + sizeof(OatQuickMethodHeader));
- }
- return method_idx;
- }
-
- uint32_t GetMethodOffset(uint32_t method_idx) {
- auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
- CHECK(result.first);
- CHECK_ALIGNED(result.second, 4u);
- return result.second;
- }
-
- std::vector<uint8_t> CompileThunk(const LinkerPatch& patch,
- /*out*/ std::string* debug_name = nullptr) {
- OptimizingUnitTestHelper helper;
- HGraph* graph = helper.CreateGraph();
- std::string error_msg;
- Arm64FeaturesUniquePtr features =
- Arm64InstructionSetFeatures::FromVariant("default", &error_msg);
- CompilerOptions options;
- arm64::CodeGeneratorARM64 codegen(graph, *features, options);
- ArenaVector<uint8_t> code(helper.GetAllocator()->Adapter());
- codegen.EmitThunkCode(patch, &code, debug_name);
- return std::vector<uint8_t>(code.begin(), code.end());
- }
-
- void AddCompiledMethod(
- MethodReference method_ref,
- const ArrayRef<const uint8_t>& code,
- const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) {
- RelativePatcherTest::AddCompiledMethod(method_ref, code, patches);
-
- // Make sure the ThunkProvider has all the necessary thunks.
- for (const LinkerPatch& patch : patches) {
- if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch ||
- patch.GetType() == LinkerPatch::Type::kCallRelative) {
- std::string debug_name;
- std::vector<uint8_t> thunk_code = CompileThunk(patch, &debug_name);
- thunk_provider_.SetThunkCode(patch, ArrayRef<const uint8_t>(thunk_code), debug_name);
- }
- }
- }
-
- std::vector<uint8_t> CompileMethodCallThunk() {
- LinkerPatch patch = LinkerPatch::RelativeCodePatch(/* literal_offset */ 0u,
- /* target_dex_file*/ nullptr,
- /* target_method_idx */ 0u);
- return CompileThunk(patch);
- }
-
- uint32_t MethodCallThunkSize() {
- return CompileMethodCallThunk().size();
- }
-
- bool CheckThunk(uint32_t thunk_offset) {
- const std::vector<uint8_t> expected_code = CompileMethodCallThunk();
- if (output_.size() < thunk_offset + expected_code.size()) {
- LOG(ERROR) << "output_.size() == " << output_.size() << " < "
- << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
- return false;
- }
- ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
- if (linked_code == ArrayRef<const uint8_t>(expected_code)) {
- return true;
- }
- // Log failure info.
- DumpDiff(ArrayRef<const uint8_t>(expected_code), linked_code);
- return false;
- }
-
- std::vector<uint8_t> GenNops(size_t num_nops) {
- std::vector<uint8_t> result;
- result.reserve(num_nops * 4u);
- for (size_t i = 0; i != num_nops; ++i) {
- PushBackInsn(&result, kNopInsn);
- }
- return result;
- }
-
- std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
- std::vector<uint8_t> result;
- result.reserve(num_nops * 4u + 4u);
- for (size_t i = 0; i != num_nops; ++i) {
- PushBackInsn(&result, kNopInsn);
- }
- PushBackInsn(&result, bl);
- return result;
- }
-
- std::vector<uint8_t> GenNopsAndAdrpAndUse(size_t num_nops,
- uint32_t method_offset,
- uint32_t target_offset,
- uint32_t use_insn) {
- std::vector<uint8_t> result;
- result.reserve(num_nops * 4u + 8u);
- for (size_t i = 0; i != num_nops; ++i) {
- PushBackInsn(&result, kNopInsn);
- }
- CHECK_ALIGNED(method_offset, 4u);
- CHECK_ALIGNED(target_offset, 4u);
- uint32_t adrp_offset = method_offset + num_nops * 4u;
- uint32_t disp = target_offset - (adrp_offset & ~0xfffu);
- if (use_insn == kLdrWInsn) {
- DCHECK_ALIGNED(disp, 1u << 2);
- use_insn |= 1 | // LDR x1, [x0, #(imm12 << 2)]
- ((disp & 0xfffu) << (10 - 2)); // imm12 = ((disp & 0xfffu) >> 2) is at bit 10.
- } else if (use_insn == kAddXInsn) {
- use_insn |= 1 | // ADD x1, x0, #imm
- (disp & 0xfffu) << 10; // imm12 = (disp & 0xfffu) is at bit 10.
- } else {
- LOG(FATAL) << "Unexpected instruction: 0x" << std::hex << use_insn;
- }
- uint32_t adrp = 0x90000000u | // ADRP x0, +SignExtend(immhi:immlo:Zeros(12), 64)
- ((disp & 0x3000u) << (29 - 12)) | // immlo = ((disp & 0x3000u) >> 12) is at bit 29,
- ((disp & 0xffffc000) >> (14 - 5)) | // immhi = (disp >> 14) is at bit 5,
- // We take the sign bit from the disp, limiting disp to +- 2GiB.
- ((disp & 0x80000000) >> (31 - 23)); // sign bit in immhi is at bit 23.
- PushBackInsn(&result, adrp);
- PushBackInsn(&result, use_insn);
- return result;
- }
-
- std::vector<uint8_t> GenNopsAndAdrpLdr(size_t num_nops,
- uint32_t method_offset,
- uint32_t target_offset) {
- return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kLdrWInsn);
- }
-
- void TestNopsAdrpLdr(size_t num_nops, uint32_t bss_begin, uint32_t string_entry_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
- bss_begin_ = bss_begin;
- auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u); // Unpatched.
- const LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex),
- LinkerPatch::StringBssEntryPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex),
- };
- AddCompiledMethod(MethodRef(1u),
- ArrayRef<const uint8_t>(code),
- ArrayRef<const LinkerPatch>(patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t target_offset = bss_begin_ + string_entry_offset;
- auto expected_code = GenNopsAndAdrpLdr(num_nops, method1_offset, target_offset);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
- }
-
- std::vector<uint8_t> GenNopsAndAdrpAdd(size_t num_nops,
- uint32_t method_offset,
- uint32_t target_offset) {
- return GenNopsAndAdrpAndUse(num_nops, method_offset, target_offset, kAddXInsn);
- }
-
- void TestNopsAdrpAdd(size_t num_nops, uint32_t string_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_offset);
- auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u); // Unpatched.
- const LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex),
- LinkerPatch::RelativeStringPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex),
- };
- AddCompiledMethod(MethodRef(1u),
- ArrayRef<const uint8_t>(code),
- ArrayRef<const LinkerPatch>(patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- auto expected_code = GenNopsAndAdrpAdd(num_nops, method1_offset, string_offset);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
- }
-
- void PrepareNopsAdrpInsn2Ldr(size_t num_nops,
- uint32_t insn2,
- uint32_t bss_begin,
- uint32_t string_entry_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
- bss_begin_ = bss_begin;
- auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u); // Unpatched.
- InsertInsn(&code, num_nops * 4u + 4u, insn2);
- const LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex),
- LinkerPatch::StringBssEntryPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex),
- };
- AddCompiledMethod(MethodRef(1u),
- ArrayRef<const uint8_t>(code),
- ArrayRef<const LinkerPatch>(patches));
- Link();
- }
-
- void PrepareNopsAdrpInsn2Add(size_t num_nops, uint32_t insn2, uint32_t string_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_offset);
- auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u); // Unpatched.
- InsertInsn(&code, num_nops * 4u + 4u, insn2);
- const LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(num_nops * 4u , nullptr, num_nops * 4u, kStringIndex),
- LinkerPatch::RelativeStringPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex),
- };
- AddCompiledMethod(MethodRef(1u),
- ArrayRef<const uint8_t>(code),
- ArrayRef<const LinkerPatch>(patches));
- Link();
- }
-
- void TestNopsAdrpInsn2AndUse(size_t num_nops,
- uint32_t insn2,
- uint32_t target_offset,
- uint32_t use_insn) {
- uint32_t method1_offset = GetMethodOffset(1u);
- auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn);
- InsertInsn(&expected_code, num_nops * 4u + 4u, insn2);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
- }
-
- void TestNopsAdrpInsn2AndUseHasThunk(size_t num_nops,
- uint32_t insn2,
- uint32_t target_offset,
- uint32_t use_insn) {
- uint32_t method1_offset = GetMethodOffset(1u);
- CHECK(!compiled_method_refs_.empty());
- CHECK_EQ(compiled_method_refs_[0].index, 1u);
- CHECK_EQ(compiled_method_refs_.size(), compiled_methods_.size());
- uint32_t method1_size = compiled_methods_[0]->GetQuickCode().size();
- uint32_t thunk_offset =
- CompiledCode::AlignCode(method1_offset + method1_size, InstructionSet::kArm64);
- uint32_t b_diff = thunk_offset - (method1_offset + num_nops * 4u);
- CHECK_ALIGNED(b_diff, 4u);
- ASSERT_LT(b_diff, 128 * MB);
- uint32_t b_out = kBPlus0 + ((b_diff >> 2) & 0x03ffffffu);
- uint32_t b_in = kBPlus0 + ((-b_diff >> 2) & 0x03ffffffu);
-
- auto expected_code = GenNopsAndAdrpAndUse(num_nops, method1_offset, target_offset, use_insn);
- InsertInsn(&expected_code, num_nops * 4u + 4u, insn2);
- // Replace adrp with bl.
- expected_code.erase(expected_code.begin() + num_nops * 4u,
- expected_code.begin() + num_nops * 4u + 4u);
- InsertInsn(&expected_code, num_nops * 4u, b_out);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-
- auto expected_thunk_code = GenNopsAndAdrpLdr(0u, thunk_offset, target_offset);
- ASSERT_EQ(expected_thunk_code.size(), 8u);
- expected_thunk_code.erase(expected_thunk_code.begin() + 4u, expected_thunk_code.begin() + 8u);
- InsertInsn(&expected_thunk_code, 4u, b_in);
- ASSERT_EQ(expected_thunk_code.size(), 8u);
-
- uint32_t thunk_size = MethodCallThunkSize();
- ASSERT_EQ(thunk_offset + thunk_size, output_.size());
- ASSERT_EQ(thunk_size, expected_thunk_code.size());
- ArrayRef<const uint8_t> thunk_code(&output_[thunk_offset], thunk_size);
- if (ArrayRef<const uint8_t>(expected_thunk_code) != thunk_code) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk_code), thunk_code);
- FAIL();
- }
- }
-
- void TestAdrpInsn2Ldr(uint32_t insn2,
- uint32_t adrp_offset,
- bool has_thunk,
- uint32_t bss_begin,
- uint32_t string_entry_offset) {
- uint32_t method1_offset =
- kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
- ASSERT_LT(method1_offset, adrp_offset);
- CHECK_ALIGNED(adrp_offset, 4u);
- uint32_t num_nops = (adrp_offset - method1_offset) / 4u;
- PrepareNopsAdrpInsn2Ldr(num_nops, insn2, bss_begin, string_entry_offset);
- uint32_t target_offset = bss_begin_ + string_entry_offset;
- if (has_thunk) {
- TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, target_offset, kLdrWInsn);
- } else {
- TestNopsAdrpInsn2AndUse(num_nops, insn2, target_offset, kLdrWInsn);
- }
- ASSERT_EQ(method1_offset, GetMethodOffset(1u)); // If this fails, num_nops is wrong.
- }
-
- void TestAdrpLdurLdr(uint32_t adrp_offset,
- bool has_thunk,
- uint32_t bss_begin,
- uint32_t string_entry_offset) {
- TestAdrpInsn2Ldr(kLdurInsn, adrp_offset, has_thunk, bss_begin, string_entry_offset);
- }
-
- void TestAdrpLdrPcRelLdr(uint32_t pcrel_ldr_insn,
- int32_t pcrel_disp,
- uint32_t adrp_offset,
- bool has_thunk,
- uint32_t bss_begin,
- uint32_t string_entry_offset) {
- ASSERT_LT(pcrel_disp, 0x100000);
- ASSERT_GE(pcrel_disp, -0x100000);
- ASSERT_EQ(pcrel_disp & 0x3, 0);
- uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5);
- TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, bss_begin, string_entry_offset);
- }
-
- void TestAdrpLdrSpRelLdr(uint32_t sprel_ldr_insn,
- uint32_t sprel_disp_in_load_units,
- uint32_t adrp_offset,
- bool has_thunk,
- uint32_t bss_begin,
- uint32_t string_entry_offset) {
- ASSERT_LT(sprel_disp_in_load_units, 0x1000u);
- uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
- TestAdrpInsn2Ldr(insn2, adrp_offset, has_thunk, bss_begin, string_entry_offset);
- }
-
- void TestAdrpInsn2Add(uint32_t insn2,
- uint32_t adrp_offset,
- bool has_thunk,
- uint32_t string_offset) {
- uint32_t method1_offset =
- kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
- ASSERT_LT(method1_offset, adrp_offset);
- CHECK_ALIGNED(adrp_offset, 4u);
- uint32_t num_nops = (adrp_offset - method1_offset) / 4u;
- PrepareNopsAdrpInsn2Add(num_nops, insn2, string_offset);
- if (has_thunk) {
- TestNopsAdrpInsn2AndUseHasThunk(num_nops, insn2, string_offset, kAddXInsn);
- } else {
- TestNopsAdrpInsn2AndUse(num_nops, insn2, string_offset, kAddXInsn);
- }
- ASSERT_EQ(method1_offset, GetMethodOffset(1u)); // If this fails, num_nops is wrong.
- }
-
- void TestAdrpLdurAdd(uint32_t adrp_offset, bool has_thunk, uint32_t string_offset) {
- TestAdrpInsn2Add(kLdurInsn, adrp_offset, has_thunk, string_offset);
- }
-
- void TestAdrpLdrPcRelAdd(uint32_t pcrel_ldr_insn,
- int32_t pcrel_disp,
- uint32_t adrp_offset,
- bool has_thunk,
- uint32_t string_offset) {
- ASSERT_LT(pcrel_disp, 0x100000);
- ASSERT_GE(pcrel_disp, -0x100000);
- ASSERT_EQ(pcrel_disp & 0x3, 0);
- uint32_t insn2 = pcrel_ldr_insn | (((static_cast<uint32_t>(pcrel_disp) >> 2) & 0x7ffffu) << 5);
- TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
- }
-
- void TestAdrpLdrSpRelAdd(uint32_t sprel_ldr_insn,
- uint32_t sprel_disp_in_load_units,
- uint32_t adrp_offset,
- bool has_thunk,
- uint32_t string_offset) {
- ASSERT_LT(sprel_disp_in_load_units, 0x1000u);
- uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
- TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
- }
-
- static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) {
- return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierFieldData(base_reg, holder_reg);
- }
-
- static uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) {
- return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierArrayData(base_reg);
- }
-
- static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) {
- return arm64::CodeGeneratorARM64::EncodeBakerReadBarrierGcRootData(root_reg);
- }
-
- std::vector<uint8_t> CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) {
- const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
- /* literal_offset */ 0u, EncodeBakerReadBarrierFieldData(base_reg, holder_reg));
- return CompileThunk(patch);
- }
-
- std::vector<uint8_t> CompileBakerArrayThunk(uint32_t base_reg) {
- LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
- /* literal_offset */ 0u, EncodeBakerReadBarrierArrayData(base_reg));
- return CompileThunk(patch);
- }
-
- std::vector<uint8_t> CompileBakerGcRootThunk(uint32_t root_reg) {
- LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
- /* literal_offset */ 0u, EncodeBakerReadBarrierGcRootData(root_reg));
- return CompileThunk(patch);
- }
-
- uint32_t GetOutputInsn(uint32_t offset) {
- CHECK_LE(offset, output_.size());
- CHECK_GE(output_.size() - offset, 4u);
- return (static_cast<uint32_t>(output_[offset]) << 0) |
- (static_cast<uint32_t>(output_[offset + 1]) << 8) |
- (static_cast<uint32_t>(output_[offset + 2]) << 16) |
- (static_cast<uint32_t>(output_[offset + 3]) << 24);
- }
-
- void TestBakerField(uint32_t offset, uint32_t ref_reg);
-};
-
-const uint8_t Arm64RelativePatcherTest::kCallRawCode[] = {
- 0x00, 0x00, 0x00, 0x94
-};
-
-const ArrayRef<const uint8_t> Arm64RelativePatcherTest::kCallCode(kCallRawCode);
-
-const uint8_t Arm64RelativePatcherTest::kNopRawCode[] = {
- 0x1f, 0x20, 0x03, 0xd5
-};
-
-const ArrayRef<const uint8_t> Arm64RelativePatcherTest::kNopCode(kNopRawCode);
-
-class Arm64RelativePatcherTestDefault : public Arm64RelativePatcherTest {
- public:
- Arm64RelativePatcherTestDefault() : Arm64RelativePatcherTest("default") { }
-};
-
-class Arm64RelativePatcherTestDenver64 : public Arm64RelativePatcherTest {
- public:
- Arm64RelativePatcherTestDenver64() : Arm64RelativePatcherTest("denver64") { }
-};
-
-TEST_F(Arm64RelativePatcherTestDefault, CallSelf) {
- const LinkerPatch patches[] = {
- LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- const std::vector<uint8_t> expected_code = RawCode({kBlPlus0});
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, CallOther) {
- const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
- const LinkerPatch method2_patches[] = {
- LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t method2_offset = GetMethodOffset(2u);
- uint32_t diff_after = method2_offset - method1_offset;
- CHECK_ALIGNED(diff_after, 4u);
- ASSERT_LT(diff_after >> 2, 1u << 8); // Simple encoding, (diff_after >> 2) fits into 8 bits.
- const std::vector<uint8_t> method1_expected_code = RawCode({kBlPlus0 + (diff_after >> 2)});
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
- uint32_t diff_before = method1_offset - method2_offset;
- CHECK_ALIGNED(diff_before, 4u);
- ASSERT_GE(diff_before, -1u << 27);
- auto method2_expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff_before >> 2) & 0x03ffffffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, CallTrampoline) {
- const LinkerPatch patches[] = {
- LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t diff = kTrampolineOffset - method1_offset;
- ASSERT_EQ(diff & 1u, 0u);
- ASSERT_GE(diff, -1u << 9); // Simple encoding, -256 <= (diff >> 1) < 0 (checked as unsigned).
- auto expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff >> 2) & 0x03ffffffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, CallTrampolineTooFar) {
- constexpr uint32_t missing_method_index = 1024u;
- auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0);
- constexpr uint32_t bl_offset_in_last_method = 1u * 4u; // After NOPs.
- ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
- ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
- const LinkerPatch last_method_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, missing_method_index),
- };
-
- constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
- uint32_t last_method_idx = Create2MethodsWithGap(
- kNopCode, ArrayRef<const LinkerPatch>(), last_method_code,
- ArrayRef<const LinkerPatch>(last_method_patches),
- just_over_max_negative_disp - bl_offset_in_last_method);
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t last_method_offset = GetMethodOffset(last_method_idx);
- ASSERT_EQ(method1_offset,
- last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
- ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
-
- // Check linked code.
- uint32_t thunk_offset =
- CompiledCode::AlignCode(last_method_offset + last_method_code.size(), InstructionSet::kArm64);
- uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
- CHECK_ALIGNED(diff, 4u);
- ASSERT_LT(diff, 128 * MB);
- auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
- ArrayRef<const uint8_t>(expected_code)));
- EXPECT_TRUE(CheckThunk(thunk_offset));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarAfter) {
- auto method1_raw_code = GenNopsAndBl(1u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method1 = 1u * 4u; // After NOPs.
- ArrayRef<const uint8_t> method1_code(method1_raw_code);
- ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
- uint32_t expected_last_method_idx = 65; // Based on 2MiB chunks in Create2MethodsWithGap().
- const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx),
- };
-
- constexpr uint32_t max_positive_disp = 128 * MB - 4u;
- uint32_t last_method_idx = Create2MethodsWithGap(method1_code,
- ArrayRef<const LinkerPatch>(method1_patches),
- kNopCode,
- ArrayRef<const LinkerPatch>(),
- bl_offset_in_method1 + max_positive_disp);
- ASSERT_EQ(expected_last_method_idx, last_method_idx);
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t last_method_offset = GetMethodOffset(last_method_idx);
- ASSERT_EQ(method1_offset + bl_offset_in_method1 + max_positive_disp, last_method_offset);
-
- // Check linked code.
- auto expected_code = GenNopsAndBl(1u, kBlPlusMax);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarBefore) {
- auto last_method_raw_code = GenNopsAndBl(0u, kBlPlus0);
- constexpr uint32_t bl_offset_in_last_method = 0u * 4u; // After NOPs.
- ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
- ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
- const LinkerPatch last_method_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
- };
-
- constexpr uint32_t max_negative_disp = 128 * MB;
- uint32_t last_method_idx = Create2MethodsWithGap(kNopCode,
- ArrayRef<const LinkerPatch>(),
- last_method_code,
- ArrayRef<const LinkerPatch>(last_method_patches),
- max_negative_disp - bl_offset_in_last_method);
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t last_method_offset = GetMethodOffset(last_method_idx);
- ASSERT_EQ(method1_offset, last_method_offset + bl_offset_in_last_method - max_negative_disp);
-
- // Check linked code.
- auto expected_code = GenNopsAndBl(0u, kBlMinusMax);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
- ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarAfter) {
- auto method1_raw_code = GenNopsAndBl(0u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method1 = 0u * 4u; // After NOPs.
- ArrayRef<const uint8_t> method1_code(method1_raw_code);
- ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
- uint32_t expected_last_method_idx = 65; // Based on 2MiB chunks in Create2MethodsWithGap().
- const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx),
- };
-
- constexpr uint32_t just_over_max_positive_disp = 128 * MB;
- uint32_t last_method_idx = Create2MethodsWithGap(
- method1_code,
- ArrayRef<const LinkerPatch>(method1_patches),
- kNopCode,
- ArrayRef<const LinkerPatch>(),
- bl_offset_in_method1 + just_over_max_positive_disp);
- ASSERT_EQ(expected_last_method_idx, last_method_idx);
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t last_method_offset = GetMethodOffset(last_method_idx);
- ASSERT_TRUE(IsAligned<kArm64Alignment>(last_method_offset));
- uint32_t last_method_header_offset = last_method_offset - sizeof(OatQuickMethodHeader);
- uint32_t thunk_size = MethodCallThunkSize();
- uint32_t thunk_offset = RoundDown(last_method_header_offset - thunk_size, kArm64Alignment);
- DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
- last_method_header_offset);
- uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1);
- CHECK_ALIGNED(diff, 4u);
- ASSERT_LT(diff, 128 * MB);
- auto expected_code = GenNopsAndBl(0u, kBlPlus0 | (diff >> 2));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
- CheckThunk(thunk_offset);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarBefore) {
- auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0);
- constexpr uint32_t bl_offset_in_last_method = 1u * 4u; // After NOPs.
- ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
- ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
- const LinkerPatch last_method_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
- };
-
- constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
- uint32_t last_method_idx = Create2MethodsWithGap(
- kNopCode, ArrayRef<const LinkerPatch>(), last_method_code,
- ArrayRef<const LinkerPatch>(last_method_patches),
- just_over_max_negative_disp - bl_offset_in_last_method);
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t last_method_offset = GetMethodOffset(last_method_idx);
- ASSERT_EQ(method1_offset,
- last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
-
- // Check linked code.
- uint32_t thunk_offset =
- CompiledCode::AlignCode(last_method_offset + last_method_code.size(), InstructionSet::kArm64);
- uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
- CHECK_ALIGNED(diff, 4u);
- ASSERT_LT(diff, 128 * MB);
- auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
- ArrayRef<const uint8_t>(expected_code)));
- EXPECT_TRUE(CheckThunk(thunk_offset));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry1) {
- TestNopsAdrpLdr(0u, 0x12345678u, 0x1234u);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry2) {
- TestNopsAdrpLdr(0u, -0x12345678u, 0x4444u);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry3) {
- TestNopsAdrpLdr(0u, 0x12345000u, 0x3ffcu);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry4) {
- TestNopsAdrpLdr(0u, 0x12345000u, 0x4000u);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringReference1) {
- TestNopsAdrpAdd(0u, 0x12345678u);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringReference2) {
- TestNopsAdrpAdd(0u, -0x12345678u);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringReference3) {
- TestNopsAdrpAdd(0u, 0x12345000u);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringReference4) {
- TestNopsAdrpAdd(0u, 0x12345ffcu);
-}
-
-#define TEST_FOR_OFFSETS(test, disp1, disp2) \
- test(0xff4u, disp1) test(0xff8u, disp1) test(0xffcu, disp1) test(0x1000u, disp1) \
- test(0xff4u, disp2) test(0xff8u, disp2) test(0xffcu, disp2) test(0x1000u, disp2)
-
-#define DEFAULT_LDUR_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## Ldur ## disp) { \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \
- TestAdrpLdurLdr(adrp_offset, has_thunk, 0x12345678u, disp); \
- }
-
-TEST_FOR_OFFSETS(DEFAULT_LDUR_LDR_TEST, 0x1234, 0x1238)
-
-#define DENVER64_LDUR_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDenver64, StringBssEntry ## adrp_offset ## Ldur ## disp) { \
- TestAdrpLdurLdr(adrp_offset, false, 0x12345678u, disp); \
- }
-
-TEST_FOR_OFFSETS(DENVER64_LDUR_LDR_TEST, 0x1234, 0x1238)
-
-// LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
-#define LDRW_PCREL_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## WPcRel ## disp) { \
- TestAdrpLdrPcRelLdr(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u, 0x1234u); \
- }
-
-TEST_FOR_OFFSETS(LDRW_PCREL_LDR_TEST, 0x1234, 0x1238)
-
-// LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
-#define LDRX_PCREL_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## XPcRel ## disp) { \
- bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(disp)); \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; \
- TestAdrpLdrPcRelLdr(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u, 0x1234u); \
- }
-
-TEST_FOR_OFFSETS(LDRX_PCREL_LDR_TEST, 0x1234, 0x1238)
-
-// LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
-#define LDRW_SPREL_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## WSpRel ## disp) { \
- TestAdrpLdrSpRelLdr(kLdrWSpRelInsn, (disp) >> 2, adrp_offset, false, 0x12345678u, 0x1234u); \
- }
-
-TEST_FOR_OFFSETS(LDRW_SPREL_LDR_TEST, 0, 4)
-
-#define LDRX_SPREL_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## XSpRel ## disp) { \
- TestAdrpLdrSpRelLdr(kLdrXSpRelInsn, (disp) >> 3, adrp_offset, false, 0x12345678u, 0x1234u); \
- }
-
-TEST_FOR_OFFSETS(LDRX_SPREL_LDR_TEST, 0, 8)
-
-#define DEFAULT_LDUR_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## Ldur ## disp) { \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \
- TestAdrpLdurAdd(adrp_offset, has_thunk, disp); \
- }
-
-TEST_FOR_OFFSETS(DEFAULT_LDUR_ADD_TEST, 0x12345678, 0xffffc840)
-
-#define DENVER64_LDUR_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDenver64, StringReference ## adrp_offset ## Ldur ## disp) { \
- TestAdrpLdurAdd(adrp_offset, false, disp); \
- }
-
-TEST_FOR_OFFSETS(DENVER64_LDUR_ADD_TEST, 0x12345678, 0xffffc840)
-
-#define DEFAULT_SUBX3X2_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubX3X2 ## disp) { \
- /* SUB unrelated to "ADRP x0, addr". */ \
- uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u; /* SUB x3, x2, #100 */ \
- TestAdrpInsn2Add(sub, adrp_offset, false, disp); \
- }
-
-TEST_FOR_OFFSETS(DEFAULT_SUBX3X2_ADD_TEST, 0x12345678, 0xffffc840)
-
-#define DEFAULT_SUBSX3X0_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubsX3X0 ## disp) { \
- /* SUBS that uses the result of "ADRP x0, addr". */ \
- uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u; /* SUBS x3, x0, #100 */ \
- TestAdrpInsn2Add(subs, adrp_offset, false, disp); \
- }
-
-TEST_FOR_OFFSETS(DEFAULT_SUBSX3X0_ADD_TEST, 0x12345678, 0xffffc840)
-
-#define DEFAULT_ADDX0X0_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddX0X0 ## disp) { \
- /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */ \
- uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u; /* ADD x0, x0, #100 */ \
- TestAdrpInsn2Add(add, adrp_offset, false, disp); \
- }
-
-TEST_FOR_OFFSETS(DEFAULT_ADDX0X0_ADD_TEST, 0x12345678, 0xffffc840)
-
-#define DEFAULT_ADDSX0X2_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddsX0X2 ## disp) { \
- /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */ \
- uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u; /* ADDS x0, x2, #100 */ \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \
- TestAdrpInsn2Add(adds, adrp_offset, has_thunk, disp); \
- }
-
-TEST_FOR_OFFSETS(DEFAULT_ADDSX0X2_ADD_TEST, 0x12345678, 0xffffc840)
-
-// LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
-#define LDRW_PCREL_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WPcRel ## disp) { \
- TestAdrpLdrPcRelAdd(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u); \
- }
-
-TEST_FOR_OFFSETS(LDRW_PCREL_ADD_TEST, 0x1234, 0x1238)
-
-// LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
-#define LDRX_PCREL_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XPcRel ## disp) { \
- bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(disp)); \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; \
- TestAdrpLdrPcRelAdd(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u); \
- }
-
-TEST_FOR_OFFSETS(LDRX_PCREL_ADD_TEST, 0x1234, 0x1238)
-
-// LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
-#define LDRW_SPREL_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WSpRel ## disp) { \
- TestAdrpLdrSpRelAdd(kLdrWSpRelInsn, (disp) >> 2, adrp_offset, false, 0x12345678u); \
- }
-
-TEST_FOR_OFFSETS(LDRW_SPREL_ADD_TEST, 0, 4)
-
-#define LDRX_SPREL_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XSpRel ## disp) { \
- TestAdrpLdrSpRelAdd(kLdrXSpRelInsn, (disp) >> 3, adrp_offset, false, 0x12345678u); \
- }
-
-TEST_FOR_OFFSETS(LDRX_SPREL_ADD_TEST, 0, 8)
-
-void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) {
- uint32_t valid_regs[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 18, 19, // IP0 and IP1 are reserved.
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
- // LR and SP/ZR are reserved.
- };
- DCHECK_ALIGNED(offset, 4u);
- DCHECK_LT(offset, 16 * KB);
- constexpr size_t kMethodCodeSize = 8u;
- constexpr size_t kLiteralOffset = 0u;
- uint32_t method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- for (uint32_t holder_reg : valid_regs) {
- uint32_t ldr = kLdrWInsn | (offset << (10 - 2)) | (base_reg << 5) | ref_reg;
- const std::vector<uint8_t> raw_code = RawCode({kCbnzIP1Plus0Insn, ldr});
- ASSERT_EQ(kMethodCodeSize, raw_code.size());
- ArrayRef<const uint8_t> code(raw_code);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(base_reg, holder_reg);
- const LinkerPatch patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data),
- };
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
- }
- }
- Link();
-
- // All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
- method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- for (uint32_t holder_reg : valid_regs) {
- ++method_idx;
- uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset);
- uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
- uint32_t ldr = kLdrWInsn | (offset << (10 - 2)) | (base_reg << 5) | ref_reg;
- const std::vector<uint8_t> expected_code = RawCode({cbnz, ldr});
- ASSERT_EQ(kMethodCodeSize, expected_code.size());
- ASSERT_TRUE(
- CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
-
- std::vector<uint8_t> expected_thunk = CompileBakerOffsetThunk(base_reg, holder_reg);
- ASSERT_GT(output_.size(), thunk_offset);
- ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
- ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
- expected_thunk.size());
- if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
- ASSERT_TRUE(false);
- }
-
- size_t gray_check_offset = thunk_offset;
- if (holder_reg == base_reg) {
- // Verify that the null-check CBZ uses the correct register, i.e. holder_reg.
- ASSERT_GE(output_.size() - gray_check_offset, 4u);
- ASSERT_EQ(0x34000000u | holder_reg, GetOutputInsn(thunk_offset) & 0xff00001fu);
- gray_check_offset +=4u;
- }
- // Verify that the lock word for gray bit check is loaded from the holder address.
- static constexpr size_t kGrayCheckInsns = 5;
- ASSERT_GE(output_.size() - gray_check_offset, 4u * kGrayCheckInsns);
- const uint32_t load_lock_word =
- kLdrWInsn |
- (mirror::Object::MonitorOffset().Uint32Value() << (10 - 2)) |
- (holder_reg << 5) |
- /* ip0 */ 16;
- EXPECT_EQ(load_lock_word, GetOutputInsn(gray_check_offset));
- // Verify the gray bit check.
- const uint32_t check_gray_bit_without_offset =
- 0x37000000u | (LockWord::kReadBarrierStateShift << 19) | /* ip0 */ 16;
- EXPECT_EQ(check_gray_bit_without_offset, GetOutputInsn(gray_check_offset + 4u) & 0xfff8001fu);
- // Verify the fake dependency.
- const uint32_t fake_dependency =
- 0x8b408000u | // ADD Xd, Xn, Xm, LSR 32
- (/* ip0 */ 16 << 16) | // Xm = ip0
- (base_reg << 5) | // Xn = base_reg
- base_reg; // Xd = base_reg
- EXPECT_EQ(fake_dependency, GetOutputInsn(gray_check_offset + 12u));
- // Do not check the rest of the implementation.
-
- // The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
- }
- }
-}
-
-#define TEST_BAKER_FIELD(offset, ref_reg) \
- TEST_F(Arm64RelativePatcherTestDefault, \
- BakerOffset##offset##_##ref_reg) { \
- TestBakerField(offset, ref_reg); \
- }
-
-TEST_BAKER_FIELD(/* offset */ 0, /* ref_reg */ 0)
-TEST_BAKER_FIELD(/* offset */ 8, /* ref_reg */ 15)
-TEST_BAKER_FIELD(/* offset */ 0x3ffc, /* ref_reg */ 29)
-
-TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) {
- // One thunk in the middle with maximum distance branches to it from both sides.
- // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
- constexpr uint32_t kLiteralOffset1 = 4;
- const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
- ArrayRef<const uint8_t> code1(raw_code1);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
- const LinkerPatch patches1[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
- };
- AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
-
- // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
- // allows the branch to reach that thunk.
- size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
- std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
- ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
- AddCompiledMethod(MethodRef(2u), filler1_code);
-
- // Enforce thunk reservation with a tiny method.
- AddCompiledMethod(MethodRef(3u), kNopCode);
-
- // Allow reaching the thunk from the very beginning of a method 1MiB away. Backward branch
- // reaches the full 1MiB. Things to subtract:
- // - thunk size and method 3 pre-header, rounded up (padding in between if needed)
- // - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
- // - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
- size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
- size_t filler2_size =
- 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - sizeof(OatQuickMethodHeader);
- std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
- ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
- AddCompiledMethod(MethodRef(4u), filler2_code);
-
- constexpr uint32_t kLiteralOffset2 = 0;
- const std::vector<uint8_t> raw_code2 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn});
- ArrayRef<const uint8_t> code2(raw_code2);
- const LinkerPatch patches2[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
- };
- AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
-
- Link();
-
- uint32_t first_method_offset = GetMethodOffset(1u);
- uint32_t last_method_offset = GetMethodOffset(5u);
- EXPECT_EQ(2 * MB, last_method_offset - first_method_offset);
-
- const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0;
- const uint32_t cbnz_max_backward = kCbnzIP1Plus0Insn | 0x00800000;
- const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn});
- const std::vector<uint8_t> expected_code2 = RawCode({cbnz_max_backward, kLdrWInsn});
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) {
- // Based on the first part of BakerOffsetThunkInTheMiddle but the CBNZ is one instruction
- // earlier, so the thunk is emitted before the filler.
- // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
- constexpr uint32_t kLiteralOffset1 = 0;
- const std::vector<uint8_t> raw_code1 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn, kNopInsn});
- ArrayRef<const uint8_t> code1(raw_code1);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
- const LinkerPatch patches1[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
- };
- AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
-
- // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
- // allows the branch to reach that thunk.
- size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
- std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
- ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
- AddCompiledMethod(MethodRef(2u), filler1_code);
-
- Link();
-
- const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64Alignment) - kLiteralOffset1;
- const uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
- const std::vector<uint8_t> expected_code1 = RawCode({cbnz, kLdrWInsn, kNopInsn});
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFromLast) {
- // Based on the BakerOffsetThunkInTheMiddle but the CBNZ in the last method is preceded
- // by NOP and cannot reach the thunk in the middle, so we emit an extra thunk at the end.
- // Use offset = 0, base_reg = 0, ref_reg = 0, the LDR is simply `kLdrWInsn`.
- constexpr uint32_t kLiteralOffset1 = 4;
- const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
- ArrayRef<const uint8_t> code1(raw_code1);
- uint32_t encoded_data = EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
- const LinkerPatch patches1[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
- };
- AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
-
- // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
- // allows the branch to reach that thunk.
- size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
- std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
- ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
- AddCompiledMethod(MethodRef(2u), filler1_code);
-
- // Enforce thunk reservation with a tiny method.
- AddCompiledMethod(MethodRef(3u), kNopCode);
-
- // If not for the extra NOP, this would allow reaching the thunk from the very beginning
- // of a method 1MiB away. Backward branch reaches the full 1MiB. Things to subtract:
- // - thunk size and method 3 pre-header, rounded up (padding in between if needed)
- // - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
- // - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
- size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
- size_t filler2_size =
- 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - sizeof(OatQuickMethodHeader);
- std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
- ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
- AddCompiledMethod(MethodRef(4u), filler2_code);
-
- // Extra NOP compared to BakerOffsetThunkInTheMiddle.
- constexpr uint32_t kLiteralOffset2 = 4;
- const std::vector<uint8_t> raw_code2 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
- ArrayRef<const uint8_t> code2(raw_code2);
- const LinkerPatch patches2[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
- };
- AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
-
- Link();
-
- const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0;
- const uint32_t cbnz_last_offset = RoundUp(raw_code2.size(), kArm64Alignment) - kLiteralOffset2;
- const uint32_t cbnz_last = kCbnzIP1Plus0Insn | (cbnz_last_offset << (5 - 2));
- const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn});
- const std::vector<uint8_t> expected_code2 = RawCode({kNopInsn, cbnz_last, kLdrWInsn});
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
- ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, BakerArray) {
- uint32_t valid_regs[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 18, 19, // IP0 and IP1 are reserved.
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
- // LR and SP/ZR are reserved.
- };
- auto ldr = [](uint32_t base_reg) {
- uint32_t index_reg = (base_reg == 0u) ? 1u : 0u;
- uint32_t ref_reg = (base_reg == 2) ? 3u : 2u;
- return kLdrWLsl2Insn | (index_reg << 16) | (base_reg << 5) | ref_reg;
- };
- constexpr size_t kMethodCodeSize = 8u;
- constexpr size_t kLiteralOffset = 0u;
- uint32_t method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- ++method_idx;
- const std::vector<uint8_t> raw_code = RawCode({kCbnzIP1Plus0Insn, ldr(base_reg)});
- ASSERT_EQ(kMethodCodeSize, raw_code.size());
- ArrayRef<const uint8_t> code(raw_code);
- const LinkerPatch patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(
- kLiteralOffset, EncodeBakerReadBarrierArrayData(base_reg)),
- };
- AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
- }
- Link();
-
- // All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
- method_idx = 0u;
- for (uint32_t base_reg : valid_regs) {
- ++method_idx;
- uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset);
- uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
- const std::vector<uint8_t> expected_code = RawCode({cbnz, ldr(base_reg)});
- ASSERT_EQ(kMethodCodeSize, expected_code.size());
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
-
- std::vector<uint8_t> expected_thunk = CompileBakerArrayThunk(base_reg);
- ASSERT_GT(output_.size(), thunk_offset);
- ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
- ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
- expected_thunk.size());
- if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
- ASSERT_TRUE(false);
- }
-
- // Verify that the lock word for gray bit check is loaded from the correct address
- // before the base_reg which points to the array data.
- static constexpr size_t kGrayCheckInsns = 5;
- ASSERT_GE(output_.size() - thunk_offset, 4u * kGrayCheckInsns);
- int32_t data_offset =
- mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
- int32_t offset = mirror::Object::MonitorOffset().Int32Value() - data_offset;
- ASSERT_LT(offset, 0);
- const uint32_t load_lock_word =
- kLdurWInsn |
- ((offset & 0x1ffu) << 12) |
- (base_reg << 5) |
- /* ip0 */ 16;
- EXPECT_EQ(load_lock_word, GetOutputInsn(thunk_offset));
- // Verify the gray bit check.
- const uint32_t check_gray_bit_without_offset =
- 0x37000000u | (LockWord::kReadBarrierStateShift << 19) | /* ip0 */ 16;
- EXPECT_EQ(check_gray_bit_without_offset, GetOutputInsn(thunk_offset + 4u) & 0xfff8001fu);
- // Verify the fake dependency.
- const uint32_t fake_dependency =
- 0x8b408000u | // ADD Xd, Xn, Xm, LSR 32
- (/* ip0 */ 16 << 16) | // Xm = ip0
- (base_reg << 5) | // Xn = base_reg
- base_reg; // Xd = base_reg
- EXPECT_EQ(fake_dependency, GetOutputInsn(thunk_offset + 12u));
- // Do not check the rest of the implementation.
-
- // The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
- }
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, BakerGcRoot) {
- uint32_t valid_regs[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 18, 19, // IP0 and IP1 are reserved.
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
- // LR and SP/ZR are reserved.
- };
- constexpr size_t kMethodCodeSize = 8u;
- constexpr size_t kLiteralOffset = 4u;
- uint32_t method_idx = 0u;
- for (uint32_t root_reg : valid_regs) {
- ++method_idx;
- uint32_t ldr = kLdrWInsn | (/* offset */ 8 << (10 - 2)) | (/* base_reg */ 0 << 5) | root_reg;
- const std::vector<uint8_t> raw_code = RawCode({ldr, kCbnzIP1Plus0Insn});
- ASSERT_EQ(kMethodCodeSize, raw_code.size());
- ArrayRef<const uint8_t> code(raw_code);
- const LinkerPatch patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(
- kLiteralOffset, EncodeBakerReadBarrierGcRootData(root_reg)),
- };
- AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
- }
- Link();
-
- // All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
- method_idx = 0u;
- for (uint32_t root_reg : valid_regs) {
- ++method_idx;
- uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset);
- uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
- uint32_t ldr = kLdrWInsn | (/* offset */ 8 << (10 - 2)) | (/* base_reg */ 0 << 5) | root_reg;
- const std::vector<uint8_t> expected_code = RawCode({ldr, cbnz});
- ASSERT_EQ(kMethodCodeSize, expected_code.size());
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
-
- std::vector<uint8_t> expected_thunk = CompileBakerGcRootThunk(root_reg);
- ASSERT_GT(output_.size(), thunk_offset);
- ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
- ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
- expected_thunk.size());
- if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
- DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
- ASSERT_TRUE(false);
- }
-
- // Verify that the fast-path null-check CBZ uses the correct register, i.e. root_reg.
- ASSERT_GE(output_.size() - thunk_offset, 4u);
- ASSERT_EQ(0x34000000u | root_reg, GetOutputInsn(thunk_offset) & 0xff00001fu);
- // Do not check the rest of the implementation.
-
- // The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
- }
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) {
- // During development, there was a `DCHECK_LE(MaxNextOffset(), next_thunk.MaxNextOffset());`
- // in `ArmBaseRelativePatcher::ThunkData::MakeSpaceBefore()` which does not necessarily
- // hold when we're reserving thunks of different sizes. This test exposes the situation
- // by using Baker thunks and a method call thunk.
-
- // Add a method call patch that can reach to method 1 offset + 128MiB.
- uint32_t method_idx = 0u;
- constexpr size_t kMethodCallLiteralOffset = 4u;
- constexpr uint32_t kMissingMethodIdx = 2u;
- const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kBlPlus0});
- const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(kMethodCallLiteralOffset, nullptr, 2u),
- };
- ArrayRef<const uint8_t> code1(raw_code1);
- ++method_idx;
- AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(method1_patches));
-
- // Skip kMissingMethodIdx.
- ++method_idx;
- ASSERT_EQ(kMissingMethodIdx, method_idx);
- // Add a method with the right size that the method code for the next one starts 1MiB
- // after code for method 1.
- size_t filler_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - sizeof(OatQuickMethodHeader);
- std::vector<uint8_t> filler_code = GenNops(filler_size / 4u);
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
- // Add 126 methods with 1MiB code+header, making the code for the next method start 1MiB
- // before the currently scheduled MaxNextOffset() for the method call thunk.
- for (uint32_t i = 0; i != 126; ++i) {
- filler_size = 1 * MB - sizeof(OatQuickMethodHeader);
- filler_code = GenNops(filler_size / 4u);
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
- }
-
- // Add 2 Baker GC root patches to the last method, one that would allow the thunk at
- // 1MiB + kArm64Alignment, i.e. kArm64Alignment after the method call thunk, and the
- // second that needs it kArm64Alignment after that. Given the size of the GC root thunk
- // is more than the space required by the method call thunk plus kArm64Alignment,
- // this pushes the first GC root thunk's pending MaxNextOffset() before the method call
- // thunk's pending MaxNextOffset() which needs to be adjusted.
- ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64Alignment) + kArm64Alignment,
- CompileBakerGcRootThunk(/* root_reg */ 0).size());
- static_assert(kArm64Alignment == 16, "Code below assumes kArm64Alignment == 16");
- constexpr size_t kBakerLiteralOffset1 = 4u + kArm64Alignment;
- constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64Alignment;
- // Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | root_reg`.
- const uint32_t ldr1 = kLdrWInsn | /* root_reg */ 1;
- const uint32_t ldr2 = kLdrWInsn | /* root_reg */ 2;
- const std::vector<uint8_t> last_method_raw_code = RawCode({
- kNopInsn, kNopInsn, kNopInsn, kNopInsn, // Padding before first GC root read barrier.
- ldr1, kCbnzIP1Plus0Insn, // First GC root LDR with read barrier.
- kNopInsn, kNopInsn, // Padding before second GC root read barrier.
- ldr2, kCbnzIP1Plus0Insn, // Second GC root LDR with read barrier.
- });
- uint32_t encoded_data1 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 1);
- uint32_t encoded_data2 = EncodeBakerReadBarrierGcRootData(/* root_reg */ 2);
- const LinkerPatch last_method_patches[] = {
- LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1),
- LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2),
- };
- ++method_idx;
- AddCompiledMethod(MethodRef(method_idx),
- ArrayRef<const uint8_t>(last_method_raw_code),
- ArrayRef<const LinkerPatch>(last_method_patches));
-
- // The main purpose of the test is to check that Link() does not cause a crash.
- Link();
-
- ASSERT_EQ(127 * MB, GetMethodOffset(method_idx) - GetMethodOffset(1u));
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc
deleted file mode 100644
index 69e0846cb7..0000000000
--- a/compiler/linker/mips/relative_patcher_mips.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/mips/relative_patcher_mips.h"
-
-#include "compiled_method.h"
-#include "debug/method_debug_info.h"
-#include "linker/linker_patch.h"
-
-namespace art {
-namespace linker {
-
-uint32_t MipsRelativePatcher::ReserveSpace(
- uint32_t offset,
- const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
- MethodReference method_ref ATTRIBUTE_UNUSED) {
- return offset; // No space reserved; no limit on relative call distance.
-}
-
-uint32_t MipsRelativePatcher::ReserveSpaceEnd(uint32_t offset) {
- return offset; // No space reserved; no limit on relative call distance.
-}
-
-uint32_t MipsRelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) {
- return offset; // No thunks added; no limit on relative call distance.
-}
-
-void MipsRelativePatcher::PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- uint32_t literal_offset ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED,
- uint32_t target_offset ATTRIBUTE_UNUSED) {
- UNIMPLEMENTED(FATAL) << "PatchCall unimplemented on MIPS";
-}
-
-void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
- uint32_t anchor_literal_offset = patch.PcInsnOffset();
- uint32_t literal_offset = patch.LiteralOffset();
- bool high_patch = ((*code)[literal_offset + 0] == 0x34) && ((*code)[literal_offset + 1] == 0x12);
-
- // Perform basic sanity checks.
- if (high_patch) {
- if (is_r6) {
- // auipc reg, offset_high
- DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
- DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
- } else {
- // lui reg, offset_high
- DCHECK_EQ(((*code)[literal_offset + 2] & 0xE0), 0x00);
- DCHECK_EQ((*code)[literal_offset + 3], 0x3C);
- }
- } else {
- // instr reg(s), offset_low
- CHECK_EQ((*code)[literal_offset + 0], 0x78);
- CHECK_EQ((*code)[literal_offset + 1], 0x56);
- }
-
- // Apply patch.
- uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
- uint32_t diff = target_offset - anchor_offset;
- diff += (diff & 0x8000) << 1; // Account for sign extension in "instr reg(s), offset_low".
-
- if (high_patch) {
- // lui reg, offset_high / auipc reg, offset_high
- (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
- (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
- } else {
- // instr reg(s), offset_low
- (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 0);
- (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 8);
- }
-}
-
-void MipsRelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "UNIMPLEMENTED";
-}
-
-std::vector<debug::MethodDebugInfo> MipsRelativePatcher::GenerateThunkDebugInfo(
- uint32_t executable_offset ATTRIBUTE_UNUSED) {
- return std::vector<debug::MethodDebugInfo>(); // No thunks added.
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/mips/relative_patcher_mips.h b/compiler/linker/mips/relative_patcher_mips.h
deleted file mode 100644
index 5714a7d1b0..0000000000
--- a/compiler/linker/mips/relative_patcher_mips.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_
-#define ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_
-
-#include "arch/mips/instruction_set_features_mips.h"
-#include "linker/relative_patcher.h"
-
-namespace art {
-namespace linker {
-
-class MipsRelativePatcher FINAL : public RelativePatcher {
- public:
- explicit MipsRelativePatcher(const MipsInstructionSetFeatures* features)
- : is_r6(features->IsR6()) {}
-
- uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) OVERRIDE;
- uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
- uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- void PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) OVERRIDE;
- std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE;
-
- private:
- bool is_r6;
-
- DISALLOW_COPY_AND_ASSIGN(MipsRelativePatcher);
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_
diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
deleted file mode 100644
index 629fdd535d..0000000000
--- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/mips/relative_patcher_mips.h"
-#include "linker/relative_patcher_test.h"
-
-namespace art {
-namespace linker {
-
-class Mips32r6RelativePatcherTest : public RelativePatcherTest {
- public:
- Mips32r6RelativePatcherTest() : RelativePatcherTest(InstructionSet::kMips, "mips32r6") {}
-
- protected:
- static const uint8_t kUnpatchedPcRelativeRawCode[];
- static const uint32_t kLiteralOffsetHigh;
- static const uint32_t kLiteralOffsetLow1;
- static const uint32_t kLiteralOffsetLow2;
- static const uint32_t kAnchorOffset;
- static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
-
- uint32_t GetMethodOffset(uint32_t method_idx) {
- auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
- CHECK(result.first);
- return result.second;
- }
-
- void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
- void TestStringBssEntry(uint32_t bss_begin, uint32_t string_entry_offset);
- void TestStringReference(uint32_t string_offset);
-};
-
-const uint8_t Mips32r6RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
- 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234
- 0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678
- 0x78, 0x56, 0x52, 0x8E, // lw s2, (low(diff))(s2) ; placeholder = 0x5678
-};
-const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetHigh = 0; // At auipc.
-const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetLow1 = 4; // At addiu.
-const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetLow2 = 8; // At lw.
-const uint32_t Mips32r6RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points).
-const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::kUnpatchedPcRelativeCode(
- kUnpatchedPcRelativeRawCode);
-
-void Mips32r6RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
- uint32_t target_offset) {
- AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
- ASSERT_TRUE(result.first);
-
- uint32_t diff = target_offset - (result.second + kAnchorOffset);
- diff += (diff & 0x8000) << 1; // Account for sign extension in addiu/lw.
-
- const uint8_t expected_code[] = {
- static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x8E,
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-void Mips32r6RelativePatcherTest::TestStringBssEntry(uint32_t bss_begin,
- uint32_t string_entry_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
- bss_begin_ = bss_begin;
- LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
- };
- CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
-}
-
-void Mips32r6RelativePatcherTest::TestStringReference(uint32_t string_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_offset);
- LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::RelativeStringPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::RelativeStringPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
- };
- CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
-}
-
-TEST_F(Mips32r6RelativePatcherTest, StringBssEntry) {
- TestStringBssEntry(/* bss_begin */ 0x12345678, /* string_entry_offset */ 0x1234);
-}
-
-TEST_F(Mips32r6RelativePatcherTest, StringReference) {
- TestStringReference(/* string_offset*/ 0x87651234);
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc
deleted file mode 100644
index d876c76daa..0000000000
--- a/compiler/linker/mips/relative_patcher_mips_test.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/mips/relative_patcher_mips.h"
-
-#include "linker/relative_patcher_test.h"
-
-namespace art {
-namespace linker {
-
-class MipsRelativePatcherTest : public RelativePatcherTest {
- public:
- MipsRelativePatcherTest() : RelativePatcherTest(InstructionSet::kMips, "mips32r2") {}
-
- protected:
- static const uint8_t kUnpatchedPcRelativeRawCode[];
- static const uint32_t kLiteralOffsetHigh;
- static const uint32_t kLiteralOffsetLow1;
- static const uint32_t kLiteralOffsetLow2;
- static const uint32_t kAnchorOffset;
- static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
-
- uint32_t GetMethodOffset(uint32_t method_idx) {
- auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
- CHECK(result.first);
- return result.second;
- }
-
- void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
- void TestStringBssEntry(uint32_t bss_begin, uint32_t string_entry_offset);
- void TestStringReference(uint32_t string_offset);
-};
-
-const uint8_t MipsRelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
- 0x00, 0x00, 0x10, 0x04, // nal
- 0x34, 0x12, 0x12, 0x3C, // lui s2, high(diff); placeholder = 0x1234
- 0x21, 0x90, 0x5F, 0x02, // addu s2, s2, ra
- 0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678
- 0x78, 0x56, 0x52, 0x8E, // lw s2, (low(diff))(s2) ; placeholder = 0x5678
-};
-const uint32_t MipsRelativePatcherTest::kLiteralOffsetHigh = 4; // At lui.
-const uint32_t MipsRelativePatcherTest::kLiteralOffsetLow1 = 12; // At addiu.
-const uint32_t MipsRelativePatcherTest::kLiteralOffsetLow2 = 16; // At lw.
-const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8; // At addu (where PC+0 points).
-const ArrayRef<const uint8_t> MipsRelativePatcherTest::kUnpatchedPcRelativeCode(
- kUnpatchedPcRelativeRawCode);
-
-void MipsRelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
- uint32_t target_offset) {
- AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
- ASSERT_TRUE(result.first);
-
- uint32_t diff = target_offset - (result.second + kAnchorOffset);
- diff += (diff & 0x8000) << 1; // Account for sign extension in addiu/lw.
-
- const uint8_t expected_code[] = {
- 0x00, 0x00, 0x10, 0x04,
- static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x12, 0x3C,
- 0x21, 0x90, 0x5F, 0x02,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x8E,
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-void MipsRelativePatcherTest::TestStringBssEntry(uint32_t bss_begin,
- uint32_t string_entry_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
- bss_begin_ = bss_begin;
- LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
- };
- CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
-}
-
-void MipsRelativePatcherTest::TestStringReference(uint32_t string_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_offset);
- LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::RelativeStringPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::RelativeStringPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
- };
- CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
-}
-
-TEST_F(MipsRelativePatcherTest, StringBssEntry) {
- TestStringBssEntry(/* bss_begin */ 0x12345678, /* string_entry_offset */ 0x1234);
-}
-
-TEST_F(MipsRelativePatcherTest, StringReference) {
- TestStringReference(/* string_offset*/ 0x87651234);
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc
deleted file mode 100644
index aae5746278..0000000000
--- a/compiler/linker/mips64/relative_patcher_mips64.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/mips64/relative_patcher_mips64.h"
-
-#include "compiled_method.h"
-#include "debug/method_debug_info.h"
-#include "linker/linker_patch.h"
-
-namespace art {
-namespace linker {
-
-uint32_t Mips64RelativePatcher::ReserveSpace(
- uint32_t offset,
- const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
- MethodReference method_ref ATTRIBUTE_UNUSED) {
- return offset; // No space reserved; no limit on relative call distance.
-}
-
-uint32_t Mips64RelativePatcher::ReserveSpaceEnd(uint32_t offset) {
- return offset; // No space reserved; no limit on relative call distance.
-}
-
-uint32_t Mips64RelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) {
- return offset; // No thunks added; no limit on relative call distance.
-}
-
-void Mips64RelativePatcher::PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- uint32_t literal_offset ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED,
- uint32_t target_offset ATTRIBUTE_UNUSED) {
- UNIMPLEMENTED(FATAL) << "PatchCall unimplemented on MIPS64";
-}
-
-void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
- uint32_t anchor_literal_offset = patch.PcInsnOffset();
- uint32_t literal_offset = patch.LiteralOffset();
- bool high_patch = ((*code)[literal_offset + 0] == 0x34) && ((*code)[literal_offset + 1] == 0x12);
-
- // Perform basic sanity checks.
- if (high_patch) {
- // auipc reg, offset_high
- DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
- DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
- } else {
- // instr reg(s), offset_low
- CHECK_EQ((*code)[literal_offset + 0], 0x78);
- CHECK_EQ((*code)[literal_offset + 1], 0x56);
- }
-
- // Apply patch.
- uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
- uint32_t diff = target_offset - anchor_offset;
- // Note that a combination of auipc with an instruction that adds a sign-extended
- // 16-bit immediate operand (e.g. ld) provides a PC-relative range of
- // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
- // by 32KB.
- diff += (diff & 0x8000) << 1; // Account for sign extension in "instr reg(s), offset_low".
-
- if (high_patch) {
- // auipc reg, offset_high
- (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
- (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
- } else {
- // instr reg(s), offset_low
- (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 0);
- (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 8);
- }
-}
-
-void Mips64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "UNIMPLEMENTED";
-}
-
-std::vector<debug::MethodDebugInfo> Mips64RelativePatcher::GenerateThunkDebugInfo(
- uint32_t executable_offset ATTRIBUTE_UNUSED) {
- return std::vector<debug::MethodDebugInfo>(); // No thunks added.
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/compiler/linker/mips64/relative_patcher_mips64.h
deleted file mode 100644
index 183bbedb39..0000000000
--- a/compiler/linker/mips64/relative_patcher_mips64.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
-#define ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
-
-#include "linker/relative_patcher.h"
-
-namespace art {
-namespace linker {
-
-class Mips64RelativePatcher FINAL : public RelativePatcher {
- public:
- Mips64RelativePatcher() {}
-
- uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) OVERRIDE;
- uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
- uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- void PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) OVERRIDE;
- std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Mips64RelativePatcher);
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc
deleted file mode 100644
index a02f5005e8..0000000000
--- a/compiler/linker/mips64/relative_patcher_mips64_test.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/mips64/relative_patcher_mips64.h"
-
-#include "linker/relative_patcher_test.h"
-
-namespace art {
-namespace linker {
-
-class Mips64RelativePatcherTest : public RelativePatcherTest {
- public:
- Mips64RelativePatcherTest() : RelativePatcherTest(InstructionSet::kMips64, "default") {}
-
- protected:
- static const uint8_t kUnpatchedPcRelativeRawCode[];
- static const uint8_t kUnpatchedPcRelativeCallRawCode[];
- static const uint32_t kLiteralOffsetHigh;
- static const uint32_t kLiteralOffsetLow1;
- static const uint32_t kLiteralOffsetLow2;
- static const uint32_t kAnchorOffset;
- static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
-
- uint32_t GetMethodOffset(uint32_t method_idx) {
- auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
- CHECK(result.first);
- return result.second;
- }
-
- void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
- void TestStringBssEntry(uint32_t bss_begin, uint32_t string_entry_offset);
- void TestStringReference(uint32_t string_offset);
-};
-
-const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
- 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234
- 0x78, 0x56, 0x52, 0x66, // daddiu s2, s2, low(diff); placeholder = 0x5678
- 0x78, 0x56, 0x52, 0x9E, // lwu s2, (low(diff))(s2) ; placeholder = 0x5678
-};
-const uint32_t Mips64RelativePatcherTest::kLiteralOffsetHigh = 0; // At auipc.
-const uint32_t Mips64RelativePatcherTest::kLiteralOffsetLow1 = 4; // At daddiu.
-const uint32_t Mips64RelativePatcherTest::kLiteralOffsetLow2 = 8; // At lwu.
-const uint32_t Mips64RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points).
-const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCode(
- kUnpatchedPcRelativeRawCode);
-
-void Mips64RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
- uint32_t target_offset) {
- AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
- ASSERT_TRUE(result.first);
-
- uint32_t diff = target_offset - (result.second + kAnchorOffset);
- diff += (diff & 0x8000) << 1; // Account for sign extension in daddiu/lwu.
-
- const uint8_t expected_code[] = {
- static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x66,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x9E,
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-void Mips64RelativePatcherTest::TestStringBssEntry(uint32_t bss_begin,
- uint32_t string_entry_offset) {
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
- bss_begin_ = bss_begin;
- LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
- LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
- };
- CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
-}
-
-TEST_F(Mips64RelativePatcherTest, StringBssEntry) {
- TestStringBssEntry(/* bss_begin */ 0x12345678, /* string_entry_offset */ 0x1234);
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
deleted file mode 100644
index b82d15230d..0000000000
--- a/compiler/linker/relative_patcher.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/relative_patcher.h"
-
-#include "debug/method_debug_info.h"
-#ifdef ART_ENABLE_CODEGEN_arm
-#include "linker/arm/relative_patcher_thumb2.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_arm64
-#include "linker/arm64/relative_patcher_arm64.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_mips
-#include "linker/mips/relative_patcher_mips.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_mips64
-#include "linker/mips64/relative_patcher_mips64.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_x86
-#include "linker/x86/relative_patcher_x86.h"
-#endif
-#ifdef ART_ENABLE_CODEGEN_x86_64
-#include "linker/x86_64/relative_patcher_x86_64.h"
-#endif
-#include "output_stream.h"
-
-namespace art {
-namespace linker {
-
-std::unique_ptr<RelativePatcher> RelativePatcher::Create(
- InstructionSet instruction_set,
- const InstructionSetFeatures* features,
- RelativePatcherThunkProvider* thunk_provider,
- RelativePatcherTargetProvider* target_provider) {
- class RelativePatcherNone FINAL : public RelativePatcher {
- public:
- RelativePatcherNone() { }
-
- uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
- MethodReference method_ref ATTRIBUTE_UNUSED) OVERRIDE {
- return offset; // No space reserved; no patches expected.
- }
-
- uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE {
- return offset; // No space reserved; no patches expected.
- }
-
- uint32_t WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) OVERRIDE {
- return offset; // No thunks added; no patches expected.
- }
-
- void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- uint32_t literal_offset ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED,
- uint32_t target_offset ATTRIBUTE_UNUSED) OVERRIDE {
- LOG(FATAL) << "Unexpected relative call patch.";
- }
-
- void PatchPcRelativeReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED,
- uint32_t target_offset ATTRIBUTE_UNUSED) OVERRIDE {
- LOG(FATAL) << "Unexpected relative dex cache array patch.";
- }
-
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "Unexpected baker read barrier branch patch.";
- }
-
- std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
- uint32_t executable_offset ATTRIBUTE_UNUSED) OVERRIDE {
- return std::vector<debug::MethodDebugInfo>(); // No thunks added.
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(RelativePatcherNone);
- };
-
- UNUSED(features);
- UNUSED(thunk_provider);
- UNUSED(target_provider);
- switch (instruction_set) {
-#ifdef ART_ENABLE_CODEGEN_x86
- case InstructionSet::kX86:
- return std::unique_ptr<RelativePatcher>(new X86RelativePatcher());
-#endif
-#ifdef ART_ENABLE_CODEGEN_x86_64
- case InstructionSet::kX86_64:
- return std::unique_ptr<RelativePatcher>(new X86_64RelativePatcher());
-#endif
-#ifdef ART_ENABLE_CODEGEN_arm
- case InstructionSet::kArm:
- // Fall through: we generate Thumb2 code for "arm".
- case InstructionSet::kThumb2:
- return std::unique_ptr<RelativePatcher>(
- new Thumb2RelativePatcher(thunk_provider, target_provider));
-#endif
-#ifdef ART_ENABLE_CODEGEN_arm64
- case InstructionSet::kArm64:
- return std::unique_ptr<RelativePatcher>(
- new Arm64RelativePatcher(thunk_provider,
- target_provider,
- features->AsArm64InstructionSetFeatures()));
-#endif
-#ifdef ART_ENABLE_CODEGEN_mips
- case InstructionSet::kMips:
- return std::unique_ptr<RelativePatcher>(
- new MipsRelativePatcher(features->AsMipsInstructionSetFeatures()));
-#endif
-#ifdef ART_ENABLE_CODEGEN_mips64
- case InstructionSet::kMips64:
- return std::unique_ptr<RelativePatcher>(new Mips64RelativePatcher());
-#endif
- default:
- return std::unique_ptr<RelativePatcher>(new RelativePatcherNone);
- }
-}
-
-bool RelativePatcher::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) {
- static const uint8_t kPadding[] = {
- 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
- };
- DCHECK_LE(aligned_code_delta, sizeof(kPadding));
- if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) {
- return false;
- }
- size_code_alignment_ += aligned_code_delta;
- return true;
-}
-
-bool RelativePatcher::WriteThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk) {
- if (UNLIKELY(!out->WriteFully(thunk.data(), thunk.size()))) {
- return false;
- }
- size_relative_call_thunks_ += thunk.size();
- return true;
-}
-
-bool RelativePatcher::WriteMiscThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk) {
- if (UNLIKELY(!out->WriteFully(thunk.data(), thunk.size()))) {
- return false;
- }
- size_misc_thunks_ += thunk.size();
- return true;
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h
deleted file mode 100644
index 06c7e70d23..0000000000
--- a/compiler/linker/relative_patcher.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_RELATIVE_PATCHER_H_
-#define ART_COMPILER_LINKER_RELATIVE_PATCHER_H_
-
-#include <vector>
-
-#include "arch/instruction_set.h"
-#include "arch/instruction_set_features.h"
-#include "base/array_ref.h"
-#include "base/macros.h"
-#include "dex/method_reference.h"
-
-namespace art {
-
-class CompiledMethod;
-
-namespace debug {
-struct MethodDebugInfo;
-} // namespace debug
-
-namespace linker {
-
-class LinkerPatch;
-class OutputStream;
-
-/**
- * @class RelativePatcherThunkProvider
- * @brief Interface for providing method offsets for relative call targets.
- */
-class RelativePatcherThunkProvider {
- public:
- /**
- * Get the code and debug name of a thunk needed by the given linker patch.
- *
- * @param patch The patch for which we need to retrieve the thunk code.
- * @param code A variable to receive the code of the thunk. This code must not be empty.
- * @param debug_name A variable to receive the debug name of the thunk.
- */
- virtual void GetThunkCode(const LinkerPatch& patch,
- /*out*/ ArrayRef<const uint8_t>* code,
- /*out*/ std::string* debug_name) = 0;
-
- protected:
- virtual ~RelativePatcherThunkProvider() { }
-};
-
-/**
- * @class RelativePatcherTargetProvider
- * @brief Interface for providing method offsets for relative call targets.
- */
-class RelativePatcherTargetProvider {
- public:
- /**
- * Find the offset of the target method of a relative call if known.
- *
- * The process of assigning target method offsets includes calls to the relative patcher's
- * ReserveSpace() which in turn can use FindMethodOffset() to determine if a method already
- * has an offset assigned and, if so, what's that offset. If the offset has not yet been
- * assigned or if it's too far for the particular architecture's relative call,
- * ReserveSpace() may need to allocate space for a special dispatch thunk.
- *
- * @param ref the target method of the relative call.
- * @return true in the first element of the pair if the method was found, false otherwise;
- * if found, the second element specifies the offset.
- */
- virtual std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) = 0;
-
- protected:
- virtual ~RelativePatcherTargetProvider() { }
-};
-
-/**
- * @class RelativePatcher
- * @brief Interface for architecture-specific link-time patching of PC-relative references.
- */
-class RelativePatcher {
- public:
- static std::unique_ptr<RelativePatcher> Create(
- InstructionSet instruction_set,
- const InstructionSetFeatures* features,
- RelativePatcherThunkProvider* thunk_provider,
- RelativePatcherTargetProvider* target_provider);
-
- virtual ~RelativePatcher() { }
-
- uint32_t CodeAlignmentSize() const {
- return size_code_alignment_;
- }
-
- uint32_t RelativeCallThunksSize() const {
- return size_relative_call_thunks_;
- }
-
- uint32_t MiscThunksSize() const {
- return size_misc_thunks_;
- }
-
- // Reserve space for thunks if needed before a method, return adjusted offset.
- virtual uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) = 0;
-
- // Reserve space for thunks if needed after the last method, return adjusted offset.
- // The caller may use this method to preemptively force thunk space reservation and
- // then resume reservation for more methods. This is useful when there is a gap in
- // the .text segment, for example when going to the next oat file for multi-image.
- virtual uint32_t ReserveSpaceEnd(uint32_t offset) = 0;
-
- // Write relative call thunks if needed, return adjusted offset. Returns 0 on write failure.
- virtual uint32_t WriteThunks(OutputStream* out, uint32_t offset) = 0;
-
- // Patch method code. The input displacement is relative to the patched location,
- // the patcher may need to adjust it if the correct base is different.
- virtual void PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) = 0;
-
- // Patch a reference to a dex cache location.
- virtual void PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) = 0;
-
- // Patch a branch to a Baker read barrier thunk.
- virtual void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) = 0;
-
- virtual std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(
- uint32_t executable_offset) = 0;
-
- protected:
- RelativePatcher()
- : size_code_alignment_(0u),
- size_relative_call_thunks_(0u),
- size_misc_thunks_(0u) {
- }
-
- bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
- bool WriteThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk);
- bool WriteMiscThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk);
-
- private:
- uint32_t size_code_alignment_;
- uint32_t size_relative_call_thunks_;
- uint32_t size_misc_thunks_;
-
- DISALLOW_COPY_AND_ASSIGN(RelativePatcher);
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_RELATIVE_PATCHER_H_
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
deleted file mode 100644
index af8dc4dbc9..0000000000
--- a/compiler/linker/relative_patcher_test.h
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_
-#define ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_
-
-#include "arch/instruction_set.h"
-#include "arch/instruction_set_features.h"
-#include "base/array_ref.h"
-#include "base/macros.h"
-#include "compiled_method-inl.h"
-#include "dex/verification_results.h"
-#include "dex/method_reference.h"
-#include "dex/string_reference.h"
-#include "driver/compiler_driver.h"
-#include "driver/compiler_options.h"
-#include "globals.h"
-#include "gtest/gtest.h"
-#include "linker/relative_patcher.h"
-#include "oat.h"
-#include "oat_quick_method_header.h"
-#include "vector_output_stream.h"
-
-namespace art {
-namespace linker {
-
-// Base class providing infrastructure for architecture-specific tests.
-class RelativePatcherTest : public testing::Test {
- protected:
- RelativePatcherTest(InstructionSet instruction_set, const std::string& variant)
- : compiler_options_(),
- verification_results_(&compiler_options_),
- driver_(&compiler_options_,
- &verification_results_,
- Compiler::kQuick,
- instruction_set,
- /* instruction_set_features*/ nullptr,
- /* image_classes */ nullptr,
- /* compiled_classes */ nullptr,
- /* compiled_methods */ nullptr,
- /* thread_count */ 1u,
- /* swap_fd */ -1,
- /* profile_compilation_info */ nullptr),
- error_msg_(),
- instruction_set_(instruction_set),
- features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)),
- method_offset_map_(),
- patcher_(RelativePatcher::Create(instruction_set,
- features_.get(),
- &thunk_provider_,
- &method_offset_map_)),
- bss_begin_(0u),
- compiled_method_refs_(),
- compiled_methods_(),
- patched_code_(),
- output_(),
- out_("test output stream", &output_) {
- CHECK(error_msg_.empty()) << instruction_set << "/" << variant;
- patched_code_.reserve(16 * KB);
- }
-
- MethodReference MethodRef(uint32_t method_idx) {
- CHECK_NE(method_idx, 0u);
- return MethodReference(nullptr, method_idx);
- }
-
- void AddCompiledMethod(
- MethodReference method_ref,
- const ArrayRef<const uint8_t>& code,
- const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) {
- compiled_method_refs_.push_back(method_ref);
- compiled_methods_.emplace_back(new CompiledMethod(
- &driver_,
- instruction_set_,
- code,
- /* frame_size_in_bytes */ 0u,
- /* core_spill_mask */ 0u,
- /* fp_spill_mask */ 0u,
- /* method_info */ ArrayRef<const uint8_t>(),
- /* vmap_table */ ArrayRef<const uint8_t>(),
- /* cfi_info */ ArrayRef<const uint8_t>(),
- patches));
- }
-
- uint32_t CodeAlignmentSize(uint32_t header_offset_to_align) {
- // We want to align the code rather than the preheader.
- uint32_t unaligned_code_offset = header_offset_to_align + sizeof(OatQuickMethodHeader);
- uint32_t aligned_code_offset =
- CompiledMethod::AlignCode(unaligned_code_offset, instruction_set_);
- return aligned_code_offset - unaligned_code_offset;
- }
-
- void Link() {
- // Reserve space.
- static_assert(kTrampolineOffset == 0u, "Unexpected trampoline offset.");
- uint32_t offset = kTrampolineSize;
- size_t idx = 0u;
- for (auto& compiled_method : compiled_methods_) {
- offset = patcher_->ReserveSpace(offset, compiled_method.get(), compiled_method_refs_[idx]);
-
- uint32_t alignment_size = CodeAlignmentSize(offset);
- offset += alignment_size;
-
- offset += sizeof(OatQuickMethodHeader);
- uint32_t quick_code_offset = offset + compiled_method->CodeDelta();
- const auto code = compiled_method->GetQuickCode();
- offset += code.size();
-
- method_offset_map_.map.Put(compiled_method_refs_[idx], quick_code_offset);
- ++idx;
- }
- offset = patcher_->ReserveSpaceEnd(offset);
- uint32_t output_size = offset;
- output_.reserve(output_size);
-
- // Write data.
- DCHECK(output_.empty());
- uint8_t dummy_trampoline[kTrampolineSize];
- memset(dummy_trampoline, 0, sizeof(dummy_trampoline));
- out_.WriteFully(dummy_trampoline, kTrampolineSize);
- offset = kTrampolineSize;
- static const uint8_t kPadding[] = {
- 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
- };
- uint8_t dummy_header[sizeof(OatQuickMethodHeader)];
- memset(dummy_header, 0, sizeof(dummy_header));
- for (auto& compiled_method : compiled_methods_) {
- offset = patcher_->WriteThunks(&out_, offset);
-
- uint32_t alignment_size = CodeAlignmentSize(offset);
- CHECK_LE(alignment_size, sizeof(kPadding));
- out_.WriteFully(kPadding, alignment_size);
- offset += alignment_size;
-
- out_.WriteFully(dummy_header, sizeof(OatQuickMethodHeader));
- offset += sizeof(OatQuickMethodHeader);
- ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
- if (!compiled_method->GetPatches().empty()) {
- patched_code_.assign(code.begin(), code.end());
- code = ArrayRef<const uint8_t>(patched_code_);
- for (const LinkerPatch& patch : compiled_method->GetPatches()) {
- if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
- auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod());
- uint32_t target_offset =
- result.first ? result.second : kTrampolineOffset + compiled_method->CodeDelta();
- patcher_->PatchCall(&patched_code_, patch.LiteralOffset(),
- offset + patch.LiteralOffset(), target_offset);
- } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) {
- uint32_t target_offset =
- bss_begin_ + string_index_to_offset_map_.Get(patch.TargetStringIndex().index_);
- patcher_->PatchPcRelativeReference(&patched_code_,
- patch,
- offset + patch.LiteralOffset(),
- target_offset);
- } else if (patch.GetType() == LinkerPatch::Type::kStringRelative) {
- uint32_t target_offset =
- string_index_to_offset_map_.Get(patch.TargetStringIndex().index_);
- patcher_->PatchPcRelativeReference(&patched_code_,
- patch,
- offset + patch.LiteralOffset(),
- target_offset);
- } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) {
- patcher_->PatchBakerReadBarrierBranch(&patched_code_,
- patch,
- offset + patch.LiteralOffset());
- } else {
- LOG(FATAL) << "Bad patch type. " << patch.GetType();
- UNREACHABLE();
- }
- }
- }
- out_.WriteFully(&code[0], code.size());
- offset += code.size();
- }
- offset = patcher_->WriteThunks(&out_, offset);
- CHECK_EQ(offset, output_size);
- CHECK_EQ(output_.size(), output_size);
- }
-
- bool CheckLinkedMethod(MethodReference method_ref, const ArrayRef<const uint8_t>& expected_code) {
- // Sanity check: original code size must match linked_code.size().
- size_t idx = 0u;
- for (auto ref : compiled_method_refs_) {
- if (ref == method_ref) {
- break;
- }
- ++idx;
- }
- CHECK_NE(idx, compiled_method_refs_.size());
- CHECK_EQ(compiled_methods_[idx]->GetQuickCode().size(), expected_code.size());
-
- auto result = method_offset_map_.FindMethodOffset(method_ref);
- CHECK(result.first); // Must have been linked.
- size_t offset = result.second - compiled_methods_[idx]->CodeDelta();
- CHECK_LT(offset, output_.size());
- CHECK_LE(offset + expected_code.size(), output_.size());
- ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size());
- if (linked_code == expected_code) {
- return true;
- }
- // Log failure info.
- DumpDiff(expected_code, linked_code);
- return false;
- }
-
- void DumpDiff(const ArrayRef<const uint8_t>& expected_code,
- const ArrayRef<const uint8_t>& linked_code) {
- std::ostringstream expected_hex;
- std::ostringstream linked_hex;
- std::ostringstream diff_indicator;
- static const char digits[] = "0123456789abcdef";
- bool found_diff = false;
- for (size_t i = 0; i != expected_code.size(); ++i) {
- expected_hex << " " << digits[expected_code[i] >> 4] << digits[expected_code[i] & 0xf];
- linked_hex << " " << digits[linked_code[i] >> 4] << digits[linked_code[i] & 0xf];
- if (!found_diff) {
- found_diff = (expected_code[i] != linked_code[i]);
- diff_indicator << (found_diff ? " ^^" : " ");
- }
- }
- CHECK(found_diff);
- std::string expected_hex_str = expected_hex.str();
- std::string linked_hex_str = linked_hex.str();
- std::string diff_indicator_str = diff_indicator.str();
- if (diff_indicator_str.length() > 60) {
- CHECK_EQ(diff_indicator_str.length() % 3u, 0u);
- size_t remove = diff_indicator_str.length() / 3 - 5;
- std::ostringstream oss;
- oss << "[stripped " << remove << "]";
- std::string replacement = oss.str();
- expected_hex_str.replace(0u, remove * 3u, replacement);
- linked_hex_str.replace(0u, remove * 3u, replacement);
- diff_indicator_str.replace(0u, remove * 3u, replacement);
- }
- LOG(ERROR) << "diff expected_code linked_code";
- LOG(ERROR) << "<" << expected_hex_str;
- LOG(ERROR) << ">" << linked_hex_str;
- LOG(ERROR) << " " << diff_indicator_str;
- }
-
- class ThunkProvider : public RelativePatcherThunkProvider {
- public:
- ThunkProvider() {}
-
- void SetThunkCode(const LinkerPatch& patch,
- ArrayRef<const uint8_t> code,
- const std::string& debug_name) {
- thunk_map_.emplace(ThunkKey(patch), ThunkValue(code, debug_name));
- }
-
- void GetThunkCode(const LinkerPatch& patch,
- /*out*/ ArrayRef<const uint8_t>* code,
- /*out*/ std::string* debug_name) OVERRIDE {
- auto it = thunk_map_.find(ThunkKey(patch));
- CHECK(it != thunk_map_.end());
- const ThunkValue& value = it->second;
- CHECK(code != nullptr);
- *code = value.GetCode();
- CHECK(debug_name != nullptr);
- *debug_name = value.GetDebugName();
- }
-
- private:
- class ThunkKey {
- public:
- explicit ThunkKey(const LinkerPatch& patch)
- : type_(patch.GetType()),
- custom_value1_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch
- ? patch.GetBakerCustomValue1() : 0u),
- custom_value2_(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch
- ? patch.GetBakerCustomValue2() : 0u) {
- CHECK(patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch ||
- patch.GetType() == LinkerPatch::Type::kCallRelative);
- }
-
- bool operator<(const ThunkKey& other) const {
- if (custom_value1_ != other.custom_value1_) {
- return custom_value1_ < other.custom_value1_;
- }
- if (custom_value2_ != other.custom_value2_) {
- return custom_value2_ < other.custom_value2_;
- }
- return type_ < other.type_;
- }
-
- private:
- const LinkerPatch::Type type_;
- const uint32_t custom_value1_;
- const uint32_t custom_value2_;
- };
-
- class ThunkValue {
- public:
- ThunkValue(ArrayRef<const uint8_t> code, const std::string& debug_name)
- : code_(code.begin(), code.end()), debug_name_(debug_name) {}
- ArrayRef<const uint8_t> GetCode() const { return ArrayRef<const uint8_t>(code_); }
- const std::string& GetDebugName() const { return debug_name_; }
-
- private:
- const std::vector<uint8_t> code_;
- const std::string debug_name_;
- };
-
- std::map<ThunkKey, ThunkValue> thunk_map_;
- };
-
- // Map method reference to assinged offset.
- // Wrap the map in a class implementing RelativePatcherTargetProvider.
- class MethodOffsetMap FINAL : public RelativePatcherTargetProvider {
- public:
- std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE {
- auto it = map.find(ref);
- if (it == map.end()) {
- return std::pair<bool, uint32_t>(false, 0u);
- } else {
- return std::pair<bool, uint32_t>(true, it->second);
- }
- }
- SafeMap<MethodReference, uint32_t> map;
- };
-
- static const uint32_t kTrampolineSize = 4u;
- static const uint32_t kTrampolineOffset = 0u;
-
- CompilerOptions compiler_options_;
- VerificationResults verification_results_;
- CompilerDriver driver_; // Needed for constructing CompiledMethod.
- std::string error_msg_;
- InstructionSet instruction_set_;
- std::unique_ptr<const InstructionSetFeatures> features_;
- ThunkProvider thunk_provider_;
- MethodOffsetMap method_offset_map_;
- std::unique_ptr<RelativePatcher> patcher_;
- uint32_t bss_begin_;
- SafeMap<uint32_t, uint32_t> string_index_to_offset_map_;
- std::vector<MethodReference> compiled_method_refs_;
- std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_;
- std::vector<uint8_t> patched_code_;
- std::vector<uint8_t> output_;
- VectorOutputStream out_;
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_RELATIVE_PATCHER_TEST_H_
diff --git a/compiler/linker/x86/relative_patcher_x86.cc b/compiler/linker/x86/relative_patcher_x86.cc
deleted file mode 100644
index cdd2cef13a..0000000000
--- a/compiler/linker/x86/relative_patcher_x86.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/x86/relative_patcher_x86.h"
-
-#include "compiled_method.h"
-#include "linker/linker_patch.h"
-
-namespace art {
-namespace linker {
-
-void X86RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
- uint32_t anchor_literal_offset = patch.PcInsnOffset();
- uint32_t literal_offset = patch.LiteralOffset();
-
- // Check that the anchor points to pop in a "call +0; pop <reg>" sequence.
- DCHECK_GE(anchor_literal_offset, 5u);
- DCHECK_LT(anchor_literal_offset, code->size());
- DCHECK_EQ((*code)[anchor_literal_offset - 5u], 0xe8u);
- DCHECK_EQ((*code)[anchor_literal_offset - 4u], 0x00u);
- DCHECK_EQ((*code)[anchor_literal_offset - 3u], 0x00u);
- DCHECK_EQ((*code)[anchor_literal_offset - 2u], 0x00u);
- DCHECK_EQ((*code)[anchor_literal_offset - 1u], 0x00u);
- DCHECK_EQ((*code)[anchor_literal_offset] & 0xf8u, 0x58u);
-
- // Check that the patched data contains kDummy32BitOffset.
- // Must match X86Mir2Lir::kDummy32BitOffset and CodeGeneratorX86_64::kDummy32BitOffset.
- constexpr int kDummy32BitOffset = 256;
- DCHECK_LE(literal_offset, code->size());
- DCHECK_EQ((*code)[literal_offset + 0u], static_cast<uint8_t>(kDummy32BitOffset >> 0));
- DCHECK_EQ((*code)[literal_offset + 1u], static_cast<uint8_t>(kDummy32BitOffset >> 8));
- DCHECK_EQ((*code)[literal_offset + 2u], static_cast<uint8_t>(kDummy32BitOffset >> 16));
- DCHECK_EQ((*code)[literal_offset + 3u], static_cast<uint8_t>(kDummy32BitOffset >> 24));
-
- // Apply patch.
- uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
- uint32_t diff = target_offset - anchor_offset;
- (*code)[literal_offset + 0u] = static_cast<uint8_t>(diff >> 0);
- (*code)[literal_offset + 1u] = static_cast<uint8_t>(diff >> 8);
- (*code)[literal_offset + 2u] = static_cast<uint8_t>(diff >> 16);
- (*code)[literal_offset + 3u] = static_cast<uint8_t>(diff >> 24);
-}
-
-void X86RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "UNIMPLEMENTED";
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/x86/relative_patcher_x86.h b/compiler/linker/x86/relative_patcher_x86.h
deleted file mode 100644
index 63a8338722..0000000000
--- a/compiler/linker/x86/relative_patcher_x86.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_
-#define ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_
-
-#include "linker/x86/relative_patcher_x86_base.h"
-
-namespace art {
-namespace linker {
-
-class X86RelativePatcher FINAL : public X86BaseRelativePatcher {
- public:
- X86RelativePatcher() { }
-
- void PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) OVERRIDE;
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_H_
diff --git a/compiler/linker/x86/relative_patcher_x86_base.cc b/compiler/linker/x86/relative_patcher_x86_base.cc
deleted file mode 100644
index 6a9690d768..0000000000
--- a/compiler/linker/x86/relative_patcher_x86_base.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/x86/relative_patcher_x86_base.h"
-
-#include "debug/method_debug_info.h"
-
-namespace art {
-namespace linker {
-
-uint32_t X86BaseRelativePatcher::ReserveSpace(
- uint32_t offset,
- const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
- MethodReference method_ref ATTRIBUTE_UNUSED) {
- return offset; // No space reserved; no limit on relative call distance.
-}
-
-uint32_t X86BaseRelativePatcher::ReserveSpaceEnd(uint32_t offset) {
- return offset; // No space reserved; no limit on relative call distance.
-}
-
-uint32_t X86BaseRelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) {
- return offset; // No thunks added; no limit on relative call distance.
-}
-
-std::vector<debug::MethodDebugInfo> X86BaseRelativePatcher::GenerateThunkDebugInfo(
- uint32_t executable_offset ATTRIBUTE_UNUSED) {
- return std::vector<debug::MethodDebugInfo>(); // No thunks added.
-}
-
-void X86BaseRelativePatcher::PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) {
- DCHECK_LE(literal_offset + 4u, code->size());
- // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
- uint32_t displacement = target_offset - patch_offset;
- displacement -= kPcDisplacement; // The base PC is at the end of the 4-byte patch.
-
- typedef __attribute__((__aligned__(1))) int32_t unaligned_int32_t;
- reinterpret_cast<unaligned_int32_t*>(&(*code)[literal_offset])[0] = displacement;
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/x86/relative_patcher_x86_base.h b/compiler/linker/x86/relative_patcher_x86_base.h
deleted file mode 100644
index 6097345657..0000000000
--- a/compiler/linker/x86/relative_patcher_x86_base.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_
-#define ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_
-
-#include "linker/relative_patcher.h"
-
-namespace art {
-namespace linker {
-
-class X86BaseRelativePatcher : public RelativePatcher {
- public:
- uint32_t ReserveSpace(uint32_t offset,
- const CompiledMethod* compiled_method,
- MethodReference method_ref) OVERRIDE;
- uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
- uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- void PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(uint32_t executable_offset) OVERRIDE;
-
- protected:
- X86BaseRelativePatcher() { }
-
- // PC displacement from patch location; the base address of x86/x86-64 relative
- // calls and x86-64 RIP-relative addressing is the PC of the next instruction and
- // the patch location is 4 bytes earlier.
- static constexpr int32_t kPcDisplacement = 4;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(X86BaseRelativePatcher);
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_X86_RELATIVE_PATCHER_X86_BASE_H_
diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/compiler/linker/x86/relative_patcher_x86_test.cc
deleted file mode 100644
index b855dec91d..0000000000
--- a/compiler/linker/x86/relative_patcher_x86_test.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/x86/relative_patcher_x86.h"
-
-#include "linker/relative_patcher_test.h"
-
-namespace art {
-namespace linker {
-
-class X86RelativePatcherTest : public RelativePatcherTest {
- public:
- X86RelativePatcherTest() : RelativePatcherTest(InstructionSet::kX86, "default") { }
-
- protected:
- static const uint8_t kCallRawCode[];
- static const ArrayRef<const uint8_t> kCallCode;
-
- uint32_t GetMethodOffset(uint32_t method_idx) {
- auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
- CHECK(result.first);
- return result.second;
- }
-};
-
-const uint8_t X86RelativePatcherTest::kCallRawCode[] = {
- 0xe8, 0x00, 0x01, 0x00, 0x00
-};
-
-const ArrayRef<const uint8_t> X86RelativePatcherTest::kCallCode(kCallRawCode);
-
-TEST_F(X86RelativePatcherTest, CallSelf) {
- LinkerPatch patches[] = {
- LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- static const uint8_t expected_code[] = {
- 0xe8, 0xfb, 0xff, 0xff, 0xff
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(X86RelativePatcherTest, CallOther) {
- LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
- LinkerPatch method2_patches[] = {
- LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t method2_offset = GetMethodOffset(2u);
- uint32_t diff_after = method2_offset - (method1_offset + kCallCode.size() /* PC adjustment */);
- static const uint8_t method1_expected_code[] = {
- 0xe8,
- static_cast<uint8_t>(diff_after),
- static_cast<uint8_t>(diff_after >> 8),
- static_cast<uint8_t>(diff_after >> 16),
- static_cast<uint8_t>(diff_after >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
- uint32_t diff_before = method1_offset - (method2_offset + kCallCode.size() /* PC adjustment */);
- static const uint8_t method2_expected_code[] = {
- 0xe8,
- static_cast<uint8_t>(diff_before),
- static_cast<uint8_t>(diff_before >> 8),
- static_cast<uint8_t>(diff_before >> 16),
- static_cast<uint8_t>(diff_before >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
-}
-
-TEST_F(X86RelativePatcherTest, CallTrampoline) {
- LinkerPatch patches[] = {
- LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1));
- ASSERT_TRUE(result.first);
- uint32_t diff = kTrampolineOffset - (result.second + kCallCode.size());
- static const uint8_t expected_code[] = {
- 0xe8,
- static_cast<uint8_t>(diff),
- static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16),
- static_cast<uint8_t>(diff >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(X86RelativePatcherTest, StringBssEntry) {
- bss_begin_ = 0x12345678;
- constexpr size_t kStringEntryOffset = 0x1234;
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, kStringEntryOffset);
- static const uint8_t raw_code[] = {
- 0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
- 0x5b, // pop ebx
- 0x8b, 0x83, 0x00, 0x01, 0x00, 0x00, // mov eax, [ebx + 256 (kDummy32BitValue)]
- };
- constexpr uint32_t anchor_offset = 5u; // After call +0.
- ArrayRef<const uint8_t> code(raw_code);
- LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(code.size() - 4u, nullptr, anchor_offset, kStringIndex),
- };
- AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
- ASSERT_TRUE(result.first);
- uint32_t diff = bss_begin_ + kStringEntryOffset - (result.second + anchor_offset);
- static const uint8_t expected_code[] = {
- 0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
- 0x5b, // pop ebx
- 0x8b, 0x83, // mov eax, [ebx + diff]
- static_cast<uint8_t>(diff),
- static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16),
- static_cast<uint8_t>(diff >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(X86RelativePatcherTest, StringReference) {
- constexpr uint32_t kStringIndex = 1u;
- constexpr uint32_t kStringOffset = 0x12345678;
- string_index_to_offset_map_.Put(kStringIndex, kStringOffset);
- static const uint8_t raw_code[] = {
- 0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
- 0x5b, // pop ebx
- 0x8d, 0x83, 0x00, 0x01, 0x00, 0x00, // lea eax, [ebx + 256 (kDummy32BitValue)]
- };
- constexpr uint32_t anchor_offset = 5u; // After call +0.
- ArrayRef<const uint8_t> code(raw_code);
- LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(code.size() - 4u, nullptr, anchor_offset, kStringIndex),
- };
- AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
- ASSERT_TRUE(result.first);
- uint32_t diff = kStringOffset - (result.second + anchor_offset);
- static const uint8_t expected_code[] = {
- 0xe8, 0x00, 0x00, 0x00, 0x00, // call +0
- 0x5b, // pop ebx
- 0x8d, 0x83, // lea eax, [ebx + diff]
- static_cast<uint8_t>(diff),
- static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16),
- static_cast<uint8_t>(diff >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/compiler/linker/x86_64/relative_patcher_x86_64.cc
deleted file mode 100644
index 9633564999..0000000000
--- a/compiler/linker/x86_64/relative_patcher_x86_64.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/x86_64/relative_patcher_x86_64.h"
-
-#include "compiled_method.h"
-#include "linker/linker_patch.h"
-
-namespace art {
-namespace linker {
-
-void X86_64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) {
- DCHECK_LE(patch.LiteralOffset() + 4u, code->size());
- // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
- uint32_t displacement = target_offset - patch_offset;
- displacement -= kPcDisplacement; // The base PC is at the end of the 4-byte patch.
-
- typedef __attribute__((__aligned__(1))) int32_t unaligned_int32_t;
- reinterpret_cast<unaligned_int32_t*>(&(*code)[patch.LiteralOffset()])[0] = displacement;
-}
-
-void X86_64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "UNIMPLEMENTED";
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/compiler/linker/x86_64/relative_patcher_x86_64.h
deleted file mode 100644
index 4f3ec498cb..0000000000
--- a/compiler/linker/x86_64/relative_patcher_x86_64.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_
-#define ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_
-
-#include "linker/x86/relative_patcher_x86_base.h"
-
-namespace art {
-namespace linker {
-
-class X86_64RelativePatcher FINAL : public X86BaseRelativePatcher {
- public:
- X86_64RelativePatcher() { }
-
- void PatchPcRelativeReference(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE;
- void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
- const LinkerPatch& patch,
- uint32_t patch_offset) OVERRIDE;
-};
-
-} // namespace linker
-} // namespace art
-
-#endif // ART_COMPILER_LINKER_X86_64_RELATIVE_PATCHER_X86_64_H_
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
deleted file mode 100644
index 6baa92de36..0000000000
--- a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "linker/x86_64/relative_patcher_x86_64.h"
-
-#include "linker/relative_patcher_test.h"
-
-namespace art {
-namespace linker {
-
-class X86_64RelativePatcherTest : public RelativePatcherTest {
- public:
- X86_64RelativePatcherTest() : RelativePatcherTest(InstructionSet::kX86_64, "default") { }
-
- protected:
- static const uint8_t kCallRawCode[];
- static const ArrayRef<const uint8_t> kCallCode;
- static const uint8_t kDexCacheLoadRawCode[];
- static const ArrayRef<const uint8_t> kDexCacheLoadCode;
- static const uint8_t kStringReferenceRawCode[];
- static const ArrayRef<const uint8_t> kStringReferenceCode;
-
- uint32_t GetMethodOffset(uint32_t method_idx) {
- auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
- CHECK(result.first);
- return result.second;
- }
-};
-
-const uint8_t X86_64RelativePatcherTest::kCallRawCode[] = {
- 0xe8, 0x00, 0x01, 0x00, 0x00
-};
-
-const ArrayRef<const uint8_t> X86_64RelativePatcherTest::kCallCode(kCallRawCode);
-
-const uint8_t X86_64RelativePatcherTest::kDexCacheLoadRawCode[] = {
- 0x8b, 0x05, // mov eax, [rip + <offset>]
- 0x00, 0x01, 0x00, 0x00
-};
-
-const ArrayRef<const uint8_t> X86_64RelativePatcherTest::kDexCacheLoadCode(
- kDexCacheLoadRawCode);
-
-const uint8_t X86_64RelativePatcherTest::kStringReferenceRawCode[] = {
- 0x8d, 0x05, // lea eax, [rip + <offset>]
- 0x00, 0x01, 0x00, 0x00
-};
-
-const ArrayRef<const uint8_t> X86_64RelativePatcherTest::kStringReferenceCode(
- kStringReferenceRawCode);
-
-TEST_F(X86_64RelativePatcherTest, CallSelf) {
- LinkerPatch patches[] = {
- LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- static const uint8_t expected_code[] = {
- 0xe8, 0xfb, 0xff, 0xff, 0xff
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(X86_64RelativePatcherTest, CallOther) {
- LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
- LinkerPatch method2_patches[] = {
- LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t method2_offset = GetMethodOffset(2u);
- uint32_t diff_after = method2_offset - (method1_offset + kCallCode.size() /* PC adjustment */);
- static const uint8_t method1_expected_code[] = {
- 0xe8,
- static_cast<uint8_t>(diff_after),
- static_cast<uint8_t>(diff_after >> 8),
- static_cast<uint8_t>(diff_after >> 16),
- static_cast<uint8_t>(diff_after >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
- uint32_t diff_before = method1_offset - (method2_offset + kCallCode.size() /* PC adjustment */);
- static const uint8_t method2_expected_code[] = {
- 0xe8,
- static_cast<uint8_t>(diff_before),
- static_cast<uint8_t>(diff_before >> 8),
- static_cast<uint8_t>(diff_before >> 16),
- static_cast<uint8_t>(diff_before >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
-}
-
-TEST_F(X86_64RelativePatcherTest, CallTrampoline) {
- LinkerPatch patches[] = {
- LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
- ASSERT_TRUE(result.first);
- uint32_t diff = kTrampolineOffset - (result.second + kCallCode.size());
- static const uint8_t expected_code[] = {
- 0xe8,
- static_cast<uint8_t>(diff),
- static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16),
- static_cast<uint8_t>(diff >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(X86_64RelativePatcherTest, StringBssEntry) {
- bss_begin_ = 0x12345678;
- constexpr size_t kStringEntryOffset = 0x1234;
- constexpr uint32_t kStringIndex = 1u;
- string_index_to_offset_map_.Put(kStringIndex, kStringEntryOffset);
- LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(kDexCacheLoadCode.size() - 4u, nullptr, 0u, kStringIndex),
- };
- AddCompiledMethod(MethodRef(1u), kDexCacheLoadCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
- ASSERT_TRUE(result.first);
- uint32_t diff = bss_begin_ + kStringEntryOffset - (result.second + kDexCacheLoadCode.size());
- static const uint8_t expected_code[] = {
- 0x8b, 0x05,
- static_cast<uint8_t>(diff),
- static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16),
- static_cast<uint8_t>(diff >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-TEST_F(X86_64RelativePatcherTest, StringReference) {
- constexpr uint32_t kStringIndex = 1u;
- constexpr uint32_t kStringOffset = 0x12345678;
- string_index_to_offset_map_.Put(kStringIndex, kStringOffset);
- LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(
- kStringReferenceCode.size() - 4u, nullptr, 0u, kStringIndex),
- };
- AddCompiledMethod(MethodRef(1u), kStringReferenceCode, ArrayRef<const LinkerPatch>(patches));
- Link();
-
- auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
- ASSERT_TRUE(result.first);
- uint32_t diff = kStringOffset - (result.second + kStringReferenceCode.size());
- static const uint8_t expected_code[] = {
- 0x8d, 0x05,
- static_cast<uint8_t>(diff),
- static_cast<uint8_t>(diff >> 8),
- static_cast<uint8_t>(diff >> 16),
- static_cast<uint8_t>(diff >> 24)
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
-}
-
-} // namespace linker
-} // namespace art
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 31887d92e8..d4cfab82de 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -6838,7 +6838,8 @@ void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_
static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler,
vixl::aarch64::Register base_reg,
vixl::aarch64::MemOperand& lock_word,
- vixl::aarch64::Label* slow_path) {
+ vixl::aarch64::Label* slow_path,
+ vixl::aarch64::Label* throw_npe = nullptr) {
// Load the lock word containing the rb_state.
__ Ldr(ip0.W(), lock_word);
// Given the numeric representation, it's enough to check the low bit of the rb_state.
@@ -6848,6 +6849,10 @@ static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler,
static_assert(
BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET,
"Field and array LDR offsets must be the same to reuse the same code.");
+ // To throw NPE, we return to the fast path; the artificial dependence below does not matter.
+ if (throw_npe != nullptr) {
+ __ Bind(throw_npe);
+ }
// Adjust the return address back to the LDR (1 instruction; 2 for heap poisoning).
static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
"Field LDR must be 1 instruction (4B) before the return address label; "
@@ -6877,10 +6882,6 @@ void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler,
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 base_reg =
Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data));
CheckValidReg(base_reg.GetCode());
@@ -6889,16 +6890,22 @@ void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler,
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
- // emitted an explicit null check before the load. Otherwise, we need to null-check
- // the holder as we do not necessarily do that check before going to the thunk.
- vixl::aarch64::Label throw_npe;
- if (holder_reg.Is(base_reg)) {
- __ Cbz(holder_reg.W(), &throw_npe);
+ // If base_reg differs from holder_reg, the offset was too large and we must have emitted
+ // an explicit null check before the load. Otherwise, for implicit null checks, we need to
+ // null-check the holder as we do not necessarily do that check before going to the thunk.
+ vixl::aarch64::Label throw_npe_label;
+ vixl::aarch64::Label* throw_npe = nullptr;
+ if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
+ throw_npe = &throw_npe_label;
+ __ Cbz(holder_reg.W(), throw_npe);
}
+ // 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 that performs further checks on the
+ // reference and marks it if needed.
vixl::aarch64::Label slow_path;
MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
- EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path);
+ EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, throw_npe);
__ Bind(&slow_path);
MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET);
__ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset.
@@ -6907,13 +6914,6 @@ void CodeGeneratorARM64::CompileBakerReadBarrierThunk(Arm64Assembler& assembler,
__ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference.
// Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
__ Br(ip1); // Jump to the entrypoint.
- if (holder_reg.Is(base_reg)) {
- // Add null check slow path. The stack map is at the address pointed to by LR.
- __ Bind(&throw_npe);
- int32_t offset = GetThreadOffset<kArm64PointerSize>(kQuickThrowNullPointer).Int32Value();
- __ Ldr(ip0, MemOperand(/* Thread* */ vixl::aarch64::x19, offset));
- __ Br(ip0);
- }
break;
}
case BakerReadBarrierKind::kArray: {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 15d952608d..7350b146f9 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -29,7 +29,6 @@
#include "gc/accounting/card_table.h"
#include "heap_poisoning.h"
#include "intrinsics_arm_vixl.h"
-#include "linker/arm/relative_patcher_thumb2.h"
#include "linker/linker_patch.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
@@ -9905,7 +9904,8 @@ static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
vixl32::Register base_reg,
vixl32::MemOperand& lock_word,
vixl32::Label* slow_path,
- int32_t raw_ldr_offset) {
+ int32_t raw_ldr_offset,
+ vixl32::Label* throw_npe = nullptr) {
// Load the lock word containing the rb_state.
__ Ldr(ip, lock_word);
// Given the numeric representation, it's enough to check the low bit of the rb_state.
@@ -9913,6 +9913,10 @@ static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
__ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted));
__ B(ne, slow_path, /* is_far_target */ false);
+ // To throw NPE, we return to the fast path; the artificial dependence below does not matter.
+ if (throw_npe != nullptr) {
+ __ Bind(throw_npe);
+ }
__ Add(lr, lr, raw_ldr_offset);
// Introduce a dependency on the lock_word including rb_state,
// to prevent load-load reordering, and without using
@@ -9926,7 +9930,7 @@ static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
static void LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler,
vixl32::Register entrypoint) {
// The register where the read barrier introspection entrypoint is loaded
- // is fixed: `Thumb2RelativePatcher::kBakerCcEntrypointRegister` (R4).
+ // is fixed: `kBakerCcEntrypointRegister` (R4).
DCHECK(entrypoint.Is(kBakerCcEntrypointRegister));
// entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
DCHECK_EQ(ip.GetCode(), 12u);
@@ -9941,10 +9945,6 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb
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.
vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
CheckValidReg(base_reg.GetCode());
vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data));
@@ -9952,19 +9952,26 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb
BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
temps.Exclude(ip);
- // If base_reg differs from holder_reg, the offset was too large and we must have
- // emitted an explicit null check before the load. Otherwise, we need to null-check
- // the holder as we do not necessarily do that check before going to the thunk.
- vixl32::Label throw_npe;
- if (holder_reg.Is(base_reg)) {
- __ CompareAndBranchIfZero(holder_reg, &throw_npe, /* is_far_target */ false);
+ // If base_reg differs from holder_reg, the offset was too large and we must have emitted
+ // an explicit null check before the load. Otherwise, for implicit null checks, we need to
+ // null-check the holder as we do not necessarily do that check before going to the thunk.
+ vixl32::Label throw_npe_label;
+ vixl32::Label* throw_npe = nullptr;
+ if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
+ throw_npe = &throw_npe_label;
+ __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target */ false);
}
+ // 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 that performs further checks on the
+ // reference and marks it if needed.
vixl32::Label slow_path;
MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide)
? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET
: BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET;
- EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset);
+ EmitGrayCheckAndFastPath(
+ assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe);
__ Bind(&slow_path);
const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
raw_ldr_offset;
@@ -9986,13 +9993,6 @@ void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assemb
}
// Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
__ Bx(ep_reg); // Jump to the entrypoint.
- if (holder_reg.Is(base_reg)) {
- // Add null check slow path. The stack map is at the address pointed to by LR.
- __ Bind(&throw_npe);
- int32_t offset = GetThreadOffset<kArmPointerSize>(kQuickThrowNullPointer).Int32Value();
- __ Ldr(ip, MemOperand(/* Thread* */ vixl32::r9, offset));
- __ Bx(ip);
- }
break;
}
case BakerReadBarrierKind::kArray: {