diff options
author | Fangrui Song <i@maskray.me> | 2024-07-30 18:38:03 -0700 |
---|---|---|
committer | Fangrui Song <i@maskray.me> | 2024-07-30 18:38:03 -0700 |
commit | be5a845e4c29aadb513ae6e5e2879dccf37efdbb (patch) | |
tree | a659acb325979bf401148e97e606b2f87fbff24a /llvm/lib/MC/MCAssembler.cpp | |
parent | c35c4c72e4977258fc1da940e0470e8d0671bf07 (diff) | |
download | llvm-be5a845e4c29aadb513ae6e5e2879dccf37efdbb.zip llvm-be5a845e4c29aadb513ae6e5e2879dccf37efdbb.tar.gz llvm-be5a845e4c29aadb513ae6e5e2879dccf37efdbb.tar.bz2 |
[MC] Compute fragment offsets eagerly
This builds on top of commit 9d0754ada5dbbc0c009bcc2f7824488419cc5530
("[MC] Relax fragments eagerly") and relaxes fragments eagerly to
eliminate MCSection::HasLayout and `getFragmentOffset` overhead. The
approach is slightly different from
1a47f3f3db66589c11f8ddacfeaecc03fb80c510 and has less performance
benefit.
The new layout algorithm also addresses the following problems:
* Size change of MCFillFragment/MCOrgFragment did not influence the
fixed-point iteration, which could be problematic for contrived cases.
* The `invalid number of bytes` error was reported too early. Since
`.zero A-B` might have temporary negative values in the first few
iterations.
* X86AsmBackend::finishLayout performed only one iteration, which might
not converge. In addition, the removed `#ifndef NDEBUG` code (disabled
by default) in X86AsmBackend::finishLayout was problematic, as !NDEBUG
and NDEBUG builds evaluated fragment offsets at different times before
this patch.
* The computed layout for relax-recompute-align.s is optimal now.
Builds with many text sections (e.g. full LTO) shall observe a decrease
in compile time while the new algorithm could be slightly slower for
some -O0 -g projects.
Aligned bundling from the deprecated PNaCl placed constraints how we can
perform iteration.
Diffstat (limited to 'llvm/lib/MC/MCAssembler.cpp')
-rw-r--r-- | llvm/lib/MC/MCAssembler.cpp | 162 |
1 files changed, 94 insertions, 68 deletions
diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp index cbeb41f5..fb27ef3 100644 --- a/llvm/lib/MC/MCAssembler.cpp +++ b/llvm/lib/MC/MCAssembler.cpp @@ -261,7 +261,11 @@ uint64_t MCAssembler::computeFragmentSize(const MCFragment &F) const { } int64_t Size = NumValues * FF.getValueSize(); if (Size < 0) { - getContext().reportError(FF.getLoc(), "invalid number of bytes"); + // The expression might use symbol values which have not yet converged. + // Allow the first few iterations to have temporary negative values. The + // limit is somewhat arbitrary but allows contrived interdependency. + if (RelaxSteps >= 2) + getContext().reportError(FF.getLoc(), "invalid number of bytes"); return 0; } return Size; @@ -428,28 +432,6 @@ void MCAssembler::layoutBundle(MCFragment *Prev, MCFragment *F) const { DF->Offset = EF->Offset; } -void MCAssembler::ensureValid(MCSection &Sec) const { - if (Sec.hasLayout()) - return; - Sec.setHasLayout(true); - MCFragment *Prev = nullptr; - uint64_t Offset = 0; - for (MCFragment &F : Sec) { - F.Offset = Offset; - if (isBundlingEnabled() && F.hasInstructions()) { - layoutBundle(Prev, &F); - Offset = F.Offset; - } - Offset += computeFragmentSize(F); - Prev = &F; - } -} - -uint64_t MCAssembler::getFragmentOffset(const MCFragment &F) const { - ensureValid(*F.getParent()); - return F.Offset; -} - // Simple getSymbolOffset helper for the non-variable case. static bool getLabelOffset(const MCAssembler &Asm, const MCSymbol &S, bool ReportError, uint64_t &Val) { @@ -929,22 +911,38 @@ void MCAssembler::layout() { // Layout until everything fits. this->HasLayout = true; - while (layoutOnce()) { + for (MCSection &Sec : *this) { + MCFragment *Prev = nullptr; + uint64_t Offset = 0; + for (MCFragment &F : Sec) { + F.Offset = Offset; + if (LLVM_UNLIKELY(isBundlingEnabled())) { + if (F.hasInstructions()) { + layoutBundle(Prev, &F); + Offset = F.Offset; + } + Prev = &F; + } + Offset += computeFragmentSize(F); + } + } + while (relaxOnce()) if (getContext().hadError()) return; - // Size of fragments in one section can depend on the size of fragments in - // another. If any fragment has changed size, we have to re-layout (and - // as a result possibly further relax) all. - for (MCSection &Sec : *this) - Sec.setHasLayout(false); - } DEBUG_WITH_TYPE("mc-dump", { errs() << "assembler backend - post-relaxation\n--\n"; dump(); }); - // Finalize the layout, including fragment lowering. - getBackend().finishLayout(*this); + // Some targets might want to adjust fragment offsets. If so, perform another + // relaxation loop. + if (getBackend().finishLayout(*this)) + while (relaxOnce()) + if (getContext().hadError()) + return; + + // Trigger computeFragmentSize errors. + RelaxSteps = UINT_MAX; DEBUG_WITH_TYPE("mc-dump", { errs() << "assembler backend - final-layout\n--\n"; @@ -1073,11 +1071,11 @@ bool MCAssembler::fragmentNeedsRelaxation(const MCRelaxableFragment *F) const { return false; } -bool MCAssembler::relaxInstruction(MCRelaxableFragment &F) { +void MCAssembler::relaxInstruction(MCRelaxableFragment &F) { assert(getEmitterPtr() && "Expected CodeEmitter defined for relaxInstruction"); if (!fragmentNeedsRelaxation(&F)) - return false; + return; ++stats::RelaxedInstructions; @@ -1095,10 +1093,9 @@ bool MCAssembler::relaxInstruction(MCRelaxableFragment &F) { F.getContents().clear(); getEmitter().encodeInstruction(Relaxed, F.getContents(), F.getFixups(), *F.getSubtargetInfo()); - return true; } -bool MCAssembler::relaxLEB(MCLEBFragment &LF) { +void MCAssembler::relaxLEB(MCLEBFragment &LF) { const unsigned OldSize = static_cast<unsigned>(LF.getContents().size()); unsigned PadTo = OldSize; int64_t Value; @@ -1134,7 +1131,6 @@ bool MCAssembler::relaxLEB(MCLEBFragment &LF) { encodeSLEB128(Value, OSE, PadTo); else encodeULEB128(Value, OSE, PadTo); - return OldSize != LF.getContents().size(); } /// Check if the branch crosses the boundary. @@ -1174,11 +1170,11 @@ static bool needPadding(uint64_t StartAddr, uint64_t Size, isAgainstBoundary(StartAddr, Size, BoundaryAlignment); } -bool MCAssembler::relaxBoundaryAlign(MCBoundaryAlignFragment &BF) { +void MCAssembler::relaxBoundaryAlign(MCBoundaryAlignFragment &BF) { // BoundaryAlignFragment that doesn't need to align any fragment should not be // relaxed. if (!BF.getLastFragment()) - return false; + return; uint64_t AlignedOffset = getFragmentOffset(BF); uint64_t AlignedSize = 0; @@ -1192,19 +1188,15 @@ bool MCAssembler::relaxBoundaryAlign(MCBoundaryAlignFragment &BF) { uint64_t NewSize = needPadding(AlignedOffset, AlignedSize, BoundaryAlignment) ? offsetToAlignment(AlignedOffset, BoundaryAlignment) : 0U; - if (NewSize == BF.getSize()) - return false; BF.setSize(NewSize); - return true; } -bool MCAssembler::relaxDwarfLineAddr(MCDwarfLineAddrFragment &DF) { +void MCAssembler::relaxDwarfLineAddr(MCDwarfLineAddrFragment &DF) { bool WasRelaxed; if (getBackend().relaxDwarfLineAddr(*this, DF, WasRelaxed)) - return WasRelaxed; + return; MCContext &Context = getContext(); - uint64_t OldSize = DF.getContents().size(); int64_t AddrDelta; bool Abs = DF.getAddrDelta().evaluateKnownAbsolute(AddrDelta, *this); assert(Abs && "We created a line delta with an invalid expression"); @@ -1217,13 +1209,12 @@ bool MCAssembler::relaxDwarfLineAddr(MCDwarfLineAddrFragment &DF) { MCDwarfLineAddr::encode(Context, getDWARFLinetableParams(), LineDelta, AddrDelta, Data); - return OldSize != Data.size(); } -bool MCAssembler::relaxDwarfCallFrameFragment(MCDwarfCallFrameFragment &DF) { +void MCAssembler::relaxDwarfCallFrameFragment(MCDwarfCallFrameFragment &DF) { bool WasRelaxed; if (getBackend().relaxDwarfCFA(*this, DF, WasRelaxed)) - return WasRelaxed; + return; MCContext &Context = getContext(); int64_t Value; @@ -1232,31 +1223,25 @@ bool MCAssembler::relaxDwarfCallFrameFragment(MCDwarfCallFrameFragment &DF) { getContext().reportError(DF.getAddrDelta().getLoc(), "invalid CFI advance_loc expression"); DF.setAddrDelta(MCConstantExpr::create(0, Context)); - return false; + return; } SmallVectorImpl<char> &Data = DF.getContents(); - uint64_t OldSize = Data.size(); Data.clear(); DF.getFixups().clear(); MCDwarfFrameEmitter::encodeAdvanceLoc(Context, Value, Data); - return OldSize != Data.size(); } -bool MCAssembler::relaxCVInlineLineTable(MCCVInlineLineTableFragment &F) { - unsigned OldSize = F.getContents().size(); +void MCAssembler::relaxCVInlineLineTable(MCCVInlineLineTableFragment &F) { getContext().getCVContext().encodeInlineLineTable(*this, F); - return OldSize != F.getContents().size(); } -bool MCAssembler::relaxCVDefRange(MCCVDefRangeFragment &F) { - unsigned OldSize = F.getContents().size(); +void MCAssembler::relaxCVDefRange(MCCVDefRangeFragment &F) { getContext().getCVContext().encodeDefRange(*this, F); - return OldSize != F.getContents().size(); } -bool MCAssembler::relaxPseudoProbeAddr(MCPseudoProbeAddrFragment &PF) { +void MCAssembler::relaxPseudoProbeAddr(MCPseudoProbeAddrFragment &PF) { uint64_t OldSize = PF.getContents().size(); int64_t AddrDelta; bool Abs = PF.getAddrDelta().evaluateKnownAbsolute(AddrDelta, *this); @@ -1269,13 +1254,12 @@ bool MCAssembler::relaxPseudoProbeAddr(MCPseudoProbeAddrFragment &PF) { // AddrDelta is a signed integer encodeSLEB128(AddrDelta, OSE, OldSize); - return OldSize != Data.size(); } -bool MCAssembler::relaxFragment(MCFragment &F) { +void MCAssembler::relaxFragment(MCFragment &F) { switch(F.getKind()) { default: - return false; + return; case MCFragment::FT_Relaxable: assert(!getRelaxAll() && "Did not expect a MCRelaxableFragment in RelaxAll mode"); @@ -1297,15 +1281,57 @@ bool MCAssembler::relaxFragment(MCFragment &F) { } } -bool MCAssembler::layoutOnce() { +bool MCAssembler::relaxOnce() { ++stats::RelaxationSteps; + ++RelaxSteps; - bool Changed = false; - for (MCSection &Sec : *this) - for (MCFragment &Frag : Sec) - if (relaxFragment(Frag)) - Changed = true; - return Changed; + // Size of fragments in one section can depend on the size of fragments in + // another. If any fragment has changed size, we have to re-layout (and + // as a result possibly further relax) all sections. + bool ChangedAny = false, Changed; + for (MCSection &Sec : *this) { + // Assume each iteration finalizes at least one extra fragment. If the + // layout does not converge after N+1 iterations, bail out. + auto MaxIter = Sec.curFragList()->Tail->getLayoutOrder() + 1; + uint64_t OldSize = getSectionAddressSize(Sec); + do { + uint64_t Offset = 0; + Changed = false; + if (LLVM_UNLIKELY(isBundlingEnabled())) { + MCFragment *Prev = nullptr; + for (MCFragment &F : Sec) { + F.Offset = Offset; + relaxFragment(F); + if (F.hasInstructions()) { + layoutBundle(Prev, &F); + Offset = F.Offset; + } + Prev = &F; + if (F.Offset != Offset) { + F.Offset = Offset; + Changed = true; + } + Offset += computeFragmentSize(F); + } + } else { + for (MCFragment &F : Sec) { + if (F.Offset != Offset) { + F.Offset = Offset; + Changed = true; + } + relaxFragment(F); + Offset += computeFragmentSize(F); + } + } + + Changed |= OldSize != Offset; + ChangedAny |= Changed; + OldSize = Offset; + } while (Changed && --MaxIter); + if (MaxIter == 0) + return false; + } + return ChangedAny; } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |