https://github.com/steffenlarsen created 
https://github.com/llvm/llvm-project/pull/214135

To avoid the size restriction that may be imposed by the address spaces of the 
target, this patch changes the embedding of the Clang AST from placing it 
inside a global variable to placing it inside a metadata node. This is done by 
introducing a new metadata node, namely `llvm.raw.sections`, which refers to 
nodes comprised of a section name, an alignment and the section data. The 
AsmPrinter lowers this to the corresponding sections.

Assisted-by: Claude Opus 4.6

>From 8af2e41c4def005000f304343c171946f598eacb Mon Sep 17 00:00:00 2001
From: Steffen Holst Larsen <[email protected]>
Date: Tue, 4 Aug 2026 01:04:45 -0500
Subject: [PATCH] [Clang][PCH] Use metadata for embedding AST

To avoid the size restriction that may be imposed by the address spaces
of the target, this patch changes the embedding of the Clang AST from
placing it inside a global variable to placing it inside a metadata
node. This is done by introducing a new metadata node, namely
`llvm.raw.sections`, which refers to nodes comprised of a section name,
an alignment and the section data. The AsmPrinter lowers this to the
corresponding sections.

Assisted-by: Claude Opus 4.6

Signed-off-by: Steffen Holst Larsen <[email protected]>
---
 .../CodeGen/ObjectFilePCHContainerWriter.cpp  | 34 ++++++++++---------
 clang/test/Modules/lsv-debuginfo.cpp          |  9 +++--
 clang/test/PCH/pch-clangast-raw-section.c     |  6 ++++
 .../CodeGen/TargetLoweringObjectFileImpl.h    |  8 +++++
 .../llvm/Target/TargetLoweringObjectFile.h    |  7 ++++
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp    | 21 ++++++++++++
 .../CodeGen/TargetLoweringObjectFileImpl.cpp  | 23 +++++++++++++
 .../CodeGen/WebAssembly/raw-sections-wasm.ll  |  7 ++++
 llvm/test/CodeGen/X86/raw-sections-coff.ll    | 12 +++++++
 llvm/test/CodeGen/X86/raw-sections-elf.ll     |  7 ++++
 llvm/test/CodeGen/X86/raw-sections-macho.ll   |  9 +++++
 11 files changed, 124 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/PCH/pch-clangast-raw-section.c
 create mode 100644 llvm/test/CodeGen/WebAssembly/raw-sections-wasm.ll
 create mode 100644 llvm/test/CodeGen/X86/raw-sections-coff.ll
 create mode 100644 llvm/test/CodeGen/X86/raw-sections-elf.ll
 create mode 100644 llvm/test/CodeGen/X86/raw-sections-macho.ll

