Skip to content

Conversation

mandlebug
Copy link
Member

Adds a metadata node that represents a special case of the XCOFF rename directive. The node must be placed on a variable that has an explict section attribute, and the global with the node will be emitted to a csect with a unique name, which is later renamed to the section attributes name. This is used in pgo when function sections is enabled, because otherwise the pgo instrumentation breaks garbage collection which many builds on AIX rely on. Other features which emit data which is meant to be globed together into a contiguous output sections, but may also contain references to other data that pollutes the symbol reference graph inhibiting garbage collection will also find this feature useful.

Adds a metadata node that represents a special case of the XCOFF rename
directive. The node must be placed on a variable that has an explict
section attribute, and the global with the node will be emitted to a csect
with a unique name, which is later renamed to the section attributes
name. This is used in pgo when function sections is enabled, becuase
otherwise the pgo instrumentation breaks garbage collection which
many builds on AIX rely on. Other features which emit data which is
meant to be globed together into a contiguous output sections, but may
also contain references to other data that pollutes the symbol reference
graph inhibiting garbage collection will also find this feature useful.
@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2025

@llvm/pr-subscribers-llvm-ir

Author: Sean Fertile (mandlebug)

Changes

Adds a metadata node that represents a special case of the XCOFF rename directive. The node must be placed on a variable that has an explict section attribute, and the global with the node will be emitted to a csect with a unique name, which is later renamed to the section attributes name. This is used in pgo when function sections is enabled, because otherwise the pgo instrumentation breaks garbage collection which many builds on AIX rely on. Other features which emit data which is meant to be globed together into a contiguous output sections, but may also contain references to other data that pollutes the symbol reference graph inhibiting garbage collection will also find this feature useful.


Full diff: https://github.com/llvm/llvm-project/pull/159435.diff

9 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+23)
  • (modified) llvm/include/llvm/IR/FixedMetadataKinds.def (+1)
  • (modified) llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (+11-3)
  • (modified) llvm/lib/IR/Verifier.cpp (+9)
  • (modified) llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp (+64-42)
  • (added) llvm/test/CodeGen/PowerPC/aix-rename.ll (+38)
  • (added) llvm/test/Verifier/PowerPC/lit.local.cfg (+2)
  • (added) llvm/test/Verifier/PowerPC/multiple_rename.ll (+8)
  • (added) llvm/test/Verifier/PowerPC/rename_operands.ll (+6)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d61ea07830123..03ba7a1e6b956 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8441,6 +8441,29 @@ The ``nofree`` metadata indicates the memory pointed by the pointer will not be
 freed after the attached instruction.
 
 
+'``rename.key``' Metadata
+^^^^^^^^^^^^^^^^^^^^^^^^^
+The rename key may be attached to a global variable definition that has an
+explicit section attribute. It is used as a flag so the associated node
+must be empty. It only takes effect when function sections is enabled, and
+only on XCOFF targets. The metadata will casue the global to be emitted to a
+control section (CSECT) with a name that is an amalgamation of both the section
+attribute and the global variables identifier. After the control section is
+defined it will be renamed to match the name of the section attribute. This
+allows the linker to aggressively garbage collect the symbol if unreferenced,
+while directing the linker to merge any control sections with the same name
+that remain after garbage collecting into the same CSECT in the output binary.
+Commonly used where there is a feature that uses a runtime to walk over a
+section using the linker-defined encapsulation symbols
+``__start_<section_name>`` and ``__stop_<section_name>``.
+
+Example:
+
+.. code-block:: llvm
+
+    @a = global i32 1, section "abc", !rename.key !0
+    !0 = !{}
+
 Module Flags Metadata
 =====================
 
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index d09cc15d65ff6..f4b7ebfcf14d5 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -55,3 +55,4 @@ LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40)
 LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41)
 LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
 LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
