From 85cfd917231c14757f01f4be66c1bbd10fb933d0 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 24 Jul 2022 11:20:49 -0700 Subject: [PATCH] [ELF] Optimize some non-constant alignTo with alignToPowerOf2. NFC My x86-64 lld executable is 2KiB smaller. .eh_frame writing gets faster as there were lots of divisions. --- lld/ELF/Driver.cpp | 8 ++++++-- lld/ELF/LinkerScript.cpp | 12 ++++++------ lld/ELF/ScriptParser.cpp | 7 ++++--- lld/ELF/SyntheticSections.cpp | 12 ++++++------ lld/ELF/Writer.cpp | 30 ++++++++++++++++-------------- lld/test/ELF/linkerscript/operators.test | 2 +- llvm/include/llvm/Support/MathExtras.h | 6 ++++++ 7 files changed, 45 insertions(+), 32 deletions(-) diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index dd17adc..30534f7 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1691,8 +1691,10 @@ void LinkerDriver::inferMachineType() { static uint64_t getMaxPageSize(opt::InputArgList &args) { uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size", target->defaultMaxPageSize); - if (!isPowerOf2_64(val)) + if (!isPowerOf2_64(val)) { error("max-page-size: value isn't a power of 2"); + return target->defaultMaxPageSize; + } if (config->nmagic || config->omagic) { if (val != target->defaultMaxPageSize) warn("-z max-page-size set, but paging disabled by omagic or nmagic"); @@ -1706,8 +1708,10 @@ static uint64_t getMaxPageSize(opt::InputArgList &args) { static uint64_t getCommonPageSize(opt::InputArgList &args) { uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size", target->defaultCommonPageSize); - if (!isPowerOf2_64(val)) + if (!isPowerOf2_64(val)) { error("common-page-size: value isn't a power of 2"); + return target->defaultCommonPageSize; + } if (config->nmagic || config->omagic) { if (val != target->defaultCommonPageSize) warn("-z common-page-size set, but paging disabled by omagic or nmagic"); diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 3be4290..9f30117 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -112,9 +112,9 @@ static StringRef getOutputSectionName(const InputSectionBase *s) { uint64_t ExprValue::getValue() const { if (sec) - return alignTo(sec->getOutputSection()->addr + sec->getOffset(val), - alignment); - return alignTo(val, alignment); + return alignToPowerOf2(sec->getOutputSection()->addr + sec->getOffset(val), + alignment); + return alignToPowerOf2(val, alignment); } uint64_t ExprValue::getSecAddr() const { @@ -989,7 +989,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) { // sec->alignment is the max of ALIGN and the maximum of input // section alignments. const uint64_t pos = dot; - dot = alignTo(dot, sec->alignment); + dot = alignToPowerOf2(dot, sec->alignment); sec->addr = dot; expandMemoryRegions(dot - pos); } @@ -1003,7 +1003,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) { if (sec->lmaExpr) { ctx->lmaOffset = sec->lmaExpr().getValue() - dot; } else if (MemoryRegion *mr = sec->lmaRegion) { - uint64_t lmaStart = alignTo(mr->curPos, sec->alignment); + uint64_t lmaStart = alignToPowerOf2(mr->curPos, sec->alignment); if (mr->curPos < lmaStart) expandMemoryRegion(mr, lmaStart - mr->curPos, sec->name); ctx->lmaOffset = lmaStart - dot; @@ -1046,7 +1046,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) { for (InputSection *isec : cast(cmd)->sections) { assert(isec->getParent() == sec); const uint64_t pos = dot; - dot = alignTo(dot, isec->alignment); + dot = alignToPowerOf2(dot, isec->alignment); isec->outSecOff = dot - sec->addr; dot += isec->getSize(); diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index 4d73541..7fc50b2 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -1392,7 +1392,7 @@ Expr ScriptParser::readPrimary() { Expr e = readExpr(); if (consume(")")) { e = checkAlignment(e, location); - return [=] { return alignTo(script->getDot(), e().getValue()); }; + return [=] { return alignToPowerOf2(script->getDot(), e().getValue()); }; } expect(","); Expr e2 = checkAlignment(readExpr(), location); @@ -1423,7 +1423,8 @@ Expr ScriptParser::readPrimary() { expect(")"); seenDataAlign = true; return [=] { - return alignTo(script->getDot(), std::max((uint64_t)1, e().getValue())); + uint64_t align = std::max(uint64_t(1), e().getValue()); + return (script->getDot() + align - 1) & -align; }; } if (tok == "DATA_SEGMENT_END") { @@ -1443,7 +1444,7 @@ Expr ScriptParser::readPrimary() { expect(")"); seenRelroEnd = true; Expr e = getPageSize(); - return [=] { return alignTo(script->getDot(), e().getValue()); }; + return [=] { return alignToPowerOf2(script->getDot(), e().getValue()); }; } if (tok == "DEFINED") { StringRef name = unquote(readParenLiteral()); diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index a0c5e6d..919afc7 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -498,7 +498,7 @@ void EhFrameSection::iterateFDEWithLSDA( static void writeCieFde(uint8_t *buf, ArrayRef d) { memcpy(buf, d.data(), d.size()); - size_t aligned = alignTo(d.size(), config->wordsize); + size_t aligned = alignToPowerOf2(d.size(), config->wordsize); assert(std::all_of(buf + d.size(), buf + aligned, [](uint8_t c) { return c == 0; })); @@ -533,11 +533,11 @@ void EhFrameSection::finalizeContents() { size_t off = 0; for (CieRecord *rec : cieRecords) { rec->cie->outputOff = off; - off += alignTo(rec->cie->size, config->wordsize); + off += alignToPowerOf2(rec->cie->size, config->wordsize); for (EhSectionPiece *fde : rec->fdes) { fde->outputOff = off; - off += alignTo(fde->size, config->wordsize); + off += alignToPowerOf2(fde->size, config->wordsize); } } @@ -919,7 +919,7 @@ void MipsGotSection::build() { for (SectionCommand *cmd : os->commands) { if (auto *isd = dyn_cast(cmd)) for (InputSection *isec : isd->sections) { - uint64_t off = alignTo(secSize, isec->alignment); + uint64_t off = alignToPowerOf2(secSize, isec->alignment); secSize = off + isec->getSize(); } } @@ -3330,7 +3330,7 @@ void MergeNoTailSection::finalizeContents() { for (size_t i = 0; i < numShards; ++i) { shards[i].finalizeInOrder(); if (shards[i].getSize() > 0) - off = alignTo(off, alignment); + off = alignToPowerOf2(off, alignment); shardOffsets[i] = off; off += shards[i].getSize(); } @@ -3612,7 +3612,7 @@ InputSection *ThunkSection::getTargetInputSection() const { bool ThunkSection::assignOffsets() { uint64_t off = 0; for (Thunk *t : thunks) { - off = alignTo(off, t->alignment); + off = alignToPowerOf2(off, t->alignment); t->setOffset(off); uint32_t size = t->size(); t->getThunkTargetSym()->size = size; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 2994e79..c9345d8 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2483,7 +2483,7 @@ template void Writer::fixSectionAlignments() { (prev->p_flags & PF_X) != (p->p_flags & PF_X)) || cmd->type == SHT_LLVM_PART_EHDR) cmd->addrExpr = [] { - return alignTo(script->getDot(), config->maxPageSize); + return alignToPowerOf2(script->getDot(), config->maxPageSize); }; // PT_TLS is at the start of the first RW PT_LOAD. If `p` includes PT_TLS, // it must be the RW. Align to p_align(PT_TLS) to make sure @@ -2500,13 +2500,13 @@ template void Writer::fixSectionAlignments() { // blocks correctly. We need to keep the workaround for a while. else if (Out::tlsPhdr && Out::tlsPhdr->firstSec == p->firstSec) cmd->addrExpr = [] { - return alignTo(script->getDot(), config->maxPageSize) + - alignTo(script->getDot() % config->maxPageSize, - Out::tlsPhdr->p_align); + return alignToPowerOf2(script->getDot(), config->maxPageSize) + + alignToPowerOf2(script->getDot() % config->maxPageSize, + Out::tlsPhdr->p_align); }; else cmd->addrExpr = [] { - return alignTo(script->getDot(), config->maxPageSize) + + return alignToPowerOf2(script->getDot(), config->maxPageSize) + script->getDot() % config->maxPageSize; }; } @@ -2540,7 +2540,7 @@ static uint64_t computeFileOffset(OutputSection *os, uint64_t off) { // If the section is not in a PT_LOAD, we just have to align it. if (!os->ptLoad) - return alignTo(off, os->alignment); + return alignToPowerOf2(off, os->alignment); // If two sections share the same PT_LOAD the file offset is calculated // using this formula: Off2 = Off1 + (VA2 - VA1). @@ -2599,15 +2599,15 @@ template void Writer::assignFileOffsets() { // following section to avoid loading non-segments parts of the file. if (config->zSeparate != SeparateSegmentKind::None && lastRX && lastRX->lastSec == sec) - off = alignTo(off, config->maxPageSize); + off = alignToPowerOf2(off, config->maxPageSize); } for (OutputSection *osec : outputSections) if (!(osec->flags & SHF_ALLOC)) { - osec->offset = alignTo(off, osec->alignment); + osec->offset = alignToPowerOf2(off, osec->alignment); off = osec->offset + osec->size; } - sectionHeaderOff = alignTo(off, config->wordsize); + sectionHeaderOff = alignToPowerOf2(off, config->wordsize); fileSize = sectionHeaderOff + (outputSections.size() + 1) * sizeof(Elf_Shdr); // Our logic assumes that sections have rising VA within the same segment. @@ -2659,8 +2659,9 @@ template void Writer::setPhdrs(Partition &part) { // musl/glibc ld.so rounds the size down, so we need to round up // to protect the last page. This is a no-op on FreeBSD which always // rounds up. - p->p_memsz = alignTo(p->p_offset + p->p_memsz, config->commonPageSize) - - p->p_offset; + p->p_memsz = + alignToPowerOf2(p->p_offset + p->p_memsz, config->commonPageSize) - + p->p_offset; } } } @@ -2880,8 +2881,9 @@ template void Writer::writeTrapInstr() { if (p->p_type == PT_LOAD && (p->p_flags & PF_X)) fillTrap(Out::bufferStart + alignDown(p->firstSec->offset + p->p_filesz, 4), - Out::bufferStart + alignTo(p->firstSec->offset + p->p_filesz, - config->maxPageSize)); + Out::bufferStart + + alignToPowerOf2(p->firstSec->offset + p->p_filesz, + config->maxPageSize)); // Round up the file size of the last segment to the page boundary iff it is // an executable segment to ensure that other tools don't accidentally @@ -2893,7 +2895,7 @@ template void Writer::writeTrapInstr() { if (last && (last->p_flags & PF_X)) last->p_memsz = last->p_filesz = - alignTo(last->p_filesz, config->maxPageSize); + alignToPowerOf2(last->p_filesz, config->maxPageSize); } } diff --git a/lld/test/ELF/linkerscript/operators.test b/lld/test/ELF/linkerscript/operators.test index 5fb73c6..3958b95 100644 --- a/lld/test/ELF/linkerscript/operators.test +++ b/lld/test/ELF/linkerscript/operators.test @@ -100,7 +100,7 @@ SECTIONS { # CHECK-NEXT: 0000000000000009 A precedence2 # CHECK-NEXT: 0000000000001000 A maxpagesize # CHECK-NEXT: 0000000000001000 A commonpagesize -# CHECK-NEXT: 000000000000ffff A datasegmentalign +# CHECK-NEXT: 0000000000010000 A datasegmentalign # CHECK-NEXT: 000000000000fff0 A datasegmentalign2 # CHECK-NEXT: 000000000000ffe0 T minus_rel # CHECK-NEXT: 000000000000fff0 A minus_abs diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h index 8079aa4..372dbaf 100644 --- a/llvm/include/llvm/Support/MathExtras.h +++ b/llvm/include/llvm/Support/MathExtras.h @@ -747,6 +747,12 @@ inline uint64_t alignTo(uint64_t Value, uint64_t Align) { return (Value + Align - 1) / Align * Align; } +inline uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) { + assert(Align != 0 && (Align & Align - 1) == 0 && + "Align must be a power of 2"); + return (Value + Align - 1) & -Align; +} + /// If non-zero \p Skew is specified, the return value will be a minimal integer /// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for /// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p -- 2.7.4