https://github.com/resistor updated 
https://github.com/llvm/llvm-project/pull/203872

>From e2e2315068751bdd243c48d043579f9e772a857a Mon Sep 17 00:00:00 2001
From: Owen Anderson <[email protected]>
Date: Wed, 10 Jun 2026 14:53:59 +0200
Subject: [PATCH] [CHERI][MTE][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 AArch64/MTE, and for 
RISCV Y-base and XCheriot. Support for other targets (such as AArch64/Morello) 
but is left as future work here.

Based on https://github.com/llvm/llvm-project/pull/121957

Co-authored-by: Florian Mayer <[email protected]>
---
 clang/test/CodeGen/memtag-globals-asm.cpp     | 54 +++++++++--------
 llvm/include/llvm/CodeGen/AsmPrinter.h        | 14 +++++
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp    | 60 +++++++------------
 llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp |  7 +++
 llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp     |  3 +-
 llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp     | 21 +++++++
 .../test/CodeGen/RISCV/cheri-global-bounds.ll | 26 ++++++++
 llvm/test/MC/AArch64/global-tagging.ll        |  4 +-
 8 files changed, 122 insertions(+), 67 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/cheri-global-bounds.ll

diff --git a/clang/test/CodeGen/memtag-globals-asm.cpp 
b/clang/test/CodeGen/memtag-globals-asm.cpp
index fb3958dd8bcb6..28fcba9adf315 100644
--- a/clang/test/CodeGen/memtag-globals-asm.cpp
+++ b/clang/test/CodeGen/memtag-globals-asm.cpp
@@ -53,12 +53,12 @@
 // CHECK-A: .memtag global_int
 // CHECK-A: .globl global_int
 // CHECK-A: .p2align 4, 0x0
-// CHECK-A: .zero 16
-// CHECK-A: .size global_int, 16
+// CHECK-A: .size global_int, 4
+// CHECK-A: .p2align 4, 0x0
 int global_int;
 // CHECK-B: .memtag _ZL9local_int
 // CHECK-B: .local _ZL9local_int
-// CHECK-B: .comm _ZL9local_int,16,16
+// CHECK-B: .comm _ZL9local_int,4,16
 static int local_int;
 
 // CHECK-C: .memtag _ZL12local_buffer
@@ -69,16 +69,16 @@ static char local_buffer[16];
 // CHECK-D: .p2align 4, 0x0
 // CHECK-D: _ZL22local_buffer_local_end:
 // CHECK-D: .xword _ZL12local_buffer+16
-// CHECK-D: .zero 8
-// CHECK-D: .size _ZL22local_buffer_local_end, 16
+// CHECK-D: .size _ZL22local_buffer_local_end, 8
+// CHECK-D: .p2align 4, 0x0
 static char* local_buffer_local_end = &local_buffer[16];
 // CHECK-E: .memtag local_buffer_global_end
 // CHECK-E: .globl local_buffer_global_end
 // CHECK-E  .p2align 4, 0x0
 // CHECK-E: local_buffer_global_end:
 // CHECK-E: .xword _ZL12local_buffer+16
-// CHECK-E: .zero 8
-// CHECK-E: .size local_buffer_global_end, 16
+// CHECK-E: .size local_buffer_global_end, 8
+// CHECK-E: .p2align 4, 0x0
 char* local_buffer_global_end = &local_buffer[16];
 
 // CHECK-F: .memtag global_buffer
@@ -91,14 +91,15 @@ char global_buffer[16];
 // CHECK-G: .p2align 4, 0x0
 // CHECK-G: _ZL23global_buffer_local_end:
 // CHECK-G: .xword global_buffer+16
-// CHECK-G: .zero 8
-// CHECK-G: .size _ZL23global_buffer_local_end, 16
+// CHECK-G: .size _ZL23global_buffer_local_end, 8
+// CHECK-F: .p2align 4, 0x0
 static char* global_buffer_local_end = &global_buffer[16];
 // CHECK-H: .memtag global_buffer_global_end
 // CHECK-H: .p2align 4, 0x0
 // CHECK-H: global_buffer_global_end:
 // CHECK-H: .xword global_buffer+16
-// CHECK-H: .size global_buffer_global_end, 16
+// CHECK-H: .size global_buffer_global_end, 8
+// CHECK-H: .p2align 4, 0x0
 char* global_buffer_global_end = &global_buffer[16];
 
 // CHECK-S-NOT: .memtag zero_sized
@@ -115,8 +116,8 @@ class MyClass {
 // CHECK-I: .memtag _ZN7MyClass12my_class_intE
 // CHECK-I: .globl _ZN7MyClass12my_class_intE
 // CHECK-I: .p2align 4, 0x0
-// CHECK-I: .zero 16
-// CHECK-I: .size _ZN7MyClass12my_class_intE, 16
+// CHECK-I: .size _ZN7MyClass12my_class_intE, 4
+// CHECK-I: .p2align 4, 0x0
 int MyClass::my_class_int;
 // CHECK-NOT: .memtag _ZN7MyClass18my_class_const_intE
 const int MyClass::my_class_const_int = 1;
@@ -124,32 +125,33 @@ const int MyClass::my_class_const_int = 1;
 // CHECK-J: .memtag global_my_class
 // CHECK-J: .globl global_my_class
 // CHECK-J: .p2align 4, 0x0
-// CHECK-J: .zero 8
-// CHECK-J: .size global_my_class, 16
+// CHECK-J: .size global_my_class, 8
+// CHECK-J: .p2align 4, 0x0
 MyClass global_my_class;
 // CHECK-K: .memtag _ZL14local_my_class
 // CHECK-K: .p2align 4, 0x0
-// CHECK-K: .zero 8
-// CHECK-K: .size _ZL14local_my_class, 16
+// CHECK-K: .size _ZL14local_my_class, 8
+// CHECK-K: .p2align 4, 0x0
 static MyClass local_my_class;
 
 // CHECK-NOT: .memtag _ZL18local_const_string
 static const char local_const_string[] = "this is a local string";
 // CHECK-L: .memtag _ZL12local_string
 // CHECK-L: .p2align 4, 0x0
-// CHECK-L: .zero 9
-// CHECK-L: .size _ZL12local_string, 32
+// CHECK-L: .size _ZL12local_string, 23
+// CHECK-L: .p2align 4, 0x0
 static char local_string[] = "this is a local string";
 
 // CHECK-M: .memtag global_atomic_int
 // CHECK-M: .globl global_atomic_int
 // CHECK-M: .p2align 4, 0x0
-// CHECK-M: .zero 16
-// CHECK-M: .size global_atomic_int, 16
+// CHECK-M: .size global_atomic_int, 4
+// CHECK-M: .p2align 4, 0x0
 _Atomic(int) global_atomic_int;
 // CHECK-N: .memtag _ZL16local_atomic_int
 // CHECK-N: .local _ZL16local_atomic_int
-// CHECK-N: .comm _ZL16local_atomic_int,16,16
+// CHECK-N: .comm _ZL16local_atomic_int,4,16
+// CHECK-N: .p2align 4, 0x0
 static _Atomic(int) local_atomic_int;
 
 union MyUnion {
@@ -160,12 +162,13 @@ union MyUnion {
 // CHECK-O: .memtag global_union
 // CHECK-O: .globl global_union
 // CHECK-O: .p2align 4, 0x0
-// CHECK-O: .zero 16
-// CHECK-O: .size global_union, 16
+// CHECK-O: .size global_union, 4
+// CHECK-O: .p2align 4, 0x0
 MyUnion global_union;
 // CHECK-P: .memtag _ZL11local_union
 // CHECK-P: .local _ZL11local_union
-// CHECK-P: .comm _ZL11local_union,16,16
+// CHECK-P: .comm _ZL11local_union,4,16
+// CHECK-P: .p2align 4, 0x0
 static MyUnion local_union;
 
 // CHECK-NOT: .memtag {{.*}}global_tls
@@ -197,7 +200,8 @@ const void* export_pointers(int c) {
 int f(int x) {
   // CHECK-R: .memtag _ZZ1fiE12function_int
   // CHECK-R: .local _ZZ1fiE12function_int
-  // CHECK-R: .comm _ZZ1fiE12function_int,16,16
+  // CHECK-R: .comm _ZZ1fiE12function_int,4,16
+  // CHECK-R: .p2align 4, 0x0
   static int function_int = 0;
   /// Prevent non-const `f` from being promoted to a constant and inlined.
   function_int += x;
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h 
b/llvm/include/llvm/CodeGen/AsmPrinter.h
index f6f0f7348836e..3692404ce133c 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);
@@ -1008,6 +1013,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 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 aedc4956f1bd8..dd4a9ec51999e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -783,6 +783,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");
@@ -848,7 +856,16 @@ 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());
+    if (Alignment < *AlignmentGranule)
+      Alignment = *AlignmentGranule;
+  }
 
   for (auto &Handler : Handlers)
     Handler->setSymbolSize(GVSym, Size);
@@ -925,8 +942,7 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable 
*GV) {
       emitAlignment(Alignment, GV);
       OutStreamer->emitLabel(MangSym);
 
-      emitGlobalConstant(GV->getDataLayout(),
-                         GV->getInitializer());
+      emitGlobalConstant(GV->getDataLayout(), GV->getInitializer());
     }
 
     OutStreamer->addBlankLine();
@@ -2837,37 +2853,6 @@ static bool shouldTagGlobal(const llvm::GlobalVariable 
&G) {
   return globalSize(G) > 0;
 }
 
-static void tagGlobalDefinition(Module &M, GlobalVariable *G) {
-  uint64_t SizeInBytes = globalSize(*G);
-
-  uint64_t NewSize = alignTo(SizeInBytes, 16);
-  if (SizeInBytes != NewSize) {
-    // Pad the initializer out to the next multiple of 16 bytes.
-    llvm::SmallVector<uint8_t> Init(NewSize - SizeInBytes, 0);
-    Constant *Padding = ConstantDataArray::get(M.getContext(), Init);
-    Constant *Initializer = G->getInitializer();
-    Initializer = ConstantStruct::getAnon({Initializer, Padding});
-    auto *NewGV = new GlobalVariable(
-        M, Initializer->getType(), G->isConstant(), G->getLinkage(),
-        Initializer, "", G, G->getThreadLocalMode(), G->getAddressSpace());
-    NewGV->copyAttributesFrom(G);
-    NewGV->setComdat(G->getComdat());
-    NewGV->copyMetadata(G, 0);
-
-    NewGV->takeName(G);
-    G->replaceAllUsesWith(NewGV);
-    G->eraseFromParent();
-    G = NewGV;
-  }
-
-  if (G->getAlign().valueOrOne() < 16)
-    G->setAlignment(Align(16));
-
-  // Ensure that tagged globals don't get merged by ICF - as they should have
-  // different tags at runtime.
-  G->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
-}
-
 static void removeMemtagFromGlobal(GlobalVariable &G) {
   auto Meta = G.getSanitizerMetadata();
   Meta.Memtag = false;
@@ -2881,7 +2866,6 @@ bool AsmPrinter::doFinalization(Module &M) {
   MF = nullptr;
   const Triple &Target = TM.getTargetTriple();
 
-  std::vector<GlobalVariable *> GlobalsToTag;
   for (GlobalVariable &G : M.globals()) {
     if (G.isDeclaration() || !G.isTagged())
       continue;
@@ -2891,10 +2875,10 @@ bool AsmPrinter::doFinalization(Module &M) {
       assert(!G.isTagged());
       continue;
     }
-    GlobalsToTag.push_back(&G);
+    // Ensure that tagged globals don't get merged by ICF - as they should have
+    // different tags at runtime.
+    G.setUnnamedAddr(GlobalValue::UnnamedAddr::None);
   }
-  for (GlobalVariable *G : GlobalsToTag)
-    tagGlobalDefinition(M, G);
 
   // Gather all GOT equivalent globals in the module. We really need two
   // passes over the globals: one to compute and another to avoid its emission
diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp 
b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 156c12c5ab8dd..7206bc464c022 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -347,6 +347,8 @@ class AArch64AsmPrinter : public AsmPrinter {
 
   void emitFunctionBodyEnd() override;
   void emitGlobalAlias(const Module &M, const GlobalAlias &GA) override;
+  MaybeAlign
+  getRequiredGlobalAlignmentGranule(const GlobalVariable &GV) override;
 
   MCSymbol *GetCPISymbol(unsigned CPID) const override;
   void emitEndOfAsmFile(Module &M) override;
@@ -4031,6 +4033,11 @@ const MCExpr *AArch64AsmPrinter::lowerConstant(const 
Constant *CV,
   return AsmPrinter::lowerConstant(CV, BaseCV, Offset);
 }
 
+MaybeAlign
+AArch64AsmPrinter::getRequiredGlobalAlignmentGranule(const GlobalVariable &GV) 
{
+  return GV.isTagged() ? MaybeAlign(16) : std::nullopt;
+}
+
 char AArch64AsmPrinter::ID = 0;
 
 INITIALIZE_PASS(AArch64AsmPrinter, "aarch64-asm-printer",
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp 
b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 51f2ff4a68be7..55691d2d694fa 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -2880,8 +2880,7 @@ void PPCAIXAsmPrinter::emitGlobalVariableHelper(const 
GlobalVariable *GV) {
     AliasList[getAliasOffset(GA->getAliasee())].push_back(GA);
 
   // Emit alias label and element value for global variable.
-  emitGlobalConstant(GV->getDataLayout(), GV->getInitializer(),
-                     &AliasList);
+  emitGlobalConstant(GV->getDataLayout(), GV->getInitializer(), &AliasList);
 }
 
 void PPCAIXAsmPrinter::emitFunctionDescriptor() {
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp 
b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index dbda25c34cee0..8ad775db18197 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -39,6 +39,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"
@@ -131,6 +132,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
 
@@ -1315,6 +1319,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..4f0df7bbe20ec
--- /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
+
+@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, 6995
+; RVY32:  .p2align 7, 0x0
+
+; RVY64-LABEL: .globl global1
+; RVY64-NEXT:  .p2align 3, 0x0
+; RVY64-NEXT:  global1:
+; RVY64-NEXT:  .zero 6995
+; RVY64-NEXT:  .size global1, 6995
+; RVY64:  .p2align 3, 0x0
+
+; CHERIOT-LABEL: .globl global1
+; CHERIOT-NEXT:  .p2align 4, 0x0
+; CHERIOT-NEXT:  global1:
+; CHERIOT-NEXT:  .zero 6995
+; CHERIOT-NEXT:  .size global1, 6995
+; CHERIOT:  .p2align 4, 0x0
diff --git a/llvm/test/MC/AArch64/global-tagging.ll 
b/llvm/test/MC/AArch64/global-tagging.ll
index 29463b1b86ff4..19f4bd2a58596 100644
--- a/llvm/test/MC/AArch64/global-tagging.ll
+++ b/llvm/test/MC/AArch64/global-tagging.ll
@@ -47,10 +47,10 @@
 
 ; CHECK-ASM: .memtag internal_four
 ; CHECK-ASM .p2align        4
-; CHECK-ASM: .size   internal_four, 16
+; CHECK-ASM: .size   internal_four, 4
 ; CHECK-ASM: .memtag four
 ; CHECK-ASM .p2align        4
-; CHECK-ASM: .size   four, 16
+; CHECK-ASM: .size   four, 4
 ; CHECK-ASM: .memtag sixteen
 ; CHECK-ASM .p2align        4
 ; CHECK-ASM: .size   sixteen, 16

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to