Skip to content

SystemZ: Add support for __builtin_setjmp and __builtin_longjmp. #119257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7988037
Support for __builtin_setjmp and __builtin_longjmp
anoopkg6 Oct 9, 2024
f8040a3
Adding test case for builtin setjmp/longjmp.
anoopkg6 Oct 25, 2024
7500b72
Incorporating code review feedback and adding test cases for builtin …
anoopkg6 Oct 25, 2024
4fa2914
Not to check FP live in setjmp.
anoopkg6 Oct 25, 2024
8211419
Merge branch 'main' of https://github.com/llvm/llvm-project
anoopkg6 Oct 25, 2024
b289df9
Remove store of R13.
anoopkg6 Oct 25, 2024
a043372
Incorporate code review changes and added new test cases.
anoopkg6 Oct 30, 2024
fb082f6
Adding test cases for setjmp/longjmp.
anoopkg6 Nov 9, 2024
2a5bd18
Incorporated code-review changes
anoopkg6 Nov 22, 2024
68aadd2
Incorporated code-review changes.
anoopkg6 Nov 22, 2024
e8c2a9c
Remove all test files first to include compile only test cases.
anoopkg6 Nov 22, 2024
98ffcee
Incorporated Code Review, git-clang-format feeback and compile-only t…
anoopkg6 Nov 25, 2024
90afe97
Fix CI failure hoisted on an Intel Machine - adding option -mtriple=s…
anoopkg6 Nov 25, 2024
81fe8ec
To redo after review feeback.
anoopkg6 Nov 29, 2024
2dd9f4e
Add test cases for builtin setjmp/longjm.
anoopkg6 Dec 2, 2024
82a49e2
Incorporated code review changes and added alloca and spills test cases.
anoopkg6 Dec 5, 2024
ec796f4
Update to Excecption Handling(llvm.eh.sjlj.setjmp) documentation and …
anoopkg6 Dec 6, 2024
caf16d6
Update ExceptionHandling (llvm.eh.sjlj.setjmp) document with target s…
anoopkg6 Dec 6, 2024
4ef3fbe
Builbot Failure fix:
anoopkg6 Dec 9, 2024
a0c48b4
Just run llvm tests with -verify-machineinstrs and minor change to Ex…
anoopkg6 Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/SystemZ.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
return RegNo < 4 ? 6 + RegNo : -1;
}

bool hasSjLjLowering() const override { return true; }

std::pair<unsigned, unsigned> hardwareInterferenceSizes() const override {
return std::make_pair(256, 256);
}
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4619,6 +4619,14 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
// Buffer is a void**.
Address Buf = EmitPointerWithAlignment(E->getArg(0));

if (getTarget().getTriple().getArch() == llvm::Triple::systemz) {
// On this target, the back end fills in the context buffer completely.
// It doesn't really matter if the frontend stores to the buffer before
// calling setjmp, the back-end is going to overwrite them anyway.
Function *F = CGM.getIntrinsic(Intrinsic::eh_sjlj_setjmp);
return RValue::get(Builder.CreateCall(F, Buf.emitRawPointer(*this)));
}

// Store the frame pointer to the setjmp buffer.
Value *FrameAddr = Builder.CreateCall(
CGM.getIntrinsic(Intrinsic::frameaddress, AllocaInt8PtrTy),
Expand Down
26 changes: 26 additions & 0 deletions clang/test/CodeGen/SystemZ/builtin-setjmp-logjmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// RUN: %clang_cc1 -triple s390x-linux -emit-llvm -o - %s | FileCheck %s

void *buf[20];

// CHECK-LABEL: define dso_local void @foo(
// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.eh.sjlj.setjmp(ptr @buf)
// CHECK-NEXT: ret void
//
void foo()
{
__builtin_setjmp (buf);
}

// CHECK-LABEL: define dso_local void @foo1(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: call void @llvm.eh.sjlj.longjmp(ptr @buf)
// CHECK-NEXT: unreachable
//
void foo1()
{
__builtin_longjmp (buf, 1);
}
10 changes: 6 additions & 4 deletions llvm/docs/ExceptionHandling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -374,10 +374,12 @@ overall functioning of this intrinsic is compatible with the GCC
to interoperate.

The single parameter is a pointer to a five word buffer in which the calling
context is saved. The front end places the frame pointer in the first word, and
the target implementation of this intrinsic should place the destination address
for a `llvm.eh.sjlj.longjmp`_ in the second word. The following three words are
available for use in a target-specific manner.
context is saved. The format and contents of the buffer are target-specific.
On certain targets (ARM, PowerPC, VE, X86), the front end places the
frame pointer in the first word and the stack pointer in the third word,
while the target implementation of this intrinsic fills in the remaining
words. On other targets (SystemZ), saving the calling context to the buffer
is left completely to the target implementation.

.. _llvm.eh.sjlj.longjmp:

Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,11 @@ void SystemZAsmPrinter::emitInstruction(const MachineInstr *MI) {
return;
}

// EH_SjLj_Setup is a dummy terminator instruction of size 0.
// It is used to handle the clobber register for builtin setjmp.
case SystemZ::EH_SjLj_Setup:
return;

default:
Lower.lower(MI, LoweredMI);
break;
Expand Down
252 changes: 252 additions & 0 deletions llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,11 @@ SystemZTargetLowering::SystemZTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom);
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);

