https://github.com/tonykuttai updated 
https://github.com/llvm/llvm-project/pull/178184

>From 01a8d4bb6b8428c21453ca98674f663ecf3b2b12 Mon Sep 17 00:00:00 2001
From: Tony Varghese <[email protected]>
Date: Mon, 2 Feb 2026 23:19:10 -0500
Subject: [PATCH 1/6] [PowerPC][AIX] Support #pragma comment copyright for AIX
 targets.

The #pragma comment(copyright, "string") for AIX/XCOFF targets embeds
the copyright string into the object file and is loadable during the
runtime.

The implementation flows through three stages:
- Clang emits !comment_string.loadtime named metadata when it encounters
  the pragma in ProcessPragmaCommentCopyright().
- LowerCommentStringPass reads this metadata and materializes a
  TU-local string global (__loadtime_comment_str), adds it to
  llvm.compiler.used to prevent elimination, and attaches !implicit.ref
  metadata to reference it from functions in the module.
- The AIX backend reads !implicit.ref and emits .ref directives to
  the __loattime_comment_str.

Co-authored-by: Hubert Tong <[email protected]>
---
 clang/docs/LanguageExtensions.rst             |  23 +++
 .../clang/Basic/DiagnosticParseKinds.td       |   4 +
 clang/include/clang/Basic/PragmaKinds.h       |   3 +-
 clang/lib/AST/TextNodeDumper.cpp              |   3 +
 clang/lib/CodeGen/CodeGenModule.cpp           |  41 +++++
 clang/lib/CodeGen/CodeGenModule.h             |  11 +-
 clang/lib/Parse/ParsePragma.cpp               |  37 ++++-
 clang/test/CXX/module/cpp.pre/module_decl.cpp |   2 +-
 .../pragma-comment-copyright-modules.cpp      |  30 ++++
 clang/test/CodeGen/PowerPC/pragma-comment.c   |  22 +++
 clang/test/CodeGen/lto-newpm-pipeline.c       |   2 +
 clang/test/Preprocessor/pragma-comment.c      |  92 +++++++++++
 .../Transforms/Utils/LowerCommentStringPass.h |  24 +++
 llvm/lib/Passes/PassBuilder.cpp               |   1 +
 llvm/lib/Passes/PassBuilderPipelines.cpp      |  16 ++
 llvm/lib/Passes/PassRegistry.def              |   1 +
 llvm/lib/Transforms/Utils/CMakeLists.txt      |   1 +
 .../Utils/LowerCommentStringPass.cpp          | 150 ++++++++++++++++++
 .../CodeGen/AArch64/print-pipeline-passes.ll  |   2 +-
 .../CodeGen/Hexagon/print-pipeline-passes.ll  |   2 +-
 .../PowerPC/pragma-comment-copyright-lto.ll   |  67 ++++++++
 llvm/test/Other/new-pm-O0-defaults.ll         |   1 +
 llvm/test/Other/new-pm-defaults.ll            |   1 +
 .../Other/new-pm-thinlto-postlink-defaults.ll |   1 +
 .../new-pm-thinlto-postlink-pgo-defaults.ll   |   1 +
 ...-pm-thinlto-postlink-samplepgo-defaults.ll |   1 +
 .../Other/new-pm-thinlto-prelink-defaults.ll  |   1 +
 .../new-pm-thinlto-prelink-pgo-defaults.ll    |   1 +
 ...w-pm-thinlto-prelink-samplepgo-defaults.ll |   1 +
 .../lower-comment-string.ll                   |  41 +++++
 30 files changed, 574 insertions(+), 9 deletions(-)
 create mode 100644 
clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
 create mode 100644 clang/test/CodeGen/PowerPC/pragma-comment.c
 create mode 100644 clang/test/Preprocessor/pragma-comment.c
 create mode 100644 llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
 create mode 100644 llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
 create mode 100644 llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
 create mode 100644 
llvm/test/Transforms/LowerCommentString/lower-comment-string.ll

diff --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index fbb9947f39d3e..992c6f2927be8 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6821,6 +6821,29 @@ The ``#pragma comment(lib, ...)`` directive is supported 
on all ELF targets.
 The second parameter is the library name (without the traditional Unix prefix 
of
 ``lib``).  This allows you to provide an implicit link of dependent libraries.
 
+Embedding Copyright Information on AIX
+======================================
+Clang supports the ``#pragma comment(copyright, "string")`` directive for AIX
+targets. This directive embeds a copyright or identifying string into the
+compiled object file. The string is included in the final executable or shared
+library and loaded into memory at program runtime. The directive is ignored on
+non-AIX targets.
+
+.. code-block:: c
+
+   #pragma comment(copyright, "string-literal")
+
+The second argument is an ordinary string literal. Concatenated ordinary string
+literals are also accepted. The directive is intended to appear at file scope;
+Clang treats it as being at file scope when it appears within other scopes.
+
+Interaction with C++20 Modules
+-------------------------------
+
+When ``#pragma comment(copyright, ...)`` appears in a C++20 module interface
+unit, the copyright string is embedded only in the object file compiled from
+that interface unit. Importing TUs do not re-emit the string.
+
 Evaluating Object Size
 ======================
 
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 7bcd1870a2600..91632f8d2376c 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1360,6 +1360,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' "
+              "ignored: it can be specified only once per translation unit">,
+      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 abb6869a2b46e..d90090b82fb7a 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2608,6 +2608,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 50089f4a5016a..9dedbed54c77a 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1744,6 +1744,8 @@ void CodeGenModule::Release() {
 
   EmitBackendOptionsMetadata(getCodeGenOpts());
 
+  EmitLoadTimeComment();
+
   // If there is device offloading code embed it in the host now.
   EmbedObject(&getModule(), CodeGenOpts, *getFileSystem(), getDiags());
 
@@ -3687,6 +3689,35 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
   LinkerOptionsMetadata.push_back(llvm::MDNode::get(C, MDOpts));
 }
 
+/// Process copyright pragma and create LLVM metadata.
+///
+/// Only one copyright pragma is allowed per translation unit. Subsequent
+/// pragmas in the same TU are ignored with a warning at the parse level.
+void CodeGenModule::ProcessPragmaCommentCopyright(StringRef Comment,
+                                                  bool isFromASTFile) {
+  assert(getTriple().isOSAIX() &&
+         "pragma comment copyright is supported only when targeting AIX");
+
+  // Interaction with C++20 Modules and PCH:
+  // When a module interface unit containing a copyright pragma is imported,
+  // Clang deserializes the PragmaCommentDecl from the precompiled module file
+  // (.pcm) into the importing TU's AST. isFromASTFile() returns true for such
+  // deserialized declarations. We skip those to ensure only the module
+  // interface TU that originally parsed the pragma emits the copyright 
metadata
+  // -- not every TU that imports it. This prevents duplicate copyright strings
+  // in the final binary.
+  if (isFromASTFile)
+    return;
+
+  assert(!LoadTimeComment &&
+         "Only one copyright pragma allowed per translation unit.");
+
+  // 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,
@@ -4207,6 +4238,13 @@ bool CodeGenModule::MustBeEmitted(const ValueDecl 
*Global) {
   return getContext().DeclMustBeEmitted(Global);
 }
 
+void CodeGenModule::EmitLoadTimeComment() {
+  if (LoadTimeComment) {
+    auto *NMD = 
getModule().getOrInsertNamedMetadata("comment_string.loadtime");
+    NMD->addOperand(LoadTimeComment);
+  }
+}
+
 bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
   // In OpenMP 5.0 variables and function may be marked as
   // device_type(host/nohost) and we should not emit them eagerly unless we 
sure
@@ -7966,6 +8004,9 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
     case PCK_Lib:
         AddDependentLib(PCD->getArg());
       break;
+    case PCK_Copyright:
+      ProcessPragmaCommentCopyright(PCD->getArg(), PCD->isFromASTFile());
+      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 959e02924a9f7..2106ad26d54f0 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -619,6 +619,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
   /// @{
 
@@ -1544,7 +1547,6 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Appends a dependent lib to the appropriate metadata value.
   void AddDependentLib(StringRef Lib);
 
-
   llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD);
 
   void setFunctionLinkage(GlobalDecl GD, llvm::Function *F) {
@@ -1953,6 +1955,9 @@ class CodeGenModule : public CodeGenTypeCache {
   ABIArgInfo convertABIArgInfo(const llvm::abi::ArgInfo &AbiInfo,
                                QualType Type);
 
+  /// Process #pragma comment(copyright, ...).
+  void ProcessPragmaCommentCopyright(StringRef Comment, bool isFromASTFile);
+
   bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) 
const;
 
   llvm::Constant *GetOrCreateLLVMFunction(
@@ -2166,6 +2171,10 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Emit deactivation symbols for any PFP fields whose offset is taken with
   /// offsetof.
   void emitPFPFieldsWithEvaluatedOffset();
+
+  /// Emit the load-time comment metadata (e.g., from
+  /// #pragma comment(copyright, ...)) for the translation unit.
+  void EmitLoadTimeComment();
 };
 
 }  // end namespace CodeGen
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 237874de9f8a3..f58d2c70b7d98 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();
   }
@@ -3269,7 +3272,9 @@ void 
PragmaDetectMismatchHandler::HandlePragma(Preprocessor &PP,
 /// \code
 ///   #pragma comment(linker, "foo")
 /// \endcode
-/// 'linker' is one of five identifiers: compiler, exestr, lib, linker, user.
+/// 'linker' is one of six identifiers: compiler, copyright, exestr, lib,
+/// linker, user.
+///
 /// "foo" is a string, which is fully macro expanded, and permits string
 /// concatenation, embedded escape characters etc.  See MSDN for more details.
 void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
@@ -3289,7 +3294,7 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
-  // Verify that this is one of the 5 explicitly listed options.
+  // Verify that this is one of the 6 explicitly listed options.
   IdentifierInfo *II = Tok.getIdentifierInfo();
   PragmaMSCommentKind Kind =
       llvm::StringSwitch<PragmaMSCommentKind>(II->getName())
@@ -3298,18 +3303,38 @@ void PragmaCommentHandler::HandlePragma(Preprocessor 
&PP,
           .Case("compiler", PCK_Compiler)
           .Case("exestr", PCK_ExeStr)
           .Case("user", PCK_User)
+          .Case("copyright", PCK_Copyright)
           .Default(PCK_Unknown);
   if (Kind == PCK_Unknown) {
     PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind);
     return;
   }
 
+  if (PP.getTargetInfo().getTriple().isOSAIX() && Kind != PCK_Copyright) {
+    // Currently, pragma comment kinds aside from "copyright" are not fully
+    // implemented by Clang for AIX targets.
+    PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_ignored)
+        << II->getName();
+    return;
+  }
+
   if (PP.getTargetInfo().getTriple().isOSBinFormatELF() && Kind != PCK_Lib) {
     PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_ignored)
         << II->getName();
     return;
   }
 
