-
Notifications
You must be signed in to change notification settings - Fork 14.3k
ELF: Add branch-to-branch optimization. #138366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
//===- TargetImpl.h -------------------------------------------------------===// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: switch to the new style as well |
||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLD_ELF_ARCH_TARGETIMPL_H | ||
#define LLD_ELF_ARCH_TARGETIMPL_H | ||
|
||
#include "InputFiles.h" | ||
#include "InputSection.h" | ||
#include "Relocations.h" | ||
#include "Symbols.h" | ||
#include "llvm/BinaryFormat/ELF.h" | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. namespace lld::elf There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
namespace lld::elf { | ||
|
||
// getControlTransferAddend: If this relocation is used for control transfer | ||
// instructions (e.g. branch, branch-link or call) or code references (e.g. | ||
// virtual function pointers) and indicates an address-insignificant reference, | ||
// return the effective addend for the relocation, otherwise return | ||
// std::nullopt. The effective addend for a relocation is the addend that is | ||
// used to determine its branch destination. | ||
// | ||
// getBranchInfoAtTarget: If a control transfer relocation referring to | ||
// is+offset directly transfers control to a relocated branch instruction in the | ||
// specified section, return the relocation for the branch target as well as its | ||
// effective addend (see above). Otherwise return {nullptr, 0}. | ||
// | ||
// redirectControlTransferRelocations: Given r1, a relocation for which | ||
// getControlTransferAddend() returned a value, and r2, a relocation returned by | ||
// getBranchInfo(), modify r1 so that it branches directly to the target of r2. | ||
template <typename GetControlTransferAddend, typename GetBranchInfoAtTarget, | ||
typename RedirectControlTransferRelocations> | ||
inline void applyBranchToBranchOptImpl( | ||
Ctx &ctx, GetControlTransferAddend getControlTransferAddend, | ||
GetBranchInfoAtTarget getBranchInfoAtTarget, | ||
RedirectControlTransferRelocations redirectControlTransferRelocations) { | ||
// Needs to run serially because it writes to the relocations array as well as | ||
// reading relocations of other sections. | ||
for (ELFFileBase *f : ctx.objectFiles) { | ||
auto getRelocBranchInfo = | ||
[&getBranchInfoAtTarget]( | ||
Relocation &r, | ||
uint64_t addend) -> std::pair<Relocation *, uint64_t> { | ||
auto *target = dyn_cast_or_null<Defined>(r.sym); | ||
// We don't allow preemptible symbols or ifuncs (may go somewhere else), | ||
// absolute symbols (runtime behavior unknown), non-executable or writable | ||
// memory (ditto) or non-regular sections (no section data). | ||
if (!target || target->isPreemptible || target->isGnuIFunc() || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While uncommon, it is possible to have SHT_REL relocs which may have a non zero addend. I know of at least one tool that can generate them. I don't think these need to be supported, but could be worth skipping any that are encountered. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't SHT_REL just work already because we read the implicit addend when producing the Relocation object? I wanted to add a test case for this but it looks like llvm-mc doesn't have an option to write SHT_REL and instead SHT_REL is tested with yaml2obj hacks, e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, just checked and it does copy the relocation addend. I agree that this wouldn't need a test case. As an aside when checking where the addends were read in I ran into this bit of copyRelocations https://github.com/llvm/llvm-project/blob/main/lld/ELF/InputSection.cpp#L433
I think I mentioned in a previous comment that bolt uses emit-relocations so it may be worth following suite here when the transformation is applied. I suspect that if bolt trusts the original relocation then in worst case the transformation is undone though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that there should be a --emit-relocs test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added --emit-relocs test, also switched to the other code path in InputSection.cpp |
||
!target->section || | ||
!(target->section->flags & llvm::ELF::SHF_EXECINSTR) || | ||
(target->section->flags & llvm::ELF::SHF_WRITE) || | ||
target->section->kind() != SectionBase::Regular) | ||
return {nullptr, 0}; | ||
return getBranchInfoAtTarget(*cast<InputSection>(target->section), | ||
target->value + addend); | ||
}; | ||
for (InputSectionBase *s : f->getSections()) { | ||
if (!s) | ||
continue; | ||
for (Relocation &r : s->relocations) { | ||
std::optional<uint64_t> addend = | ||
getControlTransferAddend(*cast<InputSection>(s), r); | ||
if (!addend) | ||
continue; | ||
std::pair<Relocation *, uint64_t> targetAndAddend = | ||
getRelocBranchInfo(r, *addend); | ||
if (!targetAndAddend.first) | ||
continue; | ||
// Avoid getting stuck in an infinite loop if we encounter a branch | ||
// that (possibly indirectly) branches to itself. It is unlikely | ||
// that more than 5 iterations will ever be needed in practice. | ||
size_t iterations = 5; | ||
while (iterations--) { | ||
std::pair<Relocation *, uint64_t> nextTargetAndAddend = | ||
getRelocBranchInfo(*targetAndAddend.first, | ||
targetAndAddend.second); | ||
if (!nextTargetAndAddend.first) | ||
break; | ||
targetAndAddend = nextTargetAndAddend; | ||
} | ||
redirectControlTransferRelocations(r, *targetAndAddend.first); | ||
} | ||
} | ||
} | ||
} | ||
|
||
} // namespace lld::elf | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -430,8 +430,10 @@ InputSectionBase *InputSection::getRelocatedSection() const { | |
|
||
template <class ELFT, class RelTy> | ||
void InputSection::copyRelocations(Ctx &ctx, uint8_t *buf) { | ||
if (ctx.arg.relax && !ctx.arg.relocatable && | ||
(ctx.arg.emachine == EM_RISCV || ctx.arg.emachine == EM_LOONGARCH)) { | ||
if (!ctx.arg.relocatable && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The condition is now complex... Perhaps define a variable for linker relaxation targets (RISCV,LoongArch)?
|
||
((ctx.arg.relax && | ||
(ctx.arg.emachine == EM_RISCV || ctx.arg.emachine == EM_LOONGARCH)) || | ||
ctx.arg.branchToBranch)) { | ||
// On LoongArch and RISC-V, relaxation might change relocations: copy | ||
// from internal ones that are updated by relaxation. | ||
InputSectionBase *sec = getRelocatedSection(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,6 +93,10 @@ Bind default visibility defined STB_GLOBAL function symbols locally for | |
.Fl shared. | ||
.It Fl -be8 | ||
Write a Big Endian ELF File using BE8 format(AArch32 only) | ||
.It Fl -branch-to-branch | ||
Enable the branch-to-branch optimizations: a branch whose target is | ||
another branch instruction is rewritten to point to the latter branch | ||
target (AArch64 and X86_64 only). Enabled by default at -O2. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. -O2 should use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
.It Fl -build-id Ns = Ns Ar value | ||
Generate a build ID note. | ||
.Ar value | ||
|
@@ -414,7 +418,7 @@ If not specified, | |
.Dv a.out | ||
is used as a default. | ||
.It Fl O Ns Ar value | ||
Optimize output file size. | ||
Optimize output file. | ||
.Ar value | ||
may be: | ||
.Pp | ||
|
@@ -424,7 +428,7 @@ Disable string merging. | |
.It Cm 1 | ||
Enable string merging. | ||
.It Cm 2 | ||
Enable string tail merging. | ||
Enable string tail merging and branch-to-branch optimization. | ||
.El | ||
.Pp | ||
.Fl O Ns Cm 1 | ||
|
Uh oh!
There was an error while loading. Please reload this page.