aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/MC/MCAssembler.cpp
diff options
context:
space:
mode:
authorFangrui Song <i@maskray.me>2024-07-30 18:38:03 -0700
committerFangrui Song <i@maskray.me>2024-07-30 18:38:03 -0700
commitbe5a845e4c29aadb513ae6e5e2879dccf37efdbb (patch)
treea659acb325979bf401148e97e606b2f87fbff24a /llvm/lib/MC/MCAssembler.cpp
parentc35c4c72e4977258fc1da940e0470e8d0671bf07 (diff)
downloadllvm-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.cpp162
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)