[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 355898a6ce)
This commit is contained in:
Dominik Steenken 2026-01-28 10:42:54 +01:00 committed by Douglas Yung
parent 0d37e79da8
commit ca06985982
No known key found for this signature in database
GPG key ID: 316C56D064CACBA5
7 changed files with 136 additions and 5 deletions

View file

@ -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>,

View file

@ -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.
}];
}

View file

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

View file

@ -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

View file

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

View file

@ -391,6 +391,8 @@ public:
ArrayRef<std::pair<unsigned, const char *>>
getSerializableDirectMachineOperandTargetFlags() const override;
MCInst getNop() const override;
};
} // end namespace llvm

View file

@ -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",@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",@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",@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",@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,@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",@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,@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",@progbits,prefix{{$}}
; CHECK: .p2align 3, 0x0
; CHECK-NEXT: .quad .Ltmp1
ret void
}