https://github.com/tonykuttai updated https://github.com/llvm/llvm-project/pull/178184
>From 9c2b2fe99ea56670e01dfce24c25ddbbfd0392c9 Mon Sep 17 00:00:00 2001 From: Tony Varghese <[email protected]> Date: Mon, 2 Feb 2026 23:19:10 -0500 Subject: [PATCH 1/3] [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/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 | 52 ++++++ clang/lib/CodeGen/CodeGenModule.h | 12 ++ clang/lib/Parse/ParsePragma.cpp | 53 +++++-- .../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 +- 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 +++++ 28 files changed, 531 insertions(+), 14 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/Transforms/LowerCommentString/lower-comment-string.ll diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 03cb02deb5e7f..b8968efd825bb 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -6819,6 +6819,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 on 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 a747c789956d4..857e8d58fecb5 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2602,6 +2602,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 20a28c39af88a..91d051894e8c7 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()); @@ -3572,6 +3574,41 @@ 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 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::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, @@ -4092,6 +4129,18 @@ 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 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); + } +} + 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 @@ -7847,6 +7896,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 d62707a3355c9..8bded97cf95f9 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,8 @@ class CodeGenModule : public CodeGenTypeCache { /// Appends a dependent lib to the appropriate metadata value. void AddDependentLib(StringRef Lib); + /// Process Pragma Comment Copyright. + void ProcessPragmaCommentCopyright(StringRef Comment, bool isFromASTFile); llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD); @@ -2079,6 +2084,13 @@ 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 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 /// 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..08379e9c20086 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 (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. + 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 +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..a1f8893013ff7 --- /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.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..d7588318ee9d7 100644 --- a/clang/test/CodeGen/lto-newpm-pipeline.c +++ b/clang/test/CodeGen/lto-newpm-pipeline.c @@ -36,6 +36,7 @@ // 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 @@ -50,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/clang/test/Preprocessor/pragma-comment.c b/clang/test/Preprocessor/pragma-comment.c new file mode 100644 index 0000000000000..4f336fb6a6186 --- /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 35ef547b36e80..28334d3ebdff3 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -367,6 +367,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 319ea2f3a69a1..2bf1f8555e0c6 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" @@ -1700,6 +1701,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; } @@ -1858,6 +1866,12 @@ PassBuilder::buildThinLTOPreLinkDefaultPipeline(OptimizationLevel Level) { addRequiredLTOPreLinkPasses(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()); + return MPM; } @@ -2425,6 +2439,11 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level, // Emit annotation remarks. addAnnotationRemarksPass(MPM); + // 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/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index b05b1d3a4a8f5..625e7ff90b11d 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..eca704dcaf806 --- /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); + + // 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. + 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..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: {{^}}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/CodeGen/Hexagon/print-pipeline-passes.ll b/llvm/test/CodeGen/Hexagon/print-pipeline-passes.ll index 0f80e9c60c441..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: {{^}}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-defaults.ll b/llvm/test/Other/new-pm-defaults.ll index 3048206add4e2..b23353a017d28 100644 --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -282,6 +282,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 c16ec4800b6bc..bfd75e6442e85 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll @@ -205,6 +205,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 15f11914bbde2..397f858c7445b 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll @@ -187,6 +187,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 8eac5cf56a106..8b0d31195ae59 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll @@ -194,6 +194,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..0530bbdaaeafb 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll @@ -184,6 +184,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 87acb355fddc7..ce01e3dd61d6e 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll @@ -187,6 +187,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 e5c1453d692eb..4e1231add71c2 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll @@ -150,6 +150,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/Transforms/LowerCommentString/lower-comment-string.ll b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll new file mode 100644 index 0000000000000..bf353bdad4a32 --- /dev/null +++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll @@ -0,0 +1,41 @@ +; RUN: opt -passes=lower-comment-string -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0 + +; Verify that lower-comment-string is enabled by default on all opt pipelines. +; RUN: opt --O0 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0 +; RUN: opt --O1 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON +; RUN: opt --O2 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON +; RUN: opt --O3 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON + +target triple = "powerpc-ibm-aix" + +define void @f0() { +entry: + ret void +} +define i32 @main() { +entry: + ret i32 0 +} + +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"wchar_size", i32 2} + +!comment_string.loadtime = !{!1} +!1 = !{!"@(#) Copyright IBM 2025"} + + +; ---- Globals-------------------------------------------- +; CHECK: @__loadtime_comment_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__loadtime_comment", align 1 +; Preservation in llvm.compiler.used sets +; CHECK-NEXT: @llvm.compiler.used = appending global [1 x ptr] [ptr @__loadtime_comment_str], section "llvm.metadata" +; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"} + +; Function has an implicit ref MD pointing at the string: +; CHECK-O0: define void @f0() !implicit.ref ![[MD:[0-9]+]] +; CHECK-ON: define void @f0() local_unnamed_addr #0 !implicit.ref ![[MD:[0-9]+]] +; CHECK-O0: define i32 @main() !implicit.ref ![[MD]] +; CHECK-ON: define noundef i32 @main() local_unnamed_addr #0 !implicit.ref ![[MD]] + +; Verify metadata content +; CHECK-O0: ![[MD]] = !{ptr @__loadtime_comment_str} +; CHECK-ON: ![[MD]] = !{ptr @__loadtime_comment_str} >From 8a947570e505916beae9047b4b23b5e86c026c4c Mon Sep 17 00:00:00 2001 From: Tony Varghese <[email protected]> Date: Wed, 13 May 2026 10:32:42 +0530 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Hubert Tong <[email protected]> --- clang/docs/LanguageExtensions.rst | 4 ++-- clang/lib/CodeGen/CodeGenModule.cpp | 11 ----------- clang/lib/CodeGen/CodeGenModule.h | 9 +++------ clang/lib/Parse/ParsePragma.cpp | 5 ++--- clang/test/Preprocessor/pragma-comment.c | 4 ++-- llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp | 2 +- 6 files changed, 10 insertions(+), 25 deletions(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index b8968efd825bb..12be40641008e 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -6821,7 +6821,7 @@ The second parameter is the library name (without the traditional Unix prefix of Embedding Copyright Information on AIX ====================================== -Clang supports the ``#pragma comment(copyright, "string")`` directive 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 @@ -6831,7 +6831,7 @@ non-AIX targets. #pragma comment(copyright, "string-literal") -The second argument is an ordinary string literal. Concatenated ordinary string +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. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 91d051894e8c7..71082dbb97891 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3575,12 +3575,6 @@ void CodeGenModule::AddDependentLib(StringRef Lib) { } /// Process copyright pragma and create LLVM metadata. -/// #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. @@ -4130,11 +4124,6 @@ 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 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); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 8bded97cf95f9..66c608ddac579 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1502,7 +1502,7 @@ class CodeGenModule : public CodeGenTypeCache { /// Appends a dependent lib to the appropriate metadata value. void AddDependentLib(StringRef Lib); - /// Process Pragma Comment Copyright. + /// Process #pragma comment(copyright, ...). void ProcessPragmaCommentCopyright(StringRef Comment, bool isFromASTFile); llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD); @@ -2084,11 +2084,8 @@ 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 IR passes to embed it in the .text section - /// of the generated object file, where it will be loaded into memory at - /// program runtime. + /// Emit the load-time comment metadata (e.g., from + /// #pragma comment(copyright, ...)) for the translation unit. 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 08379e9c20086..595e9e2e430e6 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -3311,10 +3311,9 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP, return; } - // 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. + // 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; diff --git a/clang/test/Preprocessor/pragma-comment.c b/clang/test/Preprocessor/pragma-comment.c index 4f336fb6a6186..f365edf661588 100644 --- a/clang/test/Preprocessor/pragma-comment.c +++ b/clang/test/Preprocessor/pragma-comment.c @@ -68,11 +68,11 @@ #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 +// 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 +// Concatenated ordinary string literals and escapes are accepted. #pragma comment(copyright, "@(#) Hello, " "world\n\t\"@(#) quoted\"") // expected-no-diagnostics //--- u8-literal.c diff --git a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp index eca704dcaf806..cc9bee494d597 100644 --- a/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp +++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp @@ -106,7 +106,7 @@ PreservedAnalyses LowerCommentStringPass::run(Module &M, // 1. Create a single null-terminated string global. Constant *StrInit = ConstantDataArray::getString(Ctx, Text, /*AddNull=*/true); - // Global variable should be Internal, constant, TU-local. + // 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, >From 7464d4342bc5d7a5ff6e45115b0570d9b53cf146 Mon Sep 17 00:00:00 2001 From: Tony Varghese <[email protected]> Date: Wed, 13 May 2026 11:10:55 +0530 Subject: [PATCH 3/3] [NFC] Make ProcessPragmaCommentCopyright() private and move EmitLoadTimeComment() to the end of the class. --- clang/lib/CodeGen/CodeGenModule.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 66c608ddac579..0d8f0f38c34ee 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1502,9 +1502,6 @@ class CodeGenModule : public CodeGenTypeCache { /// Appends a dependent lib to the appropriate metadata value. void AddDependentLib(StringRef Lib); - /// Process #pragma comment(copyright, ...). - void ProcessPragmaCommentCopyright(StringRef Comment, bool isFromASTFile); - llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD); void setFunctionLinkage(GlobalDecl GD, llvm::Function *F) { @@ -1907,6 +1904,9 @@ class CodeGenModule : public CodeGenTypeCache { llvm::GlobalValue *getPFPDeactivationSymbol(const FieldDecl *FD); private: + /// Process #pragma comment(copyright, ...). + void ProcessPragmaCommentCopyright(StringRef Comment, bool isFromASTFile); + bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const; llvm::Constant *GetOrCreateLLVMFunction( @@ -2084,10 +2084,6 @@ class CodeGenModule : public CodeGenTypeCache { /// false, the definition can be emitted lazily if it's used. bool MustBeEmitted(const ValueDecl *D); - /// Emit the load-time comment metadata (e.g., from - /// #pragma comment(copyright, ...)) for the translation unit. - 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 @@ -2117,6 +2113,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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
