Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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 @@ -1368,6 +1368,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 translation unit - 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 @@ -2526,6 +2526,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
38 changes: 38 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/PragmaKinds.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
Expand Down Expand Up @@ -1569,6 +1570,12 @@ void CodeGenModule::Release() {

EmitBackendOptionsMetadata(getCodeGenOpts());

// Emit copyright metadata
if (LoadTimeComment) {
auto *NMD = getModule().getOrInsertNamedMetadata("comment_string.loadtime");
NMD->addOperand(LoadTimeComment);
}

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

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

/// Process AIX copyright pragma and create LLVM metadata.
/// #pragma comment(copyright, "string") embed copyright
/// information into the object file's loader section.
///
/// Example: #pragma comment(copyright, "Copyright IBM Corp. 2024")
///
/// This should only be called once per translation unit.
void CodeGenModule::ProcessPragmaComment(PragmaMSCommentKind Kind,
StringRef Comment) {
// Ensure we are only processing Copyright Pragmas
assert(Kind == PCK_Copyright &&
"Unexpected pragma comment kind, ProcessPragmaComment should only be "
"called for PCK_Copyright");

// Only one copyright pragma allowed per translation unit
if (LoadTimeComment) {
return;
}

// 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,
Expand Down Expand Up @@ -7579,6 +7611,12 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
case PCK_Lib:
AddDependentLib(PCD->getArg());
break;
case PCK_Copyright:
// Skip pragmas deserialized from modules/PCHs. Process the pragma comment
// only if it originated in this TU and the target OS is AIX.
if (!PCD->isFromASTFile() && getTriple().isOSAIX())
ProcessPragmaComment(PCD->getCommentKind(), 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;

/// Metadata for copyright pragma comment (if present).
llvm::MDNode *LoadTimeComment = nullptr;

/// @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);

/// Process pragma comment
void ProcessPragmaComment(PragmaMSCommentKind Kind, StringRef Comment);

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

Expand Down
45 changes: 36 additions & 9 deletions clang/lib/Parse/ParsePragma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ struct PragmaCommentHandler : public PragmaHandler {

private:
Sema &Actions;
bool SeenCopyrightInTU = false; // TU-scoped
};

struct PragmaDetectMismatchHandler : public PragmaHandler {
Expand Down Expand Up @@ -473,7 +474,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 +597,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 @@ -3202,13 +3205,21 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
// Verify that this is one of the 5 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);

// Restrict copyright to AIX targets only. This could be applied for z/OS
// and extended with other IBM pragma comment kinds.
if (!PP.getTargetInfo().getTriple().isOSAIX() && Kind == PCK_Copyright) {
Kind = PCK_Unknown;
}

if (Kind == PCK_Unknown) {
PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind);
return;
Expand All @@ -3220,6 +3231,18 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
return;
}

// On AIX, pragma comment copyright can each appear only once in a TU.
if (Kind == PCK_Copyright) {
assert(PP.getTargetInfo().getTriple().isOSAIX() &&
"Pragma Comment Copyright is supported only on AIX");
if (SeenCopyrightInTU) {
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;
Expand All @@ -3246,6 +3269,10 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP,
return;
}

// 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: split-file %s %t

// Build the module interface to a PCM
// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
// RUN: -emit-module-interface %t/copymod.cppm -o %t/copymod.pcm

// Verify that module interface emits copyright global when compiled to IR
// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix -emit-llvm %t/copymod.cppm -o - \
// RUN: | FileCheck %s --check-prefix=CHECK-MOD
// CHECK-MOD: @__loadtime_comment_str = internal unnamed_addr constant [10 x i8] c"module me\00", section "__loadtime_comment"
// CHECK-MOD: @llvm.used = appending global {{.*}} @__loadtime_comment_str

// Compile an importing TU that uses the prebuilt module and verify that it
// does NOT re-emit the module's copyright global.
// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \
// RUN: -fprebuilt-module-path=%t -emit-llvm %t/importmod.cc -o - \
// RUN: | FileCheck %s --check-prefix=CHECK-IMPORT
// CHECK-IMPORT-NOT: @__loadtime_comment_str
// CHECK-IMPORT-NOT: c"module me\00"

//--- copymod.cppm
export module copymod;
#pragma comment(copyright, "module me")
export inline void f() {}

//--- importmod.cc
import copymod;
void g() { f(); }
34 changes: 34 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,34 @@
// 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 translation unit - ignored}}

int main() { return 0; }

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

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

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

int main() { return 0; }

#endif
2 changes: 2 additions & 0 deletions clang/test/CodeGen/lto-newpm-pipeline.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

// CHECK-FULL-O0: Running pass: VerifierPass
// CHECK-FULL-O0-NEXT: Running analysis: VerifierAnalysis
// CHECK-FULL-O0-NEXT: Running pass: LowerCommentStringPass
// CHECK-FULL-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
// CHECK-FULL-O0-NEXT: Running pass: EntryExitInstrumenterPass
// CHECK-FULL-O0-NEXT: Running pass: AlwaysInlinerPass
Expand All @@ -41,6 +42,7 @@

// CHECK-THIN-O0: Running pass: VerifierPass
// CHECK-THIN-O0-NEXT: Running analysis: VerifierAnalysis
// CHECK-THIN-O0-NEXT: Running pass: LowerCommentStringPass
// CHECK-THIN-O0-NEXT: Running analysis: InnerAnalysisManagerProxy
// CHECK-THIN-O0-NEXT: Running pass: EntryExitInstrumenterPass
// CHECK-THIN-O0-NEXT: Running pass: AlwaysInlinerPass
Expand Down
31 changes: 31 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8670,6 +8670,37 @@ denoting if the type contains a pointer.

!0 = !{!"<type-name>", i1 <contains-pointer>}

'``implicit.ref``' Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^


The ``implicit.ref`` metadata may be attached to a function or global variable
definition with a single argument that references a global object.
This is typically used when there is some implicit dependence between the
symbols that is otherwise opaque to the linker. One such example is metadata
which is accessed by a runtime with associated ``__start_<section_name>`` and
``__stop_<section_name>`` symbols.

It does not have any effect on non-XCOFF targets.

This metadata lowers to the .ref assembly directive which will add a relocation
representing an implicit reference from the section the global belongs to, to
the associated symbol. This link will keep the referenced symbol alive if the
section is not garbage collected. More than one ref node can be attached
to the same function or global variable.


Example:

.. code-block:: text

@a = global i32 1
@b = global i32 2
@c = global i32 3, section "abc", !implicit.ref !0, !implicit.ref !1
!0 = !{ptr @a}
!1 = !{ptr @b}


Module Flags Metadata
=====================

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/FixedMetadataKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)
LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 45)
LLVM_FIXED_MD_KIND(MD_implicit_ref, "implicit.ref", 46)
25 changes: 25 additions & 0 deletions llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===-- LowerCommentStringPass.h - Lower Comment string 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
//
//===----------------------------------------------------------------------===//

#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
25 changes: 25 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,31 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
DL.getIntPtrType(GO->getType()),
RangeLikeMetadataKind::AbsoluteSymbol);
}