// We're not using SJLJ for exception handling, but they're implemented
// solely to support use of __builtin_setjmp / __builtin_longjmp.
setOperationAction(ISD::EH_SJLJ_SETJMP, MVT::i32, Custom);
setOperationAction(ISD::EH_SJLJ_LONGJMP, MVT::Other, Custom);

// We want to use MVC in preference to even a single load/store pair.
MaxStoresPerMemcpy = Subtarget.hasVector() ? 2 : 0;
MaxStoresPerMemcpyOptSize = 0;
Expand Down Expand Up @@ -940,6 +945,241 @@ bool SystemZTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
return SystemZVectorConstantInfo(Imm).isVectorConstantLegal(Subtarget);
}

MachineBasicBlock *
SystemZTargetLowering::emitEHSjLjSetJmp(MachineInstr &MI,
MachineBasicBlock *MBB) const {
DebugLoc DL = MI.getDebugLoc();
const TargetInstrInfo *TII = Subtarget.getInstrInfo();
const SystemZRegisterInfo *TRI = Subtarget.getRegisterInfo();

MachineFunction *MF = MBB->getParent();
MachineRegisterInfo &MRI = MF->getRegInfo();

const BasicBlock *BB = MBB->getBasicBlock();
MachineFunction::iterator I = ++MBB->getIterator();

Register DstReg = MI.getOperand(0).getReg();
const TargetRegisterClass *RC = MRI.getRegClass(DstReg);
assert(TRI->isTypeLegalForClass(*RC, MVT::i32) && "Invalid destination!");
(void)TRI;
Register mainDstReg = MRI.createVirtualRegister(RC);
Register restoreDstReg = MRI.createVirtualRegister(RC);

MVT PVT = getPointerTy(MF->getDataLayout());
assert((PVT == MVT::i64 || PVT == MVT::i32) && "Invalid Pointer Size!");
// For v = setjmp(buf), we generate.
// Algorithm:
//
// ---------
// | thisMBB |
// ---------
// |
// ------------------------
// | |
// ---------- ---------------
// | mainMBB | | restoreMBB |
// | v = 0 | | v = 1 |
// ---------- ---------------
// | |
// -------------------------
// |
// -----------------------------
// | sinkMBB |
// | phi(v_mainMBB,v_restoreMBB) |
// -----------------------------
// thisMBB:
// buf[FPOffset] = Frame Pointer if hasFP.
// buf[LabelOffset] = restoreMBB <-- takes address of restoreMBB.
// buf[BCOffset] = Backchain value if building with -mbackchain.
// buf[SPOffset] = Stack Pointer.
// buf[LPOffset] = We never write this slot with R13, gcc stores R13 always.
// SjLjSetup restoreMBB
// mainMBB:
// v_main = 0
// sinkMBB:
// v = phi(v_main, v_restore)
// restoreMBB:
// v_restore = 1

MachineBasicBlock *thisMBB = MBB;
MachineBasicBlock *mainMBB = MF->CreateMachineBasicBlock(BB);
MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(BB);
MachineBasicBlock *restoreMBB = MF->CreateMachineBasicBlock(BB);

MF->insert(I, mainMBB);
MF->insert(I, sinkMBB);
MF->push_back(restoreMBB);
restoreMBB->setMachineBlockAddressTaken();

MachineInstrBuilder MIB;

// Transfer the remainder of BB and its successor edges to sinkMBB.
sinkMBB->splice(sinkMBB->begin(), MBB,
std::next(MachineBasicBlock::iterator(MI)), MBB->end());
sinkMBB->transferSuccessorsAndUpdatePHIs(MBB);

// thisMBB:
const int64_t FPOffset = 0; // Slot 1.
const int64_t LabelOffset = 1 * PVT.getStoreSize(); // Slot 2.
const int64_t BCOffset = 2 * PVT.getStoreSize(); // Slot 3.
const int64_t SPOffset = 3 * PVT.getStoreSize(); // Slot 4.

// Buf address.
Register BufReg = MI.getOperand(1).getReg();

const TargetRegisterClass *PtrRC = getRegClassFor(PVT);
unsigned LabelReg = MRI.createVirtualRegister(PtrRC);

// Prepare IP for longjmp.
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::LARL), LabelReg)
.addMBB(restoreMBB);
// Store IP for return from jmp, slot 2, offset = 1.
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
.addReg(LabelReg)
.addReg(BufReg)
.addImm(LabelOffset)
.addReg(0);

