[llvm] [CHERI][RISCV][MC] Add shared support for aligning globals to an "alignment granule", which is applied to both the alignment and the allocated size. (PR #203872)
Owen Anderson via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 20 11:27:17 PDT 2026
https://github.com/resistor updated https://github.com/llvm/llvm-project/pull/203872
>From 03f9bac007d70ae55e7234c5bd966931f9bbf7a5 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Wed, 10 Jun 2026 14:53:59 +0200
Subject: [PATCH] [CHERI][RISCV][MC] Add shared support for aligning globals to
an "alignment granule", which is applied to both the alignment and the
allocated size.
On CHERI targets, bounds are generally stored in a compressed format which imposes alignment requirements based on allocation size. When emitting globals, we need to align and tail-pad them as appropriate to ensure that the pointer to the global will have bounds that are precise, i.e. not covering any non-padding bytes that either precede or follow it.
While this patch adds the plumbing necessary to support this feature on arbitrary targets, it only concretely implements it for RISCV Y-base and XCheriot. Support for other targets (such as AArch64/MTE and AArch64/Morello) is left as future work here.
Based on https://github.com/llvm/llvm-project/pull/121957
Co-authored-by: Florian Mayer <fmayer at google.com>
---
llvm/include/llvm/CodeGen/AsmPrinter.h | 14 ++++++++++
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 20 +++++++++++++-
llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 21 +++++++++++++++
.../test/CodeGen/RISCV/cheri-global-bounds.ll | 26 +++++++++++++++++++
4 files changed, 80 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/CodeGen/RISCV/cheri-global-bounds.ll
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 335bf9de9a071..f6c19280030a9 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -534,6 +534,11 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
/// Emit the specified global variable to the .s file.
virtual void emitGlobalVariable(const GlobalVariable *GV);
+ /// Emit the specified global variable to the .s file, with an explicit
+ /// alignment granule applied to both address and size.
+ virtual void emitGlobalVariable(const GlobalVariable *GV,
+ MaybeAlign AlignmentGranule);
+
/// Check to see if the specified global is a special global used by LLVM. If
/// so, emit it and return true, otherwise do nothing and return false.
bool emitSpecialLLVMGlobal(const GlobalVariable *GV);
@@ -1021,6 +1026,15 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
virtual bool shouldEmitWeakSwiftAsyncExtendedFramePointerFlags() const {
return false;
}
+
+ /// Returns a optional minimum alignment that applies to both the address and
+ /// the allocation size of the global. This is used for systems like CHERI and
+ /// MTE that impose a minimum alignment, and require globals to be padded to
+ /// that alignment.
+ virtual MaybeAlign
+ getRequiredGlobalAlignmentGranule(const GlobalVariable &GV) {
+ return std::nullopt;
+ };
};
LLVM_ABI void setupModuleAsmPrinter(Module &M, ModuleAnalysisManager &MAM,
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 65be7a7846a71..cbd8f1932a285 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -788,6 +788,14 @@ MCSymbol *AsmPrinter::getSymbolPreferLocal(const GlobalValue &GV) const {
/// EmitGlobalVariable - Emit the specified global variable to the .s file.
void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
+ MaybeAlign AlignmentGranule = getRequiredGlobalAlignmentGranule(*GV);
+ emitGlobalVariable(GV, AlignmentGranule);
+ if (AlignmentGranule)
+ OutStreamer->emitValueToAlignment(*AlignmentGranule);
+}
+
+void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV,
+ MaybeAlign AlignmentGranule) {
bool IsEmuTLSVar = TM.useEmulatedTLS() && GV->isThreadLocal();
assert(!(IsEmuTLSVar && GV->hasCommonLinkage()) &&
"No emulated TLS variables in the common section");
@@ -853,7 +861,17 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
// If the alignment is specified, we *must* obey it. Overaligning a global
// with a specified alignment is a prompt way to break globals emitted to
// sections and expected to be contiguous (e.g. ObjC metadata).
- const Align Alignment = getGVAlignment(GV, DL);
+ //
+ // If we get passed in an explicit alignment granule, it is up to the caller
+ // to ensure that is not the case (i.e. that the GV is not in a section).
+ Align Alignment = getGVAlignment(GV, DL);
+
+ if (AlignmentGranule) {
+ assert(!GV->hasSection());
+ Size = alignTo(Size, *AlignmentGranule);
+ if (Alignment < *AlignmentGranule)
+ Alignment = *AlignmentGranule;
+ }
for (auto &Handler : Handlers)
Handler->setSymbolSize(GVSym, Size);
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index 6dee7fb37ef4c..a0a42ea1fbb8f 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -42,6 +42,7 @@
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CHERICapabilityFormat.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/RISCVISAInfo.h"
@@ -135,6 +136,9 @@ class RISCVAsmPrinter : public AsmPrinter {
void emitSled(const MachineInstr *MI, SledKind Kind);
void lowerToMCInst(const MachineInstr *MI, MCInst &OutMI);
+
+ MaybeAlign
+ getRequiredGlobalAlignmentGranule(const GlobalVariable &GV) override;
};
} // namespace
@@ -1323,6 +1327,23 @@ void RISCVAsmPrinter::emitMachineConstantPoolValue(
OutStreamer->emitValue(Expr, Size);
}
+MaybeAlign
+RISCVAsmPrinter::getRequiredGlobalAlignmentGranule(const GlobalVariable &GV) {
+ const MCSubtargetInfo &MCSTI = TM.getMCSubtargetInfo();
+ uint64_t Size = GV.getGlobalSize(getDataLayout());
+ if (MCSTI.hasFeature(RISCV::FeatureVendorXCheriot))
+ return CHERIoTCapabilityFormat::getRequiredAlignment(Size);
+
+ if (MCSTI.hasFeature(RISCV::FeatureStdExtY)) {
+ if (MCSTI.hasFeature(RISCV::Feature64Bit))
+ return RV64YCapabilityFormat::getRequiredAlignment(Size);
+ else
+ return RV32YCapabilityFormat::getRequiredAlignment(Size);
+ }
+
+ return std::nullopt;
+}
+
char RISCVAsmPrinter::ID = 0;
INITIALIZE_PASS(RISCVAsmPrinter, "riscv-asm-printer", "RISC-V Assembly Printer",
diff --git a/llvm/test/CodeGen/RISCV/cheri-global-bounds.ll b/llvm/test/CodeGen/RISCV/cheri-global-bounds.ll
new file mode 100644
index 0000000000000..0443338855e62
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/cheri-global-bounds.ll
@@ -0,0 +1,26 @@
+; RUN: llc -mtriple riscv32 -mattr=+experimental-y -target-abi il32pc64 %s -o - | FileCheck -check-prefix=RVY32 %s
+; RUN: llc -mtriple riscv64 -mattr=+experimental-y -target-abi l64pc128 %s -o - | FileCheck -check-prefix=RVY64 %s
+; RUN: llc -mtriple riscv64 -mattr=+xcheriot -target-abi cheriot %s -o - | FileCheck -check-prefix=CHERIOT %s
+
+ at global1 = global [6995 x i8] zeroinitializer, align 1
+
+; RVY32-LABEL: .globl global1
+; RVY32-NEXT: .p2align 7, 0x0
+; RVY32-NEXT: global1:
+; RVY32-NEXT: .zero 6995
+; RVY32-NEXT: .size global1, 7040
+; RVY32: .p2align 7, 0x0
+
+; RVY64-LABEL: .globl global1
+; RVY64-NEXT: .p2align 3, 0x0
+; RVY64-NEXT: global1:
+; RVY64-NEXT: .zero 6995
+; RVY64-NEXT: .size global1, 7000
+; RVY64: .p2align 3, 0x0
+
+; CHERIOT-LABEL: .globl global1
+; CHERIOT-NEXT: .p2align 4, 0x0
+; CHERIOT-NEXT: global1:
+; CHERIOT-NEXT: .zero 6995
+; CHERIOT-NEXT: .size global1, 7008
+; CHERIOT: .p2align 4, 0x0
More information about the llvm-commits
mailing list