+  // Handle pragma comment copyright kind.
+  if (Kind == PCK_Copyright) {
+    if (SeenCopyrightInTU) {
+      // pragma comment copyright can each appear only once in a TU.
+      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;
@@ -3336,6 +3361,10 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
+  // Skip further processing for well-formed copyright with an 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/CXX/module/cpp.pre/module_decl.cpp 
b/clang/test/CXX/module/cpp.pre/module_decl.cpp
index 5c29aeff1b632..1d964fab6acb9 100644
--- a/clang/test/CXX/module/cpp.pre/module_decl.cpp
+++ b/clang/test/CXX/module/cpp.pre/module_decl.cpp
@@ -5,7 +5,7 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/gnu_line_marker.cpp 
-verify -o %t/gnu_line_marker.pcm
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/include.cpp -verify -o 
%t/include.pcm
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/ident.cpp -verify -o 
%t/ident.pcm
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_comment.cpp 
-verify -o %t/pragma_comment.pcm
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-win32 -emit-module-interface 
%t/pragma_comment.cpp -verify -o %t/pragma_comment.pcm
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_mark.cpp 
-verify -o %t/pragma_mark.pcm
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface 
%t/pragma_detect_mismatch.cpp -verify -o %t/pragma_detect_mismatch.pcm
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_clang_debug.cpp 
-verify -o %t/pragma_clang_debug.pcm
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp 
b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
new file mode 100644
index 0000000000000..0eb6f932c4473
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
@@ -0,0 +1,30 @@
+// 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", align 1
+// CHECK-MOD: @llvm.compiler.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 [[gnu::always_inline]]() {}
+
+//--- importmod.cc
+import copymod;
+void g() { f(); }
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment.c 
b/clang/test/CodeGen/PowerPC/pragma-comment.c
new file mode 100644
index 0000000000000..32afb8bdfa33a
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment.c
@@ -0,0 +1,22 @@
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 %t/pragma-copyright.c -triple powerpc-ibm-aix   -emit-llvm 
-disable-llvm-passes -o - | FileCheck %s  --check-prefix=CHECK-COPYRIGHT
+// RUN: %clang_cc1 %t/pragma-copyright.c -triple powerpc64-ibm-aix -emit-llvm 
-disable-llvm-passes -o - | FileCheck %s  --check-prefix=CHECK-COPYRIGHT
+
+// RUN: %clang_cc1 %t/operator-pragma-copyright.c -triple powerpc-ibm-aix   
-emit-llvm -disable-llvm-passes -o - | FileCheck %s 
--check-prefix=CHECK-OPERATOR
+// RUN: %clang_cc1 %t/operator-pragma-copyright.c -triple powerpc64-ibm-aix 
-emit-llvm -disable-llvm-passes -o - | FileCheck %s 
--check-prefix=CHECK-OPERATOR
+
+//--- pragma-copyright.c
+#pragma comment(copyright, "@(#) Hello, " " world\n\t\"quoted\"")
+
+int main() { return 0; }
+
+// CHECK-COPYRIGHT: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
+// CHECK-COPYRIGHT: ![[COPYRIGHT]] = !{!"@(#) Hello, world\0A\09\22quoted\22"}
+
+//--- operator-pragma-copyright.c
+_Pragma("comment(copyright, \"IBM Copyright Pragma Operator\")")
+void foo() {}
+
+// CHECK-OPERATOR: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
+// CHECK-OPERATOR: ![[COPYRIGHT]] = !{!"IBM Copyright Pragma Operator"}
diff --git a/clang/test/CodeGen/lto-newpm-pipeline.c 
b/clang/test/CodeGen/lto-newpm-pipeline.c
index ea9784a76f923..664c56f0fe76f 100644
--- a/clang/test/CodeGen/lto-newpm-pipeline.c
+++ b/clang/test/CodeGen/lto-newpm-pipeline.c
@@ -32,6 +32,7 @@
 // CHECK-FULL-O0-NEXT: Running pass: AlwaysInlinerPass
 // CHECK-FULL-O0-NEXT: Running analysis: ProfileSummaryAnalysis
 // CHECK-FULL-O0-NEXT: Running pass: CoroConditionalWrapper
+// CHECK-FULL-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-FULL-O0-NEXT: Running pass: CanonicalizeAliasesPass
 // CHECK-FULL-O0-NEXT: Running pass: NameAnonGlobalPass
 // CHECK-FULL-O0-NEXT: Running pass: AnnotationRemarksPass
@@ -46,6 +47,7 @@
 // CHECK-THIN-O0-NEXT: Running pass: AlwaysInlinerPass
 // CHECK-THIN-O0-NEXT: Running analysis: ProfileSummaryAnalysis
 // CHECK-THIN-O0-NEXT: Running pass: CoroConditionalWrapper
+// CHECK-THIN-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-THIN-O0-NEXT: Running pass: CanonicalizeAliasesPass
 // CHECK-THIN-O0-NEXT: Running pass: NameAnonGlobalPass
 // CHECK-THIN-O0-NEXT: Running pass: AnnotationRemarksPass
diff --git a/clang/test/Preprocessor/pragma-comment.c 
b/clang/test/Preprocessor/pragma-comment.c
new file mode 100644
index 0000000000000..f365edf661588
--- /dev/null
+++ b/clang/test/Preprocessor/pragma-comment.c
@@ -0,0 +1,92 @@
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify 
%t/unsupported.c
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify 
%t/unsupported.c
+// RUN: %clang_cc1 -triple systemz -fsyntax-only -verify %t/unsupported.c
+// RUN: %clang_cc1 -triple powerpc64-linux-gnu -fsyntax-only -verify 
%t/unsupported.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/copyright.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/copyright.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/empty-copyright.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/empty-copyright.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/other-kinds.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/other-kinds.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/duplicate.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/duplicate.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/raw-string-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/raw-string-literal.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/concat-escape.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/concat-escape.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/u8-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/u8-literal.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/wide-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/wide-literal.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/utf16-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/utf16-literal.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix   -fsyntax-only -verify 
%t/utf32-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify 
%t/utf32-literal.c
+
+//--- unsupported.c
+// pragma comment kinds not supported on this target.
+#pragma comment(copyright, "copyright")            // expected-warning 
{{'#pragma comment copyright' ignored}}
+#pragma comment(compiler)                          // expected-warning 
{{'#pragma comment compiler' ignored}}
+#pragma comment(exestr, "foo")                     // expected-warning 
{{'#pragma comment exestr' ignored}}
+#pragma comment(user, "foo\abar\nbaz\tsomething")  // expected-warning 
{{'#pragma comment user' ignored}}
+#pragma comment(timestamp)                         // expected-error {{unknown 
kind of pragma comment}}
+#pragma comment(date)                              // expected-error {{unknown 
kind of pragma comment}}
+
+//--- copyright.c
+// Copyright pragma is accepted without diagnostics.
+#pragma comment(copyright, "copyright") // expected-no-diagnostics
+
+//--- empty-copyright.c
+// An empty copyright string is accepted without diagnostics.
+#pragma comment(copyright, "") // expected-no-diagnostics
+
+//--- other-kinds.c
+// Non-copyright comment kinds produce warnings/errors.
+#pragma comment(lib, "m")                          // expected-warning 
{{'#pragma comment lib' ignored}}
+#pragma comment(linker, "foo")                     // expected-warning 
{{'#pragma comment linker' ignored}}
+#pragma comment(compiler)                          // expected-warning 
{{'#pragma comment compiler' ignored}}
+#pragma comment(exestr, "foo")                     // expected-warning 
{{'#pragma comment exestr' ignored}}
+#pragma comment(user, "foo\abar\nbaz\tsomething")  // expected-warning 
{{'#pragma comment user' ignored}}
+#pragma comment(timestamp)                         // expected-error {{unknown 
kind of pragma comment}}
+#pragma comment(date)                              // expected-error {{unknown 
kind of pragma comment}}
+
+//--- duplicate.c
+// A second copyright pragma in the same translation unit warns.
+#pragma comment(copyright, "@(#) Copyright")
+#pragma comment(copyright, "Duplicate Copyright") // expected-warning 
{{'#pragma comment copyright' ignored: it can be specified only once per 
translation unit}}
+
+//--- raw-string-literal.c
+// Raw string literals are accepted.
+#pragma comment(copyright, R"foo(printf("Hello (world)");)foo")  // 
expected-no-diagnostics
+
+//--- concat-escape.c
+// Concatenated ordinary string literals and escapes are accepted.
+#pragma comment(copyright, "@(#) Hello, " "world\n\t\"@(#) quoted\"") // 
expected-no-diagnostics
+
+//--- u8-literal.c
+// UTF-8-prefixed string literals are rejected.
+#pragma comment(copyright, u8"@(#) Hello unicode") // expected-error 
{{expected string literal in pragma comment}}
+
+//--- wide-literal.c
+// Wide string literals are rejected.
+#pragma comment(copyright, L"@(#) Hello wide") // expected-error {{expected 
string literal in pragma comment}}
+
+//--- utf16-literal.c
+// UTF-16-prefixed string literals are rejected.
+#pragma comment(copyright, u"@(#) Hello utf16") // expected-error {{expected 
string literal in pragma comment}}
+
+//--- utf32-literal.c
+// UTF-32-prefixed string literals are rejected.
+#pragma comment(copyright, U"@(#) Hello utf32") // expected-error {{expected 
string literal in pragma comment}}
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 f7db63ef8bf74..8964784248aaa 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -370,6 +370,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 9eea552fd263e..13583fe181042 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -145,6 +145,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"
@@ -1750,6 +1751,13 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
           InlineContext{ThinOrFullLTOPhase::None, InlinePass::CGSCCInliner}));
     }
   }
+
+  // Lower !comment_string.loadtime metadata to a concrete TU-local string
+  // 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());
+
   return MPM;
 }
 
