[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