Skip to content

Commit

Permalink
[ELF] Pre-create ThunkSections at Target specific intervals
Browse files Browse the repository at this point in the history
When an OutputSection is larger than the branch range for a Target we
need to place thunks such that they are always in range of their caller,
and sufficiently spaced to maximise the number of callers that can use
the thunk. We use the simple heuristic of placing the
ThunkSection at intervals corresponding to a target specific branch range.
If the OutputSection is small we put the thunks at the end of the executable
sections.

Differential Revision: https://reviews.llvm.org/D34689

llvm-svn: 316751
  • Loading branch information
smithp35 committed Oct 27, 2017
1 parent 4a8e115 commit f0c70f8
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 19 deletions.
31 changes: 31 additions & 0 deletions lld/ELF/Arch/ARM.cpp
Expand Up @@ -63,6 +63,37 @@ ARM::ARM() {
// ARM uses Variant 1 TLS
TcbSize = 8;
NeedsThunks = true;

// The placing of pre-created ThunkSections is controlled by the
// ThunkSectionSpacing parameter. The aim is to place the
// ThunkSection such that all branches from the InputSections prior to the
// ThunkSection can reach a Thunk placed at the end of the ThunkSection.
// Graphically:
// | up to ThunkSectionSpacing .text input sections |
// | ThunkSection |
// | up to ThunkSectionSpacing .text input sections |
// | ThunkSection |

// Pre-created ThunkSections are spaced roughly 16MiB apart on ARM. This is to
// match the most common expected case of a Thumb 2 encoded BL, BLX or B.W
// ARM B, BL, BLX range +/- 32MiB
// Thumb B.W, BL, BLX range +/- 16MiB
// Thumb B<cc>.W range +/- 1MiB
// If a branch cannot reach a pre-created ThunkSection a new one will be
// created so we can handle the rare cases of a Thumb 2 conditional branch.
// We intentionally use a lower size for ThunkSectionSpacing than the maximum
// branch range so the end of the ThunkSection is more likely to be within
// range of the branch instruction that is furthest away. The value we shorten
// ThunkSectionSpacing by is set conservatively to allow us to create 16,384
// 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
// one of the Thunks going out of range.

// FIXME: lld assumes that the Thumb BL and BLX encoding permits the J1 and
// J2 bits to be used to extend the branch range. On earlier Architectures
// such as ARMv4, ARMv5 and ARMv6 (except ARMv6T2) the range is +/- 4MiB. If
// support for the earlier encodings is added then when they are used the
// ThunkSectionSpacing will need lowering.
ThunkSectionSpacing = 0x1000000 - 0x30000;
}

uint32_t ARM::calcEFlags() const {
Expand Down
64 changes: 49 additions & 15 deletions lld/ELF/Relocations.cpp
Expand Up @@ -1055,7 +1055,11 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
if (ISD->ThunkSections.empty())
return;

// Order Thunks in ascending OutSecOff
// Remove any zero sized precreated Thunks.
llvm::erase_if(ISD->ThunkSections, [](const ThunkSection *TS) {
return TS->getSize() == 0;
});
// Order Thunks in ascending OutSecOff.
std::stable_sort(ISD->ThunkSections.begin(), ISD->ThunkSections.end(),
[](const ThunkSection *A, const ThunkSection *B) {
return A->OutSecOff < B->OutSecOff;
Expand Down Expand Up @@ -1084,22 +1088,17 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
});
}

static uint32_t findEndOfFirstNonExec(OutputSection &Cmd) {
for (BaseCommand *Base : Cmd.SectionCommands)
if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
for (auto *IS : ISD->Sections)
if ((IS->Flags & SHF_EXECINSTR) == 0)
return IS->OutSecOff + IS->getSize();
return 0;
}

ThunkSection *ThunkCreator::getOSThunkSec(OutputSection *OS,
InputSectionDescription *ISD) {
ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *OS,
InputSectionDescription *ISD) {
// FIXME: When range extension thunks are supported we will need to check
// that the ThunkSection is in range of the caller.
if (!ISD->ThunkSections.empty())
return ISD->ThunkSections.front();

uint32_t Off = findEndOfFirstNonExec(*OS);
return addThunkSection(OS, ISD, Off);
// FIXME: When range extension thunks are supported we must handle the case
// where no pre-created ThunkSections are in range by creating a new one in
// range; for now, it is unreachable.
llvm_unreachable("Must have created at least one ThunkSection per ISR");
}

// Add a Thunk that needs to be placed in a ThunkSection that immediately
Expand All @@ -1126,6 +1125,38 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
return TS;
}

