[clang] [llvm] [PowerPC][AIX] Support #pragma comment copyright for AIX (PR #178184)
Tony Varghese via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 5 06:59:31 PDT 2026
https://github.com/tonykuttai updated https://github.com/llvm/llvm-project/pull/178184
>From 30291515af7c4e03ef3c899b5f3f88c57711bfc7 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] [PowerPC][AIX] Support #pragma comment copyright for AIX
targets.
The #pragma comment(copyright, "string") for AIX/XCOFF targets embeds
the copyright string into the object file and is loadable during the
runtime.
The implementation flows through three stages:
- Clang emits !comment_string.loadtime named metadata when it encounters
the pragma in ProcessPragmaCommentCopyright().
- LowerCommentStringPass reads this metadata and materializes a
TU-local string global (__loadtime_comment_str), adds it to
llvm.compiler.used to prevent elimination, and attaches !implicit.ref
metadata to reference it from functions in the module.
- The AIX backend reads !implicit.ref and emits .ref directives to
the __loattime_comment_str.
Co-authored-by: Hubert Tong <hubert.reinterpretcast at gmail.com>
---
clang/docs/LanguageExtensions.rst | 23 +++
.../clang/Basic/DiagnosticParseKinds.td | 4 +
clang/include/clang/Basic/PragmaKinds.h | 3 +-
clang/lib/AST/TextNodeDumper.cpp | 3 +
clang/lib/CodeGen/CodeGenModule.cpp | 41 +++++
clang/lib/CodeGen/CodeGenModule.h | 11 +-
clang/lib/Parse/ParsePragma.cpp | 37 ++++-
clang/test/CXX/module/cpp.pre/module_decl.cpp | 2 +-
.../pragma-comment-copyright-modules.cpp | 30 ++++
clang/test/CodeGen/PowerPC/pragma-comment.c | 22 +++
clang/test/CodeGen/lto-newpm-pipeline.c | 2 +
clang/test/Preprocessor/pragma-comment.c | 92 +++++++++++
.../Transforms/Utils/LowerCommentStringPass.h | 24 +++
llvm/lib/Passes/PassBuilder.cpp | 1 +
llvm/lib/Passes/PassBuilderPipelines.cpp | 19 +++
llvm/lib/Passes/PassRegistry.def | 1 +
llvm/lib/Transforms/Utils/CMakeLists.txt | 1 +
.../Utils/LowerCommentStringPass.cpp | 150 ++++++++++++++++++
.../CodeGen/AArch64/print-pipeline-passes.ll | 2 +-
.../CodeGen/Hexagon/print-pipeline-passes.ll | 2 +-
.../PowerPC/pragma-comment-copyright-lto.ll | 67 ++++++++
llvm/test/Other/new-pm-O0-defaults.ll | 1 +
llvm/test/Other/new-pm-defaults.ll | 1 +
.../Other/new-pm-thinlto-postlink-defaults.ll | 1 +
.../new-pm-thinlto-postlink-pgo-defaults.ll | 1 +
...-pm-thinlto-postlink-samplepgo-defaults.ll | 1 +
.../Other/new-pm-thinlto-prelink-defaults.ll | 1 +
.../new-pm-thinlto-prelink-pgo-defaults.ll | 1 +
...w-pm-thinlto-prelink-samplepgo-defaults.ll | 1 +
.../lower-comment-string.ll | 41 +++++
30 files changed, 577 insertions(+), 9 deletions(-)
create mode 100644 clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
create mode 100644 clang/test/CodeGen/PowerPC/pragma-comment.c
create mode 100644 clang/test/Preprocessor/pragma-comment.c
create mode 100644 llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
create mode 100644 llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
create mode 100644 llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
create mode 100644 llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index fbb9947f39d3e..992c6f2927be8 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6821,6 +6821,29 @@ The ``#pragma comment(lib, ...)`` directive is supported on all ELF targets.
The second parameter is the library name (without the traditional Unix prefix of
``lib``). This allows you to provide an implicit link of dependent libraries.
+Embedding Copyright Information on AIX
+======================================
+Clang supports the ``#pragma comment(copyright, "string")`` directive for AIX
+targets. This directive embeds a copyright or identifying string into the
+compiled object file. The string is included in the final executable or shared
+library and loaded into memory at program runtime. The directive is ignored on
+non-AIX targets.
+
+.. code-block:: c
+
+ #pragma comment(copyright, "string-literal")
+
+The second argument is an ordinary string literal. Concatenated ordinary string
+literals are also accepted. The directive is intended to appear at file scope;
+Clang treats it as being at file scope when it appears within other scopes.
+
+Interaction with C++20 Modules
+-------------------------------
+
+When ``#pragma comment(copyright, ...)`` appears in a C++20 module interface
+unit, the copyright string is embedded only in the object file compiled from
+that interface unit. Importing TUs do not re-emit the string.
+
Evaluating Object Size
======================
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 7bcd1870a2600..91632f8d2376c 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1360,6 +1360,10 @@ def err_pragma_comment_unknown_kind : Error<"unknown kind of pragma comment">;
// PS4 recognizes only #pragma comment(lib)
def warn_pragma_comment_ignored : Warning<"'#pragma comment %0' ignored">,
InGroup<IgnoredPragmas>;
+def warn_pragma_comment_once
+ : Warning<"'#pragma comment %0' "
+ "ignored: it can be specified only once per translation unit">,
+ InGroup<IgnoredPragmas>;
// - #pragma detect_mismatch
def err_pragma_detect_mismatch_malformed : Error<
"pragma detect_mismatch is malformed; it requires two comma-separated "
diff --git a/clang/include/clang/Basic/PragmaKinds.h b/clang/include/clang/Basic/PragmaKinds.h
index 42f049f7323d2..52ca58855d460 100644
--- a/clang/include/clang/Basic/PragmaKinds.h
+++ b/clang/include/clang/Basic/PragmaKinds.h
@@ -17,7 +17,8 @@ enum PragmaMSCommentKind {
PCK_Lib, // #pragma comment(lib, ...)
PCK_Compiler, // #pragma comment(compiler, ...)
PCK_ExeStr, // #pragma comment(exestr, ...)
- PCK_User // #pragma comment(user, ...)
+ PCK_User, // #pragma comment(user, ...)
+ PCK_Copyright // #pragma comment(copyright, ...)
};
enum PragmaMSStructKind {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index abb6869a2b46e..d90090b82fb7a 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2608,6 +2608,9 @@ void TextNodeDumper::VisitPragmaCommentDecl(const PragmaCommentDecl *D) {
case PCK_User:
OS << "user";
break;
+ case PCK_Copyright:
+ OS << "copyright";
+ break;
}
StringRef Arg = D->getArg();
if (!Arg.empty())
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 50089f4a5016a..9dedbed54c77a 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1744,6 +1744,8 @@ void CodeGenModule::Release() {
EmitBackendOptionsMetadata(getCodeGenOpts());
+ EmitLoadTimeComment();
+
// If there is device offloading code embed it in the host now.
EmbedObject(&getModule(), CodeGenOpts, *getFileSystem(), getDiags());
@@ -3687,6 +3689,35 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
LinkerOptionsMetadata.push_back(llvm::MDNode::get(C, MDOpts));
}
+/// Process copyright pragma and create LLVM metadata.
+///
+/// Only one copyright pragma is allowed per translation unit. Subsequent
+/// pragmas in the same TU are ignored with a warning at the parse level.
+void CodeGenModule::ProcessPragmaCommentCopyright(StringRef Comment,
+ bool isFromASTFile) {
+ assert(getTriple().isOSAIX() &&
+ "pragma comment copyright is supported only when targeting AIX");
+
+ // Interaction with C++20 Modules and PCH:
+ // When a module interface unit containing a copyright pragma is imported,
+ // Clang deserializes the PragmaCommentDecl from the precompiled module file
+ // (.pcm) into the importing TU's AST. isFromASTFile() returns true for such
+ // deserialized declarations. We skip those to ensure only the module
+ // interface TU that originally parsed the pragma emits the copyright metadata
+ // -- not every TU that imports it. This prevents duplicate copyright strings
+ // in the final binary.
+ if (isFromASTFile)
+ return;
+
+ assert(!LoadTimeComment &&
+ "Only one copyright pragma allowed per translation unit.");
+
+ // Create LLVM metadata with the comment string.
+ auto &C = getLLVMContext();
+ llvm::Metadata *Ops[] = {llvm::MDString::get(C, Comment)};
+ LoadTimeComment = llvm::MDNode::get(C, Ops);
+}
+
/// Add link options implied by the given module, including modules
/// it depends on, using a postorder walk.
static void addLinkOptionsPostorder(CodeGenModule &CGM, Module *Mod,
@@ -4207,6 +4238,13 @@ bool CodeGenModule::MustBeEmitted(const ValueDecl *Global) {
return getContext().DeclMustBeEmitted(Global);
}
+void CodeGenModule::EmitLoadTimeComment() {
+ if (LoadTimeComment) {
+ auto *NMD = getModule().getOrInsertNamedMetadata("comment_string.loadtime");
+ NMD->addOperand(LoadTimeComment);
+ }
+}
+
bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
// In OpenMP 5.0 variables and function may be marked as
// device_type(host/nohost) and we should not emit them eagerly unless we sure
@@ -7966,6 +8004,9 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
case PCK_Lib:
AddDependentLib(PCD->getArg());
break;
+ case PCK_Copyright:
+ ProcessPragmaCommentCopyright(PCD->getArg(), PCD->isFromASTFile());
+ break;
case PCK_Compiler:
case PCK_ExeStr:
case PCK_User:
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 959e02924a9f7..2106ad26d54f0 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -619,6 +619,9 @@ class CodeGenModule : public CodeGenTypeCache {
/// A vector of metadata strings for dependent libraries for ELF.
SmallVector<llvm::MDNode *, 16> ELFDependentLibraries;
+ /// Metadata for copyright pragma comment (if present).
+ llvm::MDNode *LoadTimeComment = nullptr;
+
/// @name Cache for Objective-C runtime types
/// @{
@@ -1544,7 +1547,6 @@ class CodeGenModule : public CodeGenTypeCache {
/// Appends a dependent lib to the appropriate metadata value.
void AddDependentLib(StringRef Lib);
-
llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD);
void setFunctionLinkage(GlobalDecl GD, llvm::Function *F) {
@@ -1953,6 +1955,9 @@ class CodeGenModule : public CodeGenTypeCache {
ABIArgInfo convertABIArgInfo(const llvm::abi::ArgInfo &AbiInfo,
QualType Type);
+ /// Process #pragma comment(copyright, ...).
+ void ProcessPragmaCommentCopyright(StringRef Comment, bool isFromASTFile);
+
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;
llvm::Constant *GetOrCreateLLVMFunction(
@@ -2166,6 +2171,10 @@ class CodeGenModule : public CodeGenTypeCache {
/// Emit deactivation symbols for any PFP fields whose offset is taken with
/// offsetof.
void emitPFPFieldsWithEvaluatedOffset();
+
+ /// Emit the load-time comment metadata (e.g., from
+ /// #pragma comment(copyright, ...)) for the translation unit.
+ void EmitLoadTimeComment();
};
} // end namespace CodeGen
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 237874de9f8a3..f58d2c70b7d98 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -237,6 +237,7 @@ struct PragmaCommentHandler : public PragmaHandler {
private:
Sema &Actions;
+ bool SeenCopyrightInTU = false; // TU-scoped
};
struct PragmaDetectMismatchHandler : public PragmaHandler {
@@ -480,7 +481,8 @@ void Parser::initializePragmaHandlers() {
PP.AddPragmaHandler(OpenACCHandler.get());
if (getLangOpts().MicrosoftExt ||
- getTargetInfo().getTriple().isOSBinFormatELF()) {
+ getTargetInfo().getTriple().isOSBinFormatELF() ||
+ getTargetInfo().getTriple().isOSAIX()) {
MSCommentHandler = std::make_unique<PragmaCommentHandler>(Actions);
PP.AddPragmaHandler(MSCommentHandler.get());
}
@@ -607,7 +609,8 @@ void Parser::resetPragmaHandlers() {
OpenACCHandler.reset();
if (getLangOpts().MicrosoftExt ||
- getTargetInfo().getTriple().isOSBinFormatELF()) {
+ getTargetInfo().getTriple().isOSBinFormatELF() ||
+ getTargetInfo().getTriple().isOSAIX()) {
PP.RemovePragmaHandler(MSCommentHandler.get());
MSCommentHandler.reset();
}
@@ -3269,7 +3272,9 @@ void PragmaDetectMismatchHandler::HandlePragma(Preprocessor &PP,
/// \code
/// #pragma comment(linker, "foo")
/// \endcode
-/// 'linker' is one of five identifiers: compiler, exestr, lib, linker, user.
+/// 'linker' is one of six identifiers: compiler, copyright, exestr, lib,
+/// linker, user.
+///
/// "foo" is a string, which is fully macro expanded, and permits string
/// concatenation, embedded escape characters etc. See MSDN for more details.
void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
@@ -3289,7 +3294,7 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
return;
}
- // Verify that this is one of the 5 explicitly listed options.
+ // Verify that this is one of the 6 explicitly listed options.
IdentifierInfo *II = Tok.getIdentifierInfo();
PragmaMSCommentKind Kind =
llvm::StringSwitch<PragmaMSCommentKind>(II->getName())
@@ -3298,18 +3303,38 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
.Case("compiler", PCK_Compiler)
.Case("exestr", PCK_ExeStr)
.Case("user", PCK_User)
+ .Case("copyright", PCK_Copyright)
.Default(PCK_Unknown);
if (Kind == PCK_Unknown) {
PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind);
return;
}
+ if (PP.getTargetInfo().getTriple().isOSAIX() && Kind != PCK_Copyright) {
+ // Currently, pragma comment kinds aside from "copyright" are not fully
+ // implemented by Clang for AIX targets.
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_ignored)
+ << II->getName();
+ return;
+ }
+
if (PP.getTargetInfo().getTriple().isOSBinFormatELF() && Kind != PCK_Lib) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_ignored)
<< II->getName();
return;
}
+ // Handle pragma comment copyright kind.
+ if (Kind == PCK_Copyright) {
+ if (SeenCopyrightInTU) {
+ // pragma comment copyright can each appear only once in a TU.
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_once)
+ << II->getName();
+ return;
+ }
+ SeenCopyrightInTU = true;
+ }
+
// Read the optional string if present.
PP.Lex(Tok);
std::string ArgumentString;
@@ -3336,6 +3361,10 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
return;
}
+ // Skip further processing for well-formed copyright with an empty string.
+ if (Kind == PCK_Copyright && ArgumentString.empty())
+ return;
+
// If the pragma is lexically sound, notify any interested PPCallbacks.
if (PP.getPPCallbacks())
PP.getPPCallbacks()->PragmaComment(CommentLoc, II, ArgumentString);
diff --git a/clang/test/CXX/module/cpp.pre/module_decl.cpp b/clang/test/CXX/module/cpp.pre/module_decl.cpp
index 5c29aeff1b632..1d964fab6acb9 100644
--- a/clang/test/CXX/module/cpp.pre/module_decl.cpp
+++ b/clang/test/CXX/module/cpp.pre/module_decl.cpp
@@ -5,7 +5,7 @@
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/gnu_line_marker.cpp -verify -o %t/gnu_line_marker.pcm
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/include.cpp -verify -o %t/include.pcm
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/ident.cpp -verify -o %t/ident.pcm
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_comment.cpp -verify -o %t/pragma_comment.pcm
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-win32 -emit-module-interface %t/pragma_comment.cpp -verify -o %t/pragma_comment.pcm
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_mark.cpp -verify -o %t/pragma_mark.pcm
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_detect_mismatch.cpp -verify -o %t/pragma_detect_mismatch.pcm
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/pragma_clang_debug.cpp -verify -o %t/pragma_clang_debug.pcm
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
new file mode 100644
index 0000000000000..0eb6f932c4473
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-modules.cpp
@@ -0,0 +1,30 @@
+// RUN: split-file %s %t
+
+// Build the module interface to a PCM
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
+// RUN: -emit-module-interface %t/copymod.cppm -o %t/copymod.pcm
+
+// Verify that module interface emits copyright global when compiled to IR
+
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix -emit-llvm %t/copymod.cppm -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK-MOD
+// CHECK-MOD: @__loadtime_comment_str = internal unnamed_addr constant [10 x i8] c"module me\00", section "__loadtime_comment", align 1
+// CHECK-MOD: @llvm.compiler.used = appending global {{.*}} @__loadtime_comment_str
+
+// Compile an importing TU that uses the prebuilt module and verify that it
+// does NOT re-emit the module's copyright global.
+
+// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
+// RUN: -fprebuilt-module-path=%t -emit-llvm %t/importmod.cc -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK-IMPORT
+// CHECK-IMPORT-NOT: @__loadtime_comment_str
+// CHECK-IMPORT-NOT: c"module me\00"
+
+//--- copymod.cppm
+export module copymod;
+#pragma comment(copyright, "module me")
+export inline void f [[gnu::always_inline]]() {}
+
+//--- importmod.cc
+import copymod;
+void g() { f(); }
diff --git a/clang/test/CodeGen/PowerPC/pragma-comment.c b/clang/test/CodeGen/PowerPC/pragma-comment.c
new file mode 100644
index 0000000000000..32afb8bdfa33a
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/pragma-comment.c
@@ -0,0 +1,22 @@
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 %t/pragma-copyright.c -triple powerpc-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-COPYRIGHT
+// RUN: %clang_cc1 %t/pragma-copyright.c -triple powerpc64-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-COPYRIGHT
+
+// RUN: %clang_cc1 %t/operator-pragma-copyright.c -triple powerpc-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-OPERATOR
+// RUN: %clang_cc1 %t/operator-pragma-copyright.c -triple powerpc64-ibm-aix -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-OPERATOR
+
+//--- pragma-copyright.c
+#pragma comment(copyright, "@(#) Hello, " " world\n\t\"quoted\"")
+
+int main() { return 0; }
+
+// CHECK-COPYRIGHT: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
+// CHECK-COPYRIGHT: ![[COPYRIGHT]] = !{!"@(#) Hello, world\0A\09\22quoted\22"}
+
+//--- operator-pragma-copyright.c
+_Pragma("comment(copyright, \"IBM Copyright Pragma Operator\")")
+void foo() {}
+
+// CHECK-OPERATOR: !comment_string.loadtime = !{![[COPYRIGHT:[0-9]+]]}
+// CHECK-OPERATOR: ![[COPYRIGHT]] = !{!"IBM Copyright Pragma Operator"}
diff --git a/clang/test/CodeGen/lto-newpm-pipeline.c b/clang/test/CodeGen/lto-newpm-pipeline.c
index ea9784a76f923..664c56f0fe76f 100644
--- a/clang/test/CodeGen/lto-newpm-pipeline.c
+++ b/clang/test/CodeGen/lto-newpm-pipeline.c
@@ -32,6 +32,7 @@
// CHECK-FULL-O0-NEXT: Running pass: AlwaysInlinerPass
// CHECK-FULL-O0-NEXT: Running analysis: ProfileSummaryAnalysis
// CHECK-FULL-O0-NEXT: Running pass: CoroConditionalWrapper
+// CHECK-FULL-O0-NEXT: Running pass: LowerCommentStringPass
// CHECK-FULL-O0-NEXT: Running pass: CanonicalizeAliasesPass
// CHECK-FULL-O0-NEXT: Running pass: NameAnonGlobalPass
// CHECK-FULL-O0-NEXT: Running pass: AnnotationRemarksPass
@@ -46,6 +47,7 @@
// CHECK-THIN-O0-NEXT: Running pass: AlwaysInlinerPass
// CHECK-THIN-O0-NEXT: Running analysis: ProfileSummaryAnalysis
// CHECK-THIN-O0-NEXT: Running pass: CoroConditionalWrapper
+// CHECK-THIN-O0-NEXT: Running pass: LowerCommentStringPass
// CHECK-THIN-O0-NEXT: Running pass: CanonicalizeAliasesPass
// CHECK-THIN-O0-NEXT: Running pass: NameAnonGlobalPass
// CHECK-THIN-O0-NEXT: Running pass: AnnotationRemarksPass
diff --git a/clang/test/Preprocessor/pragma-comment.c b/clang/test/Preprocessor/pragma-comment.c
new file mode 100644
index 0000000000000..f365edf661588
--- /dev/null
+++ b/clang/test/Preprocessor/pragma-comment.c
@@ -0,0 +1,92 @@
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %t/unsupported.c
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %t/unsupported.c
+// RUN: %clang_cc1 -triple systemz -fsyntax-only -verify %t/unsupported.c
+// RUN: %clang_cc1 -triple powerpc64-linux-gnu -fsyntax-only -verify %t/unsupported.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/copyright.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/copyright.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/empty-copyright.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/empty-copyright.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/other-kinds.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/other-kinds.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/duplicate.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/duplicate.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/raw-string-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/raw-string-literal.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/concat-escape.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/concat-escape.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/u8-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/u8-literal.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/wide-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/wide-literal.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/utf16-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/utf16-literal.c
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fsyntax-only -verify %t/utf32-literal.c
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -fsyntax-only -verify %t/utf32-literal.c
+
+//--- unsupported.c
+// pragma comment kinds not supported on this target.
+#pragma comment(copyright, "copyright") // expected-warning {{'#pragma comment copyright' ignored}}
+#pragma comment(compiler) // expected-warning {{'#pragma comment compiler' ignored}}
+#pragma comment(exestr, "foo") // expected-warning {{'#pragma comment exestr' ignored}}
+#pragma comment(user, "foo\abar\nbaz\tsomething") // expected-warning {{'#pragma comment user' ignored}}
+#pragma comment(timestamp) // expected-error {{unknown kind of pragma comment}}
+#pragma comment(date) // expected-error {{unknown kind of pragma comment}}
+
+//--- copyright.c
+// Copyright pragma is accepted without diagnostics.
+#pragma comment(copyright, "copyright") // expected-no-diagnostics
+
+//--- empty-copyright.c
+// An empty copyright string is accepted without diagnostics.
+#pragma comment(copyright, "") // expected-no-diagnostics
+
+//--- other-kinds.c
+// Non-copyright comment kinds produce warnings/errors.
+#pragma comment(lib, "m") // expected-warning {{'#pragma comment lib' ignored}}
+#pragma comment(linker, "foo") // expected-warning {{'#pragma comment linker' ignored}}
+#pragma comment(compiler) // expected-warning {{'#pragma comment compiler' ignored}}
+#pragma comment(exestr, "foo") // expected-warning {{'#pragma comment exestr' ignored}}
+#pragma comment(user, "foo\abar\nbaz\tsomething") // expected-warning {{'#pragma comment user' ignored}}
+#pragma comment(timestamp) // expected-error {{unknown kind of pragma comment}}
+#pragma comment(date) // expected-error {{unknown kind of pragma comment}}
+
+//--- duplicate.c
+// A second copyright pragma in the same translation unit warns.
+#pragma comment(copyright, "@(#) Copyright")
+#pragma comment(copyright, "Duplicate Copyright") // expected-warning {{'#pragma comment copyright' ignored: it can be specified only once per translation unit}}
+
+//--- raw-string-literal.c
+// Raw string literals are accepted.
+#pragma comment(copyright, R"foo(printf("Hello (world)");)foo") // expected-no-diagnostics
+
+//--- concat-escape.c
+// Concatenated ordinary string literals and escapes are accepted.
+#pragma comment(copyright, "@(#) Hello, " "world\n\t\"@(#) quoted\"") // expected-no-diagnostics
+
+//--- u8-literal.c
+// UTF-8-prefixed string literals are rejected.
+#pragma comment(copyright, u8"@(#) Hello unicode") // expected-error {{expected string literal in pragma comment}}
+
+//--- wide-literal.c
+// Wide string literals are rejected.
+#pragma comment(copyright, L"@(#) Hello wide") // expected-error {{expected string literal in pragma comment}}
+
+//--- utf16-literal.c
+// UTF-16-prefixed string literals are rejected.
+#pragma comment(copyright, u"@(#) Hello utf16") // expected-error {{expected string literal in pragma comment}}
+
+//--- utf32-literal.c
+// UTF-32-prefixed string literals are rejected.
+#pragma comment(copyright, U"@(#) Hello utf32") // expected-error {{expected string literal in pragma comment}}
diff --git a/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
new file mode 100644
index 0000000000000..4c6109e1176d3
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
@@ -0,0 +1,24 @@
+//===-- LowerCommentStringPass.h - Lower Comment string metadata --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
+#define LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+class LowerCommentStringPass : public PassInfoMixin<LowerCommentStringPass> {
+public:
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+
+ static bool isRequired() { return true; }
+};
+
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index f7db63ef8bf74..8964784248aaa 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -370,6 +370,7 @@
#include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
#include "llvm/Transforms/Utils/LoopSimplify.h"
#include "llvm/Transforms/Utils/LoopVersioning.h"
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
#include "llvm/Transforms/Utils/LowerGlobalDtors.h"
#include "llvm/Transforms/Utils/LowerIFunc.h"
#include "llvm/Transforms/Utils/LowerInvoke.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 9eea552fd263e..4e6e49361e415 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -145,6 +145,7 @@
#include "llvm/Transforms/Utils/ExtraPassManager.h"
#include "llvm/Transforms/Utils/InjectTLIMappings.h"
#include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
#include "llvm/Transforms/Utils/Mem2Reg.h"
#include "llvm/Transforms/Utils/MoveAutoInit.h"
#include "llvm/Transforms/Utils/NameAnonGlobals.h"
@@ -1750,6 +1751,13 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
InlineContext{ThinOrFullLTOPhase::None, InlinePass::CGSCCInliner}));
}
}
+
+ // Lower !comment_string.loadtime metadata to a concrete TU-local string
+ // global and attach !implicit.ref to all defined functions. Running late
+ // ensures compiler-generated functions (e.g. from inlining, coroutines)
+ // are also anchored to the copyright string via .ref directives.
+ MPM.addPass(LowerCommentStringPass());
+
return MPM;
}
@@ -1919,6 +1927,12 @@ PassBuilder::buildThinLTOPreLinkDefaultPipeline(OptimizationLevel Level) {
// Emit annotation remarks.
addAnnotationRemarksPass(MPM);
+ // 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());
+
addRequiredLTOPreLinkPasses(MPM);
instructionCountersPass(MPM, /* IsPreOptimization */ false);
@@ -2499,6 +2513,11 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level,
if (EnableInstrumentor)
MPM.addPass(InstrumentorPass(FS));
+ // Lower !comment_string.loadtime metadata to a concrete TU-local string
+ // global. Running at the end of the O0 pipeline ensures all functions
+ // including AlwaysInliner-generated ones are captured.
+ MPM.addPass(LowerCommentStringPass());
+
if (isLTOPreLink(Phase))
addRequiredLTOPreLinkPasses(MPM);
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 8f70c7cefa408..7ced179b78c11 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -62,6 +62,7 @@ MODULE_PASS("check-debugify", NewPMCheckDebugifyPass())
MODULE_PASS("constmerge", ConstantMergePass())
MODULE_PASS("coro-cleanup", CoroCleanupPass())
MODULE_PASS("coro-early", CoroEarlyPass())
+MODULE_PASS("lower-comment-string", LowerCommentStringPass())
MODULE_PASS("cross-dso-cfi", CrossDSOCFIPass())
MODULE_PASS("ctx-instr-gen",
PGOInstrumentationGen(PGOInstrumentationType::CTXPROF))
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index 6afd752794484..0088d439e6895 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -54,6 +54,7 @@ add_llvm_component_library(LLVMTransformUtils
LoopUtils.cpp
LoopVersioning.cpp
LowerAtomic.cpp
+ LowerCommentStringPass.cpp
LowerGlobalDtors.cpp
LowerIFunc.cpp
LowerInvoke.cpp
diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
new file mode 100644
index 0000000000000..cc9bee494d597
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp
@@ -0,0 +1,150 @@
+//===-- LowerCommentStringPass.cpp - Lower Comment string metadata -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This pass lowers the module-level comment string metadata emitted by Clang:
+//
+// !comment_string.loadtime = !{!"Copyright ..."}
+//
+// into concrete, translation-unit-local globals.
+// This Pass is enabled only for AIX.
+// For each module (translation unit), the pass performs the following:
+//
+// 1. Creates a null-terminated, internal constant string global
+// (`__loadtime_comment_str`) containing the copyright text with
+// section attribute "__loadtime_comment". The backend places this
+// in the .text section of the object file.
+//
+// 2. Marks the string in `llvm.compiler.used` so it cannot be dropped by
+// optimization or LTO.
+//
+// 3. Attaches `!implicit.ref` metadata referencing the string to every
+// defined function in the module. The PowerPC AIX backend recognizes
+// this metadata and emits a `.ref` directive from the function to the
+// string, creating a concrete relocation that prevents the linker from
+// discarding the string (as long as the referencing symbol is kept).
+//
+// Input IR:
+// !comment_string.loadtime = !{!"Copyright"}
+// Output IR:
+// @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00",
+// section "__loadtime_comment"
+// @llvm.compiler.used = appending global [1 x ptr] [ptr
+// @__loadtime_comment_str]
+//
+// define i32 @func() !implicit.ref !5 { ... }
+// !5 = !{ptr @__loadtime_comment_str}
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/MDBuilder.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/TargetParser/Triple.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+#define DEBUG_TYPE "lower-comment-string"
+
+using namespace llvm;
+
+static cl::opt<bool>
+ DisableCopyrightMetadata("disable-lower-comment-string", cl::ReallyHidden,
+ cl::desc("Disable LowerCommentString pass."),
+ cl::init(false));
+
+static bool isSupportedTarget(const Module &M) {
+ Triple T{M.getTargetTriple()};
+ return T.isOSAIX();
+}
+
+PreservedAnalyses LowerCommentStringPass::run(Module &M,
+ ModuleAnalysisManager &AM) {
+ if (DisableCopyrightMetadata || !isSupportedTarget(M))
+ return PreservedAnalyses::all();
+
+ LLVMContext &Ctx = M.getContext();
+
+ // Single-metadata: !comment_string.loadtime = !{!0}
+ // Each operand node is expected to have one MDString operand.
+ NamedMDNode *MD = M.getNamedMetadata("comment_string.loadtime");
+ if (!MD || MD->getNumOperands() == 0)
+ return PreservedAnalyses::all();
+
+ // At this point we are guaranteed that one TU contains a single copyright
+ // metadata entry. Create TU-local string global for that metadata entry.
+ MDNode *MdNode = MD->getOperand(0);
+ if (!MdNode || MdNode->getNumOperands() == 0)
+ return PreservedAnalyses::all();
+
+ auto *MdString = dyn_cast_or_null<MDString>(MdNode->getOperand(0));
+ if (!MdString)
+ return PreservedAnalyses::all();
+
+ StringRef Text = MdString->getString();
+ if (Text.empty())
+ return PreservedAnalyses::all();
+
+ // 1. Create a single null-terminated string global.
+ Constant *StrInit = ConstantDataArray::getString(Ctx, Text, /*AddNull=*/true);
+
+ // The global variable should be internal, constant, and TU-local.
+ // This avoids duplicate symbol issues across TUs.
+ auto *StrGV = new GlobalVariable(M, StrInit->getType(),
+ /*isConstant=*/true,
+ GlobalValue::InternalLinkage, StrInit,
+ /*Name=*/"__loadtime_comment_str");
+ // Set unnamed_addr to allow the linker to merge identical strings.
+ StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
+ StrGV->setAlignment(Align(1));
+ // Place in the "__loadtime_comment" section.
+ // The GV is constant, so we expect a read-only section.
+ StrGV->setSection("__loadtime_comment");
+
+ // 2. Add the string to llvm.compiler.used to prevent LLVM optimization/LTO
+ // passes from removing it.
+ appendToCompilerUsed(M, {StrGV});
+
+ // 3. Attach !implicit.ref metadata to every defined function.
+ // Create a metadata node pointing to the copyright string:
+ // !N = !{ptr @__loadtime_comment_str}
+ Metadata *Ops[] = {ConstantAsMetadata::get(StrGV)};
+ MDNode *ImplicitRefMD = MDNode::get(Ctx, Ops);
+
+ auto AddImplicitRef = [&](Function &F) {
+ if (F.isDeclaration())
+ return;
+ // Attach the !implicit.ref metadata to the function.
+ F.setMetadata(LLVMContext::MD_implicit_ref, ImplicitRefMD);
+ LLVM_DEBUG(dbgs() << "[copyright] attached implicit.ref to function: "
+ << F.getName() << "\n");
+ };
+
+ // Process all functions in the module and add !implicit.ref to the function.
+ for (Function &F : M)
+ AddImplicitRef(F);
+
+ // Cleanup the processed metadata.
+ MD->eraseFromParent();
+ LLVM_DEBUG(dbgs() << "[copyright] created string and anchor for module\n");
+
+ return PreservedAnalyses::all();
+}
diff --git a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll
index 86090324c770c..1abc5ab09b674 100644
--- a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll
+++ b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll
@@ -2,7 +2,7 @@
; RUN: opt -mtriple=aarch64 -S -passes='default<O2>' -print-pipeline-passes < %s | FileCheck %s
; CHECK: loop-idiom-vectorize
-; O0: {{^}}function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,function(annotation-remarks),verify,print{{$}}
+; O0: {{^}}function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,lower-comment-string,function(annotation-remarks),verify,print{{$}}
define void @foo() {
entry:
diff --git a/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll b/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll
index 0f80e9c60c441..2ff5d83f8ed00 100644
--- a/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll
+++ b/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll
@@ -3,7 +3,7 @@
; CHECK: hexagon-loop-idiom
; CHECK: hexagon-vlcr
-; O0: {{^}}function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,function(annotation-remarks),verify,print{{$}}
+; O0: {{^}}function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,lower-comment-string,function(annotation-remarks),verify,print{{$}}
define void @test_hexagon_passes() {
entry:
diff --git a/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
new file mode 100644
index 0000000000000..58c3b4ae42ca1
--- /dev/null
+++ b/llvm/test/LTO/PowerPC/pragma-comment-copyright-lto.ll
@@ -0,0 +1,67 @@
+; RUN: rm -rf %t && mkdir %t
+; RUN: split-file %s %t
+; RUN: llvm-as %t/tu1.ll -o %t/tu1.bc
+; RUN: llvm-as %t/tu2.ll -o %t/tu2.bc
+; RUN: llvm-lto -filetype=asm \
+; RUN: -exported-symbol=main \
+; RUN: -exported-symbol=f_tu1 \
+; RUN: %t/tu1.bc %t/tu2.bc \
+; RUN: -o %t/out.s
+; RUN: FileCheck %s < %t/out.s
+
+
+;--- tu1.ll
+;; TU1: already lowered by LowerCommentStringPass at prelink
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+ at __loadtime_comment_str = internal unnamed_addr constant
+ [14 x i8] c"Copyright TU1\00",
+ section "__loadtime_comment", align 1
+ at llvm.compiler.used = appending global [1 x ptr]
+ [ptr @__loadtime_comment_str], section "llvm.metadata"
+
+define void @f_tu1() !implicit.ref !0 {
+entry:
+ ret void
+}
+
+!0 = !{ptr @__loadtime_comment_str}
+
+;--- tu2.ll
+;; TU2: already lowered by LowerCommentStringPass at prelink
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64"
+target triple = "powerpc-ibm-aix7.3.0.0"
+
+ at __loadtime_comment_str = internal unnamed_addr constant
+ [14 x i8] c"Copyright TU2\00",
+ section "__loadtime_comment", align 1
+ at llvm.compiler.used = appending global [1 x ptr]
+ [ptr @__loadtime_comment_str], section "llvm.metadata"
+
+declare void @f_tu1()
+
+define i32 @main() !implicit.ref !0 {
+entry:
+ call void @f_tu1()
+ ret i32 0
+}
+
+!0 = !{ptr @__loadtime_comment_str}
+
+;; .ref directive emitted from f_tu1's csect anchoring TU1's string
+; CHECK-LABEL: .f_tu1:
+; CHECK-NEXT: .ref __loadtime_comment_str
+
+;; .ref directive emitted from main's csect anchoring TU2's string
+; CHECK-LABEL: .main:
+; CHECK-NEXT: .ref __loadtime_comment_str.2
+
+;; Both copyright strings in the read-only __loadtime_comment csect
+; CHECK: .csect __loadtime_comment[RO],2
+; CHECK-DAG: .lglobl __loadtime_comment_str
+; CHECK-LABEL: __loadtime_comment_str:
+; CHECK-DAG: .string "Copyright TU1"
+; CHECK-DAG: .lglobl __loadtime_comment_str.2
+; CHECK-LABEL: __loadtime_comment_str.2:
+; CHECK-DAG: .string "Copyright TU2"
diff --git a/llvm/test/Other/new-pm-O0-defaults.ll b/llvm/test/Other/new-pm-O0-defaults.ll
index bac4150caab58..5c3571aa05df2 100644
--- a/llvm/test/Other/new-pm-O0-defaults.ll
+++ b/llvm/test/Other/new-pm-O0-defaults.ll
@@ -42,6 +42,7 @@
; CHECK-MATRIX-NEXT: Running analysis: TargetIRAnalysis
; CHECK-CORO-NEXT: Running pass: CoroConditionalWrapper
; CHECK-ALLOCTOKEN-NEXT: Running pass: AllocTokenPass
+; CHECK-CORO-NEXT: Running pass: LowerCommentStringPass
; CHECK-PRE-LINK: Running pass: CanonicalizeAliasesPass
; CHECK-PRE-LINK-NEXT: Running pass: NameAnonGlobalPass
; CHECK-THINLTO: Running pass: DropTypeTestsPass
diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index d6e51f451c3a4..181c9105f625a 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -283,6 +283,7 @@
; CHECK-DEFAULT-NEXT: Running pass: CGProfilePass
; CHECK-DEFAULT-NEXT: Running pass: RelLookupTableConverterPass
; CHECK-LTO-NOT: Running pass: RelLookupTableConverterPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
; CHECK-LTO-NEXT: Running pass: CanonicalizeAliasesPass
; CHECK-LTO-NEXT: Running pass: NameAnonGlobalPass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
index dbbd10eaa8775..93b17cc87a1de 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -206,6 +206,7 @@
; CHECK-POSTLINK-O-NEXT: Running pass: RelLookupTableConverterPass
; CHECK-EP-OPT-EARLY-NEXT: Running pass: NoOpModulePass
; CHECK-EP-OPT-LAST-NEXT: Running pass: NoOpModulePass
+; CHECK-POSTLINK-O-NEXT: Running pass: LowerCommentStringPass
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
index eab1f2a257c1a..c29da3fd2c353 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -188,6 +188,7 @@
; CHECK-O-NEXT: Running pass: ConstantMergePass
; CHECK-O-NEXT: Running pass: CGProfilePass
; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
index 339e346fdf439..440d25d15bca8 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -195,6 +195,7 @@
; CHECK-O-NEXT: Running pass: ConstantMergePass
; CHECK-O-NEXT: Running pass: CGProfilePass
; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
index f4245b66b0429..59037b411d99f 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
@@ -182,6 +182,7 @@
; CHECK-EP-OPT-EARLY-NEXT: Running pass: NoOpModulePass
; CHECK-EP-OPT-LAST-NEXT: Running pass: NoOpModulePass
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
index 87acb355fddc7..8fa5bab37b92b 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
@@ -185,6 +185,7 @@
; CHECK-O-NEXT: Running pass: GlobalDCEPass
; CHECK-EXT: Running pass: {{.*}}::Bye
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
index e5c1453d692eb..e0d096f202092 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
@@ -148,6 +148,7 @@
; CHECK-O-NEXT: Running pass: GlobalOptPass
; CHECK-O-NEXT: Running pass: GlobalDCEPass
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
+; CHECK-O-NEXT: Running pass: LowerCommentStringPass
; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass
; CHECK-O-NEXT: Running pass: NameAnonGlobalPass
; CHECK-O-NEXT: Running pass: PrintModulePass
diff --git a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
new file mode 100644
index 0000000000000..bf353bdad4a32
--- /dev/null
+++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll
@@ -0,0 +1,41 @@
+; RUN: opt -passes=lower-comment-string -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0
+
+; Verify that lower-comment-string is enabled by default on all opt pipelines.
+; RUN: opt --O0 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0
+; RUN: opt --O1 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON
+; RUN: opt --O2 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON
+; RUN: opt --O3 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON
+
+target triple = "powerpc-ibm-aix"
+
+define void @f0() {
+entry:
+ ret void
+}
+define i32 @main() {
+entry:
+ ret i32 0
+}
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"wchar_size", i32 2}
+
+!comment_string.loadtime = !{!1}
+!1 = !{!"@(#) Copyright IBM 2025"}
+
+
+; ---- Globals--------------------------------------------
+; CHECK: @__loadtime_comment_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__loadtime_comment", align 1
+; Preservation in llvm.compiler.used sets
+; CHECK-NEXT: @llvm.compiler.used = appending global [1 x ptr] [ptr @__loadtime_comment_str], section "llvm.metadata"
+; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"}
+
+; Function has an implicit ref MD pointing at the string:
+; CHECK-O0: define void @f0() !implicit.ref ![[MD:[0-9]+]]
+; CHECK-ON: define void @f0() local_unnamed_addr #0 !implicit.ref ![[MD:[0-9]+]]
+; CHECK-O0: define i32 @main() !implicit.ref ![[MD]]
+; CHECK-ON: define noundef i32 @main() local_unnamed_addr #0 !implicit.ref ![[MD]]
+
+; Verify metadata content
+; CHECK-O0: ![[MD]] = !{ptr @__loadtime_comment_str}
+; CHECK-ON: ![[MD]] = !{ptr @__loadtime_comment_str}
More information about the cfe-commits
mailing list