Skip to content

Commit dc04d41

Browse files
authored
SystemZ: Add support for __builtin_setjmp and __builtin_longjmp. (#119257)
This pr includes fixes for original pr##116642. Implementation for __builtin_setjmp and __builtin_longjmp for SystemZ..
1 parent 806a936 commit dc04d41

16 files changed

+926
-5
lines changed

clang/lib/Basic/Targets/SystemZ.h

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

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

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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
2+
// RUN: %clang_cc1 -triple s390x-linux -emit-llvm -o - %s | FileCheck %s
3+
4+
void *buf[20];
5+
6+
// CHECK-LABEL: define dso_local void @foo(
7+
// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
8+
// CHECK-NEXT: [[ENTRY:.*:]]
9+
// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.eh.sjlj.setjmp(ptr @buf)
10+
// CHECK-NEXT: ret void
11+
//
12+
void foo()
13+
{
14+
__builtin_setjmp (buf);
15+
}
16+
17+
// CHECK-LABEL: define dso_local void @foo1(
18+
// CHECK-SAME: ) #[[ATTR0]] {
19+
// CHECK-NEXT: [[ENTRY:.*:]]
20+
// CHECK-NEXT: call void @llvm.eh.sjlj.longjmp(ptr @buf)
21+
// CHECK-NEXT: unreachable
22+
//
23+
void foo1()
24+
{
25+
__builtin_longjmp (buf, 1);
26+
}

llvm/docs/ExceptionHandling.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -374,10 +374,12 @@ 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
382+
is left completely to the target implementation.
381383

382384
.. _llvm.eh.sjlj.longjmp:
383385

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

97349982
case SystemZ::PROBED_ALLOCA:
97359983
return emitProbedAlloca(MI, MBB);
9984+
case SystemZ::EH_SjLj_SetJmp:
9985+
return emitEHSjLjSetJmp(MI, MBB);
9986+
case SystemZ::EH_SjLj_LongJmp:
9987+
return emitEHSjLjLongJmp(MI, MBB);
97369988

97379989
case TargetOpcode::STACKMAP:
97389990
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)