[llvm-branch-commits] [clang] [llvm] release/22.x: [SystemZ] Enable -fpatchable-function-entry=M, N (#178191) (PR #194365)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Apr 27 06:20:28 PDT 2026


https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/194365

Backport 355898a

Requested by: @dominik-steenken

>From f799159531e24dd508efea74434432b162b9d8cb Mon Sep 17 00:00:00 2001
From: Dominik Steenken <dost at de.ibm.com>
Date: Wed, 28 Jan 2026 10:42:54 +0100
Subject: [PATCH] [SystemZ] Enable -fpatchable-function-entry=M,N (#178191)

This PR enables the option `-fpatchable-function-entry` for SystemZ. It
utilizes existing common code and just adds the emission of nops after
the function label in the backend.

SystemZ provides multiple nop options of varying length, making the
semantics of this option somewhat ambiguous. In order to align with what
`gcc` does with that same option, we#re choosing `nopr` as the
canoonical nop for this purpose.

For test, this adapts an existing test file from aarch64.

(cherry picked from commit 355898a6ce901bf9285a428888068e008b5557e9)
---
 clang/include/clang/Basic/Attr.td             |   7 +-
 clang/include/clang/Basic/AttrDocs.td         |   2 +-
 clang/lib/Driver/ToolChains/Clang.cpp         |   2 +-
 llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp |  21 ++++
 llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp  |   5 +
 llvm/lib/Target/SystemZ/SystemZInstrInfo.h    |   2 +
 .../SystemZ/patchable-function-entry.ll       | 102 ++++++++++++++++++
 7 files changed, 136 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/CodeGen/SystemZ/patchable-function-entry.ll

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index efa64dbe4b51e..e98592f62b023 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -967,9 +967,10 @@ def XRayLogArgs : InheritableAttr {
 
 def PatchableFunctionEntry
     : InheritableAttr,
-      TargetSpecificAttr<TargetArch<
-          ["aarch64", "aarch64_be", "loongarch32", "loongarch64", "riscv32",
-           "riscv64", "x86", "x86_64", "ppc", "ppc64", "ppc64le"]>> {
+      TargetSpecificAttr<
+          TargetArch<["aarch64", "aarch64_be", "loongarch32", "loongarch64",
+                      "riscv32", "riscv64", "x86", "x86_64", "ppc", "ppc64",
+                      "ppc64le", "systemz"]>> {
   let Spellings = [GCC<"patchable_function_entry">];
   let Subjects = SubjectList<[Function, ObjCMethod]>;
   let Args = [UnsignedArgument<"Count">, DefaultIntArgument<"Offset", 0>,
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 812b48058d189..3854e0dff88f3 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6749,7 +6749,7 @@ if omitted.``Section`` defaults  to the ``-fpatchable-function-entry`` section n
 set, or to ``__patchable_function_entries`` otherwise.
 
 This attribute is only supported on
-aarch64/aarch64-be/loongarch32/loongarch64/riscv32/riscv64/i386/x86-64/ppc/ppc64/ppc64le targets.
+aarch64/aarch64-be/loongarch32/loongarch64/riscv32/riscv64/i386/x86-64/ppc/ppc64/ppc64le/s390x targets.
 For ppc/ppc64 targets, AIX is still not supported.
 }];
 }
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 18933cb8ad7c2..3fd5eef60b019 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6777,7 +6777,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     StringRef S0 = A->getValue(), S = S0;
     unsigned Size, Offset = 0;
     if (!Triple.isAArch64() && !Triple.isLoongArch() && !Triple.isRISCV() &&
-        !Triple.isX86() &&
+        !Triple.isX86() && !Triple.isSystemZ() &&
         !(!Triple.isOSAIX() && (Triple.getArch() == llvm::Triple::ppc ||
                                 Triple.getArch() == llvm::Triple::ppc64 ||
                                 Triple.getArch() == llvm::Triple::ppc64le)))
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index fcde175b42643..15ac5a4af0d29 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
@@ -901,6 +901,27 @@ void SystemZAsmPrinter::LowerPATCHPOINT(const MachineInstr &MI,
 
 void SystemZAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(
     const MachineInstr &MI, SystemZMCInstLower &Lower) {
+
+  const MachineFunction &MF = *(MI.getParent()->getParent());
+  const Function &F = MF.getFunction();
+
+  // If patchable-function-entry is set, emit in-function nops here.
+  if (F.hasFnAttribute("patchable-function-entry")) {
+    unsigned Num;
+    // get M-N from function attribute (CodeGenFunction subtracts N
+    // from M to yield the correct patchable-function-entry).
+    if (F.getFnAttribute("patchable-function-entry")
+            .getValueAsString()
+            .getAsInteger(10, Num))
+      return;
+    // Emit M-N 2-byte nops. Use getNop() here instead of emitNops()
+    // to keep it aligned with the common code implementation emitting
+    // the prefix nops.
+    for (unsigned I = 0; I < Num; ++I)
+      EmitToStreamer(*OutStreamer, MF.getSubtarget().getInstrInfo()->getNop());
+    return;
+  }
+  // Otherwise, emit xray sled.
   // .begin:
   //   j .end    # -> stmg    %r2, %r15, 16(%r15)
   //   nop
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp b/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp
index 913c62f9f8449..6397f237666e6 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp
@@ -33,6 +33,7 @@
 #include "llvm/CodeGen/TargetOpcodes.h"
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
 #include "llvm/CodeGen/VirtRegMap.h"
+#include "llvm/MC/MCInstBuilder.h"
 #include "llvm/MC/MCInstrDesc.h"
 #include "llvm/MC/MCRegisterInfo.h"
 #include "llvm/Support/BranchProbability.h"
@@ -2378,3 +2379,7 @@ SystemZInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
       {MO_ADA_DIRECT_FUNC_DESC, "systemz-ada-directfuncdesc"}};
   return ArrayRef(TargetFlags);
 }
+
+MCInst SystemZInstrInfo::getNop() const {
+  return MCInstBuilder(SystemZ::NOPR).addReg(0);
+}
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h
index 6fc66c72a25e6..29ae9e0ed80e7 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h
@@ -391,6 +391,8 @@ class SystemZInstrInfo : public SystemZGenInstrInfo {
 
   ArrayRef<std::pair<unsigned, const char *>>
   getSerializableDirectMachineOperandTargetFlags() const override;
+
+  MCInst getNop() const override;
 };
 
 } // end namespace llvm
diff --git a/llvm/test/CodeGen/SystemZ/patchable-function-entry.ll b/llvm/test/CodeGen/SystemZ/patchable-function-entry.ll
new file mode 100644
index 0000000000000..24c5ab4502bf0
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/patchable-function-entry.ll
@@ -0,0 +1,102 @@
+; RUN: llc -mtriple=s390x-ibm-linux %s -o - | FileCheck %s
+; RUN: llc -mtriple=s390x-ibm-linux -function-sections %s -o - | FileCheck %s
+; RUN: llc -mtriple=s390x-ibm-linux -function-sections %s -o - | FileCheck %s
+; RUN: llc -mtriple=s390x-ibm-linux -no-integrated-as -binutils-version=2.35 %s -o - | FileCheck --check-prefix=NOLINK %s
+; RUN: llc -mtriple=s390x-ibm-linux -no-integrated-as -binutils-version=2.36 %s -o - | FileCheck %s
+
+;; GNU ld < 2.36 did not support mixed SHF_LINK_ORDER and non-SHF_LINK_ORDER sections.
+; NOLINK-NOT: "awo"
+
+define i32 @f0() "patchable-function-entry"="0" {
+; CHECK-LABEL: f0:
+; CHECK-NEXT: .Lfunc_begin0:
+; CHECK-NOT:   nopr
+; CHECK:       lhi     %r2, 0
+; CHECK-NOT:   .section __patchable_function_entries
+  ret i32 0
+}
+
+define i32 @f1() "patchable-function-entry"="1" {
+; CHECK-LABEL: f1:
+; CHECK-NEXT: .Lfunc_begin1:
+; CHECK:       nopr
+; CHECK-NEXT:  lhi     %r2, 0
+; CHECK:       .section __patchable_function_entries,"awo", at progbits,f1{{$}}
+; CHECK-NEXT:  .p2align 3, 0x0
+; CHECK-NEXT: .quad   .Lfunc_begin1
+  ret i32 0
+}
+
+;; Without -function-sections, f2 is in the same text section as f1.
+;; They share the __patchable_function_entries section.
+;; With -function-sections, f1 and f2 are in different text sections.
+;; Use separate __patchable_function_entries.
+define void @f2() "patchable-function-entry"="2" {
+; CHECK-LABEL: f2:
+; CHECK-NEXT: .Lfunc_begin2:
+; CHECK-COUNT-2: nopr
+; CHECK-NEXT:  br      %r14
+; CHECK:       .section __patchable_function_entries,"awo", at progbits,f2{{$}}
+; CHECK-NEXT:  .p2align 3, 0x0
+; CHECK-NEXT: .quad   .Lfunc_begin2
+  ret void
+}
+
+$f3 = comdat any
+define void @f3() "patchable-function-entry"="3" comdat {
+; CHECK-LABEL: f3:
+; CHECK-NEXT: .Lfunc_begin3:
+; CHECK-COUNT-3: nopr
+; CHECK-NEXT:  br      %r14
+; CHECK:       .section __patchable_function_entries,"awoG", at progbits,f3,f3,comdat{{$}}
+; CHECK-NEXT:  .p2align 3, 0x0
+; CHECK-NEXT: .quad   .Lfunc_begin3
+  ret void
+}
+
+$f5 = comdat any
+define void @f5() "patchable-function-entry"="5" comdat {
+; CHECK-LABEL: f5:
+; CHECK-NEXT: .Lfunc_begin4:
+; CHECK-COUNT-5: nopr
+; CHECK-NEXT:  aghi    %r15, -176
+; CHECK:       .section __patchable_function_entries,"awoG", at progbits,f5,f5,comdat{{$}}
+; CHECK:       .p2align 3, 0x0
+; CHECK-NEXT: .quad   .Lfunc_begin4
+  %frame = alloca i8, i32 16
+  ret void
+}
+
+;; -fpatchable-function-entry=3,2
+;; "patchable-function-prefix" emits data before the function entry label.
+define void @f3_2() "patchable-function-entry"="1" "patchable-function-prefix"="2" {
+; CHECK-LABEL: .type f3_2, at function
+; CHECK-NEXT: .Ltmp0:
+; CHECK-NEXT:  nopr
+; CHECK-NEXT:  nopr
+; CHECK-NEXT: f3_2:  # @f3_2
+; CHECK:      # %bb.0:
+; CHECK-NEXT:  nopr
+; CHECK-NEXT:  br      %r14
+;; .size does not include the prefix.
+; CHECK:      .Lfunc_end5:
+; CHECK-NEXT: .size f3_2, .Lfunc_end5-f3_2
+; CHECK:      .section __patchable_function_entries,"awo", at progbits,f3_2{{$}}
+; CHECK:      .p2align 3, 0x0
+; CHECK-NEXT: .quad   .Ltmp0
+  ret void
+}
+
+;; When prefix data is used, arbitrarily place noprs after prefix data.
+define void @prefix() "patchable-function-entry"="0" "patchable-function-prefix"="1" prefix i32 1 {
+; CHECK-LABEL: .type prefix, at function
+; CHECK-NEXT: .long 1 # 0x1
+; CHECK:      .Ltmp1:
+; CHECK:       nopr
+; CHECK-NEXT: prefix:  # @prefix
+;; Emit a __patchable_function_entries entry even if "patchable-function-entry" is 0.
+; CHECK:      .section __patchable_function_entries,"awo", at progbits,prefix{{$}}
+; CHECK:      .p2align        3, 0x0
+; CHECK-NEXT: .quad   .Ltmp1
+  ret void
+}



More information about the llvm-branch-commits mailing list