Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,9 @@ 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' "
"can be specified only once per source file - ignored">,
InGroup<IgnoredPragmas>;
// - #pragma detect_mismatch
def err_pragma_detect_mismatch_malformed : Error<
"pragma detect_mismatch is malformed; it requires two comma-separated "
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/PragmaKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2523,6 +2523,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())
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,14 @@ void CodeGenModule::Release() {

EmitBackendOptionsMetadata(getCodeGenOpts());

// Emit copyright metadata for AIX
if (!AIXCopyrightComment.empty()) {
auto *NMD =
getModule().getOrInsertNamedMetadata("aix.copyright.comment");
for (auto *MD : AIXCopyrightComment)
NMD->addOperand(MD);
}

// If there is device offloading code embed it in the host now.
EmbedObject(&getModule(), CodeGenOpts, *getFileSystem(), getDiags());

Expand Down Expand Up @@ -3317,6 +3325,23 @@ void CodeGenModule::AddDependentLib(StringRef Lib) {
LinkerOptionsMetadata.push_back(llvm::MDNode::get(C, MDOpts));
}

/// Process the #pragma comment(copyright, " copy right string ")
/// and create llvm metadata for the copyrgiht
void CodeGenModule::ProcessPragmaCommentCopyright(StringRef Comment) {

// Pragma Comment Copyright is enabled only when:
// - OS is AIX
// - Comment is non empty
if (!getTriple().isOSAIX() || Comment.empty())
return;

// Create llvm metadata with the comment string
auto &C = getLLVMContext();
llvm::Metadata *Ops[] = {llvm::MDString::get(C, Comment.str())};
auto *Node = llvm::MDNode::get(C, Ops);
AIXCopyrightComment.push_back(Node); // This should be available during the runtime
}

/// Add link options implied by the given module, including modules
/// it depends on, using a postorder walk.
static void addLinkOptionsPostorder(CodeGenModule &CGM, Module *Mod,
Expand Down Expand Up @@ -7453,6 +7478,9 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
case PCK_Lib:
AddDependentLib(PCD->getArg());
break;
case PCK_Copyright:
ProcessPragmaCommentCopyright(PCD->getArg());
break;
case PCK_Compiler:
case PCK_ExeStr:
case PCK_User:
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,9 @@ class CodeGenModule : public CodeGenTypeCache {
/// A vector of metadata strings for dependent libraries for ELF.
SmallVector<llvm::MDNode *, 16> ELFDependentLibraries;

/// A vector of metadata strings for copyright comment for AIX
SmallVector<llvm::MDNode *, 16> AIXCopyrightComment;

/// @name Cache for Objective-C runtime types
/// @{

Expand Down Expand Up @@ -1458,6 +1461,8 @@ class CodeGenModule : public CodeGenTypeCache {
/// Appends a dependent lib to the appropriate metadata value.
void AddDependentLib(StringRef Lib);

/// Append AIX copyright comment to the module-level metadata.
void ProcessPragmaCommentCopyright(StringRef Comment);

llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD);

Expand Down
26 changes: 24 additions & 2 deletions clang/lib/Parse/ParsePragma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,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());
}
Expand Down Expand Up @@ -595,7 +596,8 @@ void Parser::resetPragmaHandlers() {
OpenACCHandler.reset();

if (getLangOpts().MicrosoftExt ||
getTargetInfo().getTriple().isOSBinFormatELF()) {
getTargetInfo().getTriple().isOSBinFormatELF() ||
getTargetInfo().getTriple().isOSAIX()) {
PP.RemovePragmaHandler(MSCommentHandler.get());
MSCommentHandler.reset();
}
Expand Down Expand Up @@ -3207,6 +3209,7 @@ 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);
Expand All @@ -3219,6 +3222,19 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
return;
}