@@ -1919,6 +1927,9 @@ 
PassBuilder::buildThinLTOPreLinkDefaultPipeline(OptimizationLevel Level) {
   // Emit annotation remarks.
   addAnnotationRemarksPass(MPM);
 
+  // Lower !comment_string.loadtime metadata.
+  MPM.addPass(LowerCommentStringPass());
+
   addRequiredLTOPreLinkPasses(MPM);
 
   instructionCountersPass(MPM, /* IsPreOptimization */ false);
@@ -2499,6 +2510,11 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel 
Level,
   if (EnableInstrumentor)
     MPM.addPass(InstrumentorPass(FS));
 
+  // Lower !comment_string.loadtime metadata to a concrete TU-local string
+  // global. Running at the end of the O0 pipeline ensures all functions
+  // including AlwaysInliner-generated ones are captured.
+  MPM.addPass(LowerCommentStringPass());
+
   if (isLTOPreLink(Phase))
     addRequiredLTOPreLinkPasses(MPM);
 
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 8f70c7cefa408..7ced179b78c11 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 6afd752794484..0088d439e6895 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -54,6 +54,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..cc9bee494d597
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -0,0 +1,150 @@
+//===-- 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
+//
+//===---------------------------------------------------------------------===//
+//
+// This pass lowers the 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 with
+//      section attribute "__loadtime_comment". The backend places this
+//      in the .text section of the object file.
+//
+//   2. Marks the string in `llvm.compiler.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 the string (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.compiler.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 isSupportedTarget(const Module &M) {
+  Triple T{M.getTargetTriple()};
+  return T.isOSAIX();
+}
+
+PreservedAnalyses LowerCommentStringPass::run(Module &M,
+                                              ModuleAnalysisManager &AM) {
+  if (DisableCopyrightMetadata || !isSupportedTarget(M))
+    return PreservedAnalyses::all();
+
+  LLVMContext &Ctx = M.getContext();
+
+  // Single-metadata: !comment_string.loadtime = !{!0}
+  // Each operand node is expected to have one MDString operand.
+  NamedMDNode *MD = M.getNamedMetadata("comment_string.loadtime");
+  if (!MD || MD->getNumOperands() == 0)
+    return PreservedAnalyses::all();
+
+  // At this point we are guaranteed that one TU contains a single copyright
+  // metadata entry. Create TU-local string global for that metadata entry.
+  MDNode *MdNode = MD->getOperand(0);
+  if (!MdNode || MdNode->getNumOperands() == 0)
+    return PreservedAnalyses::all();
+
+  auto *MdString = dyn_cast_or_null<MDString>(MdNode->getOperand(0));
+  if (!MdString)
+    return PreservedAnalyses::all();
+
+  StringRef Text = MdString->getString();
+  if (Text.empty())
+    return PreservedAnalyses::all();
+
+  // 1. Create a single null-terminated string global.
+  Constant *StrInit = ConstantDataArray::getString(Ctx, Text, 
/*AddNull=*/true);
+
+  // The global variable should be internal, constant, and TU-local.
+  // This avoids duplicate symbol issues across TUs.
+  auto *StrGV = new GlobalVariable(M, StrInit->getType(),
+                                   /*isConstant=*/true,
+                                   GlobalValue::InternalLinkage, StrInit,
+                                   /*Name=*/"__loadtime_comment_str");
+  // Set unnamed_addr to allow the linker to merge identical strings.
+  StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
+  StrGV->setAlignment(Align(1));
+  // Place in the "__loadtime_comment" section.
+  // The GV is constant, so we expect a read-only section.
+  StrGV->setSection("__loadtime_comment");
+
+  // 2. Add the string to llvm.compiler.used to prevent LLVM optimization/LTO
+  // passes from removing it.
+  appendToCompilerUsed(M, {StrGV});
+
+  // 3. Attach !implicit.ref metadata to every defined function.
+  // Create a metadata node pointing to the copyright string:
+  //   !N = !{ptr @__loadtime_comment_str}
+  Metadata *Ops[] = {ConstantAsMetadata::get(StrGV)};
+  MDNode *ImplicitRefMD = MDNode::get(Ctx, Ops);
+
+  auto AddImplicitRef = [&](Function &F) {
+    if (F.isDeclaration())
+      return;
+    // Attach the !implicit.ref metadata to the function.
+    F.setMetadata(LLVMContext::MD_implicit_ref, ImplicitRefMD);
+    LLVM_DEBUG(dbgs() << "[copyright] attached implicit.ref to function:  "
+                      << F.getName() << "\n");
+  };
+
+  // Process all functions in the module and add !implicit.ref to the function.
+  for (Function &F : M)
+    AddImplicitRef(F);
+
+  // Cleanup the processed metadata.
+  MD->eraseFromParent();
+  LLVM_DEBUG(dbgs() << "[copyright] created string and anchor for module\n");
+
+  return PreservedAnalyses::all();
+}
diff --git a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll 
b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll
index 86090324c770c..1abc5ab09b674 100644
--- a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll
+++ b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll
@@ -2,7 +2,7 @@
 ; RUN: opt -mtriple=aarch64 -S -passes='default<O2>' -print-pipeline-passes < 
%s | FileCheck %s
 
 ; CHECK: loop-idiom-vectorize
-; O0: 
{{^}}function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,function(annotation-remarks),verify,print{{$}}
+; O0: 
{{^}}function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,lower-comment-string,function(annotation-remarks),verify,print{{$}}
 
 define void @foo() {
 entry:
diff --git a/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll 
b/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll
index 0f80e9c60c441..2ff5d83f8ed00 100644
--- a/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll
+++ b/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll
@@ -3,7 +3,7 @@
 
 ; CHECK: hexagon-loop-idiom
 ; CHECK: hexagon-vlcr
-; O0: 
{{^}}function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,function(annotation-remarks),verify,print{{$}}
+; O0: 
{{^}}function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,lower-comment-string,function(annotation-remarks),verify,print{{$}}
 
 define void @test_hexagon_passes() {
 entry:
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll 
b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
new file mode 100644
index 0000000000000..58c3b4ae42ca1
--- /dev/null
+++ b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
@@ -0,0 +1,67 @@
+; RUN: rm -rf %t && mkdir %t
+; RUN: split-file %s %t
+; RUN: llvm-as %t/tu1.ll -o %t/tu1.bc
+; RUN: llvm-as %t/tu2.ll -o %t/tu2.bc
+; RUN: llvm-lto -filetype=asm \
+; RUN:   -exported-symbol=main \
+; RUN:   -exported-symbol=f_tu1 \
+; RUN:   %t/tu1.bc %t/tu2.bc \
+; RUN:   -o %t/out.s 
+; RUN: FileCheck %s < %t/out.s
+
+
+;--- tu1.ll
+;; TU1: already lowered by LowerCommentStringPass at prelink
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+@__loadtime_comment_str = internal unnamed_addr constant
+    [14 x i8] c"Copyright TU1\00",
+    section "__loadtime_comment", align 1
[email protected] = appending global [1 x ptr]
+    [ptr @__loadtime_comment_str], section "llvm.metadata"
+
+define void @f_tu1() !implicit.ref !0 {
+entry:
+  ret void
+}
+
+!0 = !{ptr @__loadtime_comment_str}
+
+;--- tu2.ll
+;; TU2: already lowered by LowerCommentStringPass at prelink
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+@__loadtime_comment_str = internal unnamed_addr constant
+    [14 x i8] c"Copyright TU2\00",
+    section "__loadtime_comment", align 1
[email protected] = appending global [1 x ptr]
+    [ptr @__loadtime_comment_str], section "llvm.metadata"
+
+declare void @f_tu1()
+
+define i32 @main() !implicit.ref !0 {
+entry:
+  call void @f_tu1()
+  ret i32 0
+}
+
+!0 = !{ptr @__loadtime_comment_str}
+
+;; .ref directive emitted from f_tu1's csect anchoring TU1's string
+; CHECK-LABEL: .f_tu1:
+; CHECK-NEXT:  .ref __loadtime_comment_str
+
+;; .ref directive emitted from main's csect anchoring TU2's string
+; CHECK-LABEL: .main:
+; CHECK-NEXT:  .ref __loadtime_comment_str.2
+
+;; Both copyright strings in the read-only __loadtime_comment csect
+; CHECK:      .csect __loadtime_comment[RO],2
+; CHECK-DAG:   .lglobl __loadtime_comment_str
+; CHECK-LABEL: __loadtime_comment_str:
+; CHECK-DAG:  .string "Copyright TU1"
+; CHECK-DAG:   .lglobl __loadtime_comment_str.2
+; CHECK-LABEL: __loadtime_comment_str.2:
+; CHECK-DAG:  .string "Copyright TU2"
diff --git a/llvm/test/Other/new-pm-O0-defaults.ll 
b/llvm/test/Other/new-pm-O0-defaults.ll
index bac4150caab58..5c3571aa05df2 100644
--- a/llvm/test/Other/new-pm-O0-defaults.ll
+++ b/llvm/test/Other/new-pm-O0-defaults.ll
@@ -42,6 +42,7 @@
 ; CHECK-MATRIX-NEXT: Running analysis: TargetIRAnalysis
 ; CHECK-CORO-NEXT: Running pass: CoroConditionalWrapper
 ; CHECK-ALLOCTOKEN-NEXT: Running pass: AllocTokenPass
+; CHECK-CORO-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-PRE-LINK: Running pass: CanonicalizeAliasesPass
 ; CHECK-PRE-LINK-NEXT: Running pass: NameAnonGlobalPass
 ; CHECK-THINLTO: Running pass: DropTypeTestsPass
diff --git a/llvm/test/Other/new-pm-defaults.ll 
b/llvm/test/Other/new-pm-defaults.ll
index d6e51f451c3a4..181c9105f625a 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -283,6 +283,7 @@
 ; CHECK-DEFAULT-NEXT: Running pass: CGProfilePass
 ; CHECK-DEFAULT-NEXT: Running pass: RelLookupTableConverterPass
 ; CHECK-LTO-NOT: Running pass: RelLookupTableConverterPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
 ; CHECK-LTO-NEXT: Running pass: CanonicalizeAliasesPass
 ; CHECK-LTO-NEXT: Running pass: NameAnonGlobalPass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll 
b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
index dbbd10eaa8775..93b17cc87a1de 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -206,6 +206,7 @@
 ; CHECK-POSTLINK-O-NEXT: Running pass: RelLookupTableConverterPass
 ; CHECK-EP-OPT-EARLY-NEXT: Running pass: NoOpModulePass
 ; CHECK-EP-OPT-LAST-NEXT: Running pass: NoOpModulePass
+; CHECK-POSTLINK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT:          Running pass: AnnotationRemarksPass on foo
 ; CHECK-O-NEXT: Running pass: PrintModulePass
 
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll 
b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
index eab1f2a257c1a..c29da3fd2c353 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -188,6 +188,7 @@
 ; CHECK-O-NEXT: Running pass: ConstantMergePass
 ; CHECK-O-NEXT: Running pass: CGProfilePass
 ; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
 ; CHECK-O-NEXT: Running pass: PrintModulePass
 
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll 
b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
index 339e346fdf439..440d25d15bca8 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -195,6 +195,7 @@
 ; CHECK-O-NEXT: Running pass: ConstantMergePass
 ; CHECK-O-NEXT: Running pass: CGProfilePass
 ; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
 ; CHECK-O-NEXT: Running pass: PrintModulePass
 
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll 
b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
index f4245b66b0429..59037b411d99f 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
@@ -182,6 +182,7 @@
 ; CHECK-EP-OPT-EARLY-NEXT: Running pass: NoOpModulePass
 ; CHECK-EP-OPT-LAST-NEXT: Running pass: NoOpModulePass
 ; CHECK-O-NEXT:          Running pass: AnnotationRemarksPass on foo
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
 ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
 ; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll 
b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
index 87acb355fddc7..8fa5bab37b92b 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
@@ -185,6 +185,7 @@
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
 ; CHECK-EXT: Running pass: {{.*}}::Bye
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
 ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
 ; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll 
b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
index e5c1453d692eb..e0d096f202092 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
@@ -148,6 +148,7 @@
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
 ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
 ; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll 
b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
new file mode 100644
index 0000000000000..bf353bdad4a32
--- /dev/null
+++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
@@ -0,0 +1,41 @@
+; RUN: opt -passes=lower-comment-string -S %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-O0
+
+; Verify that lower-comment-string is enabled by default on all opt pipelines.
+; RUN: opt --O0 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0
+; RUN: opt --O1 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON
+; RUN: opt --O2 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON
+; RUN: opt --O3 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON
+
+target triple = "powerpc-ibm-aix"
+
+define void @f0() {
+entry:
+  ret void    
+}
+define i32 @main() {
+entry:
+  ret i32 0
+}
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"wchar_size", i32 2}
+
+!comment_string.loadtime = !{!1}
+!1 = !{!"@(#) Copyright IBM 2025"}
+
+
+; ---- Globals--------------------------------------------
+; CHECK: @__loadtime_comment_str = internal unnamed_addr constant [24 x i8] 
c"@(#) Copyright IBM 2025\00", section "__loadtime_comment", align 1
+; Preservation in llvm.compiler.used sets
+; CHECK-NEXT: @llvm.compiler.used = appending global [1 x ptr] [ptr 
@__loadtime_comment_str], section "llvm.metadata"
+; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"}
+
+; Function has an implicit ref MD pointing at the string:
+; CHECK-O0: define void @f0() !implicit.ref ![[MD:[0-9]+]]
+; CHECK-ON: define void @f0() local_unnamed_addr #0 !implicit.ref 
![[MD:[0-9]+]]
+; CHECK-O0: define i32 @main() !implicit.ref ![[MD]]
+; CHECK-ON: define noundef i32 @main() local_unnamed_addr #0 !implicit.ref 
![[MD]]
+
+; Verify metadata content
+; CHECK-O0: ![[MD]] = !{ptr @__loadtime_comment_str}
+; CHECK-ON: ![[MD]] = !{ptr @__loadtime_comment_str}

>From ae45e12bcb0ce483580233ff671a2c52227c5f95 Mon Sep 17 00:00:00 2001
From: Tony Varghese <[email protected]>
Date: Tue, 16 Jun 2026 11:19:28 -0400
Subject: [PATCH 2/6] Remove name secton for the copyright strings, fix
 comments in PassBuilderPipelines.cpp

---
 .../pragma-comment-copyright-modules.cpp      |  2 +-
 llvm/lib/Passes/PassBuilderPipelines.cpp      | 10 ++-
 .../Utils/LowerCommentStringPass.cpp          | 11 +--
 .../PowerPC/pragma-comment-copyright-lto.ll   | 67 -------------------
 .../lower-comment-string.ll                   |  9 +--
 5 files changed, 11 insertions(+), 88 deletions(-)
 delete mode 100644 llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll

diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp 
b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
index 0eb6f932c4473..51536f5acb088 100644
--- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
@@ -8,7 +8,7 @@
 
 // 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", align 1
+// CHECK-MOD: @__loadtime_comment_str = internal unnamed_addr constant [10 x 
i8] c"module me\00", align 1
 // CHECK-MOD: @llvm.compiler.used = appending global {{.*}} 