auto *SpecialRegs = Subtarget.getSpecialRegisters();
bool HasFP = Subtarget.getFrameLowering()->hasFP(*MF);
if (HasFP) {
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
.addReg(SpecialRegs->getFramePointerRegister())
.addReg(BufReg)
.addImm(FPOffset)
.addReg(0);
}

// Store SP.
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
.addReg(SpecialRegs->getStackPointerRegister())
.addReg(BufReg)
.addImm(SPOffset)
.addReg(0);

// Slot 3(Offset = 2) Backchain value (if building with -mbackchain).
bool BackChain = MF->getSubtarget<SystemZSubtarget>().hasBackChain();
if (BackChain) {
Register BCReg = MRI.createVirtualRegister(PtrRC);
auto *TFL = Subtarget.getFrameLowering<SystemZFrameLowering>();
MIB = BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::LG), BCReg)
.addReg(SpecialRegs->getStackPointerRegister())
.addImm(TFL->getBackchainOffset(*MF))
.addReg(0);

BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
.addReg(BCReg)
.addReg(BufReg)
.addImm(BCOffset)
.addReg(0);
}

// Setup.
MIB = BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::EH_SjLj_Setup))
.addMBB(restoreMBB);

const SystemZRegisterInfo *RegInfo = Subtarget.getRegisterInfo();
MIB.addRegMask(RegInfo->getNoPreservedMask());

thisMBB->addSuccessor(mainMBB);
thisMBB->addSuccessor(restoreMBB);

// mainMBB:
BuildMI(mainMBB, DL, TII->get(SystemZ::LHI), mainDstReg).addImm(0);
mainMBB->addSuccessor(sinkMBB);

// sinkMBB:
BuildMI(*sinkMBB, sinkMBB->begin(), DL, TII->get(SystemZ::PHI), DstReg)
.addReg(mainDstReg)
.addMBB(mainMBB)
.addReg(restoreDstReg)
.addMBB(restoreMBB);

// restoreMBB.
BuildMI(restoreMBB, DL, TII->get(SystemZ::LHI), restoreDstReg).addImm(1);
BuildMI(restoreMBB, DL, TII->get(SystemZ::J)).addMBB(sinkMBB);
restoreMBB->addSuccessor(sinkMBB);

MI.eraseFromParent();

return sinkMBB;
}

MachineBasicBlock *
SystemZTargetLowering::emitEHSjLjLongJmp(MachineInstr &MI,
MachineBasicBlock *MBB) const {

DebugLoc DL = MI.getDebugLoc();
const TargetInstrInfo *TII = Subtarget.getInstrInfo();

MachineFunction *MF = MBB->getParent();
MachineRegisterInfo &MRI = MF->getRegInfo();

MVT PVT = getPointerTy(MF->getDataLayout());
assert((PVT == MVT::i64 || PVT == MVT::i32) && "Invalid Pointer Size!");
Register BufReg = MI.getOperand(0).getReg();
const TargetRegisterClass *RC = MRI.getRegClass(BufReg);
auto *SpecialRegs = Subtarget.getSpecialRegisters();

Register Tmp = MRI.createVirtualRegister(RC);
Register BCReg = MRI.createVirtualRegister(RC);

MachineInstrBuilder MIB;

const int64_t FPOffset = 0;
const int64_t LabelOffset = 1 * PVT.getStoreSize();
const int64_t BCOffset = 2 * PVT.getStoreSize();
const int64_t SPOffset = 3 * PVT.getStoreSize();
const int64_t LPOffset = 4 * PVT.getStoreSize();

MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG), Tmp)
.addReg(BufReg)
.addImm(LabelOffset)
.addReg(0);

MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG),
SpecialRegs->getFramePointerRegister())
.addReg(BufReg)
.addImm(FPOffset)
.addReg(0);

