Skip to content

Commit f2386e9

Browse files
committed
MC: Refactor FT_Align fragments when linker relaxation is enabled
Previously, two MCAsmBackend hooks were used, with shouldInsertFixupForCodeAlign calling getWriter().recordRelocation directly, bypassing generic code. This patch: * Introduces MCAsmBackend::relaxAlign to replace the two hooks. * Tracks padding size using VarContentStart and VarContentEnd (content is arbitrary). * Move setLinkerRelaxable from MCObjectStreamer::emitCodeAlignment to the backends. Pull Request: llvm#149465
1 parent 17b511e commit f2386e9

File tree

10 files changed

+146
-224
lines changed

10 files changed

+146
-224
lines changed

llvm/include/llvm/MC/MCAsmBackend.h

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,6 @@ class LLVM_ABI MCAsmBackend {
103103
/// Get information on a fixup kind.
104104
virtual MCFixupKindInfo getFixupKindInfo(MCFixupKind Kind) const;
105105

106-
/// Hook to check if extra nop bytes must be inserted for alignment directive.
107-
/// For some targets this may be necessary in order to support linker
108-
/// relaxation. The number of bytes to insert are returned in Size.
109-
virtual bool shouldInsertExtraNopBytesForCodeAlign(const MCFragment &AF,
110-
unsigned &Size) {
111-
return false;
112-
}
113-
114-
/// Hook which indicates if the target requires a fixup to be generated when
115-
/// handling an align directive in an executable section
116-
virtual bool shouldInsertFixupForCodeAlign(MCAssembler &Asm, MCFragment &AF) {
117-
return false;
118-
}
119-
120106
// Evaluate a fixup, returning std::nullopt to use default handling for
121107
// `Value` and `IsResolved`. Otherwise, returns `IsResolved` with the
122108
// expectation that the hook updates `Value`.
@@ -174,6 +160,10 @@ class LLVM_ABI MCAsmBackend {
174160
}
175161

176162
// Defined by linker relaxation targets.
163+
164+
// Return false to use default handling. Otherwise, set `Size` to the number
165+
// of padding bytes.
166+
virtual bool relaxAlign(MCFragment &F, unsigned &Size) { return false; }
177167
virtual bool relaxDwarfLineAddr(MCFragment &, bool &WasRelaxed) const {
178168
return false;
179169
}

llvm/lib/MC/MCAssembler.cpp

Lines changed: 60 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ uint64_t MCAssembler::computeFragmentSize(const MCFragment &F) const {
196196
switch (F.getKind()) {
197197
case MCFragment::FT_Data:
198198
case MCFragment::FT_Relaxable:
199+
case MCFragment::FT_Align:
199200
case MCFragment::FT_LEB:
200201
case MCFragment::FT_Dwarf:
201202
case MCFragment::FT_DwarfFrame:
@@ -226,27 +227,6 @@ uint64_t MCAssembler::computeFragmentSize(const MCFragment &F) const {
226227
case MCFragment::FT_SymbolId:
227228
return 4;
228229

229-
case MCFragment::FT_Align: {
230-
unsigned Offset = F.Offset + F.getFixedSize();
231-
unsigned Size = offsetToAlignment(Offset, F.getAlignment());
232-
233-
// Insert extra Nops for code alignment if the target define
234-
// shouldInsertExtraNopBytesForCodeAlign target hook.
235-
if (F.getParent()->useCodeAlign() && F.hasAlignEmitNops() &&
236-
getBackend().shouldInsertExtraNopBytesForCodeAlign(F, Size))
237-
return F.getFixedSize() + Size;
238-
239-
// If we are padding with nops, force the padding to be larger than the
240-
// minimum nop size.
241-
if (Size > 0 && F.hasAlignEmitNops()) {
242-
while (Size % getBackend().getMinimumNopSize())
243-
Size += F.getAlignment().value();
244-
}
245-
if (Size > F.getAlignMaxBytesToEmit())
246-
Size = 0;
247-
return F.getFixedSize() + Size;
248-
}
249-
250230
case MCFragment::FT_Org: {
251231
const MCOrgFragment &OF = cast<MCOrgFragment>(F);
252232
MCValue Value;
@@ -418,7 +398,6 @@ static void writeFragment(raw_ostream &OS, const MCAssembler &Asm,
418398
switch (F.getKind()) {
419399
case MCFragment::FT_Data:
420400
case MCFragment::FT_Relaxable:
421-
case MCFragment::FT_Align:
422401
case MCFragment::FT_LEB:
423402
case MCFragment::FT_Dwarf:
424403
case MCFragment::FT_DwarfFrame:
@@ -431,42 +410,41 @@ static void writeFragment(raw_ostream &OS, const MCAssembler &Asm,
431410
const auto &EF = cast<MCFragment>(F);
432411
OS << StringRef(EF.getContents().data(), EF.getContents().size());
433412
OS << StringRef(EF.getVarContents().data(), EF.getVarContents().size());
434-
if (F.getKind() == MCFragment::FT_Align) {
435-
++stats::EmittedAlignFragments;
436-
assert(F.getAlignFillLen() &&
437-
"Invalid virtual align in concrete fragment!");
438-
439-
uint64_t Count = (FragmentSize - F.getFixedSize()) / F.getAlignFillLen();
440-
assert((FragmentSize - F.getFixedSize()) % F.getAlignFillLen() == 0 &&
441-
"computeFragmentSize computed size is incorrect");
442-
443-
// See if we are aligning with nops, and if so do that first to try to
444-
// fill the Count bytes. Then if that did not fill any bytes or there are
445-
// any bytes left to fill use the Value and ValueSize to fill the rest. If
446-
// we are aligning with nops, ask that target to emit the right data.
447-
if (F.hasAlignEmitNops()) {
448-
if (!Asm.getBackend().writeNopData(OS, Count, F.getSubtargetInfo()))
449-
report_fatal_error("unable to write nop sequence of " + Twine(Count) +
450-
" bytes");
451-
} else {
452-
// Otherwise, write out in multiples of the value size.
453-
for (uint64_t i = 0; i != Count; ++i) {
454-
switch (F.getAlignFillLen()) {
455-
default:
456-
llvm_unreachable("Invalid size!");
457-
case 1:
458-
OS << char(F.getAlignFill());
459-
break;
460-
case 2:
461-
support::endian::write<uint16_t>(OS, F.getAlignFill(), Endian);
462-
break;
463-
case 4:
464-
support::endian::write<uint32_t>(OS, F.getAlignFill(), Endian);
465-
break;
466-
case 8:
467-
support::endian::write<uint64_t>(OS, F.getAlignFill(), Endian);
468-
break;
469-
}
413+
} break;
414+
415+
case MCFragment::FT_Align: {
416+
++stats::EmittedAlignFragments;
417+
OS << StringRef(F.getContents().data(), F.getContents().size());
418+
assert(F.getAlignFillLen() &&
419+
"Invalid virtual align in concrete fragment!");
420+
421+
uint64_t Count = (FragmentSize - F.getFixedSize()) / F.getAlignFillLen();
422+
assert((FragmentSize - F.getFixedSize()) % F.getAlignFillLen() == 0 &&
423+
"computeFragmentSize computed size is incorrect");
424+
425+
// In the nops mode, call the backend hook to write `Count` nops.
426+
if (F.hasAlignEmitNops()) {
427+
if (!Asm.getBackend().writeNopData(OS, Count, F.getSubtargetInfo()))
428+
reportFatalInternalError("unable to write nop sequence of " +
429+
Twine(Count) + " bytes");
430+
} else {
431+
// Otherwise, write out in multiples of the value size.
432+
for (uint64_t i = 0; i != Count; ++i) {
433+
switch (F.getAlignFillLen()) {
434+
default:
435+
llvm_unreachable("Invalid size!");
436+
case 1:
437+
OS << char(F.getAlignFill());
438+
break;
439+
case 2:
440+
support::endian::write<uint16_t>(OS, F.getAlignFill(), Endian);
441+
break;
442+
case 4:
443+
support::endian::write<uint32_t>(OS, F.getAlignFill(), Endian);
444+
break;
445+
case 8:
446+
support::endian::write<uint64_t>(OS, F.getAlignFill(), Endian);
447+
break;
470448
}
471449
}
472450
}
@@ -737,11 +715,6 @@ void MCAssembler::layout() {
737715
evaluateFixup(F, Fixup, Target, FixedValue,
738716
/*RecordReloc=*/true, Contents);
739717
}
740-
} else if (F.getKind() == MCFragment::FT_Align) {
741-
// For RISC-V linker relaxation, an alignment relocation might be
742-
// needed.
743-
if (F.hasAlignEmitNops())
744-
getBackend().shouldInsertFixupForCodeAlign(*this, F);
745718
}
746719
}
747720
}
@@ -992,7 +965,30 @@ void MCAssembler::layoutSection(MCSection &Sec) {
992965
uint64_t Offset = 0;
993966
for (MCFragment &F : Sec) {
994967
F.Offset = Offset;
995-
Offset += computeFragmentSize(F);
968+
if (F.getKind() == MCFragment::FT_Align) {
969+
Offset += F.getFixedSize();
970+
unsigned Size = offsetToAlignment(Offset, F.getAlignment());
971+
// In the nops mode, RISC-V style linker relaxation might adjust the size
972+
// and add a fixup, even if `Size` is originally 0.
973+
bool AlignFixup = false;
974+
if (F.hasAlignEmitNops()) {
975+
AlignFixup = getBackend().relaxAlign(F, Size);
976+
// If the backend does not handle the fragment specially, pad with nops,
977+
// but ensure that the padding is larger than the minimum nop size.
978+
if (!AlignFixup)
979+
while (Size % getBackend().getMinimumNopSize())
980+
Size += F.getAlignment().value();
981+
}
982+
if (!AlignFixup && Size > F.getAlignMaxBytesToEmit())
983+
Size = 0;
984+
// Update the variable tail size. The content is ignored.
985+
F.VarContentEnd = F.VarContentStart + Size;
986+
if (F.VarContentEnd > F.getParent()->ContentStorage.size())
987+
F.getParent()->ContentStorage.resize(F.VarContentEnd);
988+
Offset += Size;
989+
} else {
990+
Offset += computeFragmentSize(F);
991+
}
996992
}
997993
}
998994

llvm/lib/MC/MCExpr.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,6 @@ static void attemptToFoldSymbolOffsetDifference(const MCAssembler *Asm,
370370
}
371371

372372
int64_t Num;
373-
unsigned Count;
374373
if (DF) {
375374
Displacement += DF->getContents().size();
376375
} else if (F->getKind() == MCFragment::FT_Relaxable &&
@@ -380,9 +379,7 @@ static void attemptToFoldSymbolOffsetDifference(const MCAssembler *Asm,
380379
// data fragment.
381380
Displacement += F->getSize();
382381
} else if (F->getKind() == MCFragment::FT_Align && Layout &&
383-
F->hasAlignEmitNops() &&
384-
!Asm->getBackend().shouldInsertExtraNopBytesForCodeAlign(
385-
*F, Count)) {
382+
F->isLinkerRelaxable()) {
386383
Displacement += Asm->computeFragmentSize(*F);
387384
} else if (auto *FF = dyn_cast<MCFillFragment>(F);
388385
FF && FF->getNumValues().evaluateAsAbsolute(Num)) {

llvm/lib/MC/MCFragment.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,13 @@ LLVM_DUMP_METHOD void MCFragment::dump() const {
8383
auto Fixed = getContents();
8484
auto Var = getVarContents();
8585
OS << " Size:" << Fixed.size();
86-
if (getKind() != MCFragment::FT_Data)
86+
if (getKind() != MCFragment::FT_Data) {
8787
OS << '+' << Var.size();
88+
// FT_Align uses getVarContents to track the size, but the content is
89+
// ignored and not useful.
90+
if (getKind() == MCFragment::FT_Align)
91+
Var = {};
92+
}
8893
OS << " [";
8994
for (unsigned i = 0, e = Fixed.size(); i != e; ++i) {
9095
if (i) OS << ",";

llvm/lib/MC/MCObjectStreamer.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -547,14 +547,6 @@ void MCObjectStreamer::emitCodeAlignment(Align Alignment,
547547
emitValueToAlignment(Alignment, 0, 1, MaxBytesToEmit);
548548
F->u.align.EmitNops = true;
549549
F->STI = STI;
550-
551-
// With RISC-V style linker relaxation, mark the section as linker-relaxable
552-
// if the alignment is larger than the minimum NOP size.
553-
unsigned Size;
554-
if (getAssembler().getBackend().shouldInsertExtraNopBytesForCodeAlign(*F,
555-
Size)) {
556-
F->getParent()->setLinkerRelaxable();
557-
}
558550
}
559551

560552
void MCObjectStreamer::emitValueToOffset(const MCExpr *Offset,

llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp

Lines changed: 47 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -177,74 +177,6 @@ void LoongArchAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup,
177177
}
178178
}
179179

180-
// Linker relaxation may change code size. We have to insert Nops
181-
// for .align directive when linker relaxation enabled. So then Linker
182-
// could satisfy alignment by removing Nops.
183-
// The function returns the total Nops Size we need to insert.
184-
bool LoongArchAsmBackend::shouldInsertExtraNopBytesForCodeAlign(
185-
const MCFragment &AF, unsigned &Size) {
186-
// Calculate Nops Size only when linker relaxation enabled.
187-
if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))
188-
return false;
189-
190-
// Ignore alignment if MaxBytesToEmit is less than the minimum Nop size.
191-
const unsigned MinNopLen = 4;
192-
if (AF.getAlignMaxBytesToEmit() < MinNopLen)
193-
return false;
194-
Size = AF.getAlignment().value() - MinNopLen;
195-
return AF.getAlignment() > MinNopLen;
196-
}
197-
198-
// We need to insert R_LARCH_ALIGN relocation type to indicate the
199-
// position of Nops and the total bytes of the Nops have been inserted
200-
// when linker relaxation enabled.
201-
// The function inserts fixup_loongarch_align fixup which eventually will
202-
// transfer to R_LARCH_ALIGN relocation type.
203-
// The improved R_LARCH_ALIGN requires symbol index. The lowest 8 bits of
204-
// addend represent alignment and the other bits of addend represent the
205-
// maximum number of bytes to emit. The maximum number of bytes is zero
206-
// means ignore the emit limit.
207-
bool LoongArchAsmBackend::shouldInsertFixupForCodeAlign(MCAssembler &Asm,
208-
MCFragment &AF) {
209-
// Insert the fixup only when linker relaxation enabled.
210-
if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))
211-
return false;
212-
213-
// Calculate total Nops we need to insert. If there are none to insert
214-
// then simply return.
215-
unsigned InsertedNopBytes;
216-
if (!shouldInsertExtraNopBytesForCodeAlign(AF, InsertedNopBytes))
217-
return false;
218-
219-
MCSection *Sec = AF.getParent();
220-
MCContext &Ctx = getContext();
221-
const MCExpr *Dummy = MCConstantExpr::create(0, Ctx);
222-
MCFixup Fixup = MCFixup::create(AF.getFixedSize(), Dummy, ELF::R_LARCH_ALIGN);
223-
unsigned MaxBytesToEmit = AF.getAlignMaxBytesToEmit();
224-
225-
auto createExtendedValue = [&]() {
226-
const MCSymbolRefExpr *MCSym = getSecToAlignSym()[Sec];
227-
if (MCSym == nullptr) {
228-
// Define a marker symbol at the section with an offset of 0.
229-
MCSymbol *Sym = Ctx.createNamedTempSymbol("la-relax-align");
230-
Sym->setFragment(&*Sec->getBeginSymbol()->getFragment());
231-
Asm.registerSymbol(*Sym);
232-
MCSym = MCSymbolRefExpr::create(Sym, Ctx);
233-
getSecToAlignSym()[Sec] = MCSym;
234-
}
235-
return MCValue::get(&MCSym->getSymbol(), nullptr,
236-
MaxBytesToEmit << 8 | Log2(AF.getAlignment()));
237-
};
238-
239-
uint64_t FixedValue = 0;
240-
MCValue Value = MaxBytesToEmit >= InsertedNopBytes
241-
? MCValue::get(InsertedNopBytes)
242-
: createExtendedValue();
243-
Asm.getWriter().recordRelocation(AF, Fixup, Value, FixedValue);
244-
245-
return true;
246-
}
247-
248180
bool LoongArchAsmBackend::shouldForceRelocation(const MCFixup &Fixup,
249181
const MCValue &Target) {
250182
switch (Fixup.getKind()) {
@@ -279,6 +211,53 @@ getRelocPairForSize(unsigned Size) {
279211
}
280212
}
281213

214+
// Check if an R_LARCH_ALIGN relocation is needed for an alignment directive.
215+
// If conditions are met, compute the padding size and create a fixup encoding
216+
// the padding size in the addend. If MaxBytesToEmit is smaller than the padding
217+
// size, the fixup encodes MaxBytesToEmit in the higher bits and references a
218+
// per-section marker symbol.
219+
bool LoongArchAsmBackend::relaxAlign(MCFragment &F, unsigned &Size) {
220+
// Use default handling unless linker relaxation is enabled and the
221+
// MaxBytesToEmit >= the nop size.
222+
if (!F.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))
223+
return false;
224+
const unsigned MinNopLen = 4;
225+
unsigned MaxBytesToEmit = F.getAlignMaxBytesToEmit();
226+
if (MaxBytesToEmit < MinNopLen)
227+
return false;
228+
229+
Size = F.getAlignment().value() - MinNopLen;
230+
if (F.getAlignment() <= MinNopLen)
231+
return false;
232+
233+
MCContext &Ctx = getContext();
234+
const MCExpr *Expr = nullptr;
235+
if (MaxBytesToEmit >= Size) {
236+
Expr = MCConstantExpr::create(Size, getContext());
237+
} else {
238+
MCSection *Sec = F.getParent();
239+
const MCSymbolRefExpr *SymRef = getSecToAlignSym()[Sec];
240+
if (SymRef == nullptr) {
241+
// Define a marker symbol at the section with an offset of 0.
242+
MCSymbol *Sym = Ctx.createNamedTempSymbol("la-relax-align");
243+
Sym->setFragment(&*Sec->getBeginSymbol()->getFragment());
244+
Asm->registerSymbol(*Sym);
245+
SymRef = MCSymbolRefExpr::create(Sym, Ctx);
246+
getSecToAlignSym()[Sec] = SymRef;
247+
}
248+
Expr = MCBinaryExpr::createAdd(
249+
SymRef,
250+
MCConstantExpr::create((MaxBytesToEmit << 8) | Log2(F.getAlignment()),
251+
Ctx),
252+
Ctx);
253+
}
254+
MCFixup Fixup =
255+
MCFixup::create(0, Expr, FirstLiteralRelocationKind + ELF::R_LARCH_ALIGN);
256+
F.setVarFixups({Fixup});
257+
F.getParent()->setLinkerRelaxable();
258+
return true;
259+
}
260+
282261
std::pair<bool, bool> LoongArchAsmBackend::relaxLEB128(MCFragment &F,
283262
int64_t &Value) const {
284263
const MCExpr &Expr = F.getLEBValue();

llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,13 @@ class LoongArchAsmBackend : public MCAsmBackend {
4545
MutableArrayRef<char> Data, uint64_t Value,
4646
bool IsResolved) override;
4747

48-
// Return Size with extra Nop Bytes for alignment directive in code section.
49-
bool shouldInsertExtraNopBytesForCodeAlign(const MCFragment &AF,
50-
unsigned &Size) override;
51-
52-
// Insert target specific fixup type for alignment directive in code section.
53-
bool shouldInsertFixupForCodeAlign(MCAssembler &Asm, MCFragment &AF) override;
54-
5548
bool shouldForceRelocation(const MCFixup &Fixup, const MCValue &Target);
5649

5750
std::optional<MCFixupKind> getFixupKind(StringRef Name) const override;
5851

5952
MCFixupKindInfo getFixupKindInfo(MCFixupKind Kind) const override;
6053

54+
bool relaxAlign(MCFragment &F, unsigned &Size) override;
6155
bool relaxDwarfLineAddr(MCFragment &F, bool &WasRelaxed) const override;
6256
bool relaxDwarfCFA(MCFragment &F, bool &WasRelaxed) const override;
6357
std::pair<bool, bool> relaxLEB128(MCFragment &F,

0 commit comments

Comments
 (0)