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 <[email protected]> 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 <[email protected]> --- clang/docs/LanguageExtensions.rst | 23 +++ .../clang/Basic/DiagnosticParseKinds.td | 4 + clang/include/clang/Basic/PragmaKinds.h | 3 +- clang/lib/AST/TextNodeDumper.cpp | 3 + clang/lib/CodeGen/CodeGenModule.cpp | 41 +++++ clang/lib/CodeGen/CodeGenModule.h | 11 +- clang/lib/Parse/ParsePragma.cpp | 37 ++++- clang/test/CXX/module/cpp.pre/module_decl.cpp | 2 +- .../pragma-comment-copyright-modules.cpp | 30 ++++ clang/test/CodeGen/PowerPC/pragma-comment.c | 22 +++ clang/test/CodeGen/lto-newpm-pipeline.c | 2 + clang/test/Preprocessor/pragma-comment.c | 92 +++++++++++ .../Transforms/Utils/LowerCommentStringPass.h | 24 +++ llvm/lib/Passes/PassBuilder.cpp | 1 + llvm/lib/Passes/PassBuilderPipelines.cpp | 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" + +@__loadtime_comment_str = internal unnamed_addr constant + [14 x i8] c"Copyright TU1\00", + section "__loadtime_comment", align 1 [email protected] = appending global [1 x ptr] + [ptr @__loadtime_comment_str], section "llvm.metadata" + +define void @f_tu1() !implicit.ref !0 { +entry: + ret void +} + +!0 = !{ptr @__loadtime_comment_str} + +;--- tu2.ll +;; TU2: already lowered by LowerCommentStringPass at prelink +target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32-f64:32:64" +target triple = "powerpc-ibm-aix7.3.0.0" + +@__loadtime_comment_str = internal unnamed_addr constant + [14 x i8] c"Copyright TU2\00", + section "__loadtime_comment", align 1 [email protected] = appending global [1 x ptr] + [ptr @__loadtime_comment_str], section "llvm.metadata" + +declare void @f_tu1() + +define i32 @main() !implicit.ref !0 { +entry: + call void @f_tu1() + ret i32 0 +} + +!0 = !{ptr @__loadtime_comment_str} + +;; .ref directive emitted from f_tu1's csect anchoring TU1's string +; CHECK-LABEL: .f_tu1: +; CHECK-NEXT: .ref __loadtime_comment_str + +;; .ref directive emitted from main's csect anchoring TU2's string +; CHECK-LABEL: .main: +; CHECK-NEXT: .ref __loadtime_comment_str.2 + +;; Both copyright strings in the read-only __loadtime_comment csect +; CHECK: .csect __loadtime_comment[RO],2 +; CHECK-DAG: .lglobl __loadtime_comment_str +; CHECK-LABEL: __loadtime_comment_str: +; CHECK-DAG: .string "Copyright TU1" +; CHECK-DAG: .lglobl __loadtime_comment_str.2 +; CHECK-LABEL: __loadtime_comment_str.2: +; CHECK-DAG: .string "Copyright TU2" diff --git a/llvm/test/Other/new-pm-O0-defaults.ll b/llvm/test/Other/new-pm-O0-defaults.ll index bac4150caab58..5c3571aa05df2 100644 --- a/llvm/test/Other/new-pm-O0-defaults.ll +++ b/llvm/test/Other/new-pm-O0-defaults.ll @@ -42,6 +42,7 @@ ; CHECK-MATRIX-NEXT: Running analysis: TargetIRAnalysis ; CHECK-CORO-NEXT: Running pass: CoroConditionalWrapper ; CHECK-ALLOCTOKEN-NEXT: Running pass: AllocTokenPass +; CHECK-CORO-NEXT: Running pass: LowerCommentStringPass ; CHECK-PRE-LINK: Running pass: CanonicalizeAliasesPass ; CHECK-PRE-LINK-NEXT: Running pass: NameAnonGlobalPass ; CHECK-THINLTO: Running pass: DropTypeTestsPass diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll index d6e51f451c3a4..181c9105f625a 100644 --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -283,6 +283,7 @@ ; CHECK-DEFAULT-NEXT: Running pass: CGProfilePass ; CHECK-DEFAULT-NEXT: Running pass: RelLookupTableConverterPass ; CHECK-LTO-NOT: Running pass: RelLookupTableConverterPass +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-LTO-NEXT: Running pass: CanonicalizeAliasesPass ; CHECK-LTO-NEXT: Running pass: NameAnonGlobalPass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll index dbbd10eaa8775..93b17cc87a1de 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll @@ -206,6 +206,7 @@ ; CHECK-POSTLINK-O-NEXT: Running pass: RelLookupTableConverterPass ; CHECK-EP-OPT-EARLY-NEXT: Running pass: NoOpModulePass ; CHECK-EP-OPT-LAST-NEXT: Running pass: NoOpModulePass +; CHECK-POSTLINK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: PrintModulePass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll index eab1f2a257c1a..c29da3fd2c353 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll @@ -188,6 +188,7 @@ ; CHECK-O-NEXT: Running pass: ConstantMergePass ; CHECK-O-NEXT: Running pass: CGProfilePass ; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: PrintModulePass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll index 339e346fdf439..440d25d15bca8 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll @@ -195,6 +195,7 @@ ; CHECK-O-NEXT: Running pass: ConstantMergePass ; CHECK-O-NEXT: Running pass: CGProfilePass ; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: PrintModulePass diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll index f4245b66b0429..59037b411d99f 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll @@ -182,6 +182,7 @@ ; CHECK-EP-OPT-EARLY-NEXT: Running pass: NoOpModulePass ; CHECK-EP-OPT-LAST-NEXT: Running pass: NoOpModulePass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass ; CHECK-O-NEXT: Running pass: PrintModulePass diff --git a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll index 87acb355fddc7..8fa5bab37b92b 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll @@ -185,6 +185,7 @@ ; CHECK-O-NEXT: Running pass: GlobalDCEPass ; CHECK-EXT: Running pass: {{.*}}::Bye ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass ; CHECK-O-NEXT: Running pass: PrintModulePass diff --git a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll index e5c1453d692eb..e0d096f202092 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll @@ -148,6 +148,7 @@ ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: CanonicalizeAliasesPass ; CHECK-O-NEXT: Running pass: NameAnonGlobalPass ; CHECK-O-NEXT: Running pass: PrintModulePass diff --git a/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll new file mode 100644 index 0000000000000..bf353bdad4a32 --- /dev/null +++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll @@ -0,0 +1,41 @@ +; RUN: opt -passes=lower-comment-string -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0 + +; Verify that lower-comment-string is enabled by default on all opt pipelines. +; RUN: opt --O0 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0 +; RUN: opt --O1 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON +; RUN: opt --O2 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON +; RUN: opt --O3 -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ON + +target triple = "powerpc-ibm-aix" + +define void @f0() { +entry: + ret void +} +define i32 @main() { +entry: + ret i32 0 +} + +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"wchar_size", i32 2} + +!comment_string.loadtime = !{!1} +!1 = !{!"@(#) Copyright IBM 2025"} + + +; ---- Globals-------------------------------------------- +; CHECK: @__loadtime_comment_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__loadtime_comment", align 1 +; Preservation in llvm.compiler.used sets +; CHECK-NEXT: @llvm.compiler.used = appending global [1 x ptr] [ptr @__loadtime_comment_str], section "llvm.metadata" +; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"} + +; Function has an implicit ref MD pointing at the string: +; CHECK-O0: define void @f0() !implicit.ref ![[MD:[0-9]+]] +; CHECK-ON: define void @f0() local_unnamed_addr #0 !implicit.ref ![[MD:[0-9]+]] +; CHECK-O0: define i32 @main() !implicit.ref ![[MD]] +; CHECK-ON: define noundef i32 @main() local_unnamed_addr #0 !implicit.ref ![[MD]] + +; Verify metadata content +; CHECK-O0: ![[MD]] = !{ptr @__loadtime_comment_str} +; CHECK-ON: ![[MD]] = !{ptr @__loadtime_comment_str} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