// We are restoring R13 even though we never stored in setjmp from llvm,
// as gcc always stores R13 in builtin_setjmp. We could have mixed code
// gcc setjmp and llvm longjmp.
MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG), SystemZ::R13D)
.addReg(BufReg)
.addImm(LPOffset)
.addReg(0);

bool BackChain = MF->getSubtarget<SystemZSubtarget>().hasBackChain();
if (BackChain) {
MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG), BCReg)
.addReg(BufReg)
.addImm(BCOffset)
.addReg(0);
}

MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG),
SpecialRegs->getStackPointerRegister())
.addReg(BufReg)
.addImm(SPOffset)
.addReg(0);

if (BackChain) {
auto *TFL = Subtarget.getFrameLowering<SystemZFrameLowering>();
BuildMI(*MBB, MI, DL, TII->get(SystemZ::STG))
.addReg(BCReg)
.addReg(SpecialRegs->getStackPointerRegister())
.addImm(TFL->getBackchainOffset(*MF))
.addReg(0);
}

MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::BR)).addReg(Tmp);

MI.eraseFromParent();
return MBB;
}

/// Returns true if stack probing through inline assembly is requested.
bool SystemZTargetLowering::hasInlineStackProbe(const MachineFunction &MF) const {
// If the function specifically requests inline stack probes, emit them.
Expand Down Expand Up @@ -6292,6 +6532,14 @@ SDValue SystemZTargetLowering::LowerOperation(SDValue Op,
return lowerGET_ROUNDING(Op, DAG);
case ISD::READCYCLECOUNTER:
return lowerREADCYCLECOUNTER(Op, DAG);
case ISD::EH_SJLJ_SETJMP:
case ISD::EH_SJLJ_LONGJMP:
// These operations are legal on our platform, but we cannot actually
// set the operation action to Legal as common code would treat this
// as equivalent to Expand. Instead, we keep the operation action to
// Custom and just leave them unchanged here.
return Op;

default:
llvm_unreachable("Unexpected node to lower");
}
Expand Down Expand Up @@ -9724,6 +9972,10 @@ MachineBasicBlock *SystemZTargetLowering::EmitInstrWithCustomInserter(

case SystemZ::PROBED_ALLOCA:
return emitProbedAlloca(MI, MBB);
case SystemZ::EH_SjLj_SetJmp:
return emitEHSjLjSetJmp(MI, MBB);
case SystemZ::EH_SjLj_LongJmp:
return emitEHSjLjLongJmp(MI, MBB);

case TargetOpcode::STACKMAP:
case TargetOpcode::PATCHPOINT:
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/SystemZ/SystemZISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,12 @@ class SystemZTargetLowering : public TargetLowering {
// LD, and having the full constant in memory enables reg/mem opcodes.
return VT != MVT::f64;
}
MachineBasicBlock *emitEHSjLjSetJmp(MachineInstr &MI,
MachineBasicBlock *MBB) const;

MachineBasicBlock *emitEHSjLjLongJmp(MachineInstr &MI,
MachineBasicBlock *MBB) const;

bool hasInlineStackProbe(const MachineFunction &MF) const override;
AtomicExpansionKind shouldCastAtomicLoadInIR(LoadInst *LI) const override;
AtomicExpansionKind shouldCastAtomicStoreInIR(StoreInst *SI) const override;
Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/Target/SystemZ/SystemZInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,23 @@ let mayLoad = 1, mayStore = 1, Defs = [CC] in {
}
}

//--------------------------------------------------------------------------
// Setjmp/Longjmp.
//--------------------------------------------------------------------------
let isBarrier = 1, hasNoSchedulingInfo = 1 in {
let hasSideEffects = 1, usesCustomInserter = 1 in {
def EH_SjLj_SetJmp : Pseudo<(outs GR32:$dst), (ins ADDR64:$R2),
[(set GR32:$dst, (z_eh_sjlj_setjmp ADDR64:$R2))]>;
let isTerminator = 1 in {
def EH_SjLj_LongJmp : Pseudo<(outs), (ins ADDR64:$R2),
[(z_eh_sjlj_longjmp ADDR64:$R2)]>;
}
}
let isTerminator = 1, isCodeGenOnly = 1, Size = 0 in {
def EH_SjLj_Setup : Pseudo<(outs), (ins brtarget32:$dst), []>;
}
}

//===----------------------------------------------------------------------===//
// Message-security assist
//===----------------------------------------------------------------------===//
Expand Down
Loading
Loading