@__loadtime_comment_str
 
 // Compile an importing TU that uses the prebuilt module and verify that it
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp 
b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 13583fe181042..3922301ac6282 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -1753,9 +1753,7 @@ 
PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   }
 
   // Lower !comment_string.loadtime metadata to a concrete TU-local string
-  // 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.
+  // global and attach !implicit.ref to all defined functions.
   MPM.addPass(LowerCommentStringPass());
 
   return MPM;
@@ -1927,7 +1925,8 @@ 
PassBuilder::buildThinLTOPreLinkDefaultPipeline(OptimizationLevel Level) {
   // Emit annotation remarks.
   addAnnotationRemarksPass(MPM);
 
-  // Lower !comment_string.loadtime metadata.
+  // Lower !comment_string.loadtime metadata to a concrete TU-local string
+  // global and attach !implicit.ref to all defined functions.
   MPM.addPass(LowerCommentStringPass());
 
   addRequiredLTOPreLinkPasses(MPM);
@@ -2511,8 +2510,7 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel 
Level,
     MPM.addPass(InstrumentorPass(FS));
 
   // Lower !comment_string.loadtime metadata to a concrete TU-local string
-  // global. Running at the end of the O0 pipeline ensures all functions
-  // including AlwaysInliner-generated ones are captured.
+  // global and attach !implicit.ref to all defined functions.
   MPM.addPass(LowerCommentStringPass());
 
   if (isLTOPreLink(Phase))
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp 
b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
index cc9bee494d597..a7dcd4e455102 100644
--- a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -15,9 +15,8 @@
 // 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 with
-//      section attribute "__loadtime_comment". The backend places this
-//      in the .text section of the object file.
+//      (`__loadtime_comment_str`) containing the copyright text. The backend
+//      places this in the .text section of the object file.
 //
 //   2. Marks the string in `llvm.compiler.used` so it cannot be dropped by
 //      optimization or LTO.
@@ -31,8 +30,7 @@
 //  Input IR:
 //     !comment_string.loadtime = !{!"Copyright"}
 //  Output IR:
-//     @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00",
-//                          section "__loadtime_comment"
+//     @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00"
 //     @llvm.compiler.used = appending global [1 x ptr] [ptr
 //     @__loadtime_comment_str]
 //
@@ -115,9 +113,6 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M,
   // Set unnamed_addr to allow the linker to merge identical strings.
   StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
   StrGV->setAlignment(Align(1));
-  // Place in the "__loadtime_comment" section.
-  // The GV is constant, so we expect a read-only section.
-  StrGV->setSection("__loadtime_comment");
 
   // 2. Add the string to llvm.compiler.used to prevent LLVM optimization/LTO
   // passes from removing it.
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll 
b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
deleted file mode 100644
index 58c3b4ae42ca1..0000000000000
--- a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
+++ /dev/null
@@ -1,67 +0,0 @@
-; RUN: rm -rf %t && mkdir %t
-; RUN: split-file %s %t
-; RUN: llvm-as %t/tu1.ll -o %t/tu1.bc
-; RUN: llvm-as %t/tu2.ll -o %t/tu2.bc
-; RUN: llvm-lto -filetype=asm \
-; RUN:   -exported-symbol=main \
-; RUN:   -exported-symbol=f_tu1 \
-; RUN:   %t/tu1.bc %t/tu2.bc \
-; RUN:   -o %t/out.s 
-; RUN: FileCheck %s < %t/out.s
-
-
-;--- tu1.ll
-;; TU1: already lowered by LowerCommentStringPass at prelink
-target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
-target triple = "powerpc-ibm-aix7.3.0.0"
-
-@__loadtime_comment_str = internal unnamed_addr constant
-    [14 x i8] c"Copyright TU1\00",
-    section "__loadtime_comment", align 1
[email protected] = appending global [1 x ptr]
-    [ptr @__loadtime_comment_str], section "llvm.metadata"
-
-define void @f_tu1() !implicit.ref !0 {
-entry:
-  ret void
-}
-
-!0 = !{ptr @__loadtime_comment_str}
-
-;--- tu2.ll
-;; TU2: already lowered by LowerCommentStringPass at prelink
-target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
-target triple = "powerpc-ibm-aix7.3.0.0"
-
-@__loadtime_comment_str = internal unnamed_addr constant
-    [14 x i8] c"Copyright TU2\00",
-    section "__loadtime_comment", align 1
[email protected] = appending global [1 x ptr]
-    [ptr @__loadtime_comment_str], section "llvm.metadata"
-
-declare void @f_tu1()
-
-define i32 @main() !implicit.ref !0 {
-entry:
-  call void @f_tu1()
-  ret i32 0
-}
-
-!0 = !{ptr @__loadtime_comment_str}
-
-;; .ref directive emitted from f_tu1's csect anchoring TU1's string
-; CHECK-LABEL: .f_tu1:
-; CHECK-NEXT:  .ref __loadtime_comment_str
-
-;; .ref directive emitted from main's csect anchoring TU2's string
-; CHECK-LABEL: .main:
-; CHECK-NEXT:  .ref __loadtime_comment_str.2
-
-;; Both copyright strings in the read-only __loadtime_comment csect
-; CHECK:      .csect __loadtime_comment[RO],2
-; CHECK-DAG:   .lglobl __loadtime_comment_str
-; CHECK-LABEL: __loadtime_comment_str:
-; CHECK-DAG:  .string "Copyright TU1"
-; CHECK-DAG:   .lglobl __loadtime_comment_str.2
-; CHECK-LABEL: __loadtime_comment_str.2:
-; CHECK-DAG:  .string "Copyright TU2"
diff --git a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll 
b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
index bf353bdad4a32..9e3c9feca7fec 100644
--- a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
+++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
@@ -17,15 +17,12 @@ entry:
   ret i32 0
 }
 
-!llvm.module.flags = !{!0}
-!0 = !{i32 1, !"wchar_size", i32 2}
-
-!comment_string.loadtime = !{!1}
-!1 = !{!"@(#) Copyright IBM 2025"}
+!comment_string.loadtime = !{!0}
+!0 = !{!"@(#) Copyright IBM 2025"}
 
 
 ; ---- Globals--------------------------------------------
-; CHECK: @__loadtime_comment_str = internal unnamed_addr constant [24 x i8] 
c"@(#) Copyright IBM 2025\00", section "__loadtime_comment", align 1
+; CHECK: @__loadtime_comment_str = internal unnamed_addr constant [24 x i8] 
c"@(#) Copyright IBM 2025\00", align 1
 ; Preservation in llvm.compiler.used sets
 ; CHECK-NEXT: @llvm.compiler.used = appending global [1 x ptr] [ptr 
@__loadtime_comment_str], section "llvm.metadata"
 ; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"}

>From ffc466d69b0c0688ba528321d3b5b74bf2ab7695 Mon Sep 17 00:00:00 2001
From: Tony Varghese <[email protected]>
Date: Tue, 16 Jun 2026 11:49:07 -0400
Subject: [PATCH 3/6] Fix LLVM_ABI failure of
 llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h

---
 llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h 