// Create one or more ThunkSections per OS that can be used to place Thunks.
// We attempt to place the ThunkSections using the following desirable
// properties:
// - Within range of the maximum number of callers
// - Minimise the number of ThunkSections
//
// We follow a simple but conservative heuristic to place ThunkSections at
// offsets that are multiples of a Target specific branch range.
// For an InputSectionRange that is smaller than the range, a single
// ThunkSection at the end of the range will do.
void ThunkCreator::createInitialThunkSections(
ArrayRef<OutputSection *> OutputSections) {
forEachInputSectionDescription(
OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
if (ISD->Sections.empty())
return;
uint32_t ISLimit;
uint32_t PrevISLimit = ISD->Sections.front()->OutSecOff;
uint32_t ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;

for (const InputSection *IS : ISD->Sections) {
ISLimit = IS->OutSecOff + IS->getSize();
if (ISLimit > ThunkUpperBound) {
addThunkSection(OS, ISD, PrevISLimit);
ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
}
PrevISLimit = ISLimit;
}
addThunkSection(OS, ISD, ISLimit);
});
}

ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS,
InputSectionDescription *ISD,
uint64_t Off) {
Expand Down Expand Up @@ -1175,6 +1206,9 @@ void ThunkCreator::forEachInputSectionDescription(
// extension Thunks are not yet supported.
bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
bool AddressesChanged = false;
if (Pass == 0 && Target->ThunkSectionSpacing)
createInitialThunkSections(OutputSections);

// Create all the Thunks and insert them into synthetic ThunkSections. The
// ThunkSections are later inserted back into InputSectionDescriptions.
// We separate the creation of ThunkSections from the insertion of the
Expand All @@ -1198,7 +1232,7 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
if (auto *TIS = T->getTargetInputSection())
TS = getISThunkSec(TIS);
else
TS = getOSThunkSec(OS, ISD);
TS = getISDThunkSec(OS, ISD);
TS->addThunk(T);
Thunks[T->ThunkSym] = T;
}
Expand Down
6 changes: 5 additions & 1 deletion lld/ELF/Relocations.h
Expand Up @@ -139,16 +139,20 @@ class ThunkCreator {

private:
void mergeThunks(ArrayRef<OutputSection *> OutputSections);
ThunkSection *getOSThunkSec(OutputSection *OS, InputSectionDescription *ISD);
ThunkSection *getISDThunkSec(OutputSection *OS, InputSectionDescription *ISD);
ThunkSection *getISThunkSec(InputSection *IS);

void createInitialThunkSections(ArrayRef<OutputSection *> OutputSections);

void forEachInputSectionDescription(
ArrayRef<OutputSection *> OutputSections,
std::function<void(OutputSection *, InputSectionDescription *)> Fn);

std::pair<Thunk *, bool> getThunk(SymbolBody &Body, RelType Type);

ThunkSection *addThunkSection(OutputSection *OS, InputSectionDescription *,
uint64_t Off);

// Record all the available Thunks for a Symbol
llvm::DenseMap<SymbolBody *, std::vector<Thunk *>> ThunkedSymbols;

Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/SyntheticSections.cpp
Expand Up @@ -2391,6 +2391,8 @@ void ThunkSection::writeTo(uint8_t *Buf) {
}

InputSection *ThunkSection::getTargetInputSection() const {
if (Thunks.empty())
return nullptr;
const Thunk *T = Thunks.front();
return T->getTargetInputSection();
}
Expand Down
4 changes: 4 additions & 0 deletions lld/ELF/Target.h
Expand Up @@ -74,6 +74,10 @@ class TargetInfo {
// end of .got
uint64_t GotBaseSymOff = 0;

// On systems with range extensions we place collections of Thunks at
// regular spacings that enable the majority of branches reach the Thunks.
uint32_t ThunkSectionSpacing = 0;

RelType CopyRel;
RelType GotRel;
RelType PltRel;
Expand Down
6 changes: 3 additions & 3 deletions lld/test/ELF/arm-thumb-thunk-symbols.s
Expand Up @@ -25,18 +25,18 @@ arm_fn:
b thumb_fn

// CHECK: Name: __Thumbv7ABSLongThunk_arm_fn
// CHECK-NEXT: Value: 0x11005
// CHECK-NEXT: Value: 0x12005
// CHECK-NEXT: Size: 10
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: Function (0x2)
// CHECK: Name: __ARMv7ABSLongThunk_thumb_fn
// CHECK-NEXT: Value: 0x11010
// CHECK-NEXT: Value: 0x12010
// CHECK-NEXT: Size: 12
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: Function (0x2)

// CHECK-PI: Name: __ThumbV7PILongThunk_arm_fn
// CHECK-PI-NEXT: Value: 0x1005
// CHECK-PI-NEXT: Value: 0x2005
// CHECK-PI-NEXT: Size: 12
// CHECK-PI-NEXT: Binding: Local (0x0)
// CHECK-PI-NEXT: Type: Function (0x2)

0 comments on commit f0c70f8

Please sign in to comment.