diff options
| author | 2018-04-13 09:34:00 +0000 | |
|---|---|---|
| committer | 2018-04-13 09:34:00 +0000 | |
| commit | 8a78f81a16a7c8225b48bf634b67f5697f72e15f (patch) | |
| tree | 1e7e6ee76ec6dc88d55d8681bf5816108b18b51b /compiler | |
| parent | be2d2ad255585f639d657022ab55a78b682298be (diff) | |
| parent | 6d66fcf87dee9d2207ea745969b1f89836b0f8c5 (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')
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(¤t_method_thunks_, thunks_code_offset, adrp); - SetInsn(¤t_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(¤t_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: { |