// pragma comment copyright can each appear only once in a TU.
if (PP.getTargetInfo().getTriple().isOSAIX()) {
static bool SeenAIXCopyright = false;
if (Kind == PCK_Copyright) {
if (SeenAIXCopyright) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_once)
<< II->getName();
return;
}
SeenAIXCopyright = true;
}
}

// Read the optional string if present.
PP.Lex(Tok);
std::string ArgumentString;
Expand All @@ -3245,6 +3261,12 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
return;
}

if (PP.getTargetInfo().getTriple().isOSAIX()) {
// Accept and ignore well-formed copyright with 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);
Expand Down
110 changes: 110 additions & 0 deletions clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// REQUIRES: powerpc-registered-target, system-aix, clang

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this test fits with the LLVM unit testing philosophy.

My understanding is that we have tests

  • for the front-end generation of the aix.copyright.comment metadata, and
  • for the transformation of the aix.copyright.comment metadata to implicit.ref, etc.

What we do not have are targeted tests that specifically verify that the transformation is run at the right point in the various possible pipelines.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right. Removed this test.

Copy link
Author

@tonykuttai tonykuttai Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we do not have are targeted tests that specifically verify that the transformation is run at the right point in the various possible pipelines.

Added the opt levels testing to llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll. Do we need an end-to-end test ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a end-to-end test ?

I can't say that we shouldn't have one. It is just that I am not aware of there being any in upstream LLVM that involve the linker (except when it's an lld test).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find anything similar as well.

//
// This test verifies correct handling of `#pragma comment(copyright, ...)`
// on AIX for multiple Translation Units (TUs).
//
// Each TU defines one `#pragma comment(copyright, "...")` which should:
// - Generate a unique read-only `__llvm_copyright` csect containing the string.
// - Create a `.ref` directive from at least one function in that TU to the
// corresponding copyright symbol.
// - Preserve these copyright strings across LTO and ThinLTO linking.
//
// -----------------------------------------------------------------------------
// Build WITHOUT LTO
// -----------------------------------------------------------------------------
// RUN: split-file %s %t
//
// RUN: %clang_cc1 -triple powerpc-ibm-aix -emit-llvm-bc %t/file1.c -o %t/file1.bc
// RUN: %clang_cc1 -triple powerpc-ibm-aix -emit-llvm-bc %t/file2.c -o %t/file2.bc
// RUN: %clang_cc1 -triple powerpc-ibm-aix -emit-llvm-bc %t/file3.c -o %t/file3.bc
// RUN: %clang_cc1 -triple powerpc-ibm-aix -emit-llvm-bc %t/main.c -o %t/main.bc
//
// Compile each bitcode file to XCOFF object and link them together:
// RUN: %clang -c %t/file1.bc -o %t/file1.o
// RUN: %clang -c %t/file2.bc -o %t/file2.o
// RUN: %clang -c %t/file3.bc -o %t/file3.o
// RUN: %clang -c %t/main.bc -o %t/main.o
// RUN: %clang %t/file1.o %t/file2.o %t/file3.o %t/main.o -o %t/nonlto.exe
//
// Verify assembly emission and linked outputs:
// RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t/file1.bc -o - | FileCheck %s --check-prefix=CHECK-ASM
// RUN: /bin/strings -a %t/nonlto.exe | FileCheck %s --check-prefix=CHECK-STRINGS
// RUN: llvm-nm %t/nonlto.exe | FileCheck %s --check-prefix=CHECK-NM
// RUN: llvm-objdump -r %t/nonlto.exe | FileCheck %s --check-prefix=CHECK-OBJDUMP
//
// -----------------------------------------------------------------------------
// Build WITH Full LTO
// -----------------------------------------------------------------------------
// RUN: %clang -flto %t/file1.bc %t/file2.bc %t/file3.bc %t/main.bc -o %t/lto.exe
// RUN: /bin/strings -a %t/lto.exe | FileCheck %s --check-prefix=CHECK-STRINGS
// RUN: llvm-nm %t/lto.exe | FileCheck %s --check-prefix=CHECK-NM
// RUN: llvm-objdump -r %t/lto.exe | FileCheck %s --check-prefix=CHECK-OBJDUMP
//
// -----------------------------------------------------------------------------
// Build WITH ThinLTO
// -----------------------------------------------------------------------------
// RUN: %clang -flto=thin %t/file1.bc %t/file2.bc %t/file3.bc %t/main.bc -o %t/lto-thin.exe
// RUN: /bin/strings -a %t/lto-thin.exe | FileCheck %s --check-prefix=CHECK-STRINGS
// RUN: llvm-nm %t/lto-thin.exe | FileCheck %s --check-prefix=CHECK-NM
// RUN: llvm-objdump -r %t/lto-thin.exe | FileCheck %s --check-prefix=CHECK-OBJDUMP
//
// -----------------------------------------------------------------------------
// Assembly Checks (for a single TU)
// -----------------------------------------------------------------------------
//
// Verify that the backend:
// - Emits a `.ref` directive to tie the string to the TU
// - Emits the string in a dedicated read-only csect
//
// CHECK-ASM: .ref __aix_copyright_str
// CHECK-ASM: .csect __llvm_copyright[RO],2
// CHECK-ASM-NEXT: .lglobl __aix_copyright_str
// CHECK-ASM: __aix_copyright_str
// CHECK-ASM: .string "Copyright 2025 TU A"
//
// -----------------------------------------------------------------------------
// Final Binary Checks
// -----------------------------------------------------------------------------
//
// Ensure all TUs’ copyright strings are preserved.
// CHECK-STRINGS-DAG: Copyright 2025 TU A
// CHECK-STRINGS-DAG: Copyright 2025 TU B
// CHECK-STRINGS-DAG: Copyright 2025 TU C
// CHECK-STRINGS-DAG: Copyright 2025 Main Program
//
// Check that the symbols are visible in the binary symbol table.
// CHECK-NM: t __aix_copyright_str
// CHECK-NM: t __llvm_copyright
//
// Ensure there’s a relocation record referencing the copyright symbol.
// CHECK-OBJDUMP-LABEL: RELOCATION RECORDS FOR [.text]
// CHECK-OBJDUMP: R_REF __aix_copyright_str
//

//=== file1.c ===
//--- file1.c
#pragma comment(copyright, "Copyright 2025 TU A")
void func1(void) {}

//=== file2.c ===
//--- file2.c
#pragma comment(copyright, "Copyright 2025 TU B")
void func2(void) {}

//=== file3.c ===
//--- file3.c
#pragma comment(copyright, "Copyright 2025 TU C")
void func3(void) {}

//=== main.c ===
//--- main.c
#pragma comment(copyright, "Copyright 2025 Main Program")
void func1(void);
void func2(void);
void func3(void);
int main(void) {
func1();
func2();
func3();
return 0;
}
35 changes: 35 additions & 0 deletions clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// REQUIRES: powerpc-registered-target, system-aix
// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 %s -triple powerpc-ibm-aix -verify
// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix -verify
// RUN: %clang_cc1 %s -DTEST_EMPTY_COPYRIGHT -triple powerpc-ibm-aix -verify

// RUN: %clang_cc1 %s -x c++ -triple powerpc-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 %s -x c++ -triple powerpc64-ibm-aix -O0 -disable-llvm-passes -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 %s -x c++ -triple powerpc-ibm-aix -verify
// RUN: %clang_cc1 %s -x c++ -triple powerpc64-ibm-aix -verify
// RUN: %clang_cc1 %s -x c++ -DTEST_EMPTY_COPYRIGHT -triple powerpc-ibm-aix -verify

#ifndef TEST_EMPTY_COPYRIGHT
// Test basic pragma comment types
#pragma comment(copyright, "@(#) Copyright")

// Test duplicate copyright - should warn and ignore
#pragma comment(copyright, "Duplicate Copyright") // expected-warning {{'#pragma comment copyright' can be specified only once per source file - ignored}}

int main() { return 0; }

// Check that both metadata sections are present
// CHECK: !aix.copyright.comment = !{![[copyright:[0-9]+]]}

// Check individual metadata content
// CHECK: ![[copyright]] = !{!"@(#) Copyright"}

#else
// Test empty copyright string - valid with no warning
#pragma comment(copyright, "") // expected-no-diagnostics

int main() { return 0; }

#endif
43 changes: 43 additions & 0 deletions llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===-- CopyrightMetadataPass.h - Lower AIX copyright metadata -*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// The CopyrightMetadataPass lowers the module-level metadata emitted by Clang
// for `#pragma comment(copyright, "...")` on AIX:
//
// !aix.copyright.comment = !{!"Copyright ..."}
//
// into an internal constant string global that is preserved across all compiler
// and linker stages. Each translation unit produces one TU-local string symbol
// (`__aix_copyright_str`), and the pass attaches `!implicit.ref` metadata to
// defined functions referencing this symbol. The PowerPC AIX backend recognizes
// this metadata and emits `.ref` directives in the XCOFF assembly, ensuring the
// copyright strings:
//
// • survive optimization and LTO,
// • are not removed by linker garbage collection, and
// • remain visible in the final binary.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_TRANSFORMS_UTILS_COPYRIGHTMETADATAPASS_H
#define LLVM_TRANSFORMS_UTILS_COPYRIGHTMETADATAPASS_H

#include "llvm/IR/PassManager.h"

namespace llvm {
class CopyrightMetadataPass : public PassInfoMixin<CopyrightMetadataPass> {
public:
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);

static bool isRequired() { return true; }
};


} // namespace llvm

#endif // LLVM_TRANSFORMS_UTILS_WYVERN_COPYRIGHTMETADATAPASS_H
1 change: 1 addition & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@
#include "llvm/Transforms/Utils/BreakCriticalEdges.h"
#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
#include "llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h"
#include "llvm/Transforms/Utils/CopyrightMetadataPass.h"
#include "llvm/Transforms/Utils/CountVisits.h"
#include "llvm/Transforms/Utils/DXILUpgrade.h"
#include "llvm/Transforms/Utils/Debugify.h"
Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/Passes/PassBuilderPipelines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
#include "llvm/Transforms/Utils/AddDiscriminators.h"
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
#include "llvm/Transforms/Utils/CopyrightMetadataPass.h"
#include "llvm/Transforms/Utils/CountVisits.h"
#include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
#include "llvm/Transforms/Utils/ExtraPassManager.h"
Expand Down Expand Up @@ -1446,6 +1447,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
const bool LTOPreLink = isLTOPreLink(LTOPhase);
ModulePassManager MPM;

// Process copyright metadata early, before any optimizations
MPM.addPass(CopyrightMetadataPass());

// Run partial inlining pass to partially inline functions that have
// large bodies.
if (RunPartialInlining)
Expand Down Expand Up @@ -2219,6 +2223,9 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level,

ModulePassManager MPM;

// Process copyright metadata at O0 before any other transformations
MPM.addPass(CopyrightMetadataPass());

// Perform pseudo probe instrumentation in O0 mode. This is for the
// consistency between different build modes. For example, a LTO build can be
// mixed with an O0 prelink and an O2 postlink. Loading a sample profile in
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ MODULE_PASS("check-debugify", NewPMCheckDebugifyPass())
MODULE_PASS("constmerge", ConstantMergePass())
MODULE_PASS("coro-cleanup", CoroCleanupPass())
MODULE_PASS("coro-early", CoroEarlyPass())
MODULE_PASS("copyright-metadata", CopyrightMetadataPass())
MODULE_PASS("cross-dso-cfi", CrossDSOCFIPass())
MODULE_PASS("ctx-instr-gen",
PGOInstrumentationGen(PGOInstrumentationType::CTXPROF))
Expand Down
Loading