Skip to content

[PowerPC][AIX] Support #pragma comment copyright for AIX#178184

Open
tonykuttai wants to merge 4 commits into
llvm:mainfrom
tonykuttai:tvarghese/pragma-copyright-comment-update
Open

[PowerPC][AIX] Support #pragma comment copyright for AIX#178184
tonykuttai wants to merge 4 commits into
llvm:mainfrom
tonykuttai:tvarghese/pragma-copyright-comment-update

Conversation

@tonykuttai
Copy link
Copy Markdown
Contributor

@tonykuttai tonykuttai commented Jan 27, 2026

This change supports pragma comment copyright.
Signature: #pragma comment (copyright, "token_sequence")

  • The token_sequence is included in the generated executable and loaded into memory when the program is run.
  • copyright comment directive can appear only once in a translation unit.

Implementation is done through:

  • Emitting !comment_string.loadtime from Clang for the pragma.
  • Lowering it in LLVM to a TU-local string + llvm.compiler.used + !implicit.ref by LowerCommentStringPass
  • backend reads !implicit.ref and emits .ref directives into the object file

This change builds on top of XCOFF associated metadata and was reviewed at [PowerPC][AIX] Support #pragma comment copyright for AIX

here is the documentation for #pragma comment directives : https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1.0?topic=descriptions-pragma-comment

@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch from 750cee4 to 527b870 Compare February 3, 2026 04:21
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 3, 2026

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

@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch 2 times, most recently from 552eba6 to bcb075b Compare February 3, 2026 04:32
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 3, 2026

🐧 Linux x64 Test Results

  • 204577 tests passed
  • 6540 tests skipped

✅ The build succeeded and all tests passed.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 3, 2026

🪟 Windows x64 Test Results

  • 136630 tests passed
  • 4664 tests skipped

✅ The build succeeded and all tests passed.

@tonykuttai tonykuttai marked this pull request as ready for review February 3, 2026 07:35
@llvmbot llvmbot added backend:AArch64 backend:Hexagon backend:PowerPC clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. llvm:transforms labels Feb 3, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Feb 3, 2026

@llvm/pr-subscribers-lto
@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-backend-powerpc

@llvm/pr-subscribers-llvm-transforms

Author: Tony Varghese (tonykuttai)

Changes
  • Emit !aix.copyright.comment from Clang for the pragma.
  • Lower it in LLVM to a TU-local string + llvm.used + !implicit.ref.
  • Add module-import and backend relocation tests.

This change builds on top of XCOFF associated metadata and was reviewed at [PowerPC][AIX] Support #pragma comment copyright for AIX


Patch is 29.81 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/178184.diff

22 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+4)
  • (modified) clang/include/clang/Basic/PragmaKinds.h (+2-1)
  • (modified) clang/lib/AST/TextNodeDumper.cpp (+3)
  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+37)
  • (modified) clang/lib/CodeGen/CodeGenModule.h (+5)
  • (modified) clang/lib/Parse/ParsePragma.cpp (+36-9)
  • (added) clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp (+28)
  • (added) clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c (+34)
  • (modified) clang/test/CodeGen/lto-newpm-pipeline.c (+2)
  • (added) llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h (+24)
  • (modified) llvm/lib/Passes/PassBuilder.cpp (+1)
  • (modified) llvm/lib/Passes/PassBuilderPipelines.cpp (+7)
  • (modified) llvm/lib/Passes/PassRegistry.def (+1)
  • (modified) llvm/lib/Transforms/Utils/CMakeLists.txt (+1)
  • (added) llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp (+148)
  • (modified) llvm/test/CodeGen/AArch64/print-pipeline-passes.ll (+1-1)
  • (modified) llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll (+1-1)
  • (modified) llvm/test/Other/new-pm-defaults.ll (+1)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-defaults.ll (+1)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll (+1)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll (+1)
  • (added) llvm/test/Transforms/LowerCommentString/lower-comment-string.ll (+59)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 457d3644de35a..ede6a90a31cc6 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1358,6 +1358,10 @@ def err_pragma_comment_unknown_kind : Error<"unknown kind of pragma comment">;
 // PS4 recognizes only #pragma comment(lib)
 def warn_pragma_comment_ignored : Warning<"'#pragma comment %0' ignored">,
   InGroup<IgnoredPragmas>;
+def warn_pragma_comment_once
+    : Warning<"'#pragma comment %0' "
+              "can be specified only once per translation unit - ignored">,
+      InGroup<IgnoredPragmas>;
 // - #pragma detect_mismatch
 def err_pragma_detect_mismatch_malformed : Error<
   "pragma detect_mismatch is malformed; it requires two comma-separated "