b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
index 4c6109e1176d3..e78b36b3314ac 100644
--- a/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
+++ b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
@@ -14,7 +14,7 @@
 namespace llvm {
 class LowerCommentStringPass : public PassInfoMixin<LowerCommentStringPass> {
 public:
-  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+  LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
 
   static bool isRequired() { return true; }
 };

>From 352c632ae8d1a0bae964ff43dfcaf225be27b257 Mon Sep 17 00:00:00 2001
From: Tony Varghese <[email protected]>
Date: Sat, 23 May 2026 20:06:17 +0530
Subject: [PATCH 4/6] [ThinLTO][AIX] Teach ModuleSummaryAnalysis to include
 globals referenced via !implicit.ref metadata as explicit reference edges in
 the ThinLTO module summary via a new helper findImplicitRefEdges. Add
 imported implicit ref strings (available_externally GVs) to
 llvm.compiler.used during thinLTO interaction with pragma comment copyright.

---
 .../pragma-comment-copyright-modules.cpp      |  6 +-
 llvm/lib/Analysis/ModuleSummaryAnalysis.cpp   | 24 +++++
 llvm/lib/Transforms/IPO/FunctionImport.cpp    | 34 +++++++
 .../Utils/LowerCommentStringPass.cpp          | 28 +++---
 .../implicit-ref-edges.ll                     | 39 ++++++++
 .../PowerPC/pragma-comment-copyright-lto.ll   | 89 +++++++++++++++++++
 .../pragma-comment-copyright-thinlto.ll       | 67 ++++++++++++++
 .../lower-comment-string.ll                   | 17 ++--
 8 files changed, 283 insertions(+), 21 deletions(-)
 create mode 100644 
llvm/test/Analysis/ModuleSummaryAnalysis/implicit-ref-edges.ll
 create mode 100644 llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
 create mode 100644 llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll

diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp 
b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
index 51536f5acb088..b2ea09e17be74 100644
--- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
@@ -8,8 +8,8 @@
 
 // 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", align 1
-// CHECK-MOD: @llvm.compiler.used = appending global {{.*}} 
@__loadtime_comment_str
+// CHECK-MOD: @[[LOADTIME_COMMENT_STR:__loadtime_comment_str_[0-9a-f]+]] = 
weak_odr hidden unnamed_addr constant [10 x i8] c"module me\00", align 1
+// CHECK-MOD: @llvm.compiler.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.
@@ -17,7 +17,7 @@
 // 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: @__loadtime_comment_str_
 // CHECK-IMPORT-NOT: c"module me\00"
 
 //--- copymod.cppm
diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp 
b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
index 0879fa627c709..0a2e5583c240b 100644
--- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -40,6 +40,7 @@
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/ModuleSummaryIndex.h"
@@ -177,6 +178,27 @@ findRefEdges(ModuleSummaryIndex &Index, const User 
*CurUser,
   return HasBlockAddress;
 }
 
+/// Collect globals referenced via !implicit.ref metadata on a function
+/// and add them as reference edges in the module summary. This ensures
+/// ThinLTO liveness analysis treats them as live when the function is
+/// live, and imports them alongside the function during cross-module
+/// importing.
+static void findImplicitRefEdges(
+    ModuleSummaryIndex &Index, const Function &F,
+    SetVector<ValueInfo, SmallVector<ValueInfo, 0>> &RefEdges) {
+  if (!F.hasMetadata(LLVMContext::MD_implicit_ref))
+    return;
+  SmallVector<MDNode *, 4> MDs;
+  F.getMetadata(LLVMContext::MD_implicit_ref, MDs);
+  for (MDNode *MD : MDs) {
+    for (const MDOperand &Op : MD->operands()) {
+      if (auto *VAM = dyn_cast_or_null<ValueAsMetadata>(Op.get()))
+        if (auto *GV = dyn_cast<GlobalValue>(VAM->getValue()))
+          RefEdges.insert(Index.getOrInsertValueInfo(GV));
+    }
+  }
+}
+
 static CalleeInfo::HotnessType getHotness(uint64_t ProfileCount,
                                           ProfileSummaryInfo *PSI) {
   if (!PSI)
@@ -346,6 +368,8 @@ static void computeFunctionSummary(
   // list.
   bool HasLocalIFuncCallOrRef = false;
   findRefEdges(Index, &F, RefEdges, Visited, HasLocalIFuncCallOrRef);
+  findImplicitRefEdges(Index, F, RefEdges);
+
   std::vector<const Instruction *> NonVolatileLoads;
   std::vector<const Instruction *> NonVolatileStores;
 
diff --git a/llvm/lib/Transforms/IPO/FunctionImport.cpp 
b/llvm/lib/Transforms/IPO/FunctionImport.cpp
index d305eadc12f35..8898974fea764 100644
--- a/llvm/lib/Transforms/IPO/FunctionImport.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionImport.cpp
@@ -24,6 +24,7 @@
 #include "llvm/IR/GlobalObject.h"
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/ModuleSummaryIndex.h"
@@ -45,6 +46,7 @@
 #include "llvm/Transforms/IPO/Internalize.h"
 #include "llvm/Transforms/Utils/Cloning.h"
 #include "llvm/Transforms/Utils/FunctionImportUtils.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
 #include "llvm/Transforms/Utils/ValueMapper.h"
 #include <cassert>
 #include <memory>
@@ -1903,6 +1905,33 @@ static void internalizeGVsAfterImport(Module &M) {
     }
 }
 
+/// When a function carrying !implicit.ref is imported via ThinLTO, the
+/// referenced global arrives as available_externally. This string can be dead
+/// code eliminated since it has no IR uses -- only metadata references. Adding
+/// it to llvm.compiler.used prevents elimination.
+static void protectImplicitRefGlobals(Module &M) {
+  SmallPtrSet<GlobalValue *, 4> Seen;
+  SmallVector<GlobalValue *, 4> ToProtect;
+  for (Function &F : M) {
+    if (!F.hasAvailableExternallyLinkage() ||
+        !F.hasMetadata(LLVMContext::MD_implicit_ref))
+      continue;
+    SmallVector<MDNode *> MDs;
+    F.getMetadata(LLVMContext::MD_implicit_ref, MDs);
+    for (MDNode *MD : MDs) {
+      auto *Op = MD->getOperand(0).get();
+      if (!Op)
+        continue;
+      if (auto *VAM = dyn_cast<ValueAsMetadata>(Op))
+        if (auto *GV = dyn_cast<GlobalVariable>(VAM->getValue()))
+          if (GV->hasAvailableExternallyLinkage() && Seen.insert(GV).second)
+            ToProtect.push_back(GV);
+    }
+  }
+  if (!ToProtect.empty())
+    appendToCompilerUsed(M, ToProtect);
+}
+
 // Automatically import functions in Module \p DestModule based on the 
summaries
 // index.
 Expected<bool> FunctionImporter::importFunctions(
@@ -2084,6 +2113,11 @@ Expected<bool> FunctionImporter::importFunctions(
 
   internalizeGVsAfterImport(DestModule);
 
+  // Protect !implicit.ref-referenced globals imported as available_externally
+  // from DCE'd. Only needed when globals were actually imported.
+  if (ImportedGVCount > 0)
+    protectImplicitRefGlobals(DestModule);
+
   NumImportedFunctions += (ImportedCount - ImportedGVCount);
   NumImportedGlobalVars += ImportedGVCount;
 
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp 
b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
index a7dcd4e455102..ade6843be576b 100644
--- a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -10,11 +10,11 @@
 //
 //     !comment_string.loadtime = !{!"Copyright ..."}
 //
-// into concrete, translation-unit-local globals.
+// into concrete, translation-unit-weak-hidden 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
+//   1. Creates a null-terminated, weak_odr hidden constant string global
 //      (`__loadtime_comment_str`) containing the copyright text. The backend
 //      places this in the .text section of the object file.
 //
@@ -30,12 +30,13 @@
 //  Input IR:
 //     !comment_string.loadtime = !{!"Copyright"}
 //  Output IR:
-//     @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00"
+//     @__loadtime_comment_str_HASH = weak_odr constant [N x i8]
+//     c"Copyright\00"
 //     @llvm.compiler.used = appending global [1 x ptr] [ptr
-//     @__loadtime_comment_str]
+//     @__loadtime_comment_str_HASH]
 //
 //     define i32 @func() !implicit.ref !5 { ... }
-//     !5 = !{ptr @__loadtime_comment_str}
+//     !5 = !{ptr @__loadtime_comment_str_HASH}
 //
 
//===----------------------------------------------------------------------===//
 
@@ -57,8 +58,10 @@
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/xxhash.h"
 #include "llvm/TargetParser/Triple.h"
 #include "llvm/Transforms/Utils/ModuleUtils.h"
+#include <string>
 
 #define DEBUG_TYPE "lower-comment-string"
 
@@ -101,15 +104,18 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M,
   if (Text.empty())
     return PreservedAnalyses::all();
 
+  uint64_t Hash = xxh3_64bits(Text);
+  std::string GlobalName =
+      ("__loadtime_comment_str_" + Twine::utohexstr(Hash)).str();
+
   // 1. Create a single null-terminated string global.
   Constant *StrInit = ConstantDataArray::getString(Ctx, Text, 
/*AddNull=*/true);
 
-  // The global variable should be internal, constant, and TU-local.
-  // This avoids duplicate symbol issues across TUs.
-  auto *StrGV = new GlobalVariable(M, StrInit->getType(),
-                                   /*isConstant=*/true,
-                                   GlobalValue::InternalLinkage, StrInit,
-                                   /*Name=*/"__loadtime_comment_str");
+  // The global variable should be weak_odr, constant, and TU-hidden.
+  auto *StrGV = new GlobalVariable(
+      M, StrInit->getType(),
+      /*isConstant=*/true, GlobalValue::WeakODRLinkage, StrInit, GlobalName);
+  StrGV->setVisibility(GlobalValue::HiddenVisibility);
   // Set unnamed_addr to allow the linker to merge identical strings.
   StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
   StrGV->setAlignment(Align(1));
diff --git a/llvm/test/Analysis/ModuleSummaryAnalysis/implicit-ref-edges.ll 
b/llvm/test/Analysis/ModuleSummaryAnalysis/implicit-ref-edges.ll
new file mode 100644
index 0000000000000..644ad641809dd
--- /dev/null
+++ b/llvm/test/Analysis/ModuleSummaryAnalysis/implicit-ref-edges.ll
@@ -0,0 +1,39 @@
+;; Tests that globals referenced via !implicit.ref metadata on a function
+;; are added as explicit reference edges in the module summary.
+;; This ensures ThinLTO liveness analysis marks them live when the function
+;; is live, preventing GlobalDCE from eliminating them.
+
+; RUN: opt -module-summary %s -o %t.bc
+; RUN: llvm-dis %t.bc -o - | FileCheck %s
+
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+@counter = global i32 0, align 4
+@__loadtime_comment_str = internal unnamed_addr constant [14 x i8] c"Copyright 
TU1\00",
+                          section "__loadtime_comment", align 1
[email protected] = appending global [1 x ptr] [ptr 
@__loadtime_comment_str], 
+                     section "llvm.metadata"
+
+define void @f_tu1() !implicit.ref !0 {
+entry:
+  %0 = load i32, ptr @counter, align 4
+  %inc = add nsw i32 %0, 1
+  store i32 %inc, ptr @counter, align 4
+  ret void
+}
+
+!llvm.module.flags = !{!1, !2}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 1, !"EnableSplitLTOUnit", i32 0}
+!0 = !{ptr @__loadtime_comment_str}
+
+;; Verify key globals appear in the summary (order is platform-dependent
+;; since internal globals have GUIDs that include the module path).
+; CHECK-DAG: gv: (name: "__loadtime_comment_str"{{.*}}linkage: 
internal{{.*}}notEligibleToImport: 1
+; CHECK-DAG: gv: (name: "counter"
+
+;; f_tu1 must have exactly two ref edges -- counter (existing IR use) and
+;; __loadtime_comment_str (added by findImplicitRefEdges fix). It must also
+;; be notEligibleToImport because it references an internal global.
+; CHECK-DAG: gv: (name: "f_tu1"{{.*}}notEligibleToImport: 1{{.*}}refs: 
(^{{[0-9]+}}, ^{{[0-9]+}})
\ No newline at end of file
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll 
b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
new file mode 100644
index 0000000000000..5ff95437d974b
--- /dev/null
+++ b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
@@ -0,0 +1,89 @@
+;;
+;; Full LTO test for #pragma comment(copyright, ...) on AIX.
+;; Two TUs each with a copyright string are linked via Full LTO.
+;;
+;; Verifies:
+;;   - LowerCommentStringPass runs at lto-pre-link<O2> per TU
+;;   - f_unused is DCE'd -- not exported and never called
+;;   - f_add is inlined into main and f_add(1,2) constant-folded to li 3,3
+;;   - Both copyright strings appear in the merged __loadtime_comment[RO] csect
+;;   - .ref directives anchor each string to its function csect
+
+; REQUIRES: powerpc-registered-target
+
+; RUN: rm -rf %t && mkdir %t
+; RUN: split-file %s %t
+; RUN: opt -passes='lto-pre-link<O2>' %t/tu1.ll -S -o %t/tu1_lowered.ll
+; RUN: opt -passes='lto-pre-link<O2>' %t/tu2.ll -S -o %t/tu2_lowered.ll
+; RUN: llvm-as %t/tu1_lowered.ll -o %t/tu1.bc
+; RUN: llvm-as %t/tu2_lowered.ll -o %t/tu2.bc
+; RUN: llvm-lto -filetype=asm \
+; RUN:   -exported-symbol=main \
+; RUN:   -exported-symbol=f_add \
+; RUN:   %t/tu1.bc %t/tu2.bc \
+; RUN:   -o %t/out.s
+; RUN: FileCheck %s < %t/out.s
+
+;--- tu1.ll
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+define i32 @f_add(i32 noundef %a, i32 noundef %b) {
+entry:
+  %add = add nsw i32 %a, %b
+  ret i32 %add
+}
+
+;; f_unused is not exported and never called -- Full LTO must DCE it
+define void @f_unused() {
+entry:
+  ret void
+}
+
+!comment_string.loadtime = !{!0}
+!llvm.module.flags = !{!1, !2}
+!0 = !{!"Copyright TU1"}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
+
+;--- tu2.ll
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+declare i32 @f_add(i32 noundef, i32 noundef)
+
+define i32 @main() {
+entry:
+  %call = call i32 @f_add(i32 1, i32 2)
+  ret i32 %call
+}
+
+!comment_string.loadtime = !{!0}
+!llvm.module.flags = !{!1, !2}
+!0 = !{!"Copyright TU2"}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
+
+;; f_add csect anchors TU1 copyright string.
+; CHECK-LABEL: .f_add:
+; CHECK-NEXT:  .ref [[TU1_STR:__loadtime_comment_str_[0-9a-f]+]]
+
+;; main anchors both strings: TU2's own and TU1's via inlined f_add.
+; CHECK-LABEL: .main:
+; CHECK-DAG:   .ref [[TU1_STR]]
+; CHECK-DAG:   .ref [[TU2_STR:__loadtime_comment_str_[0-9a-f]+]]
+
+;; f_add(1,2) constant-folded -- no optimization regression with pragma.
+; CHECK:       li 3, 3
+
+;; Both copyright strings in the read-only __loadtime_comment csect.
+; CHECK:       .csect __loadtime_comment[RO]
+; CHECK-NEXT:  .lglobl [[TU1_STR]]
+; CHECK:       [[TU1_STR]]:
+; CHECK-NEXT:  .string "Copyright TU1"
+; CHECK-NEXT:  .lglobl [[TU2_STR]]
+; CHECK:       [[TU2_STR]]:
+; CHECK-NEXT:  .string "Copyright TU2"
+
+;; f_unused is DCE'd -- must not appear in assembly.
+; CHECK-NOT:   .f_unused:
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll 
b/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll
new file mode 100644
index 0000000000000..1517d8429801a
--- /dev/null
+++ b/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll
@@ -0,0 +1,67 @@
+;; ThinLTO test for #pragma comment(copyright, ...).
+;; Tests that Prgam commentcopyright strings from module TU1 gets imported 
+;; to module TU2 and is gets added to TU2's @llvm.compiler.used.
+
+; REQUIRES: powerpc-registered-target
+
+; RUN: rm -rf %t && mkdir %t
+; RUN: split-file %s %t
+; RUN: opt -passes='thinlto-pre-link<O2>' %t/tu1.ll -o - | \
+; RUN:   opt -module-summary -o %t/tu1.bc
+; RUN: opt -passes='thinlto-pre-link<O2>' %t/tu2.ll -o - | \
+; RUN:   opt -module-summary -o %t/tu2.bc
+; RUN: llvm-lto --thinlto-action=thinlink -o combined %t/tu1.bc %t/tu2.bc
+; RUN: llvm-lto --thinlto-action=import  \
+; RUN:         --thinlto-index=combined  \
+; RUN:         --exported-symbol=main    \
+; RUN:         --exported-symbol=f_add   \
+; RUN:         --exported-symbol=my_function \
+; RUN:        %t/tu2.bc -o %t/tu2.imported.bc
+; RUN: llvm-dis %t/tu2.imported.bc -o - | FileCheck %s 
--check-prefix=CHECK-TU2-IMPORTED
+
+;--- tu1.ll
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+define i32 @f_add(i32 noundef %a, i32 noundef %b) {
+entry:
+  %add = add nsw i32 %a, %b
+  ret i32 %add
+}
+
+!comment_string.loadtime = !{!0}
+!llvm.module.flags = !{!1, !2}
+!0 = !{!"@(#) Copyright TU1"}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 1, !"EnableSplitLTOUnit", i32 0}
+
+;--- tu2.ll
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+declare i32 @f_add(i32 noundef, i32 noundef)
+
+define i32 @main() {
+entry:
+  %call = tail call i32 @f_add(i32 noundef 1, i32 noundef 2)
+  ret i32 %call
+}
+
+!comment_string.loadtime = !{!0}
+!llvm.module.flags = !{!1, !2}
+!0 = !{!"@(#) Copyright TU2"}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 1, !"EnableSplitLTOUnit", i32 0}
+
+; CHECK-TU2-IMPORTED: @[[TU2_STR:__loadtime_comment_str_[0-9a-f]+]] = weak_odr 
hidden unnamed_addr constant [19 x i8] c"@(#) Copyright TU2\00", section 
"__loadtime_comment", align 1
+; CHECK-TU2-IMPORTED-NEXT: @[[TU1_STR:__loadtime_comment_str_[0-9a-f]+]] = 
available_externally hidden unnamed_addr constant [19 x i8] c"@(#) Copyright 
TU1\00", section "__loadtime_comment", align 1
+; CHECK-TU2-IMPORTED-NEXT: @llvm.compiler.used = appending global [2 x ptr] 
[ptr @[[TU2_STR]], ptr @[[TU1_STR]]], section "llvm.metadata"
+
+; CHECK-TU2-IMPORTED-LABEL: define i32 @main()
+; CHECK-TU2-IMPORTED-SAME: local_unnamed_addr !implicit.ref ![[MAIN_MD:[0-9]+]]
+
+; CHECK-TU2-IMPORTED-LABEL: define available_externally i32 @f_add(
+; CHECK-TU2-IMPORTED-SAME: local_unnamed_addr #[[FADD_ATTR:[0-9]+]] 
!implicit.ref ![[FADD_MD:[0-9]+]]
+
+; CHECK-TU2-IMPORTED: ![[MAIN_MD]] = !{ptr @[[TU2_STR]]}
+; CHECK-TU2-IMPORTED: ![[FADD_MD]] = !{ptr @[[TU1_STR]]}
diff --git a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll 
b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
index 9e3c9feca7fec..da855af4331f2 100644
--- a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
+++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
@@ -18,21 +18,24 @@ entry:
 }
 
 !comment_string.loadtime = !{!0}
-!0 = !{!"@(#) Copyright IBM 2025"}
+!0 = !{!"@(#) Copyright String 2025"}
 
+; ---- Globals --------------------------------------------
+; CHECK: @[[LOADTIME_COMMENT_STR:__loadtime_comment_str_[0-9a-f]+]] = weak_odr 
hidden unnamed_addr constant [27 x i8] c"@(#) Copyright String 2025\00", align 1
 
-; ---- Globals--------------------------------------------
-; CHECK: @__loadtime_comment_str = internal unnamed_addr constant [24 x i8] 
c"@(#) Copyright IBM 2025\00", align 1
 ; Preservation in llvm.compiler.used sets
-; CHECK-NEXT: @llvm.compiler.used = appending global [1 x ptr] [ptr 
@__loadtime_comment_str], section "llvm.metadata"
-; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"}
+; CHECK-NEXT: @llvm.compiler.used = appending global [1 x ptr] [ptr 
@[[LOADTIME_COMMENT_STR]]], section "llvm.metadata"
+
+; Make sure the old string metadata form is gone
+; CHECK-NOT: !{{[0-9]+}} = !{!"@(#) Copyright String 2025"}
 
 ; Function has an implicit ref MD pointing at the string:
 ; CHECK-O0: define void @f0() !implicit.ref ![[MD:[0-9]+]]
 ; CHECK-ON: define void @f0() local_unnamed_addr #0 !implicit.ref 
![[MD:[0-9]+]]
+
 ; CHECK-O0: define i32 @main() !implicit.ref ![[MD]]
 ; CHECK-ON: define noundef i32 @main() local_unnamed_addr #0 !implicit.ref 
![[MD]]
 
 ; Verify metadata content
-; CHECK-O0: ![[MD]] = !{ptr @__loadtime_comment_str}
-; CHECK-ON: ![[MD]] = !{ptr @__loadtime_comment_str}
+; CHECK-O0: ![[MD]] = !{ptr @[[LOADTIME_COMMENT_STR]]}
+; CHECK-ON: ![[MD]] = !{ptr @[[LOADTIME_COMMENT_STR]]}

>From 44b4179d560b91a359b7199a4679f0095c39fa2d Mon Sep 17 00:00:00 2001
From: Tony Varghese <[email protected]>
Date: Tue, 16 Jun 2026 11:36:45 -0400
Subject: [PATCH 5/6] Fix sccpsolver to propagate implict.ref nodes while
 inlining functions.

---
 llvm/lib/Transforms/Utils/SCCPSolver.cpp      | 28 ++++++++++++++++++-
 .../PowerPC/pragma-comment-copyright-lto.ll   |  9 +++---
 .../pragma-comment-copyright-thinlto.ll       |  4 +--
 3 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp 
b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 4cae040f96fc6..3b3a45b5771ae 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -90,6 +90,27 @@ bool SCCPSolver::tryToReplaceWithConstant(Value *V) {
   return true;
 }
 
+/// Helper for propagting !implicit.ref metadata from callee to caller before
+/// erasing a call instruction. This ensures that references to global objects
+/// (e.g., copyright strings) are preserved even when calls are optimized away.
+static void propagateImplicitRefFromCall(CallBase *CB) {
+  Function *Callee = CB->getCalledFunction();
+  if (!Callee)
+    return;
+
+  if (!Callee->hasMetadata(LLVMContext::MD_implicit_ref))
+    return;
+
+  Function *Caller = CB->getParent()->getParent();
+  if (!Caller)
+    return;
+
+  SmallVector<MDNode *> MDs;
+  Callee->getMetadata(LLVMContext::MD_implicit_ref, MDs);
+  for (MDNode *MD : MDs)
+    Caller->addMetadata(LLVMContext::MD_implicit_ref, *MD);
+}
+
 /// Helper for getting ranges from \p Solver. Instructions inserted during
 /// simplification are unavailable in the solver, so we return a full range for
 /// them.
@@ -356,8 +377,13 @@ bool SCCPSolver::simplifyInstsInBlock(BasicBlock &BB,
     if (Inst.getType()->isVoidTy())
       continue;
     if (tryToReplaceWithConstant(&Inst)) {
-      if (wouldInstructionBeTriviallyDead(&Inst))
+      if (wouldInstructionBeTriviallyDead(&Inst)) {
+        // Propagate !implicit.ref before erasing the call.
+        if (auto *CB = dyn_cast<CallBase>(&Inst))
+          propagateImplicitRefFromCall(CB);
+
         Inst.eraseFromParent();
+      }
 
       MadeChanges = true;
       ++InstRemovedStat;
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll 
b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
index 5ff95437d974b..c9677b8d959cb 100644
--- a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
+++ b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
@@ -77,12 +77,11 @@ entry:
 ; CHECK:       li 3, 3
 
 ;; Both copyright strings in the read-only __loadtime_comment csect.
-; CHECK:       .csect __loadtime_comment[RO]
-; CHECK-NEXT:  .lglobl [[TU1_STR]]
-; CHECK:       [[TU1_STR]]:
+; CHECK:       .csect [[TU1_STR]][RO],2
+; CHECK-NEXT:  .lglobl [[TU1_STR]][RO]
 ; CHECK-NEXT:  .string "Copyright TU1"
-; CHECK-NEXT:  .lglobl [[TU2_STR]]
-; CHECK:       [[TU2_STR]]:
+; CHECK:       .csect [[TU2_STR]][RO],2
+; CHECK-NEXT:  .lglobl [[TU2_STR]][RO]
 ; CHECK-NEXT:  .string "Copyright TU2"
 
 ;; f_unused is DCE'd -- must not appear in assembly.
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll 
b/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll
index 1517d8429801a..a0779b626ca77 100644
--- a/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll
+++ b/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll
@@ -53,8 +53,8 @@ entry:
 !1 = !{i32 8, !"PIC Level", i32 2}
 !2 = !{i32 1, !"EnableSplitLTOUnit", i32 0}
 
-; CHECK-TU2-IMPORTED: @[[TU2_STR:__loadtime_comment_str_[0-9a-f]+]] = weak_odr 
hidden unnamed_addr constant [19 x i8] c"@(#) Copyright TU2\00", section 
"__loadtime_comment", align 1
-; CHECK-TU2-IMPORTED-NEXT: @[[TU1_STR:__loadtime_comment_str_[0-9a-f]+]] = 
available_externally hidden unnamed_addr constant [19 x i8] c"@(#) Copyright 
TU1\00", section "__loadtime_comment", align 1
+; CHECK-TU2-IMPORTED: @[[TU2_STR:__loadtime_comment_str_[0-9a-f]+]] = weak_odr 
hidden unnamed_addr constant [19 x i8] c"@(#) Copyright TU2\00", align 1
+; CHECK-TU2-IMPORTED-NEXT: @[[TU1_STR:__loadtime_comment_str_[0-9a-f]+]] = 
available_externally hidden unnamed_addr constant [19 x i8] c"@(#) Copyright 
TU1\00", align 1
 ; CHECK-TU2-IMPORTED-NEXT: @llvm.compiler.used = appending global [2 x ptr] 
[ptr @[[TU2_STR]], ptr @[[TU1_STR]]], section "llvm.metadata"
 
 ; CHECK-TU2-IMPORTED-LABEL: define i32 @main()

>From fae4240c35041e971341c3a8d8440ec1e7af2b15 Mon Sep 17 00:00:00 2001
From: Tony Varghese <[email protected]>
Date: Tue, 16 Jun 2026 15:50:15 -0400
Subject: [PATCH 6/6] [clang]Move copyright pragma lowering from metadata to IR
 global

---
 clang/lib/CodeGen/CodeGenModule.cpp           | 44 +++++++---
 clang/lib/CodeGen/CodeGenModule.h             |  8 +-
 clang/test/CodeGen/PowerPC/pragma-comment.c   |  8 +-
 .../Utils/LowerCommentStringPass.cpp          | 88 ++++++-------------
 .../PowerPC/pragma-comment-copyright-lto.ll   | 22 +++--
 .../pragma-comment-copyright-thinlto.ll       | 44 +++++-----
 .../lower-comment-string.ll                   | 13 ++-
 7 files changed, 100 insertions(+), 127 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 9dedbed54c77a..8b1148a0401a6 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -79,6 +79,7 @@
 #include "llvm/Transforms/Instrumentation/KCFI.h"
 #include "llvm/Transforms/Utils/BuildLibCalls.h"
 #include "llvm/Transforms/Utils/KCFIHash.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
 #include <optional>
 #include <set>
 
@@ -1744,8 +1745,6 @@ void CodeGenModule::Release() {
 
   EmitBackendOptionsMetadata(getCodeGenOpts());
 
-  EmitLoadTimeComment();
-
   // If there is device offloading code embed it in the host now.
   EmbedObject(&getModule(), CodeGenOpts, *getFileSystem(), getDiags());
 
@@ -3689,7 +3688,7 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
   LinkerOptionsMetadata.push_back(llvm::MDNode::get(C, MDOpts));
 }
 
-/// Process copyright pragma and create LLVM metadata.
+/// Process copyright pragma and and create a global variable with metadata.
 ///
 /// Only one copyright pragma is allowed per translation unit. Subsequent
 /// pragmas in the same TU are ignored with a warning at the parse level.
@@ -3709,13 +3708,37 @@ void 
CodeGenModule::ProcessPragmaCommentCopyright(StringRef Comment,
   if (isFromASTFile)
     return;
 
-  assert(!LoadTimeComment &&
+  assert(!LoadTimeCommentGlobal &&
          "Only one copyright pragma allowed per translation unit.");
 
-  // Create LLVM metadata with the comment string.
+  // Create a weak_odr hidden global variable containing the copyright string.
+  // Hash the content to generate a stable, unique name across TUs.
   auto &C = getLLVMContext();
-  llvm::Metadata *Ops[] = {llvm::MDString::get(C, Comment)};
-  LoadTimeComment = llvm::MDNode::get(C, Ops);
+  uint64_t Hash = xxh3_64bits(Comment);
+  std::string GlobalName =
+      ("__loadtime_comment_str_" + Twine::utohexstr(Hash)).str();
+
+  // Create null-terminated string constant
+  llvm::Constant *StrInit =
+      llvm::ConstantDataArray::getString(C, Comment, /*AddNull=*/true);
+
+  // Create weak_odr linkage so multiple TUs with identical strings merge
+  auto *GV = new llvm::GlobalVariable(getModule(), StrInit->getType(),
+                                      /*isConstant=*/true,
+                                      llvm::GlobalValue::WeakODRLinkage,
+                                      StrInit, GlobalName);
+
+  GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
+  GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+  GV->setAlignment(llvm::Align(1));
+
+  // Mark with loadtime_comment metadata for LowerCommentStringPass
+  GV->setMetadata("loadtime_comment", llvm::MDNode::get(C, {}));
+
+  // Prevent optimizer from removing the Global Var.
+  llvm::appendToCompilerUsed(getModule(), {GV});
+
+  LoadTimeCommentGlobal = GV;
 }
 
 /// Add link options implied by the given module, including modules
@@ -4238,13 +4261,6 @@ bool CodeGenModule::MustBeEmitted(const ValueDecl 
*Global) {
   return getContext().DeclMustBeEmitted(Global);
 }
 
-void CodeGenModule::EmitLoadTimeComment() {
-  if (LoadTimeComment) {
-    auto *NMD = 
getModule().getOrInsertNamedMetadata("comment_string.loadtime");
-    NMD->addOperand(LoadTimeComment);
-  }
-}
-
 bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
   // In OpenMP 5.0 variables and function may be marked as
   // device_type(host/nohost) and we should not emit them eagerly unless we 
sure
diff --git a/clang/lib/CodeGen/CodeGenModule.h 
b/clang/lib/CodeGen/CodeGenModule.h
index 2106ad26d54f0..badb740f0ba32 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -619,8 +619,8 @@ 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;
+  /// Global variable for copyright pragma comment (if present).
+  llvm::GlobalVariable *LoadTimeCommentGlobal = nullptr;
 
   /// @name Cache for Objective-C runtime types
   /// @{
@@ -2171,10 +2171,6 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Emit deactivation symbols for any PFP fields whose offset is taken with
   /// offsetof.
   void emitPFPFieldsWithEvaluatedOffset();
-
-  /// Emit the load-time comment metadata (e.g., from
-  /// #pragma comment(copyright, ...)) for the translation unit.
-  void EmitLoadTimeComment();
 };
 
 }  // end namespace CodeGen
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment.c 
b/clang/test/CodeGen/PowerPC/pragma-comment.c
index 32afb8bdfa33a..b9dc43fca6184 100644
--- a/clang/test/CodeGen/PowerPC/pragma-comment.c
+++ b/clang/test/CodeGen/PowerPC/pragma-comment.c
@@ -11,12 +11,12 @@
 
 int main() { return 0; }
 
-// CHECK-COPYRIGHT: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
-// CHECK-COPYRIGHT: ![[COPYRIGHT]] = !{!"@(#) Hello, world\0A\09\22quoted\22"}
+// CHECK-COPYRIGHT: @[[LOADTIME_STR:__loadtime_comment_str_[0-9a-f]+]] = 
weak_odr hidden unnamed_addr constant [29 x i8] c"@(#) Hello, 
world\0A\09\22quoted\22\00", align 1, !loadtime_comment !0
+// CHECK-COPYRIGHT: @llvm.compiler.used = appending global [1 x ptr] [ptr 
@[[LOADTIME_STR]]], section "llvm.metadata"
 
 //--- operator-pragma-copyright.c
 _Pragma("comment(copyright, \"IBM Copyright Pragma Operator\")")
 void foo() {}
 
-// CHECK-OPERATOR: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
-// CHECK-OPERATOR: ![[COPYRIGHT]] = !{!"IBM Copyright Pragma Operator"}
+// CHECK-OPERATOR: @[[LOADTIME_STR:__loadtime_comment_str_[0-9a-f]+]] = 
weak_odr hidden unnamed_addr constant [30 x i8] c"IBM Copyright Pragma 
Operator\00", align 1, !loadtime_comment !0
+// CHECK-OPERATOR: @llvm.compiler.used = appending global [1 x ptr] [ptr 
@[[LOADTIME_STR]]], section "llvm.metadata"
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp 
b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
index ade6843be576b..3a4aa5e86df3f 100644
--- a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -45,7 +45,6 @@
 #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"
@@ -58,10 +57,8 @@
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
-#include "llvm/Support/xxhash.h"
 #include "llvm/TargetParser/Triple.h"
 #include "llvm/Transforms/Utils/ModuleUtils.h"
-#include <string>
 
 #define DEBUG_TYPE "lower-comment-string"
 
@@ -84,68 +81,35 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M,
 
   LLVMContext &Ctx = M.getContext();
 
-  // Single-metadata: !comment_string.loadtime = !{!0}
-  // Each operand node is expected to have one MDString operand.
-  NamedMDNode *MD = M.getNamedMetadata("comment_string.loadtime");
-  if (!MD || MD->getNumOperands() == 0)
-    return PreservedAnalyses::all();
-
-  // At this point we are guaranteed that one TU contains a single copyright
-  // metadata entry. Create TU-local string global for that metadata entry.
-  MDNode *MdNode = MD->getOperand(0);
-  if (!MdNode || MdNode->getNumOperands() == 0)
-    return PreservedAnalyses::all();
+  // Collect all globals marked with !loadtime_comment metadata.
+  SmallVector<GlobalValue *, 4> LoadTimeCommentGlobals;
+  for (GlobalVariable &GV : M.globals()) {
+    if (GV.hasMetadata("loadtime_comment"))
+      LoadTimeCommentGlobals.push_back(&GV);
+  }
 
-  auto *MdString = dyn_cast_or_null<MDString>(MdNode->getOperand(0));
-  if (!MdString)
+  if (LoadTimeCommentGlobals.empty())
     return PreservedAnalyses::all();
 
-  StringRef Text = MdString->getString();
-  if (Text.empty())
-    return PreservedAnalyses::all();
-
-  uint64_t Hash = xxh3_64bits(Text);
-  std::string GlobalName =
-      ("__loadtime_comment_str_" + Twine::utohexstr(Hash)).str();
-
-  // 1. Create a single null-terminated string global.
-  Constant *StrInit = ConstantDataArray::getString(Ctx, Text, 
/*AddNull=*/true);
-
-  // The global variable should be weak_odr, constant, and TU-hidden.
-  auto *StrGV = new GlobalVariable(
-      M, StrInit->getType(),
-      /*isConstant=*/true, GlobalValue::WeakODRLinkage, StrInit, GlobalName);
-  StrGV->setVisibility(GlobalValue::HiddenVisibility);
-  // Set unnamed_addr to allow the linker to merge identical strings.
-  StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
-  StrGV->setAlignment(Align(1));
-
-  // 2. Add the string to llvm.compiler.used to prevent LLVM optimization/LTO
-  // passes from removing it.
-  appendToCompilerUsed(M, {StrGV});
-
-  // 3. Attach !implicit.ref metadata to every defined function.
-  // Create a metadata node pointing to the copyright string:
-  //   !N = !{ptr @__loadtime_comment_str}
-  Metadata *Ops[] = {ConstantAsMetadata::get(StrGV)};
-  MDNode *ImplicitRefMD = MDNode::get(Ctx, Ops);
-
-  auto AddImplicitRef = [&](Function &F) {
-    if (F.isDeclaration())
-      return;
-    // Attach the !implicit.ref metadata to the function.
-    F.setMetadata(LLVMContext::MD_implicit_ref, ImplicitRefMD);
-    LLVM_DEBUG(dbgs() << "[copyright] attached implicit.ref to function:  "
-                      << F.getName() << "\n");
-  };
-
-  // Process all functions in the module and add !implicit.ref to the function.
-  for (Function &F : M)
-    AddImplicitRef(F);
-
-  // Cleanup the processed metadata.
-  MD->eraseFromParent();
-  LLVM_DEBUG(dbgs() << "[copyright] created string and anchor for module\n");
+  // Add implicit.ref from every function to each loadtime comment global.
+  for (GlobalValue *GV : LoadTimeCommentGlobals) {
+    for (Function &F : M) {
+      if (F.isDeclaration())
+        continue;
+
+      Metadata *Ops[] = {ConstantAsMetadata::get(GV)};
+      MDNode *NewMD = MDNode::get(Ctx, Ops);
+      F.addMetadata(LLVMContext::MD_implicit_ref, *NewMD);
+
+      LLVM_DEBUG(
+          dbgs() << "[loadtime-comment] attached implicit.ref to function: "
+                 << F.getName() << " for global: " << GV->getName() << "\n");
+    }
+  }
+
+  LLVM_DEBUG(dbgs() << "[loadtime-comment] processed "
+                    << LoadTimeCommentGlobals.size()
+                    << " loadtime comment globals\n");
 
   return PreservedAnalyses::all();
 }
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll 
b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
index c9677b8d959cb..91d1a932dba1e 100644
--- a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
+++ b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
@@ -28,6 +28,9 @@
 target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
 target triple = "powerpc-ibm-aix7.3.0.0"
 
+@__loadtime_comment_str_43ac0464497b8531 = weak_odr hidden unnamed_addr 
constant [14 x i8] c"Copyright TU1\00", align 1, !loadtime_comment !0
[email protected] = appending global [1 x ptr] [ptr 
@__loadtime_comment_str_43ac0464497b8531], section "llvm.metadata"
+
 define i32 @f_add(i32 noundef %a, i32 noundef %b) {
 entry:
   %add = add nsw i32 %a, %b
@@ -40,16 +43,15 @@ entry:
   ret void
 }
 
-!comment_string.loadtime = !{!0}
-!llvm.module.flags = !{!1, !2}
-!0 = !{!"Copyright TU1"}
-!1 = !{i32 8, !"PIC Level", i32 2}
-!2 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
+!0 = !{}
 
 ;--- tu2.ll
 target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
 target triple = "powerpc-ibm-aix7.3.0.0"
 
+@__loadtime_comment_str_645206960c47d270 = weak_odr hidden unnamed_addr 
constant [14 x i8] c"Copyright TU2\00", align 1, !loadtime_comment !0
[email protected] = appending global [1 x ptr] [ptr 
@__loadtime_comment_str_645206960c47d270], section "llvm.metadata"
+
 declare i32 @f_add(i32 noundef, i32 noundef)
 
 define i32 @main() {
@@ -58,11 +60,7 @@ entry:
   ret i32 %call
 }
 
-!comment_string.loadtime = !{!0}
-!llvm.module.flags = !{!1, !2}
-!0 = !{!"Copyright TU2"}
-!1 = !{i32 8, !"PIC Level", i32 2}
-!2 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
+!0 = !{}
 
 ;; f_add csect anchors TU1 copyright string.
 ; CHECK-LABEL: .f_add:
@@ -77,10 +75,10 @@ entry:
 ; CHECK:       li 3, 3
 
 ;; Both copyright strings in the read-only __loadtime_comment csect.
-; CHECK:       .csect [[TU1_STR]][RO],2
+; CHECK-DAG:   .csect [[TU1_STR]][RO],2
 ; CHECK-NEXT:  .lglobl [[TU1_STR]][RO]
 ; CHECK-NEXT:  .string "Copyright TU1"
-; CHECK:       .csect [[TU2_STR]][RO],2
+; CHECK-DAG:   .csect [[TU2_STR]][RO],2
 ; CHECK-NEXT:  .lglobl [[TU2_STR]][RO]
 ; CHECK-NEXT:  .string "Copyright TU2"
 
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll 
b/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll
index a0779b626ca77..2e5d1bf59ddbd 100644
--- a/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll
+++ b/llvm/test/LTO/PowerPC/pragma-comment-copyright-thinlto.ll
@@ -1,6 +1,7 @@
 ;; ThinLTO test for #pragma comment(copyright, ...).
-;; Tests that Prgam commentcopyright strings from module TU1 gets imported 
-;; to module TU2 and is gets added to TU2's @llvm.compiler.used.
+;; Verifies that the copyright string from TU1 gets imported into TU2
+;; and added to TU2's @llvm.compiler.used, with !implicit.ref attached
+;; to both main and the imported f_tu1.
 
 ; REQUIRES: powerpc-registered-target
 
@@ -10,9 +11,9 @@
 ; RUN:   opt -module-summary -o %t/tu1.bc
 ; RUN: opt -passes='thinlto-pre-link<O2>' %t/tu2.ll -o - | \
 ; RUN:   opt -module-summary -o %t/tu2.bc
-; RUN: llvm-lto --thinlto-action=thinlink -o combined %t/tu1.bc %t/tu2.bc
+; RUN: llvm-lto --thinlto-action=thinlink -o %t/combined %t/tu1.bc %t/tu2.bc
 ; RUN: llvm-lto --thinlto-action=import  \
-; RUN:         --thinlto-index=combined  \
+; RUN:         --thinlto-index=%t/combined  \
 ; RUN:         --exported-symbol=main    \
 ; RUN:         --exported-symbol=f_add   \
 ; RUN:         --exported-symbol=my_function \
@@ -23,22 +24,24 @@
 target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
 target triple = "powerpc-ibm-aix7.3.0.0"
 
+@__loadtime_comment_str_43ac0464497b8531 = weak_odr hidden unnamed_addr 
constant [14 x i8] c"Copyright TU1\00", align 1, !loadtime_comment !0
[email protected] = appending global [1 x ptr] [ptr 
@__loadtime_comment_str_43ac0464497b8531], section "llvm.metadata"
+
 define i32 @f_add(i32 noundef %a, i32 noundef %b) {
 entry:
   %add = add nsw i32 %a, %b
   ret i32 %add
 }
 
-!comment_string.loadtime = !{!0}
-!llvm.module.flags = !{!1, !2}
-!0 = !{!"@(#) Copyright TU1"}
-!1 = !{i32 8, !"PIC Level", i32 2}
-!2 = !{i32 1, !"EnableSplitLTOUnit", i32 0}
+!0 = !{}
 
 ;--- tu2.ll
 target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
 target triple = "powerpc-ibm-aix7.3.0.0"
 
+@__loadtime_comment_str_645206960c47d270 = weak_odr hidden unnamed_addr 
constant [14 x i8] c"Copyright TU2\00", align 1, !loadtime_comment !0
[email protected] = appending global [1 x ptr] [ptr 
@__loadtime_comment_str_645206960c47d270], section "llvm.metadata"
+
 declare i32 @f_add(i32 noundef, i32 noundef)
 
 define i32 @main() {
@@ -47,21 +50,20 @@ entry:
   ret i32 %call
 }
 
-!comment_string.loadtime = !{!0}
-!llvm.module.flags = !{!1, !2}
-!0 = !{!"@(#) Copyright TU2"}
-!1 = !{i32 8, !"PIC Level", i32 2}
-!2 = !{i32 1, !"EnableSplitLTOUnit", i32 0}
+!0 = !{}
 
-; CHECK-TU2-IMPORTED: @[[TU2_STR:__loadtime_comment_str_[0-9a-f]+]] = weak_odr 
hidden unnamed_addr constant [19 x i8] c"@(#) Copyright TU2\00", align 1
-; CHECK-TU2-IMPORTED-NEXT: @[[TU1_STR:__loadtime_comment_str_[0-9a-f]+]] = 
available_externally hidden unnamed_addr constant [19 x i8] c"@(#) Copyright 
TU1\00", align 1
-; CHECK-TU2-IMPORTED-NEXT: @llvm.compiler.used = appending global [2 x ptr] 
[ptr @[[TU2_STR]], ptr @[[TU1_STR]]], section "llvm.metadata"
+;; After ThinLTO import, TU2's module should contain both copyright globals.
+;; TU2's own string stays weak_odr; TU1's string is imported as 
available_externally.
+; CHECK-TU2-IMPORTED: @[[TU2_STR:__loadtime_comment_str_[0-9a-f]+]] = weak_odr 
hidden unnamed_addr constant [14 x i8] c"Copyright TU2\00", align 1, 
!loadtime_comment
+; CHECK-TU2-IMPORTED: @[[TU1_STR:__loadtime_comment_str_[0-9a-f]+]] = 
available_externally hidden unnamed_addr constant [14 x i8] c"Copyright 
TU1\00", align 1, !loadtime_comment
+; CHECK-TU2-IMPORTED: @llvm.compiler.used = appending global [2 x ptr] [ptr 
@[[TU2_STR]], ptr @[[TU1_STR]]], section "llvm.metadata"
 
-; CHECK-TU2-IMPORTED-LABEL: define i32 @main()
-; CHECK-TU2-IMPORTED-SAME: local_unnamed_addr !implicit.ref ![[MAIN_MD:[0-9]+]]
+;; main carries TU2's implicit.ref.
+; CHECK-TU2-IMPORTED: define i32 @main(){{.*}}!implicit.ref ![[MAIN_MD:[0-9]+]]
 
-; CHECK-TU2-IMPORTED-LABEL: define available_externally i32 @f_add(
-; CHECK-TU2-IMPORTED-SAME: local_unnamed_addr #[[FADD_ATTR:[0-9]+]] 
!implicit.ref ![[FADD_MD:[0-9]+]]
+;; f_add is imported and carries TU1's implicit.ref.
+; CHECK-TU2-IMPORTED: define available_externally {{.*}}i32 
@f_add({{.*}}){{.*}}!implicit.ref ![[FADD_MD:[0-9]+]]
 
+;; Verify metadata content.
 ; CHECK-TU2-IMPORTED: ![[MAIN_MD]] = !{ptr @[[TU2_STR]]}
 ; CHECK-TU2-IMPORTED: ![[FADD_MD]] = !{ptr @[[TU1_STR]]}
diff --git a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll 
b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
index da855af4331f2..724977b4ffe3f 100644
--- a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
+++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
@@ -8,6 +8,9 @@
 
 target triple = "powerpc-ibm-aix"
 
+@__loadtime_comment_str_f20696a95b638f0b = weak_odr hidden unnamed_addr 
constant [24 x i8] c"@(#) Copyright TU1 v1.0\00", align 1, !loadtime_comment !0
[email protected] = appending global [1 x ptr] [ptr 
@__loadtime_comment_str_f20696a95b638f0b], section "llvm.metadata"
+
 define void @f0() {
 entry:
   ret void    
@@ -17,17 +20,11 @@ entry:
   ret i32 0
 }
 
-!comment_string.loadtime = !{!0}
-!0 = !{!"@(#) Copyright String 2025"}
-
+!0 = !{}
 ; ---- Globals --------------------------------------------
-; CHECK: @[[LOADTIME_COMMENT_STR:__loadtime_comment_str_[0-9a-f]+]] = weak_odr 
hidden unnamed_addr constant [27 x i8] c"@(#) Copyright String 2025\00", align 1
-
-; Preservation in llvm.compiler.used sets
+; CHECK: @[[LOADTIME_COMMENT_STR:__loadtime_comment_str_[0-9a-f]+]] = weak_odr 
hidden unnamed_addr constant [24 x i8] c"@(#) Copyright TU1 v1.0\00", align 1, 
!loadtime_comment !0
 ; CHECK-NEXT: @llvm.compiler.used = appending global [1 x ptr] [ptr 
@[[LOADTIME_COMMENT_STR]]], section "llvm.metadata"
 
-; Make sure the old string metadata form is gone
-; CHECK-NOT: !{{[0-9]+}} = !{!"@(#) Copyright String 2025"}
 
 ; Function has an implicit ref MD pointing at the string:
 ; CHECK-O0: define void @f0() !implicit.ref ![[MD:[0-9]+]]

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

Reply via email to