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
