Skip to content

Commit e279b3d

Browse files
committed
[LLD] Parallelizing section assignment
Assigning input sections to output sections is currently single-threaded, which is not a big problem for most of the cases, as section assignment doesn't take much time. But, in some cases where there are large linker script with many output sections that none of input sections are mapped to, the section assignment can take quite a while. This patch tries to parallelize this process a bit, simultaneously trying to match multiple input sections with corresponding output sections.
1 parent b5edd2b commit e279b3d

File tree

5 files changed

+295
-21
lines changed

5 files changed

+295
-21
lines changed

lld/ELF/InputFiles.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,12 +342,29 @@ StringRef InputFile::getNameForScript() const {
342342
if (archiveName.empty())
343343
return getName();
344344

345-
if (nameForScriptCache.empty())
346-
nameForScriptCache = (archiveName + Twine(':') + getName()).str();
345+
this->initializeCachedArchiveName();
347346

348347
return nameForScriptCache;
349348
}
350349

350+
StringRef InputFile::getNameForScriptAlreadyCached() const {
351+
if (!nameForScriptCache.empty())
352+
return nameForScriptCache;
353+
354+
assert(archiveName.empty() &&
355+
"Archive object file names should be cached before this!");
356+
357+
return getName();
358+
}
359+
360+
void InputFile::initializeCachedArchiveName() const {
361+
assert(!archiveName.empty() && "File should be from an archive file if "
362+
"initializing the cached archive name");
363+
364+
if (nameForScriptCache.empty())
365+
nameForScriptCache = (archiveName + Twine(':') + getName()).str();
366+
}
367+
351368
// An ELF object file may contain a `.deplibs` section. If it exists, the
352369
// section contains a list of library specifiers such as `m` for libm. This
353370
// function resolves a given name by finding the first matching library checking

