-
Notifications
You must be signed in to change notification settings - Fork 14.8k
MC: Add ELF section and directive for specifying a section's preferred alignment. #150151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: users/pcc/spr/main.mc-add-elf-section-and-directive-for-specifying-a-sections-preferred-alignment
Are you sure you want to change the base?
Conversation
Created using spr 1.3.6-beta.1
@llvm/pr-subscribers-llvm-binary-utilities @llvm/pr-subscribers-mc Author: Peter Collingbourne (pcc) ChangesThe new The new asm directive: .prefalign n specifies that the preferred alignment of the current section is Full diff: https://github.com/llvm/llvm-project/pull/150151.diff 13 Files Affected:
diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst
index d8fb87b6998ad..e6b2db0745623 100644
--- a/llvm/docs/Extensions.rst
+++ b/llvm/docs/Extensions.rst
@@ -601,6 +601,28 @@ sees fit (generally the section that would provide the best locality).
.. _CFI jump table: https://clang.llvm.org/docs/ControlFlowIntegrityDesign.html#forward-edge-cfi-for-indirect-function-calls
+``SHT_LLVM_MIN_ADDRALIGN`` Section (minimum section alignment)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This section is used to specify the minimum alignment of a section
+where that differs from its preferred alignment. Its ``sh_link``
+field identifies the section whose alignment is being specified, its
+``sh_addralign`` field specifies the linked section's minimum alignment
+and the ``sh_addralign`` field of the linked section's section header
+specifies its preferred alignment. This section has the ``SHF_EXCLUDE``
+flag so that it is stripped from the final executable or shared library,
+and the ``SHF_LINK_ORDER`` flag so that the ``sh_link`` field is updated
+by tools such as ``ld -r`` and ``objcopy``. The contents of the section
+must be empty.
+
+.. code-block:: gas
+
+ .prefalign n
+
+Specifies that the preferred alignment of the current section is
+determined by taking the maximum of ``n`` and the section's minimum
+alignment, and causes an ``SHT_LLVM_MIN_ADDRALIGN`` section to be emitted
+if necessary.
+
CodeView-Dependent
------------------
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index e4f82ad96a084..95600f39153d4 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1160,6 +1160,7 @@ enum : unsigned {
SHT_LLVM_LTO = 0x6fff4c0c, // .llvm.lto for fat LTO.
SHT_LLVM_JT_SIZES = 0x6fff4c0d, // LLVM jump tables sizes.
SHT_LLVM_CFI_JUMP_TABLE = 0x6fff4c0e, // LLVM CFI jump table.
+ SHT_LLVM_MIN_ADDRALIGN = 0x6fff4c0f, // Minimum alignment specification.
// Android's experimental support for SHT_RELR sections.
// https://android.googlesource.com/platform/bionic/+/b7feec74547f84559a1467aca02708ff61346d2a/libc/include/elf.h#512
SHT_ANDROID_RELR = 0x6fffff00, // Relocation entries; only offsets.
diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h
index 319e131999d48..27de57f2ccd91 100644
--- a/llvm/include/llvm/MC/MCObjectStreamer.h
+++ b/llvm/include/llvm/MC/MCObjectStreamer.h
@@ -113,6 +113,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 313071ec75033..61ae6dff4d146 100644
--- a/llvm/include/llvm/MC/MCSection.h
+++ b/llvm/include/llvm/MC/MCSection.h
@@ -82,6 +82,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;
@@ -147,6 +148,17 @@ 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;
+ }
+
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 4b91dbc794682..e7947657e1356 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -832,6 +832,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 9f52b3e3e85c0..7709cf56ffd32 100644
--- a/llvm/lib/MC/ELFObjectWriter.cpp
+++ b/llvm/lib/MC/ELFObjectWriter.cpp
@@ -926,10 +926,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.getPreferredAlignment(), Section.getEntrySize());
}
void ELFWriter::writeSectionHeaders() {
@@ -1062,6 +1062,15 @@ uint64_t ELFWriter::writeObject() {
Relocations.push_back(RelSection);
}
+ if (Sec.getPreferredAlignment() != Sec.getAlign()) {
+ MCSectionELF *MinAlign = Ctx.getELFSection(
+ ".llvm.minalign", ELF::SHT_LLVM_MIN_ADDRALIGN,
+ ELF::SHF_EXCLUDE | ELF::SHF_LINK_ORDER, 0, "", false, 0,
+ cast<MCSymbolELF>(Section.getBeginSymbol()));
+ MinAlign->setOrdinal(addToSectionTable(MinAlign));
+ MinAlign->setAlignment(Sec.getAlign());
+ }
+
if (GroupIdxEntry) {
auto &Members = Groups[GroupMap[*GroupIdxEntry]];
Members.second.push_back(Section.getOrdinal());
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 67c53e01a6111..4d53607400a27 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -275,6 +275,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 << ".prefalign " << 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 d5b8f22463894..e860f6c61c5f7 100644
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -567,6 +567,10 @@ void MCObjectStreamer::emitCodeAlignment(Align Alignment,
}
}
+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 77bf84364c5a3..2e85c08852c15 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);
@@ -2000,6 +2002,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:
@@ -3426,6 +3430,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]
@@ -5377,6 +5396,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/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp
index c3ecf8fc717f5..eeca414da580d 100644
--- a/llvm/lib/MC/MCStreamer.cpp
+++ b/llvm/lib/MC/MCStreamer.cpp
@@ -1329,6 +1329,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/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index 788c6020a7f99..ab23a0b1f25b9 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -322,6 +322,7 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_LTO);
STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_JT_SIZES)
STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_CFI_JUMP_TABLE)
+ STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_MIN_ADDRALIGN)
STRINGIFY_ENUM_CASE(ELF, SHT_GNU_SFRAME);
STRINGIFY_ENUM_CASE(ELF, SHT_GNU_ATTRIBUTES);
STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH);
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..f3537029b23c1
--- /dev/null
+++ b/llvm/test/MC/ELF/prefalign.s
@@ -0,0 +1,47 @@
+// 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 SHT_LLVM_MIN_ADDRALIGN needed.
+// ASM: .section .text.f1
+// ASM: .p2align 2
+// ASM: .prefalign 4
+// OBJ: .text.f1
+// OBJ-NOT: .llvm.minalign
+.section .text.f1,"ax",@progbits
+.p2align 2
+.prefalign 4
+
+// Minimum alignment < preferred alignment, SHT_LLVM_MIN_ADDRALIGN emitted.
+// ASM: .section .text.f2
+// ASM: .p2align 2
+// ASM: .prefalign 8
+// OBJ: [ 4] .text.f2 PROGBITS 0000000000000000 000040 000000 00 AX 0 0 8
+// OBJ: [ 5] .llvm.minalign LLVM_MIN_ADDRALIGN 0000000000000000 000000 000000 00 LE 4 0 4
+.section .text.f2,"ax",@progbits
+.p2align 2
+.prefalign 8
+
+// Minimum alignment > preferred alignment, preferred alignment rounded up to
+// minimum alignment. No SHT_LLVM_MIN_ADDRALIGN emitted.
+// ASM: .section .text.f3
+// ASM: .p2align 3
+// ASM: .prefalign 4
+// OBJ: .text.f3
+// OBJ-NOT: .llvm.minalign
+.section .text.f3,"ax",@progbits
+.p2align 3
+.prefalign 4
+
+// Maximum of all .prefalign directives written to object file.
+// ASM: .section .text.f4
+// ASM: .p2align 2
+// ASM: .prefalign 8
+// ASM: .prefalign 16
+// ASM: .prefalign 8
+// OBJ: [ 7] .text.f4 PROGBITS 0000000000000000 000040 000000 00 AX 0 0 16
+// OBJ: [ 8] .llvm.minalign LLVM_MIN_ADDRALIGN 0000000000000000 000000 000000 00 LE 7 0 4
+.section .text.f4,"ax",@progbits
+.p2align 2
+.prefalign 8
+.prefalign 16
+.prefalign 8
|
You can test this locally with the following command:git-clang-format --diff HEAD~1 HEAD --extensions cpp,h -- llvm/include/llvm/BinaryFormat/ELF.h llvm/include/llvm/MC/MCObjectStreamer.h llvm/include/llvm/MC/MCSection.h llvm/include/llvm/MC/MCStreamer.h llvm/lib/MC/ELFObjectWriter.cpp llvm/lib/MC/MCAsmStreamer.cpp llvm/lib/MC/MCObjectStreamer.cpp llvm/lib/MC/MCParser/AsmParser.cpp llvm/lib/MC/MCStreamer.cpp llvm/lib/Object/ELF.cpp View the diff from clang-format here.diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index e7947657e..1f9ee1c5f 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -833,7 +833,7 @@ public:
unsigned MaxBytesToEmit = 0);
virtual void emitPrefAlign(Align A);
-
+
/// Emit some number of copies of \p Value until the byte offset \p
/// Offset is reached.
///
|
There are some discussions on a codegen patch #149444 that I did not follow. Could you summarize the motivation in this patch's description? |
Ping, any thoughts on this approach? |
The notion of having a section's preferred function alignment be separate
from the minimum function alignment is useful in combination with CFI
jump table relaxation (#147424) as it allows functions to be well aligned
in the usual case with the associated performance benefits while giving
the linker permission to move functions into a lesser-aligned jump table
entry when that is the right tradeoff for the best performance. As noted
in #147424, jump table relaxation has been measured to reduce
the overhead of CFI in a large realistic internal Google benchmark
by between 0.2 and 0.5 percentage points, or 10-25%, depending on the
microarchitecture.
The new
SHT_LLVM_MIN_ADDRALIGN
section is used to specify theminimum alignment of a section where that differs from its preferred
alignment. Its
sh_link
field identifies the section whose alignment isbeing specified, its
sh_addralign
field specifies the linked section'sminimum alignment and the
sh_addralign
field of the linked section'ssection header specifies its preferred alignment. This section has the
SHF_EXCLUDE
flag so that it is stripped from the final executable orshared library, and the
SHF_LINK_ORDER
flag so that thesh_link
field is updated by tools such as
ld -r
andobjcopy
. The contentsof the section must be empty.
The new asm directive:
.prefalign n
specifies that the preferred alignment of the current section is
determined by taking the maximum of
n
and the section's minimumalignment, and causes an
SHT_LLVM_MIN_ADDRALIGN
section to be emittedif necessary.