[clang] [llvm] [PowerPC][AIX] Support #pragma comment copyright for AIX (PR #178184)

Tony Varghese via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 23:17:19 PDT 2026


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

>From 06f8ed1e8987a70ba847aa06a5cde25e19e61dc5 Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Mon, 2 Feb 2026 23:19:10 -0500
Subject: [PATCH 01/10] [PowerPC][AIX] Support #pragma comment copyright for
 AIX

- Emit !aix.copyright.comment from Clang for the pragma.
- Lower it in LLVM to a TU-local string + llvm.used + !implicit.ref.
- Add module-import and backend relocation tests.
---
 .../clang/Basic/DiagnosticParseKinds.td       |   4 +
 clang/include/clang/Basic/PragmaKinds.h       |   3 +-
 clang/lib/AST/TextNodeDumper.cpp              |   3 +
 clang/lib/CodeGen/CodeGenModule.cpp           |  51 +++++-
 clang/lib/CodeGen/CodeGenModule.h             |  11 ++
 clang/lib/Parse/ParsePragma.cpp               |  53 +++++--
 .../pragma-comment-copyright-modules.cpp      |  30 ++++
 clang/test/CodeGen/PowerPC/pragma-comment.c   |  42 +++++
 clang/test/CodeGen/lto-newpm-pipeline.c       |   2 +
 clang/test/Parser/pragma-comment.c            |  11 ++
 .../Transforms/Utils/LowerCommentStringPass.h |  24 +++
 llvm/lib/Passes/PassBuilder.cpp               |   1 +
 llvm/lib/Passes/PassBuilderPipelines.cpp      |   7 +
 llvm/lib/Passes/PassRegistry.def              |   1 +
 llvm/lib/Transforms/Utils/CMakeLists.txt      |   1 +
 .../Utils/LowerCommentStringPass.cpp          | 148 ++++++++++++++++++
 .../CodeGen/AArch64/print-pipeline-passes.ll  |   2 +-
 .../CodeGen/Hexagon/print-pipeline-passes.ll  |   2 +-
 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 +
 .../lower-comment-string.ll                   |  59 +++++++
 23 files changed, 444 insertions(+), 15 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/Parser/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/Transforms/LowerCommentString/lower-comment-string.ll

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 53982025dfd4d..f00ac451d020d 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2560,6 +2560,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 ba51e7a3ff678..4dc103bd7aaa4 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1642,6 +1642,8 @@ void CodeGenModule::Release() {
 
   EmitBackendOptionsMetadata(getCodeGenOpts());
 
+  EmitLoadTimeComment();
+
   // If there is device offloading code embed it in the host now.
   EmbedObject(&getModule(), CodeGenOpts, *getFileSystem(), getDiags());
 
@@ -3575,6 +3577,39 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
   LinkerOptionsMetadata.push_back(llvm::MDNode::get(C, MDOpts));
 }
 
+/// Process copyright pragma and create LLVM metadata.
+/// #pragma comment(copyright, "string") embeds copyright information into a
+/// loadable program-data section of the object file for inclusion in the linked
+/// module.
+///
+/// Example: #pragma comment(copyright, "Copyright string")
+///
+/// This should only be called once per translation unit.
+void CodeGenModule::ProcessPragmaComment(PragmaMSCommentKind Kind,
+                                         StringRef Comment,
+                                         bool isFromASTFile) {
+  // Ensure we are only processing Copyright Pragmas
+  assert(Kind == PCK_Copyright &&
+         "Unexpected pragma comment kind, ProcessPragmaComment should only be "
+         "called for PCK_Copyright");
+  // Target Guard: Only AIX supports PCK_Copyright currently.
+  assert(getTriple().isOSAIX() &&
+         "pragma comment copyright is supported only when targeting AIX");
+
+  // Deserialization Guard: Only process if copyright originated in this TU.
+  if (isFromASTFile)
+    return;
+
+  // Only one copyright pragma allowed per translation unit
+  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,
@@ -4095,6 +4130,16 @@ bool CodeGenModule::MustBeEmitted(const ValueDecl *Global) {
   return getContext().DeclMustBeEmitted(Global);
 }
 
+void CodeGenModule::EmitLoadTimeComment() {
+  // Emit copyright metadata for #pragma comment(copyright, ...).
+  // The LoadTimeComment is set during pragma processing and contains the
+  // copyright string that should be embedded in the executable.
+  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
@@ -7848,7 +7893,11 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
       AppendLinkerOptions(PCD->getArg());
       break;
     case PCK_Lib:
-        AddDependentLib(PCD->getArg());
+      AddDependentLib(PCD->getArg());
+      break;
+    case PCK_Copyright:
+      ProcessPragmaComment(PCD->getCommentKind(), PCD->getArg(),
+                           PCD->isFromASTFile());
       break;
     case PCK_Compiler:
     case PCK_ExeStr:
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index d62707a3355c9..d08d5f108267d 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -592,6 +592,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
   /// @{
 
@@ -1499,6 +1502,9 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Appends a dependent lib to the appropriate metadata value.
   void AddDependentLib(StringRef Lib);
 
+  /// Process pragma comment
+  void ProcessPragmaComment(PragmaMSCommentKind Kind, StringRef Comment,
+                            bool isFromASTFile);
 
   llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD);
 
@@ -2079,6 +2085,11 @@ class CodeGenModule : public CodeGenTypeCache {
   /// false, the definition can be emitted lazily if it's used.
   bool MustBeEmitted(const ValueDecl *D);
 
+  /// Emit load-time comment metadata for #pragma comment(copyright, ...).
+  /// This adds the copyright string to the module's named metadata, which will
+  /// be processed by the backend to include it in the generated executable.
+  void EmitLoadTimeComment();
+
   /// Determine whether the definition can be emitted eagerly, or should be
   /// delayed until the end of the translation unit. This is relevant for
   /// definitions whose linkage can change, e.g. implicit function instantions
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cb792e175ee7c..8678efeab02ff 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,27 +3294,49 @@ 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())
-    .Case("linker",   PCK_Linker)
-    .Case("lib",      PCK_Lib)
-    .Case("compiler", PCK_Compiler)
-    .Case("exestr",   PCK_ExeStr)
-    .Case("user",     PCK_User)
-    .Default(PCK_Unknown);
+      llvm::StringSwitch<PragmaMSCommentKind>(II->getName())
+          .Case("linker", PCK_Linker)
+          .Case("lib", PCK_Lib)
+          .Case("compiler", PCK_Compiler)
+          .Case("exestr", PCK_ExeStr)
+          .Case("user", PCK_User)
+          .Case("copyright", PCK_Copyright)
+          .Default(PCK_Unknown);
+
   if (Kind == PCK_Unknown) {
     PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind);
     return;
   }
 