+LLVM_FIXED_MD_KIND(MD_rename_key, "rename.key", 44)
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index ae681b9aebdfb..759b6a8e349cc 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2431,16 +2431,24 @@ MCSection *TargetLoweringObjectFileXCOFF::getExplicitSectionGlobal(
   if (!GO->hasSection())
     report_fatal_error("#pragma clang section is not yet supported");
 
-  StringRef SectionName = GO->getSection();
+  std::string SectionName(GO->getSection());
 
-  // Handle the XCOFF::TD case first, then deal with the rest.
-  if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GO))
+  // Have to check for either attributes or metadata that can affect the
+  // section type or section name.
+  if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GO)) {
     if (GVar->hasAttribute("toc-data"))
       return getContext().getXCOFFSection(
           SectionName, Kind,
           XCOFF::CsectProperties(/*MappingClass*/ XCOFF::XMC_TD, XCOFF::XTY_SD),
           /* MultiSymbolsAllowed*/ true);
 
+    if (TM.getFunctionSections() &&
+        GVar->hasMetadata(LLVMContext::MD_rename_key)) {
+      SectionName += ".";
+      SectionName += GO->getName();
+    }
+  }
+
   XCOFF::StorageMappingClass MappingClass;
   if (Kind.isText())
     MappingClass = XCOFF::XMC_PR;
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c06b60fd2d9a9..1d11e3f4e3aae 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -766,6 +766,15 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
                               DL.getIntPtrType(GO->getType()),
                               RangeLikeMetadataKind::AbsoluteSymbol);
     }
