diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index edf74404c4925..fd84d4b28ec66 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -722,8 +722,7 @@ features cannot lower the translation-unit ABI level; - Added a new warning when the same interrupt type is specified more than once in a RISC-V `interrupt` attribute. -- SiFive CLIC preemptible interrupt handlers now diagnose unsupported frame - pointers instead of producing a backend fatal error. +- SiFive CLIC preemptible interrupt handlers now support frame pointers. - Added `-march=native` for better compatibility with ARM, AArch64, and X86. This option will be treated like `-mcpu=native` if `-mcpu` is not present. If diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp index 458cf32bf8c1b..3ba4ef969c4ed 100644 --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -265,9 +265,8 @@ createSiFivePreemptibleInterruptFrameEntries(MachineFunction &MF, *MF.getSubtarget().getRegisterInfo(); MachineFrameInfo &MFI = MF.getFrameInfo(); - // Create two frame objects for spilling X8 and X9, which will be done in - // `emitSiFiveCLICPreemptibleSaves`. This is in addition to any other stack - // objects we might have for X8 and X9, as they might be saved twice. + // Create two frame objects for preserving X8 and X9 or saving mcause and + // mepc, depending on whether the function uses a frame pointer. for (int I = 0; I < 2; ++I) { int FI = MFI.CreateStackObject(TRI.getSpillSize(RC), TRI.getSpillAlign(RC), true); @@ -289,35 +288,60 @@ static void emitSiFiveCLICPreemptibleSaves(MachineFunction &MF, // FIXME: CFI Information here is nonexistent/wrong. - // X8 and X9 might be stored into the stack twice, initially into the - // `interruptCSRFrameIndex` here, and then maybe again into their CSI frame - // index. - // - // This is done instead of telling the register allocator that we need two - // VRegs to store the value of `mcause` and `mepc` through the instruction, - // which affects other passes. - TII->storeRegToStackSlot(MBB, MBBI, RISCV::X8, /* IsKill=*/true, - RVFI->getInterruptCSRFrameIndex(0), - &RISCV::GPRRegClass, Register(), - MachineInstr::FrameSetup); - TII->storeRegToStackSlot(MBB, MBBI, RISCV::X9, /* IsKill=*/true, - RVFI->getInterruptCSRFrameIndex(1), - &RISCV::GPRRegClass, Register(), - MachineInstr::FrameSetup); - - // Put `mcause` into X8 (s0), and `mepc` into X9 (s1). If either of these are - // used in the function, then they will appear in `getUnmanagedCSI` and will - // be saved again. - BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRS)) - .addReg(RISCV::X8, RegState::Define) - .addImm(RISCVSysReg::mcause) - .addReg(RISCV::X0) - .setMIFlag(MachineInstr::FrameSetup); - BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRS)) - .addReg(RISCV::X9, RegState::Define) - .addImm(RISCVSysReg::mepc) - .addReg(RISCV::X0) - .setMIFlag(MachineInstr::FrameSetup); + if (STI.getFrameLowering()->hasFP(MF)) { + // With a frame pointer, use X5 as a temporary to save mcause and mepc to + // the stack. `determineCalleeSaves` adds X5 to the callee-saved set, so + // X5's original value has already been preserved. + BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRS)) + .addReg(RISCV::X5, RegState::Define) + .addImm(RISCVSysReg::mcause) + .addReg(RISCV::X0) + .setMIFlag(MachineInstr::FrameSetup); + TII->storeRegToStackSlot(MBB, MBBI, RISCV::X5, /* IsKill=*/true, + RVFI->getInterruptCSRFrameIndex(0), + &RISCV::GPRRegClass, Register(), + MachineInstr::FrameSetup); + + BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRS)) + .addReg(RISCV::X5, RegState::Define) + .addImm(RISCVSysReg::mepc) + .addReg(RISCV::X0) + .setMIFlag(MachineInstr::FrameSetup); + TII->storeRegToStackSlot(MBB, MBBI, RISCV::X5, /* IsKill=*/true, + RVFI->getInterruptCSRFrameIndex(1), + &RISCV::GPRRegClass, Register(), + MachineInstr::FrameSetup); + } else { + // X8 and X9 might be stored into the stack twice, initially into the + // `interruptCSRFrameIndex` here, and then maybe again into their CSI frame + // index. + // + // This is done instead of telling the register allocator that we need two + // VRegs to store the value of `mcause` and `mepc` through the instruction, + // which affects other passes. + TII->storeRegToStackSlot(MBB, MBBI, RISCV::X8, /* IsKill=*/true, + RVFI->getInterruptCSRFrameIndex(0), + &RISCV::GPRRegClass, Register(), + MachineInstr::FrameSetup); + TII->storeRegToStackSlot(MBB, MBBI, RISCV::X9, /* IsKill=*/true, + RVFI->getInterruptCSRFrameIndex(1), + &RISCV::GPRRegClass, Register(), + MachineInstr::FrameSetup); + + // Put `mcause` into X8 (s0), and `mepc` into X9 (s1). If either of these + // are used in the function, then they will appear in `getUnmanagedCSI` and + // will be saved again. + BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRS)) + .addReg(RISCV::X8, RegState::Define) + .addImm(RISCVSysReg::mcause) + .addReg(RISCV::X0) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRS)) + .addReg(RISCV::X9, RegState::Define) + .addImm(RISCVSysReg::mepc) + .addReg(RISCV::X0) + .setMIFlag(MachineInstr::FrameSetup); + } // Enable interrupts. BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRSI)) @@ -348,30 +372,54 @@ static void emitSiFiveCLICPreemptibleRestores(MachineFunction &MF, .addImm(8) .setMIFlag(MachineInstr::FrameDestroy); - // Restore `mepc` from x9 (s1), and `mcause` from x8 (s0). If either were used - // in the function, they have already been restored once, so now have the - // value stored in `emitSiFiveCLICPreemptibleSaves`. - BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW)) - .addReg(RISCV::X0, RegState::Define) - .addImm(RISCVSysReg::mepc) - .addReg(RISCV::X9, RegState::Kill) - .setMIFlag(MachineInstr::FrameDestroy); - BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW)) - .addReg(RISCV::X0, RegState::Define) - .addImm(RISCVSysReg::mcause) - .addReg(RISCV::X8, RegState::Kill) - .setMIFlag(MachineInstr::FrameDestroy); + if (STI.getFrameLowering()->hasFP(MF)) { + // Restore `mepc` and `mcause` through X5. The normal callee-saved register + // handling will restore the value X5 held on entry to the handler later. + TII->loadRegFromStackSlot(MBB, MBBI, RISCV::X5, + RVFI->getInterruptCSRFrameIndex(1), + &RISCV::GPRRegClass, Register(), + RISCV::NoSubRegister, MachineInstr::FrameDestroy); + BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW)) + .addReg(RISCV::X0, RegState::Define) + .addImm(RISCVSysReg::mepc) + .addReg(RISCV::X5, RegState::Kill) + .setMIFlag(MachineInstr::FrameDestroy); - // X8 and X9 need to be restored to their values on function entry, which we - // saved onto the stack in `emitSiFiveCLICPreemptibleSaves`. - TII->loadRegFromStackSlot(MBB, MBBI, RISCV::X9, - RVFI->getInterruptCSRFrameIndex(1), - &RISCV::GPRRegClass, Register(), - RISCV::NoSubRegister, MachineInstr::FrameDestroy); - TII->loadRegFromStackSlot(MBB, MBBI, RISCV::X8, - RVFI->getInterruptCSRFrameIndex(0), - &RISCV::GPRRegClass, Register(), - RISCV::NoSubRegister, MachineInstr::FrameDestroy); + TII->loadRegFromStackSlot(MBB, MBBI, RISCV::X5, + RVFI->getInterruptCSRFrameIndex(0), + &RISCV::GPRRegClass, Register(), + RISCV::NoSubRegister, MachineInstr::FrameDestroy); + BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW)) + .addReg(RISCV::X0, RegState::Define) + .addImm(RISCVSysReg::mcause) + .addReg(RISCV::X5, RegState::Kill) + .setMIFlag(MachineInstr::FrameDestroy); + } else { + // Restore `mepc` from x9 (s1), and `mcause` from x8 (s0). If either were + // used in the function, they have already been restored once, so now have + // the value stored in `emitSiFiveCLICPreemptibleSaves`. + BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW)) + .addReg(RISCV::X0, RegState::Define) + .addImm(RISCVSysReg::mepc) + .addReg(RISCV::X9, RegState::Kill) + .setMIFlag(MachineInstr::FrameDestroy); + BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW)) + .addReg(RISCV::X0, RegState::Define) + .addImm(RISCVSysReg::mcause) + .addReg(RISCV::X8, RegState::Kill) + .setMIFlag(MachineInstr::FrameDestroy); + + // X8 and X9 need to be restored to their values on function entry, which we + // saved onto the stack in `emitSiFiveCLICPreemptibleSaves`. + TII->loadRegFromStackSlot(MBB, MBBI, RISCV::X9, + RVFI->getInterruptCSRFrameIndex(1), + &RISCV::GPRRegClass, Register(), + RISCV::NoSubRegister, MachineInstr::FrameDestroy); + TII->loadRegFromStackSlot(MBB, MBBI, RISCV::X8, + RVFI->getInterruptCSRFrameIndex(0), + &RISCV::GPRRegClass, Register(), + RISCV::NoSubRegister, MachineInstr::FrameDestroy); + } } // Get the ID of the libcall used for spilling and restoring callee saved @@ -1138,8 +1186,10 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, NeedProbe, ProbeSize, DynAllocation, MachineInstr::FrameSetup); - // Save SiFive CLIC CSRs into Stack - emitSiFiveCLICPreemptibleSaves(MF, MBB, MBBI, DL); + // Without a frame pointer, `mcause` and `mepc` remain in X8 and X9 across + // the handler. + if (!hasFP(MF)) + emitSiFiveCLICPreemptibleSaves(MF, MBB, MBBI, DL); // The frame pointer is callee-saved, and code has been generated for us to // save it to the stack. We need to skip over the storing of callee-saved @@ -1191,6 +1241,11 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, CFIBuilder.buildDefCFA(FPReg, RVFI->getVarArgsSaveSize()); } + // With a frame pointer, X5 is used as a temporary, so save the CSRs after + // the normal callee-saved spill has preserved X5's original value. + if (hasFP(MF)) + emitSiFiveCLICPreemptibleSaves(MF, MBB, MBBI, DL); + uint64_t SecondSPAdjustAmount = 0; // Emit the second SP adjustment after saving callee saved registers. if (FirstSPAdjustAmount) { @@ -1401,6 +1456,10 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF, if (NeedsDwarfCFI && hasFP(MF)) CFIBuilder.buildDefCFA(SPReg, RealStackSize); + // With a frame pointer, restore the SiFive CLIC CSRs through X5. + if (hasFP(MF)) + emitSiFiveCLICPreemptibleRestores(MF, MBB, FirstScalarCSRRestoreInsn, DL); + // Skip to after the restores of scalar callee-saved registers // FIXME: assumes exactly one instruction is used to restore each // callee-saved register. @@ -1469,7 +1528,9 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF, } } - emitSiFiveCLICPreemptibleRestores(MF, MBB, MBBI, DL); + // Without a frame pointer, X8 and X9 hold mcause and mepc here. + if (!hasFP(MF)) + emitSiFiveCLICPreemptibleRestores(MF, MBB, MBBI, DL); // Deallocate stack if StackSize isn't a zero yet. If this is a QCI interrupt // function, there will be a leftover offset which is deallocated by @@ -1869,8 +1930,13 @@ void RISCVFrameLowering::determineCalleeSaves(MachineFunction &MF, if (hasBP(MF)) SavedRegs.set(RISCVABI::getBPReg()); - // When using cm.push/pop we must save X27 if we save X26. auto *RVFI = MF.getInfo(); + // X5 is used as a temporary for saving and restoring mcause and mepc when + // X8 is occupied by the frame pointer. + if (RVFI->isSiFivePreemptibleInterrupt(MF) && hasFP(MF)) + SavedRegs.set(RISCV::X5); + + // When using cm.push/pop we must save X27 if we save X26. if (RVFI->isPushable(MF) && SavedRegs.test(RISCV::X26)) SavedRegs.set(RISCV::X27); diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index b2f684952dd90..bec9f87ff2a19 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -27073,12 +27073,6 @@ SDValue RISCVTargetLowering::LowerFormalArguments( if (Kind == "rnmi" && !Subtarget.hasStdExtSmrnmi()) reportFatalUsageError("'rnmi' interrupt kind requires Srnmi extension"); - const TargetFrameLowering *TFI = Subtarget.getFrameLowering(); - if (Kind.starts_with("SiFive-CLIC-preemptible") && TFI->hasFP(MF)) - Func.getContext().diagnose(DiagnosticInfoUnsupported{ - Func, - "'SiFive-CLIC-preemptible' interrupt functions cannot have a frame " - "pointer"}); } EVT PtrVT = getPointerTy(DAG.getDataLayout()); diff --git a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr-err.ll b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr-err.ll deleted file mode 100644 index f0e1f16cf6e54..0000000000000 --- a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr-err.ll +++ /dev/null @@ -1,12 +0,0 @@ -; RUN: not llc -mtriple riscv32-unknown-elf -mattr=+experimental-xsfmclic -o - %s 2>&1 \ -; RUN: | FileCheck %s -; RUN: not llc -mtriple riscv64-unknown-elf -mattr=+experimental-xsfmclic -o - %s 2>&1 \ -; RUN: | FileCheck %s - -;; Test that these report regular errors. - -; CHECK: error: :0:0: in function preemptible void (): 'SiFive-CLIC-preemptible' interrupt functions cannot have a frame pointer - -define void @preemptible() "interrupt"="SiFive-CLIC-preemptible" "frame-pointer"="all" { - ret void -} diff --git a/llvm/test/CodeGen/RISCV/sifive-interrupt-frame-pointer.ll b/llvm/test/CodeGen/RISCV/sifive-interrupt-frame-pointer.ll new file mode 100644 index 0000000000000..da025cf86e65e --- /dev/null +++ b/llvm/test/CodeGen/RISCV/sifive-interrupt-frame-pointer.ll @@ -0,0 +1,155 @@ +; RUN: llc -mtriple riscv32-unknown-elf -mattr=+experimental-xsfmclic \ +; RUN: -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32 +; RUN: llc -mtriple riscv64-unknown-elf -mattr=+experimental-xsfmclic \ +; RUN: -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64 + +@size = external global i32 + +declare void @use(ptr) + +define void @preemptible() "interrupt"="SiFive-CLIC-preemptible" "frame-pointer"="all" { +; RV32-LABEL: preemptible: +; RV32: sw t0, 16(sp) +; RV32: sw s0, 12(sp) +; RV32: addi s0, sp, 32 +; RV32: csrr t0, mcause +; RV32-NEXT: sw t0, 28(sp) +; RV32-NEXT: csrr t0, mepc +; RV32-NEXT: sw t0, 24(sp) +; RV32-NEXT: csrsi mstatus, 8 +; RV32: csrci mstatus, 8 +; RV32-NEXT: lw t0, 24(sp) +; RV32-NEXT: csrw mepc, t0 +; RV32-NEXT: lw t0, 28(sp) +; RV32-NEXT: csrw mcause, t0 +; RV32: lw t0, 16(sp) +; RV32: lw s0, 12(sp) +; RV32: mret +; +; RV64-LABEL: preemptible: +; RV64: sd t0, 16(sp) +; RV64: sd s0, 8(sp) +; RV64: addi s0, sp, 48 +; RV64: csrr t0, mcause +; RV64-NEXT: sd t0, 40(sp) +; RV64-NEXT: csrr t0, mepc +; RV64-NEXT: sd t0, 32(sp) +; RV64-NEXT: csrsi mstatus, 8 +; RV64: csrci mstatus, 8 +; RV64-NEXT: ld t0, 32(sp) +; RV64-NEXT: csrw mepc, t0 +; RV64-NEXT: ld t0, 40(sp) +; RV64-NEXT: csrw mcause, t0 +; RV64: ld t0, 16(sp) +; RV64: ld s0, 8(sp) +; RV64: mret + ret void +} + +define void @preemptible_stack_swap() "interrupt"="SiFive-CLIC-preemptible-stack-swap" "frame-pointer"="all" { +; RV32-LABEL: preemptible_stack_swap: +; RV32: csrrw sp, sf.mscratchcsw, sp +; RV32: addi s0, sp, 32 +; RV32: csrr t0, mcause +; RV32: csrsi mstatus, 8 +; RV32: csrci mstatus, 8 +; RV32: csrw mepc, t0 +; RV32: csrw mcause, t0 +; RV32: lw s0, 12(sp) +; RV32: csrrw sp, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: preemptible_stack_swap: +; RV64: csrrw sp, sf.mscratchcsw, sp +; RV64: addi s0, sp, 48 +; RV64: csrr t0, mcause +; RV64: csrsi mstatus, 8 +; RV64: csrci mstatus, 8 +; RV64: csrw mepc, t0 +; RV64: csrw mcause, t0 +; RV64: ld s0, 8(sp) +; RV64: csrrw sp, sf.mscratchcsw, sp +; RV64-NEXT: mret + ret void +} + +define void @preemptible_var_alloca() "interrupt"="SiFive-CLIC-preemptible" "frame-pointer"="none" { +; RV32-LABEL: preemptible_var_alloca: +; RV32: addi s0, sp, 80 +; RV32: csrr t0, mcause +; RV32-NEXT: sw t0, -4(s0) +; RV32-NEXT: csrr t0, mepc +; RV32-NEXT: sw t0, -8(s0) +; RV32-NEXT: csrsi mstatus, 8 +; RV32: sub a0, sp, a0 +; RV32: mv sp, a0 +; RV32: call use +; RV32: addi sp, s0, -80 +; RV32: csrci mstatus, 8 +; RV32-NEXT: lw t0, -8(s0) +; RV32-NEXT: csrw mepc, t0 +; RV32-NEXT: lw t0, -4(s0) +; RV32-NEXT: csrw mcause, t0 +; RV32: mret +; +; RV64-LABEL: preemptible_var_alloca: +; RV64: addi s0, sp, 160 +; RV64: csrr t0, mcause +; RV64-NEXT: sd t0, -8(s0) +; RV64-NEXT: csrr t0, mepc +; RV64-NEXT: sd t0, -16(s0) +; RV64-NEXT: csrsi mstatus, 8 +; RV64: sub a0, sp, a0 +; RV64: mv sp, a0 +; RV64: call use +; RV64: addi sp, s0, -160 +; RV64: csrci mstatus, 8 +; RV64-NEXT: ld t0, -16(s0) +; RV64-NEXT: csrw mepc, t0 +; RV64-NEXT: ld t0, -8(s0) +; RV64-NEXT: csrw mcause, t0 +; RV64: mret + %count = load volatile i32, ptr @size + %object = alloca i8, i32 %count, align 16 + call void @use(ptr %object) + ret void +} + +define void @preemptible_realign() "interrupt"="SiFive-CLIC-preemptible" "frame-pointer"="none" { +; RV32-LABEL: preemptible_realign: +; RV32: addi s0, sp, +; RV32: csrr t0, mcause +; RV32-NEXT: sw t0, +; RV32-NEXT: csrr t0, mepc +; RV32-NEXT: sw t0, +; RV32-NEXT: csrsi mstatus, 8 +; RV32: andi sp, sp, -64 +; RV32: call use +; RV32: addi sp, s0, +; RV32: csrci mstatus, 8 +; RV32-NEXT: lw t0, 184(sp) +; RV32-NEXT: csrw mepc, t0 +; RV32-NEXT: lw t0, 188(sp) +; RV32-NEXT: csrw mcause, t0 +; RV32: mret +; +; RV64-LABEL: preemptible_realign: +; RV64: addi s0, sp, +; RV64: csrr t0, mcause +; RV64-NEXT: sd t0, +; RV64-NEXT: csrr t0, mepc +; RV64-NEXT: sd t0, +; RV64-NEXT: csrsi mstatus, 8 +; RV64: andi sp, sp, -64 +; RV64: call use +; RV64: addi sp, s0, +; RV64: csrci mstatus, 8 +; RV64-NEXT: ld t0, 240(sp) +; RV64-NEXT: csrw mepc, t0 +; RV64-NEXT: ld t0, 248(sp) +; RV64-NEXT: csrw mcause, t0 +; RV64: mret + %object = alloca [64 x i8], align 64 + call void @use(ptr %object) + ret void +}