diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst index 89a0e80fef5ef..ecce9e1eacda5 100644 --- a/llvm/docs/Extensions.rst +++ b/llvm/docs/Extensions.rst @@ -28,6 +28,21 @@ hexadecimal format instead of decimal if desired. .section .data .float 0x1c2.2ap3 +``.prefalign`` directive +------------------------ + +The ``.prefalign`` directive sets the preferred alignment for a section, +and enables the section's final alignment to be set in a way that is +dependent on the section size (currently only supported with ELF). + +If the section size is less than the section's minimum alignment as +determined using ``.align`` family directives, the section's alignment +will be equal to its minimum alignment. Otherwise, if the section size is +between the minimum alignment and the preferred alignment, the section's +alignment will be equal to the power of 2 greater than or equal to the +section size. Otherwise, the section's alignment will be equal to the +preferred alignment. + Machine-specific Assembly Syntax ================================ diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h index 7a2e9ad154f01..acf99cb8e5631 100644 --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -301,6 +301,9 @@ class LLVM_ABI MCAsmInfo { // most targets, so defaults to true. bool HasFunctionAlignment = true; + // True if the target respects .prefalign directives. + bool HasPreferredAlignment = false; + /// True if the target has .type and .size directives, this is true for most /// ELF targets. Defaults to true. bool HasDotTypeDotSizeDirective = true; @@ -603,6 +606,7 @@ class LLVM_ABI MCAsmInfo { } bool hasFunctionAlignment() const { return HasFunctionAlignment; } + bool hasPreferredAlignment() const { return HasPreferredAlignment; } bool hasDotTypeDotSizeDirective() const { return HasDotTypeDotSizeDirective; } bool hasSingleParameterDotFile() const { return HasSingleParameterDotFile; } bool hasIdentDirective() const { return HasIdentDirective; } diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h index 1899cb6331c6f..30dd1bc5f2242 100644 --- a/llvm/include/llvm/MC/MCObjectStreamer.h +++ b/llvm/include/llvm/MC/MCObjectStreamer.h @@ -137,6 +137,7 @@ class MCObjectStreamer : public MCStreamer { unsigned MaxBytesToEmit = 0) override; void emitCodeAlignment(Align ByteAlignment, const MCSubtargetInfo *STI, unsigned MaxBytesToEmit = 0) override; + void emitPrefAlign(Align Alignment) override; void emitValueToOffset(const MCExpr *Offset, unsigned char Value, SMLoc Loc) override; void emitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column, diff --git a/llvm/include/llvm/MC/MCSection.h b/llvm/include/llvm/MC/MCSection.h index a26e6cfb2158a..38b512f31d5df 100644 --- a/llvm/include/llvm/MC/MCSection.h +++ b/llvm/include/llvm/MC/MCSection.h @@ -550,6 +550,7 @@ class LLVM_ABI MCSection { MCSymbol *End = nullptr; /// The alignment requirement of this section. Align Alignment; + MaybeAlign PreferredAlignment; /// The section index in the assemblers section list. unsigned Ordinal = 0; // If not -1u, the first linker-relaxable fragment's order within the @@ -610,6 +611,19 @@ class LLVM_ABI MCSection { Alignment = MinAlignment; } + Align getPreferredAlignment() const { + if (!PreferredAlignment || Alignment > *PreferredAlignment) + return Alignment; + return *PreferredAlignment; + } + + void ensurePreferredAlignment(Align PrefAlign) { + if (!PreferredAlignment || PrefAlign > *PreferredAlignment) + PreferredAlignment = PrefAlign; + } + + Align getAlignmentForObjectFile(uint64_t Size) const; + unsigned getOrdinal() const { return Ordinal; } void setOrdinal(unsigned Value) { Ordinal = Value; } diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 79c715e3820a6..0cc42f0995e5b 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -839,6 +839,8 @@ class LLVM_ABI MCStreamer { virtual void emitCodeAlignment(Align Alignment, const MCSubtargetInfo *STI, unsigned MaxBytesToEmit = 0); + virtual void emitPrefAlign(Align A); + /// Emit some number of copies of \p Value until the byte offset \p /// Offset is reached. /// diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index 759d3e0e14291..3d7f503bf08b0 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -912,10 +912,10 @@ void ELFWriter::writeSectionHeader(uint32_t GroupSymbolIndex, uint64_t Offset, sh_link = Sym->getSection().getOrdinal(); } - writeSectionHeaderEntry(StrTabBuilder.getOffset(Section.getName()), - Section.getType(), Section.getFlags(), 0, Offset, - Size, sh_link, sh_info, Section.getAlign(), - Section.getEntrySize()); + writeSectionHeaderEntry( + StrTabBuilder.getOffset(Section.getName()), Section.getType(), + Section.getFlags(), 0, Offset, Size, sh_link, sh_info, + Section.getAlignmentForObjectFile(Size), Section.getEntrySize()); } void ELFWriter::writeSectionHeaders() { diff --git a/llvm/lib/MC/MCAsmInfoELF.cpp b/llvm/lib/MC/MCAsmInfoELF.cpp index 98090d34bcbdc..e5d33c04d6998 100644 --- a/llvm/lib/MC/MCAsmInfoELF.cpp +++ b/llvm/lib/MC/MCAsmInfoELF.cpp @@ -42,6 +42,7 @@ bool MCAsmInfoELF::useCodeAlign(const MCSection &Sec) const { MCAsmInfoELF::MCAsmInfoELF() { HasIdentDirective = true; + HasPreferredAlignment = true; WeakRefDirective = "\t.weak\t"; PrivateGlobalPrefix = ".L"; PrivateLabelPrefix = ".L"; diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index be8c022f39ad1..47002dbcd1728 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -274,6 +274,7 @@ class MCAsmStreamer final : public MCStreamer { void emitCodeAlignment(Align Alignment, const MCSubtargetInfo *STI, unsigned MaxBytesToEmit = 0) override; + void emitPrefAlign(Align Alignment) override; void emitValueToOffset(const MCExpr *Offset, unsigned char Value, @@ -1540,6 +1541,11 @@ void MCAsmStreamer::emitCodeAlignment(Align Alignment, emitAlignmentDirective(Alignment.value(), std::nullopt, 1, MaxBytesToEmit); } +void MCAsmStreamer::emitPrefAlign(Align Alignment) { + OS << "\t.prefalign\t" << Alignment.value(); + EmitEOL(); +} + void MCAsmStreamer::emitValueToOffset(const MCExpr *Offset, unsigned char Value, SMLoc Loc) { diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp index 701a0836d2c70..573bb02f18984 100644 --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -677,6 +677,10 @@ void MCObjectStreamer::emitCodeAlignment(Align Alignment, F->STI = STI; } +void MCObjectStreamer::emitPrefAlign(Align Alignment) { + getCurrentSectionOnly()->ensurePreferredAlignment(Alignment); +} + void MCObjectStreamer::emitValueToOffset(const MCExpr *Offset, unsigned char Value, SMLoc Loc) { diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index acea3ab23680a..d64a3547375d6 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -418,6 +418,7 @@ class AsmParser : public MCAsmParser { DK_P2ALIGN, DK_P2ALIGNW, DK_P2ALIGNL, + DK_PREFALIGN, DK_ORG, DK_FILL, DK_ENDR, @@ -565,6 +566,7 @@ class AsmParser : public MCAsmParser { bool parseDirectiveOrg(); // ".org" // ".align{,32}", ".p2align{,w,l}" bool parseDirectiveAlign(bool IsPow2, uint8_t ValueSize); + bool parseDirectivePrefAlign(); // ".file", ".line", ".loc", ".loc_label", ".stabs" bool parseDirectiveFile(SMLoc DirectiveLoc); @@ -2002,6 +2004,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info, return parseDirectiveAlign(/*IsPow2=*/true, /*ExprSize=*/2); case DK_P2ALIGNL: return parseDirectiveAlign(/*IsPow2=*/true, /*ExprSize=*/4); + case DK_PREFALIGN: + return parseDirectivePrefAlign(); case DK_ORG: return parseDirectiveOrg(); case DK_FILL: @@ -3427,6 +3431,21 @@ bool AsmParser::parseDirectiveAlign(bool IsPow2, uint8_t ValueSize) { return ReturnVal; } +bool AsmParser::parseDirectivePrefAlign() { + SMLoc AlignmentLoc = getLexer().getLoc(); + int64_t Alignment; + if (checkForValidSection() || parseAbsoluteExpression(Alignment)) + return true; + if (parseEOL()) + return true; + + if (!isPowerOf2_64(Alignment)) + return Error(AlignmentLoc, "alignment must be a power of 2"); + getStreamer().emitPrefAlign(Align(Alignment)); + + return false; +} + /// parseDirectiveFile /// ::= .file filename /// ::= .file number [directory] filename [md5 checksum] [source source-text] @@ -5365,6 +5384,7 @@ void AsmParser::initializeDirectiveKindMap() { DirectiveKindMap[".p2align"] = DK_P2ALIGN; DirectiveKindMap[".p2alignw"] = DK_P2ALIGNW; DirectiveKindMap[".p2alignl"] = DK_P2ALIGNL; + DirectiveKindMap[".prefalign"] = DK_PREFALIGN; DirectiveKindMap[".org"] = DK_ORG; DirectiveKindMap[".fill"] = DK_FILL; DirectiveKindMap[".zero"] = DK_ZERO; diff --git a/llvm/lib/MC/MCSection.cpp b/llvm/lib/MC/MCSection.cpp index a668e7919b7b9..8285379eeaf81 100644 --- a/llvm/lib/MC/MCSection.cpp +++ b/llvm/lib/MC/MCSection.cpp @@ -30,6 +30,16 @@ MCSymbol *MCSection::getEndSymbol(MCContext &Ctx) { return End; } +Align MCSection::getAlignmentForObjectFile(uint64_t Size) const { + if (Size < getAlign().value()) + return getAlign(); + + if (Size < getPreferredAlignment().value()) + return Align(NextPowerOf2(Size - 1)); + + return getPreferredAlignment(); +} + bool MCSection::hasEnded() const { return End && End->isInSection(); } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index bc7398120096e..f33e4348dc9ad 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -1339,6 +1339,7 @@ void MCStreamer::emitFill(const MCExpr &NumBytes, uint64_t Value, SMLoc Loc) {} void MCStreamer::emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr, SMLoc Loc) {} void MCStreamer::emitValueToAlignment(Align, int64_t, uint8_t, unsigned) {} +void MCStreamer::emitPrefAlign(Align A) {} void MCStreamer::emitCodeAlignment(Align Alignment, const MCSubtargetInfo *STI, unsigned MaxBytesToEmit) {} void MCStreamer::emitValueToOffset(const MCExpr *Offset, unsigned char Value, diff --git a/llvm/test/MC/ELF/prefalign-errors.s b/llvm/test/MC/ELF/prefalign-errors.s new file mode 100644 index 0000000000000..363638f9bcb1e --- /dev/null +++ b/llvm/test/MC/ELF/prefalign-errors.s @@ -0,0 +1,5 @@ +// RUN: not llvm-mc -filetype=asm -triple x86_64-pc-linux-gnu %s -o - 2>&1 | FileCheck %s + +.section .text.f1,"ax",@progbits +// CHECK: error: alignment must be a power of 2 +.prefalign 3 diff --git a/llvm/test/MC/ELF/prefalign.s b/llvm/test/MC/ELF/prefalign.s new file mode 100644 index 0000000000000..0732b64292ab2 --- /dev/null +++ b/llvm/test/MC/ELF/prefalign.s @@ -0,0 +1,104 @@ +// RUN: llvm-mc -filetype=asm -triple x86_64-pc-linux-gnu %s -o - | FileCheck --check-prefix=ASM %s +// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readelf -SW - | FileCheck --check-prefix=OBJ %s + +// Minimum alignment >= preferred alignment, no effect on sh_addralign. +// ASM: .section .text.f1lt +// ASM: .p2align 2 +// ASM: .prefalign 2 +// OBJ: .text.f1lt PROGBITS 0000000000000000 000040 000003 00 AX 0 0 4 +.section .text.f1lt,"ax",@progbits +.p2align 2 +.prefalign 2 +.rept 3 +nop +.endr + +// ASM: .section .text.f1eq +// ASM: .p2align 2 +// ASM: .prefalign 2 +// OBJ: .text.f1eq PROGBITS 0000000000000000 000044 000004 00 AX 0 0 4 +.section .text.f1eq,"ax",@progbits +.p2align 2 +.prefalign 2 +.rept 4 +nop +.endr + +// ASM: .section .text.f1gt +// ASM: .p2align 2 +// ASM: .prefalign 2 +// OBJ: .text.f1gt PROGBITS 0000000000000000 000048 000005 00 AX 0 0 4 +.section .text.f1gt,"ax",@progbits +.p2align 2 +.prefalign 2 +.rept 5 +nop +.endr + +// Minimum alignment < preferred alignment, sh_addralign influenced by section size. +// Use maximum of all .prefalign directives. +// ASM: .section .text.f2lt +// ASM: .p2align 2 +// ASM: .prefalign 8 +// ASM: .prefalign 16 +// ASM: .prefalign 8 +// OBJ: .text.f2lt PROGBITS 0000000000000000 000050 000003 00 AX 0 0 4 +.section .text.f2lt,"ax",@progbits +.p2align 2 +.prefalign 8 +.prefalign 16 +.prefalign 8 +.rept 3 +nop +.endr + +// ASM: .section .text.f2between1 +// OBJ: .text.f2between1 PROGBITS 0000000000000000 000054 000008 00 AX 0 0 8 +.section .text.f2between1,"ax",@progbits +.p2align 2 +.prefalign 8 +.prefalign 16 +.prefalign 8 +.rept 8 +nop +.endr + +// OBJ: .text.f2between2 PROGBITS 0000000000000000 00005c 000009 00 AX 0 0 16 +.section .text.f2between2,"ax",@progbits +.p2align 2 +.prefalign 8 +.prefalign 16 +.prefalign 8 +.rept 9 +nop +.endr + +// OBJ: .text.f2between3 PROGBITS 0000000000000000 000068 000010 00 AX 0 0 16 +.section .text.f2between3,"ax",@progbits +.p2align 2 +.prefalign 8 +.prefalign 16 +.prefalign 8 +.rept 16 +nop +.endr + +// OBJ: .text.f2gt1 PROGBITS 0000000000000000 000078 000011 00 AX 0 0 16 +.section .text.f2gt1,"ax",@progbits +.p2align 2 +.prefalign 8 +.prefalign 16 +.prefalign 8 +.rept 17 +nop +.endr + +// OBJ: .text.f2gt2 PROGBITS 0000000000000000 00008c 000021 00 AX 0 0 16 +.section .text.f2gt2,"ax",@progbits +.p2align 2 +.prefalign 8 +.prefalign 16 +.prefalign 8 +.rept 33 +nop +.endr