[llvm] [LFI][X86] Enable bundling for the LFI target (PR #214111)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 4 18:20:14 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-mc
Author: Zachary Yedidia (zyedidia)
<details>
<summary>Changes</summary>
This enables 32-byte bundling for the LFI x86-64 target, and uses bundle grouping for existing rewrites where appropriate.
---
Full diff: https://github.com/llvm/llvm-project/pull/214111.diff
7 Files Affected:
- (modified) llvm/docs/LFI.rst (+28-1)
- (modified) llvm/include/llvm/MC/MCLFI.h (+2)
- (modified) llvm/lib/MC/MCELFStreamer.cpp (+2)
- (modified) llvm/lib/MC/MCLFI.cpp (+10)
- (modified) llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp (+7)
- (modified) llvm/lib/Target/X86/MCTargetDesc/X86MCLFIRewriter.cpp (+5)
- (modified) llvm/test/MC/X86/LFI/syscall.s (+3-1)
``````````diff
diff --git a/llvm/docs/LFI.rst b/llvm/docs/LFI.rst
index 928079dd047de..094c488fe6e1b 100644
--- a/llvm/docs/LFI.rst
+++ b/llvm/docs/LFI.rst
@@ -562,6 +562,31 @@ The X86-64 LFI target reserves the following registers:
* ``r15``: context register (see `Context Register`_).
* ``r11``: scratch register.
+Bundling
+========
+
+The X86-64 LFI target confines control flow using 32-byte bundles. Indirect
+branch targets are masked to a 32-byte boundary, so control can only enter code
+at a bundle-aligned address. For this to constrain the instruction stream, no
+instruction may span a bundle boundary.
+
+LFI object files are therefore assembled with :doc:`aligned instruction
+bundling <AlignedBundling>` enabled, which inserts ``nop`` padding wherever an
+instruction would otherwise cross a boundary. Bundling is enabled implicitly
+for the ``x86_64_lfi`` target.
+
+A rewrite that expands one instruction into a sequence which must not be
+entered in the middle wraps that sequence in ``.bundle_lock`` /
+``.bundle_unlock``. This keeps the whole group within a single bundle, so no
+masked indirect branch can land between its instructions.
+
+**Note**: the masking of indirect branch targets is part of the control flow
+rewrites, which have not been implemented yet.
+
+Bundling is specific to X86-64. AArch64 instructions are fixed-width and
+naturally aligned, and the AArch64 LFI target confines indirect branches by
+guarding the target register instead.
+
Assembly Rewrites
=================
@@ -607,9 +632,11 @@ block below).
+-------------------+-------------------------------+
| .. code-block:: | .. code-block:: |
| | |
-| syscall | leaq .Ltmp(%rip), %r11 |
+| syscall | .bundle_lock |
+| | leaq .Ltmp(%rip), %r11 |
| | jmpq *-8(%r14) |
| | .Ltmp: |
+| | .bundle_unlock |
| | |
+-------------------+-------------------------------+
diff --git a/llvm/include/llvm/MC/MCLFI.h b/llvm/include/llvm/MC/MCLFI.h
index 4f941a16137ab..15176dedd8381 100644
--- a/llvm/include/llvm/MC/MCLFI.h
+++ b/llvm/include/llvm/MC/MCLFI.h
@@ -22,6 +22,8 @@ class Triple;
LLVM_ABI void initializeLFIMCStreamer(MCStreamer &Streamer, MCContext &Ctx,
const Triple &TheTriple);
+LLVM_ABI void emitLFIBundleAlign(MCStreamer &Streamer, MCContext &Ctx);
+
LLVM_ABI void emitLFINoteSection(MCStreamer &Streamer, MCContext &Ctx);
} // namespace llvm
diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp
index 03bbf4e81120e..efe08d7e09a84 100644
--- a/llvm/lib/MC/MCELFStreamer.cpp
+++ b/llvm/lib/MC/MCELFStreamer.cpp
@@ -55,6 +55,8 @@ void MCELFStreamer::initSections(const MCSubtargetInfo &STI) {
switchSection(Ctx.getObjectFileInfo()->getTextSection());
emitCodeAlignment(Align(Ctx.getObjectFileInfo()->getTextSectionAlignment()),
STI);
+ if (Ctx.getTargetTriple().isLFI())
+ emitLFIBundleAlign(*this, Ctx);
}
void MCELFStreamer::emitLabel(MCSymbol *S, SMLoc Loc) {
diff --git a/llvm/lib/MC/MCLFI.cpp b/llvm/lib/MC/MCLFI.cpp
index 7b149f4b53b4b..baa9e04bf808e 100644
--- a/llvm/lib/MC/MCLFI.cpp
+++ b/llvm/lib/MC/MCLFI.cpp
@@ -26,6 +26,8 @@
static const char NoteNamespace[] = "LFI";
+static constexpr unsigned X86BundleSize = 32;
+
namespace llvm {
cl::opt<bool> FlagEnableRewriting("lfi-enable-rewriter",
@@ -50,6 +52,14 @@ void initializeLFIMCStreamer(MCStreamer &Streamer, MCContext &Ctx,
}
}
+void emitLFIBundleAlign(MCStreamer &Streamer, MCContext &Ctx) {
+ const Triple &TheTriple = Ctx.getTargetTriple();
+ assert(TheTriple.isLFI());
+
+ if (TheTriple.getArch() == Triple::x86_64)
+ Streamer.emitBundleAlignMode(Align(X86BundleSize));
+}
+
void emitLFINoteSection(MCStreamer &Streamer, MCContext &Ctx) {
const Triple &TheTriple = Ctx.getTargetTriple();
assert(TheTriple.isLFI());
diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp
index 5d245f7f90cf3..acee4f524966a 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp
@@ -23,6 +23,7 @@
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCLFIRewriter.h"
#include "llvm/MC/MCObjectStreamer.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCRegisterInfo.h"
@@ -477,6 +478,12 @@ void X86_MC::emitInstruction(MCObjectStreamer &S, const MCInst &Inst,
return;
}
+ // Run the LFI rewriter outside of emitInstructionBegin/End so that nested
+ // instructions or bundle_lock/unlock directives do not corrupt the Begin/End
+ // bookkeeping for the original instruction.
+ if (S.getLFIRewriter() && S.getLFIRewriter()->rewriteInst(Inst, S, STI))
+ return;
+
auto &Backend = static_cast<X86AsmBackend &>(S.getAssembler().getBackend());
Backend.emitInstructionBegin(S, Inst, STI);
S.MCObjectStreamer::emitInstruction(Inst, STI);
diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86MCLFIRewriter.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86MCLFIRewriter.cpp
index ac1577ee4d0c7..bf199791ed212 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86MCLFIRewriter.cpp
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86MCLFIRewriter.cpp
@@ -71,11 +71,15 @@ static bool isGR64OrNone(MCRegister Reg) {
// syscall
// ->
+// .bundle_lock
// leaq .Ltmp(%rip), %r11
// jmpq *(%r14)
// .Ltmp:
+// .bundle_unlock
void X86::X86MCLFIRewriter::rewriteSyscall(const MCInst &Inst, MCStreamer &Out,
const MCSubtargetInfo &STI) {
+ Out.emitBundleLock(/*AlignToEnd=*/false, STI);
+
MCSymbol *Symbol = Out.getContext().createTempSymbol();
// leaq .Ltmp(%rip), %r11
@@ -101,6 +105,7 @@ void X86::X86MCLFIRewriter::rewriteSyscall(const MCInst &Inst, MCStreamer &Out,
Out.emitInstruction(Jmp, STI);
Out.emitLabel(Symbol);
+ Out.emitBundleUnlock(STI);
}
// Emit: movq TPOffset(%r15), %Reg
diff --git a/llvm/test/MC/X86/LFI/syscall.s b/llvm/test/MC/X86/LFI/syscall.s
index b184bd481df17..7b47bfe96f8ff 100644
--- a/llvm/test/MC/X86/LFI/syscall.s
+++ b/llvm/test/MC/X86/LFI/syscall.s
@@ -1,6 +1,8 @@
// RUN: llvm-mc -triple x86_64_lfi %s | FileCheck %s
syscall
-// CHECK: leaq .Ltmp0(%rip), %r11
+// CHECK: .bundle_lock
+// CHECK-NEXT: leaq .Ltmp0(%rip), %r11
// CHECK-NEXT: jmpq *-8(%r14)
// CHECK-NEXT: .Ltmp0:
+// CHECK-NEXT: .bundle_unlock
``````````
</details>
https://github.com/llvm/llvm-project/pull/214111
More information about the llvm-commits
mailing list