+
+    if (GO->hasMetadata(LLVMContext::MD_rename_key)) {
+      SmallVector<MDNode *, 1> MDs;
+      GO->getMetadata(LLVMContext::MD_rename_key, MDs);
+      Check(MDs.size() == 1,
+            "global value cannot have more then 1 rename metadata", GO);
+      Check(MDs[0]->getNumOperands() == 0,
+            "rename metadata must have no operands", GO);
+    }
   }
 
   Check(!GV.hasAppendingLinkage() || isa<GlobalVariable>(GV),
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 023fd147535ec..962cf518614c8 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -2970,58 +2970,80 @@ void PPCAIXAsmPrinter::emitGCOVRefs() {
 }
 
 void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) {
-  // If there are no functions and there are no toc-data definitions in this
-  // module, we will never need to reference the TOC base.
-  if (M.empty() && TOCDataGlobalVars.empty())
-    return;
 
   emitPGORefs(M);
   emitGCOVRefs();
 
-  // Switch to section to emit TOC base.
-  OutStreamer->switchSection(getObjFileLowering().getTOCBaseSection());
+  // If there are no functions and there are no toc-data definitions in this
+  // module, we will never need to reference the TOC base.
+  if (!M.empty() || !TOCDataGlobalVars.empty()) {
+    // Switch to section to emit TOC base.
+    OutStreamer->switchSection(getObjFileLowering().getTOCBaseSection());
 
-  PPCTargetStreamer *TS =
-      static_cast<PPCTargetStreamer *>(OutStreamer->getTargetStreamer());
+    PPCTargetStreamer *TS =
+        static_cast<PPCTargetStreamer *>(OutStreamer->getTargetStreamer());
+
+    for (auto &I : TOC) {
+      MCSectionXCOFF *TCEntry;
+      // Setup the csect for the current TC entry. If the variant kind is
+      // VK_AIX_TLSGDM the entry represents the region handle, we create a
+      // new symbol to prefix the name with a dot.
+      // If TLS model opt is turned on, create a new symbol to prefix the name
+      // with a dot.
+      if (I.first.second == PPC::S_AIX_TLSGDM ||
+          (Subtarget->hasAIXShLibTLSModelOpt() &&
+           I.first.second == PPC::S_AIX_TLSLD)) {
+        SmallString<128> Name;
+        StringRef Prefix = ".";
+        Name += Prefix;
+        Name += static_cast<const MCSymbolXCOFF *>(I.first.first)
+                    ->getSymbolTableName();
+        MCSymbol *S = OutContext.getOrCreateSymbol(Name);
+        TCEntry = static_cast<MCSectionXCOFF *>(
+            getObjFileLowering().getSectionForTOCEntry(S, TM));
+      } else {
+        TCEntry = static_cast<MCSectionXCOFF *>(
+            getObjFileLowering().getSectionForTOCEntry(I.first.first, TM));
+      }
+      OutStreamer->switchSection(TCEntry);
 
-  for (auto &I : TOC) {
-    MCSectionXCOFF *TCEntry;
-    // Setup the csect for the current TC entry. If the variant kind is
-    // VK_AIX_TLSGDM the entry represents the region handle, we create a
-    // new symbol to prefix the name with a dot.
-    // If TLS model opt is turned on, create a new symbol to prefix the name
-    // with a dot.
-    if (I.first.second == PPC::S_AIX_TLSGDM ||
-        (Subtarget->hasAIXShLibTLSModelOpt() &&
-         I.first.second == PPC::S_AIX_TLSLD)) {
-      SmallString<128> Name;
-      StringRef Prefix = ".";
-      Name += Prefix;
-      Name += static_cast<const MCSymbolXCOFF *>(I.first.first)
-                  ->getSymbolTableName();
-      MCSymbol *S = OutContext.getOrCreateSymbol(Name);
-      TCEntry = static_cast<MCSectionXCOFF *>(
-          getObjFileLowering().getSectionForTOCEntry(S, TM));
-    } else {
-      TCEntry = static_cast<MCSectionXCOFF *>(
-          getObjFileLowering().getSectionForTOCEntry(I.first.first, TM));
+      OutStreamer->emitLabel(I.second);
+      TS->emitTCEntry(*I.first.first, I.first.second);
     }
-    OutStreamer->switchSection(TCEntry);
 
-    OutStreamer->emitLabel(I.second);
-    TS->emitTCEntry(*I.first.first, I.first.second);
+    // Traverse the list of global variables twice, emitting all of the
+    // non-common global variables before the common ones, as emitting a
+    // .comm directive changes the scope from .toc to the common symbol.
+    for (const auto *GV : TOCDataGlobalVars) {
+      if (!GV->hasCommonLinkage())
+        emitGlobalVariableHelper(GV);
+    }
+    for (const auto *GV : TOCDataGlobalVars) {
+      if (GV->hasCommonLinkage())
+        emitGlobalVariableHelper(GV);
+    }
   }
 
-  // Traverse the list of global variables twice, emitting all of the
-  // non-common global variables before the common ones, as emitting a
-  // .comm directive changes the scope from .toc to the common symbol.
-  for (const auto *GV : TOCDataGlobalVars) {
-    if (!GV->hasCommonLinkage())
-      emitGlobalVariableHelper(GV);
-  }
-  for (const auto *GV : TOCDataGlobalVars) {
-    if (GV->hasCommonLinkage())
-      emitGlobalVariableHelper(GV);
+  // Renames only take effect when function sections is enabled.
+  if (!TM.getFunctionSections())
+    return;
+
+  for (const GlobalVariable &GV : M.globals()) {
+    if (GV.hasMetadata(LLVMContext::MD_rename_key)) {
+      // Get orginal csect.
+      SectionKind GVKind = getObjFileLowering().getKindForGlobal(&GV, TM);
+      auto *CSect = static_cast<MCSectionXCOFF *>(
+          getObjFileLowering().SectionForGlobal(&GV, GVKind, TM));
+
+      // Get the section to rename to.
+      if (!GV.hasSection())
+        reportFatalInternalError(
+            "rename.key metadata used without a section attribute");
+
+      StringRef SectionName = GV.getSection();
+      OutStreamer->emitXCOFFRenameDirective(CSect->getQualNameSymbol(),
+                                            SectionName);
+    }
   }
 }
 
diff --git a/llvm/test/CodeGen/PowerPC/aix-rename.ll b/llvm/test/CodeGen/PowerPC/aix-rename.ll
new file mode 100644
index 0000000000000..3fe379b8cd33b
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/aix-rename.ll
@@ -0,0 +1,38 @@
+; RUN: llc --function-sections -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s
+; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck --check-prefix=NOFUNCSECT %s
+
+@a = global i32 1, section "abcd", !rename.key !0
+@b = global i32 2, section "abcd", !rename.key !0
+@c = global i32 3, section "abcd", !rename.key !0
+@d = global i32 4, section "abcd", !rename.key !0
+
+!0 = !{}
+
+;CHECK:     .csect abcd.a[RW]
+;CHECK:     .globl  a
+
+;CHECK:     .csect abcd.b[RW]
+;CHECK:     .globl  b
+
+;CHECK:     .csect abcd.c[RW]
+;CHECK:     .globl  c
+
+;CHECK:     .csect abcd.d[RW]
+;CHECK:     .globl  d
+
+;CHECK:     .rename abcd.a[RW],"abcd"
+;CHECK:     .rename abcd.b[RW],"abcd"
+;CHECK:     .rename abcd.c[RW],"abcd"
+;CHECK:     .rename abcd.d[RW],"abcd"
+
+;NOFUNCSECT:     .csect abcd[RW],2
+;NOFUNCSECT-NOT: .csect
+;NOFUNCSECT:     .globl  a
+;NOFUNCSECT-NOT: .csect
+;NOFUNCSECT:     .globl  b
+;NOFUNCSECT-NOT: .csect
+;NOFUNCSECT:     .globl  c
+;NOFUNCSECT-NOT: .csect
+;NOFUNCSECT:     .globl  d
+
+;NOFUNCSECT-NOT: .rename
diff --git a/llvm/test/Verifier/PowerPC/lit.local.cfg b/llvm/test/Verifier/PowerPC/lit.local.cfg
new file mode 100644
index 0000000000000..bb982488eb15e
--- /dev/null
+++ b/llvm/test/Verifier/PowerPC/lit.local.cfg
@@ -0,0 +1,2 @@
+if not "PowerPC" in config.root.targets:
+    config.unsupported = True
diff --git a/llvm/test/Verifier/PowerPC/multiple_rename.ll b/llvm/test/Verifier/PowerPC/multiple_rename.ll
new file mode 100644
index 0000000000000..16b03dc34be7e
--- /dev/null
+++ b/llvm/test/Verifier/PowerPC/multiple_rename.ll
@@ -0,0 +1,8 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+@a = global i32 1, section "abc", !rename.key !0, !rename.key !1
+
+!0 = !{}
+!1 = !{}
+
+; CHECK: global value cannot have more then 1 rename metadata
diff --git a/llvm/test/Verifier/PowerPC/rename_operands.ll b/llvm/test/Verifier/PowerPC/rename_operands.ll
new file mode 100644
index 0000000000000..a76575bc5293e
--- /dev/null
+++ b/llvm/test/Verifier/PowerPC/rename_operands.ll
@@ -0,0 +1,6 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+@a = global i32 1, section "abc", !rename.key !0
+
+!0 = !{!"Hello World!"}
+; CHECK:  rename metadata must have no operands

@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2025

@llvm/pr-subscribers-backend-powerpc

Author: Sean Fertile (mandlebug)

Changes

Adds a metadata node that represents a special case of the XCOFF rename directive. The node must be placed on a variable that has an explict section attribute, and the global with the node will be emitted to a csect with a unique name, which is later renamed to the section attributes name. This is used in pgo when function sections is enabled, because otherwise the pgo instrumentation breaks garbage collection which many builds on AIX rely on. Other features which emit data which is meant to be globed together into a contiguous output sections, but may also contain references to other data that pollutes the symbol reference graph inhibiting garbage collection will also find this feature useful.


Full diff: https://github.com/llvm/llvm-project/pull/159435.diff

9 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+23)
  • (modified) llvm/include/llvm/IR/FixedMetadataKinds.def (+1)
  • (modified) llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (+11-3)
  • (modified) llvm/lib/IR/Verifier.cpp (+9)
  • (modified) llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp (+64-42)
  • (added) llvm/test/CodeGen/PowerPC/aix-rename.ll (+38)
  • (added) llvm/test/Verifier/PowerPC/lit.local.cfg (+2)
  • (added) llvm/test/Verifier/PowerPC/multiple_rename.ll (+8)
  • (added) llvm/test/Verifier/PowerPC/rename_operands.ll (+6)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d61ea07830123..03ba7a1e6b956 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8441,6 +8441,29 @@ The ``nofree`` metadata indicates the memory pointed by the pointer will not be
 freed after the attached instruction.
 
 
+'``rename.key``' Metadata
+^^^^^^^^^^^^^^^^^^^^^^^^^
+The rename key may be attached to a global variable definition that has an
+explicit section attribute. It is used as a flag so the associated node
+must be empty. It only takes effect when function sections is enabled, and
+only on XCOFF targets. The metadata will casue the global to be emitted to a
+control section (CSECT) with a name that is an amalgamation of both the section
+attribute and the global variables identifier. After the control section is
+defined it will be renamed to match the name of the section attribute. This
+allows the linker to aggressively garbage collect the symbol if unreferenced,
+while directing the linker to merge any control sections with the same name
+that remain after garbage collecting into the same CSECT in the output binary.
+Commonly used where there is a feature that uses a runtime to walk over a
+section using the linker-defined encapsulation symbols
+``__start_<section_name>`` and ``__stop_<section_name>``.
+
+Example:
+
+.. code-block:: llvm
+
+    @a = global i32 1, section "abc", !rename.key !0
+    !0 = !{}
+
 Module Flags Metadata
 =====================
 
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index d09cc15d65ff6..f4b7ebfcf14d5 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -55,3 +55,4 @@ LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40)
 LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41)
 LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
 LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