if (GO->hasMetadata(LLVMContext::MD_implicit_ref)) {
Check(!GO->isDeclaration(),
"ref metadata must not be placed on a declaration", GO);

SmallVector<MDNode *> MDs;
GO->getMetadata(LLVMContext::MD_implicit_ref, MDs);
for (const MDNode *MD : MDs) {
Check(MD->getNumOperands() == 1, "ref metadata must have one operand",
&GV, MD);
const Metadata *Op = MD->getOperand(0).get();
const auto *VM = dyn_cast_or_null<ValueAsMetadata>(Op);
Check(VM, "ref metadata must be ValueAsMetadata", GO, MD);
if (VM) {
Check(isa<PointerType>(VM->getValue()->getType()),
"ref value must be pointer typed", GV, MD);

const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases();
Check(isa<GlobalObject>(Stripped) || isa<Constant>(Stripped),
"ref metadata must point to a GlobalObject", GO, Stripped);
Check(Stripped != GO, "values should not reference themselves", GO,
MD);
}
}
}
}

Check(!GV.hasAppendingLinkage() || isa<GlobalVariable>(GV),
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@
#include "llvm/Transforms/Utils/BreakCriticalEdges.h"
#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
#include "llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h"
#include "llvm/Transforms/Utils/LowerCommentStringPass.h"
#include "llvm/Transforms/Utils/CountVisits.h"
#include "llvm/Transforms/Utils/DXILUpgrade.h"
#include "llvm/Transforms/Utils/Debugify.h"
Expand Down
Loading