lld/ELF/InputFiles.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ class InputFile {
9292
// Get filename to use for linker script processing.
9393
StringRef getNameForScript() const;
9494

95+
StringRef getNameForScriptAlreadyCached() const;
96+
97+
// Initialize cached archive name for later usage
98+
void initializeCachedArchiveName() const;
99+
95100
// Check if a non-common symbol should be extracted to override a common
96101
// definition.
97102
bool shouldExtractForCommon(StringRef name);

lld/ELF/LinkerScript.cpp

Lines changed: 257 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
#include "Writer.h"
2323
#include "lld/Common/CommonLinkerContext.h"
2424
#include "lld/Common/Strings.h"
25+
#include "llvm/ADT/ArrayRef.h"
26+
#include "llvm/ADT/DenseMap.h"
2527
#include "llvm/ADT/STLExtras.h"
28+
#include "llvm/ADT/SmallVector.h"
2629
#include "llvm/ADT/StringRef.h"
2730
#include "llvm/BinaryFormat/ELF.h"
2831
#include "llvm/Support/Casting.h"
@@ -390,6 +393,10 @@ static inline StringRef getFilename(const InputFile *file) {
390393
return file ? file->getNameForScript() : StringRef();
391394
}
392395

396+
static inline StringRef getFilenameAlreadyCached(const InputFile *file) {
397+
return file ? file->getNameForScriptAlreadyCached() : StringRef();
398+
}
399+
393400
bool InputSectionDescription::matchesFile(const InputFile *file) const {
394401
if (filePat.isTrivialMatchAll())
395402
return true;
@@ -400,6 +407,14 @@ bool InputSectionDescription::matchesFile(const InputFile *file) const {
400407
return matchesFileCache->second;
401408
}
402409

410+
bool InputSectionDescription::matchesFileCacheless(
411+
const InputFile *file) const {
412+
if (filePat.isTrivialMatchAll())
413+
return true;
414+
415+
return filePat.match(getFilenameAlreadyCached(file));
416+
}
417+
403418
bool SectionPattern::excludesFile(const InputFile *file) const {
404419
if (excludedFilePat.empty())
405420
return false;
@@ -410,6 +425,13 @@ bool SectionPattern::excludesFile(const InputFile *file) const {
410425
return excludesFileCache->second;
411426
}
412427

428+
bool SectionPattern::excludesFileCacheless(const InputFile *file) const {
429+
if (excludedFilePat.empty())
430+
return false;
431+
432+
return excludedFilePat.match(getFilenameAlreadyCached(file));
433+
}
434+
413435
bool LinkerScript::shouldKeep(InputSectionBase *s) {
414436
for (InputSectionDescription *id : keptSections)
415437
if (id->matchesFile(s->file))
@@ -600,11 +622,223 @@ LinkerScript::createInputSectionList(OutputSection &outCmd) {
600622
return ret;
601623
}
602624

625+
// Helper struct to relate input sections with descriptions and patterns
626+
struct IsecBaseIsecDescPattern {
627+
size_t isecIndex = 0;
628+
// Because of constraints that can be enforced on sections
629+
// (ONLY_IF_R[OW]) we need to try to match to more than one
630+
// output section, in case the constraints are not fully satisfied.
631+
SmallVector<InputSectionDescription *, 1> isecDescs;
632+
SmallVector<size_t, 1> patternIndexes;
633+
634+
IsecBaseIsecDescPattern(size_t index) : isecIndex(index) {}
635+
};
636+
637+
// Helper function
638+
static inline void assignPatternsToInputSections(
639+
IsecBaseIsecDescPattern &isecBaseIsecDescPattern,
640+
ArrayRef<OutputSection *> osecs,
641+
DenseMap<OutputSection *, SmallVector<InputSectionDescription *, 0>>
642+
&outSecToISecDescs) {
643+
InputSectionBase *isec = ctx.inputSections[isecBaseIsecDescPattern.isecIndex];
644+
645+
if (!isec->isLive() || isec->parent)
646+
return;
647+
648+
// For --emit-relocs we have to ignore entries like
649+
// .rela.dyn : { *(.rela.data) }
650+
// which are common because they are in the default bfd script.
651+
// We do not ignore SHT_REL[A] linker-synthesized sections here because
652+
// want to support scripts that do custom layout for them.
653+
if (isa<InputSection>(isec) &&
654+
cast<InputSection>(isec)->getRelocatedSection())
655+
return;
656+
657+
for (OutputSection *osec : osecs) {
658+
bool assignedToConstrainedSec = false;
659+
if (osec->constraintUnsatisfied &&
660+
osec->constraint == ConstraintKind::ReadOnly)
661+
continue;
662+
for (InputSectionDescription *isd : outSecToISecDescs[osec]) {
663+
if (!isd->matchesFileCacheless(isec->file) ||
664+
(isec->flags & isd->withFlags) != isd->withFlags ||
665+
(isec->flags & isd->withoutFlags) != 0)
666+
continue;
667+
for (auto [i, pat] : llvm::enumerate(isd->sectionPatterns)) {
668+
if (!pat.sectionPat.match(isec->name) ||
669+
pat.excludesFileCacheless(isec->file))
670+
continue;
671+
672+
isecBaseIsecDescPattern.isecDescs.push_back(isd);
673+
isecBaseIsecDescPattern.patternIndexes.push_back(i);
674+
if (osec->constraint != ConstraintKind::NoConstraint) {
675+
// This is for ONLY_IF_RO and ONLY_IF_RW. An output section directive
676+
// ".foo : ONLY_IF_R[OW] { ... }" is handled only if all member input
677+
// sections satisfy a given constraint. If not, a directive is handled
678+
// as if it wasn't present from the beginning.
679+
bool isRW = isec->flags & SHF_WRITE;
680+
if (isRW && osec->constraint == ConstraintKind::ReadOnly)
681+
osec->constraintUnsatisfied = true;
682+
else if (isRW && osec->constraint == ConstraintKind::ReadWrite)
683+
osec->constraintUnsatisfied = false;
684+
685+
assignedToConstrainedSec = true;
686+
break;
687+
}
688+
return;
689+
}
690+
691+
if (assignedToConstrainedSec)
692+
break;
693+
}
694+
}
695+
}
696+
697+
// Helper function
698+
static inline DenseMap<OutputSection *, SmallVector<InputSectionBase *, 0>>
699+
assignAndSortInputToOutputSections(
700+
ArrayRef<OutputSection *> osecs,
701+
DenseMap<OutputSection *, SmallVector<InputSectionDescription *, 0>>
702+
&outSecToISecDescs) {
703+
704+
DenseMap<OutputSection *, SmallVector<InputSectionBase *, 0>> outSecToISecs;
705+
SmallVector<size_t, 0> sectionIndexes;
706+
707+
auto sortByPositionThenCommandLine =
708+
[&](size_t begin, size_t end, MutableArrayRef<InputSectionBase *> secs) {
709+
llvm::sort(
710+
MutableArrayRef<size_t>(sectionIndexes).slice(begin, end - begin));
711+
for (size_t i = begin; i != end; ++i)
712+
secs[i] = ctx.inputSections[sectionIndexes[i]];
713+
sortInputSections(secs.slice(begin, end - begin), config->sortSection,
714+
SortSectionPolicy::None);
715+
};
716+
717+
for (OutputSection *osec : osecs) {
718+
outSecToISecs[osec] = SmallVector<InputSectionBase *, 0>();
719+
// If constraints are unsatisfied ignore this output section
720+
if (osec->constraintUnsatisfied)
721+
continue;
722+
for (InputSectionDescription *isd : outSecToISecDescs[osec]) {
723+
size_t sizeAfterPrevSort = 0;
724+
sectionIndexes.clear();
725+
for (SectionPattern &pat : isd->sectionPatterns) {
726+
if (pat.matchedSections.size() == 0)
727+
continue;
728+
729+
size_t sizeBeforeCurrPat = isd->sectionBases.size();
730+
731+
for (size_t index : pat.matchedSections) {
732+
// There might be input sections repeated more than one time in
733+
// several output sections, because of constraints, here we check
734+
// if they are already mapped.
735+
if (!ctx.inputSections[index]->parent) {
736+
isd->sectionBases.push_back(ctx.inputSections[index]);
737+
ctx.inputSections[index]->parent = osec;
738+
sectionIndexes.push_back(index);
739+
}
740+
}
741+
if (pat.sortOuter != SortSectionPolicy::Default) {
742+
// Matched sections are ordered by radix sort with the keys being
743+
// (SORT*,
744+
// --sort-section, input order), where SORT* (if present) is most
745+
// significant.
746+
//
747+
// Matched sections between the previous SORT* and this SORT* are
748+
// sorted by
749+
// (--sort-alignment, input order).
750+
751+
sortByPositionThenCommandLine(
752+
sizeAfterPrevSort, sizeBeforeCurrPat,
753+
MutableArrayRef<InputSectionBase *>(isd->sectionBases));
754+
755+
// Matched sections by this SORT* pattern are sorted using all 3 keys.
756+
// ret[sizeBeforeCurrPat,ret.size()) are already in the input order,
757+
// so we just sort by sortOuter and sortInner.
758+
sortInputSections(
759+
MutableArrayRef<InputSectionBase *>(isd->sectionBases)
760+
.slice(sizeBeforeCurrPat),
761+
pat.sortOuter, pat.sortInner);
762+
763+
sizeAfterPrevSort = isd->sectionBases.size();
764+
}
765+
}
766+
// Matched sections after the last SORT* are sorted by (--sort-alignment,
767+
// input order).
768+
sortByPositionThenCommandLine(
769+
sizeAfterPrevSort, isd->sectionBases.size(),
770+
MutableArrayRef<InputSectionBase *>(isd->sectionBases));
771+
outSecToISecs[osec].append(isd->sectionBases);
772+
}
773+
}
774+
775+
return outSecToISecs;
776+
}
777+
DenseMap<OutputSection *, SmallVector<InputSectionBase *, 0>>
778+
LinkerScript::mapInputToOutputSections(ArrayRef<OutputSection *> osecs) {
779+
// Input section descriptions per output sections in the linker script
780+
DenseMap<OutputSection *, SmallVector<InputSectionDescription *, 0>>
781+
outSecToISecDescs;
782+
DenseMap<OutputSection *, SmallVector<InputSectionDescription *, 0>>
783+
outSecToUsedISecDescs;
784+
// Input sections matched to section patterns and their descriptions
785+
SmallVector<IsecBaseIsecDescPattern, 0> isecsToPatterns;
786+
787+
isecsToPatterns.reserve(ctx.inputSections.size());
788+
for (size_t i = 0; i < ctx.inputSections.size(); ++i) {
789+
isecsToPatterns.push_back({i});
790+
// Initialize name for script for archives to avoid problems because of
791+
// multithreading later
792+
InputFile *file = ctx.inputSections[i]->file;
793+
if (file && !file->archiveName.empty())
794+
file->initializeCachedArchiveName();
795+
}
796+
797+
// Initialize the osec if ReadWrite constrained and
798+
// map out secs to input sec description
799+
for (OutputSection *osec : osecs) {
800+
if (osec->constraint == ConstraintKind::ReadWrite)
801+
osec->constraintUnsatisfied = true;
802+
803+
SmallVector<InputSectionDescription *, 0> osecInputSecCmds;
804+
for (SectionCommand *cmd : osec->commands) {
805+
if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
806+
osecInputSecCmds.push_back(isd);
807+
}
808+
809+
outSecToISecDescs.insert({osec, osecInputSecCmds});
810+
}
811+
812+
parallelForEach(isecsToPatterns,
813+
[&](IsecBaseIsecDescPattern &isecBaseIsecDescPattern) {
814+
assignPatternsToInputSections(isecBaseIsecDescPattern,
815+
osecs, outSecToISecDescs);
816+
});
817+
818+
// Add input sections to their matching patterns
819+
for (IsecBaseIsecDescPattern &isecBaseIsecDescPattern : isecsToPatterns) {
820+
for (auto [i, isecDesc] :
821+
llvm::enumerate(isecBaseIsecDescPattern.isecDescs)) {
822+
isecDesc->sectionPatterns[isecBaseIsecDescPattern.patternIndexes[i]]
823+
.matchedSections.push_back(isecBaseIsecDescPattern.isecIndex);
824+
}
825+
}
826+
827+
return assignAndSortInputToOutputSections(osecs, outSecToISecDescs);
828+
}
829+
603830
// Create output sections described by SECTIONS commands.
604831
void LinkerScript::processSectionCommands() {
605-
auto process = [this](OutputSection *osec) {
606-
SmallVector<InputSectionBase *, 0> v = createInputSectionList(*osec);
607832

833+
auto process = [this](OutputSection *osec,
834+
SmallVector<InputSectionBase *, 0> &v) {
835+
// If constraints are unsatisfied, skip this section and make it as if it
836+
// never existed, we do this by clearing all of its commands (this is the
837+
// easiest way)
838+
if (osec->constraintUnsatisfied) {
839+
osec->commands.clear();
840+
return false;
841+
}
608842
// The output section name `/DISCARD/' is special.
609843
// Any input section assigned to it is discarded.
610844
if (osec->name == "/DISCARD/") {
@@ -615,20 +849,6 @@ void LinkerScript::processSectionCommands() {
615849
return false;
616850
}
617851

618-
// This is for ONLY_IF_RO and ONLY_IF_RW. An output section directive
619-
// ".foo : ONLY_IF_R[OW] { ... }" is handled only if all member input
620-
// sections satisfy a given constraint. If not, a directive is handled
621-
// as if it wasn't present from the beginning.
622-
//
623-
// Because we'll iterate over SectionCommands many more times, the easy
624-
// way to "make it as if it wasn't present" is to make it empty.
625-
if (!matchConstraints(v, osec->constraint)) {
626-
for (InputSectionBase *s : v)
627-
s->parent = nullptr;
628-
osec->commands.clear();
629-
return false;
630-
}
631-
632852
// Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
633853
// is given, input sections are aligned to that value, whether the
634854
// given value is larger or smaller than the original section alignment.
@@ -649,23 +869,41 @@ void LinkerScript::processSectionCommands() {
649869
// or orphans.
650870
DenseMap<CachedHashStringRef, OutputDesc *> map;
651871
size_t i = 0;
872+
873+
SmallVector<OutputSection *, 0> overwriteSectionsVec;
874+
overwriteSectionsVec.reserve(overwriteSections.size());
875+
for (OutputDesc *osd : overwriteSections)
876+
overwriteSectionsVec.push_back(&osd->osec);
877+
878+
DenseMap<OutputSection *, SmallVector<InputSectionBase *, 0>>
879+
outSecToISecsOverwrite = mapInputToOutputSections(overwriteSectionsVec);
652880
for (OutputDesc *osd : overwriteSections) {
653881
OutputSection *osec = &osd->osec;
654-
if (process(osec) &&
882+
if (process(osec, outSecToISecsOverwrite[osec]) &&
655883
!map.try_emplace(CachedHashStringRef(osec->name), osd).second)
656884
warn("OVERWRITE_SECTIONS specifies duplicate " + osec->name);
657885
}
658-
for (SectionCommand *&base : sectionCommands)
886+
887+
SmallVector<OutputSection *, 0> outputSecVec;
888+
for (SectionCommand *base : sectionCommands) {
889+
if (auto *osd = dyn_cast<OutputDesc>(base))
890+
outputSecVec.push_back(&osd->osec);
891+
}
892+
893+
DenseMap<OutputSection *, SmallVector<InputSectionBase *, 0>> outSecToISecs =
894+
mapInputToOutputSections(outputSecVec);
895+
for (SectionCommand *&base : sectionCommands) {
659896
if (auto *osd = dyn_cast<OutputDesc>(base)) {
660897
OutputSection *osec = &osd->osec;
661898
if (OutputDesc *overwrite = map.lookup(CachedHashStringRef(osec->name))) {
662899
log(overwrite->osec.location + " overwrites " + osec->name);
663900
overwrite->osec.sectionIndex = i++;
664901
base = overwrite;
665-
} else if (process(osec)) {
902+
} else if (process(osec, outSecToISecs[osec])) {
666903
osec->sectionIndex = i++;
667904
}
668905
}
906+
}
669907

670908
// If an OVERWRITE_SECTIONS specified output section is not in
671909
// sectionCommands, append it to the end. The section will be inserted by

0 commit comments

Comments
 (0)