+LLVM_FIXED_MD_KIND(MD_rename_key, "rename.key", 44)
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index ae681b9aebdfb..759b6a8e349cc 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2431,16 +2431,24 @@ MCSection *TargetLoweringObjectFileXCOFF::getExplicitSectionGlobal(
   if (!GO->hasSection())
     report_fatal_error("#pragma clang section is not yet supported");
 
-  StringRef SectionName = GO->getSection();
+  std::string SectionName(GO->getSection());
 
-  // Handle the XCOFF::TD case first, then deal with the rest.
-  if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GO))
+  // Have to check for either attributes or metadata that can affect the
+  // section type or section name.
+  if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GO)) {
     if (GVar->hasAttribute("toc-data"))
       return getContext().getXCOFFSection(
           SectionName, Kind,
           XCOFF::CsectProperties(/*MappingClass*/ XCOFF::XMC_TD, XCOFF::XTY_SD),
           /* MultiSymbolsAllowed*/ true);
 
+    if (TM.getFunctionSections() &&
+        GVar->hasMetadata(LLVMContext::MD_rename_key)) {
+      SectionName += ".";
+      SectionName += GO->getName();
+    }
+  }
+
   XCOFF::StorageMappingClass MappingClass;
   if (Kind.isText())
     MappingClass = XCOFF::XMC_PR;
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c06b60fd2d9a9..1d11e3f4e3aae 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -766,6 +766,15 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
                               DL.getIntPtrType(GO->getType()),
                               RangeLikeMetadataKind::AbsoluteSymbol);
     }