+  // Handle AIX Target restrictions (all non-copyright kinds)
+  if (PP.getTargetInfo().getTriple().isOSAIX() && Kind != PCK_Copyright) {
+    // pragma comment kinds linker, lib, compiler, exestr and user are
+    // ignored when targeting AIX.
+    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
+  if (Kind == PCK_Copyright) {
+    if (SeenCopyrightInTU) {
+      // On AIX, 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 +3363,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/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
new file mode 100644
index 0000000000000..b8641531177ac
--- /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"
+// CHECK-MOD: @llvm.used = appending global {{.*}} @__loadtime_comment_str
+
+// Compile an importing TU that uses the prebuilt module and verify that it
+// does NOT re-emit the module's copyright global.
+
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
+// RUN:   -fprebuilt-module-path=%t -emit-llvm %t/importmod.cc -o - \
+// RUN:   | FileCheck %s --check-prefix=CHECK-IMPORT
+// CHECK-IMPORT-NOT: @__loadtime_comment_str
+// CHECK-IMPORT-NOT: c"module me\00"
+
+//--- copymod.cppm
+export module copymod;
+#pragma comment(copyright, "module me")
+export inline void f() {}
+
+//--- importmod.cc
+import copymod;
+void g() { f(); }
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment.c b/clang/test/CodeGen/PowerPC/pragma-comment.c
new file mode 100644
index 0000000000000..a8eb71b558180
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment.c
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 %s -DTEST_EMPTY_COPYRIGHT -triple powerpc-ibm-aix -verify
+// RUN: %clang_cc1 %s -DTEST_OTHER_COMMENT_KINDS -triple powerpc-ibm-aix -verify
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -verify
+
+// RUN: %clang_cc1 %s -DTEST_EMPTY_COPYRIGHT -triple powerpc64-ibm-aix -verify
+// RUN: %clang_cc1 %s -DTEST_OTHER_COMMENT_KINDS -triple powerpc64-ibm-aix -verify
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -verify
+
+#ifdef TEST_EMPTY_COPYRIGHT
+
+// 1. Verify no diagnostics for empty copyright string
+#pragma comment(copyright, "") // expected-no-diagnostics
+int main() {return 0; }
+
+#elif defined(TEST_OTHER_COMMENT_KINDS)
+
+// 2. Verify warnings for lib/linker and silent ignore for others
+#pragma comment(lib, "m") // expected-warning {{'#pragma comment lib' ignored}}
+#pragma comment(linker, "foo") // expected-warning {{'#pragma comment linker' ignored}}
+
+// These are recognized but ignored in CodeGen
+#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}}
+int main() {return 0; }
+
+#else
+
+// 3. Default Path: Verify metadata generation and duplicate warning
+#pragma comment(copyright, "@(#) Copyright")
+#pragma comment(copyright, "Duplicate Copyright") // expected-warning {{'#pragma comment copyright' ignored: it can be specified only once per translation unit}}
+
+int main() { return 0; }
+// Check that both metadata sections are present
+// CHECK: !comment_string.loadtime = !{![[copyright:[0-9]+]]}
+
+// Check individual metadata content
+// CHECK: ![[copyright]] = !{!"@(#) Copyright"}
+
+#endif
diff --git a/clang/test/CodeGen/lto-newpm-pipeline.c b/clang/test/CodeGen/lto-newpm-pipeline.c
index ea9784a76f923..b9466b9558b06 100644
--- a/clang/test/CodeGen/lto-newpm-pipeline.c
+++ b/clang/test/CodeGen/lto-newpm-pipeline.c
@@ -27,6 +27,7 @@
 
 // CHECK-FULL-O0: Running pass: VerifierPass
 // CHECK-FULL-O0-NEXT: Running analysis: VerifierAnalysis
+// CHECK-FULL-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-FULL-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
 // CHECK-FULL-O0-NEXT: Running pass: EntryExitInstrumenterPass
 // CHECK-FULL-O0-NEXT: Running pass: AlwaysInlinerPass
@@ -41,6 +42,7 @@
 
 // CHECK-THIN-O0: Running pass: VerifierPass
 // CHECK-THIN-O0-NEXT: Running analysis: VerifierAnalysis
+// CHECK-THIN-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-THIN-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
 // CHECK-THIN-O0-NEXT: Running pass: EntryExitInstrumenterPass
 // CHECK-THIN-O0-NEXT: Running pass: AlwaysInlinerPass
diff --git a/clang/test/Parser/pragma-comment.c b/clang/test/Parser/pragma-comment.c
new file mode 100644
index 0000000000000..c9618d44c0f2e
--- /dev/null
+++ b/clang/test/Parser/pragma-comment.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple systemz -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple powerpc64-linux-gnu -fsyntax-only -verify %s
+// RUN: %clang_cc1 -DEXPOK -triple powerpc-ibm-aix -fsyntax-only -verify %s
+
+#ifdef EXPOK
+#pragma comment(copyright,"copyright") // expected-no-diagnostics
+#else
+#pragma comment(copyright,"copyright") // expected-warning {{'#pragma comment copyright' ignored}}
+#endif
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 ec5d6c0eef16e..eeec9b440dc89 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -366,6 +366,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 0b732c6f14197..227e45aae3141 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -144,6 +144,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"
@@ -1471,6 +1472,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   const bool LTOPreLink = isLTOPreLink(LTOPhase);
   ModulePassManager MPM;
 
+  // Process copyright metadata early, before any optimizations
+  MPM.addPass(LowerCommentStringPass());
+
   // Run partial inlining pass to partially inline functions that have
   // large bodies.
   if (RunPartialInlining)
@@ -2340,6 +2344,9 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level,
 
   ModulePassManager MPM;
 
+  // Process copyright metadata at O0 before any other transformations
+  MPM.addPass(LowerCommentStringPass());
+
   instructionCountersPass(MPM, /*IsPreOptimization=*/true);
 
   // Perform pseudo probe instrumentation in O0 mode. This is for the
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 2241a383ebce1..5c4544af55e79 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 82e9edf674866..44c7bec7b25cb 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -53,6 +53,7 @@ add_llvm_component_library(LLVMTransformUtils
   LoopUtils.cpp
   LoopVersioning.cpp
   LowerAtomic.cpp
+  LowerCommentStringPass.cpp
   LowerGlobalDtors.cpp
   LowerIFunc.cpp
   LowerInvoke.cpp
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
new file mode 100644
index 0000000000000..6deef2f75e0a3
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -0,0 +1,148 @@
+//===-- LowerCommentStringPass.cpp - Lower Comment string metadata -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// LowerCommentStringPass pass lowers module-level comment string metadata
+// emitted by Clang:
+//
+//     !comment_string.loadtime = !{!"Copyright ..."}
+//
+// into concrete, translation-unit–local globals.
+// This Pass is enabled only for AIX.
+// For each module (translation unit), the pass performs the following:
+//
+//   1. Creates a null-terminated, internal constant string global
+//      (`__loadtime_comment_str`) containing the copyright text in
+//      `__loadtime_comment` section.
+//
+//   2. Marks the string in `llvm.used` so it cannot be dropped by
+//      optimization or LTO.
+//
+//   3. Attaches `!implicit.ref` metadata referencing the string to every
+//      defined function in the module. The PowerPC AIX backend recognizes
+//      this metadata and emits a `.ref` directive from the function to the
+//      string, creating a concrete relocation that prevents the linker from
+//      discarding it (as long as the referencing symbol is kept).
+//
+//  Input IR:
+//     !comment_string.loadtime = !{!"Copyright"}
+//  Output IR:
+//     @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00",
+//                          section "__loadtime_comment"
+//     @llvm.used = appending global [1 x ptr] [ptr @__loadtime_comment_str]
+//
+//     define i32 @func() !implicit.ref !5 { ... }
+//     !5 = !{ptr @__loadtime_comment_str}
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/MDBuilder.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/TargetParser/Triple.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+#define DEBUG_TYPE "lower-comment-string"
+
+using namespace llvm;
+
+static cl::opt<bool>
+    DisableCopyrightMetadata("disable-lower-comment-string", cl::ReallyHidden,
+                             cl::desc("Disable LowerCommentString pass."),
+                             cl::init(false));
+
+static bool isAIXTriple(const Module &M) {
+  return Triple(M.getTargetTriple()).isOSAIX();
+}
+
+PreservedAnalyses LowerCommentStringPass::run(Module &M,
+                                              ModuleAnalysisManager &AM) {
+  if (DisableCopyrightMetadata || !isAIXTriple(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 guarateed 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);
+
+  // Internal, constant, TU-local--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.used to prevent LLVM optimization/LTO passes from
+  // removing it.
+  appendToUsed(M, {StrGV});
+
+  // 3. Attach !implicit ref 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);
+
+  // Lambda to attach implicit.ref metadata to a function.
+  auto AddImplicitRef = [&](Function &F) {
+    if (F.isDeclaration())
+      return;
+    // Attach the implicit.ref metadata to the function
+    F.setMetadata("implicit.ref", ImplicitRefMD);
+    LLVM_DEBUG(dbgs() << "[copyright] attached implicit.ref to function:  "
+                      << F.getName() << "\n");
+  };
+
+  // Process all functions in the module
+  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..f2b2738b89416 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: {{^}}lower-comment-string,function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,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..50476ae020d68 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: {{^}}lower-comment-string,function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,function(annotation-remarks),verify,print{{$}}
 
 define void @test_hexagon_passes() {
 entry:
diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index 41ac7314deeb3..abd26a2883c3a 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -228,6 +228,7 @@
 ; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-DEFAULT-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-LTO-NOT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
index 06e9e1e988b33..420f4a7662c89 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -155,6 +155,7 @@
 ; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalDCEPass
+; CHECK-POSTLINK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: RecomputeGlobalsAAPass
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 654c9ea154e29..d881b29126f48 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -139,6 +139,7 @@
 ; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass
 ; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass
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 0ad18be5328b7..6f9f9fbd63c93 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -146,6 +146,7 @@
 ; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass
 ; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass
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..ba7ad17f4c4b7
--- /dev/null
+++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
@@ -0,0 +1,59 @@
+; 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
+
+; Verify that LowerCommentStringPass lowers !loadtime.copyright.comment 
+; into concrete, translation-unit–local globals.
+;
+; For each module (translation unit), the pass performs the following:
+;
+;   1. Creates a null-terminated, internal constant string global
+;      (`__loadtime_comment_str`) containing the copyright text in
+;      `__loadtime_comment` section.
+;
+;   2. Marks the string in `llvm.used` so it cannot be dropped by
+;      optimization or LTO.
+;
+;   3. Attaches `!implicit.ref` metadata referencing the string to every
+;      defined function in the module. The PowerPC AIX backend recognizes
+;      this metadata and emits a `.ref` directive from the function to the
+;      string, creating a concrete relocation that prevents the linker from
+;      discarding it (as long as the referencing symbol is kept).
+
+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.used sets
+; CHECK-NEXT: @llvm.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 c20986df1786b85913d4afa929b84812ef65ccef Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Wed, 1 Apr 2026 04:55:18 -0400
Subject: [PATCH 02/10] [Clang][AIX] Document #pragma comment(copyright) for
 AIX in LanguageExtensions.rst

---
 clang/docs/LanguageExtensions.rst | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index e2e98604008ea..681a284105484 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6810,6 +6810,37 @@ 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 on AIX
+targets. This directive embeds a copyright or identifying string into the
+compiled object file so that the string survives into shared libraries and
+executables. The directive is silently ignored on non-AIX targets.
+
+.. code-block:: c
+
+   #pragma comment(copyright, "string-literal")
+
+The *string-literal* may be any ordinary string constant, including
+concatenated string literals. The directive may appear at file scope; it is
+not meaningful inside a function body.
+
+When the directive is processed, Clang emits two read-only XCOFF csect symbols
+into the ``.text`` section of the object file:
+
+``__loadtime_comment``
+  A csect (storage-class ``unamex``, type ``RO``) that contains the comment
+  string data and serves as the owning section for the symbol.
+
+``__loadtime_comment_str``
+  A label (storage-class ``unamex``, type ``LD``) at the same address as
+  ``__loadtime_comment``, pointing directly to the embedded string. This is
+  the symbol that tools such as ``strings`` and ``what`` typically resolve.
+
+Both symbols reside in the ``.text`` segment and are marked read-only, so the
+string is linked into any binary that includes the object file and remains
+visible at run time.
+
 Evaluating Object Size
 ======================
 

>From ea1030511d05d0005591455f101c84b303001691 Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Mon, 13 Apr 2026 06:48:01 -0400
Subject: [PATCH 03/10] Add diagnostic tests to
 clang/test/Preprocessor/pragma-comment.c and add other pragma comment kinds
 to unsupported check for non-aix targets, Update target support function name
 in llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp

---
 clang/lib/CodeGen/CodeGenModule.cpp           |  2 +-
 clang/lib/Parse/ParsePragma.cpp               |  2 +-
 .../pragma-comment-copyright-modules.cpp      |  2 +-
 clang/test/CodeGen/PowerPC/pragma-comment.c   | 39 ++--------------
 clang/test/Parser/pragma-comment.c            | 11 -----
 clang/test/Preprocessor/pragma-comment.c      | 46 +++++++++++++++++++
 .../Utils/LowerCommentStringPass.cpp          | 18 ++++----
 .../lower-comment-string.ll                   | 22 +--------
 8 files changed, 63 insertions(+), 79 deletions(-)
 delete mode 100644 clang/test/Parser/pragma-comment.c
 create mode 100644 clang/test/Preprocessor/pragma-comment.c

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 4dc103bd7aaa4..e415d9850129e 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -7893,7 +7893,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
       AppendLinkerOptions(PCD->getArg());
       break;
     case PCK_Lib:
-      AddDependentLib(PCD->getArg());
+        AddDependentLib(PCD->getArg());
       break;
     case PCK_Copyright:
       ProcessPragmaComment(PCD->getCommentKind(), PCD->getArg(),
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 8678efeab02ff..d60a279da090f 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -3329,7 +3329,7 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
   // Handle pragma comment copyright
   if (Kind == PCK_Copyright) {
     if (SeenCopyrightInTU) {
-      // On AIX, pragma comment copyright can each appear only once in a TU.
+      // pragma comment copyright can each appear only once in a TU.
       PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_once)
           << II->getName();
       return;
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
index b8641531177ac..ca59893f6afe7 100644
--- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
@@ -9,7 +9,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"
-// CHECK-MOD: @llvm.used = appending global {{.*}} @__loadtime_comment_str
+// 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.
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment.c b/clang/test/CodeGen/PowerPC/pragma-comment.c
index a8eb71b558180..426e6f92fff7c 100644
--- a/clang/test/CodeGen/PowerPC/pragma-comment.c
+++ b/clang/test/CodeGen/PowerPC/pragma-comment.c
@@ -1,42 +1,9 @@
-// RUN: %clang_cc1 %s -DTEST_EMPTY_COPYRIGHT -triple powerpc-ibm-aix -verify
-// RUN: %clang_cc1 %s -DTEST_OTHER_COMMENT_KINDS -triple powerpc-ibm-aix -verify
-// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -verify
-
-// RUN: %clang_cc1 %s -DTEST_EMPTY_COPYRIGHT -triple powerpc64-ibm-aix -verify
-// RUN: %clang_cc1 %s -DTEST_OTHER_COMMENT_KINDS -triple powerpc64-ibm-aix -verify
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
 // RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -verify
-
-#ifdef TEST_EMPTY_COPYRIGHT
-
-// 1. Verify no diagnostics for empty copyright string
-#pragma comment(copyright, "") // expected-no-diagnostics
-int main() {return 0; }
-
-#elif defined(TEST_OTHER_COMMENT_KINDS)
 
-// 2. Verify warnings for lib/linker and silent ignore for others
-#pragma comment(lib, "m") // expected-warning {{'#pragma comment lib' ignored}}
-#pragma comment(linker, "foo") // expected-warning {{'#pragma comment linker' ignored}}
-
-// These are recognized but ignored in CodeGen
-#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}}
-int main() {return 0; }
-
-#else
-
-// 3. Default Path: Verify metadata generation and duplicate warning
 #pragma comment(copyright, "@(#) Copyright")
-#pragma comment(copyright, "Duplicate Copyright") // expected-warning {{'#pragma comment copyright' ignored: it can be specified only once per translation unit}}
 
 int main() { return 0; }
-// Check that both metadata sections are present
-// CHECK: !comment_string.loadtime = !{![[copyright:[0-9]+]]}
-
-// Check individual metadata content
-// CHECK: ![[copyright]] = !{!"@(#) Copyright"}
 
-#endif
+// CHECK: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
+// CHECK: ![[COPYRIGHT]] = !{!"@(#) Copyright"}
\ No newline at end of file
diff --git a/clang/test/Parser/pragma-comment.c b/clang/test/Parser/pragma-comment.c
deleted file mode 100644
index c9618d44c0f2e..0000000000000
--- a/clang/test/Parser/pragma-comment.c
+++ /dev/null
@@ -1,11 +0,0 @@
-// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %s
-// RUN: %clang_cc1 -triple systemz -fsyntax-only -verify %s
-// RUN: %clang_cc1 -triple powerpc64-linux-gnu -fsyntax-only -verify %s
-// RUN: %clang_cc1 -DEXPOK -triple powerpc-ibm-aix -fsyntax-only -verify %s
-
-#ifdef EXPOK
-#pragma comment(copyright,"copyright") // expected-no-diagnostics
-#else
-#pragma comment(copyright,"copyright") // expected-warning {{'#pragma comment copyright' ignored}}
-#endif
diff --git a/clang/test/Preprocessor/pragma-comment.c b/clang/test/Preprocessor/pragma-comment.c
new file mode 100644
index 0000000000000..210c48a513a45
--- /dev/null
+++ b/clang/test/Preprocessor/pragma-comment.c
@@ -0,0 +1,46 @@
+// 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
+
+//--- 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}}
+
+//--- 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.
+#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}}
+
+//--- 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}}
\ No newline at end of file
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
index 6deef2f75e0a3..d762125755f7f 100644
--- a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -6,12 +6,11 @@
 //
 //===---------------------------------------------------------------------===//
 //
-// LowerCommentStringPass pass lowers module-level comment string metadata
-// emitted by Clang:
+// This pass lowers the module-level comment string metadata emitted by Clang:
 //
 //     !comment_string.loadtime = !{!"Copyright ..."}
 //
-// into concrete, translation-unit–local globals.
+// into concrete, translation-unit-local globals.
 // This Pass is enabled only for AIX.
 // For each module (translation unit), the pass performs the following:
 //
@@ -26,7 +25,7 @@
 //      defined function in the module. The PowerPC AIX backend recognizes
 //      this metadata and emits a `.ref` directive from the function to the
 //      string, creating a concrete relocation that prevents the linker from
-//      discarding it (as long as the referencing symbol is kept).
+//      discarding the string (as long as the referencing symbol is kept).
 //
 //  Input IR:
 //     !comment_string.loadtime = !{!"Copyright"}
@@ -70,13 +69,14 @@ static cl::opt<bool>
                              cl::desc("Disable LowerCommentString pass."),
                              cl::init(false));
 
-static bool isAIXTriple(const Module &M) {
-  return Triple(M.getTargetTriple()).isOSAIX();
+static bool isSupportedTarget(const Module &M) {
+  Triple T{M.getTargetTriple()};
+  return T.isOSAIX();
 }
 
 PreservedAnalyses LowerCommentStringPass::run(Module &M,
                                               ModuleAnalysisManager &AM) {
-  if (DisableCopyrightMetadata || !isAIXTriple(M))
+  if (DisableCopyrightMetadata || !isSupportedTarget(M))
     return PreservedAnalyses::all();
 
   LLVMContext &Ctx = M.getContext();
@@ -118,7 +118,7 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M,
 
   // 2. Add the string to llvm.used to prevent LLVM optimization/LTO passes from
   // removing it.
-  appendToUsed(M, {StrGV});
+  appendToCompilerUsed(M, {StrGV});
 
   // 3. Attach !implicit ref to every defined function
   // Create a metadata node pointing to the copyright string:
@@ -131,7 +131,7 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M,
     if (F.isDeclaration())
       return;
     // Attach the implicit.ref metadata to the function
-    F.setMetadata("implicit.ref", ImplicitRefMD);
+    F.setMetadata(LLVMContext::MD_implicit_ref, ImplicitRefMD);
     LLVM_DEBUG(dbgs() << "[copyright] attached implicit.ref to function:  "
                       << F.getName() << "\n");
   };
diff --git a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
index ba7ad17f4c4b7..bf353bdad4a32 100644
--- a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
+++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
@@ -6,24 +6,6 @@
 ; 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
 
-; Verify that LowerCommentStringPass lowers !loadtime.copyright.comment 
-; into concrete, translation-unit–local globals.
-;
-; For each module (translation unit), the pass performs the following:
-;
-;   1. Creates a null-terminated, internal constant string global
-;      (`__loadtime_comment_str`) containing the copyright text in
-;      `__loadtime_comment` section.
-;
-;   2. Marks the string in `llvm.used` so it cannot be dropped by
-;      optimization or LTO.
-;
-;   3. Attaches `!implicit.ref` metadata referencing the string to every
-;      defined function in the module. The PowerPC AIX backend recognizes
-;      this metadata and emits a `.ref` directive from the function to the
-;      string, creating a concrete relocation that prevents the linker from
-;      discarding it (as long as the referencing symbol is kept).
-
 target triple = "powerpc-ibm-aix"
 
 define void @f0() {
@@ -44,8 +26,8 @@ entry:
 
 ; ---- 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.used sets
-; CHECK-NEXT: @llvm.used = appending global [1 x ptr] [ptr @__loadtime_comment_str], section "llvm.metadata"
+; 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:

>From 8a1d29e89157842ab4f23b34cd9d903dab320506 Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Tue, 14 Apr 2026 01:28:01 -0400
Subject: [PATCH 04/10] [Clang][AIX] Add tests for evaluated pragma
 comment(copyright) string literals

---
 clang/docs/LanguageExtensions.rst           |  6 +++---
 clang/test/CodeGen/PowerPC/pragma-comment.c |  4 ++--
 clang/test/Preprocessor/pragma-comment.c    | 16 +++++++++++++++-
 3 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 681a284105484..fdb8147007c10 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6821,9 +6821,9 @@ executables. The directive is silently ignored on non-AIX targets.
 
    #pragma comment(copyright, "string-literal")
 
-The *string-literal* may be any ordinary string constant, including
-concatenated string literals. The directive may appear at file scope; it is
-not meaningful inside a function body.
+The second argument is an ordinary string literal. Concatenated ordinary string 
+literals are also accepted. The directive may appear at file scope; it is not
+meaningful inside a function body.
 
 When the directive is processed, Clang emits two read-only XCOFF csect symbols
 into the ``.text`` section of the object file:
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment.c b/clang/test/CodeGen/PowerPC/pragma-comment.c
index 426e6f92fff7c..be99852bccccb 100644
--- a/clang/test/CodeGen/PowerPC/pragma-comment.c
+++ b/clang/test/CodeGen/PowerPC/pragma-comment.c
@@ -1,9 +1,9 @@
 // RUN: %clang_cc1 %s -triple powerpc-ibm-aix   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
 // RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s
 
-#pragma comment(copyright, "@(#) Copyright")
+#pragma comment(copyright, "@(#) Hello, " " world\n\t\"quoted\"")
 
 int main() { return 0; }
 
 // CHECK: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
-// CHECK: ![[COPYRIGHT]] = !{!"@(#) Copyright"}
\ No newline at end of file
+// CHECK: ![[COPYRIGHT]] = !{!"@(#) Hello,  world\0A\09\22quoted\22"}
diff --git a/clang/test/Preprocessor/pragma-comment.c b/clang/test/Preprocessor/pragma-comment.c
index 210c48a513a45..1df54309e1908 100644
--- a/clang/test/Preprocessor/pragma-comment.c
+++ b/clang/test/Preprocessor/pragma-comment.c
@@ -17,6 +17,12 @@
 // 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/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/prefixed-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/prefixed-literal.c
+
 //--- unsupported.c
 // pragma comment kinds not supported on this target.
 #pragma comment(copyright, "copyright")            // expected-warning {{'#pragma comment copyright' ignored}}
@@ -43,4 +49,12 @@
 //--- 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}}
\ No newline at end of file
+#pragma comment(copyright, "Duplicate Copyright") // expected-warning {{'#pragma comment copyright' ignored: it can be specified only once per translation unit}}
+
+//--- concat-escape.c
+// Concatenated ordinary string literals and escapes are accepted
+#pragma comment(copyright, "@(#) Hello, " "world\n\t\"@(#) quoted\"") // expected-no-diagnostics
+
+//--- prefixed-literal.c
+// UTF-8-prefixed literals are rejected.
+#pragma comment(copyright, u8"@(#) Hello unicode") // expected-error {{expected string literal in pragma comment}}

>From 1598927ff4f3b3afd3d972941a9a3022e4c084c5 Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Tue, 14 Apr 2026 01:43:38 -0400
Subject: [PATCH 05/10] Add more prefixed string literal tests in
 Preprocessor/pragma-comment.c

---
 clang/test/Preprocessor/pragma-comment.c | 29 ++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)

diff --git a/clang/test/Preprocessor/pragma-comment.c b/clang/test/Preprocessor/pragma-comment.c
index 1df54309e1908..4ab5b64943432 100644
--- a/clang/test/Preprocessor/pragma-comment.c
+++ b/clang/test/Preprocessor/pragma-comment.c
@@ -20,8 +20,17 @@
 // 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/prefixed-literal.c
-// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/prefixed-literal.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.
@@ -55,6 +64,18 @@
 // Concatenated ordinary string literals and escapes are accepted
 #pragma comment(copyright, "@(#) Hello, " "world\n\t\"@(#) quoted\"") // expected-no-diagnostics
 
-//--- prefixed-literal.c
-// UTF-8-prefixed literals are rejected.
+//--- 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}}

>From 4fbe654368b718f27b859e3d10b7dd9bd738ab68 Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Tue, 14 Apr 2026 02:59:28 -0400
Subject: [PATCH 06/10] Minor update on LowerCommentStringPass.cpp

---
 llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
index d762125755f7f..7893ea6a00235 100644
--- a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -18,7 +18,7 @@
 //      (`__loadtime_comment_str`) containing the copyright text in
 //      `__loadtime_comment` section.
 //
-//   2. Marks the string in `llvm.used` so it cannot be dropped by
+//   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
@@ -32,7 +32,8 @@
 //  Output IR:
 //     @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00",
 //                          section "__loadtime_comment"
-//     @llvm.used = appending global [1 x ptr] [ptr @__loadtime_comment_str]
+//     @llvm.compiler.used = appending global [1 x ptr] [ptr
+//     @__loadtime_comment_str]
 //
 //     define i32 @func() !implicit.ref !5 { ... }
 //     !5 = !{ptr @__loadtime_comment_str}
@@ -116,8 +117,8 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M,
   // The GV is constant, so we expect a read-only section.
   StrGV->setSection("__loadtime_comment");
 
-  // 2. Add the string to llvm.used to prevent LLVM optimization/LTO passes from
-  // removing it.
+  // 2. Add the string to llvm.compiler.used to prevent LLVM optimization/LTO
+  // passes from removing it.
   appendToCompilerUsed(M, {StrGV});
 
   // 3. Attach !implicit ref to every defined function

>From 05496842735621baa87507dddc1e378538ad511d Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Wed, 15 Apr 2026 03:29:34 -0400
Subject: [PATCH 07/10] Add documentation and comments for C++ 20 modules
 interaction

---
 clang/docs/LanguageExtensions.rst   |  6 ++++++
 clang/lib/CodeGen/CodeGenModule.cpp | 12 ++++++++++--
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index fdb8147007c10..e827ca70599a1 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6841,6 +6841,12 @@ Both symbols reside in the ``.text`` segment and are marked read-only, so the
 string is linked into any binary that includes the object file and remains
 visible at run time.
 
+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/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e415d9850129e..4503cb491f038 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3584,7 +3584,8 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
 ///
 /// Example: #pragma comment(copyright, "Copyright string")
 ///
-/// This should only be called once per translation unit.
+/// 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::ProcessPragmaComment(PragmaMSCommentKind Kind,
                                          StringRef Comment,
                                          bool isFromASTFile) {
@@ -3596,7 +3597,14 @@ void CodeGenModule::ProcessPragmaComment(PragmaMSCommentKind Kind,
   assert(getTriple().isOSAIX() &&
          "pragma comment copyright is supported only when targeting AIX");
 
-  // Deserialization Guard: Only process if copyright originated in this TU.
+  // 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;
 

>From b8094c5ee256d63539fc2117d1cdbff5325e0c49 Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Mon, 20 Apr 2026 13:41:09 -0400
Subject: [PATCH 08/10] [PowerPC][AIX] Move LowerCommentStringPass to end of
 pipeline

---
 clang/test/CodeGen/lto-newpm-pipeline.c       |  4 ++--
 llvm/lib/Passes/PassBuilderPipelines.cpp      | 24 ++++++++++++++-----
 .../CodeGen/AArch64/print-pipeline-passes.ll  |  2 +-
 llvm/test/Other/new-pm-defaults.ll            |  2 +-
 .../Other/new-pm-thinlto-postlink-defaults.ll |  2 +-
 .../new-pm-thinlto-postlink-pgo-defaults.ll   |  2 +-
 ...-pm-thinlto-postlink-samplepgo-defaults.ll |  2 +-
 7 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/clang/test/CodeGen/lto-newpm-pipeline.c b/clang/test/CodeGen/lto-newpm-pipeline.c
index b9466b9558b06..d7588318ee9d7 100644
--- a/clang/test/CodeGen/lto-newpm-pipeline.c
+++ b/clang/test/CodeGen/lto-newpm-pipeline.c
@@ -27,7 +27,6 @@
 
 // CHECK-FULL-O0: Running pass: VerifierPass
 // CHECK-FULL-O0-NEXT: Running analysis: VerifierAnalysis
-// CHECK-FULL-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-FULL-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
 // CHECK-FULL-O0-NEXT: Running pass: EntryExitInstrumenterPass
 // CHECK-FULL-O0-NEXT: Running pass: AlwaysInlinerPass
@@ -37,12 +36,12 @@
 // CHECK-FULL-O0-NEXT: Running pass: NameAnonGlobalPass
 // CHECK-FULL-O0-NEXT: Running pass: AnnotationRemarksPass
 // CHECK-FULL-O0-NEXT: Running analysis: TargetLibraryAnalysis
+// CHECK-FULL-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-FULL-O0-NEXT: Running pass: VerifierPass
 // CHECK-FULL-O0-NEXT: Running pass: BitcodeWriterPass
 
 // CHECK-THIN-O0: Running pass: VerifierPass
 // CHECK-THIN-O0-NEXT: Running analysis: VerifierAnalysis
-// CHECK-THIN-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-THIN-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
 // CHECK-THIN-O0-NEXT: Running pass: EntryExitInstrumenterPass
 // CHECK-THIN-O0-NEXT: Running pass: AlwaysInlinerPass
@@ -52,6 +51,7 @@
 // CHECK-THIN-O0-NEXT: Running pass: NameAnonGlobalPass
 // CHECK-THIN-O0-NEXT: Running pass: AnnotationRemarksPass
 // CHECK-THIN-O0-NEXT: Running analysis: TargetLibraryAnalysis
+// CHECK-THIN-O0-NEXT: Running pass: LowerCommentStringPass
 // CHECK-THIN-O0-NEXT: Running pass: VerifierPass
 // CHECK-THIN-O0-NEXT: Running pass: ThinLTOBitcodeWriterPass
 
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 227e45aae3141..95c2463838c61 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -1472,9 +1472,6 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   const bool LTOPreLink = isLTOPreLink(LTOPhase);
   ModulePassManager MPM;
 
-  // Process copyright metadata early, before any optimizations
-  MPM.addPass(LowerCommentStringPass());
-
   // Run partial inlining pass to partially inline functions that have
   // large bodies.
   if (RunPartialInlining)
@@ -1708,6 +1705,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;
 }
 
@@ -1880,6 +1884,12 @@ PassBuilder::buildThinLTOPreLinkDefaultPipeline(OptimizationLevel Level) {
 
   instructionCountersPass(MPM, /*IsPreOptimization=*/false);
 
+  // Lower !comment_string.loadtime metadata. Must run at the end of prelink
+  // so all compiler-generated functions are present before !implicit.ref
+  // is attached. !implicit.ref metadata will travel with any function
+  // imported by ThinLTO postlink.
+  MPM.addPass(LowerCommentStringPass());
+
   return MPM;
 }
 
@@ -2344,9 +2354,6 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level,
 
   ModulePassManager MPM;
 
-  // Process copyright metadata at O0 before any other transformations
-  MPM.addPass(LowerCommentStringPass());
-
   instructionCountersPass(MPM, /*IsPreOptimization=*/true);
 
   // Perform pseudo probe instrumentation in O0 mode. This is for the
@@ -2464,6 +2471,11 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level,
 
   instructionCountersPass(MPM, /*IsPreOptimization=*/false);
 
+  // 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());
+
   return MPM;
 }
 
diff --git a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll
index f2b2738b89416..2cb166c4c9a20 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: {{^}}lower-comment-string,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,function(annotation-remarks),lower-comment-string,verify,print{{$}}
 
 define void @foo() {
 entry:
diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index abd26a2883c3a..55f83c0431ef0 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -228,7 +228,6 @@
 ; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
-; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-DEFAULT-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-LTO-NOT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass
@@ -287,6 +286,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 420f4a7662c89..e906c12b04cef 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -155,7 +155,6 @@
 ; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalDCEPass
-; CHECK-POSTLINK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass
 ; CHECK-POSTLINK-O-NEXT: Running pass: RecomputeGlobalsAAPass
@@ -205,6 +204,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 d881b29126f48..70a3dfdb27a16 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -139,7 +139,6 @@
 ; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
-; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass
 ; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass
@@ -187,6 +186,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 6f9f9fbd63c93..72891f7220fb1 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -146,7 +146,6 @@
 ; CHECK-O-NEXT: Running pass: CoroCleanupPass
 ; CHECK-O-NEXT: Running pass: GlobalOptPass
 ; CHECK-O-NEXT: Running pass: GlobalDCEPass
-; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass
 ; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass
@@ -194,6 +193,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
 

>From 2a134d8e925a441489f3b7001fd198f7a9458369 Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Tue, 21 Apr 2026 03:19:20 -0400
Subject: [PATCH 09/10] Fix test failures related to adding
 LowerCommentStringPass to the end of pipelines

---
 llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll           | 2 +-
 llvm/test/Other/new-pm-O0-defaults.ll                        | 1 +
 llvm/test/Other/new-pm-thinlto-prelink-defaults.ll           | 1 +
 llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll       | 1 +
 llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll | 1 +
 5 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll b/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll
index 50476ae020d68..433f9d8962e39 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: {{^}}lower-comment-string,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,function(annotation-remarks),lower-comment-string,verify,print{{$}}
 
 define void @test_hexagon_passes() {
 entry:
diff --git a/llvm/test/Other/new-pm-O0-defaults.ll b/llvm/test/Other/new-pm-O0-defaults.ll
index bac4150caab58..e07594496841e 100644
--- a/llvm/test/Other/new-pm-O0-defaults.ll
+++ b/llvm/test/Other/new-pm-O0-defaults.ll
@@ -61,6 +61,7 @@
 ; CHECK-CORO-NEXT: Running analysis: TargetLibraryAnalysis
 ; CHECK-LTO-NEXT: Running pass: AnnotationRemarksPass
 ; CHECK-LTO-NEXT: Running analysis: TargetLibraryAnalysis
+; CHECK-CORO-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-NEXT: Running pass: PrintModulePass
 
 ; Make sure we get the IR back out without changes when we print the module.
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
index 20d75913750a0..d830decf6e51b 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
@@ -183,6 +183,7 @@
 ; CHECK-O-NEXT:          Running pass: AnnotationRemarksPass on foo
 ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
 ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: PrintModulePass
 
 ; Make sure we get the IR back out without changes when we print the module.
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 5e0ad37da0dee..415b691939849 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
@@ -186,6 +186,7 @@
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
 ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
 ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: PrintModulePass
 
 ; Make sure we get the IR back out without changes when we print the module.
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 426144c12ec46..d36d87385049f 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
@@ -149,6 +149,7 @@
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
 ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
 ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
 ; CHECK-O-NEXT: Running pass: PrintModulePass
 
 ; Make sure we get the IR back out without changes when we print the module.

>From 7d468a5266dd1d56fa9d4d2c818fae7f94c221ef Mon Sep 17 00:00:00 2001
From: Tony Varghese <tony.varghese at ibm.com>
Date: Tue, 28 Apr 2026 02:11:46 -0400
Subject: [PATCH 10/10] Addressing review comments. Copyright string stored to
 .text section and is loaded and the program runtime. Added test points for
 _Pragma operator along with other minor modifications.

---
 clang/docs/LanguageExtensions.rst             | 28 +++++-------------
 clang/lib/CodeGen/CodeGenModule.cpp           | 29 ++++++++-----------
 clang/lib/CodeGen/CodeGenModule.h             |  9 +++---
 clang/lib/Parse/ParsePragma.cpp               |  6 ++--
 .../pragma-comment-copyright-modules.cpp      |  2 +-
 clang/test/CodeGen/PowerPC/pragma-comment.c   | 21 +++++++++++---
 clang/test/Preprocessor/pragma-comment.c      | 13 ++++++++-
 .../Utils/LowerCommentStringPass.cpp          | 19 ++++++------
 8 files changed, 67 insertions(+), 60 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index e827ca70599a1..54361b49c4133 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6814,34 +6814,20 @@ Embedding Copyright Information on AIX
 ======================================
 Clang supports the ``#pragma comment(copyright, "string")`` directive on AIX
 targets. This directive embeds a copyright or identifying string into the
-compiled object file so that the string survives into shared libraries and
-executables. The directive is silently ignored on non-AIX targets.
+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 may appear at file scope; it is not
-meaningful inside a function body.
+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.
 
-When the directive is processed, Clang emits two read-only XCOFF csect symbols
-into the ``.text`` section of the object file:
-
-``__loadtime_comment``
-  A csect (storage-class ``unamex``, type ``RO``) that contains the comment
-  string data and serves as the owning section for the symbol.
-
-``__loadtime_comment_str``
-  A label (storage-class ``unamex``, type ``LD``) at the same address as
-  ``__loadtime_comment``, pointing directly to the embedded string. This is
-  the symbol that tools such as ``strings`` and ``what`` typically resolve.
-
-Both symbols reside in the ``.text`` segment and are marked read-only, so the
-string is linked into any binary that includes the object file and remains
-visible at run time.
-
-Interaction with C++20 Modules:
+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
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 4503cb491f038..bb9077adb1cec 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3578,22 +3578,17 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
 }
 
 /// Process copyright pragma and create LLVM metadata.
-/// #pragma comment(copyright, "string") embeds copyright information into a
-/// loadable program-data section of the object file for inclusion in the linked
-/// module.
+/// #pragma comment(copyright, "string") embeds copyright information into the
+/// .text section of the object file as read-only data. The string is
+/// loaded into memory when the program runs and survives into the final
+/// executable or shared library.
 ///
 /// Example: #pragma comment(copyright, "Copyright string")
 ///
 /// 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::ProcessPragmaComment(PragmaMSCommentKind Kind,
-                                         StringRef Comment,
-                                         bool isFromASTFile) {
-  // Ensure we are only processing Copyright Pragmas
-  assert(Kind == PCK_Copyright &&
-         "Unexpected pragma comment kind, ProcessPragmaComment should only be "
-         "called for PCK_Copyright");
-  // Target Guard: Only AIX supports PCK_Copyright currently.
+void CodeGenModule::ProcessPragmaCommentCopyright(StringRef Comment,
+                                                  bool isFromASTFile) {
   assert(getTriple().isOSAIX() &&
          "pragma comment copyright is supported only when targeting AIX");
 
@@ -3608,11 +3603,10 @@ void CodeGenModule::ProcessPragmaComment(PragmaMSCommentKind Kind,
   if (isFromASTFile)
     return;
 
-  // Only one copyright pragma allowed per translation unit
   assert(!LoadTimeComment &&
-         "Only one copyright pragma allowed per translation unit");
+         "Only one copyright pragma allowed per translation unit.");
 
-  // Create llvm metadata with the comment string
+  // Create LLVM metadata with the comment string.
   auto &C = getLLVMContext();
   llvm::Metadata *Ops[] = {llvm::MDString::get(C, Comment)};
   LoadTimeComment = llvm::MDNode::get(C, Ops);
@@ -4141,7 +4135,9 @@ bool CodeGenModule::MustBeEmitted(const ValueDecl *Global) {
 void CodeGenModule::EmitLoadTimeComment() {
   // Emit copyright metadata for #pragma comment(copyright, ...).
   // The LoadTimeComment is set during pragma processing and contains the
-  // copyright string that should be embedded in the executable.
+  // copyright string that will be embedded in the .text section of the
+  // XCOFF object file and loaded into memory when the program runs.
+
   if (LoadTimeComment) {
     auto *NMD = getModule().getOrInsertNamedMetadata("comment_string.loadtime");
     NMD->addOperand(LoadTimeComment);
@@ -7904,8 +7900,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
         AddDependentLib(PCD->getArg());
       break;
     case PCK_Copyright:
-      ProcessPragmaComment(PCD->getCommentKind(), PCD->getArg(),
-                           PCD->isFromASTFile());
+      ProcessPragmaCommentCopyright(PCD->getArg(), PCD->isFromASTFile());
       break;
     case PCK_Compiler:
     case PCK_ExeStr:
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index d08d5f108267d..8bded97cf95f9 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1502,9 +1502,8 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Appends a dependent lib to the appropriate metadata value.
   void AddDependentLib(StringRef Lib);
 
-  /// Process pragma comment
-  void ProcessPragmaComment(PragmaMSCommentKind Kind, StringRef Comment,
-                            bool isFromASTFile);
+  /// Process Pragma Comment Copyright.
+  void ProcessPragmaCommentCopyright(StringRef Comment, bool isFromASTFile);
 
   llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD);
 
@@ -2087,7 +2086,9 @@ class CodeGenModule : public CodeGenTypeCache {
 
   /// Emit load-time comment metadata for #pragma comment(copyright, ...).
   /// This adds the copyright string to the module's named metadata, which will
-  /// be processed by the backend to include it in the generated executable.
+  /// be processed by the IR passes to embed it in the .text section
+  /// of the generated object file, where it will be loaded into memory at
+  /// program runtime.
   void EmitLoadTimeComment();
 
   /// Determine whether the definition can be emitted eagerly, or should be
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index d60a279da090f..08379e9c20086 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -3311,7 +3311,7 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
-  // Handle AIX Target restrictions (all non-copyright kinds)
+  // Handle AIX Target restrictions (i.e., warn on all non-copyright kinds).
   if (PP.getTargetInfo().getTriple().isOSAIX() && Kind != PCK_Copyright) {
     // pragma comment kinds linker, lib, compiler, exestr and user are
     // ignored when targeting AIX.
@@ -3326,7 +3326,7 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
-  // Handle pragma comment copyright
+  // Handle pragma comment copyright kind.
   if (Kind == PCK_Copyright) {
     if (SeenCopyrightInTU) {
       // pragma comment copyright can each appear only once in a TU.
@@ -3363,7 +3363,7 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
     return;
   }
 
-  // Skip further processing for well-formed copyright with an empty string
+  // Skip further processing for well-formed copyright with an empty string.
   if (Kind == PCK_Copyright && ArgumentString.empty())
     return;
 
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
index ca59893f6afe7..a1f8893013ff7 100644
--- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
@@ -23,7 +23,7 @@
 //--- copymod.cppm
 export module copymod;
 #pragma comment(copyright, "module me")
-export inline void f() {}
+export inline void f [[gnu::always_inline]]() {}
 
 //--- importmod.cc
 import copymod;
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment.c b/clang/test/CodeGen/PowerPC/pragma-comment.c
index be99852bccccb..32afb8bdfa33a 100644
--- a/clang/test/CodeGen/PowerPC/pragma-comment.c
+++ b/clang/test/CodeGen/PowerPC/pragma-comment.c
@@ -1,9 +1,22 @@
-// RUN: %clang_cc1 %s -triple powerpc-ibm-aix   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+// 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: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
-// CHECK: ![[COPYRIGHT]] = !{!"@(#) Hello,  world\0A\09\22quoted\22"}
+// 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/Preprocessor/pragma-comment.c b/clang/test/Preprocessor/pragma-comment.c
index 4ab5b64943432..4f336fb6a6186 100644
--- a/clang/test/Preprocessor/pragma-comment.c
+++ b/clang/test/Preprocessor/pragma-comment.c
@@ -17,6 +17,9 @@
 // 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
 
@@ -38,6 +41,8 @@
 #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.
@@ -48,18 +53,24 @@
 #pragma comment(copyright, "") // expected-no-diagnostics
 
 //--- other-kinds.c
-// Non-copyright comment kinds produce warnings.
+// 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
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
index 7893ea6a00235..cc17e77370459 100644
--- a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -15,8 +15,9 @@
 // For each module (translation unit), the pass performs the following:
 //
 //   1. Creates a null-terminated, internal constant string global
-//      (`__loadtime_comment_str`) containing the copyright text in
-//      `__loadtime_comment` section.
+//      (`__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.
@@ -102,15 +103,16 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M,
   if (Text.empty())
     return PreservedAnalyses::all();
 
-  // 1. Create a single NULL-terminated string global
+  // 1. Create a single NULL-terminated string global.
   Constant *StrInit = ConstantDataArray::getString(Ctx, Text, /*AddNull=*/true);
 
-  // Internal, constant, TU-local--avoids duplicate symbol issues across TUs.
+  // Global variable should be Internal, constant, 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
+  // 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.
@@ -121,23 +123,22 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M,
   // passes from removing it.
   appendToCompilerUsed(M, {StrGV});
 
-  // 3. Attach !implicit ref to every defined function
+  // 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);
 
-  // Lambda to attach implicit.ref metadata to a function.
   auto AddImplicitRef = [&](Function &F) {
     if (F.isDeclaration())
       return;
-    // Attach the implicit.ref metadata to the function
+    // 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
+  // Process all functions in the module and add !implicit.ref to the function.
   for (Function &F : M)
     AddImplicitRef(F);
 



More information about the cfe-commits mailing list