diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h --- a/llvm/lib/Target/RISCV/RISCV.h +++ b/llvm/lib/Target/RISCV/RISCV.h @@ -30,6 +30,8 @@ class RISCVSubtarget; class RISCVTargetMachine; +void initializeRISCVCFIFixupPass(PassRegistry &); + FunctionPass *createRISCVCodeGenPreparePass(); void initializeRISCVCodeGenPreparePass(PassRegistry &); diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.h b/llvm/lib/Target/RISCV/RISCVFrameLowering.h --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.h +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.h @@ -17,6 +17,8 @@ #include "llvm/Support/TypeSize.h" namespace llvm { + +class MCCFIInstruction; class RISCVSubtarget; class RISCVFrameLowering : public TargetFrameLowering { @@ -30,6 +32,7 @@ void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + void resetCFIToInitialState(MachineBasicBlock &MBB) const override; uint64_t getStackSizeWithRVVPadding(const MachineFunction &MF) const; diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -611,6 +611,8 @@ const RISCVRegisterInfo *RI = STI.getRegisterInfo(); MachineFrameInfo &MFI = MF.getFrameInfo(); auto *RVFI = MF.getInfo(); + const RISCVInstrInfo *TII = STI.getInstrInfo(); + bool EmitCFI = MF.getInfo()->needsAsyncDwarfUnwindInfo(MF); Register FPReg = getFPReg(STI); Register SPReg = getSPReg(STI); @@ -683,6 +685,53 @@ RI->adjustReg(MBB, LastFrameDestroy, DL, SPReg, SPReg, StackOffset::getFixed(SecondSPAdjustAmount), MachineInstr::FrameDestroy, getStackAlign()); + + // Emit ".cfi_def_cfa_offset FirstSPAdjustAmount" if using an sp-based CFA + if (!hasFP(MF)) { + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::cfiDefCfaOffset(nullptr, -FirstSPAdjustAmount)); + BuildMI(MBB, LastFrameDestroy, DL, + TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } + } + + if (hasFP(MF)) { + // To find the instruction restoring FP from stack. + for (auto &I = LastFrameDestroy; I != MBBI; ++I) { + if (I->mayLoad() && I->getOperand(0).isReg()) { + Register DestReg = I->getOperand(0).getReg(); + if (DestReg == FPReg) { + // If there is frame pointer, after restoring $fp registers, we + // need adjust CFA back to the correct sp-based offset. + // Emit ".cfi_def_cfa $sp, CFAOffset" + uint64_t CFAOffset = + FirstSPAdjustAmount + ? -FirstSPAdjustAmount + RVFI->getVarArgsSaveSize() + : -FPOffset; + unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa( + nullptr, RI->getDwarfRegNum(SPReg, true), CFAOffset)); + BuildMI(MBB, std::next(I), DL, + TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + break; + } + } + } + } + + // Add CFI directives for callee-saved registers. + // Iterate over list of callee-saved registers and emit .cfi_restore + // directives. + if (EmitCFI) { + for (const auto &Entry : CSI) { + Register Reg = Entry.getReg(); + unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestore( + nullptr, RI->getDwarfRegNum(Reg, true))); + BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlag(MachineInstr::FrameDestroy); + } } if (FirstSPAdjustAmount) @@ -694,6 +743,15 @@ // Emit epilogue for shadow call stack. emitSCSEpilogue(MF, MBB, MBBI, DL); + + // After restoring $sp, we need to adjust CFA to $(sp + 0) + // Emit ".cfi_def_cfa_offset 0" + if (EmitCFI) { + unsigned CFIIndex = + MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 0)); + BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } } StackOffset @@ -1367,3 +1425,39 @@ TargetStackID::Value RISCVFrameLowering::getStackIDForScalableVectors() const { return TargetStackID::ScalableVector; } + +static void insertCFISameValue(const MCInstrDesc &Desc, MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator InsertPt, + unsigned DwarfReg) { + unsigned CFIIndex = + MF.addFrameInst(MCCFIInstruction::createSameValue(nullptr, DwarfReg)); + BuildMI(MBB, InsertPt, DebugLoc(), Desc).addCFIIndex(CFIIndex); +} + +void RISCVFrameLowering::resetCFIToInitialState(MachineBasicBlock &MBB) const { + + MachineFunction &MF = *MBB.getParent(); + const auto &Subtarget = MF.getSubtarget(); + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + const auto &TRI = + static_cast(*Subtarget.getRegisterInfo()); + const auto &MFI = *MF.getInfo(); + + const MCInstrDesc &CFIDesc = TII.get(TargetOpcode::CFI_INSTRUCTION); + DebugLoc DL; + // Reset the CFA to `SP + 0`. + MachineBasicBlock::iterator InsertPt = MBB.begin(); + unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa( + nullptr, TRI.getDwarfRegNum(RISCV::X2, true), 0)); + BuildMI(MBB, InsertPt, DL, CFIDesc).addCFIIndex(CFIIndex); + + // Emit .cfi_same_value for callee-saved registers. + const std::vector &CSI = + MF.getFrameInfo().getCalleeSavedInfo(); + for (const auto &Info : CSI) { + unsigned Reg = Info.getReg(); + insertCFISameValue(CFIDesc, MF, MBB, InsertPt, + TRI.getDwarfRegNum(Reg, true)); + } +} diff --git a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h --- a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h +++ b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h @@ -129,6 +129,8 @@ bool isVectorCall() const { return IsVectorCall; } void setIsVectorCall() { IsVectorCall = true; } + bool needsAsyncDwarfUnwindInfo(const MachineFunction &MF) const; + bool needsDwarfUnwindInfo(const MachineFunction &MF) const; }; } // end namespace llvm diff --git a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "RISCVMachineFunctionInfo.h" +#include "llvm/MC/MCAsmInfo.h" using namespace llvm; @@ -43,3 +44,16 @@ bool RISCVMachineFunctionInfo::isSExt32Register(Register Reg) const { return is_contained(SExt32Registers, Reg); } + +bool RISCVMachineFunctionInfo::needsDwarfUnwindInfo( + const MachineFunction &MF) const { + return MF.needsFrameMoves(); +} + +bool RISCVMachineFunctionInfo::needsAsyncDwarfUnwindInfo( + const MachineFunction &MF) const { + + const Function &F = MF.getFunction(); + return needsDwarfUnwindInfo(MF) && F.getUWTableKind() == UWTableKind::Async && + !F.hasMinSize(); +} diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -20,6 +20,7 @@ #include "TargetInfo/RISCVTargetInfo.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/CFIFixup.h" #include "llvm/CodeGen/GlobalISel/IRTranslator.h" #include "llvm/CodeGen/GlobalISel/InstructionSelect.h" #include "llvm/CodeGen/GlobalISel/Legalizer.h" @@ -29,7 +30,9 @@ #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/InitializePasses.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Target/TargetOptions.h" @@ -119,6 +122,10 @@ if (TT.isOSFuchsia() && !TT.isArch64Bit()) report_fatal_error("Fuchsia is only supported for 64-bit"); + + // RISC-V supports fixing up the DWARF unwind information. + if (!getMCAsmInfo()->usesWindowsCFI()) + setCFIFixup(true); } const RISCVSubtarget * diff --git a/llvm/test/CodeGen/RISCV/cfiemit-remember-restore.ll b/llvm/test/CodeGen/RISCV/cfiemit-remember-restore.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/cfiemit-remember-restore.ll @@ -0,0 +1,68 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=riscv64 -o - | FileCheck %s +target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-unknown-elf" + +@.str = private unnamed_addr constant [3 x i8] c"Hi\00", align 1 + +; Function Attrs: nounwind uwtable +define dso_local void @f(ptr nocapture noundef %0) local_unnamed_addr #0 { +; CHECK: # %bb.0: +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK: .cfi_offset ra, -8 +; CHECK-NEXT: .cfi_offset s0, -16 +; CHECK-NEXT: .cfi_remember_state +; CHECK: # %bb.1: +; CHECK: .cfi_restore ra +; CHECK-NEXT: .cfi_restore s0 +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: .cfi_def_cfa_offset 0 +; CHECK-NEXT: ret +; CHECK-NEXT: .LBB0_2: +; CHECK-NEXT: .cfi_restore_state +; CHECK: .cfi_restore ra +; CHECK-NEXT: .cfi_restore s0 +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: .cfi_def_cfa_offset 0 +; CHECK-NEXT: tail foo + %2 = load i32, ptr %0, align 4, !tbaa !5 + %3 = icmp eq i32 %2, 0 + br i1 %3, label %4, label %5 + +4: ; preds = %1 + store i32 1, ptr %0, align 4, !tbaa !5 + tail call void @foo() #3 + br label %7 + +5: ; preds = %1 + %6 = tail call signext i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @.str) + store i32 0, ptr %0, align 4, !tbaa !5 + br label %7 + +7: ; preds = %4, %5 + ret void +} + +declare dso_local void @foo() local_unnamed_addr #1 + +; Function Attrs: nofree nounwind +declare dso_local noundef signext i32 @printf(ptr nocapture noundef readonly, ...) local_unnamed_addr #2 + +attributes #0 = { nounwind uwtable "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit,+a,+c,+m,+relax,-d,-e,-experimental-zca,-experimental-zcb,-experimental-zcd,-experimental-zcf,-experimental-zfa,-experimental-zihintntl,-experimental-ztso,-experimental-zvfh,-f,-h,-save-restore,-svinval,-svnapot,-svpbmt,-v,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zdinx,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zicbom,-zicbop,-zicboz,-zicsr,-zifencei,-zihintpause,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-zmmul,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b" } +attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit,+a,+c,+m,+relax,-d,-e,-experimental-zca,-experimental-zcb,-experimental-zcd,-experimental-zcf,-experimental-zfa,-experimental-zihintntl,-experimental-ztso,-experimental-zvfh,-f,-h,-save-restore,-svinval,-svnapot,-svpbmt,-v,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zdinx,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zicbom,-zicbop,-zicboz,-zicsr,-zifencei,-zihintpause,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-zmmul,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b" } +attributes #2 = { nofree nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit,+a,+c,+m,+relax,-d,-e,-experimental-zca,-experimental-zcb,-experimental-zcd,-experimental-zcf,-experimental-zfa,-experimental-zihintntl,-experimental-ztso,-experimental-zvfh,-f,-h,-save-restore,-svinval,-svnapot,-svpbmt,-v,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zdinx,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zicbom,-zicbop,-zicboz,-zicsr,-zifencei,-zihintpause,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-zmmul,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b" } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0, !1, !2, !3} +!llvm.ident = !{!4} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, !"target-abi", !"lp64"} +!2 = !{i32 7, !"uwtable", i32 2} +!3 = !{i32 8, !"SmallDataLimit", i32 8} +!4 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git a999669982d0cedacbb7371c96fce95682d582e1)"} +!5 = !{!6, !6, i64 0} +!6 = !{!"int", !7, i64 0} +!7 = !{!"omnipotent char", !8, i64 0} +!8 = !{!"Simple C/C++ TBAA"}