+
+    if (GO->hasMetadata(LLVMContext::MD_rename_key)) {
+      SmallVector<MDNode *, 1> MDs;
+      GO->getMetadata(LLVMContext::MD_rename_key, MDs);
+      Check(MDs.size() == 1,
+            "global value cannot have more then 1 rename metadata", GO);
+      Check(MDs[0]->getNumOperands() == 0,
+            "rename metadata must have no operands", GO);
+    }
   }
 
   Check(!GV.hasAppendingLinkage() || isa<GlobalVariable>(GV),
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 023fd147535ec..962cf518614c8 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -2970,58 +2970,80 @@ void PPCAIXAsmPrinter::emitGCOVRefs() {
 }
 
 void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) {
-  // If there are no functions and there are no toc-data definitions in this
-  // module, we will never need to reference the TOC base.
-  if (M.empty() && TOCDataGlobalVars.empty())
-    return;
 
   emitPGORefs(M);
   emitGCOVRefs();
 
-  // Switch to section to emit TOC base.
-  OutStreamer->switchSection(getObjFileLowering().getTOCBaseSection());
+  // If there are no functions and there are no toc-data definitions in this
+  // module, we will never need to reference the TOC base.
+  if (!M.empty() || !TOCDataGlobalVars.empty()) {
+    // Switch to section to emit TOC base.
+    OutStreamer->switchSection(getObjFileLowering().getTOCBaseSection());
 
-  PPCTargetStreamer *TS =
-      static_cast<PPCTargetStreamer *>(OutStreamer->getTargetStreamer());
+    PPCTargetStreamer *TS =
+        static_cast<PPCTargetStreamer *>(OutStreamer->getTargetStreamer());
+
+    for (auto &I : TOC) {
+      MCSectionXCOFF *TCEntry;
+      // Setup the csect for the current TC entry. If the variant kind is
+      // VK_AIX_TLSGDM the entry represents the region handle, we create a
+      // new symbol to prefix the name with a dot.
+      // If TLS model opt is turned on, create a new symbol to prefix the name
+      // with a dot.
+      if (I.first.second == PPC::S_AIX_TLSGDM ||
+          (Subtarget->hasAIXShLibTLSModelOpt() &&
+           I.first.second == PPC::S_AIX_TLSLD)) {
+        SmallString<128> Name;
+        StringRef Prefix = ".";
+        Name += Prefix;
+        Name += static_cast<const MCSymbolXCOFF *>(I.first.first)
+                    ->getSymbolTableName();
+        MCSymbol *S = OutContext.getOrCreateSymbol(Name);
+        TCEntry = static_cast<MCSectionXCOFF *>(
+            getObjFileLowering().getSectionForTOCEntry(S, TM));
+      } else {
+        TCEntry = static_cast<MCSectionXCOFF *>(
+            getObjFileLowering().getSectionForTOCEntry(I.first.first, TM));
+      }
+      OutStreamer->switchSection(TCEntry);
 
-  for (auto &I : TOC) {
-    MCSectionXCOFF *TCEntry;
-    // Setup the csect for the current TC entry. If the variant kind is
-    // VK_AIX_TLSGDM the entry represents the region handle, we create a
-    // new symbol to prefix the name with a dot.
-    // If TLS model opt is turned on, create a new symbol to prefix the name
-    // with a dot.
-    if (I.first.second == PPC::S_AIX_TLSGDM ||
-        (Subtarget->hasAIXShLibTLSModelOpt() &&
-         I.first.second == PPC::S_AIX_TLSLD)) {
-      SmallString<128> Name;
-      StringRef Prefix = ".";
-      Name += Prefix;
-      Name += static_cast<const MCSymbolXCOFF *>(I.first.first)
-                  ->getSymbolTableName();
-      MCSymbol *S = OutContext.getOrCreateSymbol(Name);
-      TCEntry = static_cast<MCSectionXCOFF *>(
-          getObjFileLowering().getSectionForTOCEntry(S, TM));
-    } else {
-      TCEntry = static_cast<MCSectionXCOFF *>(
-          getObjFileLowering().getSectionForTOCEntry(I.first.first, TM));
+      OutStreamer->emitLabel(I.second);
+      TS->emitTCEntry(*I.first.first, I.first.second);
     }
-    OutStreamer->switchSection(TCEntry);
 
-    OutStreamer->emitLabel(I.second);
-    TS->emitTCEntry(*I.first.first, I.first.second);
+    // Traverse the list of global variables twice, emitting all of the
+    // non-common global variables before the common ones, as emitting a
+    // .comm directive changes the scope from .toc to the common symbol.
+    for (const auto *GV : TOCDataGlobalVars) {
+      if (!GV->hasCommonLinkage())
+        emitGlobalVariableHelper(GV);
+    }
+    for (const auto *GV : TOCDataGlobalVars) {
+      if (GV->hasCommonLinkage())
+        emitGlobalVariableHelper(GV);
+    }
   }
 
-  // Traverse the list of global variables twice, emitting all of the
-  // non-common global variables before the common ones, as emitting a
-  // .comm directive changes the scope from .toc to the common symbol.
-  for (const auto *GV : TOCDataGlobalVars) {
-    if (!GV->hasCommonLinkage())
-      emitGlobalVariableHelper(GV);
-  }
-  for (const auto *GV : TOCDataGlobalVars) {
-    if (GV->hasCommonLinkage())
-      emitGlobalVariableHelper(GV);
+  // Renames only take effect when function sections is enabled.
+  if (!TM.getFunctionSections())
+    return;
+
+  for (const GlobalVariable &GV : M.globals()) {
+    if (GV.hasMetadata(LLVMContext::MD_rename_key)) {
+      // Get orginal csect.
+      SectionKind GVKind = getObjFileLowering().getKindForGlobal(&GV, TM);
+      auto *CSect = static_cast<MCSectionXCOFF *>(
+          getObjFileLowering().SectionForGlobal(&GV, GVKind, TM));
+
+      // Get the section to rename to.
+      if (!GV.hasSection())
+        reportFatalInternalError(
+            "rename.key metadata used without a section attribute");
+
+      StringRef SectionName = GV.getSection();
+      OutStreamer->emitXCOFFRenameDirective(CSect->getQualNameSymbol(),
+                                            SectionName);
+    }
   }
 }
 
diff --git a/llvm/test/CodeGen/PowerPC/aix-rename.ll b/llvm/test/CodeGen/PowerPC/aix-rename.ll
new file mode 100644
index 0000000000000..3fe379b8cd33b
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/aix-rename.ll
@@ -0,0 +1,38 @@
+; RUN: llc --function-sections -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s
+; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck --check-prefix=NOFUNCSECT %s
+
+@a = global i32 1, section "abcd", !rename.key !0
+@b = global i32 2, section "abcd", !rename.key !0
+@c = global i32 3, section "abcd", !rename.key !0
+@d = global i32 4, section "abcd", !rename.key !0
+
+!0 = !{}
+
+;CHECK:     .csect abcd.a[RW]
+;CHECK:     .globl  a
+
+;CHECK:     .csect abcd.b[RW]
+;CHECK:     .globl  b
+
+;CHECK:     .csect abcd.c[RW]
+;CHECK:     .globl  c
+
+;CHECK:     .csect abcd.d[RW]
+;CHECK:     .globl  d
+
+;CHECK:     .rename abcd.a[RW],"abcd"
+;CHECK:     .rename abcd.b[RW],"abcd"
+;CHECK:     .rename abcd.c[RW],"abcd"
+;CHECK:     .rename abcd.d[RW],"abcd"
+
+;NOFUNCSECT:     .csect abcd[RW],2
+;NOFUNCSECT-NOT: .csect
+;NOFUNCSECT:     .globl  a
+;NOFUNCSECT-NOT: .csect
+;NOFUNCSECT:     .globl  b
+;NOFUNCSECT-NOT: .csect
+;NOFUNCSECT:     .globl  c
+;NOFUNCSECT-NOT: .csect
+;NOFUNCSECT:     .globl  d
+
+;NOFUNCSECT-NOT: .rename
diff --git a/llvm/test/Verifier/PowerPC/lit.local.cfg b/llvm/test/Verifier/PowerPC/lit.local.cfg
new file mode 100644
index 0000000000000..bb982488eb15e
--- /dev/null
+++ b/llvm/test/Verifier/PowerPC/lit.local.cfg
@@ -0,0 +1,2 @@
+if not "PowerPC" in config.root.targets:
+    config.unsupported = True
diff --git a/llvm/test/Verifier/PowerPC/multiple_rename.ll b/llvm/test/Verifier/PowerPC/multiple_rename.ll
new file mode 100644
index 0000000000000..16b03dc34be7e
--- /dev/null
+++ b/llvm/test/Verifier/PowerPC/multiple_rename.ll
@@ -0,0 +1,8 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+@a = global i32 1, section "abc", !rename.key !0, !rename.key !1
+
+!0 = !{}
+!1 = !{}
+
+; CHECK: global value cannot have more then 1 rename metadata
diff --git a/llvm/test/Verifier/PowerPC/rename_operands.ll b/llvm/test/Verifier/PowerPC/rename_operands.ll
new file mode 100644
index 0000000000000..a76575bc5293e
--- /dev/null
+++ b/llvm/test/Verifier/PowerPC/rename_operands.ll
@@ -0,0 +1,6 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+@a = global i32 1, section "abc", !rename.key !0
+
+!0 = !{!"Hello World!"}
+; CHECK:  rename metadata must have no operands

The rename key may be attached to a global variable definition that has an
explicit section attribute. It is used as a flag so the associated node
must be empty. It only takes effect when function sections is enabled, and
only on XCOFF targets. The metadata will casue the global to be emitted to a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: cause

@github-actions
Copy link

github-actions bot commented Sep 25, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@w2yehia
Copy link
Contributor

w2yehia commented Oct 9, 2025

I was reading about COMDAT in llvm, and it mentions an example of mixing comdat with explicit sections:

$foo = comdat any
$bar = comdat any
@g1 = global i32 42, section "sec", comdat($foo)
@g2 = global i32 42, section "sec", comdat($bar)

and says

From the object file perspective, this requires the creation of two sections with the same name. This is necessary because both globals belong to different COMDAT groups and COMDATs, at the object file level, are represented by section

So that led me to wonder:
Why don't we simulate nodeduplicate COMDAT groups + explicit sections in XCOFF by using the XCOFF .rename directive + named sections?
As a start, in XCOFF, we will only accept a very limited form of COMDAT (the above).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants