Skip to content

Commit 030bbc9

Browse files
authored
[SystemZ] Add support for __builtin_setjmp and __builtin_longjmp (llvm#116642)
Implementation for __builtin_setjmp and __builtin_longjmp for SystemZ.
1 parent 01d8e0f commit 030bbc9

16 files changed

+923
-5
lines changed

clang/lib/Basic/Targets/SystemZ.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
247247
return RegNo < 4 ? 6 + RegNo : -1;
248248
}
249249

250+
bool hasSjLjLowering() const override { return true; }
251+
250252
std::pair<unsigned, unsigned> hardwareInterferenceSizes() const override {
251253
return std::make_pair(256, 256);
252254
}

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4860,6 +4860,14 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
48604860
// Buffer is a void**.
48614861
Address Buf = EmitPointerWithAlignment(E->getArg(0));
48624862

4863+
if (getTarget().getTriple().getArch() == llvm::Triple::systemz) {
4864+
// On this target, the back end fills in the context buffer completely.
4865+
// It doesn't really matter if the frontend stores to the buffer before
4866+
// calling setjmp, the back-end is going to overwrite them anyway.
4867+
Function *F = CGM.getIntrinsic(Intrinsic::eh_sjlj_setjmp);
4868+
return RValue::get(Builder.CreateCall(F, Buf.emitRawPointer(*this)));
4869+
}
4870+
48634871
// Store the frame pointer to the setjmp buffer.
48644872
Value *FrameAddr = Builder.CreateCall(
48654873
CGM.getIntrinsic(Intrinsic::frameaddress, AllocaInt8PtrTy),
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
2+
// RUN: %clang --target=s390x-linux -S -emit-llvm -o - %s | FileCheck %s
3+
4+
void *buf[20];
5+
// CHECK-LABEL: define dso_local void @foo(
6+
// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
7+
// CHECK-NEXT: [[ENTRY:.*:]]
8+
// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.eh.sjlj.setjmp(ptr @buf)
9+
// CHECK-NEXT: ret void
10+
//
11+
void foo()
12+
{
13+
__builtin_setjmp (buf);
14+
}
15+
16+
// CHECK-LABEL: define dso_local void @foo1(
17+
// CHECK-SAME: ) #[[ATTR0]] {
18+
// CHECK-NEXT: [[ENTRY:.*:]]
19+
// CHECK-NEXT: call void @llvm.eh.sjlj.longjmp(ptr @buf)
20+
// CHECK-NEXT: unreachable
21+
//
22+
void foo1()
23+
{
24+
__builtin_longjmp (buf, 1);
25+
}

llvm/docs/ExceptionHandling.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -374,10 +374,11 @@ overall functioning of this intrinsic is compatible with the GCC
374374
to interoperate.
375375

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

382383
.. _llvm.eh.sjlj.longjmp:
383384

llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,11 @@ void SystemZAsmPrinter::emitInstruction(const MachineInstr *MI) {
705705
return;
706706
}
707707

708+
// EH_SjLj_Setup is a dummy terminator instruction of size 0.
709+
// It is used to handle the clobber register for builtin setjmp.
710+
case SystemZ::EH_SjLj_Setup:
711+
return;
712+
708713
default:
709714
Lower.lower(MI, LoweredMI);
710715
break;

llvm/lib/Target/SystemZ/SystemZISelLowering.cpp

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,11 @@ SystemZTargetLowering::SystemZTargetLowering(const TargetMachine &TM,
751751
setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom);
752752
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
753753

754+
// We're not using SJLJ for exception handling, but they're implemented
755+
// solely to support use of __builtin_setjmp / __builtin_longjmp.
756+
setOperationAction(ISD::EH_SJLJ_SETJMP, MVT::i32, Custom);
757+
setOperationAction(ISD::EH_SJLJ_LONGJMP, MVT::Other, Custom);
758+
754759
// We want to use MVC in preference to even a single load/store pair.
755760
MaxStoresPerMemcpy = Subtarget.hasVector() ? 2 : 0;
756761
MaxStoresPerMemcpyOptSize = 0;
@@ -940,6 +945,240 @@ bool SystemZTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
940945
return SystemZVectorConstantInfo(Imm).isVectorConstantLegal(Subtarget);
941946
}
942947

948+
MachineBasicBlock *
949+
SystemZTargetLowering::emitEHSjLjSetJmp(MachineInstr &MI,
950+
MachineBasicBlock *MBB) const {
951+
DebugLoc DL = MI.getDebugLoc();
952+
const TargetInstrInfo *TII = Subtarget.getInstrInfo();
953+
const SystemZRegisterInfo *TRI = Subtarget.getRegisterInfo();
954+
955+
MachineFunction *MF = MBB->getParent();
956+
MachineRegisterInfo &MRI = MF->getRegInfo();
957+
958+
const BasicBlock *BB = MBB->getBasicBlock();
959+
MachineFunction::iterator I = ++MBB->getIterator();
960+
961+
Register DstReg = MI.getOperand(0).getReg();
962+
const TargetRegisterClass *RC = MRI.getRegClass(DstReg);
963+
assert(TRI->isTypeLegalForClass(*RC, MVT::i32) && "Invalid destination!");
964+
Register mainDstReg = MRI.createVirtualRegister(RC);
965+
Register restoreDstReg = MRI.createVirtualRegister(RC);
966+
967+
MVT PVT = getPointerTy(MF->getDataLayout());
968+
assert((PVT == MVT::i64 || PVT == MVT::i32) && "Invalid Pointer Size!");
969+
// For v = setjmp(buf), we generate.
970+
// Algorithm:
971+
//
972+
// ---------
973+
// | thisMBB |
974+
// ---------
975+
// |
976+
// ------------------------
977+
// | |
978+
// ---------- ---------------
979+
// | mainMBB | | restoreMBB |
980+
// | v = 0 | | v = 1 |
981+
// ---------- ---------------
982+
// | |
983+
// -------------------------
984+
// |
985+
// -----------------------------
986+
// | sinkMBB |
987+
// | phi(v_mainMBB,v_restoreMBB) |
988+
// -----------------------------
989+
// thisMBB:
990+
// buf[FPOffset] = Frame Pointer if hasFP.
991+
// buf[LabelOffset] = restoreMBB <-- takes address of restoreMBB.
992+
// buf[BCOffset] = Backchain value if building with -mbackchain.
993+
// buf[SPOffset] = Stack Pointer.
994+
// buf[LPOffset] = We never write this slot with R13, gcc stores R13 always.
995+
// SjLjSetup restoreMBB
996+
// mainMBB:
997+
// v_main = 0
998+
// sinkMBB:
999+
// v = phi(v_main, v_restore)
1000+
// restoreMBB:
1001+
// v_restore = 1
1002+
1003+
MachineBasicBlock *thisMBB = MBB;
1004+
MachineBasicBlock *mainMBB = MF->CreateMachineBasicBlock(BB);
1005+
MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(BB);
1006+
MachineBasicBlock *restoreMBB = MF->CreateMachineBasicBlock(BB);
1007+
1008+
MF->insert(I, mainMBB);
1009+
MF->insert(I, sinkMBB);
1010+
MF->push_back(restoreMBB);
1011+
restoreMBB->setMachineBlockAddressTaken();
1012+
1013+
MachineInstrBuilder MIB;
1014+
1015+
// Transfer the remainder of BB and its successor edges to sinkMBB.
1016+
sinkMBB->splice(sinkMBB->begin(), MBB,
1017+
std::next(MachineBasicBlock::iterator(MI)), MBB->end());
1018+
sinkMBB->transferSuccessorsAndUpdatePHIs(MBB);
1019+
1020+
// thisMBB:
1021+
const int64_t FPOffset = 0; // Slot 1.
1022+
const int64_t LabelOffset = 1 * PVT.getStoreSize(); // Slot 2.
1023+
const int64_t BCOffset = 2 * PVT.getStoreSize(); // Slot 3.
1024+
const int64_t SPOffset = 3 * PVT.getStoreSize(); // Slot 4.
1025+
1026+
// Buf address.
1027+
Register BufReg = MI.getOperand(1).getReg();
1028+
1029+
const TargetRegisterClass *PtrRC = getRegClassFor(PVT);
1030+
unsigned LabelReg = MRI.createVirtualRegister(PtrRC);
1031+
1032+
// Prepare IP for longjmp.
1033+
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::LARL), LabelReg)
1034+
.addMBB(restoreMBB);
1035+
// Store IP for return from jmp, slot 2, offset = 1.
1036+
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
1037+
.addReg(LabelReg)
1038+
.addReg(BufReg)
1039+
.addImm(LabelOffset)
1040+
.addReg(0);
1041+
1042+
auto *SpecialRegs = Subtarget.getSpecialRegisters();
1043+
bool HasFP = Subtarget.getFrameLowering()->hasFP(*MF);
1044+
if (HasFP) {
1045+
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
1046+
.addReg(SpecialRegs->getFramePointerRegister())
1047+
.addReg(BufReg)
1048+
.addImm(FPOffset)
1049+
.addReg(0);
1050+
}
1051+
1052+
// Store SP.
1053+
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
1054+
.addReg(SpecialRegs->getStackPointerRegister())
1055+
.addReg(BufReg)
1056+
.addImm(SPOffset)
1057+
.addReg(0);
1058+
1059+
// Slot 3(Offset = 2) Backchain value (if building with -mbackchain).
1060+
bool BackChain = MF->getSubtarget<SystemZSubtarget>().hasBackChain();
1061+
if (BackChain) {
1062+
Register BCReg = MRI.createVirtualRegister(RC);
1063+
auto *TFL = Subtarget.getFrameLowering<SystemZFrameLowering>();
1064+
MIB = BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::LG), BCReg)
1065+
.addReg(SpecialRegs->getStackPointerRegister())
1066+
.addImm(TFL->getBackchainOffset(*MF))
1067+
.addReg(0);
1068+
1069+
BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
1070+
.addReg(BCReg)
1071+
.addReg(BufReg)
1072+
.addImm(BCOffset)
1073+
.addReg(0);
1074+
}
1075+
1076+
// Setup.
1077+
MIB = BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::EH_SjLj_Setup))
1078+
.addMBB(restoreMBB);
1079+
1080+
const SystemZRegisterInfo *RegInfo = Subtarget.getRegisterInfo();
1081+
MIB.addRegMask(RegInfo->getNoPreservedMask());
1082+
1083+
thisMBB->addSuccessor(mainMBB);
1084+
thisMBB->addSuccessor(restoreMBB);
1085+
1086+
// mainMBB:
1087+
BuildMI(mainMBB, DL, TII->get(SystemZ::LHI), mainDstReg).addImm(0);
1088+
mainMBB->addSuccessor(sinkMBB);
1089+
1090+
// sinkMBB:
1091+
BuildMI(*sinkMBB, sinkMBB->begin(), DL, TII->get(SystemZ::PHI), DstReg)
1092+
.addReg(mainDstReg)
1093+
.addMBB(mainMBB)
1094+
.addReg(restoreDstReg)
1095+
.addMBB(restoreMBB);
1096+
1097+
// restoreMBB.
1098+
BuildMI(restoreMBB, DL, TII->get(SystemZ::LHI), restoreDstReg).addImm(1);
1099+
BuildMI(restoreMBB, DL, TII->get(SystemZ::J)).addMBB(sinkMBB);
1100+
restoreMBB->addSuccessor(sinkMBB);
1101+
1102+
MI.eraseFromParent();
1103+
1104+
return sinkMBB;
1105+
}
1106+
1107+
MachineBasicBlock *
1108+
SystemZTargetLowering::emitEHSjLjLongJmp(MachineInstr &MI,
1109+
MachineBasicBlock *MBB) const {
1110+
1111+
DebugLoc DL = MI.getDebugLoc();
1112+
const TargetInstrInfo *TII = Subtarget.getInstrInfo();
1113+
1114+
MachineFunction *MF = MBB->getParent();
1115+
MachineRegisterInfo &MRI = MF->getRegInfo();
1116+
1117+
MVT PVT = getPointerTy(MF->getDataLayout());
1118+
assert((PVT == MVT::i64 || PVT == MVT::i32) && "Invalid Pointer Size!");
1119+
Register BufReg = MI.getOperand(0).getReg();
1120+
const TargetRegisterClass *RC = MRI.getRegClass(BufReg);
1121+
auto *SpecialRegs = Subtarget.getSpecialRegisters();
1122+
1123+
Register Tmp = MRI.createVirtualRegister(RC);
1124+
Register BCReg = MRI.createVirtualRegister(RC);
1125+
1126+
MachineInstrBuilder MIB;
1127+
1128+
const int64_t FPOffset = 0;
1129+
const int64_t LabelOffset = 1 * PVT.getStoreSize();
1130+
const int64_t BCOffset = 2 * PVT.getStoreSize();
1131+
const int64_t SPOffset = 3 * PVT.getStoreSize();
1132+
const int64_t LPOffset = 4 * PVT.getStoreSize();
1133+
1134+
MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG), Tmp)
1135+
.addReg(BufReg)
1136+
.addImm(LabelOffset)
1137+
.addReg(0);
1138+
1139+
MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG),
1140+
SpecialRegs->getFramePointerRegister())
1141+
.addReg(BufReg)
1142+
.addImm(FPOffset)
1143+
.addReg(0);
1144+
1145+
// We are restoring R13 even though we never stored in setjmp from llvm,
1146+
// as gcc always stores R13 in builtin_setjmp. We could have mixed code
1147+
// gcc setjmp and llvm longjmp.
1148+
MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG), SystemZ::R13D)
1149+
.addReg(BufReg)
1150+
.addImm(LPOffset)
1151+
.addReg(0);
1152+
1153+
bool BackChain = MF->getSubtarget<SystemZSubtarget>().hasBackChain();
1154+
if (BackChain) {
1155+
MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG), BCReg)
1156+
.addReg(BufReg)
1157+
.addImm(BCOffset)
1158+
.addReg(0);
1159+
}
1160+
1161+
MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::LG),
1162+
SpecialRegs->getStackPointerRegister())
1163+
.addReg(BufReg)
1164+
.addImm(SPOffset)
1165+
.addReg(0);
1166+
1167+
if (BackChain) {
1168+
auto *TFL = Subtarget.getFrameLowering<SystemZFrameLowering>();
1169+
BuildMI(*MBB, MI, DL, TII->get(SystemZ::STG))
1170+
.addReg(BCReg)
1171+
.addReg(SpecialRegs->getStackPointerRegister())
1172+
.addImm(TFL->getBackchainOffset(*MF))
1173+
.addReg(0);
1174+
}
1175+
1176+
MIB = BuildMI(*MBB, MI, DL, TII->get(SystemZ::BR)).addReg(Tmp);
1177+
1178+
MI.eraseFromParent();
1179+
return MBB;
1180+
}
1181+
9431182
/// Returns true if stack probing through inline assembly is requested.
9441183
bool SystemZTargetLowering::hasInlineStackProbe(const MachineFunction &MF) const {
9451184
// If the function specifically requests inline stack probes, emit them.
@@ -6292,6 +6531,14 @@ SDValue SystemZTargetLowering::LowerOperation(SDValue Op,
62926531
return lowerGET_ROUNDING(Op, DAG);
62936532
case ISD::READCYCLECOUNTER:
62946533
return lowerREADCYCLECOUNTER(Op, DAG);
6534+
case ISD::EH_SJLJ_SETJMP:
6535+
case ISD::EH_SJLJ_LONGJMP:
6536+
// These operations are legal on our platform, but we cannot actually
6537+
// set the operation action to Legal as common code would treat this
6538+
// as equivalent to Expand. Instead, we keep the operation action to
6539+
// Custom and just leave them unchanged here.
6540+
return Op;
6541+
62956542
default:
62966543
llvm_unreachable("Unexpected node to lower");
62976544
}
@@ -9733,6 +9980,10 @@ MachineBasicBlock *SystemZTargetLowering::EmitInstrWithCustomInserter(
97339980

97349981
case SystemZ::PROBED_ALLOCA:
97359982
return emitProbedAlloca(MI, MBB);
9983+
case SystemZ::EH_SjLj_SetJmp:
9984+
return emitEHSjLjSetJmp(MI, MBB);
9985+
case SystemZ::EH_SjLj_LongJmp:
9986+
return emitEHSjLjLongJmp(MI, MBB);
97369987

97379988
case TargetOpcode::STACKMAP:
97389989
case TargetOpcode::PATCHPOINT:

llvm/lib/Target/SystemZ/SystemZISelLowering.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,12 @@ class SystemZTargetLowering : public TargetLowering {
476476
// LD, and having the full constant in memory enables reg/mem opcodes.
477477
return VT != MVT::f64;
478478
}
479+
MachineBasicBlock *emitEHSjLjSetJmp(MachineInstr &MI,
480+
MachineBasicBlock *MBB) const;
481+
482+
MachineBasicBlock *emitEHSjLjLongJmp(MachineInstr &MI,
483+
MachineBasicBlock *MBB) const;
484+
479485
bool hasInlineStackProbe(const MachineFunction &MF) const override;
480486
AtomicExpansionKind shouldCastAtomicLoadInIR(LoadInst *LI) const override;
481487
AtomicExpansionKind shouldCastAtomicStoreInIR(StoreInst *SI) const override;

llvm/lib/Target/SystemZ/SystemZInstrInfo.td

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,6 +1862,23 @@ let mayLoad = 1, mayStore = 1, Defs = [CC] in {
18621862
}
18631863
}
18641864

1865+
//--------------------------------------------------------------------------
1866+
// Setjmp/Longjmp.
1867+
//--------------------------------------------------------------------------
1868+
let isBarrier = 1, hasNoSchedulingInfo = 1 in {
1869+
let hasSideEffects = 1, usesCustomInserter = 1 in {
1870+
def EH_SjLj_SetJmp : Pseudo<(outs GR32:$dst), (ins ADDR64:$R2),
1871+
[(set GR32:$dst, (z_eh_sjlj_setjmp ADDR64:$R2))]>;
1872+
let isTerminator = 1 in {
1873+
def EH_SjLj_LongJmp : Pseudo<(outs), (ins ADDR64:$R2),
1874+
[(z_eh_sjlj_longjmp ADDR64:$R2)]>;
1875+
}
1876+
}
1877+
let isTerminator = 1, isCodeGenOnly = 1, Size = 0 in {
1878+
def EH_SjLj_Setup : Pseudo<(outs), (ins brtarget32:$dst), []>;
1879+
}
1880+
}
1881+
18651882
//===----------------------------------------------------------------------===//
18661883
// Message-security assist
18671884
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)