-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[SystemZ] Add support for __builtin_setjmp and __builtin_longjmp #116642
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
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-clang @llvm/pr-subscribers-backend-systemz Author: None (anoopkg6) ChangesImplementation for __builtin_setjmp and __builtin_longjmp for SystemZ. Patch is 665.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/116642.diff 76 Files Affected:
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index ef9a07033a6e4f..e6405f174f660f 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -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);
}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index e2d03eff8ab4a0..1de6aded302ceb 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4619,6 +4619,12 @@ 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) {
+ // Call LLVM's EH setjmp, which is lightweight.
+ 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),
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index 050a482c69d528..a75dac5b91ceca 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
@@ -687,6 +687,10 @@ void SystemZAsmPrinter::emitInstruction(const MachineInstr *MI) {
MCInstBuilder(SystemZ::EXRL).addReg(LenMinus1Reg).addExpr(Dot));
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);
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 3e05f3b0180a78..f498f9eab865f1 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -751,6 +751,13 @@ 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;
@@ -940,7 +947,242 @@ bool SystemZTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
return SystemZVectorConstantInfo(Imm).isVectorConstantLegal(Subtarget);
}
-/// Returns true if stack probing through inline assembly is requested.
+
+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!");
+ 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[0] = 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 LabelOffset = 1 * PVT.getStoreSize(); // Slot 2.
+ const int64_t SPOffset = 3 * PVT.getStoreSize(); // Slot 4.
+
+ // Buf address.
+ Register BufReg = MI.getOperand(1).getReg();
+
+ unsigned LabelReg = 0;
+ const TargetRegisterClass *PtrRC = getRegClassFor(PVT);
+ 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);
+
+ bool HasFP = Subtarget.getFrameLowering()->hasFP(*MF);
+ if (HasFP) {
+ const int64_t FPOffset = 0;
+ BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
+ .addReg(SystemZ::R11D)
+ .addReg(BufReg)
+ .addImm(FPOffset)
+ .addReg(0);
+ }
+
+ // store SP.
+ BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
+ .addReg(SystemZ::R15D)
+ .addReg(BufReg)
+ .addImm(SPOffset)
+ .addReg(0);
+
+ // Slot 3(Offset = 2) Backchain value (if building with -mbackchain).
+ bool BackChain = MF->getSubtarget<SystemZSubtarget>().hasBackChain();
+ if (BackChain) {
+ const int64_t BCOffset = 2 * PVT.getStoreSize();
+ Register BCReg = MRI.createVirtualRegister(RC);
+ MIB = BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::LG), BCReg)
+ .addReg(SystemZ::R15D)
+ .addImm(0)
+ .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);
+
+ 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 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), SystemZ::R11D)
+ .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) {
+ const int64_t BCOffset = 2 * PVT.getStoreSize();
+ 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), SystemZ::R15D)
+ .addReg(BufReg)
+ .addImm(SPOffset)
+ .addReg(0);
+
+ if (BackChain) {
+ BuildMI(*MBB, MI, DL, TII->get(SystemZ::STG))
+ .addReg(BCReg)
+ .addReg(SystemZ::R15D)
+ .addImm(0)
+ .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.
if (MF.getFunction().hasFnAttribute("probe-stack"))
@@ -6292,6 +6534,10 @@ 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:
+ return Op;
+
default:
llvm_unreachable("Unexpected node to lower");
}
@@ -9724,6 +9970,11 @@ 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:
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index 3c06c1fdf2b1bc..92ac938c903b68 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -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;
@@ -723,6 +729,7 @@ class SystemZTargetLowering : public TargetLowering {
SDValue lowerGET_ROUNDING(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerREADCYCLECOUNTER(SDValue Op, SelectionDAG &DAG) const;
+
bool canTreatAsByteVector(EVT VT) const;
SDValue combineExtract(const SDLoc &DL, EVT ElemVT, EVT VecVT, SDValue OrigOp,
unsigned Index, DAGCombinerInfo &DCI,
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index f3baf896658de5..ad7ee522a8c6f7 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1871,6 +1871,20 @@ let mayLoad = 1, mayStore = 1, Defs = [CC] in {
}
}
+//--------------------------------------------------------------------------
+// Setjmp/Longjmp.
+//--------------------------------------------------------------------------
+let isTerminator = 1, isBarrier = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1, Size = 0 in {
+ def EH_SjLj_Setup : Pseudo<(outs), (ins brtarget32:$dst), []>;
+}
+
+let hasSideEffects = 1, isBarrier = 1, usesCustomInserter = 1, hasNoSchedulingInfo = 1 in {
+ def EH_SjLj_SetJmp : Pseudo<(outs GR32:$dst), (ins ADDR64:$R2), [(set GR32:$dst, (z_eh_sjlj_setjmp ADDR64:$R2))]>;
+}
+
+let hasSideEffects = 1, isTerminator = 1, isBarrier = 1, usesCustomInserter = 1, hasNoSchedulingInfo = 1 in {
+ def EH_SjLj_LongJmp : Pseudo<(outs), (ins ADDR64:$R2), [(z_eh_sjlj_longjmp ADDR64:$R2)]>;
+}
//===----------------------------------------------------------------------===//
// Message-security assist
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/SystemZ/SystemZLongBranch.cpp b/llvm/lib/Target/SystemZ/SystemZLongBranch.cpp
index 632218cc61eefe..8267d398c50ed6 100644
--- a/llvm/lib/Target/SystemZ/SystemZLongBranch.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZLongBranch.cpp
@@ -220,7 +220,10 @@ static unsigned getInstSizeInBytes(const MachineInstr &MI,
MI.isImplicitDef() || MI.getOpcode() == TargetOpcode::MEMBARRIER ||
// These have a size that may be zero:
MI.isInlineAsm() || MI.getOpcode() == SystemZ::STACKMAP ||
- MI.getOpcode() == SystemZ::PATCHPOINT) &&
+ MI.getOpcode() == SystemZ::PATCHPOINT ||
+ // EH_SjLj_Setup is a dummy terminator instruction of size 0,
+ // It is used to handle the clobber register for builtin setjmp.
+ MI.getOpcode() == SystemZ::EH_SjLj_Setup) &&
"Missing size value for instruction.");
return Size;
}
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index 6cb89ccff85e68..90fb4e5f370dab 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -238,6 +238,12 @@ def SDT_ZTest : SDTypeProfile<1, 2,
[SDTCisVT<0, i32>,
SDTCisVT<2, i64>]>;
+def SDT_ZSetJmp : SDTypeProfile<1, 1,
+ [SDTCisInt<0>,
+ SDTCisPtrTy<1>]>;
+def SDT_ZLongJmp : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>;
+
+
//===----------------------------------------------------------------------===//
// Node definitions
//===----------------------------------------------------------------------===//
@@ -314,6 +320,12 @@ def z_stckf : SDNode<"SystemZISD::STCKF", SDT_ZStoreInherent,
def z_tdc : SDNode<"SystemZISD::TDC", SDT_ZTest>;
+def z_eh_sjlj_setjmp : SDNode<"ISD::EH_SJLJ_SETJMP", SDT_ZSetJmp,
+ [SDNPHasChain, SDNPSideEffect]>;
+def z_eh_sjlj_longjmp : SDNode<"ISD::EH_SJLJ_LONGJMP", SDT_ZLongJmp,
+ [SDNPHasChain, SDNPSideEffect]>;
+
+
// Defined because the index is an i32 rather than a pointer.
def z_vector_insert : SDNode<"ISD::INSERT_VECTOR_ELT",
SDT_ZInsertVectorElt>;
diff --git a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp
index d246d3f3c5bd11..6d3e542c1b4e81 100644
--- a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp
@@ -254,6 +254,11 @@ SystemZRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
return Regs->getCallPreservedMask(MF, CC);
}
+const uint32_t*
+SystemZRegisterInfo::getNoPreservedMask() const {
+ return CSR_SystemZ_NoRegs_RegMask;
+}
+
BitVector
SystemZRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
BitVector Reserved(getNumRegs());
diff --git a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
index cbc02c73f1ac70..4f497f8d23d29a 100644
--- a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
+++ b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
@@ -161,6 +161,7 @@ struct SystemZRegisterInfo : public SystemZGenRegisterInfo {
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
const uint32_t *getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID CC) const override;
+ const uint32_t *getNoPreservedMask() const override;
BitVector getReservedRegs(const MachineFunction &MF) const override;
bool eliminateFrameIndex(MachineBasicBlock::iterator MI,
int SPAdj, unsigned FIOperandNum,
diff --git a/llvm/test/CodeGen/SystemZ/builtin-longjmp-01.ll b/llvm/test/CodeGen/SystemZ/builtin-longjmp-01.ll
new file mode 100644
index 00000000000000..413b651acb64a7
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/builtin-longjmp-01.ll
@@ -0,0 +1,44 @@
+;Test longjmp load from jmp_buf.
+; Frame pointer from Slot 1.
+; Jump address from Slot 2.
+; Stack Pointer from Slot 4.
+; Literal Pool Pointer from Slot 5.
+
+; RUN: llc -O2 < %s | FileCheck %s
+
+
+; ModuleID = 'longjmp.c'
+source_filename = "longjmp.c"
+target datalayout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64"
+target triple = "s390x-unknown-linux-gnu"
+
+@buf = dso_local global [20 x ptr] zeroinitializer, align 8
+
+; Function Attrs: noreturn nounwind
+define dso_local void @foo() local_unnamed_addr #0 {
+entry:
+; CHECK: stmg %r11, %r15, 88(%r15)
+; CHECK: larl %r1, buf
+; CHECK: lg %r2, 8(%r1)
+; CHECK: lg %r11, 0(%r1)
+; CHECK: lg %r13, 32(%r1)
+; CHECK: lg %r15, 24(%r1)
+; CHECK: br %r2
+
+ tail call void @llvm.eh.sjlj.longjmp(ptr nonnull @buf)
+ unreachable
+}
+
+; Function Attrs: noreturn nounwind
+declare void @llvm.eh.sjlj.longjmp(ptr) #1
+
+attributes #0 = { noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="z10" }
+attributes #1 = { noreturn nounwind }
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.ident = !{!3}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 7, !"PIE Level", i32 2}
+!3 = !{!"clang version 20.0.0git (https://github.com/llvm/llvm-project.git 79880371396d6e486bf6bacd6c4087ebdac591f8)"}
diff --git a/llvm/test/CodeGen/SystemZ/builtin-longjmp-backchain-01.ll b/llvm/test/CodeGen/SystemZ/builtin-longjmp-backchain-01.ll
new file mode 100644
index 00000000000000..39ee483c27cbd5
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/builtin-longjmp-backchain-01.ll
@@ -0,0 +1,46 @@
+;Test -mba...
[truncated]
|
@llvm/pr-subscribers-clang-codegen Author: None (anoopkg6) ChangesImplementation for __builtin_setjmp and __builtin_longjmp for SystemZ. Patch is 665.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/116642.diff 76 Files Affected:
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index ef9a07033a6e4f..e6405f174f660f 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -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);
}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index e2d03eff8ab4a0..1de6aded302ceb 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4619,6 +4619,12 @@ 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) {
+ // Call LLVM's EH setjmp, which is lightweight.
+ 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),
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index 050a482c69d528..a75dac5b91ceca 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
@@ -687,6 +687,10 @@ void SystemZAsmPrinter::emitInstruction(const MachineInstr *MI) {
MCInstBuilder(SystemZ::EXRL).addReg(LenMinus1Reg).addExpr(Dot));
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);
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 3e05f3b0180a78..f498f9eab865f1 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -751,6 +751,13 @@ 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;
@@ -940,7 +947,242 @@ bool SystemZTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
return SystemZVectorConstantInfo(Imm).isVectorConstantLegal(Subtarget);
}
-/// Returns true if stack probing through inline assembly is requested.
+
+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!");
+ 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[0] = 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 LabelOffset = 1 * PVT.getStoreSize(); // Slot 2.
+ const int64_t SPOffset = 3 * PVT.getStoreSize(); // Slot 4.
+
+ // Buf address.
+ Register BufReg = MI.getOperand(1).getReg();
+
+ unsigned LabelReg = 0;
+ const TargetRegisterClass *PtrRC = getRegClassFor(PVT);
+ 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);
+
+ bool HasFP = Subtarget.getFrameLowering()->hasFP(*MF);
+ if (HasFP) {
+ const int64_t FPOffset = 0;
+ BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
+ .addReg(SystemZ::R11D)
+ .addReg(BufReg)
+ .addImm(FPOffset)
+ .addReg(0);
+ }
+
+ // store SP.
+ BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::STG))
+ .addReg(SystemZ::R15D)
+ .addReg(BufReg)
+ .addImm(SPOffset)
+ .addReg(0);
+
+ // Slot 3(Offset = 2) Backchain value (if building with -mbackchain).
+ bool BackChain = MF->getSubtarget<SystemZSubtarget>().hasBackChain();
+ if (BackChain) {
+ const int64_t BCOffset = 2 * PVT.getStoreSize();
+ Register BCReg = MRI.createVirtualRegister(RC);
+ MIB = BuildMI(*thisMBB, MI, DL, TII->get(SystemZ::LG), BCReg)
+ .addReg(SystemZ::R15D)
+ .addImm(0)
+ .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);
+
+ 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 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), SystemZ::R11D)
+ .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) {
+ const int64_t BCOffset = 2 * PVT.getStoreSize();
+ 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), SystemZ::R15D)
+ .addReg(BufReg)
+ .addImm(SPOffset)
+ .addReg(0);
+
+ if (BackChain) {
+ BuildMI(*MBB, MI, DL, TII->get(SystemZ::STG))
+ .addReg(BCReg)
+ .addReg(SystemZ::R15D)
+ .addImm(0)
+ .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.
if (MF.getFunction().hasFnAttribute("probe-stack"))
@@ -6292,6 +6534,10 @@ 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:
+ return Op;
+
default:
llvm_unreachable("Unexpected node to lower");
}
@@ -9724,6 +9970,11 @@ 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:
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index 3c06c1fdf2b1bc..92ac938c903b68 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -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;
@@ -723,6 +729,7 @@ class SystemZTargetLowering : public TargetLowering {
SDValue lowerGET_ROUNDING(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerREADCYCLECOUNTER(SDValue Op, SelectionDAG &DAG) const;
+
bool canTreatAsByteVector(EVT VT) const;
SDValue combineExtract(const SDLoc &DL, EVT ElemVT, EVT VecVT, SDValue OrigOp,
unsigned Index, DAGCombinerInfo &DCI,
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index f3baf896658de5..ad7ee522a8c6f7 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1871,6 +1871,20 @@ let mayLoad = 1, mayStore = 1, Defs = [CC] in {
}
}
+//--------------------------------------------------------------------------
+// Setjmp/Longjmp.
+//--------------------------------------------------------------------------
+let isTerminator = 1, isBarrier = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1, Size = 0 in {
+ def EH_SjLj_Setup : Pseudo<(outs), (ins brtarget32:$dst), []>;
+}
+
+let hasSideEffects = 1, isBarrier = 1, usesCustomInserter = 1, hasNoSchedulingInfo = 1 in {
+ def EH_SjLj_SetJmp : Pseudo<(outs GR32:$dst), (ins ADDR64:$R2), [(set GR32:$dst, (z_eh_sjlj_setjmp ADDR64:$R2))]>;
+}
+
+let hasSideEffects = 1, isTerminator = 1, isBarrier = 1, usesCustomInserter = 1, hasNoSchedulingInfo = 1 in {
+ def EH_SjLj_LongJmp : Pseudo<(outs), (ins ADDR64:$R2), [(z_eh_sjlj_longjmp ADDR64:$R2)]>;
+}
//===----------------------------------------------------------------------===//
// Message-security assist
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/SystemZ/SystemZLongBranch.cpp b/llvm/lib/Target/SystemZ/SystemZLongBranch.cpp
index 632218cc61eefe..8267d398c50ed6 100644
--- a/llvm/lib/Target/SystemZ/SystemZLongBranch.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZLongBranch.cpp
@@ -220,7 +220,10 @@ static unsigned getInstSizeInBytes(const MachineInstr &MI,
MI.isImplicitDef() || MI.getOpcode() == TargetOpcode::MEMBARRIER ||
// These have a size that may be zero:
MI.isInlineAsm() || MI.getOpcode() == SystemZ::STACKMAP ||
- MI.getOpcode() == SystemZ::PATCHPOINT) &&
+ MI.getOpcode() == SystemZ::PATCHPOINT ||
+ // EH_SjLj_Setup is a dummy terminator instruction of size 0,
+ // It is used to handle the clobber register for builtin setjmp.
+ MI.getOpcode() == SystemZ::EH_SjLj_Setup) &&
"Missing size value for instruction.");
return Size;
}
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index 6cb89ccff85e68..90fb4e5f370dab 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -238,6 +238,12 @@ def SDT_ZTest : SDTypeProfile<1, 2,
[SDTCisVT<0, i32>,
SDTCisVT<2, i64>]>;
+def SDT_ZSetJmp : SDTypeProfile<1, 1,
+ [SDTCisInt<0>,
+ SDTCisPtrTy<1>]>;
+def SDT_ZLongJmp : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>;
+
+
//===----------------------------------------------------------------------===//
// Node definitions
//===----------------------------------------------------------------------===//
@@ -314,6 +320,12 @@ def z_stckf : SDNode<"SystemZISD::STCKF", SDT_ZStoreInherent,
def z_tdc : SDNode<"SystemZISD::TDC", SDT_ZTest>;
+def z_eh_sjlj_setjmp : SDNode<"ISD::EH_SJLJ_SETJMP", SDT_ZSetJmp,
+ [SDNPHasChain, SDNPSideEffect]>;
+def z_eh_sjlj_longjmp : SDNode<"ISD::EH_SJLJ_LONGJMP", SDT_ZLongJmp,
+ [SDNPHasChain, SDNPSideEffect]>;
+
+
// Defined because the index is an i32 rather than a pointer.
def z_vector_insert : SDNode<"ISD::INSERT_VECTOR_ELT",
SDT_ZInsertVectorElt>;
diff --git a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp
index d246d3f3c5bd11..6d3e542c1b4e81 100644
--- a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp
@@ -254,6 +254,11 @@ SystemZRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
return Regs->getCallPreservedMask(MF, CC);
}
+const uint32_t*
+SystemZRegisterInfo::getNoPreservedMask() const {
+ return CSR_SystemZ_NoRegs_RegMask;
+}
+
BitVector
SystemZRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
BitVector Reserved(getNumRegs());
diff --git a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
index cbc02c73f1ac70..4f497f8d23d29a 100644
--- a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
+++ b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
@@ -161,6 +161,7 @@ struct SystemZRegisterInfo : public SystemZGenRegisterInfo {
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
const uint32_t *getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID CC) const override;
+ const uint32_t *getNoPreservedMask() const override;
BitVector getReservedRegs(const MachineFunction &MF) const override;
bool eliminateFrameIndex(MachineBasicBlock::iterator MI,
int SPAdj, unsigned FIOperandNum,
diff --git a/llvm/test/CodeGen/SystemZ/builtin-longjmp-01.ll b/llvm/test/CodeGen/SystemZ/builtin-longjmp-01.ll
new file mode 100644
index 00000000000000..413b651acb64a7
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/builtin-longjmp-01.ll
@@ -0,0 +1,44 @@
+;Test longjmp load from jmp_buf.
+; Frame pointer from Slot 1.
+; Jump address from Slot 2.
+; Stack Pointer from Slot 4.
+; Literal Pool Pointer from Slot 5.
+
+; RUN: llc -O2 < %s | FileCheck %s
+
+
+; ModuleID = 'longjmp.c'
+source_filename = "longjmp.c"
+target datalayout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64"
+target triple = "s390x-unknown-linux-gnu"
+
+@buf = dso_local global [20 x ptr] zeroinitializer, align 8
+
+; Function Attrs: noreturn nounwind
+define dso_local void @foo() local_unnamed_addr #0 {
+entry:
+; CHECK: stmg %r11, %r15, 88(%r15)
+; CHECK: larl %r1, buf
+; CHECK: lg %r2, 8(%r1)
+; CHECK: lg %r11, 0(%r1)
+; CHECK: lg %r13, 32(%r1)
+; CHECK: lg %r15, 24(%r1)
+; CHECK: br %r2
+
+ tail call void @llvm.eh.sjlj.longjmp(ptr nonnull @buf)
+ unreachable
+}
+
+; Function Attrs: noreturn nounwind
+declare void @llvm.eh.sjlj.longjmp(ptr) #1
+
+attributes #0 = { noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="z10" }
+attributes #1 = { noreturn nounwind }
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.ident = !{!3}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 7, !"PIE Level", i32 2}
+!3 = !{!"clang version 20.0.0git (https://github.com/llvm/llvm-project.git 79880371396d6e486bf6bacd6c4087ebdac591f8)"}
diff --git a/llvm/test/CodeGen/SystemZ/builtin-longjmp-backchain-01.ll b/llvm/test/CodeGen/SystemZ/builtin-longjmp-backchain-01.ll
new file mode 100644
index 00000000000000..39ee483c27cbd5
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/builtin-longjmp-backchain-01.ll
@@ -0,0 +1,46 @@
+;Test -mba...
[truncated]
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the contribution! Please find a number of comments inline. Also, you should address the issues found by the CI, e.g. about coding style.
clang/lib/CodeGen/CGBuiltin.cpp
Outdated
@@ -4619,6 +4619,12 @@ 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) { | |||
// Call LLVM's EH setjmp, which is lightweight. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the comment here should explain that we're not filling any fields of the jmp_buf here and leave it all to the back-end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little concerned about modifying the semantics of the intrinsic like this. I mean, the semantics are inherently a bit platform-specific, but this contradicts the documentation in llvm/docs/ExceptionHandling.rst.
If we need different semantics here for some reason, I'd prefer to use an intrinsic with a different name, to avoid any confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The underlying problem is that current implementation and documentation of the builtin is somewhat inconsistent with itself. The documentation starts out with:
The buffer format and the overall functioning of this intrinsic is compatible with the GCC __builtin_setjmp implementation allowing code built with the clang and GCC to interoperate.
But in GCC the format of the buffer is completely target-dependent (and actually materially different between targets), while LLVM attempts to impose some constraints on certain fields. (These happen to be OK for the small subset of targets that currently implement these intrinsics in LLVM, but not all other GCC targets.)
Specifically, the LLVM documentation continues with
The front end places the frame pointer in the first word
Now, even in GCC every target places the frame pointer in the first word, but in GCC this means the contents of the target frame pointer register, if any is used. What clang puts into the first word, however, is the result of the frameaddress
intrinsic - this value matches the frame pointer register contents on some but not all targets. On SystemZ these values differ, which would cause incompatibilties with GCC.
Finally, the clang front-end also writes the stack pointer into the third slot. Not only is this not even mentioned in the docs, it also does not match GCC behavior on all targets. While indeed all targets have the to save the stack pointer somewhere, they're not all using the third slot. On SystemZ we use the fourth slot.
It is a bit unclear to me what the purpose of writing those two slots in the front end is in the first place. It would seem more straightforward to just leave the writing of the buffer completely to the target back-end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. That's pretty messy. See also 02269a6 and faa3abb . I think what happened is that the meaning of the intrinsic was adapted to suit sjlj lowering, and nobody really thought about trying to expose a stable interface to frontends.
I still think it's a bit of a hazard to adjust the semantics of the intrinsic at this point without changing the name. But there isn't any existing SystemZ bitcode to break, so it might be enough to just update the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. It seems back in the day the eh.sjlj.setjmp
intrinsic was used by both the front-end to implement __builtin_setjmp
and by the middle-end (SjLjEHPrepare.cpp) for exception handling. Since these two users apparently require somewhat different information in the buffer, filling this in was left to the user.
However, these days those two users actually emit two different intrinsics. The middle-end now uses eh.sjlj.setup.dispatch
(which is apparently only intended for internal usage; it's not documented at all), while only the front-end continues to use eh.sjlj.setjmp
. See http://reviews.llvm.org/D9313.
I still think the most straightfoward way would be to have eh.sjlj.setjmp
just be completely implemented in the back end, and simply do exactly what is needed to implement __builtin_setjmp
on the platform, just like is done with longjmp. This could be done for all targets (probably the cleanest solution, but would require updating the existing targets and might introduce incompatibilities with existing bytecode on those targets), or just for selected targets (I guess this would then need to be documented? And should it be a positive or negative list of targets?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. I don't think there's any benefit to having the frontend to generate explicit stores. I just want to make sure whatever solution we choose, it doesn't silently break code generated by other frontends (or older versions of clang).
setOperationAction(ISD::EH_SJLJ_SETJMP, MVT::i32, Custom); | ||
setOperationAction(ISD::EH_SJLJ_LONGJMP, MVT::Other, Custom); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't add those additional newlines. One empty line is enough.
// Buf address. | ||
Register BufReg = MI.getOperand(1).getReg(); | ||
|
||
unsigned LabelReg = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need the zero initialization here. Just move the declaration down.
|
||
bool HasFP = Subtarget.getFrameLowering()->hasFP(*MF); | ||
if (HasFP) { | ||
const int64_t FPOffset = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be preferable to have all five Offset constant declaration in one place near the top of the function.
@@ -723,6 +729,7 @@ class SystemZTargetLowering : public TargetLowering { | |||
SDValue lowerGET_ROUNDING(SDValue Op, SelectionDAG &DAG) const; | |||
SDValue lowerREADCYCLECOUNTER(SDValue Op, SelectionDAG &DAG) const; | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be here.
//-------------------------------------------------------------------------- | ||
// Setjmp/Longjmp. | ||
//-------------------------------------------------------------------------- | ||
let isTerminator = 1, isBarrier = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1, Size = 0 in { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lines here and below are a bit long and should be broken up - see other statements in the file as examples.
; ModuleID = 'longjmp.c' | ||
source_filename = "longjmp.c" | ||
target datalayout = "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64" | ||
target triple = "s390x-unknown-linux-gnu" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you generated the test file via running clang. Usual practice is to try an strip the case down to the essentials, so you wouldn't have the datalayout/triple here, or all the attribute setting at the bottom. Have a look at the other files in the directory to see how this is usually done.
@@ -0,0 +1,72 @@ | |||
; Test output of setjmp/longjmp. | |||
; RUN: clang -o %t %s | |||
; RUN: %t | FileCheck %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. This is a problem. The unit tests here cannot be execution tests; they'll have to run on any host system (e.g. on the automated CI on GitHub). You should try to test the critical codegen parts via compile-only test. If it makes sense to have additional execute test, they'll need to go somewhere else, e.g. the test-suite repository.
Hi @anoopkg6 , the tests are still failing in CI because this is hosted on an Intel machine, so when you run just with To make sure the tests run correctly and always target s390x, you should use something along the following lines - see other tests in this directory for examples:
|
…390x-linux-gnu to the test cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some additional comments on the test cases inline.
@buf = dso_local global [20 x ptr] zeroinitializer, align 8 | ||
|
||
; Function Attrs: noreturn nounwind | ||
define dso_local void @foo() local_unnamed_addr #0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't need most of those annotations here. We do not need the Function Attrs comment, nor the #0
attribute reference (which doesn't even exist in this file!). We also do not need the dso_local
or local_unnamed_addr
markers.
; CHECK: lg %r15, 24(%r1) | ||
; CHECK: br %r2 | ||
|
||
tail call void @llvm.eh.sjlj.longjmp(ptr nonnull @buf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need a declaration of that intrinsic somewhere in this file?
@@ -0,0 +1,24 @@ | |||
;Test longjmp load from jmp_buf. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix the white spaces. Space after ';', only one space between words.
; CHECK: lg %r11, 0(%r1) | ||
; CHECK: lg %r13, 32(%r1) | ||
; CHECK: lg %r15, 24(%r1) | ||
; CHECK: br %r2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be preferable to auto-generate these checks. This can be done by running the utils/update_llc_test_checks.py
script - see many of the other test cases. You'll have to pass the path to the llc
binary you built via the --llc=
option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
B.t.w. the same applies to all the following tests as well.
@buf = dso_local global [20 x ptr] zeroinitializer, align 8 | ||
|
||
; Function Attrs: noreturn nounwind | ||
define dso_local void @foo() local_unnamed_addr #0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above for removing unnecessary annotations. Here, we do need the backchain
attribute, but not the others. That attribute can be specified inline rather than as part of an attribute bundle - something alone these lines should work:
define void @foo() "backchain" {
attributes #0 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="z10" } | ||
attributes #1 = { nounwind } | ||
attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="z10" } | ||
attributes #3 = { nofree nounwind } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of those attributes look essential.
attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="z10" } | ||
attributes #3 = { nofree nounwind } | ||
|
||
!llvm.module.flags = !{!0, !1, !2} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither any of these module flags.
@@ -0,0 +1,216 @@ | |||
; Simulate register pressure around setjmp call for double precision | |||
; arguments and return sum of 20 vaiables. It also prints the variables. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is not an executable test, I don't think the printf code-gen tests anything relevant. The above register pressure test should be sufficient.
@@ -0,0 +1,146 @@ | |||
; -mbackchain option. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I don't think we need -mbackchain versions of all of those. We have the simple tests above that verify -mbackchain works, we don't need to repeat this for all other tests.
@@ -0,0 +1,215 @@ | |||
; Simulate register pressure around setjmp call for integer arguments and | |||
; return sum of 20 vaiables. It also prints the variables. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above comments on the -double tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're getting close now - but I still have a few comments below. Thanks!
clang/lib/CodeGen/CGBuiltin.cpp
Outdated
// 2. Clang front-end also writes the stack pointer into the third | ||
// slot. Not only is this not even mentioned in the docs, it also does | ||
// not match GCC behavior on all targets. On SystemZ we use the fourth | ||
// slot. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@anoopkg6 , we shouldn't add a long comment here why the documentation is wrong - instead, the patch should actually fix the documentation. This particular bit would be in docs/ExceptionHandling.rst
. I would change the paragraph that currently reads:
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.
to something like:
The single parameter is a pointer to a five word buffer in which the calling
context is saved. The format and contents of the buffer are target-specific.
On certain targets, 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, saving the
calling context to the buffer is left completely to the target implementation.
Then can we simply put a short notice in this file, along the lines of
"On this target, the back end fills in the context buffer completely."
@efriedma-quic would this be OK with you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should specifically call out which targets are which, for reference. No point in being overly generic. Otherwise I guess that's fine.
It's worth pointing out that if the backend completely fills the buffer, it doesn't really matter if the frontend stores to the buffer before calling setjmp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should specifically call out which targets are which, for reference. No point in being overly generic.
Fair enough.
It's worth pointing out that if the backend completely fills the buffer, it doesn't really matter if the frontend stores to the buffer before calling setjmp.
Understood. It would still seem cleaner to not emit those stores if the back-end is going to overwrite them anyway ...
@@ -0,0 +1,25 @@ | |||
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need a -backchain version of the test case here - what's tested here is completely identical as far as the front-end goes. Just the other test should be fine.
// | phi(v_mainMBB,v_restoreMBB) | | ||
// ----------------------------- | ||
// thisMBB: | ||
// buf[0] = Frame Pointer if hasFP. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use FPOffset
here to be consistent with the code below.
; CHECK-NEXT: lmg %r6, %r15, 496(%r15) | ||
; CHECK-NEXT: br %r14 | ||
entry: | ||
%0 = tail call i32 @llvm.eh.sjlj.setjmp(ptr nonnull @buf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this again, I'm a bit confused what this is supposed to test? There are no registers live across the setjmp anyway ...
I'd have throught a good test would be to make a large number of virtual registers live before the setjmp, and then use all of them after the setjmp. The test would then verify that the compiler doesn't attempt to keep any of these virtual registers alive in call-saved GPRs, but rather saves all of them to the stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now looks good to me (see one inline comment). The only part missing is the doc change as discussed above - can you add this to the PR here? Then it should be good to go. Thanks!
@@ -1,4 +1,4 @@ | |||
//===-- SystemZISelLowering.cpp - SystemZ DAG lowering implementation -----===// | |||
//===-- systemzisellowering.cpp - systemz dag lowering implementation -----===// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be some accidental change? Please remove.
…undo accidental changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One last comment, then it looks good to go for me. Thanks!
llvm/docs/ExceptionHandling.rst
Outdated
On certain targets, 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, saving the | ||
calling context to the buffer is left completely to the target implementation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given @efriedma-quic 's comment, we should specify the targets here. Maybe change the first sentence to "On certain targets (ARM, PowerPC, VE, X86), ..." and the second sentence to "On other targets (SystemZ), ..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM now. Thanks for your contribution!
@anoopkg6 Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR. Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues. How to do this, and the rest of the post-merge process, is covered in detail here. If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/11/builds/9334 Here is the relevant piece of the build log for the reference
|
This seems to have broken build bot and Fuchsia's Linux CI https://ci.chromium.org/ui/p/fuchsia/builders/toolchain.ci/clang-host-linux-x64/b8729261510660874657/overview. Would you mind reverting until a fix is ready? |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/16/builds/10237 Here is the relevant piece of the build log for the reference
|
Hi @anoopkg6 , I had to revert this again as it was causing a number of issues:
You'll have to open a new PR with a patch including those fixes. |
@uweigand Thanks for the prompt revert. For reference the Fuchsia bot is just a normal Linux build configured for building Fuchsia in a debian container. The toolchain isn't novel in anyway, except it normally builds with a recent toolchain, and uses the full runtimes build. The bot will list all the CMAKE options we use, and the DockerFile should be with the zorg build bot configs for other buildbots if you have trouble reproducing. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/51/builds/7267 Here is the relevant piece of the build log for the reference
|
Implementation for __builtin_setjmp and __builtin_longjmp for SystemZ.