diff --git a/clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp 
b/clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp
index 074f2a520704d..3a507782bb66e 100644
--- a/clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp
+++ b/clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp
@@ -297,25 +297,27 @@ class PCHContainerGenerator : public ASTConsumer {
       auto *NameAndContent = llvm::MDTuple::get(*VMContext, Ops);
       MD->addOperand(NameAndContent);
     } else {
-      auto Int8Ty = llvm::Type::getInt8Ty(*VMContext);
-      auto *Ty = llvm::ArrayType::get(Int8Ty, Size);
-      auto *Data = llvm::ConstantDataArray::getString(
-          *VMContext, StringRef(SerializedAST.data(), Size),
-          /*AddNull=*/false);
-      auto *ASTSym = new llvm::GlobalVariable(
-          *M, Ty, /*constant*/ true, llvm::GlobalVariable::InternalLinkage,
-          Data, "__clang_ast");
-      // The on-disk hashtable needs to be aligned.
-      ASTSym->setAlignment(llvm::Align(8));
-
-      // Mach-O also needs a segment name.
+      // Emit the serialized AST into a named section via llvm.raw.sections
+      // metadata, which the AsmPrinter emits directly at the MC layer.
+      // This avoids IR-level size constraints from the target's address space.
+      llvm::NamedMDNode *RawSections =
+          M->getOrInsertNamedMetadata("llvm.raw.sections");
+
+      StringRef SectionName;
       if (Triple.isOSBinFormatMachO())
-        ASTSym->setSection("__CLANG,__clangast");
-      // COFF has an eight character length limit.
+        SectionName = "__CLANG,__clangast";
       else if (Triple.isOSBinFormatCOFF())
-        ASTSym->setSection("clangast");
+        SectionName = "clangast";
       else
-        ASTSym->setSection("__clangast");
+        SectionName = "__clangast";
+
+      llvm::Metadata *Ops[] = {
+          llvm::MDString::get(*VMContext, SectionName),
+          llvm::ConstantAsMetadata::get(
+              llvm::ConstantInt::get(llvm::Type::getInt32Ty(*VMContext), 8)),
+          llvm::MDString::get(*VMContext,
+                              StringRef(SerializedAST.data(), Size))};
+      RawSections->addOperand(llvm::MDTuple::get(*VMContext, Ops));
     }
 
     LLVM_DEBUG({
diff --git a/clang/test/Modules/lsv-debuginfo.cpp 
b/clang/test/Modules/lsv-debuginfo.cpp
index 40455727ecdda..61d2ff9514ed3 100644
--- a/clang/test/Modules/lsv-debuginfo.cpp
+++ b/clang/test/Modules/lsv-debuginfo.cpp
@@ -21,17 +21,20 @@
 // RUN: cat %t-mod.ll | FileCheck %s
 
 // ADT
-// CHECK: @__clang_ast =
+// CHECK: !llvm.raw.sections = !{[[ADT_SEC:![0-9]+]]}
+// CHECK: [[ADT_SEC]] = !{!"__clangast",
 
 // B
-// CHECK: @__clang_ast =
+// CHECK: !llvm.raw.sections = !{[[B_SEC:![0-9]+]]}
+// CHECK: [[B_SEC]] = !{!"__clangast",
 
 // This type isn't anchored anywhere, expect a full definition.
 // CHECK: !DICompositeType({{.*}}, name: "AlignedCharArray<4U, 16U>",
 // CHECK-SAME:             elements:
 
 // C
-// CHECK: @__clang_ast =
+// CHECK: !llvm.raw.sections = !{[[C_SEC:![0-9]+]]}
+// CHECK: [[C_SEC]] = !{!"__clangast",
 
 // Here, too.
 // CHECK: !DICompositeType({{.*}}, name: "AlignedCharArray<4U, 16U>",
diff --git a/clang/test/PCH/pch-clangast-raw-section.c 
b/clang/test/PCH/pch-clangast-raw-section.c
new file mode 100644
index 0000000000000..2ef54cf640e1c
--- /dev/null
+++ b/clang/test/PCH/pch-clangast-raw-section.c
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-pch -fmodule-format=obj 
%S/pchpch1.h -o - | llvm-readelf --sections - | FileCheck %s
+
+// Ensure the serialized AST is emitted via llvm.raw.sections metadata into
+// a __clangast section with 8-byte alignment.
+
+// CHECK: __clangast        PROGBITS  {{[0-9a-f]+}} {{[0-9a-f]+}} 
{{[0-9a-f]+}} 00   A  0   0  8
diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h 
b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
index 78954a8bb2121..a56e8ed6e0879 100644
--- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
+++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
@@ -49,6 +49,8 @@ class LLVM_ABI TargetLoweringObjectFileELF : public 
TargetLoweringObjectFile {
   /// Emit Obj-C garbage collection and linker options.
   void emitModuleMetadata(MCStreamer &Streamer, Module &M) const override;
 
+  MCSection *getNamedReadOnlySection(StringRef Name) const override;
+
   void emitPersonalityValue(MCStreamer &Streamer, const DataLayout &DL,
                             const MCSymbol *Sym,
                             const MachineModuleInfo *MMI) const override;
@@ -144,6 +146,8 @@ class LLVM_ABI TargetLoweringObjectFileMachO : public 
TargetLoweringObjectFile {
   /// Emit the module flags that specify the garbage collection information.
   void emitModuleMetadata(MCStreamer &Streamer, Module &M) const override;
 
+  MCSection *getNamedReadOnlySection(StringRef Name) const override;
+
   void emitLinkerDirectives(MCStreamer &Streamer, Module &M) const override;
 
   MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
@@ -207,6 +211,8 @@ class LLVM_ABI TargetLoweringObjectFileCOFF : public 
TargetLoweringObjectFile {
   /// Emit Obj-C garbage collection and linker options.
   void emitModuleMetadata(MCStreamer &Streamer, Module &M) const override;
 
+  MCSection *getNamedReadOnlySection(StringRef Name) const override;
+
   void emitLinkerDirectives(MCStreamer &Streamer, Module &M) const override;
 
   MCSection *getStaticCtorSection(unsigned Priority,
@@ -245,6 +251,8 @@ class LLVM_ABI TargetLoweringObjectFileWasm : public 
TargetLoweringObjectFile {
   bool shouldPutJumpTableInFunctionSection(bool UsesLabelDifference,
                                            const Function &F) const override;
 
+  MCSection *getNamedReadOnlySection(StringRef Name) const override;
+
   void InitializeWasm();
   MCSection *getStaticCtorSection(unsigned Priority,
                                   const MCSymbol *KeySym) const override;
diff --git a/llvm/include/llvm/Target/TargetLoweringObjectFile.h 
b/llvm/include/llvm/Target/TargetLoweringObjectFile.h
index 3cce5974e6705..df8dd3466451e 100644
--- a/llvm/include/llvm/Target/TargetLoweringObjectFile.h
+++ b/llvm/include/llvm/Target/TargetLoweringObjectFile.h
@@ -90,6 +90,13 @@ class LLVM_ABI TargetLoweringObjectFile : public 
MCObjectFileInfo {
   /// Emit the module-level metadata that the platform cares about.
   virtual void emitModuleMetadata(MCStreamer &Streamer, Module &M) const {}
 
+  /// Get a read-only data section with the given name, using 
format-appropriate
+  /// defaults.
+  /// Returns nullptr if not supported by this object file format.
+  virtual MCSection *getNamedReadOnlySection(StringRef Name) const {
+    return nullptr;
+  }
+
   /// Emit Call Graph Profile metadata.
   void emitCGProfileMetadata(MCStreamer &Streamer, Module &M) const;
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 0580fc0f3a034..af48b444b7991 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -2980,6 +2980,27 @@ bool AsmPrinter::doFinalization(Module &M) {
 
   TLOF.emitModuleMetadata(*OutStreamer, M);
 
+  // Emit raw section data from llvm.raw.sections metadata.
+  if (const NamedMDNode *RawSections =
+          M.getNamedMetadata("llvm.raw.sections")) {
+    for (const MDNode *Op : RawSections->operands()) {
+      assert(Op->getNumOperands() == 3 &&
+             "llvm.raw.sections metadata entry must have three operands");
+      auto *SectionName = cast<MDString>(Op->getOperand(0));
+      auto *AlignCI = mdconst::extract<ConstantInt>(Op->getOperand(1));
+      auto *Data = cast<MDString>(Op->getOperand(2));
+
+      if (MCSection *Section =
+              TLOF.getNamedReadOnlySection(SectionName->getString())) {
+        OutStreamer->pushSection();
+        OutStreamer->switchSection(Section);
+        OutStreamer->emitValueToAlignment(Align(AlignCI->getZExtValue()));
+        OutStreamer->emitBytes(Data->getString());
+        OutStreamer->popSection();
+      }
+    }
+  }
+
   if (Target.isOSBinFormatELF()) {
     MachineModuleInfoELF &MMIELF = MMI->getObjFileInfo<MachineModuleInfoELF>();
 
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp 
b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 84b72321b1f55..bcdb2c3d63715 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -377,6 +377,11 @@ void 
TargetLoweringObjectFileELF::emitModuleMetadata(MCStreamer &Streamer,
   emitCGProfileMetadata(Streamer, M);
 }
 
+MCSection *
+TargetLoweringObjectFileELF::getNamedReadOnlySection(StringRef Name) const {
+  return getContext().getELFSection(Name, ELF::SHT_PROGBITS, ELF::SHF_ALLOC);
+}
+
 void TargetLoweringObjectFileELF::emitLinkerDirectives(MCStreamer &Streamer,
                                                        Module &M) const {
   auto &C = getContext();
@@ -1350,6 +1355,13 @@ void 
TargetLoweringObjectFileMachO::emitModuleMetadata(MCStreamer &Streamer,
   Streamer.addBlankLine();
 }
 
+MCSection *
+TargetLoweringObjectFileMachO::getNamedReadOnlySection(StringRef Name) const {
+  auto [Segment, SecName] = Name.split(',');
+  return getContext().getMachOSection(Segment, SecName, 0,
+                                      SectionKind::getReadOnly());
+}
+
 void TargetLoweringObjectFileMachO::emitLinkerDirectives(MCStreamer &Streamer,
                                                          Module &M) const {
   if (auto *LinkerOptions = M.getNamedMetadata("llvm.linker.options")) {
@@ -1944,6 +1956,12 @@ void 
TargetLoweringObjectFileCOFF::emitModuleMetadata(MCStreamer &Streamer,
   });
 }
 
+MCSection *
+TargetLoweringObjectFileCOFF::getNamedReadOnlySection(StringRef Name) const {
+  return getContext().getCOFFSection(
+      Name, COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ);
+}
+
 void TargetLoweringObjectFileCOFF::emitLinkerDirectives(
     MCStreamer &Streamer, Module &M) const {
   if (NamedMDNode *LinkerOptions = M.getNamedMetadata("llvm.linker.options")) {
@@ -2258,6 +2276,11 @@ void 
TargetLoweringObjectFileWasm::getModuleMetadata(Module &M) {
       Used.insert(GO);
 }
 
+MCSection *
+TargetLoweringObjectFileWasm::getNamedReadOnlySection(StringRef Name) const {
+  return getContext().getWasmSection(Name, SectionKind::getReadOnly());
+}
+
 MCSection *TargetLoweringObjectFileWasm::getExplicitSectionGlobal(
     const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
   // We don't support explict section names for functions in the wasm object
diff --git a/llvm/test/CodeGen/WebAssembly/raw-sections-wasm.ll 
b/llvm/test/CodeGen/WebAssembly/raw-sections-wasm.ll
new file mode 100644
index 0000000000000..a326a5e912508
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/raw-sections-wasm.ll
@@ -0,0 +1,7 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-unknown %s -o %t.o
+; RUN: llvm-readobj --sections %t.o | FileCheck %s
+
+; CHECK: Name: __clangast
+
+!0 = !{!"__clangast", i32 8, !"\de\ad\be\ef"}
+!llvm.raw.sections = !{!0}
diff --git a/llvm/test/CodeGen/X86/raw-sections-coff.ll 
b/llvm/test/CodeGen/X86/raw-sections-coff.ll
new file mode 100644
index 0000000000000..fc9d2e7515f94
--- /dev/null
+++ b/llvm/test/CodeGen/X86/raw-sections-coff.ll
@@ -0,0 +1,12 @@
+; RUN: llc -filetype=obj -mtriple=x86_64-windows-msvc %s -o %t.o
+; RUN: llvm-readobj --sections %t.o | FileCheck %s
+
+; CHECK:      Name: clangast
+; CHECK:      RawDataSize:
+; CHECK:      Characteristics [
+; CHECK-DAG:    IMAGE_SCN_CNT_INITIALIZED_DATA
+; CHECK-DAG:    IMAGE_SCN_MEM_READ
+; CHECK:      ]
+
+!0 = !{!"clangast", i32 8, !"\de\ad\be\ef"}
+!llvm.raw.sections = !{!0}
diff --git a/llvm/test/CodeGen/X86/raw-sections-elf.ll 
b/llvm/test/CodeGen/X86/raw-sections-elf.ll
new file mode 100644
index 0000000000000..c1afbb043066b
--- /dev/null
+++ b/llvm/test/CodeGen/X86/raw-sections-elf.ll
@@ -0,0 +1,7 @@
+; RUN: llc -filetype=obj -mtriple=x86_64-linux-gnu %s -o %t.o
+; RUN: llvm-readelf --sections %t.o | FileCheck %s
+
+; CHECK: __clangast        PROGBITS  {{[0-9a-f]+}} {{[0-9a-f]+}} {{[0-9a-f]+}} 
00   A  0   0  8
+
+!0 = !{!"__clangast", i32 8, !"\de\ad\be\ef"}
+!llvm.raw.sections = !{!0}
diff --git a/llvm/test/CodeGen/X86/raw-sections-macho.ll 
b/llvm/test/CodeGen/X86/raw-sections-macho.ll
new file mode 100644
index 0000000000000..54d16b45529c4
--- /dev/null
+++ b/llvm/test/CodeGen/X86/raw-sections-macho.ll
@@ -0,0 +1,9 @@
+; RUN: llc -filetype=obj -mtriple=x86_64-apple-darwin %s -o %t.o
+; RUN: llvm-readobj --sections %t.o | FileCheck %s
+
+; CHECK:      Name: __clangast
+; CHECK-NEXT: Segment: __CLANG
+; CHECK:      Size:
+
+!0 = !{!"__CLANG,__clangast", i32 8, !"\de\ad\be\ef"}
+!llvm.raw.sections = !{!0}

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

Reply via email to