[clang] [llvm] [RISCV] Support frame pointers in SiFive CLIC preemptible handlers (PR #221318)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 4 12:00:20 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-risc-v
Author: Zeyi Xu (zeyi2)
<details>
<summary>Changes</summary>
This commit adds support for frame pointers in SiFive CLIC preemptible handlers.
Keep the existing X8/X9 CSR preservation path when a frame pointer is not required. When X8 is used as the frame pointer, preserve `mcause` and `mepc` in dedicated stack slots through `X5`.
---
Full diff: https://github.com/llvm/llvm-project/pull/221318.diff
5 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+1-2)
- (modified) llvm/lib/Target/RISCV/RISCVFrameLowering.cpp (+125-59)
- (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (-6)
- (removed) llvm/test/CodeGen/RISCV/sifive-interrupt-attr-err.ll (-12)
- (added) llvm/test/CodeGen/RISCV/sifive-interrupt-frame-pointer.ll (+155)
``````````diff
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<RISCVSubtarget>().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<RISCVMachineFunctionInfo>();
+ // 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: <unknown>: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
+
+ at 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
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/221318
More information about the llvm-commits
mailing list