diff --git a/clang/include/clang/Basic/PragmaKinds.h b/clang/include/clang/Basic/PragmaKinds.h
index 42f049f7323d2..52ca58855d460 100644
--- a/clang/include/clang/Basic/PragmaKinds.h
+++ b/clang/include/clang/Basic/PragmaKinds.h
@@ -17,7 +17,8 @@ enum PragmaMSCommentKind {
   PCK_Lib,      // #pragma comment(lib, ...)
   PCK_Compiler, // #pragma comment(compiler, ...)
   PCK_ExeStr,   // #pragma comment(exestr, ...)
-  PCK_User      // #pragma comment(user, ...)
+  PCK_User,     // #pragma comment(user, ...)
+  PCK_Copyright // #pragma comment(copyright, ...)
 };
 
 enum PragmaMSStructKind {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 7bc0404db1bee..c58c33f95c193 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2526,6 +2526,9 @@ void TextNodeDumper::VisitPragmaCommentDecl(const PragmaCommentDecl *D) {
   case PCK_User:
     OS << "user";
     break;
+  case PCK_Copyright:
+    OS << "copyright";
+    break;
   }
   StringRef Arg = D->getArg();
   if (!Arg.empty())
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index d50c9605a30b3..e63adc98a95aa 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1588,6 +1588,12 @@ void CodeGenModule::Release() {
 
   EmitBackendOptionsMetadata(getCodeGenOpts());
 
+  // Emit copyright metadata
+  if (LoadTimeComment) {
+    auto *NMD = getModule().getOrInsertNamedMetadata("comment_string.loadtime");
+    NMD->addOperand(LoadTimeComment);
+  }
+
   // If there is device offloading code embed it in the host now.
   EmbedObject(&getModule(), CodeGenOpts, *getFileSystem(), getDiags());
 
@@ -3481,6 +3487,31 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
   LinkerOptionsMetadata.push_back(llvm::MDNode::get(C, MDOpts));
 }
 
+/// Process AIX copyright pragma and create LLVM metadata.
+/// #pragma comment(copyright, "string") embed copyright
+/// information into the object file's loader section.
+///
+/// Example: #pragma comment(copyright, "Copyright IBM Corp. 2024")
+///
+/// This should only be called once per translation unit.
+void CodeGenModule::ProcessPragmaComment(PragmaMSCommentKind Kind,
+                                         StringRef Comment) {
+  // Ensure we are only processing Copyright Pragmas
+  assert(Kind == PCK_Copyright &&
+         "Unexpected pragma comment kind, ProcessPragmaComment should only be "
+         "called for PCK_Copyright");
+
+  // Only one copyright pragma allowed per translation unit
+  if (LoadTimeComment) {
+    return;
+  }
+
+  // Create llvm metadata with the comment string
+  auto &C = getLLVMContext();
+  llvm::Metadata *Ops[] = {llvm::MDString::get(C, Comment)};
+  LoadTimeComment = llvm::MDNode::get(C, Ops);
+}
+
 /// Add link options implied by the given module, including modules
 /// it depends on, using a postorder walk.
 static void addLinkOptionsPostorder(CodeGenModule &CGM, Module *Mod,
@@ -7683,6 +7714,12 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
     case PCK_Lib:
         AddDependentLib(PCD->getArg());
       break;
+    case PCK_Copyright:
+      // Skip pragmas deserialized from modules/PCHs. Process the pragma comment
+      // only if it originated in this TU and the target OS is AIX.
+      if (!PCD->isFromASTFile() && getTriple().isOSAIX())
+        ProcessPragmaComment(PCD->getCommentKind(), PCD->getArg());
+      break;
     case PCK_Compiler:
     case PCK_ExeStr:
     case PCK_User:
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index cc18e21d45759..21a75958dcc9d 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -587,6 +587,9 @@ class CodeGenModule : public CodeGenTypeCache {
   /// A vector of metadata strings for dependent libraries for ELF.
   SmallVector<llvm::MDNode *, 16> ELFDependentLibraries;
 
+  /// Metadata for copyright pragma comment (if present).
+  llvm::MDNode *LoadTimeComment = nullptr;
+
   /// @name Cache for Objective-C runtime types
   /// @{
 
@@ -1490,6 +1493,8 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Appends a dependent lib to the appropriate metadata value.
   void AddDependentLib(StringRef Lib);
 
+  /// Process pragma comment
+  void ProcessPragmaComment(PragmaMSCommentKind Kind, StringRef Comment);
 
   llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD);
 
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index def2817c930b2..5865319470511 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -237,6 +237,7 @@ struct PragmaCommentHandler : public PragmaHandler {
 
 private:
   Sema &Actions;
+  bool SeenCopyrightInTU = false; // TU-scoped
 };
 
 struct PragmaDetectMismatchHandler : public PragmaHandler {
@@ -480,7 +481,8 @@ void Parser::initializePragmaHandlers() {
   PP.AddPragmaHandler(OpenACCHandler.get());
 
   if (getLangOpts().MicrosoftExt ||
-      getTargetInfo().getTriple().isOSBinFormatELF()) {
+      getTargetInfo().getTriple().isOSBinFormatELF() ||
+      getTargetInfo().getTriple().isOSAIX()) {
     MSCommentHandler = std::make_unique<PragmaCommentHandler>(Actions);
     PP.AddPragmaHandler(MSCommentHandler.get());
   }
@@ -607,7 +609,8 @@ void Parser::resetPragmaHandlers() {
   OpenACCHandler.reset();
 
   if (getLangOpts().MicrosoftExt ||
-      getTargetInfo().getTriple().isOSBinFormatELF()) {
+      getTargetInfo().getTriple().isOSBinFormatELF() ||
+      getTargetInfo().getTriple().isOSAIX()) {
     PP.RemovePragmaHandler(MSCommentHandler.get());
     MSCommentHandler.reset();
   }
@@ -3287,13 +3290,21 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
   // Verify that this is one of the 5 explicitly listed options.
   IdentifierInfo *II = Tok.getIdentifierInfo();
   PragmaMSCommentKind Kind =
-    llvm::StringSwitch<PragmaMSCommentKind>(II->getName())
-    .Case("linker",   PCK_Linker)
-    .Case("lib",      PCK_Lib)
-    .Case("compiler", PCK_Compiler)
-    .Case("exestr",   PCK_ExeStr)
-    .Case("user",     PCK_User)
-    .Default(PCK_Unknown);
+      llvm::StringSwitch<PragmaMSCommentKind>(II->getName())
+          .Case("linker", PCK_Linker)
+          .Case("lib", PCK_Lib)
+          .Case("compiler", PCK_Compiler)
+          .Case("exestr", PCK_ExeStr)
+          .Case("user", PCK_User)
+          .Case("copyright", PCK_Copyright)
+          .Default(PCK_Unknown);
+
+  // Restrict copyright to AIX targets only. This could be applied for z/OS
+  // and extended with other IBM pragma comment kinds.
+  if (!PP.getTargetInfo().getTriple().isOSAIX() && Kind == PCK_Copyright) {
+    Kind = PCK_Unknown;
+  }
+
   if (Kind == PCK_Unknown) {
     PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind);
     return;
@@ -3305,6 +3316,18 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
+  // On AIX, pragma comment copyright can each appear only once in a TU.
+  if (Kind == PCK_Copyright) {
+    assert(PP.getTargetInfo().getTriple().isOSAIX() &&
+           "Pragma Comment Copyright is supported only on AIX");
+    if (SeenCopyrightInTU) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_once)
+          << II->getName();
+      return;
+    }
+    SeenCopyrightInTU = true;
+  }
+
   // Read the optional string if present.
   PP.Lex(Tok);
   std::string ArgumentString;
@@ -3331,6 +3354,10 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
+  // Accept and ignore well-formed copyright with empty string.
+  if (Kind == PCK_Copyright && ArgumentString.empty())
+    return;
+
   // If the pragma is lexically sound, notify any interested PPCallbacks.
   if (PP.getPPCallbacks())
     PP.getPPCallbacks()->PragmaComment(CommentLoc, II, ArgumentString);
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp
new file mode 100644
index 0000000000000..9020df6e737d6
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp
@@ -0,0 +1,28 @@
+// RUN: split-file %s %t
+
+// Build the module interface to a PCM
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
+// RUN:   -emit-module-interface %t/copymod.cppm -o %t/copymod.pcm
+
+// Verify that module interface emits copyright global when compiled to IR
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix -emit-llvm %t/copymod.cppm -o - \
+// RUN:   | FileCheck %s --check-prefix=CHECK-MOD
+// CHECK-MOD: @__loadtime_comment_str = internal unnamed_addr constant [10 x i8] c"module me\00", section "__loadtime_comment"
+// CHECK-MOD: @llvm.used = appending global {{.*}} @__loadtime_comment_str
+
+// Compile an importing TU that uses the prebuilt module and verify that it
+// does NOT re-emit the module's copyright global.
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
+// RUN:   -fprebuilt-module-path=%t -emit-llvm %t/importmod.cc -o - \
+// RUN:   | FileCheck %s --check-prefix=CHECK-IMPORT
+// CHECK-IMPORT-NOT: @__loadtime_comment_str
+// CHECK-IMPORT-NOT: c"module me\00"
+
+//--- copymod.cppm
+export module copymod;
+#pragma comment(copyright, "module me")
+export inline void f() {}
+
+//--- importmod.cc
+import copymod;
+void g() { f(); }
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c
new file mode 100644
index 0000000000000..98d2db416496e
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -verify
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -verify
+// RUN: %clang_cc1 %s -DTEST_EMPTY_COPYRIGHT -triple powerpc-ibm-aix -verify
+
+// RUN: %clang_cc1 %s -x c++ -triple powerpc-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -x c++ -triple powerpc64-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -x c++ -triple powerpc-ibm-aix -verify
+// RUN: %clang_cc1 %s -x c++ -triple powerpc64-ibm-aix -verify
+// RUN: %clang_cc1 %s -x c++ -DTEST_EMPTY_COPYRIGHT -triple powerpc-ibm-aix -verify
+
+#ifndef TEST_EMPTY_COPYRIGHT
+// Test basic pragma comment types
+#pragma comment(copyright, "@(#) Copyright")
+
+// Test duplicate copyright - should warn and ignore
+#pragma comment(copyright, "Duplicate Copyright") // expected-warning {{'#pragma comment copyright' can be specified only once per translation unit - ignored}}
+
+int main() { return 0; }
+
+// Check that both metadata sections are present
+// CHECK: !comment_string.loadtime = !{![[copyright:[0-9]+]]}
+
+// Check individual metadata content
+// CHECK: ![[copyright]] = !{!"@(#) Copyright"}
+
+#else
+// Test empty copyright string - valid with no warning
+#pragma comment(copyright, "") // expected-no-diagnostics
+
+int main() { return 0; }
+
+#endif
diff --git a/clang/test/CodeGen/lto-newpm-pipeline.c b/clang/test/CodeGen/lto-newpm-pipeline.c
index ea9784a76f923..b9466b9558b06 100644
--- a/clang/test/CodeGen/lto-newpm-pipeline.c
+++ b/clang/test/CodeGen/lto-newpm-pipeline.c
@@ -27,6 +27,7 @@
 
 // CHECK-FULL-O0: Running pass: VerifierPass
 // CHECK-FULL-O0-NEXT: Running analysis: VerifierAnalysis
+// CHECK-FULL-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-FULL-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
 // CHECK-FULL-O0-NEXT: Running pass: EntryExitInstrumenterPass
 // CHECK-FULL-O0-NEXT: Running pass: AlwaysInlinerPass
@@ -41,6 +42,7 @@
 
 // CHECK-THIN-O0: Running pass: VerifierPass
 // CHECK-THIN-O0-NEXT: Running analysis: VerifierAnalysis
+// CHECK-THIN-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-THIN-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
 // CHECK-THIN-O0-NEXT: Running pass: EntryExitInstrumenterPass
 // CHECK-THIN-O0-NEXT: Running pass: AlwaysInlinerPass
diff --git a/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
new file mode 100644
index 0000000000000..4c6109e1176d3
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
@@ -0,0 +1,24 @@
+//===-- LowerCommentStringPass.h - Lower Comment string metadata        --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
+#define LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+class LowerCommentStringPass : public PassInfoMixin<LowerCommentStringPass> {
+public:
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+
+  static bool isRequired() { return true; }
+};
+
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 45955426d66a0..5c014644a6150 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -361,6 +361,7 @@
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
 #include "llvm/Transforms/Utils/LoopSimplify.h"
 #include "llvm/Transforms/Utils/LoopVersioning.h"
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
 #include "llvm/Transforms/Utils/LowerGlobalDtors.h"
 #include "llvm/Transforms/Utils/LowerIFunc.h"
 #include "llvm/Transforms/Utils/LowerInvoke.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 1584d30875570..e48833528c85a 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -142,6 +142,7 @@
 #include "llvm/Transforms/Utils/ExtraPassManager.h"
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
 #include "llvm/Transforms/Utils/Mem2Reg.h"
 #include "llvm/Transforms/Utils/MoveAutoInit.h"
 #include "llvm/Transforms/Utils/NameAnonGlobals.h"
@@ -1470,6 +1471,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   const bool LTOPreLink = isLTOPreLink(LTOPhase);
   ModulePassManager MPM;
 
+  // Process copyright metadata early, before any optimizations
+  MPM.addPass(LowerCommentStringPass());
+
   // Run partial inlining pass to partially inline functions that have
   // large bodies.
   if (RunPartialInlining)
@@ -2318,6 +2322,9 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level,
 
   ModulePassManager MPM;
 
+  // Process copyright metadata at O0 before any other transformations
+  MPM.addPass(LowerCommentStringPass());
+
   // Perform pseudo probe instrumentation in O0 mode. This is for the
   // consistency between different build modes. For example, a LTO build can be
   // mixed with an O0 prelink and an O2 postlink. Loading a sample profile in
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 2cfb5b2592601..05cc8775b8d71 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -62,6 +62,7 @@ MODULE_PASS("check-debugify", NewPMCheckDebugifyPass())
 MODULE_PASS("constmerge", ConstantMergePass())
 MODULE_PASS("coro-cleanup", CoroCleanupPass())
 MODULE_PASS("coro-early", CoroEarlyPass())
+MODULE_PASS("lower-comment-string", LowerCommentStringPass())
 MODULE_PASS("cross-dso-cfi", CrossDSOCFIPass())
 MODULE_PASS("ctx-instr-gen",
             PGOInstrumentationGen(PGOInstrumentationType::CTXPROF))
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index 2b5f5cf344e60..28f86c5e96010 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -53,6 +53,7 @@ add_llvm_component_library(LLVMTransformUtils
   LoopUtils.cpp
   LoopVersioning.cpp
   LowerAtomic.cpp
+  LowerCommentStringPass.cpp
   LowerGlobalDtors.cpp
   LowerIFunc.cpp
   LowerInvoke.cpp
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
new file mode 100644
index 0000000000000..6deef2f75e0a3
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -0,0 +1,148 @@
+//===-- LowerCommentStringPass.cpp - Lower Comment string metadata -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// LowerCommentStringPass pass lowers module-level comment string metadata
+// emitted by Clang:
+//
+//     !comment_string.loadtime = !{!"Copyright ..."}
+//
+// into concrete, translation-unit–local globals.
+// This Pass is enabled only for AIX.
+// For each module (translation unit), the pass performs the following:
+//
+//   1. Creates a null-terminated, internal constant string global
+//      (`__loadtime_comment_str`) containing the copyright text in
+//      `__loadtime_comment` section.
+//
+//   2. Marks the string in `llvm.used` so it cannot be dropped by
+//      optimization or LTO.
+//
+//   3. Attaches `!implicit.ref` metadata referencing the string to every
+//      defined function in the module. The PowerPC AIX backend recognizes
+//      this metadata and emits a `.ref` directive from the function to the
+//      string, creating a concrete relocation that prevents the linker from
+//      discarding it (as long as the referencing symbol is kept).
+//
+//  Input IR:
+//     !comment_string.loadtime = !{!"Copyright"}
+//  Output IR:
+//     @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00",
+//                          section "__loadtime_comment"
+//     @llvm.used = appending global [1 x ptr] [ptr @__loadtime_comment_str]
+//
+//     define i32 @func() !implicit.ref !5 { ... }
+//     !5 = !{ptr @__loadtime_comment_str}
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/MDBuilder.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/TargetParser/Triple.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+#define DEBUG_TYPE "lower-comment-string"
+
+using namespace llvm;
+
+static cl::opt<bool>
+    DisableCopyrightMetadata("disable-lower-comment-string", cl::ReallyHidden,
+                             cl::desc("Disable LowerCommentString pass."),
+                             cl::init(false));
+
+static bool isAIXTriple(const Module &M) {
+  return Triple(M.getTargetTriple()).isOSAIX();
+}
+
+P...
[truncated]

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Feb 3, 2026

@llvm/pr-subscribers-backend-aarch64

Author: Tony Varghese (tonykuttai)

Changes
  • Emit !aix.copyright.comment from Clang for the pragma.
  • Lower it in LLVM to a TU-local string + llvm.used + !implicit.ref.
  • Add module-import and backend relocation tests.

This change builds on top of XCOFF associated metadata and was reviewed at [PowerPC][AIX] Support #pragma comment copyright for AIX


Patch is 29.81 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/178184.diff

22 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+4)
  • (modified) clang/include/clang/Basic/PragmaKinds.h (+2-1)
  • (modified) clang/lib/AST/TextNodeDumper.cpp (+3)
  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+37)
  • (modified) clang/lib/CodeGen/CodeGenModule.h (+5)
  • (modified) clang/lib/Parse/ParsePragma.cpp (+36-9)
  • (added) clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp (+28)
  • (added) clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c (+34)
  • (modified) clang/test/CodeGen/lto-newpm-pipeline.c (+2)
  • (added) llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h (+24)
  • (modified) llvm/lib/Passes/PassBuilder.cpp (+1)
  • (modified) llvm/lib/Passes/PassBuilderPipelines.cpp (+7)
  • (modified) llvm/lib/Passes/PassRegistry.def (+1)
  • (modified) llvm/lib/Transforms/Utils/CMakeLists.txt (+1)
  • (added) llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp (+148)
  • (modified) llvm/test/CodeGen/AArch64/print-pipeline-passes.ll (+1-1)
  • (modified) llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll (+1-1)
  • (modified) llvm/test/Other/new-pm-defaults.ll (+1)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-defaults.ll (+1)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll (+1)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll (+1)
  • (added) llvm/test/Transforms/LowerCommentString/lower-comment-string.ll (+59)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 457d3644de35a..ede6a90a31cc6 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1358,6 +1358,10 @@ def err_pragma_comment_unknown_kind : Error<"unknown kind of pragma comment">;
 // PS4 recognizes only #pragma comment(lib)
 def warn_pragma_comment_ignored : Warning<"'#pragma comment %0' ignored">,
   InGroup<IgnoredPragmas>;
+def warn_pragma_comment_once
+    : Warning<"'#pragma comment %0' "
+              "can be specified only once per translation unit - ignored">,
+      InGroup<IgnoredPragmas>;
 // - #pragma detect_mismatch
 def err_pragma_detect_mismatch_malformed : Error<
   "pragma detect_mismatch is malformed; it requires two comma-separated "
diff --git a/clang/include/clang/Basic/PragmaKinds.h b/clang/include/clang/Basic/PragmaKinds.h
index 42f049f7323d2..52ca58855d460 100644
--- a/clang/include/clang/Basic/PragmaKinds.h
+++ b/clang/include/clang/Basic/PragmaKinds.h
@@ -17,7 +17,8 @@ enum PragmaMSCommentKind {
   PCK_Lib,      // #pragma comment(lib, ...)
   PCK_Compiler, // #pragma comment(compiler, ...)
   PCK_ExeStr,   // #pragma comment(exestr, ...)
-  PCK_User      // #pragma comment(user, ...)
+  PCK_User,     // #pragma comment(user, ...)
+  PCK_Copyright // #pragma comment(copyright, ...)
 };
 
 enum PragmaMSStructKind {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 7bc0404db1bee..c58c33f95c193 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2526,6 +2526,9 @@ void TextNodeDumper::VisitPragmaCommentDecl(const PragmaCommentDecl *D) {
   case PCK_User:
     OS << "user";
     break;
+  case PCK_Copyright:
+    OS << "copyright";
+    break;
   }
   StringRef Arg = D->getArg();
   if (!Arg.empty())
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index d50c9605a30b3..e63adc98a95aa 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1588,6 +1588,12 @@ void CodeGenModule::Release() {
 
   EmitBackendOptionsMetadata(getCodeGenOpts());
 
+  // Emit copyright metadata
+  if (LoadTimeComment) {
+    auto *NMD = getModule().getOrInsertNamedMetadata("comment_string.loadtime");
+    NMD->addOperand(LoadTimeComment);
+  }
+
   // If there is device offloading code embed it in the host now.
   EmbedObject(&getModule(), CodeGenOpts, *getFileSystem(), getDiags());
 
@@ -3481,6 +3487,31 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
   LinkerOptionsMetadata.push_back(llvm::MDNode::get(C, MDOpts));
 }
 
+/// Process AIX copyright pragma and create LLVM metadata.
+/// #pragma comment(copyright, "string") embed copyright
+/// information into the object file's loader section.
+///
+/// Example: #pragma comment(copyright, "Copyright IBM Corp. 2024")
+///
+/// This should only be called once per translation unit.
+void CodeGenModule::ProcessPragmaComment(PragmaMSCommentKind Kind,
+                                         StringRef Comment) {
+  // Ensure we are only processing Copyright Pragmas
+  assert(Kind == PCK_Copyright &&
+         "Unexpected pragma comment kind, ProcessPragmaComment should only be "
+         "called for PCK_Copyright");
+
+  // Only one copyright pragma allowed per translation unit
+  if (LoadTimeComment) {
+    return;
+  }
+
+  // Create llvm metadata with the comment string
+  auto &C = getLLVMContext();
+  llvm::Metadata *Ops[] = {llvm::MDString::get(C, Comment)};
+  LoadTimeComment = llvm::MDNode::get(C, Ops);
+}
+
 /// Add link options implied by the given module, including modules
 /// it depends on, using a postorder walk.
 static void addLinkOptionsPostorder(CodeGenModule &CGM, Module *Mod,
@@ -7683,6 +7714,12 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
     case PCK_Lib:
         AddDependentLib(PCD->getArg());
       break;
+    case PCK_Copyright:
+      // Skip pragmas deserialized from modules/PCHs. Process the pragma comment
+      // only if it originated in this TU and the target OS is AIX.
+      if (!PCD->isFromASTFile() && getTriple().isOSAIX())
+        ProcessPragmaComment(PCD->getCommentKind(), PCD->getArg());
+      break;
     case PCK_Compiler:
     case PCK_ExeStr:
     case PCK_User:
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index cc18e21d45759..21a75958dcc9d 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -587,6 +587,9 @@ class CodeGenModule : public CodeGenTypeCache {
   /// A vector of metadata strings for dependent libraries for ELF.
   SmallVector<llvm::MDNode *, 16> ELFDependentLibraries;
 
+  /// Metadata for copyright pragma comment (if present).
+  llvm::MDNode *LoadTimeComment = nullptr;
+
   /// @name Cache for Objective-C runtime types
   /// @{
 
@@ -1490,6 +1493,8 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Appends a dependent lib to the appropriate metadata value.
   void AddDependentLib(StringRef Lib);
 
+  /// Process pragma comment
+  void ProcessPragmaComment(PragmaMSCommentKind Kind, StringRef Comment);
 
   llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD);
 
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index def2817c930b2..5865319470511 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -237,6 +237,7 @@ struct PragmaCommentHandler : public PragmaHandler {
 
 private:
   Sema &Actions;
+  bool SeenCopyrightInTU = false; // TU-scoped
 };
 
 struct PragmaDetectMismatchHandler : public PragmaHandler {
@@ -480,7 +481,8 @@ void Parser::initializePragmaHandlers() {
   PP.AddPragmaHandler(OpenACCHandler.get());
 
   if (getLangOpts().MicrosoftExt ||
-      getTargetInfo().getTriple().isOSBinFormatELF()) {
+      getTargetInfo().getTriple().isOSBinFormatELF() ||
+      getTargetInfo().getTriple().isOSAIX()) {
     MSCommentHandler = std::make_unique<PragmaCommentHandler>(Actions);
     PP.AddPragmaHandler(MSCommentHandler.get());
   }
@@ -607,7 +609,8 @@ void Parser::resetPragmaHandlers() {
   OpenACCHandler.reset();
 
   if (getLangOpts().MicrosoftExt ||
-      getTargetInfo().getTriple().isOSBinFormatELF()) {
+      getTargetInfo().getTriple().isOSBinFormatELF() ||
+      getTargetInfo().getTriple().isOSAIX()) {
     PP.RemovePragmaHandler(MSCommentHandler.get());
     MSCommentHandler.reset();
   }
@@ -3287,13 +3290,21 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
   // Verify that this is one of the 5 explicitly listed options.
   IdentifierInfo *II = Tok.getIdentifierInfo();
   PragmaMSCommentKind Kind =
-    llvm::StringSwitch<PragmaMSCommentKind>(II->getName())
-    .Case("linker",   PCK_Linker)
-    .Case("lib",      PCK_Lib)
-    .Case("compiler", PCK_Compiler)
-    .Case("exestr",   PCK_ExeStr)
-    .Case("user",     PCK_User)
-    .Default(PCK_Unknown);
+      llvm::StringSwitch<PragmaMSCommentKind>(II->getName())
+          .Case("linker", PCK_Linker)
+          .Case("lib", PCK_Lib)
+          .Case("compiler", PCK_Compiler)
+          .Case("exestr", PCK_ExeStr)
+          .Case("user", PCK_User)
+          .Case("copyright", PCK_Copyright)
+          .Default(PCK_Unknown);
+
+  // Restrict copyright to AIX targets only. This could be applied for z/OS
+  // and extended with other IBM pragma comment kinds.
+  if (!PP.getTargetInfo().getTriple().isOSAIX() && Kind == PCK_Copyright) {
+    Kind = PCK_Unknown;
+  }
+
   if (Kind == PCK_Unknown) {
     PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind);
     return;
@@ -3305,6 +3316,18 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
+  // On AIX, pragma comment copyright can each appear only once in a TU.
+  if (Kind == PCK_Copyright) {
+    assert(PP.getTargetInfo().getTriple().isOSAIX() &&
+           "Pragma Comment Copyright is supported only on AIX");
+    if (SeenCopyrightInTU) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_once)
+          << II->getName();
+      return;
+    }
+    SeenCopyrightInTU = true;
+  }
+
   // Read the optional string if present.
   PP.Lex(Tok);
   std::string ArgumentString;
@@ -3331,6 +3354,10 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
+  // Accept and ignore well-formed copyright with empty string.
+  if (Kind == PCK_Copyright && ArgumentString.empty())
+    return;
+
   // If the pragma is lexically sound, notify any interested PPCallbacks.
   if (PP.getPPCallbacks())
     PP.getPPCallbacks()->PragmaComment(CommentLoc, II, ArgumentString);
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp
new file mode 100644
index 0000000000000..9020df6e737d6
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp
@@ -0,0 +1,28 @@
+// RUN: split-file %s %t
+
+// Build the module interface to a PCM
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
+// RUN:   -emit-module-interface %t/copymod.cppm -o %t/copymod.pcm
+
+// Verify that module interface emits copyright global when compiled to IR
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix -emit-llvm %t/copymod.cppm -o - \
+// RUN:   | FileCheck %s --check-prefix=CHECK-MOD
+// CHECK-MOD: @__loadtime_comment_str = internal unnamed_addr constant [10 x i8] c"module me\00", section "__loadtime_comment"
+// CHECK-MOD: @llvm.used = appending global {{.*}} @__loadtime_comment_str
+
+// Compile an importing TU that uses the prebuilt module and verify that it
+// does NOT re-emit the module's copyright global.
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
+// RUN:   -fprebuilt-module-path=%t -emit-llvm %t/importmod.cc -o - \
+// RUN:   | FileCheck %s --check-prefix=CHECK-IMPORT
+// CHECK-IMPORT-NOT: @__loadtime_comment_str
+// CHECK-IMPORT-NOT: c"module me\00"
+
+//--- copymod.cppm
+export module copymod;
+#pragma comment(copyright, "module me")
+export inline void f() {}
+
+//--- importmod.cc
+import copymod;
+void g() { f(); }
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c
new file mode 100644
index 0000000000000..98d2db416496e
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -verify
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -verify
+// RUN: %clang_cc1 %s -DTEST_EMPTY_COPYRIGHT -triple powerpc-ibm-aix -verify
+
+// RUN: %clang_cc1 %s -x c++ -triple powerpc-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -x c++ -triple powerpc64-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -x c++ -triple powerpc-ibm-aix -verify
+// RUN: %clang_cc1 %s -x c++ -triple powerpc64-ibm-aix -verify
+// RUN: %clang_cc1 %s -x c++ -DTEST_EMPTY_COPYRIGHT -triple powerpc-ibm-aix -verify
+
+#ifndef TEST_EMPTY_COPYRIGHT
+// Test basic pragma comment types
+#pragma comment(copyright, "@(#) Copyright")
+
+// Test duplicate copyright - should warn and ignore
+#pragma comment(copyright, "Duplicate Copyright") // expected-warning {{'#pragma comment copyright' can be specified only once per translation unit - ignored}}
+
+int main() { return 0; }
+
+// Check that both metadata sections are present
+// CHECK: !comment_string.loadtime = !{![[copyright:[0-9]+]]}
+
+// Check individual metadata content
+// CHECK: ![[copyright]] = !{!"@(#) Copyright"}
+
+#else
+// Test empty copyright string - valid with no warning
+#pragma comment(copyright, "") // expected-no-diagnostics
+
+int main() { return 0; }
+
+#endif
diff --git a/clang/test/CodeGen/lto-newpm-pipeline.c b/clang/test/CodeGen/lto-newpm-pipeline.c
index ea9784a76f923..b9466b9558b06 100644
--- a/clang/test/CodeGen/lto-newpm-pipeline.c
+++ b/clang/test/CodeGen/lto-newpm-pipeline.c
@@ -27,6 +27,7 @@
 
 // CHECK-FULL-O0: Running pass: VerifierPass
 // CHECK-FULL-O0-NEXT: Running analysis: VerifierAnalysis
+// CHECK-FULL-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-FULL-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
 // CHECK-FULL-O0-NEXT: Running pass: EntryExitInstrumenterPass
 // CHECK-FULL-O0-NEXT: Running pass: AlwaysInlinerPass
@@ -41,6 +42,7 @@
 
 // CHECK-THIN-O0: Running pass: VerifierPass
 // CHECK-THIN-O0-NEXT: Running analysis: VerifierAnalysis
+// CHECK-THIN-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-THIN-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
 // CHECK-THIN-O0-NEXT: Running pass: EntryExitInstrumenterPass
 // CHECK-THIN-O0-NEXT: Running pass: AlwaysInlinerPass
diff --git a/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
new file mode 100644
index 0000000000000..4c6109e1176d3
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
@@ -0,0 +1,24 @@
+//===-- LowerCommentStringPass.h - Lower Comment string metadata        --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
+#define LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+class LowerCommentStringPass : public PassInfoMixin<LowerCommentStringPass> {
+public:
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+
+  static bool isRequired() { return true; }
+};
+
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 45955426d66a0..5c014644a6150 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -361,6 +361,7 @@
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
 #include "llvm/Transforms/Utils/LoopSimplify.h"
 #include "llvm/Transforms/Utils/LoopVersioning.h"
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
 #include "llvm/Transforms/Utils/LowerGlobalDtors.h"
 #include "llvm/Transforms/Utils/LowerIFunc.h"
 #include "llvm/Transforms/Utils/LowerInvoke.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 1584d30875570..e48833528c85a 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -142,6 +142,7 @@
 #include "llvm/Transforms/Utils/ExtraPassManager.h"
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
 #include "llvm/Transforms/Utils/Mem2Reg.h"
 #include "llvm/Transforms/Utils/MoveAutoInit.h"
 #include "llvm/Transforms/Utils/NameAnonGlobals.h"
@@ -1470,6 +1471,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   const bool LTOPreLink = isLTOPreLink(LTOPhase);
   ModulePassManager MPM;
 
+  // Process copyright metadata early, before any optimizations
+  MPM.addPass(LowerCommentStringPass());
+
   // Run partial inlining pass to partially inline functions that have
   // large bodies.
   if (RunPartialInlining)
@@ -2318,6 +2322,9 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level,
 
   ModulePassManager MPM;
 
+  // Process copyright metadata at O0 before any other transformations
+  MPM.addPass(LowerCommentStringPass());
+
   // Perform pseudo probe instrumentation in O0 mode. This is for the
   // consistency between different build modes. For example, a LTO build can be
   // mixed with an O0 prelink and an O2 postlink. Loading a sample profile in
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 2cfb5b2592601..05cc8775b8d71 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -62,6 +62,7 @@ MODULE_PASS("check-debugify", NewPMCheckDebugifyPass())
 MODULE_PASS("constmerge", ConstantMergePass())
 MODULE_PASS("coro-cleanup", CoroCleanupPass())
 MODULE_PASS("coro-early", CoroEarlyPass())
+MODULE_PASS("lower-comment-string", LowerCommentStringPass())
 MODULE_PASS("cross-dso-cfi", CrossDSOCFIPass())
 MODULE_PASS("ctx-instr-gen",
             PGOInstrumentationGen(PGOInstrumentationType::CTXPROF))
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index 2b5f5cf344e60..28f86c5e96010 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -53,6 +53,7 @@ add_llvm_component_library(LLVMTransformUtils
   LoopUtils.cpp
   LoopVersioning.cpp
   LowerAtomic.cpp
+  LowerCommentStringPass.cpp
   LowerGlobalDtors.cpp
   LowerIFunc.cpp
   LowerInvoke.cpp
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
new file mode 100644
index 0000000000000..6deef2f75e0a3
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -0,0 +1,148 @@
+//===-- LowerCommentStringPass.cpp - Lower Comment string metadata -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// LowerCommentStringPass pass lowers module-level comment string metadata
+// emitted by Clang:
+//
+//     !comment_string.loadtime = !{!"Copyright ..."}
+//
+// into concrete, translation-unit–local globals.
+// This Pass is enabled only for AIX.
+// For each module (translation unit), the pass performs the following:
+//
+//   1. Creates a null-terminated, internal constant string global
+//      (`__loadtime_comment_str`) containing the copyright text in
+//      `__loadtime_comment` section.
+//
+//   2. Marks the string in `llvm.used` so it cannot be dropped by
+//      optimization or LTO.
+//
+//   3. Attaches `!implicit.ref` metadata referencing the string to every
+//      defined function in the module. The PowerPC AIX backend recognizes
+//      this metadata and emits a `.ref` directive from the function to the
+//      string, creating a concrete relocation that prevents the linker from
+//      discarding it (as long as the referencing symbol is kept).
+//
+//  Input IR:
+//     !comment_string.loadtime = !{!"Copyright"}
+//  Output IR:
+//     @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00",
+//                          section "__loadtime_comment"
+//     @llvm.used = appending global [1 x ptr] [ptr @__loadtime_comment_str]
+//
+//     define i32 @func() !implicit.ref !5 { ... }
+//     !5 = !{ptr @__loadtime_comment_str}
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/MDBuilder.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/TargetParser/Triple.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+#define DEBUG_TYPE "lower-comment-string"
+
+using namespace llvm;
+
+static cl::opt<bool>
+    DisableCopyrightMetadata("disable-lower-comment-string", cl::ReallyHidden,
+                             cl::desc("Disable LowerCommentString pass."),
+                             cl::init(false));
+
+static bool isAIXTriple(const Module &M) {
+  return Triple(M.getTargetTriple()).isOSAIX();
+}
+
+P...
[truncated]

@tonykuttai tonykuttai requested a review from perry-ca February 3, 2026 07:36
@tonykuttai
Copy link
Copy Markdown
Contributor Author

The following tests had to be modified as we are keeping the LowerCommentStringPass in both PassBuilder::buildO0DefaultPipeline and PassBuilder::buildModuleOptimizationPipeline.

Failed Tests (6):
  Clang :: CodeGen/lto-newpm-pipeline.c
  LLVM :: CodeGen/AArch64/print-pipeline-passes.ll
  LLVM :: Other/new-pm-defaults.ll
  LLVM :: Other/new-pm-thinlto-postlink-defaults.ll
  LLVM :: Other/new-pm-thinlto-postlink-pgo-defaults.ll
  LLVM :: Other/new-pm-thinlto-postlink-samplepgo-defaults.ll

@tonykuttai tonykuttai requested a review from redstar February 3, 2026 07:41
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch from 2b44311 to 2abc275 Compare February 6, 2026 09:16
@tonykuttai tonykuttai requested a review from perry-ca February 6, 2026 09:17
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Copy link
Copy Markdown
Member

@hubert-reinterpretcast hubert-reinterpretcast left a comment

Choose a reason for hiding this comment

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

Reviewed Clang changes (excluding tests).

Comment thread clang/include/clang/Basic/DiagnosticParseKinds.td Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch from 2c769c5 to 06546f7 Compare February 10, 2026 06:44
Copy link
Copy Markdown
Member

@hubert-reinterpretcast hubert-reinterpretcast left a comment

Choose a reason for hiding this comment

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

Next round of review of Clang changes (excluding tests).

Comment thread clang/include/clang/Basic/DiagnosticParseKinds.td Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch 2 times, most recently from 08540ef to 0c36839 Compare April 14, 2026 07:39
@tonykuttai tonykuttai requested review from cor3ntin and w2yehia April 15, 2026 07:43
@tonykuttai
Copy link
Copy Markdown
Contributor Author

Addressed the review comments.

@hubert-reinterpretcast @w2yehia @perry-ca

@@ -1472,6 +1473,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
const bool LTOPreLink = isLTOPreLink(LTOPhase);
ModulePassManager MPM;

// Process copyright metadata early, before any optimizations
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why are we processing the metadata early rather than late (but not as late as link-time LTO)?

Copy link
Copy Markdown
Contributor Author

@tonykuttai tonykuttai Apr 16, 2026

Choose a reason for hiding this comment

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

The design rationale was to get an IR pass to replace the metadata with an IR definition and create the necessary .refs in the IR from the definitions in the IR. Thus by keeping the pass early per TU, the metadata only survives from the point of the front-end codegen until that IR pass. By the time LTO runs, the metadata is already consumed and concrete globals + .ref directives are already in the object file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I did say "not as late as link-time LTO".
I meant late in the IR-IR pipeline during prelink (-flto -c) LTO or regular nonLTO (-c) compilation.
Looking at the source, at the end of buildModuleOptimizationPipeline might be what I meant.
The advantage of doing it late is that any compiler generated functions will also have a .ref to the copyright variables.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You are right. I have moved the pass to the end of the pipelines in buildO0DefaultPipeline, buildModuleOptimizationPipeline and buildThinLTOPreLinkDefaultPipeline.

@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch 2 times, most recently from 29019ca to 865add9 Compare April 20, 2026 18:32
@tonykuttai tonykuttai requested a review from w2yehia April 21, 2026 09:36
Copy link
Copy Markdown
Member

@hubert-reinterpretcast hubert-reinterpretcast left a comment

Choose a reason for hiding this comment

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

I have limited my review to the front end (and front-end tests) only.

Comment thread clang/test/Preprocessor/pragma-comment.c
Comment thread clang/test/Preprocessor/pragma-comment.c Outdated
Comment thread clang/test/CodeGen/PowerPC/pragma-comment.c
Comment thread clang/docs/LanguageExtensions.rst Outdated
Comment thread clang/docs/LanguageExtensions.rst Outdated
Comment thread clang/docs/LanguageExtensions.rst Outdated
Comment thread clang/docs/LanguageExtensions.rst Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.h Outdated
Comment thread clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp Outdated
Comment thread llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp Outdated
Comment thread llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp Outdated
Comment thread llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp Outdated
Comment thread llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp Outdated
@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch from 83acf57 to 7d468a5 Compare April 28, 2026 06:17
@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch from f010482 to 9c2b2fe Compare May 5, 2026 05:50
@hubert-reinterpretcast
Copy link
Copy Markdown
Member

hubert-reinterpretcast commented May 7, 2026

@tonykuttai, for future reference, it is easier for reviewers to do incremental reviews if force pushes are avoided on the PR branch.

Note also that for 9c2b2fe, the commit message for the squashed commit is out-of-date due to changes made during the PR review process.

// global and attach !implicit.ref to all defined functions. Running late
// ensures compiler-generated functions (e.g. from inlining, coroutines)
// are also anchored to the copyright string via .ref directives.
MPM.addPass(LowerCommentStringPass());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this pass being added to the opt pipeline so that we can get early mapping from functions to the string with the implicit.ref? Since its AIX only I would have expected it to get put into the target specific pipeline we build for llc instead of getting run for all targets.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Primary motivation was to convert !comment_string.loadtime metadata to LLVM IR earlier in the pipeline. Also it needs to run in both pre-link and post-link LTO phases, which is simpler in opt. The pass itself is bailed out early for non-aix targets. Having it in the general pipeline makes future extension much easier. update isSupportedTarget() when adding z/OS or other targets that need similar load-time comment functionality.

@tonykuttai tonykuttai requested a review from mandlebug May 9, 2026 00:54
Copy link
Copy Markdown
Member

@hubert-reinterpretcast hubert-reinterpretcast left a comment

Choose a reason for hiding this comment

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

LGTM (for the front-end) with specific suggestions to address code-style issues.

@tonykuttai, the general take-away from this round of review is that code comments should provide value to the understanding of the code where it appears and that said value needs to be balanced against the drafting, review, and maintenance burden of having the comment.

Especially for comments of a "design overview" nature, the information can instead be added to PR descriptions or separate documentation artifacts.

Edit: One thing that I did not state explicitly is that, among other issues, the comments were too AIX-specific.

Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
Comment thread clang/lib/Parse/ParsePragma.cpp Outdated
Comment thread llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp Outdated
Comment thread clang/test/Preprocessor/pragma-comment.c Outdated
Comment thread clang/test/Preprocessor/pragma-comment.c Outdated
Comment thread clang/docs/LanguageExtensions.rst Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.h Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
Comment thread clang/lib/CodeGen/CodeGenModule.h Outdated
@tonykuttai tonykuttai force-pushed the tvarghese/pragma-copyright-comment-update branch from 9116b1e to 7464d43 Compare May 13, 2026 05:44
@llvmorg-github-actions llvmorg-github-actions Bot added the LTO Link time optimization (regular/full LTO or ThinLTO) label May 23, 2026
Tony Varghese and others added 4 commits May 23, 2026 20:20
- Emit !aix.copyright.comment from Clang for the pragma.
- Lower it in LLVM to a TU-local string + llvm.used + !implicit.ref.
- Add module-import and backend relocation tests.
Co-authored-by: Hubert Tong <hubert.reinterpretcast@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:AArch64 backend:Hexagon backend:PowerPC clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" llvm:transforms LTO Link time optimization (regular/full LTO or ThinLTO)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants