From c2e71b2d63f988db2d98f9fba533e4147cb8b746 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Mon, 15 Sep 2025 15:46:17 -0400 Subject: [PATCH 01/18] Extend the associated metadata node impl for XCOFF. Add support for the associated metadata node for AIX.. Map it to the .ref assembly directive. Placing the node on a symbol will cause the back end to emit a .ref directive in the section of the global the metadata is placed on. the .ref will refrence the associated symbol which will keep it alive if the section is not garbage collected. --- llvm/docs/LangRef.rst | 30 +++++++++++++++---- llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 19 ++++++++++++ .../PowerPC/aix-associated-metadata.ll | 23 ++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 02865f8a29c67..f86f09d130931 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -8337,6 +8337,13 @@ See :doc:`CalleeTypeMetadata`. The ``associated`` metadata may be attached to a global variable definition with a single argument that references a global object (optionally through an alias). +The metadata is often used with an explicit section consisting of valid C +identifiers so that the runtime can find the metadata section with +linker-defined encapsulation symbols ``__start_`` and +``__stop_``. + +ELF Targets +""""""""""" This metadata lowers to the ELF section flag ``SHF_LINK_ORDER`` which prevents discarding of the global variable in linker GC unless the referenced object is @@ -8354,12 +8361,6 @@ alive, but this many-to-one relationship is not representable. Moreover, if the metadata is retained while the function is discarded, the linker will report an error of a relocation referencing a discarded section. -The metadata is often used with an explicit section consisting of valid C -identifiers so that the runtime can find the metadata section with -linker-defined encapsulation symbols ``__start_`` and -``__stop_``. - -It does not have any effect on non-ELF targets. Example: @@ -8370,6 +8371,23 @@ Example: @b = internal global i32 2, comdat $a, section "abc", !associated !0 !0 = !{ptr @a} +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 associated symbol alive if the +section is not garbage collected. More than one associated node can be attached +to the same global variable. + +Example: +.. code-block:: text + + @a = global i32 1 + @b = global i32 2 + @c = global i32 3, section "abc", !associated !0, !associated !1 + !0 = !{ptr @a} + !1 = !{ptr @b} '``prof``' Metadata ^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp index 122738caa6827..6b045947c4d4f 100644 --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -305,6 +305,8 @@ class PPCAIXAsmPrinter : public PPCAsmPrinter { void emitTTypeReference(const GlobalValue *GV, unsigned Encoding) override; void emitModuleCommandLines(Module &M) override; + + void emitAssociatedMetadata(const GlobalObject *); }; } // end anonymous namespace @@ -2801,6 +2803,10 @@ void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) { // Switch to the containing csect. OutStreamer->switchSection(Csect); + if (GV->hasMetadata(LLVMContext::MD_associated)) { + emitAssociatedMetadata(GV); + } + const DataLayout &DL = GV->getDataLayout(); // Handle common and zero-initialized local symbols. @@ -3336,6 +3342,19 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV, OutStreamer->emitIntValue(0, GetSizeOfEncodedValue(Encoding)); } +void PPCAIXAsmPrinter::emitAssociatedMetadata(const GlobalObject *GO) { + SmallVector< MDNode * > MDs; + GO->getMetadata(LLVMContext::MD_associated, MDs); + assert(MDs.size() && "Expected asscoiated metadata nodes"); + + for (const MDNode *MD : MDs) { + const ValueAsMetadata *VAM = cast(MD->getOperand(0).get()); + const GlobalValue *Associated = cast(VAM->getValue()); + MCSymbol *Referenced = TM.getSymbol(Associated); + OutStreamer->emitXCOFFRefDirective(Referenced); + } +} + // Return a pass that prints the PPC assembly code for a MachineFunction to the // given output stream. static AsmPrinter * diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll new file mode 100644 index 0000000000000..edf157934b205 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll @@ -0,0 +1,23 @@ +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s + +@a = global i32 1 +@b = global i32 2 +@c = global i32 3, section "custom_section_c" +@d = global i32 4, !associated !0 +@e = constant i32 5, !associated !1, !associated !2 +@f = global i32 6, section "custom_section_f", !associated !1 + + +!0 = !{ptr @a} +!1 = !{ptr @b} +!2 = !{ptr @c} + +; CHECK: .csect d[RW] +; CHECK: .ref a[RW] + +; CHECK: .csect e[RO] +; CHECK: .ref b[RW] +; CHECK: .ref c + +; CHECK: .csect custom_section_f[RW] +; CHECK: .ref b[RW] From a48a304508ff24ce97124e4cc056b411d9085e46 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Tue, 16 Sep 2025 09:37:13 -0400 Subject: [PATCH 02/18] Run clang-format over changes. --- llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp index 6b045947c4d4f..14ed96bc07b0e 100644 --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -3343,7 +3343,7 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV, } void PPCAIXAsmPrinter::emitAssociatedMetadata(const GlobalObject *GO) { - SmallVector< MDNode * > MDs; + SmallVector MDs; GO->getMetadata(LLVMContext::MD_associated, MDs); assert(MDs.size() && "Expected asscoiated metadata nodes"); From 28feedef41365a18bf67d68c52319cbb7f097365 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Thu, 18 Sep 2025 10:38:07 -0400 Subject: [PATCH 03/18] Test .ref impl with data sections disabled. --- .../PowerPC/aix-associated-metadata.ll | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll index edf157934b205..8a5c36fd2b4b8 100644 --- a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll +++ b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll @@ -1,4 +1,5 @@ ; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s +; RUN: llc -data-sections=false -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck -check-prefix=NODATA %s @a = global i32 1 @b = global i32 2 @@ -21,3 +22,21 @@ ; CHECK: .csect custom_section_f[RW] ; CHECK: .ref b[RW] + +; NODATA: .csect .data[RW] +; NODATA-NOT: .csect +; NODATA: .globl a +; NODATA-NOT: .csect +; NODATA: .globl b +; NODATA: .csect custom_section_c[RW] +; NODATA: .globl c +; NODATA: .csect .data[RW] +; NODATA: .ref a +; NODATA: .globl d +; NODATA: .csect .rodata[RO] +; NODATA: .ref b +; NODATA: .ref c +; NODATA: .globl e +; NODATA: .csect custom_section_f[RW] +; NODATA: .ref b +; NODATA: .globl f From bae536431eef172553b7993245926da64acb13d9 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Thu, 18 Sep 2025 11:03:20 -0400 Subject: [PATCH 04/18] Check multiple associated metadata nodes in the verifier. --- llvm/lib/IR/Verifier.cpp | 47 ++++++++++++++--------- llvm/test/Verifier/associated-multiple.ll | 13 +++++++ 2 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 llvm/test/Verifier/associated-multiple.ll diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index a1e14d8f25bf7..983db82a1c22f 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -740,25 +740,36 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) { "Global is external, but doesn't have external or weak linkage!", &GV); if (const GlobalObject *GO = dyn_cast(&GV)) { - if (const MDNode *Associated = - GO->getMetadata(LLVMContext::MD_associated)) { - Check(Associated->getNumOperands() == 1, - "associated metadata must have one operand", &GV, Associated); - const Metadata *Op = Associated->getOperand(0).get(); - Check(Op, "associated metadata must have a global value", GO, Associated); - - const auto *VM = dyn_cast_or_null(Op); - Check(VM, "associated metadata must be ValueAsMetadata", GO, Associated); - if (VM) { - Check(isa(VM->getValue()->getType()), - "associated value must be pointer typed", GV, Associated); - - const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases(); - Check(isa(Stripped) || isa(Stripped), - "associated metadata must point to a GlobalObject", GO, Stripped); - Check(Stripped != GO, - "global values should not associate to themselves", GO, + if (GO->hasMetadata(LLVMContext::MD_associated)) { + SmallVector MDs; + GO->getMetadata(LLVMContext::MD_associated, MDs); + + if (TT.isOSBinFormatELF()) + Check(MDs.size() == 1, "only a single associated metadata is supported", + &GV); + + for (const MDNode *Associated : MDs) { + Check(Associated->getNumOperands() == 1, + "associated metadata must have one operand", &GV, Associated); + const Metadata *Op = Associated->getOperand(0).get(); + Check(Op, "associated metadata must have a global value", GO, Associated); + + const auto *VM = dyn_cast_or_null(Op); + Check(VM, "associated metadata must be ValueAsMetadata", GO, + Associated); + if (VM) { + Check(isa(VM->getValue()->getType()), + "associated value must be pointer typed", GV, Associated); + + const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases(); + Check(isa(Stripped) || isa(Stripped), + "associated metadata must point to a GlobalObject", GO, + Stripped); + Check(Stripped != GO, + "global values should not associate to themselves", GO, + Associated); + } } } diff --git a/llvm/test/Verifier/associated-multiple.ll b/llvm/test/Verifier/associated-multiple.ll new file mode 100644 index 0000000000000..5a640fbe5efed --- /dev/null +++ b/llvm/test/Verifier/associated-multiple.ll @@ -0,0 +1,13 @@ +; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s + +target triple = "unknown-unknown-linux-gnu" + +@a = global i32 1 +@b = global i32 2 +@c = global i32 3, !associated !0, !associated !1 + +!0 = !{ptr @a} +!1 = !{ptr @b} + +; CHECK: only a single associated metadata is supported +; CHECK: ptr @c From 0b4808cc168827bca665fdfff8cd70b2d670b4df Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Thu, 18 Sep 2025 16:31:35 -0400 Subject: [PATCH 05/18] Add testing of the integrated assembler path. --- .../PowerPC/aix-associated-metadata.ll | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll index 8a5c36fd2b4b8..8613b32f06e05 100644 --- a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll +++ b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll @@ -1,6 +1,12 @@ ; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s ; RUN: llc -data-sections=false -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck -check-prefix=NODATA %s +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -filetype=obj -o %t.o < %s +; RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck -check-prefix=OBJ %s + +; RUN: llc -data-sections=false -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -filetype=obj -o %t.o < %s +; RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck -check-prefix=OBJ-NODATA %s + @a = global i32 1 @b = global i32 2 @c = global i32 3, section "custom_section_c" @@ -40,3 +46,32 @@ ; NODATA: .csect custom_section_f[RW] ; NODATA: .ref b ; NODATA: .globl f + +; OBJ: Disassembly of section .text: + +; OBJ: e[RO]: +; OBJ: R_REF {{.*}} b[RW] +; OBJ: R_REF {{.*}} c + +; OBJ: Disassembly of section .data: +; OBJ: a[RW]: +; OBJ: b[RW]: +; OBJ: c: +; OBJ: d[RW]: +; OBJ: R_REF {{.*}} a[RW] +; OBJ: f: +; OBJ: R_REF {{.*}} b[RW] + +; OBJ-NODATA: Disassembly of section .text: +; OBJ-NODATA: e: +; OBJ-NODATA: R_REF {{.*}} b +; OBJ-NODATA: R_REF {{.*}} c + +; OBJ-NODATA: Disassembly of section .data: +; OBJ-NODATA: a: +; OBJ-NODATA: R_REF {{.*}} a +; OBJ-NODATA: b: +; OBJ-NODATA: d: +; OBJ-NODATA: c: +; OBJ-NODATA: f: +; OBJ-NODATA: R_REF {{.*}} b From ecf915e61670c9631baaf2a52b97e402dd2e08bf Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Wed, 24 Sep 2025 14:27:56 -0400 Subject: [PATCH 06/18] Add a new metadata node rather then piggybacking on associated. --- llvm/docs/LangRef.rst | 60 +++++++++------- llvm/include/llvm/IR/FixedMetadataKinds.def | 1 + llvm/lib/IR/Verifier.cpp | 71 +++++++++++-------- llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 14 ++-- .../PowerPC/aix-associated-metadata.ll | 6 +- llvm/test/Verifier/associated-multiple.ll | 13 ---- llvm/test/Verifier/ref.ll | 28 ++++++++ 7 files changed, 117 insertions(+), 76 deletions(-) delete mode 100644 llvm/test/Verifier/associated-multiple.ll create mode 100644 llvm/test/Verifier/ref.ll diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index f86f09d130931..2ad9a9b362f19 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -8337,13 +8337,6 @@ See :doc:`CalleeTypeMetadata`. The ``associated`` metadata may be attached to a global variable definition with a single argument that references a global object (optionally through an alias). -The metadata is often used with an explicit section consisting of valid C -identifiers so that the runtime can find the metadata section with -linker-defined encapsulation symbols ``__start_`` and -``__stop_``. - -ELF Targets -""""""""""" This metadata lowers to the ELF section flag ``SHF_LINK_ORDER`` which prevents discarding of the global variable in linker GC unless the referenced object is @@ -8361,6 +8354,12 @@ alive, but this many-to-one relationship is not representable. Moreover, if the metadata is retained while the function is discarded, the linker will report an error of a relocation referencing a discarded section. +The metadata is often used with an explicit section consisting of valid C +identifiers so that the runtime can find the metadata section with +linker-defined encapsulation symbols ``__start_`` and +``__stop_``. + +It does not have any effect on non-ELF targets. Example: @@ -8371,23 +8370,6 @@ Example: @b = internal global i32 2, comdat $a, section "abc", !associated !0 !0 = !{ptr @a} -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 associated symbol alive if the -section is not garbage collected. More than one associated node can be attached -to the same global variable. - -Example: -.. code-block:: text - - @a = global i32 1 - @b = global i32 2 - @c = global i32 3, section "abc", !associated !0, !associated !1 - !0 = !{ptr @a} - !1 = !{ptr @b} '``prof``' Metadata ^^^^^^^^^^^^^^^^^^^ @@ -8688,6 +8670,36 @@ denoting if the type contains a pointer. !0 = !{!"", i1 } +'``ref``' Metadata +^^^^^^^^^^^^^^^^^^ + +The ``ref`` metadata may be attached to a global variable definition with a +single argument that references a global object. The metadata is lowered to a +.ref directive which will emit a relocation introducing an explicit dependence +to the referenced symbol. 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_`` and ``__stop_`` symbols. + +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 associated symbol alive if the +section is not garbage collected. More than one associated node can be attached +to the same global variable. + +It does not have any effect on non-XCOFF targets. + +Example: + +.. code-block:: text + + @a = global i32 1 + @b = global i32 2 + @c = global i32 3, section "abc", !ref !0, !ref !1 + !0 = !{ptr @a} + !1 = !{ptr @b} + + Module Flags Metadata ===================== diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index 74746cced6f23..b65a5bb8ab272 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -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_ref, "ref", 46) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 983db82a1c22f..ea37bbcc13e11 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -740,36 +740,25 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) { "Global is external, but doesn't have external or weak linkage!", &GV); if (const GlobalObject *GO = dyn_cast(&GV)) { - if (GO->hasMetadata(LLVMContext::MD_associated)) { - SmallVector MDs; - GO->getMetadata(LLVMContext::MD_associated, MDs); - - if (TT.isOSBinFormatELF()) - Check(MDs.size() == 1, "only a single associated metadata is supported", - &GV); - - for (const MDNode *Associated : MDs) { - Check(Associated->getNumOperands() == 1, - "associated metadata must have one operand", &GV, Associated); - const Metadata *Op = Associated->getOperand(0).get(); - Check(Op, "associated metadata must have a global value", GO, + if (const MDNode *Associated = + GO->getMetadata(LLVMContext::MD_associated)) { + Check(Associated->getNumOperands() == 1, + "associated metadata must have one operand", &GV, Associated); + const Metadata *Op = Associated->getOperand(0).get(); + Check(Op, "associated metadata must have a global value", GO, Associated); + + const auto *VM = dyn_cast_or_null(Op); + Check(VM, "associated metadata must be ValueAsMetadata", GO, Associated); + if (VM) { + Check(isa(VM->getValue()->getType()), + "associated value must be pointer typed", GV, Associated); + + const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases(); + Check(isa(Stripped) || isa(Stripped), + "associated metadata must point to a GlobalObject", GO, Stripped); + Check(Stripped != GO, + "global values should not associate to themselves", GO, Associated); - - const auto *VM = dyn_cast_or_null(Op); - Check(VM, "associated metadata must be ValueAsMetadata", GO, - Associated); - if (VM) { - Check(isa(VM->getValue()->getType()), - "associated value must be pointer typed", GV, Associated); - - const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases(); - Check(isa(Stripped) || isa(Stripped), - "associated metadata must point to a GlobalObject", GO, - Stripped); - Check(Stripped != GO, - "global values should not associate to themselves", GO, - Associated); - } } } @@ -780,6 +769,30 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) { DL.getIntPtrType(GO->getType()), RangeLikeMetadataKind::AbsoluteSymbol); } + + if (GO->hasMetadata(LLVMContext::MD_ref)) { + SmallVector MDs; + GO->getMetadata(LLVMContext::MD_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(); + Check(Op, "ref metadata must have a global value", GO, MD); + + const auto *VM = dyn_cast_or_null(Op); + Check(VM, "ref metadata must be ValueAsMetadata", GO, MD); + if (VM) { + Check(isa(VM->getValue()->getType()), + "ref value must be pointer typed", GV, MD); + + const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases(); + Check(isa(Stripped) || isa(Stripped), + "ref metadata must point to a GlobalObject", GO, Stripped); + Check(Stripped != GO, "values should not reference themselves", GO, + MD); + } + } + } } Check(!GV.hasAppendingLinkage() || isa(GV), diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp index 14ed96bc07b0e..8e6a0a4f2e48b 100644 --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -306,7 +306,7 @@ class PPCAIXAsmPrinter : public PPCAsmPrinter { void emitModuleCommandLines(Module &M) override; - void emitAssociatedMetadata(const GlobalObject *); + void emitRefMetadata(const GlobalObject *); }; } // end anonymous namespace @@ -2803,8 +2803,8 @@ void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) { // Switch to the containing csect. OutStreamer->switchSection(Csect); - if (GV->hasMetadata(LLVMContext::MD_associated)) { - emitAssociatedMetadata(GV); + if (GV->hasMetadata(LLVMContext::MD_ref)) { + emitRefMetadata(GV); } const DataLayout &DL = GV->getDataLayout(); @@ -3342,15 +3342,15 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV, OutStreamer->emitIntValue(0, GetSizeOfEncodedValue(Encoding)); } -void PPCAIXAsmPrinter::emitAssociatedMetadata(const GlobalObject *GO) { +void PPCAIXAsmPrinter::emitRefMetadata(const GlobalObject *GO) { SmallVector MDs; - GO->getMetadata(LLVMContext::MD_associated, MDs); + GO->getMetadata(LLVMContext::MD_ref, MDs); assert(MDs.size() && "Expected asscoiated metadata nodes"); for (const MDNode *MD : MDs) { const ValueAsMetadata *VAM = cast(MD->getOperand(0).get()); - const GlobalValue *Associated = cast(VAM->getValue()); - MCSymbol *Referenced = TM.getSymbol(Associated); + const GlobalValue *GV = cast(VAM->getValue()); + MCSymbol *Referenced = TM.getSymbol(GV); OutStreamer->emitXCOFFRefDirective(Referenced); } } diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll index 8613b32f06e05..1eb7eaa3c58d2 100644 --- a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll +++ b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll @@ -10,9 +10,9 @@ @a = global i32 1 @b = global i32 2 @c = global i32 3, section "custom_section_c" -@d = global i32 4, !associated !0 -@e = constant i32 5, !associated !1, !associated !2 -@f = global i32 6, section "custom_section_f", !associated !1 +@d = global i32 4, !ref !0 +@e = constant i32 5, !ref !1, !ref!2 +@f = global i32 6, section "custom_section_f", !ref !1 !0 = !{ptr @a} diff --git a/llvm/test/Verifier/associated-multiple.ll b/llvm/test/Verifier/associated-multiple.ll deleted file mode 100644 index 5a640fbe5efed..0000000000000 --- a/llvm/test/Verifier/associated-multiple.ll +++ /dev/null @@ -1,13 +0,0 @@ -; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s - -target triple = "unknown-unknown-linux-gnu" - -@a = global i32 1 -@b = global i32 2 -@c = global i32 3, !associated !0, !associated !1 - -!0 = !{ptr @a} -!1 = !{ptr @b} - -; CHECK: only a single associated metadata is supported -; CHECK: ptr @c diff --git a/llvm/test/Verifier/ref.ll b/llvm/test/Verifier/ref.ll new file mode 100644 index 0000000000000..5f5dca0f965d6 --- /dev/null +++ b/llvm/test/Verifier/ref.ll @@ -0,0 +1,28 @@ +; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s + +@a = global i32 1, !ref !0 +@b = global i32 2, !ref !1 +@c = global i32 3, !ref !1, !ref !2 +@d = global i32 4, !ref !3 + +!0 = !{i32 1} +!1 = !{ptr @b} +!2 = !{!"Hello World!"} +!3 = !{ptr @c, ptr @a} + +; CHECK: ref value must be pointer typed +; CHECK: ptr @a +; CHECK: !0 = !{i32 1} + +; CHECK: values should not reference themselves +; CHECK: ptr @b +; CHECK: !1 = !{ptr @b} + +; CHECK: ref metadata must be ValueAsMetadata +; CHECK: ptr @c +; CHECK: !2 = !{!"Hello World!"} + +; CHECK: ref metadata must have one operand +; CHECK: ptr @d +; CHECK: !3 = !{ptr @c, ptr @a} + From d9ec86e472c0f0efb6fc2d9c9a6f30b5d3868704 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Tue, 30 Sep 2025 11:04:49 -0400 Subject: [PATCH 07/18] Address review comments and minor cleanup. * Added support for emitting ref metadata on functions. * Added testing for ref metadata on functions. * Fixed up lang ref description. * Renamed tests to match metadatas nameing. --- llvm/docs/LangRef.rst | 22 ++++++------- llvm/lib/IR/Verifier.cpp | 2 -- llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 8 ++++- llvm/test/CodeGen/PowerPC/aix-func-ref.ll | 31 +++++++++++++++++++ ...ciated-metadata.ll => aix-ref-metadata.ll} | 0 llvm/test/Verifier/ref-func.ll | 12 +++++++ llvm/test/Verifier/ref.ll | 1 - 7 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 llvm/test/CodeGen/PowerPC/aix-func-ref.ll rename llvm/test/CodeGen/PowerPC/{aix-associated-metadata.ll => aix-ref-metadata.ll} (100%) create mode 100644 llvm/test/Verifier/ref-func.ll diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 2ad9a9b362f19..33b2fba50011b 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -8673,21 +8673,21 @@ denoting if the type contains a pointer. '``ref``' Metadata ^^^^^^^^^^^^^^^^^^ -The ``ref`` metadata may be attached to a global variable definition with a -single argument that references a global object. The metadata is lowered to a -.ref directive which will emit a relocation introducing an explicit dependence -to the referenced symbol. 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_`` and ``__stop_`` symbols. +The ``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_`` and +``__stop_`` 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 associated symbol alive if the -section is not garbage collected. More than one associated node can be attached -to the same global variable. +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. -It does not have any effect on non-XCOFF targets. Example: diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index ea37bbcc13e11..5b25c7d2f647c 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -777,8 +777,6 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) { Check(MD->getNumOperands() == 1, "ref metadata must have one operand", &GV, MD); const Metadata *Op = MD->getOperand(0).get(); - Check(Op, "ref metadata must have a global value", GO, MD); - const auto *VM = dyn_cast_or_null(Op); Check(VM, "ref metadata must be ValueAsMetadata", GO, MD); if (VM) { diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp index 8e6a0a4f2e48b..e4d113ca660e5 100644 --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -2899,10 +2899,16 @@ void PPCAIXAsmPrinter::emitFunctionEntryLabel() { if (!TM.getFunctionSections() || MF->getFunction().hasSection()) PPCAsmPrinter::emitFunctionEntryLabel(); + const Function *F = &MF->getFunction(); + // Emit aliasing label for function entry point label. - for (const GlobalAlias *Alias : GOAliasMap[&MF->getFunction()]) + for (const GlobalAlias *Alias : GOAliasMap[F]) OutStreamer->emitLabel( getObjFileLowering().getFunctionEntryPointSymbol(Alias, TM)); + + if (F->hasMetadata(LLVMContext::MD_ref)) { + emitRefMetadata(F); + } } void PPCAIXAsmPrinter::emitPGORefs(Module &M) { diff --git a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll new file mode 100644 index 0000000000000..d4a47fdafa781 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll @@ -0,0 +1,31 @@ +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | \ +; RUN: FileCheck %s -check-prefixes=NOFSECTS,CHECK + +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff --function-sections < %s | \ +; RUN: FileCheck %s -check-prefixes=FSECTS,CHECK + +@a = global i32 1 +@b = global i32 2 +@c = global i32 3 + +define i32 @foo() !ref !0 { + ret i32 0 +} + +define i32 @bar() !ref !1 !ref !2 { + ret i32 0 +} + +!0 = !{ptr @a} +!1 = !{ptr @b} +!2 = !{ptr @c} + +; NOFSECTS: .foo: +; FSECTS: .csect .foo[PR] +; CHECK: .ref a[RW] + +; NOFSECTS: .bar: +; FSECTS: .csect .bar[PR] +; CHECK: .ref b[RW] +; CHECK: .ref c[RW] + diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll similarity index 100% rename from llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll rename to llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll diff --git a/llvm/test/Verifier/ref-func.ll b/llvm/test/Verifier/ref-func.ll new file mode 100644 index 0000000000000..9a7c7b5d3405e --- /dev/null +++ b/llvm/test/Verifier/ref-func.ll @@ -0,0 +1,12 @@ +; RUN: llvm-as < %s -o /dev/null 2>&1 + +@a = global i32 1 +@b = global i32 2 +@c = global i32 3, !ref !0 + +define i32 @foo() !ref !1 { + ret i32 0 +} + +!0 = !{ptr @a} +!1 = !{ptr @b} diff --git a/llvm/test/Verifier/ref.ll b/llvm/test/Verifier/ref.ll index 5f5dca0f965d6..c00b4bf7577ab 100644 --- a/llvm/test/Verifier/ref.ll +++ b/llvm/test/Verifier/ref.ll @@ -25,4 +25,3 @@ ; CHECK: ref metadata must have one operand ; CHECK: ptr @d ; CHECK: !3 = !{ptr @c, ptr @a} - From 7228f5d9312d9b14f513a4310c1d6879b65d0ed0 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Wed, 1 Oct 2025 11:32:40 -0400 Subject: [PATCH 08/18] Add object file testing of ref reloc in functions. --- llvm/test/CodeGen/PowerPC/aix-func-ref.ll | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll index d4a47fdafa781..72b24862b751f 100644 --- a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll +++ b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll @@ -4,6 +4,12 @@ ; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff --function-sections < %s | \ ; RUN: FileCheck %s -check-prefixes=FSECTS,CHECK +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -filetype=obj -o %t.o < %s +; RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck -check-prefix=OBJ %s + +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff --function-sections -filetype=obj -o %t.o < %s +; RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck -check-prefix=FSECTOBJ %s + @a = global i32 1 @b = global i32 2 @c = global i32 3 @@ -29,3 +35,21 @@ define i32 @bar() !ref !1 !ref !2 { ; CHECK: .ref b[RW] ; CHECK: .ref c[RW] +; OBJ: Disassembly of section .text: +; OBJ: .foo: +; OBJ: li 3, 0 +; OBJ: R_REF {{.*}} a[RW] +; OBJ: R_REF {{.*}} b[RW] +; OBJ: R_REF {{.*}} c[RW] +; OBJ: blr +; OBJ: .bar + +; FSECTOBJ: .foo[PR]: +; FSECTOBJ: li 3, 0 +; FSECTOBJ: R_REF {{.*}} a[RW] +; FSECTOBJ: blr +; FSECTOBJ: .bar[PR]: +; FSECTOBJ: li 3, 0 +; FSECTOBJ: R_REF {{.*}} b[RW] +; FSECTOBJ: R_REF {{.*}} c[RW] +; FSECTOBJ: blr From 84f0277ed71e63d250c01b2e4622e612e6194ab9 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Wed, 1 Oct 2025 13:42:36 -0400 Subject: [PATCH 09/18] Verify ref metadata node is not placed on a declaration. --- llvm/lib/IR/Verifier.cpp | 3 +++ llvm/test/Verifier/ref.ll | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 5b25c7d2f647c..ba370252012fc 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -771,6 +771,9 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) { } if (GO->hasMetadata(LLVMContext::MD_ref)) { + Check(!GO->isDeclaration(), + "ref metadata must not be placed on a declaration", GO); + SmallVector MDs; GO->getMetadata(LLVMContext::MD_ref, MDs); for (const MDNode *MD : MDs) { diff --git a/llvm/test/Verifier/ref.ll b/llvm/test/Verifier/ref.ll index c00b4bf7577ab..d3b5169062662 100644 --- a/llvm/test/Verifier/ref.ll +++ b/llvm/test/Verifier/ref.ll @@ -4,6 +4,7 @@ @b = global i32 2, !ref !1 @c = global i32 3, !ref !1, !ref !2 @d = global i32 4, !ref !3 +@e = external global i32, !ref !1 !0 = !{i32 1} !1 = !{ptr @b} @@ -25,3 +26,6 @@ ; CHECK: ref metadata must have one operand ; CHECK: ptr @d ; CHECK: !3 = !{ptr @c, ptr @a} + +; CHECK: ref metadata must not be placed on a declaration +; CHECK: @e From c9ed9d9e50aa59cca357607ac78aa554433e4e8d Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Mon, 6 Oct 2025 14:56:05 -0400 Subject: [PATCH 10/18] Rename metadata from ref to implicit.ref. --- llvm/docs/LangRef.rst | 9 +++++---- llvm/include/llvm/IR/FixedMetadataKinds.def | 2 +- llvm/lib/IR/Verifier.cpp | 4 ++-- llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 6 +++--- llvm/test/CodeGen/PowerPC/aix-func-ref.ll | 4 ++-- llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll | 6 +++--- llvm/test/Verifier/ref-func.ll | 4 ++-- llvm/test/Verifier/ref.ll | 10 +++++----- 8 files changed, 23 insertions(+), 22 deletions(-) diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 33b2fba50011b..518b0f9d48a54 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -8670,10 +8670,11 @@ denoting if the type contains a pointer. !0 = !{!"", i1 } -'``ref``' Metadata -^^^^^^^^^^^^^^^^^^ +'``implicit.ref``' Metadata +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + -The ``ref`` metadata may be attached to a function or global variable +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 @@ -8695,7 +8696,7 @@ Example: @a = global i32 1 @b = global i32 2 - @c = global i32 3, section "abc", !ref !0, !ref !1 + @c = global i32 3, section "abc", !implicit.ref !0, !implicit.ref !1 !0 = !{ptr @a} !1 = !{ptr @b} diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index b65a5bb8ab272..01d22bdd7c996 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -57,4 +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_ref, "ref", 46) +LLVM_FIXED_MD_KIND(MD_implicit_ref, "implicit.ref", 46) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index ba370252012fc..6f2b056be492e 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -770,12 +770,12 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) { RangeLikeMetadataKind::AbsoluteSymbol); } - if (GO->hasMetadata(LLVMContext::MD_ref)) { + if (GO->hasMetadata(LLVMContext::MD_implicit_ref)) { Check(!GO->isDeclaration(), "ref metadata must not be placed on a declaration", GO); SmallVector MDs; - GO->getMetadata(LLVMContext::MD_ref, 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); diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp index e4d113ca660e5..718d51c5a0673 100644 --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -2803,7 +2803,7 @@ void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) { // Switch to the containing csect. OutStreamer->switchSection(Csect); - if (GV->hasMetadata(LLVMContext::MD_ref)) { + if (GV->hasMetadata(LLVMContext::MD_implicit_ref)) { emitRefMetadata(GV); } @@ -2906,7 +2906,7 @@ void PPCAIXAsmPrinter::emitFunctionEntryLabel() { OutStreamer->emitLabel( getObjFileLowering().getFunctionEntryPointSymbol(Alias, TM)); - if (F->hasMetadata(LLVMContext::MD_ref)) { + if (F->hasMetadata(LLVMContext::MD_implicit_ref)) { emitRefMetadata(F); } } @@ -3350,7 +3350,7 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV, void PPCAIXAsmPrinter::emitRefMetadata(const GlobalObject *GO) { SmallVector MDs; - GO->getMetadata(LLVMContext::MD_ref, MDs); + GO->getMetadata(LLVMContext::MD_implicit_ref, MDs); assert(MDs.size() && "Expected asscoiated metadata nodes"); for (const MDNode *MD : MDs) { diff --git a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll index 72b24862b751f..5cb0575f370e1 100644 --- a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll +++ b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll @@ -14,11 +14,11 @@ @b = global i32 2 @c = global i32 3 -define i32 @foo() !ref !0 { +define i32 @foo() !implicit.ref !0 { ret i32 0 } -define i32 @bar() !ref !1 !ref !2 { +define i32 @bar() !implicit.ref !1 !implicit.ref !2 { ret i32 0 } diff --git a/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll index 1eb7eaa3c58d2..7dc9466a7ba9b 100644 --- a/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll +++ b/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll @@ -10,9 +10,9 @@ @a = global i32 1 @b = global i32 2 @c = global i32 3, section "custom_section_c" -@d = global i32 4, !ref !0 -@e = constant i32 5, !ref !1, !ref!2 -@f = global i32 6, section "custom_section_f", !ref !1 +@d = global i32 4, !implicit.ref !0 +@e = constant i32 5, !implicit.ref !1, !implicit.ref!2 +@f = global i32 6, section "custom_section_f", !implicit.ref !1 !0 = !{ptr @a} diff --git a/llvm/test/Verifier/ref-func.ll b/llvm/test/Verifier/ref-func.ll index 9a7c7b5d3405e..e3cddbb5ee20f 100644 --- a/llvm/test/Verifier/ref-func.ll +++ b/llvm/test/Verifier/ref-func.ll @@ -2,9 +2,9 @@ @a = global i32 1 @b = global i32 2 -@c = global i32 3, !ref !0 +@c = global i32 3, !implicit.ref !0 -define i32 @foo() !ref !1 { +define i32 @foo() !implicit.ref !1 { ret i32 0 } diff --git a/llvm/test/Verifier/ref.ll b/llvm/test/Verifier/ref.ll index d3b5169062662..58e195320583e 100644 --- a/llvm/test/Verifier/ref.ll +++ b/llvm/test/Verifier/ref.ll @@ -1,10 +1,10 @@ ; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s -@a = global i32 1, !ref !0 -@b = global i32 2, !ref !1 -@c = global i32 3, !ref !1, !ref !2 -@d = global i32 4, !ref !3 -@e = external global i32, !ref !1 +@a = global i32 1, !implicit.ref !0 +@b = global i32 2, !implicit.ref !1 +@c = global i32 3, !implicit.ref !1, !implicit.ref !2 +@d = global i32 4, !implicit.ref !3 +@e = external global i32, !implicit.ref !1 !0 = !{i32 1} !1 = !{ptr @b} From 4d6f3655321ba10a8aa2089725aaf2833fb49cb5 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Wed, 29 Oct 2025 14:15:52 -0400 Subject: [PATCH 11/18] Propagate implicit.ref metadata when inlining. Propagate the metadata from callee to caller on inlining. Also adds a test verifying that the metadata is maintained when cloning a function. --- llvm/lib/Transforms/Utils/InlineFunction.cpp | 9 ++++ .../function-specialization-implicit-ref.ll | 43 ++++++++++++++++ .../Transforms/Inline/inline-ref-metadata.ll | 50 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll create mode 100644 llvm/test/Transforms/Inline/inline-ref-metadata.ll diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp index f49fbf8807bac..aa902f687d8aa 100644 --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -2830,6 +2830,15 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI, // Propagate metadata on the callsite if necessary. PropagateCallSiteMetadata(CB, FirstNewBlock, Caller->end()); + // Propagate implicit ref metadata. + if (CalledFunc->hasMetadata(LLVMContext::MD_implicit_ref)) { + SmallVector MDs; + CalledFunc->getMetadata(LLVMContext::MD_implicit_ref, MDs); + for (MDNode *MD : MDs) { + Caller->addMetadata(LLVMContext::MD_implicit_ref, *MD); + } + } + // Register any cloned assumptions. if (IFI.GetAssumptionCache) for (BasicBlock &NewBlock : diff --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll new file mode 100644 index 0000000000000..1e38e5c80fa5f --- /dev/null +++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll @@ -0,0 +1,43 @@ +; RUN: opt -passes="ipsccp" -S -force-specialization < %s 2>&1 | FileCheck %s + +@a = global i32 1 + +define i64 @main(i64 %x, i1 %flag) { +entry: + br i1 %flag, label %plus, label %minus + +plus: + %tmp0 = call i64 @compute(i64 %x, ptr @plus) + br label %merge + +minus: + %tmp1 = call i64 @compute(i64 %x, ptr @minus) + br label %merge + +merge: + %tmp2 = phi i64 [ %tmp0, %plus ], [ %tmp1, %minus] + ret i64 %tmp2 +} + +define internal i64 @compute(i64 %x, ptr %binop) !implicit.ref !0 { +entry: + %tmp0 = call i64 %binop(i64 %x) + ret i64 %tmp0 +} + +define internal i64 @plus(i64 %x) { +entry: + %tmp0 = add i64 %x, 1 + ret i64 %tmp0 +} + +define internal i64 @minus(i64 %x) { +entry: + %tmp0 = sub i64 %x, 1 + ret i64 %tmp0 +} + +!0 = !{ptr @a} + +; CHECK: @compute.specialized.1(i64 %x, ptr %binop) !implicit.ref !0 +; CHECK: @compute.specialized.2(i64 %x, ptr %binop) !implicit.ref !0 diff --git a/llvm/test/Transforms/Inline/inline-ref-metadata.ll b/llvm/test/Transforms/Inline/inline-ref-metadata.ll new file mode 100644 index 0000000000000..a252ee4ec36ce --- /dev/null +++ b/llvm/test/Transforms/Inline/inline-ref-metadata.ll @@ -0,0 +1,50 @@ +; RUN: opt < %s -passes='cgscc(inline)' -S | FileCheck %s + +@a = global i32 1 +@b = global i32 2 +@c = global double 3.141593e+00 + +define i32 @callee1() !implicit.ref !0 { + ret i32 0 +} + +define i32 @callee2() !implicit.ref !1 { + ret i32 1 +} + +define i32 @callee3() { + %i = call i32 @callee2() + ret i32 %i +} +; CHECK: @callee3() !implicit.ref !1 + +define i32 @caller1() { + %i = call i32 @callee1() + ret i32 %i +} +; CHECK: @caller1() !implicit.ref !0 + +define i32 @caller2() !implicit.ref !2 { + %i = call i32 @callee1() + ret i32 %i +} +; CHECK: @caller2() !implicit.ref !2 !implicit.ref !0 + +define i32 @caller3() { + %i = call i32 @caller4() + ret i32 %i +} +; CHECK: @caller3() !implicit.ref !0 !implicit.ref !1 + +define i32 @caller4() { + %a = call i32 @callee1() + %b = call i32 @callee2() + %add = add i32 %a, %b + ret i32 %add +} +; CHECK: @caller4() !implicit.ref !0 !implicit.ref !1 + +!0 = !{ptr @a} +!1 = !{ptr @b} +!2 = !{ptr @c} + From d216a07e5abc61246ee82c77c900afa6706cf553 Mon Sep 17 00:00:00 2001 From: Tony Varghese Date: Sat, 18 Oct 2025 11:39:43 -0400 Subject: [PATCH 12/18] [PowerPC][AIX] Support #pragma comment copyright for AIX --- .../clang/Basic/DiagnosticParseKinds.td | 3 + clang/include/clang/Basic/PragmaKinds.h | 3 +- clang/lib/AST/TextNodeDumper.cpp | 3 + clang/lib/CodeGen/CodeGenModule.cpp | 28 ++++ clang/lib/CodeGen/CodeGenModule.h | 5 + clang/lib/Parse/ParsePragma.cpp | 26 +++- .../pragma-comment-copyright-aix-multi-lto.c | 110 +++++++++++++ .../PowerPC/pragma-comment-copyright-aix.c | 35 +++++ .../Transforms/Utils/CopyrightMetadataPass.h | 43 ++++++ llvm/lib/Passes/PassBuilder.cpp | 1 + llvm/lib/Passes/PassBuilderPipelines.cpp | 7 + llvm/lib/Passes/PassRegistry.def | 1 + llvm/lib/Transforms/Utils/CMakeLists.txt | 1 + .../Utils/CopyrightMetadataPass.cpp | 144 ++++++++++++++++++ .../PowerPC/pragma-comment-copyright-aix.ll | 51 +++++++ .../CopyrightMetadata/copyright-metadata.ll | 35 +++++ 16 files changed, 493 insertions(+), 3 deletions(-) create mode 100644 clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c create mode 100644 clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c create mode 100644 llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h create mode 100644 llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp create mode 100644 llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll create mode 100644 llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 9401377002223..fcbb1ab555619 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -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; +def warn_pragma_comment_once : Warning<"'#pragma comment %0' " + "can be specified only once per source file - ignored">, + InGroup; // - #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 7bc0404db1bee..c58c33f95c193 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -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()) diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index a6a1b84e278b9..3da6be86d216c 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1569,6 +1569,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()); @@ -3402,6 +3410,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, @@ -7579,6 +7604,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: diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index a253bcda2d06c..e6cd9ca10f344 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -587,6 +587,9 @@ class CodeGenModule : public CodeGenTypeCache { /// A vector of metadata strings for dependent libraries for ELF. SmallVector ELFDependentLibraries; + /// A vector of metadata strings for copyright comment for AIX + SmallVector AIXCopyrightComment; + /// @name Cache for Objective-C runtime types /// @{ @@ -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); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 7c2b9280f0b76..655e64674b866 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -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(Actions); PP.AddPragmaHandler(MSCommentHandler.get()); } @@ -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(); } @@ -3208,6 +3210,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); @@ -3220,6 +3223,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; @@ -3246,6 +3262,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); diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c new file mode 100644 index 0000000000000..ebba95742c21b --- /dev/null +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c @@ -0,0 +1,110 @@ +// REQUIRES: powerpc-registered-target, system-aix, clang +// +// 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; +} diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c new file mode 100644 index 0000000000000..43f085f3cf459 --- /dev/null +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c @@ -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 \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h b/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h new file mode 100644 index 0000000000000..2da265de81fd2 --- /dev/null +++ b/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h @@ -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 { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + + static bool isRequired() { return true; } +}; + + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_UTILS_WYVERN_COPYRIGHTMETADATAPASS_H \ No newline at end of file diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index f5281ea69b512..24526d9f973ff 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -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/CopyrightMetadataPass.h" #include "llvm/Transforms/Utils/CountVisits.h" #include "llvm/Transforms/Utils/DXILUpgrade.h" #include "llvm/Transforms/Utils/Debugify.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index c6beb3fdf09bd..cd6cd126ef2bb 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -135,6 +135,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" @@ -1454,6 +1455,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) @@ -2274,6 +2278,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 diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 074c328ef0931..3f60ce0734a33 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -61,6 +61,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)) diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt index f367ca2fdf56b..13643e9f411b9 100644 --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -17,6 +17,7 @@ add_llvm_component_library(LLVMTransformUtils CodeLayout.cpp CodeMoverUtils.cpp ControlFlowUtils.cpp + CopyrightMetadataPass.cpp CtorUtils.cpp CountVisits.cpp Debugify.cpp diff --git a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp b/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp new file mode 100644 index 0000000000000..4d594f7ba5936 --- /dev/null +++ b/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp @@ -0,0 +1,144 @@ +//===-- CopyrightMetadataPass.cpp - Lower AIX copyright 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 module-level copyright metadata emitted by Clang: +// +// !aix.copyright.comment = !{!"Copyright ..."} +// +// into concrete, translation-unit–local globals to ensure that copyright +// strings: +// +// • survive all optimization and LTO pipelines, +// • are not removed by linker garbage collection, and +// • remain visible in the final XCOFF binary. +// +// For each module (translation unit), the pass performs the following: +// +// 1. Creates a null-terminated, internal constant string global +// (`__aix_copyright_str`) containing the copyright text. +// +// 2. Marks the string in `llvm.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 it. +// +//===----------------------------------------------------------------------===// + + +#include "llvm/Transforms/Utils/CopyrightMetadataPass.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/IRBuilder.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 "copyright-metadata" + +using namespace llvm; + +namespace llvm { + +static cl::opt + DisableCopyrightMetadata("disable-copyright-metadata", cl::ReallyHidden, + cl::desc("Disable copyright metadata pass."), + cl::init(false)); + +static bool isAIXTriple(const Module &M) { + return Triple(M.getTargetTriple()).isOSAIX(); +} + +PreservedAnalyses CopyrightMetadataPass::run(Module &M, + ModuleAnalysisManager &AM) { + if (DisableCopyrightMetadata || !isAIXTriple(M)) + return PreservedAnalyses::all(); + + LLVMContext &Ctx = M.getContext(); + + // Single-metadata: !aix.copyright.comment = !{!0} + // Each operand node is expected to have one MDString operand. + NamedMDNode *MD = M.getNamedMetadata("aix.copyright.comment"); + if (!MD || MD->getNumOperands() == 0) + return PreservedAnalyses::all(); + + // At this point we are guarateed 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(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); + + // Internal, constant, TU-local — avoids duplicate symbol issues across TUs. + auto *StrGV = new GlobalVariable(M, StrInit->getType(), + /*isConstant=*/true, + GlobalValue::InternalLinkage, StrInit, + /*Name=*/"__aix_copyright_str"); + StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + StrGV->setAlignment(Align(1)); + StrGV->setSection("__llvm_copyright"); + + // 2. Ensure LLVM doesn't delete it (through all pipelines/LTO). + appendToUsed(M, {StrGV}); + // appendToCompilerUsed(M, {StrGV}); + + // 3. Attach !implicit ref to every defined function + // Create a metadata node pointing to the copyright string: + // !N = !{ptr @__aix_copyright_str} + Metadata *Ops[] = {ConstantAsMetadata::get(StrGV)}; + auto *ValMD = ValueAsMetadata::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("implicit.ref", ImplicitRefMD); + LLVM_DEBUG(dbgs() << "[copyright] attached implicit.ref to function: " + << F.getName() << "\n"); + }; + + for (Function &F : M) + addImplicitRef(F); + + // Remove the original metadata since we've processed it + // This prevents reprocessing if the pass runs multiple times + MD->eraseFromParent(); + LLVM_DEBUG(dbgs() << "[copyright] created string and anchor for module\n"); + + return PreservedAnalyses::all(); +} + +} // namespace llvm \ No newline at end of file diff --git a/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll b/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll new file mode 100644 index 0000000000000..d73a5642c107e --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll @@ -0,0 +1,51 @@ +; REQUIRES: powerpc-registered-target, system-aix + +; Ensure the CopyrightMetadataPass (direct-byte variant) materializes +; a TU-local string and forces a text→data relocation via a tiny ctor, +; so the AIX/XCOFF linker keeps the string alive. + +; Build IR with the pass: +; RUN: opt -passes=copyright-metadata %s -o %t.bc + +; ---------------- 32-bit AIX ---------------- +; RUN: llc -mtriple=powerpc-ibm-aix -filetype=obj -o %t32.o %t.bc +; RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t.bc -o - | FileCheck %s --check-prefix=CHECK-ASM + +; Symbol table should show our TU-local names (locals are listed with -t). +; RUN: llvm-objdump -t %t32.o | FileCheck %s --check-prefix=CHECK-SYMS + +; Relocations should reference the string (due to the volatile byte load). +; RUN: llvm-objdump -r %t32.o | FileCheck %s --check-prefix=CHECK-REL + +; ---------------- 64-bit AIX ---------------- +; RUN: llc -mtriple=powerpc64-ibm-aix -filetype=obj -o %t64.o %t.bc +; RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t.bc -o - | FileCheck %s --check-prefix=CHECK-ASM +; RUN: llvm-objdump -t %t64.o | FileCheck %s --check-prefix=CHECK-SYMS +; RUN: llvm-objdump -r %t64.o | FileCheck %s --check-prefix=CHECK-REL + +target triple = "powerpc-ibm-aix" + +define i32 @main() { entry: ret i32 0 } + +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"wchar_size", i32 2} +!aix.copyright.comment = !{!1} +!1 = !{!"Copyright IBM"} + +; 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 IBM" + +; TU-local globals/functions should be present in the symbol table +; CHECK-SYMS: __aix_copyright_str + +; Relocation in the object should target the string (from the ctor’s volatile load) +; CHECK-LABEL: RELOCATION RECORDS FOR [.text] +; CHECK-REL: __aix_copyright_str \ No newline at end of file diff --git a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll new file mode 100644 index 0000000000000..5ddcd84e97ca3 --- /dev/null +++ b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll @@ -0,0 +1,35 @@ +; RUN: opt -passes=copyright-metadata -S %s -o - | FileCheck %s + +; Verify the pass converts !aix.copyright.comment into: +; - internal constant string global +; - marks it in llvm.used and llvm.compiler.used +; - creates an internal anchor function and registers it in llvm.global_ctors + +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} + +!aix.copyright.comment = !{!1} +!1 = !{!"@(#) Copyright IBM 2025"} + + +; ---- Globals-------------------------------------------- +; CHECK: @__aix_copyright_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__llvm_copyright", align 1 +; Preservation in used sets +; CHECK-NEXT: @llvm.used = appending global [1 x ptr] [ptr @__aix_copyright_str], section "llvm.metadata" +; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"} + +; Function has an implicit ref MD pointing at the string: +; CHECK: define void @f0() !implicit.ref ![[MD:[0-9]+]] +; CHECK: define i32 @main() !implicit.ref ![[MD]] +; CHECK: ![[MD]] = !{ptr @__aix_copyright_str} \ No newline at end of file From ed469d43c7151c6b9f9d87ac9c75ec529d48c660 Mon Sep 17 00:00:00 2001 From: Tony Varghese Date: Tue, 21 Oct 2025 09:56:06 -0400 Subject: [PATCH 13/18] Addressed the review comments. removed redundant tests --- .../clang/Basic/DiagnosticParseKinds.td | 2 +- clang/lib/CodeGen/CodeGenModule.cpp | 20 ++-- clang/lib/CodeGen/CodeGenModule.h | 7 +- clang/lib/Parse/ParsePragma.cpp | 8 +- .../pragma-comment-copyright-aix-multi-lto.c | 110 ------------------ .../PowerPC/pragma-comment-copyright-aix.c | 3 +- .../Transforms/Utils/CopyrightMetadataPass.h | 20 +--- .../Utils/CopyrightMetadataPass.cpp | 60 ++++++---- .../PowerPC/pragma-comment-copyright-aix.ll | 51 -------- .../CopyrightMetadata/copyright-metadata.ll | 45 +++++-- 10 files changed, 90 insertions(+), 236 deletions(-) delete mode 100644 clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c delete mode 100644 llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index fcbb1ab555619..ee59b6469eee3 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1369,7 +1369,7 @@ def err_pragma_comment_unknown_kind : Error<"unknown kind of pragma comment">; def warn_pragma_comment_ignored : Warning<"'#pragma comment %0' ignored">, InGroup; def warn_pragma_comment_once : Warning<"'#pragma comment %0' " - "can be specified only once per source file - ignored">, + "can be specified only once per translation unit - ignored">, InGroup; // - #pragma detect_mismatch def err_pragma_detect_mismatch_malformed : Error< diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 3da6be86d216c..38d6447e7e0df 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1570,11 +1570,9 @@ 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 (AIXCopyrightComment) { + auto *NMD = getModule().getOrInsertNamedMetadata("aix.copyright.comment"); + NMD->addOperand(AIXCopyrightComment); } // If there is device offloading code embed it in the host now. @@ -3410,21 +3408,19 @@ 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 +/// Process the #pragma comment(copyright, "copyright string ") +/// and create llvm metadata for the copyright void CodeGenModule::ProcessPragmaCommentCopyright(StringRef Comment) { - // Pragma Comment Copyright is enabled only when: - // - OS is AIX - // - Comment is non empty - if (!getTriple().isOSAIX() || Comment.empty()) + if (!getTriple().isOSAIX()) 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 + if(!AIXCopyrightComment) + AIXCopyrightComment = Node; } /// Add link options implied by the given module, including modules diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index e6cd9ca10f344..bb71575309b6e 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -587,8 +587,9 @@ class CodeGenModule : public CodeGenTypeCache { /// A vector of metadata strings for dependent libraries for ELF. SmallVector ELFDependentLibraries; - /// A vector of metadata strings for copyright comment for AIX - SmallVector AIXCopyrightComment; + /// Single module-level copyright comment for AIX (if any). + /// We only ever accept one per TU. + llvm::MDNode *AIXCopyrightComment = nullptr; /// @name Cache for Objective-C runtime types /// @{ @@ -1461,7 +1462,7 @@ 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. + /// Record the AIX copyright comment in module-level metadata. void ProcessPragmaCommentCopyright(StringRef Comment); llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 655e64674b866..6bcc799402e07 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -236,6 +236,7 @@ struct PragmaCommentHandler : public PragmaHandler { private: Sema &Actions; + bool SeenAIXCopyright = false; // TU-scoped }; struct PragmaDetectMismatchHandler : public PragmaHandler { @@ -3223,17 +3224,14 @@ 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) { + // On AIX, pragma comment copyright can each appear only once in a TU. + if (PP.getTargetInfo().getTriple().isOSAIX() && 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. diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c deleted file mode 100644 index ebba95742c21b..0000000000000 --- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-multi-lto.c +++ /dev/null @@ -1,110 +0,0 @@ -// REQUIRES: powerpc-registered-target, system-aix, clang -// -// 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; -} diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c index 43f085f3cf459..68e599ca577ce 100644 --- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c @@ -1,4 +1,3 @@ -// 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 @@ -16,7 +15,7 @@ #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}} +#pragma comment(copyright, "Duplicate Copyright") // expected-warning {{'#pragma comment copyright' can be specified only once per translation unit - ignored}} int main() { return 0; } diff --git a/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h b/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h index 2da265de81fd2..38b9d724905f9 100644 --- a/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h +++ b/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h @@ -5,24 +5,6 @@ // 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 @@ -40,4 +22,4 @@ class CopyrightMetadataPass : public PassInfoMixin { } // namespace llvm -#endif // LLVM_TRANSFORMS_UTILS_WYVERN_COPYRIGHTMETADATAPASS_H \ No newline at end of file +#endif // LLVM_TRANSFORMS_UTILS_COPYRIGHTMETADATAPASS_H \ No newline at end of file diff --git a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp b/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp index 4d594f7ba5936..55f1ee6e048cf 100644 --- a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp +++ b/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp @@ -6,21 +6,22 @@ // //===----------------------------------------------------------------------===// // -// This pass lowers module-level copyright metadata emitted by Clang: +// CopyrightMetadataPass pass lowers module-level copyright metadata emitted by +// Clang: // // !aix.copyright.comment = !{!"Copyright ..."} // // into concrete, translation-unit–local globals to ensure that copyright // strings: -// -// • survive all optimization and LTO pipelines, -// • are not removed by linker garbage collection, and -// • remain visible in the final XCOFF binary. +// - survive all optimization and LTO pipelines, +// - are not removed by linker garbage collection, and +// - remain visible in the final XCOFF binary. // // For each module (translation unit), the pass performs the following: // // 1. Creates a null-terminated, internal constant string global -// (`__aix_copyright_str`) containing the copyright text. +// (`__aix_copyright_str`) containing the copyright text in +// `__aix_copyright` section.. // // 2. Marks the string in `llvm.used` so it cannot be dropped by // optimization or LTO. @@ -29,11 +30,25 @@ // 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 it. +// discarding it (as long as the referencing symbol is kept). +// +// Input IR: +// !aix.copyright.comment = !{!"Copyright"} +// Output IR: +// @__aix_copyright_str = internal constant [N x i8] c"Copyright\00", +// section "__aix_copyright" +// @llvm.used = appending global [1 x ptr] [ptr @__aix_copyright_str] // +// define i32 @func() !implicit.ref !5 { ... } +// !5 = !{ptr @__aix_copyright_str} +// +// The copyright string is placed in the "__aix_copyright" section (mapped to +// an XCOFF csect with [RO] storage class), making it easily identifiable in +// object files and executables. The R_REF relocation prevents the linker +// from discarding this section during garbage collection. Copyright string (if +// kept by the linker) is expected to be loaded at run time. //===----------------------------------------------------------------------===// - #include "llvm/Transforms/Utils/CopyrightMetadataPass.h" #include "llvm/ADT/SmallVector.h" @@ -44,7 +59,6 @@ #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/IRBuilder.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" @@ -60,8 +74,6 @@ using namespace llvm; -namespace llvm { - static cl::opt DisableCopyrightMetadata("disable-copyright-metadata", cl::ReallyHidden, cl::desc("Disable copyright metadata pass."), @@ -106,22 +118,28 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, /*isConstant=*/true, GlobalValue::InternalLinkage, StrInit, /*Name=*/"__aix_copyright_str"); + // Set unnamed_addr to allow the linker to merge identical strings StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); StrGV->setAlignment(Align(1)); - StrGV->setSection("__llvm_copyright"); - - // 2. Ensure LLVM doesn't delete it (through all pipelines/LTO). + // Place in the "__aix_copyright" section. + // Backend maps this to an appropriate XCOFF csect (typically [RO]) + // The section will appear in assembly as: + // .csect __aix_copyright[RO],2 + StrGV->setSection("__aix_copyright"); + + // 2. Add the string to llvm.used to prevent LLVM optimization/LTO passes from + // removing it. appendToUsed(M, {StrGV}); - // appendToCompilerUsed(M, {StrGV}); // 3. Attach !implicit ref to every defined function // Create a metadata node pointing to the copyright string: // !N = !{ptr @__aix_copyright_str} Metadata *Ops[] = {ConstantAsMetadata::get(StrGV)}; - auto *ValMD = ValueAsMetadata::get(StrGV); MDNode *ImplicitRefMD = MDNode::get(Ctx, Ops); - auto addImplicitRef = [&](Function &F) { + // Lambda to attach implicit.ref metadata to a function. + // The backend will translate this into .ref assembly directives. + auto AddImplicitRef = [&](Function &F) { if (F.isDeclaration()) return; // Attach the implicit.ref metadata to the function @@ -130,15 +148,13 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, << F.getName() << "\n"); }; + // Process all functions in the module for (Function &F : M) - addImplicitRef(F); + AddImplicitRef(F); - // Remove the original metadata since we've processed it - // This prevents reprocessing if the pass runs multiple times + // Cleanup the processed metadata. MD->eraseFromParent(); LLVM_DEBUG(dbgs() << "[copyright] created string and anchor for module\n"); return PreservedAnalyses::all(); } - -} // namespace llvm \ No newline at end of file diff --git a/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll b/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll deleted file mode 100644 index d73a5642c107e..0000000000000 --- a/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll +++ /dev/null @@ -1,51 +0,0 @@ -; REQUIRES: powerpc-registered-target, system-aix - -; Ensure the CopyrightMetadataPass (direct-byte variant) materializes -; a TU-local string and forces a text→data relocation via a tiny ctor, -; so the AIX/XCOFF linker keeps the string alive. - -; Build IR with the pass: -; RUN: opt -passes=copyright-metadata %s -o %t.bc - -; ---------------- 32-bit AIX ---------------- -; RUN: llc -mtriple=powerpc-ibm-aix -filetype=obj -o %t32.o %t.bc -; RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t.bc -o - | FileCheck %s --check-prefix=CHECK-ASM - -; Symbol table should show our TU-local names (locals are listed with -t). -; RUN: llvm-objdump -t %t32.o | FileCheck %s --check-prefix=CHECK-SYMS - -; Relocations should reference the string (due to the volatile byte load). -; RUN: llvm-objdump -r %t32.o | FileCheck %s --check-prefix=CHECK-REL - -; ---------------- 64-bit AIX ---------------- -; RUN: llc -mtriple=powerpc64-ibm-aix -filetype=obj -o %t64.o %t.bc -; RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t.bc -o - | FileCheck %s --check-prefix=CHECK-ASM -; RUN: llvm-objdump -t %t64.o | FileCheck %s --check-prefix=CHECK-SYMS -; RUN: llvm-objdump -r %t64.o | FileCheck %s --check-prefix=CHECK-REL - -target triple = "powerpc-ibm-aix" - -define i32 @main() { entry: ret i32 0 } - -!llvm.module.flags = !{!0} -!0 = !{i32 1, !"wchar_size", i32 2} -!aix.copyright.comment = !{!1} -!1 = !{!"Copyright IBM"} - -; 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 IBM" - -; TU-local globals/functions should be present in the symbol table -; CHECK-SYMS: __aix_copyright_str - -; Relocation in the object should target the string (from the ctor’s volatile load) -; CHECK-LABEL: RELOCATION RECORDS FOR [.text] -; CHECK-REL: __aix_copyright_str \ No newline at end of file diff --git a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll index 5ddcd84e97ca3..f0dc46f66b809 100644 --- a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll +++ b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll @@ -1,9 +1,27 @@ -; RUN: opt -passes=copyright-metadata -S %s -o - | FileCheck %s - -; Verify the pass converts !aix.copyright.comment into: -; - internal constant string global -; - marks it in llvm.used and llvm.compiler.used -; - creates an internal anchor function and registers it in llvm.global_ctors +; RUN: opt -passes=copyright-metadata -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0 + +; Verify that copyright-metadata 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 + +; Verify that CopyrightMetadataPass lowers !aix.copyright.comment +; into concrete, translation-unit–local globals. +; +; For each module (translation unit), the pass performs the following: +; +; 1. Creates a null-terminated, internal constant string global +; (`__aix_copyright_str`) containing the copyright text. +; +; 2. Marks the string in `llvm.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 it. target triple = "powerpc-ibm-aix" @@ -24,12 +42,17 @@ entry: ; ---- Globals-------------------------------------------- -; CHECK: @__aix_copyright_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__llvm_copyright", align 1 -; Preservation in used sets +; CHECK: @__aix_copyright_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__aix_copyright", align 1 +; Preservation in llvm.used sets ; CHECK-NEXT: @llvm.used = appending global [1 x ptr] [ptr @__aix_copyright_str], section "llvm.metadata" ; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"} ; Function has an implicit ref MD pointing at the string: -; CHECK: define void @f0() !implicit.ref ![[MD:[0-9]+]] -; CHECK: define i32 @main() !implicit.ref ![[MD]] -; CHECK: ![[MD]] = !{ptr @__aix_copyright_str} \ No newline at end of file +; 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 @__aix_copyright_str} +; CHECK-ON: ![[MD]] = !{ptr @__aix_copyright_str} \ No newline at end of file From f47b0d4dc6e58bd41c3f9b4470824c6e9809a502 Mon Sep 17 00:00:00 2001 From: Tony Varghese Date: Wed, 22 Oct 2025 07:52:13 -0400 Subject: [PATCH 14/18] Pragmas from imported C++20 modules are ignored in the importing translation unit, add backend tests --- clang/lib/CodeGen/CodeGenModule.cpp | 5 +- .../pragma-comment-copyright-aix-modules.cpp | 26 ++++++++ .../PowerPC/pragma-comment-copyright-aix.ll | 61 +++++++++++++++++++ .../CopyrightMetadata/copyright-metadata.ll | 2 +- 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp create mode 100644 llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 38d6447e7e0df..1986ea0f0834e 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7600,7 +7600,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case PCK_Lib: AddDependentLib(PCD->getArg()); break; - case PCK_Copyright: + case PCK_Copyright: + // Skip pragmas deserialized from modules/PCHs + if (PCD->isFromASTFile()) + break; ProcessPragmaCommentCopyright(PCD->getArg()); break; case PCK_Compiler: diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp new file mode 100644 index 0000000000000..e1fc99796f867 --- /dev/null +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp @@ -0,0 +1,26 @@ +// 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 emit copyright string when compiled to assembly +// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix -S %t/copymod.cppm -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK-MOD +// CHECK-MOD: .string "module me" + +// Compile an importing TU that uses the prebuilt module and verify that it +// does NOT re-emit the module's copyright string. +// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \ +// RUN: -fprebuilt-module-path=%t -S %t/importmod.cc -o - \ +// RUN: | FileCheck %s +// CHECK-NOT: .string "module me" + +//--- copymod.cppm +export module copymod; +#pragma comment(copyright, "module me") +export inline void f() {} + +//--- importmod.cc +import copymod; +void g() { f(); } diff --git a/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll b/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll new file mode 100644 index 0000000000000..15be40f0d8525 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll @@ -0,0 +1,61 @@ +; REQUIRES: powerpc-registered-target, system-aix + +; This test verify that the CopyrightMetadataPass and the PowerPC AIX backend +; correctly lower and preserve copyright metadata emitted by Clang +; from `#pragma comment(copyright, "...")`. + +; Build IR with the pass: +; RUN: opt -passes=copyright-metadata %s -o %t.bc + +; ---------------- 32-bit AIX ---------------- +; RUN: llc -mtriple=powerpc-ibm-aix -filetype=obj -o %t32.o %t.bc +; RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t.bc -o - | FileCheck %s --check-prefix=CHECK-ASM + +; Verify that TU-local symbols and relocations reference the string. +; RUN: llvm-objdump -t %t32.o | FileCheck %s --check-prefix=CHECK-SYMS +; RUN: llvm-objdump -r %t32.o | FileCheck %s --check-prefix=CHECK-REL + +; ---------------- 64-bit AIX ---------------- +; RUN: llc -mtriple=powerpc64-ibm-aix -filetype=obj -o %t64.o %t.bc +; RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t.bc -o - | FileCheck %s --check-prefix=CHECK-ASM +; RUN: llvm-objdump -t %t64.o | FileCheck %s --check-prefix=CHECK-SYMS +; RUN: llvm-objdump -r %t64.o | FileCheck %s --check-prefix=CHECK-REL + +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} + +!aix.copyright.comment = !{!1} +!1 = !{!"Copyright IBM"} + +; ---------------- Assembly checks ---------------- +; Verify that the backend: +; - Emits a `.ref` directive for the string +; - Emits the string in a dedicated read-only csect +; +; CHECK-ASM: .ref __aix_copyright_str +; CHECK-ASM: .csect __aix_copyright[RO],2 +; CHECK-ASM-NEXT: .lglobl __aix_copyright_str +; CHECK-ASM: __aix_copyright_str +; CHECK-ASM: .string "Copyright IBM" + +; ---------------- Symbol table checks ---------------- +; TU-local globals/functions should be present in the symbol table +; CHECK-SYMS: __aix_copyright_str + +; ---------------- Relocation checks ---------------- +; The object should contain a relocation in the text section +; that references the copyright string. +; CHECK-LABEL: RELOCATION RECORDS FOR [.text] +; CHECK-REL: __aix_copyright_str \ No newline at end of file diff --git a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll index f0dc46f66b809..df10534347b19 100644 --- a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll +++ b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll @@ -21,7 +21,7 @@ ; 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 it. +; discarding it (as long as the referencing symbol is kept). target triple = "powerpc-ibm-aix" From 696ca9ff4551e84725146129b5f356726c92aa3a Mon Sep 17 00:00:00 2001 From: Tony Varghese Date: Wed, 29 Oct 2025 02:54:53 -0400 Subject: [PATCH 15/18] Chnaged to platform agnostic names for llvm metadata and variables used --- clang/lib/CodeGen/CodeGenModule.cpp | 22 ++++--- clang/lib/CodeGen/CodeGenModule.h | 8 +-- clang/lib/Parse/ParsePragma.cpp | 52 +++++++++------- .../PowerPC/pragma-comment-copyright-aix.c | 2 +- .../Utils/CopyrightMetadataPass.cpp | 36 +++++------ .../PowerPC/pragma-comment-copyright-aix.ll | 61 ------------------- .../CopyrightMetadata/copyright-metadata.ll | 15 ++--- 7 files changed, 75 insertions(+), 121 deletions(-) delete mode 100644 llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 1986ea0f0834e..5257a8fcff11a 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -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" @@ -1569,10 +1570,10 @@ void CodeGenModule::Release() { EmitBackendOptionsMetadata(getCodeGenOpts()); - // Emit copyright metadata for AIX - if (AIXCopyrightComment) { - auto *NMD = getModule().getOrInsertNamedMetadata("aix.copyright.comment"); - NMD->addOperand(AIXCopyrightComment); + // Emit copyright metadata + if (CopyrightCommentInTU) { + auto *NMD = getModule().getOrInsertNamedMetadata("loadtime.copyright.comment"); + NMD->addOperand(CopyrightCommentInTU); } // If there is device offloading code embed it in the host now. @@ -3410,17 +3411,20 @@ void CodeGenModule::AddDependentLib(StringRef Lib) { /// Process the #pragma comment(copyright, "copyright string ") /// and create llvm metadata for the copyright -void CodeGenModule::ProcessPragmaCommentCopyright(StringRef Comment) { +void CodeGenModule::ProcessPragmaComment(PragmaMSCommentKind Kind, + StringRef Comment) { - if (!getTriple().isOSAIX()) + if (!getTriple().isOSAIX() || Kind != PCK_Copyright) return; + assert( + !CopyrightCommentInTU && + "Only one copyright comment should be present in the Translation Unit"); // 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); - if(!AIXCopyrightComment) - AIXCopyrightComment = Node; + CopyrightCommentInTU = Node; } /// Add link options implied by the given module, including modules @@ -7604,7 +7608,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { // Skip pragmas deserialized from modules/PCHs if (PCD->isFromASTFile()) break; - ProcessPragmaCommentCopyright(PCD->getArg()); + ProcessPragmaComment(PCD->getCommentKind(), PCD->getArg()); break; case PCK_Compiler: case PCK_ExeStr: diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index bb71575309b6e..6ff3281e5df48 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -587,9 +587,9 @@ class CodeGenModule : public CodeGenTypeCache { /// A vector of metadata strings for dependent libraries for ELF. SmallVector ELFDependentLibraries; - /// Single module-level copyright comment for AIX (if any). + /// Single module-level copyright comment (if any). /// We only ever accept one per TU. - llvm::MDNode *AIXCopyrightComment = nullptr; + llvm::MDNode *CopyrightCommentInTU = nullptr; /// @name Cache for Objective-C runtime types /// @{ @@ -1462,8 +1462,8 @@ class CodeGenModule : public CodeGenTypeCache { /// Appends a dependent lib to the appropriate metadata value. void AddDependentLib(StringRef Lib); - /// Record the AIX copyright comment in module-level metadata. - void ProcessPragmaCommentCopyright(StringRef Comment); + /// Process pragma comment + void ProcessPragmaComment(PragmaMSCommentKind Kind, StringRef Comment); llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 6bcc799402e07..e0e05ae0f97b3 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -236,7 +236,7 @@ struct PragmaCommentHandler : public PragmaHandler { private: Sema &Actions; - bool SeenAIXCopyright = false; // TU-scoped + bool SeenCopyrightInTU = false; // TU-scoped }; struct PragmaDetectMismatchHandler : public PragmaHandler { @@ -3205,14 +3205,26 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP, // Verify that this is one of the 5 explicitly listed options. IdentifierInfo *II = Tok.getIdentifierInfo(); PragmaMSCommentKind Kind = - llvm::StringSwitch(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); + llvm::StringSwitch(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 + if (!PP.getTargetInfo().getTriple().isOSAIX()) { + switch (Kind) { + case PCK_Copyright: + Kind = PCK_Unknown; + break; + default: + break; + } + } + if (Kind == PCK_Unknown) { PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind); return; @@ -3225,13 +3237,13 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP, } // On AIX, pragma comment copyright can each appear only once in a TU. - if (PP.getTargetInfo().getTriple().isOSAIX() && Kind == PCK_Copyright) { - if (SeenAIXCopyright) { - PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_once) - << II->getName(); - return; - } - SeenAIXCopyright = true; + if (Kind == PCK_Copyright) { + if (SeenCopyrightInTU) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_comment_once) + << II->getName(); + return; + } + SeenCopyrightInTU = true; } // Read the optional string if present. @@ -3260,11 +3272,9 @@ 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; - } + // 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()) diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c index 68e599ca577ce..5ace127629dda 100644 --- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c @@ -20,7 +20,7 @@ int main() { return 0; } // Check that both metadata sections are present -// CHECK: !aix.copyright.comment = !{![[copyright:[0-9]+]]} +// CHECK: !loadtime.copyright.comment = !{![[copyright:[0-9]+]]} // Check individual metadata content // CHECK: ![[copyright]] = !{!"@(#) Copyright"} diff --git a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp b/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp index 55f1ee6e048cf..edba4b479df06 100644 --- a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp +++ b/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp @@ -1,15 +1,15 @@ -//===-- CopyrightMetadataPass.cpp - Lower AIX copyright metadata ----------===// +//===-- CopyrightMetadataPass.cpp - Lower copyright 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 // -//===----------------------------------------------------------------------===// +//===---------------------------------------------------------------------===// // // CopyrightMetadataPass pass lowers module-level copyright metadata emitted by // Clang: // -// !aix.copyright.comment = !{!"Copyright ..."} +// !loadtime.copyright.comment = !{!"Copyright ..."} // // into concrete, translation-unit–local globals to ensure that copyright // strings: @@ -20,8 +20,8 @@ // For each module (translation unit), the pass performs the following: // // 1. Creates a null-terminated, internal constant string global -// (`__aix_copyright_str`) containing the copyright text in -// `__aix_copyright` section.. +// (`__loadtime_copyright_str`) containing the copyright text in +// `__copyright_comment` section. // // 2. Marks the string in `llvm.used` so it cannot be dropped by // optimization or LTO. @@ -33,16 +33,16 @@ // discarding it (as long as the referencing symbol is kept). // // Input IR: -// !aix.copyright.comment = !{!"Copyright"} +// !loadtime.copyright.comment = !{!"Copyright"} // Output IR: -// @__aix_copyright_str = internal constant [N x i8] c"Copyright\00", -// section "__aix_copyright" -// @llvm.used = appending global [1 x ptr] [ptr @__aix_copyright_str] +// @__loadtime_copyright_str = internal constant [N x i8] c"Copyright\00", +// section "__copyright_comment" +// @llvm.used = appending global [1 x ptr] [ptr @__loadtime_copyright_str] // // define i32 @func() !implicit.ref !5 { ... } -// !5 = !{ptr @__aix_copyright_str} +// !5 = !{ptr @__loadtime_copyright_str} // -// The copyright string is placed in the "__aix_copyright" section (mapped to +// The copyright string is placed in the "__copyright_comment" section (mapped to // an XCOFF csect with [RO] storage class), making it easily identifiable in // object files and executables. The R_REF relocation prevents the linker // from discarding this section during garbage collection. Copyright string (if @@ -90,9 +90,9 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, LLVMContext &Ctx = M.getContext(); - // Single-metadata: !aix.copyright.comment = !{!0} + // Single-metadata: !loadtime.copyright.comment = !{!0} // Each operand node is expected to have one MDString operand. - NamedMDNode *MD = M.getNamedMetadata("aix.copyright.comment"); + NamedMDNode *MD = M.getNamedMetadata("loadtime.copyright.comment"); if (!MD || MD->getNumOperands() == 0) return PreservedAnalyses::all(); @@ -117,15 +117,15 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, auto *StrGV = new GlobalVariable(M, StrInit->getType(), /*isConstant=*/true, GlobalValue::InternalLinkage, StrInit, - /*Name=*/"__aix_copyright_str"); + /*Name=*/"__loadtime_copyright_str"); // Set unnamed_addr to allow the linker to merge identical strings StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); StrGV->setAlignment(Align(1)); - // Place in the "__aix_copyright" section. + // Place in the "__copyright_comment" section. // Backend maps this to an appropriate XCOFF csect (typically [RO]) // The section will appear in assembly as: - // .csect __aix_copyright[RO],2 - StrGV->setSection("__aix_copyright"); + // .csect __copyright_comment[RO],2 + StrGV->setSection("__copyright_comment"); // 2. Add the string to llvm.used to prevent LLVM optimization/LTO passes from // removing it. @@ -133,7 +133,7 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, // 3. Attach !implicit ref to every defined function // Create a metadata node pointing to the copyright string: - // !N = !{ptr @__aix_copyright_str} + // !N = !{ptr @__loadtime_copyright_str} Metadata *Ops[] = {ConstantAsMetadata::get(StrGV)}; MDNode *ImplicitRefMD = MDNode::get(Ctx, Ops); diff --git a/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll b/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll deleted file mode 100644 index 15be40f0d8525..0000000000000 --- a/llvm/test/CodeGen/PowerPC/pragma-comment-copyright-aix.ll +++ /dev/null @@ -1,61 +0,0 @@ -; REQUIRES: powerpc-registered-target, system-aix - -; This test verify that the CopyrightMetadataPass and the PowerPC AIX backend -; correctly lower and preserve copyright metadata emitted by Clang -; from `#pragma comment(copyright, "...")`. - -; Build IR with the pass: -; RUN: opt -passes=copyright-metadata %s -o %t.bc - -; ---------------- 32-bit AIX ---------------- -; RUN: llc -mtriple=powerpc-ibm-aix -filetype=obj -o %t32.o %t.bc -; RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t.bc -o - | FileCheck %s --check-prefix=CHECK-ASM - -; Verify that TU-local symbols and relocations reference the string. -; RUN: llvm-objdump -t %t32.o | FileCheck %s --check-prefix=CHECK-SYMS -; RUN: llvm-objdump -r %t32.o | FileCheck %s --check-prefix=CHECK-REL - -; ---------------- 64-bit AIX ---------------- -; RUN: llc -mtriple=powerpc64-ibm-aix -filetype=obj -o %t64.o %t.bc -; RUN: llc -mtriple=powerpc-ibm-aix -filetype=asm %t.bc -o - | FileCheck %s --check-prefix=CHECK-ASM -; RUN: llvm-objdump -t %t64.o | FileCheck %s --check-prefix=CHECK-SYMS -; RUN: llvm-objdump -r %t64.o | FileCheck %s --check-prefix=CHECK-REL - -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} - -!aix.copyright.comment = !{!1} -!1 = !{!"Copyright IBM"} - -; ---------------- Assembly checks ---------------- -; Verify that the backend: -; - Emits a `.ref` directive for the string -; - Emits the string in a dedicated read-only csect -; -; CHECK-ASM: .ref __aix_copyright_str -; CHECK-ASM: .csect __aix_copyright[RO],2 -; CHECK-ASM-NEXT: .lglobl __aix_copyright_str -; CHECK-ASM: __aix_copyright_str -; CHECK-ASM: .string "Copyright IBM" - -; ---------------- Symbol table checks ---------------- -; TU-local globals/functions should be present in the symbol table -; CHECK-SYMS: __aix_copyright_str - -; ---------------- Relocation checks ---------------- -; The object should contain a relocation in the text section -; that references the copyright string. -; CHECK-LABEL: RELOCATION RECORDS FOR [.text] -; CHECK-REL: __aix_copyright_str \ No newline at end of file diff --git a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll index df10534347b19..53140489a34f2 100644 --- a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll +++ b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll @@ -6,13 +6,14 @@ ; 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 -; Verify that CopyrightMetadataPass lowers !aix.copyright.comment +; Verify that CopyrightMetadataPass lowers !loadtime.copyright.comment ; into concrete, translation-unit–local globals. ; ; For each module (translation unit), the pass performs the following: ; ; 1. Creates a null-terminated, internal constant string global -; (`__aix_copyright_str`) containing the copyright text. +; (`__loadtime_copyright_str`) containing the copyright text in +; `__copyright_comment` section. ; ; 2. Marks the string in `llvm.used` so it cannot be dropped by ; optimization or LTO. @@ -37,14 +38,14 @@ entry: !llvm.module.flags = !{!0} !0 = !{i32 1, !"wchar_size", i32 2} -!aix.copyright.comment = !{!1} +!loadtime.copyright.comment = !{!1} !1 = !{!"@(#) Copyright IBM 2025"} ; ---- Globals-------------------------------------------- -; CHECK: @__aix_copyright_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__aix_copyright", align 1 +; CHECK: @__loadtime_copyright_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__copyright_comment", align 1 ; Preservation in llvm.used sets -; CHECK-NEXT: @llvm.used = appending global [1 x ptr] [ptr @__aix_copyright_str], section "llvm.metadata" +; CHECK-NEXT: @llvm.used = appending global [1 x ptr] [ptr @__loadtime_copyright_str], section "llvm.metadata" ; CHECK-NOT: ![[copyright:[0-9]+]] = !{!"@(#) Copyright IBM 2025"} ; Function has an implicit ref MD pointing at the string: @@ -54,5 +55,5 @@ entry: ; CHECK-ON: define noundef i32 @main() local_unnamed_addr #0 !implicit.ref ![[MD]] ; Verify metadata content -; CHECK-O0: ![[MD]] = !{ptr @__aix_copyright_str} -; CHECK-ON: ![[MD]] = !{ptr @__aix_copyright_str} \ No newline at end of file +; CHECK-O0: ![[MD]] = !{ptr @__loadtime_copyright_str} +; CHECK-ON: ![[MD]] = !{ptr @__loadtime_copyright_str} \ No newline at end of file From c21153eefcdbb6118307e41ddc75e56fe68cbca4 Mon Sep 17 00:00:00 2001 From: Tony Varghese Date: Wed, 3 Dec 2025 13:55:15 -0500 Subject: [PATCH 16/18] Changed the name to comment_string.loadtime - Fix test failures: Update pipeline tests for CopyrightMetadataPass - Fixed pragma comment copyright test expectations --- clang/lib/CodeGen/CodeGenModule.cpp | 41 +++++++++++-------- clang/lib/CodeGen/CodeGenModule.h | 5 +-- clang/lib/Parse/ParsePragma.cpp | 13 +++--- .../PowerPC/pragma-comment-copyright-aix.c | 2 +- clang/test/CodeGen/lto-newpm-pipeline.c | 2 + .../Utils/CopyrightMetadataPass.cpp | 28 ++++--------- .../CodeGen/AArch64/print-pipeline-passes.ll | 2 +- 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 + .../CopyrightMetadata/copyright-metadata.ll | 2 +- 12 files changed, 48 insertions(+), 51 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 5257a8fcff11a..07b5daaa5f98f 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -40,7 +40,7 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Diagnostic.h" -#include "clang/Basic/DiagnosticFrontend.h" +// #include "clang/Basic/DiagnosticParse.h" #include "clang/Basic/Module.h" #include "clang/Basic/PragmaKinds.h" #include "clang/Basic/SourceManager.h" @@ -1571,9 +1571,9 @@ void CodeGenModule::Release() { EmitBackendOptionsMetadata(getCodeGenOpts()); // Emit copyright metadata - if (CopyrightCommentInTU) { - auto *NMD = getModule().getOrInsertNamedMetadata("loadtime.copyright.comment"); - NMD->addOperand(CopyrightCommentInTU); + if (LoadTimeComment) { + auto *NMD = getModule().getOrInsertNamedMetadata("comment_string.loadtime"); + NMD->addOperand(LoadTimeComment); } // If there is device offloading code embed it in the host now. @@ -3409,22 +3409,29 @@ void CodeGenModule::AddDependentLib(StringRef Lib) { LinkerOptionsMetadata.push_back(llvm::MDNode::get(C, MDOpts)); } -/// Process the #pragma comment(copyright, "copyright string ") -/// and create llvm metadata for the copyright +/// 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"); - if (!getTriple().isOSAIX() || Kind != PCK_Copyright) + // Only one copyright pragma allowed per translation unit + if (LoadTimeComment) { return; + } - assert( - !CopyrightCommentInTU && - "Only one copyright comment should be present in the Translation Unit"); // 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); - CopyrightCommentInTU = Node; + llvm::Metadata *Ops[] = {llvm::MDString::get(C, Comment)}; + LoadTimeComment = llvm::MDNode::get(C, Ops); } /// Add link options implied by the given module, including modules @@ -7605,10 +7612,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { AddDependentLib(PCD->getArg()); break; case PCK_Copyright: - // Skip pragmas deserialized from modules/PCHs - if (PCD->isFromASTFile()) - break; - ProcessPragmaComment(PCD->getCommentKind(), PCD->getArg()); + // 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: diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 6ff3281e5df48..895f91691cae3 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -587,9 +587,8 @@ class CodeGenModule : public CodeGenTypeCache { /// A vector of metadata strings for dependent libraries for ELF. SmallVector ELFDependentLibraries; - /// Single module-level copyright comment (if any). - /// We only ever accept one per TU. - llvm::MDNode *CopyrightCommentInTU = nullptr; + /// Metadata for copyright pragma comment (if present). + llvm::MDNode *LoadTimeComment = nullptr; /// @name Cache for Objective-C runtime types /// @{ diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index e0e05ae0f97b3..81cb3fb6efe44 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -3214,15 +3214,10 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP, .Case("copyright", PCK_Copyright) .Default(PCK_Unknown); - // Restrict copyright to AIX targets only - if (!PP.getTargetInfo().getTriple().isOSAIX()) { - switch (Kind) { - case PCK_Copyright: + // 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; - break; - default: - break; - } } if (Kind == PCK_Unknown) { @@ -3238,6 +3233,8 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP, // 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(); diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c index 5ace127629dda..ad682825954fa 100644 --- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c @@ -20,7 +20,7 @@ int main() { return 0; } // Check that both metadata sections are present -// CHECK: !loadtime.copyright.comment = !{![[copyright:[0-9]+]]} +// CHECK: !comment_string.loadtime = !{![[copyright:[0-9]+]]} // Check individual metadata content // CHECK: ![[copyright]] = !{!"@(#) Copyright"} diff --git a/clang/test/CodeGen/lto-newpm-pipeline.c b/clang/test/CodeGen/lto-newpm-pipeline.c index ea9784a76f923..28f291aff052e 100644 --- a/clang/test/CodeGen/lto-newpm-pipeline.c +++ b/clang/test/CodeGen/lto-newpm-pipeline.c @@ -27,6 +27,7 @@ // CHECK-FULL-O0: Running pass: VerifierPass // CHECK-FULL-O0-NEXT: Running analysis: VerifierAnalysis +// CHECK-FULL-O0-NEXT: Running pass: CopyrightMetadataPass // CHECK-FULL-O0-NEXT: Running analysis: InnerAnalysisManagerProxy // CHECK-FULL-O0-NEXT: Running pass: EntryExitInstrumenterPass // CHECK-FULL-O0-NEXT: Running pass: AlwaysInlinerPass @@ -41,6 +42,7 @@ // CHECK-THIN-O0: Running pass: VerifierPass // CHECK-THIN-O0-NEXT: Running analysis: VerifierAnalysis +// CHECK-THIN-O0-NEXT: Running pass: CopyrightMetadataPass // CHECK-THIN-O0-NEXT: Running analysis: InnerAnalysisManagerProxy // CHECK-THIN-O0-NEXT: Running pass: EntryExitInstrumenterPass // CHECK-THIN-O0-NEXT: Running pass: AlwaysInlinerPass diff --git a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp b/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp index edba4b479df06..08768e05b42ee 100644 --- a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp +++ b/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp @@ -9,14 +9,10 @@ // CopyrightMetadataPass pass lowers module-level copyright metadata emitted by // Clang: // -// !loadtime.copyright.comment = !{!"Copyright ..."} -// -// into concrete, translation-unit–local globals to ensure that copyright -// strings: -// - survive all optimization and LTO pipelines, -// - are not removed by linker garbage collection, and -// - remain visible in the final XCOFF binary. +// !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 @@ -33,7 +29,7 @@ // discarding it (as long as the referencing symbol is kept). // // Input IR: -// !loadtime.copyright.comment = !{!"Copyright"} +// !comment_string.loadtime = !{!"Copyright"} // Output IR: // @__loadtime_copyright_str = internal constant [N x i8] c"Copyright\00", // section "__copyright_comment" @@ -42,11 +38,6 @@ // define i32 @func() !implicit.ref !5 { ... } // !5 = !{ptr @__loadtime_copyright_str} // -// The copyright string is placed in the "__copyright_comment" section (mapped to -// an XCOFF csect with [RO] storage class), making it easily identifiable in -// object files and executables. The R_REF relocation prevents the linker -// from discarding this section during garbage collection. Copyright string (if -// kept by the linker) is expected to be loaded at run time. //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/CopyrightMetadataPass.h" @@ -90,9 +81,9 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, LLVMContext &Ctx = M.getContext(); - // Single-metadata: !loadtime.copyright.comment = !{!0} + // Single-metadata: !comment_string.loadtime = !{!0} // Each operand node is expected to have one MDString operand. - NamedMDNode *MD = M.getNamedMetadata("loadtime.copyright.comment"); + NamedMDNode *MD = M.getNamedMetadata("comment_string.loadtime"); if (!MD || MD->getNumOperands() == 0) return PreservedAnalyses::all(); @@ -113,7 +104,7 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, // 1. Create a single NULL-terminated string global Constant *StrInit = ConstantDataArray::getString(Ctx, Text, /*AddNull=*/true); - // Internal, constant, TU-local — avoids duplicate symbol issues across TUs. + // Internal, constant, TU-local--avoids duplicate symbol issues across TUs. auto *StrGV = new GlobalVariable(M, StrInit->getType(), /*isConstant=*/true, GlobalValue::InternalLinkage, StrInit, @@ -122,9 +113,7 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, StrGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); StrGV->setAlignment(Align(1)); // Place in the "__copyright_comment" section. - // Backend maps this to an appropriate XCOFF csect (typically [RO]) - // The section will appear in assembly as: - // .csect __copyright_comment[RO],2 + // The GV is constant, so we expect a read-only section. StrGV->setSection("__copyright_comment"); // 2. Add the string to llvm.used to prevent LLVM optimization/LTO passes from @@ -138,7 +127,6 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, MDNode *ImplicitRefMD = MDNode::get(Ctx, Ops); // Lambda to attach implicit.ref metadata to a function. - // The backend will translate this into .ref assembly directives. auto AddImplicitRef = [&](Function &F) { if (F.isDeclaration()) return; diff --git a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll index 86090324c770c..f3600bc2ded8d 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' -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: {{^}}copyright-metadata,function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),function(annotation-remarks),verify,print{{$}} define void @foo() { entry: diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll index f074b2fdd3ab8..c690c2df1af82 100644 --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -238,6 +238,7 @@ ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-O-NEXT: Running pass: CopyrightMetadataPass ; CHECK-DEFAULT-NEXT: Running pass: EliminateAvailableExternallyPass ; CHECK-LTO-NOT: Running pass: EliminateAvailableExternallyPass ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll index b0d08316de4f0..0f9f9abfdac28 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll @@ -162,6 +162,7 @@ ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalOptPass ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-POSTLINK-O-NEXT: Running pass: CopyrightMetadataPass ; CHECK-POSTLINK-O-NEXT: Running pass: EliminateAvailableExternallyPass ; CHECK-POSTLINK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass ; CHECK-POSTLINK-O-NEXT: Running pass: RecomputeGlobalsAAPass 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 6b3e82a752899..528d7105a0516 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll @@ -146,6 +146,7 @@ ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-O-NEXT: Running pass: CopyrightMetadataPass ; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass 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 88dc18f605ce2..2e07520462912 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll @@ -155,6 +155,7 @@ ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-O-NEXT: Running pass: CopyrightMetadataPass ; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass diff --git a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll index 53140489a34f2..2a950a69fb5c6 100644 --- a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll +++ b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll @@ -38,7 +38,7 @@ entry: !llvm.module.flags = !{!0} !0 = !{i32 1, !"wchar_size", i32 2} -!loadtime.copyright.comment = !{!1} +!comment_string.loadtime = !{!1} !1 = !{!"@(#) Copyright IBM 2025"} From a2904bae723c0eb0aa9094ddc76e6e5e1eeba828 Mon Sep 17 00:00:00 2001 From: Tony Varghese Date: Wed, 3 Dec 2025 15:07:01 -0500 Subject: [PATCH 17/18] Update clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp to be an llvm based test --- clang/lib/CodeGen/CodeGenModule.cpp | 2 +- .../pragma-comment-copyright-aix-modules.cpp | 16 +++++++++------- .../PowerPC/pragma-comment-copyright-aix.c | 2 +- .../Transforms/Utils/CopyrightMetadataPass.h | 2 +- .../CodeGen/AArch64/print-pipeline-passes.ll | 3 +-- .../CopyrightMetadata/copyright-metadata.ll | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 07b5daaa5f98f..9047eaa400b72 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -40,7 +40,7 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Diagnostic.h" -// #include "clang/Basic/DiagnosticParse.h" +#include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/Module.h" #include "clang/Basic/PragmaKinds.h" #include "clang/Basic/SourceManager.h" diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp index e1fc99796f867..fa8482db94e7a 100644 --- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp @@ -4,17 +4,19 @@ // 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 emit copyright string when compiled to assembly -// RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix -S %t/copymod.cppm -o - \ +// 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: .string "module me" +// CHECK-MOD: @__loadtime_copyright_str = internal unnamed_addr constant [10 x i8] c"module me\00", section "__copyright_comment" +// CHECK-MOD: @llvm.used = appending global {{.*}} @__loadtime_copyright_str // Compile an importing TU that uses the prebuilt module and verify that it -// does NOT re-emit the module's copyright string. +// does NOT re-emit the module's copyright global. // RUN: %clang_cc1 -std=c++20 -triple powerpc-ibm-aix \ -// RUN: -fprebuilt-module-path=%t -S %t/importmod.cc -o - \ -// RUN: | FileCheck %s -// CHECK-NOT: .string "module me" +// RUN: -fprebuilt-module-path=%t -emit-llvm %t/importmod.cc -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK-IMPORT +// CHECK-IMPORT-NOT: @__loadtime_copyright_str +// CHECK-IMPORT-NOT: c"module me\00" //--- copymod.cppm export module copymod; diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c index ad682825954fa..98d2db416496e 100644 --- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix.c @@ -31,4 +31,4 @@ int main() { return 0; } int main() { return 0; } -#endif \ No newline at end of file +#endif diff --git a/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h b/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h index 38b9d724905f9..894cbfdcb8dc3 100644 --- a/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h +++ b/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h @@ -22,4 +22,4 @@ class CopyrightMetadataPass : public PassInfoMixin { } // namespace llvm -#endif // LLVM_TRANSFORMS_UTILS_COPYRIGHTMETADATAPASS_H \ No newline at end of file +#endif // LLVM_TRANSFORMS_UTILS_COPYRIGHTMETADATAPASS_H diff --git a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll index f3600bc2ded8d..27e9552d35ef5 100644 --- a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll +++ b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll @@ -2,8 +2,7 @@ ; RUN: opt -mtriple=aarch64 -S -passes='default' -print-pipeline-passes < %s | FileCheck %s ; CHECK: loop-idiom-vectorize -; O0: {{^}}copyright-metadata,function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),function(annotation-remarks),verify,print{{$}} - +; O0: {{^}}copyright-metadata,function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,function(annotation-remarks),verify,print{{$}} define void @foo() { entry: ret void diff --git a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll index 2a950a69fb5c6..e613f2cdd4d5c 100644 --- a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll +++ b/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll @@ -56,4 +56,4 @@ entry: ; Verify metadata content ; CHECK-O0: ![[MD]] = !{ptr @__loadtime_copyright_str} -; CHECK-ON: ![[MD]] = !{ptr @__loadtime_copyright_str} \ No newline at end of file +; CHECK-ON: ![[MD]] = !{ptr @__loadtime_copyright_str} From 0402627f1bb2d58525eb0b10ebb6e9797f494821 Mon Sep 17 00:00:00 2001 From: Tony Varghese Date: Fri, 5 Dec 2025 13:11:14 -0500 Subject: [PATCH 18/18] Changed the name of the pass to LowerCommentStringPass --- .../pragma-comment-copyright-aix-modules.cpp | 6 ++-- clang/test/CodeGen/lto-newpm-pipeline.c | 4 +-- ...etadataPass.h => LowerCommentStringPass.h} | 10 +++--- llvm/lib/Passes/PassBuilder.cpp | 2 +- llvm/lib/Passes/PassBuilderPipelines.cpp | 6 ++-- llvm/lib/Passes/PassRegistry.def | 2 +- llvm/lib/Transforms/Utils/CMakeLists.txt | 2 +- ...ataPass.cpp => LowerCommentStringPass.cpp} | 36 +++++++++---------- .../CodeGen/AArch64/print-pipeline-passes.ll | 2 +- llvm/test/Other/new-pm-defaults.ll | 2 +- .../Other/new-pm-thinlto-postlink-defaults.ll | 2 +- .../new-pm-thinlto-postlink-pgo-defaults.ll | 2 +- ...-pm-thinlto-postlink-samplepgo-defaults.ll | 2 +- .../lower-comment-string.ll} | 18 +++++----- 14 files changed, 48 insertions(+), 48 deletions(-) rename llvm/include/llvm/Transforms/Utils/{CopyrightMetadataPass.h => LowerCommentStringPass.h} (59%) rename llvm/lib/Transforms/Utils/{CopyrightMetadataPass.cpp => LowerCommentStringPass.cpp} (82%) rename llvm/test/Transforms/{CopyrightMetadata/copyright-metadata.ll => LowerCommentString/lower-comment-string.ll} (73%) diff --git a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp index fa8482db94e7a..9020df6e737d6 100644 --- a/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp +++ b/clang/test/CodeGen/PowerPC/pragma-comment-copyright-aix-modules.cpp @@ -7,15 +7,15 @@ // 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_copyright_str = internal unnamed_addr constant [10 x i8] c"module me\00", section "__copyright_comment" -// CHECK-MOD: @llvm.used = appending global {{.*}} @__loadtime_copyright_str +// 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_copyright_str +// CHECK-IMPORT-NOT: @__loadtime_comment_str // CHECK-IMPORT-NOT: c"module me\00" //--- copymod.cppm diff --git a/clang/test/CodeGen/lto-newpm-pipeline.c b/clang/test/CodeGen/lto-newpm-pipeline.c index 28f291aff052e..b9466b9558b06 100644 --- a/clang/test/CodeGen/lto-newpm-pipeline.c +++ b/clang/test/CodeGen/lto-newpm-pipeline.c @@ -27,7 +27,7 @@ // CHECK-FULL-O0: Running pass: VerifierPass // CHECK-FULL-O0-NEXT: Running analysis: VerifierAnalysis -// CHECK-FULL-O0-NEXT: Running pass: CopyrightMetadataPass +// 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 @@ -42,7 +42,7 @@ // CHECK-THIN-O0: Running pass: VerifierPass // CHECK-THIN-O0-NEXT: Running analysis: VerifierAnalysis -// CHECK-THIN-O0-NEXT: Running pass: CopyrightMetadataPass +// 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 diff --git a/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h similarity index 59% rename from llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h rename to llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h index 894cbfdcb8dc3..567c42ddd3714 100644 --- a/llvm/include/llvm/Transforms/Utils/CopyrightMetadataPass.h +++ b/llvm/include/llvm/Transforms/Utils/LowerCommentStringPass.h @@ -1,4 +1,4 @@ -//===-- CopyrightMetadataPass.h - Lower AIX copyright metadata -*- C++ -*-===// +//===-- 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. @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TRANSFORMS_UTILS_COPYRIGHTMETADATAPASS_H -#define LLVM_TRANSFORMS_UTILS_COPYRIGHTMETADATAPASS_H +#ifndef LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H +#define LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H #include "llvm/IR/PassManager.h" namespace llvm { -class CopyrightMetadataPass : public PassInfoMixin { +class LowerCommentStringPass : public PassInfoMixin { public: PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); @@ -22,4 +22,4 @@ class CopyrightMetadataPass : public PassInfoMixin { } // namespace llvm -#endif // LLVM_TRANSFORMS_UTILS_COPYRIGHTMETADATAPASS_H +#endif // LLVM_TRANSFORMS_UTILS_LOWERCOMMENTSTRINGPASS_H diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 24526d9f973ff..df95d76bfc1b1 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -347,7 +347,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/LowerCommentStringPass.h" #include "llvm/Transforms/Utils/CountVisits.h" #include "llvm/Transforms/Utils/DXILUpgrade.h" #include "llvm/Transforms/Utils/Debugify.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index cd6cd126ef2bb..99764994bfcc3 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -135,7 +135,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/LowerCommentStringPass.h" #include "llvm/Transforms/Utils/CountVisits.h" #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" #include "llvm/Transforms/Utils/ExtraPassManager.h" @@ -1456,7 +1456,7 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level, ModulePassManager MPM; // Process copyright metadata early, before any optimizations - MPM.addPass(CopyrightMetadataPass()); + MPM.addPass(LowerCommentStringPass()); // Run partial inlining pass to partially inline functions that have // large bodies. @@ -2279,7 +2279,7 @@ PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level, ModulePassManager MPM; // Process copyright metadata at O0 before any other transformations - MPM.addPass(CopyrightMetadataPass()); + MPM.addPass(LowerCommentStringPass()); // Perform pseudo probe instrumentation in O0 mode. This is for the // consistency between different build modes. For example, a LTO build can be diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 3f60ce0734a33..16f5cdb8f58fb 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -61,7 +61,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("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 13643e9f411b9..943add58cca5d 100644 --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -17,7 +17,7 @@ add_llvm_component_library(LLVMTransformUtils CodeLayout.cpp CodeMoverUtils.cpp ControlFlowUtils.cpp - CopyrightMetadataPass.cpp + LowerCommentStringPass.cpp CtorUtils.cpp CountVisits.cpp Debugify.cpp diff --git a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp similarity index 82% rename from llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp rename to llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp index 08768e05b42ee..5173a29adcaf3 100644 --- a/llvm/lib/Transforms/Utils/CopyrightMetadataPass.cpp +++ b/llvm/lib/Transforms/Utils/LowerCommentStringPass.cpp @@ -1,4 +1,4 @@ -//===-- CopyrightMetadataPass.cpp - Lower copyright metadata -------------===// +//===-- 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. @@ -6,8 +6,8 @@ // //===---------------------------------------------------------------------===// // -// CopyrightMetadataPass pass lowers module-level copyright metadata emitted by -// Clang: +// LowerCommentStringPass pass lowers module-level comment string metadata +// emitted by Clang: // // !comment_string.loadtime = !{!"Copyright ..."} // @@ -16,8 +16,8 @@ // For each module (translation unit), the pass performs the following: // // 1. Creates a null-terminated, internal constant string global -// (`__loadtime_copyright_str`) containing the copyright text in -// `__copyright_comment` section. +// (`__loadtime_comment_str`) containing the copyright text in +// `__loadtime_comment` section. // // 2. Marks the string in `llvm.used` so it cannot be dropped by // optimization or LTO. @@ -31,16 +31,16 @@ // Input IR: // !comment_string.loadtime = !{!"Copyright"} // Output IR: -// @__loadtime_copyright_str = internal constant [N x i8] c"Copyright\00", -// section "__copyright_comment" -// @llvm.used = appending global [1 x ptr] [ptr @__loadtime_copyright_str] +// @__loadtime_comment_str = internal constant [N x i8] c"Copyright\00", +// section "__loadtime_comment" +// @llvm.used = appending global [1 x ptr] [ptr @__loadtime_comment_str] // // define i32 @func() !implicit.ref !5 { ... } -// !5 = !{ptr @__loadtime_copyright_str} +// !5 = !{ptr @__loadtime_comment_str} // //===----------------------------------------------------------------------===// -#include "llvm/Transforms/Utils/CopyrightMetadataPass.h" +#include "llvm/Transforms/Utils/LowerCommentStringPass.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -61,20 +61,20 @@ #include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/Utils/ModuleUtils.h" -#define DEBUG_TYPE "copyright-metadata" +#define DEBUG_TYPE "lower-comment-string" using namespace llvm; static cl::opt - DisableCopyrightMetadata("disable-copyright-metadata", cl::ReallyHidden, - cl::desc("Disable copyright metadata pass."), + DisableCopyrightMetadata("disable-lower-comment-string", cl::ReallyHidden, + cl::desc("Disable LowerCommentString pass."), cl::init(false)); static bool isAIXTriple(const Module &M) { return Triple(M.getTargetTriple()).isOSAIX(); } -PreservedAnalyses CopyrightMetadataPass::run(Module &M, +PreservedAnalyses LowerCommentStringPass::run(Module &M, ModuleAnalysisManager &AM) { if (DisableCopyrightMetadata || !isAIXTriple(M)) return PreservedAnalyses::all(); @@ -108,13 +108,13 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, auto *StrGV = new GlobalVariable(M, StrInit->getType(), /*isConstant=*/true, GlobalValue::InternalLinkage, StrInit, - /*Name=*/"__loadtime_copyright_str"); + /*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 "__copyright_comment" section. + // Place in the "__loadtime_comment" section. // The GV is constant, so we expect a read-only section. - StrGV->setSection("__copyright_comment"); + StrGV->setSection("__loadtime_comment"); // 2. Add the string to llvm.used to prevent LLVM optimization/LTO passes from // removing it. @@ -122,7 +122,7 @@ PreservedAnalyses CopyrightMetadataPass::run(Module &M, // 3. Attach !implicit ref to every defined function // Create a metadata node pointing to the copyright string: - // !N = !{ptr @__loadtime_copyright_str} + // !N = !{ptr @__loadtime_comment_str} Metadata *Ops[] = {ConstantAsMetadata::get(StrGV)}; MDNode *ImplicitRefMD = MDNode::get(Ctx, Ops); diff --git a/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll b/llvm/test/CodeGen/AArch64/print-pipeline-passes.ll index 27e9552d35ef5..eaa4441029fff 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' -print-pipeline-passes < %s | FileCheck %s ; CHECK: loop-idiom-vectorize -; O0: {{^}}copyright-metadata,function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,function(annotation-remarks),verify,print{{$}} +; O0: {{^}}lower-comment-string,function(ee-instrument<>),always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),alloc-token,function(annotation-remarks),verify,print{{$}} define void @foo() { entry: ret void diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll index c690c2df1af82..979a7897b4c02 100644 --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -238,7 +238,7 @@ ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass -; CHECK-O-NEXT: Running pass: CopyrightMetadataPass +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-DEFAULT-NEXT: Running pass: EliminateAvailableExternallyPass ; CHECK-LTO-NOT: Running pass: EliminateAvailableExternallyPass ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll index 0f9f9abfdac28..d03b15d276e0b 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll @@ -162,7 +162,7 @@ ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalOptPass ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalDCEPass -; CHECK-POSTLINK-O-NEXT: Running pass: CopyrightMetadataPass +; CHECK-POSTLINK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-POSTLINK-O-NEXT: Running pass: EliminateAvailableExternallyPass ; CHECK-POSTLINK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass ; CHECK-POSTLINK-O-NEXT: Running pass: RecomputeGlobalsAAPass 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 528d7105a0516..b5b0d55bf410c 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll @@ -146,7 +146,7 @@ ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass -; CHECK-O-NEXT: Running pass: CopyrightMetadataPass +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass 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 2e07520462912..57561975769d0 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll @@ -155,7 +155,7 @@ ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass -; CHECK-O-NEXT: Running pass: CopyrightMetadataPass +; CHECK-O-NEXT: Running pass: LowerCommentStringPass ; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass diff --git a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll similarity index 73% rename from llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll rename to llvm/test/Transforms/LowerCommentString/lower-comment-string.ll index e613f2cdd4d5c..ba7ad17f4c4b7 100644 --- a/llvm/test/Transforms/CopyrightMetadata/copyright-metadata.ll +++ b/llvm/test/Transforms/LowerCommentString/lower-comment-string.ll @@ -1,19 +1,19 @@ -; RUN: opt -passes=copyright-metadata -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0 +; RUN: opt -passes=lower-comment-string -S %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-O0 -; Verify that copyright-metadata is enabled by default on all opt pipelines. +; 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 -; Verify that CopyrightMetadataPass lowers !loadtime.copyright.comment +; Verify that LowerCommentStringPass lowers !loadtime.copyright.comment ; into concrete, translation-unit–local globals. ; ; For each module (translation unit), the pass performs the following: ; ; 1. Creates a null-terminated, internal constant string global -; (`__loadtime_copyright_str`) containing the copyright text in -; `__copyright_comment` section. +; (`__loadtime_comment_str`) containing the copyright text in +; `__loadtime_comment` section. ; ; 2. Marks the string in `llvm.used` so it cannot be dropped by ; optimization or LTO. @@ -43,9 +43,9 @@ entry: ; ---- Globals-------------------------------------------- -; CHECK: @__loadtime_copyright_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__copyright_comment", align 1 +; CHECK: @__loadtime_comment_str = internal unnamed_addr constant [24 x i8] c"@(#) Copyright IBM 2025\00", section "__loadtime_comment", align 1 ; Preservation in llvm.used sets -; CHECK-NEXT: @llvm.used = appending global [1 x ptr] [ptr @__loadtime_copyright_str], section "llvm.metadata" +; CHECK-NEXT: @llvm.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: @@ -55,5 +55,5 @@ entry: ; CHECK-ON: define noundef i32 @main() local_unnamed_addr #0 !implicit.ref ![[MD]] ; Verify metadata content -; CHECK-O0: ![[MD]] = !{ptr @__loadtime_copyright_str} -; CHECK-ON: ![[MD]] = !{ptr @__loadtime_copyright_str} +; CHECK-O0: ![[MD]] = !{ptr @__loadtime_comment_str} +; CHECK-ON: ![[MD]] = !{ptr @__loadtime_comment_str}