diff options
author | 2019-05-17 12:05:28 +0100 | |
---|---|---|
committer | 2019-05-23 11:47:28 +0000 | |
commit | f667508a2103cfafd1582df6aeea144490f1d11d (patch) | |
tree | 7394cec1f1463a86deb75dcecca9f3eacd8ecb03 | |
parent | 8fa839cfe6f72adabdf79f938c57300e589e0803 (diff) |
ARM/ARM64: Use trampolines for slow-path entrypoint calls.
This reduces the size of the generated code. We do this only
for AOT compilation where we get the most benefit.
Sizes of aosp_taimen-userdebug prebuilts:
- before:
arm/boot*.oat: 19624804
arm64/boot*.oat: 23265752
oat/arm64/services.odex: 22417968
- after:
arm/boot*.oat: 19460500 (-160KiB)
arm64/boot*.oat: 22957928 (-301KiB)
oat/arm64/services.odex: 21957864 (-449KiB)
Test: m test-art-host-gtest
Test: aosp_taimen-userdebug boots.
Test: run-gtests.sh
Test: testrunner.py --target --optimizing
Bug: 12607709
Change-Id: Ie9dbd1ba256173e4e439e8bbb8832a791965cbe6
29 files changed, 403 insertions, 87 deletions
diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index 31062fb390..03c906b991 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -216,6 +216,9 @@ CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey( uint32_t custom_value1 = 0u; uint32_t custom_value2 = 0u; switch (linker_patch.GetType()) { + case linker::LinkerPatch::Type::kCallEntrypoint: + custom_value1 = linker_patch.EntrypointOffset(); + break; case linker::LinkerPatch::Type::kBakerReadBarrierBranch: custom_value1 = linker_patch.GetBakerCustomValue1(); custom_value2 = linker_patch.GetBakerCustomValue2(); diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index f9e3930f56..1c523dee36 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -52,6 +52,7 @@ class LinkerPatch { kTypeBssEntry, kStringRelative, kStringBssEntry, + kCallEntrypoint, kBakerReadBarrierBranch, }; @@ -141,6 +142,15 @@ class LinkerPatch { return patch; } + static LinkerPatch CallEntrypointPatch(size_t literal_offset, + uint32_t entrypoint_offset) { + LinkerPatch patch(literal_offset, + Type::kCallEntrypoint, + /* target_dex_file= */ nullptr); + patch.entrypoint_offset_ = entrypoint_offset; + return patch; + } + static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset, uint32_t custom_value1 = 0u, uint32_t custom_value2 = 0u) { @@ -216,6 +226,11 @@ class LinkerPatch { return pc_insn_offset_; } + uint32_t EntrypointOffset() const { + DCHECK(patch_type_ == Type::kCallEntrypoint); + return entrypoint_offset_; + } + uint32_t GetBakerCustomValue1() const { DCHECK(patch_type_ == Type::kBakerReadBarrierBranch); return baker_custom_value1_; @@ -249,6 +264,7 @@ class LinkerPatch { uint32_t type_idx_; // Type index for Type patches. uint32_t string_idx_; // String index for String patches. uint32_t intrinsic_data_; // Data for IntrinsicObjects. + uint32_t entrypoint_offset_; // Entrypoint offset in the Thread object. uint32_t baker_custom_value1_; static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators"); static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators"); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 177d98248f..651a3f7df6 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -887,10 +887,6 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, move_resolver_(graph->GetAllocator(), this), assembler_(graph->GetAllocator(), compiler_options.GetInstructionSetFeatures()->AsArm64InstructionSetFeatures()), - uint32_literals_(std::less<uint32_t>(), - graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), - uint64_literals_(std::less<uint64_t>(), - graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -898,7 +894,12 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + call_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + uint32_literals_(std::less<uint32_t>(), + graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + uint64_literals_(std::less<uint64_t>(), + graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_class_patches_(TypeReferenceValueComparator(), @@ -1687,14 +1688,25 @@ void CodeGeneratorARM64::InvokeRuntime(QuickEntrypointEnum entrypoint, SlowPathCode* slow_path) { ValidateInvokeRuntime(entrypoint, instruction, slow_path); - __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64PointerSize>(entrypoint).Int32Value())); - { + ThreadOffset64 entrypoint_offset = GetThreadOffset<kArm64PointerSize>(entrypoint); + // Reduce code size for AOT by using shared trampolines for slow path runtime calls across the + // entire oat file. This adds an extra branch and we do not want to slow down the main path. + // For JIT, thunk sharing is per-method, so the gains would be smaller or even negative. + if (slow_path == nullptr || Runtime::Current()->UseJitCompilation()) { + __ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value())); // Ensure the pc position is recorded immediately after the `blr` instruction. ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize); __ blr(lr); if (EntrypointRequiresStackMap(entrypoint)) { RecordPcInfo(instruction, dex_pc, slow_path); } + } else { + // Ensure the pc position is recorded immediately after the `bl` instruction. + ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize); + EmitEntrypointThunkCall(entrypoint_offset); + if (EntrypointRequiresStackMap(entrypoint)) { + RecordPcInfo(instruction, dex_pc, slow_path); + } } } @@ -4250,6 +4262,15 @@ vixl::aarch64::Label* CodeGeneratorARM64::NewStringBssEntryPatch( return NewPcRelativePatch(&dex_file, string_index.index_, adrp_label, &string_bss_entry_patches_); } +void CodeGeneratorARM64::EmitEntrypointThunkCall(ThreadOffset64 entrypoint_offset) { + DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope. + DCHECK(!Runtime::Current()->UseJitCompilation()); + call_entrypoint_patches_.emplace_back(/*dex_file*/ nullptr, entrypoint_offset.Uint32Value()); + vixl::aarch64::Label* bl_label = &call_entrypoint_patches_.back().label; + __ bind(bl_label); + __ bl(static_cast<int64_t>(0)); // Placeholder, patched at link-time. +} + void CodeGeneratorARM64::EmitBakerReadBarrierCbnz(uint32_t custom_data) { DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope. if (Runtime::Current()->UseJitCompilation()) { @@ -4406,6 +4427,7 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* lin boot_image_string_patches_.size() + string_bss_entry_patches_.size() + boot_image_intrinsic_patches_.size() + + call_entrypoint_patches_.size() + baker_read_barrier_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { @@ -4430,6 +4452,11 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* lin type_bss_entry_patches_, linker_patches); EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>( string_bss_entry_patches_, linker_patches); + for (const PatchInfo<vixl::aarch64::Label>& info : call_entrypoint_patches_) { + DCHECK(info.target_dex_file == nullptr); + linker_patches->push_back(linker::LinkerPatch::CallEntrypointPatch( + info.label.GetLocation(), info.offset_or_index)); + } for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch( info.label.GetLocation(), info.custom_data)); @@ -4438,7 +4465,8 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* lin } bool CodeGeneratorARM64::NeedsThunkCode(const linker::LinkerPatch& patch) const { - return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || + return patch.GetType() == linker::LinkerPatch::Type::kCallEntrypoint || + patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || patch.GetType() == linker::LinkerPatch::Type::kCallRelative; } @@ -4458,6 +4486,14 @@ void CodeGeneratorARM64::EmitThunkCode(const linker::LinkerPatch& patch, } break; } + case linker::LinkerPatch::Type::kCallEntrypoint: { + Offset offset(patch.EntrypointOffset()); + assembler.JumpTo(ManagedRegister(arm64::TR), offset, ManagedRegister(arm64::IP0)); + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + *debug_name = "EntrypointCallThunk_" + std::to_string(offset.Uint32Value()); + } + break; + } case linker::LinkerPatch::Type::kBakerReadBarrierBranch: { DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index ada5742fc0..2680bd0222 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -629,6 +629,9 @@ class CodeGeneratorARM64 : public CodeGenerator { dex::StringIndex string_index, vixl::aarch64::Label* adrp_label = nullptr); + // Emit the BL instruction for entrypoint thunk call and record the associated patch for AOT. + void EmitEntrypointThunkCall(ThreadOffset64 entrypoint_offset); + // Emit the CBNZ instruction for baker read barrier and record // the associated patch for AOT or slow path for JIT. void EmitBakerReadBarrierCbnz(uint32_t custom_data); @@ -887,10 +890,6 @@ class CodeGeneratorARM64 : public CodeGenerator { ParallelMoveResolverARM64 move_resolver_; Arm64Assembler assembler_; - // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. - Uint32ToLiteralMap uint32_literals_; - // Deduplication map for 64-bit literals, used for non-patchable method address or method code. - Uint64ToLiteralMap uint64_literals_; // PC-relative method patch info for kBootImageLinkTimePcRelative/BootImageRelRo. // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque<PcRelativePatchInfo> boot_image_method_patches_; @@ -906,9 +905,15 @@ class CodeGeneratorARM64 : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> string_bss_entry_patches_; // PC-relative patch info for IntrinsicObjects. ArenaDeque<PcRelativePatchInfo> boot_image_intrinsic_patches_; + // Patch info for calls to entrypoint dispatch thunks. Used for slow paths. + ArenaDeque<PatchInfo<vixl::aarch64::Label>> call_entrypoint_patches_; // Baker read barrier patch info. ArenaDeque<BakerReadBarrierPatchInfo> baker_read_barrier_patches_; + // Deduplication map for 32-bit literals, used for JIT for boot image addresses. + Uint32ToLiteralMap uint32_literals_; + // Deduplication map for 64-bit literals, used for JIT for method address or method code. + Uint64ToLiteralMap uint64_literals_; // Patches for string literals in JIT compiled code. StringToLiteralMap jit_string_patches_; // Patches for class literals in JIT compiled code. diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 19d04c9fdc..ac09183c8b 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -1856,8 +1856,6 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, instruction_visitor_(graph, this), move_resolver_(graph->GetAllocator(), this), assembler_(graph->GetAllocator()), - uint32_literals_(std::less<uint32_t>(), - graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), @@ -1865,7 +1863,10 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + call_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), + uint32_literals_(std::less<uint32_t>(), + graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)), jit_class_patches_(TypeReferenceValueComparator(), @@ -2383,15 +2384,31 @@ void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint, uint32_t dex_pc, SlowPathCode* slow_path) { ValidateInvokeRuntime(entrypoint, instruction, slow_path); - __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value())); - // Ensure the pc position is recorded immediately after the `blx` instruction. - // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - ExactAssemblyScope aas(GetVIXLAssembler(), - vixl32::k16BitT32InstructionSizeInBytes, - CodeBufferCheckScope::kExactSize); - __ blx(lr); - if (EntrypointRequiresStackMap(entrypoint)) { - RecordPcInfo(instruction, dex_pc, slow_path); + + ThreadOffset32 entrypoint_offset = GetThreadOffset<kArmPointerSize>(entrypoint); + // Reduce code size for AOT by using shared trampolines for slow path runtime calls across the + // entire oat file. This adds an extra branch and we do not want to slow down the main path. + // For JIT, thunk sharing is per-method, so the gains would be smaller or even negative. + if (slow_path == nullptr || Runtime::Current()->UseJitCompilation()) { + __ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value())); + // Ensure the pc position is recorded immediately after the `blx` instruction. + // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); + __ blx(lr); + if (EntrypointRequiresStackMap(entrypoint)) { + RecordPcInfo(instruction, dex_pc, slow_path); + } + } else { + // Ensure the pc position is recorded immediately after the `bl` instruction. + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::k32BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); + EmitEntrypointThunkCall(entrypoint_offset); + if (EntrypointRequiresStackMap(entrypoint)) { + RecordPcInfo(instruction, dex_pc, slow_path); + } } } @@ -8858,6 +8875,17 @@ CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePa return &patches->back(); } +void CodeGeneratorARMVIXL::EmitEntrypointThunkCall(ThreadOffset32 entrypoint_offset) { + DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope. + DCHECK(!Runtime::Current()->UseJitCompilation()); + call_entrypoint_patches_.emplace_back(/*dex_file*/ nullptr, entrypoint_offset.Uint32Value()); + vixl::aarch32::Label* bl_label = &call_entrypoint_patches_.back().label; + __ bind(bl_label); + vixl32::Label placeholder_label; + __ bl(&placeholder_label); // Placeholder, patched at link-time. + __ bind(&placeholder_label); +} + void CodeGeneratorARMVIXL::EmitBakerReadBarrierBne(uint32_t custom_data) { DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope. if (Runtime::Current()->UseJitCompilation()) { @@ -8980,6 +9008,7 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* l /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() + /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() + /* MOVW+MOVT for each entry */ 2u * boot_image_intrinsic_patches_.size() + + call_entrypoint_patches_.size() + baker_read_barrier_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { @@ -9004,6 +9033,11 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* l type_bss_entry_patches_, linker_patches); EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>( string_bss_entry_patches_, linker_patches); + for (const PatchInfo<vixl32::Label>& info : call_entrypoint_patches_) { + DCHECK(info.target_dex_file == nullptr); + linker_patches->push_back(linker::LinkerPatch::CallEntrypointPatch( + info.label.GetLocation(), info.offset_or_index)); + } for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch( info.label.GetLocation(), info.custom_data)); @@ -9012,7 +9046,8 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* l } bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const { - return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || + return patch.GetType() == linker::LinkerPatch::Type::kCallEntrypoint || + patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch || patch.GetType() == linker::LinkerPatch::Type::kCallRelative; } @@ -9021,23 +9056,30 @@ void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch, /*out*/ std::string* debug_name) { arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator()); switch (patch.GetType()) { - case linker::LinkerPatch::Type::kCallRelative: + case linker::LinkerPatch::Type::kCallRelative: { // The thunk just uses the entry point in the ArtMethod. This works even for calls // to the generic JNI and interpreter trampolines. - assembler.LoadFromOffset( - arm::kLoadWord, - vixl32::pc, - vixl32::r0, - ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); + MemberOffset offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize); + assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, vixl32::r0, offset.Int32Value()); assembler.GetVIXLAssembler()->Bkpt(0); if (GetCompilerOptions().GenerateAnyDebugInfo()) { *debug_name = "MethodCallThunk"; } break; - case linker::LinkerPatch::Type::kBakerReadBarrierBranch: + } + case linker::LinkerPatch::Type::kCallEntrypoint: { + assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, tr, patch.EntrypointOffset()); + assembler.GetVIXLAssembler()->Bkpt(0); + if (GetCompilerOptions().GenerateAnyDebugInfo()) { + *debug_name = "EntrypointCallThunk_" + std::to_string(patch.EntrypointOffset()); + } + break; + } + case linker::LinkerPatch::Type::kBakerReadBarrierBranch: { DCHECK_EQ(patch.GetBakerCustomValue2(), 0u); CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name); break; + } default: LOG(FATAL) << "Unexpected patch type " << patch.GetType(); UNREACHABLE(); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 5edca87147..4742f78bae 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -589,6 +589,9 @@ class CodeGeneratorARMVIXL : public CodeGenerator { PcRelativePatchInfo* NewStringBssEntryPatch(const DexFile& dex_file, dex::StringIndex string_index); + // Emit the BL instruction for entrypoint thunk call and record the associated patch for AOT. + void EmitEntrypointThunkCall(ThreadOffset32 entrypoint_offset); + // Emit the BNE instruction for baker read barrier and record // the associated patch for AOT or slow path for JIT. void EmitBakerReadBarrierBne(uint32_t custom_data); @@ -869,8 +872,6 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArmVIXLAssembler assembler_; - // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. - Uint32ToLiteralMap uint32_literals_; // PC-relative method patch info for kBootImageLinkTimePcRelative/kBootImageRelRo. // Also used for type/string patches for kBootImageRelRo (same linker patch as for methods). ArenaDeque<PcRelativePatchInfo> boot_image_method_patches_; @@ -886,9 +887,13 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> string_bss_entry_patches_; // PC-relative patch info for IntrinsicObjects. ArenaDeque<PcRelativePatchInfo> boot_image_intrinsic_patches_; + // Patch info for calls to entrypoint dispatch thunks. Used for slow paths. + ArenaDeque<PatchInfo<vixl::aarch32::Label>> call_entrypoint_patches_; // Baker read barrier patch info. ArenaDeque<BakerReadBarrierPatchInfo> baker_read_barrier_patches_; + // Deduplication map for 32-bit literals, used for JIT for boot image addresses. + Uint32ToLiteralMap uint32_literals_; // Patches for string literals in JIT compiled code. StringToLiteralMap jit_string_patches_; // Patches for class literals in JIT compiled code. diff --git a/dex2oat/linker/arm/relative_patcher_arm_base.cc b/dex2oat/linker/arm/relative_patcher_arm_base.cc index 828dc5d7c3..35e799ac73 100644 --- a/dex2oat/linker/arm/relative_patcher_arm_base.cc +++ b/dex2oat/linker/arm/relative_patcher_arm_base.cc @@ -386,6 +386,12 @@ ArmBaseRelativePatcher::ThunkKey ArmBaseRelativePatcher::GetMethodCallKey() { return ThunkKey(ThunkType::kMethodCall); } +ArmBaseRelativePatcher::ThunkKey ArmBaseRelativePatcher::GetEntrypointCallKey( + const LinkerPatch& patch) { + DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kCallEntrypoint); + return ThunkKey(ThunkType::kEntrypointCall, patch.EntrypointOffset()); +} + ArmBaseRelativePatcher::ThunkKey ArmBaseRelativePatcher::GetBakerThunkKey( const LinkerPatch& patch) { DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kBakerReadBarrierBranch); @@ -399,6 +405,7 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho for (const LinkerPatch& patch : compiled_method->GetPatches()) { uint32_t patch_offset = code_offset + patch.LiteralOffset(); ThunkKey key(static_cast<ThunkType>(-1)); + bool simple_thunk_patch = false; ThunkData* old_data = nullptr; if (patch.GetType() == LinkerPatch::Type::kCallRelative) { key = GetMethodCallKey(); @@ -411,8 +418,14 @@ void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_metho } else { old_data = method_call_thunk_; } + } else if (patch.GetType() == LinkerPatch::Type::kCallEntrypoint) { + key = GetEntrypointCallKey(patch); + simple_thunk_patch = true; } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) { key = GetBakerThunkKey(patch); + simple_thunk_patch = true; + } + if (simple_thunk_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); diff --git a/dex2oat/linker/arm/relative_patcher_arm_base.h b/dex2oat/linker/arm/relative_patcher_arm_base.h index 0eb4417771..bf3e81f6b9 100644 --- a/dex2oat/linker/arm/relative_patcher_arm_base.h +++ b/dex2oat/linker/arm/relative_patcher_arm_base.h @@ -44,6 +44,7 @@ class ArmBaseRelativePatcher : public RelativePatcher { enum class ThunkType { kMethodCall, // Method call thunk. + kEntrypointCall, // Entrypoint call. kBakerReadBarrier, // Baker read barrier. }; @@ -84,6 +85,7 @@ class ArmBaseRelativePatcher : public RelativePatcher { }; static ThunkKey GetMethodCallKey(); + static ThunkKey GetEntrypointCallKey(const LinkerPatch& patch); static ThunkKey GetBakerThunkKey(const LinkerPatch& patch); uint32_t ReserveSpaceInternal(uint32_t offset, diff --git a/dex2oat/linker/arm/relative_patcher_thumb2.cc b/dex2oat/linker/arm/relative_patcher_thumb2.cc index 697fb09f73..72b93ec1f0 100644 --- a/dex2oat/linker/arm/relative_patcher_thumb2.cc +++ b/dex2oat/linker/arm/relative_patcher_thumb2.cc @@ -58,28 +58,10 @@ 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_ALIGNED(patch_offset, 2u); 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); + PatchBl(code, literal_offset, displacement); } void Thumb2RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, @@ -102,6 +84,17 @@ void Thumb2RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, SetInsn32(code, literal_offset, insn); } +void Thumb2RelativePatcher::PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) { + DCHECK_ALIGNED(patch_offset, 2u); + ThunkKey key = GetEntrypointCallKey(patch); + uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); + DCHECK_ALIGNED(target_offset, 4u); + uint32_t displacement = target_offset - patch_offset; + PatchBl(code, patch.LiteralOffset(), displacement); +} + void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, uint32_t patch_offset) { @@ -127,6 +120,7 @@ void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* co uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: + case ThunkType::kEntrypointCall: return kMaxMethodCallPositiveDisplacement; case ThunkType::kBakerReadBarrier: return kMaxBcondPositiveDisplacement; @@ -136,12 +130,35 @@ uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { uint32_t Thumb2RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: + case ThunkType::kEntrypointCall: return kMaxMethodCallNegativeDisplacement; case ThunkType::kBakerReadBarrier: return kMaxBcondNegativeDisplacement; } } +void Thumb2RelativePatcher::PatchBl(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t displacement) { + 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::SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) { DCHECK_LE(offset + 4u, code->size()); DCHECK_ALIGNED(offset, 2u); diff --git a/dex2oat/linker/arm/relative_patcher_thumb2.h b/dex2oat/linker/arm/relative_patcher_thumb2.h index dbf64a13da..d360482d2a 100644 --- a/dex2oat/linker/arm/relative_patcher_thumb2.h +++ b/dex2oat/linker/arm/relative_patcher_thumb2.h @@ -42,6 +42,9 @@ class Thumb2RelativePatcher final : public ArmBaseRelativePatcher { const LinkerPatch& patch, uint32_t patch_offset, uint32_t target_offset) override; + void PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) override; void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, uint32_t patch_offset) override; @@ -51,7 +54,9 @@ class Thumb2RelativePatcher final : public ArmBaseRelativePatcher { uint32_t MaxNegativeDisplacement(const ThunkKey& key) override; private: - void SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value); + static void PatchBl(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t displacement); + + static 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> diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc index 04a897ef48..296bf61cbc 100644 --- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc +++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc @@ -225,7 +225,8 @@ class Thumb2RelativePatcherTest : public RelativePatcherTest { // Make sure the ThunkProvider has all the necessary thunks. for (const LinkerPatch& patch : patches) { - if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + if (patch.GetType() == LinkerPatch::Type::kCallEntrypoint || + patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || patch.GetType() == LinkerPatch::Type::kCallRelative) { std::string debug_name; std::vector<uint8_t> thunk_code = CompileThunk(patch, &debug_name); @@ -662,6 +663,35 @@ TEST_F(Thumb2RelativePatcherTest, StringReference4) { ASSERT_LT(GetMethodOffset(1u), 0xfcu); } +TEST_F(Thumb2RelativePatcherTest, EntrypointCall) { + constexpr uint32_t kEntrypointOffset = 512; + const LinkerPatch patches[] = { + LinkerPatch::CallEntrypointPatch(0u, kEntrypointOffset), + }; + AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches)); + Link(); + + uint32_t method_offset = GetMethodOffset(1u); + uint32_t thunk_offset = CompiledCode::AlignCode(method_offset + kCallCode.size(), + InstructionSet::kThumb2); + uint32_t diff = thunk_offset - method_offset - kPcAdjustment; + ASSERT_TRUE(IsAligned<2u>(diff)); + ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits. + auto expected_code = GenNopsAndBl(0u, kBlPlus0 | ((diff >> 1) & 0xffu)); + EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); + + // Verify the thunk. + uint32_t ldr_pc_tr_offset = + 0xf8d00000 | // LDR Rt, [Rn, #<imm12>] + (/* tr */ 9 << 16) | // Rn = TR + (/* pc */ 15 << 12) | // Rt = PC + kEntrypointOffset; // imm12 + uint16_t bkpt = 0xbe00; + ASSERT_LE(6u, output_.size() - thunk_offset); + EXPECT_EQ(ldr_pc_tr_offset, GetOutputInsn32(thunk_offset)); + EXPECT_EQ(bkpt, GetOutputInsn16(thunk_offset + 4u)); +} + const uint32_t kBakerValidRegs[] = { 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, // r8 (rMR), IP, SP, LR and PC are reserved. diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc index ee8d4d1a7f..2260f66d1f 100644 --- a/dex2oat/linker/arm64/relative_patcher_arm64.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64.cc @@ -58,6 +58,7 @@ constexpr uint32_t kAdrpThunkSize = 8u; inline bool IsAdrpPatch(const LinkerPatch& patch) { switch (patch.GetType()) { case LinkerPatch::Type::kCallRelative: + case LinkerPatch::Type::kCallEntrypoint: case LinkerPatch::Type::kBakerReadBarrierBranch: return false; case LinkerPatch::Type::kIntrinsicReference: @@ -189,30 +190,21 @@ uint32_t Arm64RelativePatcher::WriteThunks(OutputStream* out, uint32_t 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 patch_offset, + uint32_t target_offset) { + DCHECK_ALIGNED(literal_offset, 4u); + DCHECK_ALIGNED(patch_offset, 4u); + DCHECK_ALIGNED(target_offset, 4u); 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); + PatchBl(code, literal_offset, displacement); } 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); + DCHECK_ALIGNED(patch_offset, 4u); + DCHECK_ALIGNED(target_offset, 4u); uint32_t literal_offset = patch.LiteralOffset(); uint32_t insn = GetInsn(code, literal_offset); uint32_t pc_insn_offset = patch.PcInsnOffset(); @@ -307,13 +299,21 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, } } +void Arm64RelativePatcher::PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) { + DCHECK_ALIGNED(patch_offset, 4u); + ThunkKey key = GetEntrypointCallKey(patch); + uint32_t target_offset = GetThunkTargetOffset(key, patch_offset); + uint32_t displacement = target_offset - patch_offset; + PatchBl(code, patch.LiteralOffset(), displacement); +} + 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); @@ -328,6 +328,7 @@ void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* cod uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: + case ThunkType::kEntrypointCall: return kMaxMethodCallPositiveDisplacement; case ThunkType::kBakerReadBarrier: return kMaxBcondPositiveDisplacement; @@ -337,6 +338,7 @@ uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(const ThunkKey& key) { uint32_t Arm64RelativePatcher::MaxNegativeDisplacement(const ThunkKey& key) { switch (key.GetType()) { case ThunkType::kMethodCall: + case ThunkType::kEntrypointCall: return kMaxMethodCallNegativeDisplacement; case ThunkType::kBakerReadBarrier: return kMaxBcondNegativeDisplacement; @@ -357,6 +359,20 @@ uint32_t Arm64RelativePatcher::PatchAdrp(uint32_t adrp, uint32_t disp) { ((disp & 0x80000000u) >> (31 - 23)); } +void Arm64RelativePatcher::PatchBl(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t displacement) { + DCHECK_ALIGNED(displacement, 4u); + 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); +} + bool Arm64RelativePatcher::NeedsErratum843419Thunk(ArrayRef<const uint8_t> code, uint32_t literal_offset, uint32_t patch_offset) { @@ -409,7 +425,7 @@ bool Arm64RelativePatcher::NeedsErratum843419Thunk(ArrayRef<const uint8_t> code, 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); + DCHECK_ALIGNED(offset, 4u); uint8_t* addr = &(*code)[offset]; addr[0] = (value >> 0) & 0xff; addr[1] = (value >> 8) & 0xff; @@ -419,7 +435,7 @@ void Arm64RelativePatcher::SetInsn(std::vector<uint8_t>* code, uint32_t offset, uint32_t Arm64RelativePatcher::GetInsn(ArrayRef<const uint8_t> code, uint32_t offset) { DCHECK_LE(offset + 4u, code.size()); - DCHECK_EQ(offset & 3u, 0u); + DCHECK_ALIGNED(offset, 4u); const uint8_t* addr = &code[offset]; return (static_cast<uint32_t>(addr[0]) << 0) + diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.h b/dex2oat/linker/arm64/relative_patcher_arm64.h index e95d0fee6f..9ad2c961bd 100644 --- a/dex2oat/linker/arm64/relative_patcher_arm64.h +++ b/dex2oat/linker/arm64/relative_patcher_arm64.h @@ -47,6 +47,9 @@ class Arm64RelativePatcher final : public ArmBaseRelativePatcher { const LinkerPatch& patch, uint32_t patch_offset, uint32_t target_offset) override; + void PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) override; void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, uint32_t patch_offset) override; @@ -57,10 +60,11 @@ class Arm64RelativePatcher final : public ArmBaseRelativePatcher { private: static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp); + static void PatchBl(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t displacement); 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 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> diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc index 9e54bbf9c7..8bae5d47f1 100644 --- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc +++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc @@ -198,7 +198,8 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { // Make sure the ThunkProvider has all the necessary thunks. for (const LinkerPatch& patch : patches) { - if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || + if (patch.GetType() == LinkerPatch::Type::kCallEntrypoint || + patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || patch.GetType() == LinkerPatch::Type::kCallRelative) { std::string debug_name; std::vector<uint8_t> thunk_code = CompileThunk(patch, &debug_name); @@ -1005,6 +1006,36 @@ TEST_F(Arm64RelativePatcherTestDefault, StringReferenceXSpRel) { { 0u, 8u }); } +TEST_F(Arm64RelativePatcherTestDefault, EntrypointCall) { + constexpr uint32_t kEntrypointOffset = 512; + const LinkerPatch patches[] = { + LinkerPatch::CallEntrypointPatch(0u, kEntrypointOffset), + }; + AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches)); + Link(); + + uint32_t method_offset = GetMethodOffset(1u); + uint32_t thunk_offset = CompiledCode::AlignCode(method_offset + kCallCode.size(), + InstructionSet::kArm64); + uint32_t diff = thunk_offset - method_offset; + ASSERT_TRUE(IsAligned<4u>(diff)); + ASSERT_LT(diff, 128 * MB); + auto expected_code = RawCode({kBlPlus0 | (diff >> 2)}); + EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); + + // Verify the thunk. + uint32_t ldr_ip0_tr_offset = + 0xf9400000 | // LDR Xt, [Xn, #<simm>] + ((kEntrypointOffset >> 3) << 10) | // imm12 = (simm >> scale), scale = 3 + (/* tr */ 19 << 5) | // Xn = TR + /* ip0 */ 16; // Xt = ip0 + uint32_t br_ip0 = 0xd61f0000 | (/* ip0 */ 16 << 5); + auto expected_thunk = RawCode({ ldr_ip0_tr_offset, br_ip0 }); + ASSERT_LE(8u, output_.size() - thunk_offset); + EXPECT_EQ(ldr_ip0_tr_offset, GetOutputInsn(thunk_offset)); + EXPECT_EQ(br_ip0, GetOutputInsn(thunk_offset + 4u)); +} + void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) { uint32_t valid_regs[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, diff --git a/dex2oat/linker/mips/relative_patcher_mips.cc b/dex2oat/linker/mips/relative_patcher_mips.cc index 69e0846cb7..4f4dc48ab4 100644 --- a/dex2oat/linker/mips/relative_patcher_mips.cc +++ b/dex2oat/linker/mips/relative_patcher_mips.cc @@ -86,6 +86,12 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, } } +void MipsRelativePatcher::PatchEntrypointCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + const LinkerPatch& patch ATTRIBUTE_UNUSED, + uint32_t patch_offset ATTRIBUTE_UNUSED) { + LOG(FATAL) << "UNIMPLEMENTED"; +} + void MipsRelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, const LinkerPatch& patch ATTRIBUTE_UNUSED, uint32_t patch_offset ATTRIBUTE_UNUSED) { diff --git a/dex2oat/linker/mips/relative_patcher_mips.h b/dex2oat/linker/mips/relative_patcher_mips.h index 4c385a3fec..7cdac4511f 100644 --- a/dex2oat/linker/mips/relative_patcher_mips.h +++ b/dex2oat/linker/mips/relative_patcher_mips.h @@ -41,6 +41,9 @@ class MipsRelativePatcher final : public RelativePatcher { const LinkerPatch& patch, uint32_t patch_offset, uint32_t target_offset) override; + void PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) override; void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, uint32_t patch_offset) override; diff --git a/dex2oat/linker/mips64/relative_patcher_mips64.cc b/dex2oat/linker/mips64/relative_patcher_mips64.cc index aae5746278..2992487062 100644 --- a/dex2oat/linker/mips64/relative_patcher_mips64.cc +++ b/dex2oat/linker/mips64/relative_patcher_mips64.cc @@ -84,6 +84,12 @@ void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, } } +void Mips64RelativePatcher::PatchEntrypointCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + const LinkerPatch& patch ATTRIBUTE_UNUSED, + uint32_t patch_offset ATTRIBUTE_UNUSED) { + LOG(FATAL) << "UNIMPLEMENTED"; +} + void Mips64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, const LinkerPatch& patch ATTRIBUTE_UNUSED, uint32_t patch_offset ATTRIBUTE_UNUSED) { diff --git a/dex2oat/linker/mips64/relative_patcher_mips64.h b/dex2oat/linker/mips64/relative_patcher_mips64.h index 7b7c2ccd9f..9d27b87546 100644 --- a/dex2oat/linker/mips64/relative_patcher_mips64.h +++ b/dex2oat/linker/mips64/relative_patcher_mips64.h @@ -39,6 +39,9 @@ class Mips64RelativePatcher final : public RelativePatcher { const LinkerPatch& patch, uint32_t patch_offset, uint32_t target_offset) override; + void PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) override; void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, uint32_t patch_offset) override; diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index 9b47a0d5b0..2daada44bc 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.h +++ b/dex2oat/linker/multi_oat_relative_patcher.h @@ -114,6 +114,13 @@ class MultiOatRelativePatcher final { relative_patcher_->PatchPcRelativeReference(code, patch, patch_offset, target_offset); } + void PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) { + patch_offset += adjustment_; + relative_patcher_->PatchEntrypointCall(code, patch, patch_offset); + } + void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, uint32_t patch_offset) { diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc index 274084f030..2a05816206 100644 --- a/dex2oat/linker/multi_oat_relative_patcher_test.cc +++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc @@ -94,6 +94,12 @@ class MultiOatRelativePatcherTest : public testing::Test { last_target_offset_ = target_offset; } + void PatchEntrypointCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + const LinkerPatch& patch ATTRIBUTE_UNUSED, + uint32_t patch_offset ATTRIBUTE_UNUSED) override { + LOG(FATAL) << "UNIMPLEMENTED"; + } + void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, const LinkerPatch& patch ATTRIBUTE_UNUSED, uint32_t patch_offset ATTRIBUTE_UNUSED) override { diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 3c496341f9..7368ae42d0 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1812,6 +1812,12 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { target_offset); break; } + case LinkerPatch::Type::kCallEntrypoint: { + writer_->relative_patcher_->PatchEntrypointCall(&patched_code_, + patch, + offset_ + literal_offset); + break; + } case LinkerPatch::Type::kBakerReadBarrierBranch: { writer_->relative_patcher_->PatchBakerReadBarrierBranch(&patched_code_, patch, diff --git a/dex2oat/linker/relative_patcher.cc b/dex2oat/linker/relative_patcher.cc index 4db0e8a94e..f746cfbd45 100644 --- a/dex2oat/linker/relative_patcher.cc +++ b/dex2oat/linker/relative_patcher.cc @@ -77,6 +77,12 @@ std::unique_ptr<RelativePatcher> RelativePatcher::Create( LOG(FATAL) << "Unexpected relative dex cache array patch."; } + void PatchEntrypointCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + const LinkerPatch& patch ATTRIBUTE_UNUSED, + uint32_t patch_offset ATTRIBUTE_UNUSED) override { + LOG(FATAL) << "Unexpected entrypoint call patch."; + } + void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, const LinkerPatch& patch ATTRIBUTE_UNUSED, uint32_t patch_offset ATTRIBUTE_UNUSED) override { diff --git a/dex2oat/linker/relative_patcher.h b/dex2oat/linker/relative_patcher.h index e8e15c9ea8..c05445c196 100644 --- a/dex2oat/linker/relative_patcher.h +++ b/dex2oat/linker/relative_patcher.h @@ -137,6 +137,11 @@ class RelativePatcher { uint32_t patch_offset, uint32_t target_offset) = 0; + // Patch a call to an entrypoint trampoline. + virtual void PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) = 0; + // Patch a branch to a Baker read barrier thunk. virtual void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h index dead38d3df..dc53ac4321 100644 --- a/dex2oat/linker/relative_patcher_test.h +++ b/dex2oat/linker/relative_patcher_test.h @@ -174,8 +174,10 @@ class RelativePatcherTest : public testing::Test { 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); + 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_); @@ -190,6 +192,10 @@ class RelativePatcherTest : public testing::Test { patch, offset + patch.LiteralOffset(), target_offset); + } else if (patch.GetType() == LinkerPatch::Type::kCallEntrypoint) { + patcher_->PatchEntrypointCall(&patched_code_, + patch, + offset + patch.LiteralOffset()); } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) { patcher_->PatchBakerReadBarrierBranch(&patched_code_, patch, @@ -300,11 +306,10 @@ class RelativePatcherTest : public testing::Test { 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 || + custom_value1_(CustomValue1(patch)), + custom_value2_(CustomValue2(patch)) { + CHECK(patch.GetType() == LinkerPatch::Type::kCallEntrypoint || + patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch || patch.GetType() == LinkerPatch::Type::kCallRelative); } @@ -319,6 +324,26 @@ class RelativePatcherTest : public testing::Test { } private: + static uint32_t CustomValue1(const LinkerPatch& patch) { + switch (patch.GetType()) { + case LinkerPatch::Type::kCallEntrypoint: + return patch.EntrypointOffset(); + case LinkerPatch::Type::kBakerReadBarrierBranch: + return patch.GetBakerCustomValue1(); + default: + return 0; + } + } + + static uint32_t CustomValue2(const LinkerPatch& patch) { + switch (patch.GetType()) { + case LinkerPatch::Type::kBakerReadBarrierBranch: + return patch.GetBakerCustomValue2(); + default: + return 0; + } + } + const LinkerPatch::Type type_; const uint32_t custom_value1_; const uint32_t custom_value2_; diff --git a/dex2oat/linker/x86/relative_patcher_x86.cc b/dex2oat/linker/x86/relative_patcher_x86.cc index cdd2cef13a..332350692e 100644 --- a/dex2oat/linker/x86/relative_patcher_x86.cc +++ b/dex2oat/linker/x86/relative_patcher_x86.cc @@ -57,6 +57,12 @@ void X86RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, (*code)[literal_offset + 3u] = static_cast<uint8_t>(diff >> 24); } +void X86RelativePatcher::PatchEntrypointCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + const LinkerPatch& patch ATTRIBUTE_UNUSED, + uint32_t patch_offset ATTRIBUTE_UNUSED) { + LOG(FATAL) << "UNIMPLEMENTED"; +} + void X86RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, const LinkerPatch& patch ATTRIBUTE_UNUSED, uint32_t patch_offset ATTRIBUTE_UNUSED) { diff --git a/dex2oat/linker/x86/relative_patcher_x86.h b/dex2oat/linker/x86/relative_patcher_x86.h index 3da62fb23b..589a4989f4 100644 --- a/dex2oat/linker/x86/relative_patcher_x86.h +++ b/dex2oat/linker/x86/relative_patcher_x86.h @@ -30,6 +30,9 @@ class X86RelativePatcher final : public X86BaseRelativePatcher { const LinkerPatch& patch, uint32_t patch_offset, uint32_t target_offset) override; + void PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) override; void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, uint32_t patch_offset) override; diff --git a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc index c80f6a92f2..0b9d07e9cf 100644 --- a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc +++ b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc @@ -35,6 +35,12 @@ void X86_64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, reinterpret_cast<unaligned_int32_t*>(&(*code)[patch.LiteralOffset()])[0] = displacement; } +void X86_64RelativePatcher::PatchEntrypointCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + const LinkerPatch& patch ATTRIBUTE_UNUSED, + uint32_t patch_offset ATTRIBUTE_UNUSED) { + LOG(FATAL) << "UNIMPLEMENTED"; +} + void X86_64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, const LinkerPatch& patch ATTRIBUTE_UNUSED, uint32_t patch_offset ATTRIBUTE_UNUSED) { diff --git a/dex2oat/linker/x86_64/relative_patcher_x86_64.h b/dex2oat/linker/x86_64/relative_patcher_x86_64.h index a82fef3b56..7b99bd881f 100644 --- a/dex2oat/linker/x86_64/relative_patcher_x86_64.h +++ b/dex2oat/linker/x86_64/relative_patcher_x86_64.h @@ -30,6 +30,9 @@ class X86_64RelativePatcher final : public X86BaseRelativePatcher { const LinkerPatch& patch, uint32_t patch_offset, uint32_t target_offset) override; + void PatchEntrypointCall(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset) override; void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, const LinkerPatch& patch, uint32_t patch_offset) override; diff --git a/test/521-checker-array-set-null/src/Main.java b/test/521-checker-array-set-null/src/Main.java index 74bb73f04b..f166b92fa4 100644 --- a/test/521-checker-array-set-null/src/Main.java +++ b/test/521-checker-array-set-null/src/Main.java @@ -22,19 +22,19 @@ public class Main { } /// CHECK-START: void Main.testWithNull(java.lang.Object[]) disassembly (after) - /// CHECK-NOT: pAputObject + /// CHECK: ArraySet needs_type_check:false public static void testWithNull(Object[] o) { o[0] = null; } /// CHECK-START: void Main.testWithUnknown(java.lang.Object[], java.lang.Object) disassembly (after) - /// CHECK: pAputObject + /// CHECK: ArraySet needs_type_check:true public static void testWithUnknown(Object[] o, Object obj) { o[0] = obj; } /// CHECK-START: void Main.testWithSame(java.lang.Object[]) disassembly (after) - /// CHECK-NOT: pAputObject + /// CHECK: ArraySet needs_type_check:false public static void testWithSame(Object[] o) { o[0] = o[1]; } |