[clang] [llvm] [RISCV] SiFive CLIC Support (PR #132481)
Sam Elliott via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 26 16:40:34 PDT 2025
https://github.com/lenary updated https://github.com/llvm/llvm-project/pull/132481
>From efbd52bb91e432d999d42ecf2a3f3b7adbe439a5 Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Fri, 21 Mar 2025 12:09:34 -0700
Subject: [PATCH 01/10] [RISCV] Add SiFive CLIC CSRs
---
.../Driver/print-supported-extensions-riscv.c | 2 +
llvm/lib/Target/RISCV/RISCVFeatures.td | 8 ++++
llvm/lib/Target/RISCV/RISCVSystemOperands.td | 23 ++++++++++
llvm/test/MC/RISCV/rvsfmclic-invalid.s | 20 ++++++++
llvm/test/MC/RISCV/rvsfmclic-valid.s | 46 +++++++++++++++++++
llvm/test/MC/RISCV/rvsfsclic-invalid.s | 20 ++++++++
llvm/test/MC/RISCV/rvsfsclic-valid.s | 46 +++++++++++++++++++
.../TargetParser/RISCVISAInfoTest.cpp | 2 +
8 files changed, 167 insertions(+)
create mode 100644 llvm/test/MC/RISCV/rvsfmclic-invalid.s
create mode 100644 llvm/test/MC/RISCV/rvsfmclic-valid.s
create mode 100644 llvm/test/MC/RISCV/rvsfsclic-invalid.s
create mode 100644 llvm/test/MC/RISCV/rvsfsclic-valid.s
diff --git a/clang/test/Driver/print-supported-extensions-riscv.c b/clang/test/Driver/print-supported-extensions-riscv.c
index 35de2820ef84f..ca695221dee9d 100644
--- a/clang/test/Driver/print-supported-extensions-riscv.c
+++ b/clang/test/Driver/print-supported-extensions-riscv.c
@@ -213,6 +213,8 @@
// CHECK-NEXT: xqcisls 0.2 'Xqcisls' (Qualcomm uC Scaled Load Store Extension)
// CHECK-NEXT: xrivosvisni 0.1 'XRivosVisni' (Rivos Vector Integer Small New)
// CHECK-NEXT: xrivosvizip 0.1 'XRivosVizip' (Rivos Vector Register Zips)
+// CHECK-NEXT: xsfmclic 0.1 'XSfmclic' (SiFive CLIC Machine-mode CSRs)
+// CHECK-NEXT: xsfsclic 0.1 'XSfsclic' (SiFive CLIC Supervisor-mode CSRs)
// CHECK-EMPTY:
// CHECK-NEXT: Supported Profiles
// CHECK-NEXT: rva20s64
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index f23a855e7049f..0afff24e55a04 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1226,6 +1226,14 @@ def HasVendorXSfcease
AssemblerPredicate<(all_of FeatureVendorXSfcease),
"'XSfcease' (SiFive sf.cease Instruction)">;
+def FeatureVendorXSfmclic
+ : RISCVExperimentalExtension<0, 1,
+ "SiFive CLIC Machine-mode CSRs">;
+
+def FeatureVendorXSfsclic
+ : RISCVExperimentalExtension<0, 1,
+ "SiFive CLIC Supervisor-mode CSRs">;
+
// Core-V Extensions
def FeatureVendorXCVelw
diff --git a/llvm/lib/Target/RISCV/RISCVSystemOperands.td b/llvm/lib/Target/RISCV/RISCVSystemOperands.td
index 5b46e7df25fc8..7534b6921cb25 100644
--- a/llvm/lib/Target/RISCV/RISCVSystemOperands.td
+++ b/llvm/lib/Target/RISCV/RISCVSystemOperands.td
@@ -471,3 +471,26 @@ def : SysReg<"sctrstatus", 0x14f>;
def : SysReg<"sctrdepth", 0x15f>;
def : SysReg<"vsctrctl", 0x24e>;
def : SysReg<"mctrctl", 0x34e>;
+
+
+//===-----------------------------------------------
+// Vendor CSRs
+//===-----------------------------------------------
+
+// XSfmclic
+let FeaturesRequired = [{ {RISCV::FeatureVendorXSfmclic} }] in {
+def : SysReg<"mtvt", 0x307>;
+def : SysReg<"mnxti", 0x345>;
+def : SysReg<"mintstatus", 0x346>;
+def : SysReg<"mscratchcsw", 0x348>;
+def : SysReg<"mscratchcswl", 0x349>;
+}
+
+// XSfsclic
+let FeaturesRequired = [{ {RISCV::FeatureVendorXSfsclic} }] in {
+def : SysReg<"stvt", 0x107>;
+def : SysReg<"snxti", 0x145>;
+def : SysReg<"sintstatus", 0x146>;
+def : SysReg<"sscratchcsw", 0x148>;
+def : SysReg<"sscratchcswl", 0x149>;
+}
diff --git a/llvm/test/MC/RISCV/rvsfmclic-invalid.s b/llvm/test/MC/RISCV/rvsfmclic-invalid.s
new file mode 100644
index 0000000000000..c2899ab090627
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvsfmclic-invalid.s
@@ -0,0 +1,20 @@
+# RUN: not llvm-mc -triple riscv32 -mattr=-experimental-xsfmclic < %s 2>&1 \
+# RUN: | FileCheck -check-prefixes=CHECK-FEATURE %s
+
+# RUN: not llvm-mc -triple riscv64 -mattr=-experimental-xsfmclic < %s 2>&1 \
+# RUN: | FileCheck -check-prefixes=CHECK-FEATURE %s
+
+csrrs t1, mtvt, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'mtvt' requires 'experimental-xsfmclic' to be enabled
+
+csrrs t1, mnxti, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'mnxti' requires 'experimental-xsfmclic' to be enabled
+
+csrrs t1, mintstatus, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'mintstatus' requires 'experimental-xsfmclic' to be enabled
+
+csrrs t1, mscratchcsw, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'mscratchcsw' requires 'experimental-xsfmclic' to be enabled
+
+csrrs t1, mscratchcswl, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'mscratchcswl' requires 'experimental-xsfmclic' to be enabled
diff --git a/llvm/test/MC/RISCV/rvsfmclic-valid.s b/llvm/test/MC/RISCV/rvsfmclic-valid.s
new file mode 100644
index 0000000000000..7eca0cee416bf
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvsfmclic-valid.s
@@ -0,0 +1,46 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-xsfmclic -riscv-no-aliases -show-encoding \
+# RUN: | FileCheck -check-prefixes=CHECK-INST,CHECK-ENC %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xsfmclic < %s \
+# RUN: | llvm-objdump -d --mattr=+experimental-xsfmclic -M no-aliases - \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+#
+# RUN: llvm-mc %s -triple=riscv64 -mattr=+experimental-xsfmclic -riscv-no-aliases -show-encoding \
+# RUN: | FileCheck -check-prefixes=CHECK-INST,CHECK-ENC %s
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+experimental-xsfmclic < %s \
+# RUN: | llvm-objdump -d --mattr=+experimental-xsfmclic -M no-aliases - \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+
+# CHECK-INST: csrrs t1, mtvt, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x70,0x30]
+csrrs t1, mtvt, zero
+# CHECK-INST: csrrs t2, mtvt, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x70,0x30]
+csrrs t2, 0x307, zero
+
+# CHECK-INST: csrrs t1, mnxti, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x50,0x34]
+csrrs t1, mnxti, zero
+# CHECK-INST: csrrs t2, mnxti, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x50,0x34]
+csrrs t2, 0x345, zero
+
+# CHECK-INST: csrrs t1, mintstatus, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x60,0x34]
+csrrs t1, mintstatus, zero
+# CHECK-INST: csrrs t2, mintstatus, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x60,0x34]
+csrrs t2, 0x346, zero
+
+# CHECK-INST: csrrs t1, mscratchcsw, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x80,0x34]
+csrrs t1, mscratchcsw, zero
+# CHECK-INST: csrrs t2, mscratchcsw, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x80,0x34]
+csrrs t2, 0x348, zero
+
+# CHECK-INST: csrrs t1, mscratchcswl, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x90,0x34]
+csrrs t1, mscratchcswl, zero
+# CHECK-INST: csrrs t2, mscratchcswl, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x90,0x34]
+csrrs t2, 0x349, zero
diff --git a/llvm/test/MC/RISCV/rvsfsclic-invalid.s b/llvm/test/MC/RISCV/rvsfsclic-invalid.s
new file mode 100644
index 0000000000000..31770d6c2a494
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvsfsclic-invalid.s
@@ -0,0 +1,20 @@
+# RUN: not llvm-mc -triple riscv32 -mattr=-experimental-xsfsclic < %s 2>&1 \
+# RUN: | FileCheck -check-prefixes=CHECK-FEATURE %s
+
+# RUN: not llvm-mc -triple riscv64 -mattr=-experimental-xsfsclic < %s 2>&1 \
+# RUN: | FileCheck -check-prefixes=CHECK-FEATURE %s
+
+csrrs t1, stvt, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'stvt' requires 'experimental-xsfsclic' to be enabled
+
+csrrs t1, snxti, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'snxti' requires 'experimental-xsfsclic' to be enabled
+
+csrrs t1, sintstatus, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sintstatus' requires 'experimental-xsfsclic' to be enabled
+
+csrrs t1, sscratchcsw, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sscratchcsw' requires 'experimental-xsfsclic' to be enabled
+
+csrrs t1, sscratchcswl, zero
+// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sscratchcswl' requires 'experimental-xsfsclic' to be enabled
diff --git a/llvm/test/MC/RISCV/rvsfsclic-valid.s b/llvm/test/MC/RISCV/rvsfsclic-valid.s
new file mode 100644
index 0000000000000..883e0ee21d79f
--- /dev/null
+++ b/llvm/test/MC/RISCV/rvsfsclic-valid.s
@@ -0,0 +1,46 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-xsfsclic -riscv-no-aliases -show-encoding \
+# RUN: | FileCheck -check-prefixes=CHECK-INST,CHECK-ENC %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xsfsclic < %s \
+# RUN: | llvm-objdump -d --mattr=+experimental-xsfsclic -M no-aliases - \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+#
+# RUN: llvm-mc %s -triple=riscv64 -mattr=+experimental-xsfsclic -riscv-no-aliases -show-encoding \
+# RUN: | FileCheck -check-prefixes=CHECK-INST,CHECK-ENC %s
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+experimental-xsfsclic < %s \
+# RUN: | llvm-objdump -d --mattr=+experimental-xsfsclic -M no-aliases - \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+
+# CHECK-INST: csrrs t1, stvt, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x70,0x10]
+csrrs t1, stvt, zero
+# CHECK-INST: csrrs t2, stvt, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x70,0x10]
+csrrs t2, 0x107, zero
+
+# CHECK-INST: csrrs t1, snxti, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x50,0x14]
+csrrs t1, snxti, zero
+# CHECK-INST: csrrs t2, snxti, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x50,0x14]
+csrrs t2, 0x145, zero
+
+# CHECK-INST: csrrs t1, sintstatus, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x60,0x14]
+csrrs t1, sintstatus, zero
+# CHECK-INST: csrrs t2, sintstatus, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x60,0x14]
+csrrs t2, 0x146, zero
+
+# CHECK-INST: csrrs t1, sscratchcsw, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x80,0x14]
+csrrs t1, sscratchcsw, zero
+# CHECK-INST: csrrs t2, sscratchcsw, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x80,0x14]
+csrrs t2, 0x148, zero
+
+# CHECK-INST: csrrs t1, sscratchcswl, zero
+# CHECK-ENC: encoding: [0x73,0x23,0x90,0x14]
+csrrs t1, sscratchcswl, zero
+# CHECK-INST: csrrs t2, sscratchcswl, zero
+# CHECK-ENC: encoding: [0xf3,0x23,0x90,0x14]
+csrrs t2, 0x149, zero
diff --git a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
index c734f8a66d289..7b1016bdf177a 100644
--- a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
+++ b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
@@ -1158,6 +1158,8 @@ Experimental extensions
xqcisls 0.2
xrivosvisni 0.1
xrivosvizip 0.1
+ xsfmclic 0.1
+ xsfsclic 0.1
Supported Profiles
rva20s64
>From 84c5ffc37c755696b5f421d25cab04bb523bc619 Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Thu, 20 Mar 2025 18:48:06 -0700
Subject: [PATCH 02/10] [RISCV] Implement SiFive CLIC Interrupt Attributes
This Change adds support for two SiFive vendor attributes in clang:
- "SiFive-CLIC-preemptible"
- "SiFive-CLIC-stack-swap"
These can be given together, and can be combined with "machine", but
cannot be combined with any other interrupt attribute values.
These are handled primarily in RISCVFrameLowering:
- "SiFive-CLIC-stack-swap" entails swapping `sp` with `mscratchcsw` at
function entry and exit, which holds the trap stack pointer.
- "SiFive-CLIC-preemptible" entails saving `mcause` and `mepc` before
re-enabling interrupts using `mstatus`. To save these, `s0` and `s1`
are first spilled to the stack, and then the values are read into
these registers. If these registers are used in the function, their
values will be spilled a second time onto the stack with the generic
callee-saved-register handling. At the end of the function interrupts
are disabled again before `mepc` and `mcause` are restored.
The CFI information for this implementation is not correct.
Co-authored-by: Ana Pazos <apazos at quicinc.com>
---
clang/include/clang/Basic/Attr.td | 21 +-
clang/include/clang/Basic/AttrDocs.td | 15 +-
.../clang/Basic/DiagnosticSemaKinds.td | 4 +-
clang/lib/CodeGen/Targets/RISCV.cpp | 43 +-
clang/lib/Sema/SemaRISCV.cpp | 132 ++-
clang/test/Sema/riscv-interrupt-attr-qci.c | 49 +-
clang/test/Sema/riscv-interrupt-attr-sifive.c | 98 ++
clang/test/Sema/riscv-interrupt-attr.c | 46 +-
llvm/lib/CodeGen/PrologEpilogInserter.cpp | 28 +
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 160 +++
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 10 +-
.../Target/RISCV/RISCVMachineFunctionInfo.cpp | 19 +-
.../Target/RISCV/RISCVMachineFunctionInfo.h | 38 +-
.../CodeGen/RISCV/sifive-interrupt-attr.ll | 1050 +++++++++++++++++
14 files changed, 1620 insertions(+), 93 deletions(-)
create mode 100644 clang/test/Sema/riscv-interrupt-attr-sifive.c
create mode 100644 llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b8cd3475bb88a..8f1f5baef24a2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2224,10 +2224,23 @@ def NoMicroMips : InheritableAttr, TargetSpecificAttr<TargetMips32> {
def RISCVInterrupt : InheritableAttr, TargetSpecificAttr<TargetRISCV> {
let Spellings = [GCC<"interrupt">];
let Subjects = SubjectList<[Function]>;
- let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
- ["supervisor", "machine", "qci-nest", "qci-nonest"],
- ["supervisor", "machine", "qcinest", "qcinonest"],
- 1>];
+ let Args = [VariadicEnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
+ [
+ "supervisor",
+ "machine",
+ "qci-nest",
+ "qci-nonest",
+ "SiFive-CLIC-preemptible",
+ "SiFive-CLIC-stack-swap",
+ ],
+ [
+ "supervisor",
+ "machine",
+ "qcinest",
+ "qcinonest",
+ "SiFiveCLICPreemptible",
+ "SiFiveCLICStackSwap",
+ ]>];
let ParseKind = "Interrupt";
let Documentation = [RISCVInterruptDocs];
}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 34e7ff9612859..552cb7f45a7d2 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -2863,8 +2863,9 @@ targets. This attribute may be attached to a function definition and instructs
the backend to generate appropriate function entry/exit code so that it can be
used directly as an interrupt service routine.
-Permissible values for this parameter are ``supervisor``, ``machine``,
-``qci-nest`` and ``qci-nonest``. If there is no parameter, then it defaults to
+Permissible values for this parameter are ``machine``, ``supervisor``,
+``qci-nest``, ``qci-nonest``, ``SiFive-CLIC-preemptible``, and
+``SiFive-CLIC-stack-swap``. If there is no parameter, then it defaults to
``machine``.
The ``qci-nest`` and ``qci-nonest`` values require Qualcomm's Xqciint extension
@@ -2875,6 +2876,15 @@ restore interrupt state to the stack -- the ``qci-nest`` value will use
begin the interrupt handler. Both of these will use ``qc.c.mileaveret`` to
restore the state and return to the previous context.
+The ``SiFive-CLIC-preemptible`` and ``SiFive-CLIC-stack-swap`` values are used
+for machine-mode interrupts. For ``SiFive-CLIC-preemptible`` interrupts, the
+values of ``mcause`` and ``mepc`` are saved onto the stack, and interrupts are
+re-enabled. For ``SiFive-CLIC-stack-swap`` interrupts, the stack pointer is
+swapped with ``mscratch`` before its first use and after its last use.
+
+The SiFive CLIC values may be combined with each other and with the ``machine``
+attribute value. Any other combination of different values is not allowed.
+
Repeated interrupt attribute on the same declaration will cause a warning
to be emitted. In case of repeated declarations, the last one prevails.
@@ -2884,6 +2894,7 @@ https://riscv.org/specifications/privileged-isa/
The RISC-V Instruction Set Manual Volume II: Privileged Architecture
Version 1.10.
https://github.com/quic/riscv-unified-db/releases/tag/Xqci-0.7
+https://sifive.cdn.prismic.io/sifive/d1984d2b-c9b9-4c91-8de0-d68a5e64fa0f_sifive-interrupt-cookbook-v1p2.pdf
}];
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1536a3b8c920a..0c587d8b677d5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12705,7 +12705,9 @@ def err_riscv_builtin_invalid_lmul : Error<
def err_riscv_type_requires_extension : Error<
"RISC-V type %0 requires the '%1' extension">;
def err_riscv_attribute_interrupt_requires_extension : Error<
- "RISC-V interrupt attribute '%0' requires extension '%1'">;
+ "RISC-V 'interrupt' attribute '%0' requires extension '%1'">;
+def err_riscv_attribute_interrupt_invalid_combination : Error<
+ "RISC-V 'interrupt' attribute contains invalid combination of interrupt types">;
def err_std_source_location_impl_not_found : Error<
"'std::source_location::__impl' was not found; it must be defined before '__builtin_source_location' is called">;
diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp
index 5aa10ba41f5ed..14d4cee7c61d3 100644
--- a/clang/lib/CodeGen/Targets/RISCV.cpp
+++ b/clang/lib/CodeGen/Targets/RISCV.cpp
@@ -829,16 +829,39 @@ class RISCVTargetCodeGenInfo : public TargetCodeGenInfo {
if (!Attr)
return;
- const char *Kind;
- switch (Attr->getInterrupt()) {
- case RISCVInterruptAttr::supervisor: Kind = "supervisor"; break;
- case RISCVInterruptAttr::machine: Kind = "machine"; break;
- case RISCVInterruptAttr::qcinest:
- Kind = "qci-nest";
- break;
- case RISCVInterruptAttr::qcinonest:
- Kind = "qci-nonest";
- break;
+ StringRef Kind = "machine";
+ bool HasSiFiveCLICPreemptible = false;
+ bool HasSiFiveCLICStackSwap = false;
+ for (RISCVInterruptAttr::InterruptType type : Attr->interrupt()) {
+ switch (type) {
+ case RISCVInterruptAttr::machine:
+ // Do not update `Kind` because `Kind` is already "machine", or the
+ // kinds also contains SiFive types which need to be applied.
+ break;
+ case RISCVInterruptAttr::supervisor:
+ Kind = "supervisor";
+ break;
+ case RISCVInterruptAttr::qcinest:
+ Kind = "qci-nest";
+ break;
+ case RISCVInterruptAttr::qcinonest:
+ Kind = "qci-nonest";
+ break;
+ // There are three different LLVM IR attribute values for SiFive CLIC
+ // interrupt kinds, one for each kind and one extra for their combination.
+ case RISCVInterruptAttr::SiFiveCLICPreemptible: {
+ HasSiFiveCLICPreemptible = true;
+ Kind = HasSiFiveCLICStackSwap ? "SiFive-CLIC-preemptible-stack-swap"
+ : "SiFive-CLIC-preemptible";
+ break;
+ }
+ case RISCVInterruptAttr::SiFiveCLICStackSwap: {
+ HasSiFiveCLICStackSwap = true;
+ Kind = HasSiFiveCLICPreemptible ? "SiFive-CLIC-preemptible-stack-swap"
+ : "SiFive-CLIC-stack-swap";
+ break;
+ }
+ }
}
Fn->addFnAttr("interrupt", Kind);
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index f23827d566610..25ff1e99498ec 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -13,6 +13,7 @@
#include "clang/Sema/SemaRISCV.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/Attrs.inc"
#include "clang/AST/Decl.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetBuiltins.h"
@@ -1453,25 +1454,14 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
return;
}
- // Check the attribute argument. Argument is optional.
- if (!AL.checkAtMostNumArgs(SemaRef, 1))
- return;
-
- StringRef Str;
- SourceLocation ArgLoc;
-
- // 'machine'is the default interrupt mode.
- if (AL.getNumArgs() == 0)
- Str = "machine";
- else if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
- return;
-
// Semantic checks for a function with the 'interrupt' attribute:
// - Must be a function.
// - Must have no parameters.
// - Must have the 'void' return type.
- // - The attribute itself must either have no argument or one of the
- // valid interrupt types, see [RISCVInterruptDocs].
+ // - The attribute itself must have at most 2 arguments
+ // - The attribute arguments must be string literals, and valid choices.
+ // - The attribute arguments must be a valid combination
+ // - The current target must support the right extensions for the combination.
if (D->getFunctionType() == nullptr) {
Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
@@ -1491,35 +1481,105 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
return;
}
- RISCVInterruptAttr::InterruptType Kind;
- if (!RISCVInterruptAttr::ConvertStrToInterruptType(Str, Kind)) {
- Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
- << AL << Str << ArgLoc;
+ if (!AL.checkAtMostNumArgs(SemaRef, 2))
return;
- }
- switch (Kind) {
- default:
- break;
- case RISCVInterruptAttr::InterruptType::qcinest:
- case RISCVInterruptAttr::InterruptType::qcinonest: {
- const TargetInfo &TI = getASTContext().getTargetInfo();
- llvm::StringMap<bool> FunctionFeatureMap;
- getASTContext().getFunctionFeatureMap(FunctionFeatureMap,
- dyn_cast<FunctionDecl>(D));
+ bool HasSiFiveCLICType = false;
+ bool HasUnaryType = false;
+
+ SmallSet<RISCVInterruptAttr::InterruptType, 2> Types;
+ for (unsigned ArgIndex = 0; ArgIndex < AL.getNumArgs(); ++ArgIndex) {
+ RISCVInterruptAttr::InterruptType Type;
+ StringRef TypeString;
+ SourceLocation Loc;
- if (!TI.hasFeature("experimental-xqciint") &&
- !FunctionFeatureMap.lookup("experimental-xqciint")) {
- Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_requires_extension)
- << Str << "Xqciint";
+ if (!SemaRef.checkStringLiteralArgumentAttr(AL, ArgIndex, TypeString, &Loc))
+ return;
+
+ if (!RISCVInterruptAttr::ConvertStrToInterruptType(TypeString, Type)) {
+ std::string TypeLiteral = ("\"" + TypeString + "\"").str();
+ Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
+ << AL << TypeLiteral << Loc;
return;
}
- break;
+
+ switch (Type) {
+ case RISCVInterruptAttr::machine:
+ // "machine" could be combined with the SiFive CLIC types, or could be
+ // just "machine".
+ break;
+ case RISCVInterruptAttr::SiFiveCLICPreemptible:
+ case RISCVInterruptAttr::SiFiveCLICStackSwap:
+ // SiFive-CLIC types can be combined with each other and "machine"
+ HasSiFiveCLICType = true;
+ break;
+ case RISCVInterruptAttr::supervisor:
+ case RISCVInterruptAttr::qcinest:
+ case RISCVInterruptAttr::qcinonest:
+ // "supervisor" and "qci-(no)nest" cannot be combined with any other types
+ HasUnaryType = true;
+ break;
+ }
+
+ Types.insert(Type);
+ }
+
+ if (HasUnaryType && Types.size() > 1) {
+ Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_invalid_combination);
+ return;
}
+
+ if (HasUnaryType && HasSiFiveCLICType) {
+ Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_invalid_combination);
+ return;
+ }
+
+ // "machine" is the default, if nothing is specified.
+ if (AL.getNumArgs() == 0)
+ Types.insert(RISCVInterruptAttr::machine);
+
+ const TargetInfo &TI = getASTContext().getTargetInfo();
+ llvm::StringMap<bool> FunctionFeatureMap;
+ getASTContext().getFunctionFeatureMap(FunctionFeatureMap,
+ dyn_cast<FunctionDecl>(D));
+
+ auto HasFeature = [&](StringRef FeatureName) -> bool {
+ return TI.hasFeature(FeatureName) || FunctionFeatureMap.lookup(FeatureName);
};
- D->addAttr(::new (getASTContext())
- RISCVInterruptAttr(getASTContext(), AL, Kind));
+ for (RISCVInterruptAttr::InterruptType Type : Types) {
+ switch (Type) {
+ // The QCI interrupt types require Xqciint
+ case RISCVInterruptAttr::qcinest:
+ case RISCVInterruptAttr::qcinonest: {
+ if (!HasFeature("experimental-xqciint")) {
+ Diag(AL.getLoc(),
+ diag::err_riscv_attribute_interrupt_requires_extension)
+ << RISCVInterruptAttr::ConvertInterruptTypeToStr(Type) << "Xqciint";
+ return;
+ }
+ } break;
+ // The SiFive CLIC interrupt types require Xsfmclic
+ case RISCVInterruptAttr::SiFiveCLICPreemptible:
+ case RISCVInterruptAttr::SiFiveCLICStackSwap: {
+ if (!HasFeature("experimental-xsfmclic")) {
+ Diag(AL.getLoc(),
+ diag::err_riscv_attribute_interrupt_requires_extension)
+ << RISCVInterruptAttr::ConvertInterruptTypeToStr(Type)
+ << "XSfmclic";
+ return;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+
+ SmallVector<RISCVInterruptAttr::InterruptType, 2> TypesVec(Types.begin(),
+ Types.end());
+
+ D->addAttr(::new (getASTContext()) RISCVInterruptAttr(
+ getASTContext(), AL, TypesVec.data(), TypesVec.size()));
}
bool SemaRISCV::isAliasValid(unsigned BuiltinID, StringRef AliasName) {
diff --git a/clang/test/Sema/riscv-interrupt-attr-qci.c b/clang/test/Sema/riscv-interrupt-attr-qci.c
index bdac4e154bb3c..e54c50c0e25bb 100644
--- a/clang/test/Sema/riscv-interrupt-attr-qci.c
+++ b/clang/test/Sema/riscv-interrupt-attr-qci.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple riscv32-unknown-elf -target-feature +experimental-xqciint -emit-llvm -DCHECK_IR < %s| FileCheck %s
+// RUN: %clang_cc1 -triple riscv32-unknown-elf -target-feature +experimental-xqciint -emit-llvm -DCHECK_IR < %s | FileCheck %s
// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -target-feature +experimental-xqciint -verify=enabled,both -fsyntax-only
// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -verify=disabled,both -fsyntax-only
// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -target-feature -experimental-xqciint -verify=disabled,both -fsyntax-only
@@ -11,10 +11,20 @@
__attribute__((interrupt("qci-nest")))
void foo_nest_interrupt(void) {}
-// CHECK-LABEL: @foo_nonnest_interrupt() #1
+// CHECK-LABEL: @foo_nest_nest_interrupt() #0
+// CHECK: ret void
+__attribute__((interrupt("qci-nest", "qci-nest")))
+void foo_nest_nest_interrupt(void) {}
+
+// CHECK-LABEL: @foo_nonest_interrupt() #1
// CHECK: ret void
__attribute__((interrupt("qci-nonest")))
-void foo_nonnest_interrupt(void) {}
+void foo_nonest_interrupt(void) {}
+
+// CHECK-LABEL: @foo_nonest_nonest_interrupt() #1
+// CHECK: ret void
+__attribute__((interrupt("qci-nonest", "qci-nonest")))
+void foo_nonest_nonest_interrupt(void) {}
// CHECK: attributes #0
// CHECK: "interrupt"="qci-nest"
@@ -22,18 +32,23 @@ void foo_nonnest_interrupt(void) {}
// CHECK: "interrupt"="qci-nonest"
#else
// Test for QCI extension's interrupt attribute support
-__attribute__((interrupt("qci-est"))) void foo_nest1(void) {} // both-warning {{'interrupt' attribute argument not supported: qci-est}}
-__attribute__((interrupt("qci-noest"))) void foo_nonest1(void) {} // both-warning {{'interrupt' attribute argument not supported: qci-noest}}
-__attribute__((interrupt(1))) void foo_nest2(void) {} // both-error {{expected string literal as argument of 'interrupt' attribute}}
-__attribute__((interrupt("qci-nest", "qci-nonest"))) void foo1(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}}
-__attribute__((interrupt("qci-nonest", "qci-nest"))) void foo2(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}}
-__attribute__((interrupt("", "qci-nonest"))) void foo3(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}}
-__attribute__((interrupt("", "qci-nest"))) void foo4(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}}
-__attribute__((interrupt("qci-nonest", 1))) void foo5(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}}
-__attribute__((interrupt("qci-nest", 1))) void foo6(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}}
-
-__attribute__((interrupt("qci-nest"))) void foo_nest(void) {} // disabled-error {{RISC-V interrupt attribute 'qci-nest' requires extension 'Xqciint'}}
-__attribute__((interrupt("qci-nonest"))) void foo_nonest(void) {} // disabled-error {{RISC-V interrupt attribute 'qci-nonest' requires extension 'Xqciint'}}
+__attribute__((interrupt(1))) void foo1(void) {} // both-error {{expected string literal as argument of 'interrupt' attribute}}
+__attribute__((interrupt("qci-nonest", 1))) void foo_nonest2(void) {} // both-error {{expected string literal as argument of 'interrupt' attribute}}
+__attribute__((interrupt("qci-nest", 1))) void foo_nest2(void) {} // both-error {{expected string literal as argument of 'interrupt' attribute}}
+__attribute__((interrupt("qci-est"))) void foo_nest3(void) {} // both-warning {{'interrupt' attribute argument not supported: "qci-est"}}
+__attribute__((interrupt("qci-noest"))) void foo_nonest3(void) {} // both-warning {{'interrupt' attribute argument not supported: "qci-noest"}}
+__attribute__((interrupt("", "qci-nonest"))) void foo_nonest4(void) {} // both-warning {{'interrupt' attribute argument not supported: ""}}
+__attribute__((interrupt("", "qci-nest"))) void foo_nest4(void) {} // both-warning {{'interrupt' attribute argument not supported: ""}}
+
+__attribute__((interrupt("qci-nonest", "qci-nest"))) void foo_nonest5(void) {} // both-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
+__attribute__((interrupt("qci-nest", "qci-nonest"))) void foo_nest5(void) {} // both-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
+
+__attribute__((interrupt("qci-nest"))) void foo_nest(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'qci-nest' requires extension 'Xqciint'}}
+__attribute__((interrupt("qci-nonest"))) void foo_nonest(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'qci-nonest' requires extension 'Xqciint'}}
+
+__attribute__((interrupt("qci-nest", "qci-nest"))) void foo_nest_nest(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'qci-nest' requires extension 'Xqciint'}}
+__attribute__((interrupt("qci-nonest", "qci-nonest"))) void foo_nonest_nonest(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'qci-nonest' requires extension 'Xqciint'}}
+
// This tests the errors for the qci interrupts when using
// `__attribute__((target(...)))` - but they fail on RV64, because you cannot
@@ -44,8 +59,8 @@ __attribute__((target("arch=+xqciint"))) __attribute__((interrupt("qci-nonest"))
// The attribute order is important, the interrupt attribute must come after the
// target attribute
-__attribute__((interrupt("qci-nest"))) __attribute__((target("arch=+xqciint"))) void foo_nest_xqciint2(void) {} // disabled-error {{RISC-V interrupt attribute 'qci-nest' requires extension 'Xqciint'}}
-__attribute__((interrupt("qci-nonest"))) __attribute__((target("arch=+xqciint"))) void foo_nonest_xqciint2(void) {} // disabled-error {{RISC-V interrupt attribute 'qci-nonest' requires extension 'Xqciint'}}
+__attribute__((interrupt("qci-nest"))) __attribute__((target("arch=+xqciint"))) void foo_nest_xqciint2(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'qci-nest' requires extension 'Xqciint'}}
+__attribute__((interrupt("qci-nonest"))) __attribute__((target("arch=+xqciint"))) void foo_nonest_xqciint2(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'qci-nonest' requires extension 'Xqciint'}}
#endif
#endif
diff --git a/clang/test/Sema/riscv-interrupt-attr-sifive.c b/clang/test/Sema/riscv-interrupt-attr-sifive.c
new file mode 100644
index 0000000000000..792d72df07563
--- /dev/null
+++ b/clang/test/Sema/riscv-interrupt-attr-sifive.c
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -triple riscv32-unknown-elf -emit-llvm -DCHECK_IR < %s| FileCheck %s
+// RUN: %clang_cc1 -triple riscv64-unknown-elf -emit-llvm -DCHECK_IR < %s| FileCheck %s
+// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -verify -fsyntax-only
+// RUN: %clang_cc1 %s -triple riscv64-unknown-elf -verify -fsyntax-only
+
+#if defined(CHECK_IR)
+// CHECK-LABEL: @foo_stack_swap() #0
+// CHECK: ret void
+__attribute__((interrupt("SiFive-CLIC-stack-swap"))) void foo_stack_swap(void) {}
+
+// CHECK-LABEL: @foo_preemptible() #1
+// CHECK: ret void
+__attribute__((interrupt("SiFive-CLIC-preemptible"))) void foo_preemptible(void) {}
+
+// CHECK-LABEL: @foo_stack_swap_preemptible() #2
+// CHECK: ret void
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "SiFive-CLIC-preemptible")))
+void foo_stack_swap_preemptible(void) {}
+
+// CHECK-LABEL: @foo_preemptible_stack_swap() #2
+// CHECK: ret void
+__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-stack-swap")))
+void foo_preemptible_stack_swap(void) {}
+
+// CHECK-LABEL: @foo_stack_swap_repeat() #0
+// CHECK: ret void
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "SiFive-CLIC-stack-swap")))
+void foo_stack_swap_repeat(void) {}
+
+// CHECK-LABEL: @foo_preemptible_repeat() #1
+// CHECK: ret void
+__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-preemptible")))
+void foo_preemptible_repeat(void) {}
+
+// CHECK-LABEL: @foo_machine_stack_swap() #0
+// CHECK: ret void
+__attribute__((interrupt("machine", "SiFive-CLIC-stack-swap")))
+void foo_machine_stack_swap(void) {}
+
+// CHECK-LABEL: @foo_stack_swap_machine() #0
+// CHECK: ret void
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "machine")))
+void foo_stack_swap_machine(void) {}
+
+// CHECK-LABEL: @foo_preemptible_machine() #1
+// CHECK: ret void
+__attribute__((interrupt("SiFive-CLIC-preemptible", "machine")))
+void foo_preemptible_machine(void) {}
+
+// CHECK-LABEL: @foo_machine_preemptible() #1
+// CHECK: ret void
+__attribute__((interrupt("machine", "SiFive-CLIC-preemptible")))
+void foo_machine_preemptible(void) {}
+
+
+// CHECK: attributes #0
+// CHECK: "interrupt"="SiFive-CLIC-stack-swap"
+// CHECK: attributes #1
+// CHECK: "interrupt"="SiFive-CLIC-preemptible"
+// CHECK: attributes #2
+// CHECK: "interrupt"="SiFive-CLIC-preemptible-stack-swap"
+#else
+
+__attribute__((interrupt("SiFive-CLIC-stack-swap"))) void foo15(void);
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "SiFive-CLIC-stack-swap"))) void foo15(void);
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "machine"))) void foo15(void);
+__attribute__((interrupt("machine", "SiFive-CLIC-stack-swap"))) void foo15(void);
+
+__attribute__((interrupt("SiFive-CLIC-preemptible"))) void foo15(void);
+__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-preemptible"))) void foo15(void);
+__attribute__((interrupt("SiFive-CLIC-preemptible", "machine"))) void foo15(void);
+__attribute__((interrupt("machine", "SiFive-CLIC-preemptible"))) void foo15(void);
+
+__attribute__((interrupt("SiFive-CLIC-stack-swap"))) void foo16(void) {}
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "SiFive-CLIC-stack-swap"))) void foo17(void) {}
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "machine"))) void foo18(void) {}
+__attribute__((interrupt("machine", "SiFive-CLIC-stack-swap"))) void foo19(void) {}
+
+__attribute__((interrupt("SiFive-CLIC-preemptible"))) void foo20(void) {}
+__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-preemptible"))) void foo21(void) {}
+__attribute__((interrupt("SiFive-CLIC-preemptible", "machine"))) void foo22(void) {}
+__attribute__((interrupt("machine", "SiFive-CLIC-preemptible"))) void foo23(void) {}
+
+__attribute__((interrupt("machine", "machine", "SiFive-CLIC-preemptible"))) void foo24(void) {} // expected-error {{'interrupt' attribute takes no more than 2 arguments}}
+
+__attribute__((interrupt("SiFive-CLIC-preemptible", "supervisor"))) void foo27(void) {} // expected-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
+
+__attribute__((interrupt("supervisor", "SiFive-CLIC-stack-swap"))) void foo28(void) {} // expected-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
+
+__attribute__((interrupt("SiFive-CLIC-stack-swap", 1))) void foo29(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
+
+__attribute__((interrupt(1, "SiFive-CLIC-stack-swap"))) void foo30(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
+
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "foo"))) void foo31(void) {} // expected-warning {{'interrupt' attribute argument not supported: "foo"}}
+
+__attribute__((interrupt("foo", "SiFive-CLIC-stack-swap"))) void foo32(void) {} // expected-warning {{'interrupt' attribute argument not supported: "foo"}}
+
+#endif
diff --git a/clang/test/Sema/riscv-interrupt-attr.c b/clang/test/Sema/riscv-interrupt-attr.c
index 756bfa0582de7..f46723e892fb6 100644
--- a/clang/test/Sema/riscv-interrupt-attr.c
+++ b/clang/test/Sema/riscv-interrupt-attr.c
@@ -16,37 +16,48 @@ __attribute__((interrupt())) void foo_default(void) {}
// CHECK-LABEL: @foo_default2() #1
// CHECK: ret void
__attribute__((interrupt())) void foo_default2(void) {}
+// CHECK-LABEL: @foo_machine_twice() #1
+// CHECK: ret void
+__attribute__((interrupt("machine", "machine")))
+void foo_machine_twice(void) {}
+// CHECK-LABEL: @foo_supervisor_twice() #0
+// CHECK: ret void
+__attribute__((interrupt("supervisor", "supervisor")))
+void foo_supervisor_twice(void) {}
+
// CHECK: attributes #0
// CHECK: "interrupt"="supervisor"
// CHECK: attributes #1
// CHECK: "interrupt"="machine"
#else
+__attribute__((interrupt("machine"), interrupt("supervisor"))) void foo6(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \
+ // expected-note {{repeated RISC-V 'interrupt' attribute is here}}
+
+__attribute__((interrupt, interrupt)) void foo7(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \
+ // expected-note {{repeated RISC-V 'interrupt' attribute is here}}
struct a { int b; };
struct a test __attribute__((interrupt)); // expected-warning {{'interrupt' attribute only applies to functions}}
-__attribute__((interrupt(42))) void foo0(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
-__attribute__((interrupt("USER"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: USER}}
-__attribute__((interrupt("user"))) void foo1b(void) {} // expected-warning {{'interrupt' attribute argument not supported: user}}
-__attribute__((interrupt("MACHINE"))) void foo1c(void) {} // expected-warning {{'interrupt' attribute argument not supported: MACHINE}}
-
-__attribute__((interrupt("machine", 1))) void foo2(void) {} // expected-error {{'interrupt' attribute takes no more than 1 argument}}
-
__attribute__((interrupt)) int foo3(void) {return 0;} // expected-warning {{RISC-V 'interrupt' attribute only applies to functions that have a 'void' return type}}
-
-__attribute__((interrupt())) void foo4(void);
-__attribute__((interrupt())) void foo4(void) {}
-
__attribute__((interrupt())) void foo5(int a) {} // expected-warning {{RISC-V 'interrupt' attribute only applies to functions that have no parameters}}
-__attribute__((interrupt("machine"), interrupt("supervisor"))) void foo6(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \
- // expected-note {{repeated RISC-V 'interrupt' attribute is here}}
+__attribute__((interrupt("machine", "supervisor", "machine"))) void foo15(void) {} // expected-error {{'interrupt' attribute takes no more than 2 arguments}}
-__attribute__((interrupt, interrupt)) void foo7(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \
- // expected-note {{repeated RISC-V 'interrupt' attribute is here}}
+__attribute__((interrupt(42))) void foo0(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
+__attribute__((interrupt("machine", 1))) void foo2(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
+__attribute__((interrupt("USER"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: "USER"}}
+__attribute__((interrupt("user"))) void foo1b(void) {} // expected-warning {{'interrupt' attribute argument not supported: "user"}}
+__attribute__((interrupt("MACHINE"))) void foo1c(void) {} // expected-warning {{'interrupt' attribute argument not supported: "MACHINE"}}
__attribute__((interrupt(""))) void foo8(void) {} // expected-warning {{'interrupt' attribute argument not supported}}
+__attribute__((interrupt("machine", "supervisor"))) void foo_machine_supervisor(void) {} // expected-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
+__attribute__((interrupt("supervisor", "machine"))) void foo_supervisor_machine(void) {} // expected-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
+
+__attribute__((interrupt())) void foo4(void);
+__attribute__((interrupt())) void foo4(void) {}
+
__attribute__((interrupt("supervisor"))) void foo9(void);
__attribute__((interrupt("machine"))) void foo9(void);
@@ -54,5 +65,10 @@ __attribute__((interrupt("supervisor"))) void foo11(void) {}
__attribute__((interrupt("machine"))) void foo12(void) {}
__attribute__((interrupt())) void foo13(void) {}
__attribute__((interrupt)) void foo14(void) {}
+
+__attribute__((interrupt("machine", "machine"))) void foo_machine_twice(void) {}
+__attribute__((interrupt("supervisor", "supervisor"))) void foo_supervisor_supervisor(void) {}
+
+
#endif
diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index 9b852c0fd49cf..bd60faab0911a 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -230,10 +230,16 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
// with stack arguments.
TFI->spillFPBP(MF);
+ LLVM_DEBUG(llvm::dbgs() << "Before calculateCallFrameInfo \n");
+ LLVM_DEBUG(MF.dump());
+
// Calculate the MaxCallFrameSize value for the function's frame
// information. Also eliminates call frame pseudo instructions.
calculateCallFrameInfo(MF);
+ LLVM_DEBUG(llvm::dbgs() << "Before calculateSaveRestoreBlocks \n");
+ LLVM_DEBUG(MF.dump());
+
// Determine placement of CSR spill/restore code and prolog/epilog code:
// place all spills in the entry block, all restores in return blocks.
calculateSaveRestoreBlocks(MF);
@@ -243,17 +249,29 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
for (MachineBasicBlock *SaveBlock : SaveBlocks)
stashEntryDbgValues(*SaveBlock, EntryDbgValues);
+ LLVM_DEBUG(llvm::dbgs() << "Before spillCalleeSavedRegs \n");
+ LLVM_DEBUG(MF.dump());
+
// Handle CSR spilling and restoring, for targets that need it.
if (MF.getTarget().usesPhysRegsForValues())
spillCalleeSavedRegs(MF);
+ LLVM_DEBUG(llvm::dbgs() << "Before processFunctionBeforeFrameFinalized \n");
+ LLVM_DEBUG(MF.dump());
+
// Allow the target machine to make final modifications to the function
// before the frame layout is finalized.
TFI->processFunctionBeforeFrameFinalized(MF, RS);
+ LLVM_DEBUG(llvm::dbgs() << "Before calculateFrameObjectOffsets \n");
+ LLVM_DEBUG(MF.dump());
+
// Calculate actual frame offsets for all abstract stack objects...
calculateFrameObjectOffsets(MF);
+ LLVM_DEBUG(llvm::dbgs() << "Before insertPrologEpilogCode \n");
+ LLVM_DEBUG(MF.dump());
+
// Add prolog and epilog code to the function. This function is required
// to align the stack frame as necessary for any stack variables or
// called functions. Because of this, calculateCalleeSavedRegisters()
@@ -266,10 +284,17 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
for (auto &I : EntryDbgValues)
I.first->insert(I.first->begin(), I.second.begin(), I.second.end());
+ LLVM_DEBUG(
+ llvm::dbgs() << "Before processFunctionBeforeFrameIndicesReplaced \n");
+ LLVM_DEBUG(MF.dump());
+
// Allow the target machine to make final modifications to the function
// before the frame layout is finalized.
TFI->processFunctionBeforeFrameIndicesReplaced(MF, RS);
+ LLVM_DEBUG(llvm::dbgs() << "Before frame index resolution \n");
+ LLVM_DEBUG(MF.dump());
+
// Replace all MO_FrameIndex operands with physical register references
// and actual offsets.
if (TFI->needsFrameIndexResolution(MF)) {
@@ -284,6 +309,9 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
replaceFrameIndices(MF);
}
+ LLVM_DEBUG(llvm::dbgs() << "After frame index resolution \n");
+ LLVM_DEBUG(MF.dump());
+
// If register scavenging is needed, as we've enabled doing it as a
// post-pass, scavenge the virtual registers that frame index elimination
// inserted.
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index e1314d4fee8a0..50375a37957ad 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "RISCVFrameLowering.h"
+#include "MCTargetDesc/RISCVBaseInfo.h"
#include "RISCVMachineFunctionInfo.h"
#include "RISCVSubtarget.h"
#include "llvm/BinaryFormat/Dwarf.h"
@@ -26,6 +27,8 @@
#include <algorithm>
+#define DEBUG_TYPE "riscv-frame"
+
using namespace llvm;
namespace {
@@ -264,6 +267,149 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
.setMIFlags(MachineInstr::FrameDestroy);
}
+// Insert instruction to swap mscratchsw with sp
+static void emitSifiveCLICStackSwap(MachineFunction &MF, MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL) {
+ auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+ if (!RVFI->isSiFiveStackSwapInterrupt(MF))
+ return;
+
+ const auto &STI = MF.getSubtarget<RISCVSubtarget>();
+ const RISCVInstrInfo *TII = STI.getInstrInfo();
+
+ assert(STI.hasVendorXSfmclic() && "Stack Swapping Requires XSfmclic");
+
+ BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW))
+ .addReg(SPReg, RegState::Define)
+ .addImm(RISCVSysReg::mscratchcsw)
+ .addReg(SPReg, RegState::Kill)
+ .setMIFlag(MachineInstr::FrameSetup);
+
+ // FIXME: CFI Information for this swap.
+}
+
+static void
+createSiFivePreemptibleInterruptFrameEntries(MachineFunction &MF,
+ RISCVMachineFunctionInfo &RVFI) {
+ if (!RVFI.isSiFivePreemptibleInterrupt(MF))
+ return;
+
+ const TargetRegisterClass &RC = RISCV::GPRRegClass;
+ const TargetRegisterInfo &TRI =
+ *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.
+ for (int I = 0; I < 2; ++I) {
+ int FI = MFI.CreateStackObject(TRI.getSpillSize(RC), TRI.getSpillAlign(RC),
+ false);
+ RVFI.pushInterruptCSRFrameIndex(FI);
+ }
+}
+
+static void emitSifiveCLICPreemptibleSaves(MachineFunction &MF,
+ MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL) {
+ auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+ if (!RVFI->isSiFivePreemptibleInterrupt(MF))
+ return;
+
+ const auto &STI = MF.getSubtarget<RISCVSubtarget>();
+ const RISCVInstrInfo *TII = STI.getInstrInfo();
+
+ // 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, STI.getRegisterInfo(),
+ Register(), MachineInstr::FrameSetup);
+ TII->storeRegToStackSlot(MBB, MBBI, RISCV::X9, /* IsKill=*/true,
+ RVFI->getInterruptCSRFrameIndex(1),
+ &RISCV::GPRRegClass, STI.getRegisterInfo(),
+ 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::lookupSysRegByName("MEPC")->Encoding)
+ .addReg(RISCV::X0)
+ .setMIFlag(MachineInstr::FrameSetup);
+
+ // Enable interrupts.
+ BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRSI))
+ .addReg(RISCV::X0)
+ .addImm(RISCVSysReg::mstatus)
+ .addImm(8)
+ .setMIFlag(MachineInstr::FrameSetup);
+}
+
+static void emitSifiveCLICPreemptibleRestores(MachineFunction &MF,
+ MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL) {
+ auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+ if (!RVFI->isSiFivePreemptibleInterrupt(MF))
+ return;
+
+ const auto &STI = MF.getSubtarget<RISCVSubtarget>();
+ const RISCVInstrInfo *TII = STI.getInstrInfo();
+
+ // FIXME: CFI Information here is nonexistent/wrong.
+
+ // Disable interrupts.
+ BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRCI))
+ .addReg(RISCV::X0)
+ .addImm(RISCVSysReg::mstatus)
+ .addImm(8)
+ .setMIFlag(MachineInstr::FrameSetup);
+
+ // 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)
+ .addImm(RISCVSysReg::mepc)
+ .addReg(RISCV::X9, RegState::Kill)
+ .setMIFlag(MachineInstr::FrameSetup);
+ BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW))
+ .addReg(RISCV::X0)
+ .addImm(RISCVSysReg::mcause)
+ .addReg(RISCV::X8, RegState::Kill)
+ .setMIFlag(MachineInstr::FrameSetup);
+
+ // 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, STI.getRegisterInfo(),
+ Register(), MachineInstr::FrameSetup);
+ TII->loadRegFromStackSlot(MBB, MBBI, RISCV::X8,
+ RVFI->getInterruptCSRFrameIndex(0),
+ &RISCV::GPRRegClass, STI.getRegisterInfo(),
+ Register(), MachineInstr::FrameSetup);
+}
+
// Get the ID of the libcall used for spilling and restoring callee saved
// registers. The ID is representative of the number of registers saved or
// restored by the libcall, except it is zero-indexed - ID 0 corresponds to a
@@ -869,6 +1015,9 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
if (MF.getFunction().getCallingConv() == CallingConv::GHC)
return;
+ // SiFive CLIC needs to swap `sp` into `mscratchcsw`
+ emitSifiveCLICStackSwap(MF, MBB, MBBI, DL);
+
// Emit prologue for shadow call stack.
emitSCSPrologue(MF, MBB, MBBI, DL);
@@ -988,6 +1137,9 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
allocateStack(MBB, MBBI, MF, StackSize, RealStackSize, /*EmitCFI=*/true,
NeedProbe, ProbeSize, DynAllocation);
+ // Save SiFive CLIC CSRs into Stack
+ 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
// registers as the frame pointer must be modified after it has been saved
@@ -1304,12 +1456,17 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF,
}
}
+ emitSifiveCLICPreemptibleRestores(MF, MBB, MBBI, DL);
+
// Deallocate stack if StackSize isn't a zero yet
if (StackSize != 0)
deallocateStack(MF, MBB, MBBI, DL, StackSize, RealStackSize - StackSize);
// Emit epilogue for shadow call stack.
emitSCSEpilogue(MF, MBB, MBBI, DL);
+
+ //
+ emitSifiveCLICStackSwap(MF, MBB, MBBI, DL);
}
StackOffset
@@ -1502,6 +1659,9 @@ void RISCVFrameLowering::determineCalleeSaves(MachineFunction &MF,
auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
if (RVFI->isPushable(MF) && SavedRegs.test(RISCV::X26))
SavedRegs.set(RISCV::X27);
+
+ // SiFive Preemptible Interrupt Handlers need additional frame entries
+ createSiFivePreemptibleInterruptFrameEntries(MF, *RVFI);
}
std::pair<int64_t, Align>
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 48d8fc23dc1bb..c5b19023d8bf5 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -20836,15 +20836,21 @@ SDValue RISCVTargetLowering::LowerFormalArguments(
"supervisor",
"qci-nest",
"qci-nonest",
+ "SiFive-CLIC-preemptible",
+ "SiFive-CLIC-stack-swap",
+ "SiFive-CLIC-preemptible-stack-swap",
};
if (llvm::find(SupportedInterruptKinds, Kind) ==
std::end(SupportedInterruptKinds))
report_fatal_error(
"Function interrupt attribute argument not supported!");
- if ((Kind == "qci-nest" || Kind == "qci-nonest") &&
- !Subtarget.hasVendorXqciint())
+ if (Kind.starts_with("qci-") && !Subtarget.hasVendorXqciint())
report_fatal_error("'qci-*' interrupt kinds require Xqciint extension");
+
+ if (Kind.starts_with("SiFive-CLIC-") && !Subtarget.hasVendorXSfmclic())
+ report_fatal_error(
+ "'SiFive-CLIC-*' interrupt kinds require XSfmclic extension");
}
EVT PtrVT = getPointerTy(DAG.getDataLayout());
diff --git a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp
index 1c8eda10f1958..920a795737138 100644
--- a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp
@@ -67,12 +67,17 @@ RISCVMachineFunctionInfo::getInterruptStackKind(
StringRef InterruptVal =
MF.getFunction().getFnAttribute("interrupt").getValueAsString();
- if (InterruptVal == "qci-nest")
- return InterruptStackKind::QCINest;
- if (InterruptVal == "qci-nonest")
- return InterruptStackKind::QCINoNest;
- return InterruptStackKind::None;
+ return StringSwitch<RISCVMachineFunctionInfo::InterruptStackKind>(
+ InterruptVal)
+ .Case("qci-nest", InterruptStackKind::QCINest)
+ .Case("qci-nonest", InterruptStackKind::QCINoNest)
+ .Case("SiFive-CLIC-preemptible",
+ InterruptStackKind::SiFiveCLICPreemptible)
+ .Case("SiFive-CLIC-stack-swap", InterruptStackKind::SiFiveCLICStackSwap)
+ .Case("SiFive-CLIC-preemptible-stack-swap",
+ InterruptStackKind::SiFiveCLICPreemptibleStackSwap)
+ .Default(InterruptStackKind::None);
}
void yaml::RISCVMachineFunctionInfo::mappingImpl(yaml::IO &YamlIO) {
@@ -87,6 +92,10 @@ RISCVMachineFunctionInfo::getPushPopKind(const MachineFunction &MF) const {
if (VarArgsSaveSize != 0)
return PushPopKind::None;
+ // SiFive interrupts are not compatible with push/pop.
+ if (useSiFiveInterrupt(MF))
+ return PushPopKind::None;
+
// Zcmp is not compatible with the frame pointer convention.
if (MF.getSubtarget<RISCVSubtarget>().hasStdExtZcmp() &&
!MF.getTarget().Options.DisableFramePointerElim(MF))
diff --git a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
index 4d06dea7414f1..4fa93f157f52b 100644
--- a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
@@ -78,6 +78,9 @@ class RISCVMachineFunctionInfo : public MachineFunctionInfo {
/// Size of any opaque stack adjustment due to QCI Interrupt instructions.
unsigned QCIInterruptStackSize = 0;
+ /// Store Frame Indexes for Interrupt-Related CSR Spills.
+ SmallVector<int, 2> InterruptCSRFrameIndexes;
+
int64_t StackProbeSize = 0;
/// Does it probe the stack for a dynamic allocation?
@@ -153,7 +156,14 @@ class RISCVMachineFunctionInfo : public MachineFunctionInfo {
unsigned getRVPushStackSize() const { return RVPushStackSize; }
void setRVPushStackSize(unsigned Size) { RVPushStackSize = Size; }
- enum class InterruptStackKind { None = 0, QCINest, QCINoNest };
+ enum class InterruptStackKind {
+ None = 0,
+ QCINest,
+ QCINoNest,
+ SiFiveCLICPreemptible,
+ SiFiveCLICStackSwap,
+ SiFiveCLICPreemptibleStackSwap
+ };
InterruptStackKind getInterruptStackKind(const MachineFunction &MF) const;
@@ -166,6 +176,32 @@ class RISCVMachineFunctionInfo : public MachineFunctionInfo {
unsigned getQCIInterruptStackSize() const { return QCIInterruptStackSize; }
void setQCIInterruptStackSize(unsigned Size) { QCIInterruptStackSize = Size; }
+ bool useSiFiveInterrupt(const MachineFunction &MF) const {
+ InterruptStackKind Kind = getInterruptStackKind(MF);
+ return Kind == InterruptStackKind::SiFiveCLICPreemptible ||
+ Kind == InterruptStackKind::SiFiveCLICStackSwap ||
+ Kind == InterruptStackKind::SiFiveCLICPreemptibleStackSwap;
+ }
+
+ bool isSiFivePreemptibleInterrupt(const MachineFunction &MF) const {
+ InterruptStackKind Kind = getInterruptStackKind(MF);
+ return Kind == InterruptStackKind::SiFiveCLICPreemptible ||
+ Kind == InterruptStackKind::SiFiveCLICPreemptibleStackSwap;
+ }
+
+ bool isSiFiveStackSwapInterrupt(const MachineFunction &MF) const {
+ InterruptStackKind Kind = getInterruptStackKind(MF);
+ return Kind == InterruptStackKind::SiFiveCLICStackSwap ||
+ Kind == InterruptStackKind::SiFiveCLICPreemptibleStackSwap;
+ }
+
+ void pushInterruptCSRFrameIndex(int FI) {
+ InterruptCSRFrameIndexes.push_back(FI);
+ }
+ int getInterruptCSRFrameIndex(size_t Idx) const {
+ return InterruptCSRFrameIndexes[Idx];
+ }
+
// Some Stack Management Variants automatically update FP in a frame-pointer
// convention compatible way - which means we don't need to manually update
// the FP, but we still need to emit the correct CFI information for
diff --git a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
new file mode 100644
index 0000000000000..9485827ba179e
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
@@ -0,0 +1,1050 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple riscv32-unknown-elf -mattr=+experimental-xsfmclic -o - %s 2>&1 \
+; RUN: | FileCheck %s --check-prefix=RV32
+; RUN: llc -mtriple riscv64-unknown-elf -mattr=+experimental-xsfmclic -o - %s 2>&1 \
+; RUN: | FileCheck %s --check-prefix=RV64
+
+; Test Handling of the SiFive-CLIC interrupt attributes.
+;
+; "stack-swap" means that sp should be swapped into `mscratchcsw`
+;
+; "preemptible" means that `mcause` and `mepc` should be saved and interrupts
+; should be re-enabled by setting a bit in `mstatus`.
+
+; FIXME: A lot of the CFI information here is wrong.
+
+define void @stack_swap_empty() "interrupt"="SiFive-CLIC-stack-swap" {
+; RV32-LABEL: stack_swap_empty:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: stack_swap_empty:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ ret void
+}
+
+define void @stack_swap_empty_fp() "interrupt"="SiFive-CLIC-stack-swap" "frame-pointer"="all" {
+; RV32-LABEL: stack_swap_empty_fp:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: .cfi_def_cfa_offset 16
+; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s0, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset ra, -4
+; RV32-NEXT: .cfi_offset s0, -8
+; RV32-NEXT: addi s0, sp, 16
+; RV32-NEXT: .cfi_def_cfa s0, 0
+; RV32-NEXT: .cfi_def_cfa sp, 16
+; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore ra
+; RV32-NEXT: .cfi_restore s0
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: stack_swap_empty_fp:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: addi sp, sp, -16
+; RV64-NEXT: .cfi_def_cfa_offset 16
+; RV64-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s0, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset ra, -8
+; RV64-NEXT: .cfi_offset s0, -16
+; RV64-NEXT: addi s0, sp, 16
+; RV64-NEXT: .cfi_def_cfa s0, 0
+; RV64-NEXT: .cfi_def_cfa sp, 16
+; RV64-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore ra
+; RV64-NEXT: .cfi_restore s0
+; RV64-NEXT: addi sp, sp, 16
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ ret void
+}
+
+define void @preemptible_empty() "interrupt"="SiFive-CLIC-preemptible" {
+; RV32-LABEL: preemptible_empty:
+; RV32: # %bb.0:
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: .cfi_def_cfa_offset 16
+; RV32-NEXT: sw s0, 12(sp)
+; RV32-NEXT: sw s1, 8(sp)
+; RV32-NEXT: csrr s0, mcause
+; RV32-NEXT: csrr s1, mepc
+; RV32-NEXT: csrsi mstatus, 8
+; RV32-NEXT: csrci mstatus, 8
+; RV32-NEXT: csrw mepc, s1
+; RV32-NEXT: csrw mcause, s0
+; RV32-NEXT: lw s1, 8(sp)
+; RV32-NEXT: lw s0, 12(sp)
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: mret
+;
+; RV64-LABEL: preemptible_empty:
+; RV64: # %bb.0:
+; RV64-NEXT: addi sp, sp, -16
+; RV64-NEXT: .cfi_def_cfa_offset 16
+; RV64-NEXT: sd s0, 8(sp)
+; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: csrr s0, mcause
+; RV64-NEXT: csrr s1, mepc
+; RV64-NEXT: csrsi mstatus, 8
+; RV64-NEXT: csrci mstatus, 8
+; RV64-NEXT: csrw mepc, s1
+; RV64-NEXT: csrw mcause, s0
+; RV64-NEXT: ld s1, 0(sp)
+; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: addi sp, sp, 16
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: mret
+ ret void
+}
+
+define void @both_empty() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
+; RV32-LABEL: both_empty:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: .cfi_def_cfa_offset 16
+; RV32-NEXT: sw s0, 12(sp)
+; RV32-NEXT: sw s1, 8(sp)
+; RV32-NEXT: csrr s0, mcause
+; RV32-NEXT: csrr s1, mepc
+; RV32-NEXT: csrsi mstatus, 8
+; RV32-NEXT: csrci mstatus, 8
+; RV32-NEXT: csrw mepc, s1
+; RV32-NEXT: csrw mcause, s0
+; RV32-NEXT: lw s1, 8(sp)
+; RV32-NEXT: lw s0, 12(sp)
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: both_empty:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: addi sp, sp, -16
+; RV64-NEXT: .cfi_def_cfa_offset 16
+; RV64-NEXT: sd s0, 8(sp)
+; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: csrr s0, mcause
+; RV64-NEXT: csrr s1, mepc
+; RV64-NEXT: csrsi mstatus, 8
+; RV64-NEXT: csrci mstatus, 8
+; RV64-NEXT: csrw mepc, s1
+; RV64-NEXT: csrw mcause, s0
+; RV64-NEXT: ld s1, 0(sp)
+; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: addi sp, sp, 16
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ ret void
+}
+
+declare void @callee()
+
+define void @stack_swap_caller() "interrupt"="SiFive-CLIC-stack-swap" {
+; RV32-LABEL: stack_swap_caller:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: addi sp, sp, -64
+; RV32-NEXT: .cfi_def_cfa_offset 64
+; RV32-NEXT: sw ra, 60(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t0, 56(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t1, 52(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t2, 48(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a0, 44(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a1, 40(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a2, 36(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a3, 32(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a4, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a5, 24(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a6, 20(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a7, 16(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t3, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t4, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t5, 4(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t6, 0(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset ra, -4
+; RV32-NEXT: .cfi_offset t0, -8
+; RV32-NEXT: .cfi_offset t1, -12
+; RV32-NEXT: .cfi_offset t2, -16
+; RV32-NEXT: .cfi_offset a0, -20
+; RV32-NEXT: .cfi_offset a1, -24
+; RV32-NEXT: .cfi_offset a2, -28
+; RV32-NEXT: .cfi_offset a3, -32
+; RV32-NEXT: .cfi_offset a4, -36
+; RV32-NEXT: .cfi_offset a5, -40
+; RV32-NEXT: .cfi_offset a6, -44
+; RV32-NEXT: .cfi_offset a7, -48
+; RV32-NEXT: .cfi_offset t3, -52
+; RV32-NEXT: .cfi_offset t4, -56
+; RV32-NEXT: .cfi_offset t5, -60
+; RV32-NEXT: .cfi_offset t6, -64
+; RV32-NEXT: call callee
+; RV32-NEXT: lw ra, 60(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t0, 56(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t1, 52(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t2, 48(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a0, 44(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a1, 40(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a2, 36(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a3, 32(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a4, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a5, 24(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a6, 20(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a7, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t3, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t4, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t5, 4(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t6, 0(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore ra
+; RV32-NEXT: .cfi_restore t0
+; RV32-NEXT: .cfi_restore t1
+; RV32-NEXT: .cfi_restore t2
+; RV32-NEXT: .cfi_restore a0
+; RV32-NEXT: .cfi_restore a1
+; RV32-NEXT: .cfi_restore a2
+; RV32-NEXT: .cfi_restore a3
+; RV32-NEXT: .cfi_restore a4
+; RV32-NEXT: .cfi_restore a5
+; RV32-NEXT: .cfi_restore a6
+; RV32-NEXT: .cfi_restore a7
+; RV32-NEXT: .cfi_restore t3
+; RV32-NEXT: .cfi_restore t4
+; RV32-NEXT: .cfi_restore t5
+; RV32-NEXT: .cfi_restore t6
+; RV32-NEXT: addi sp, sp, 64
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: stack_swap_caller:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: addi sp, sp, -128
+; RV64-NEXT: .cfi_def_cfa_offset 128
+; RV64-NEXT: sd ra, 120(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t0, 112(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t1, 104(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t2, 96(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a0, 88(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a1, 80(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a2, 72(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a3, 64(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a4, 56(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a5, 48(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a6, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a7, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t3, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t4, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t5, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t6, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset ra, -8
+; RV64-NEXT: .cfi_offset t0, -16
+; RV64-NEXT: .cfi_offset t1, -24
+; RV64-NEXT: .cfi_offset t2, -32
+; RV64-NEXT: .cfi_offset a0, -40
+; RV64-NEXT: .cfi_offset a1, -48
+; RV64-NEXT: .cfi_offset a2, -56
+; RV64-NEXT: .cfi_offset a3, -64
+; RV64-NEXT: .cfi_offset a4, -72
+; RV64-NEXT: .cfi_offset a5, -80
+; RV64-NEXT: .cfi_offset a6, -88
+; RV64-NEXT: .cfi_offset a7, -96
+; RV64-NEXT: .cfi_offset t3, -104
+; RV64-NEXT: .cfi_offset t4, -112
+; RV64-NEXT: .cfi_offset t5, -120
+; RV64-NEXT: .cfi_offset t6, -128
+; RV64-NEXT: call callee
+; RV64-NEXT: ld ra, 120(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t0, 112(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t1, 104(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t2, 96(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a0, 88(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a1, 80(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a2, 72(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a3, 64(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a4, 56(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a5, 48(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a6, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a7, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t3, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t4, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t5, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t6, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore ra
+; RV64-NEXT: .cfi_restore t0
+; RV64-NEXT: .cfi_restore t1
+; RV64-NEXT: .cfi_restore t2
+; RV64-NEXT: .cfi_restore a0
+; RV64-NEXT: .cfi_restore a1
+; RV64-NEXT: .cfi_restore a2
+; RV64-NEXT: .cfi_restore a3
+; RV64-NEXT: .cfi_restore a4
+; RV64-NEXT: .cfi_restore a5
+; RV64-NEXT: .cfi_restore a6
+; RV64-NEXT: .cfi_restore a7
+; RV64-NEXT: .cfi_restore t3
+; RV64-NEXT: .cfi_restore t4
+; RV64-NEXT: .cfi_restore t5
+; RV64-NEXT: .cfi_restore t6
+; RV64-NEXT: addi sp, sp, 128
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ call void @callee()
+ ret void
+}
+
+define void @stack_swap_caller_fp() "interrupt"="SiFive-CLIC-stack-swap" "frame-pointer"="all" {
+; RV32-LABEL: stack_swap_caller_fp:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: addi sp, sp, -80
+; RV32-NEXT: .cfi_def_cfa_offset 80
+; RV32-NEXT: sw ra, 76(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t0, 72(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t1, 68(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t2, 64(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s0, 60(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a0, 56(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a1, 52(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a2, 48(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a3, 44(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a4, 40(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a5, 36(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a6, 32(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a7, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t3, 24(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t4, 20(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t5, 16(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t6, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset ra, -4
+; RV32-NEXT: .cfi_offset t0, -8
+; RV32-NEXT: .cfi_offset t1, -12
+; RV32-NEXT: .cfi_offset t2, -16
+; RV32-NEXT: .cfi_offset s0, -20
+; RV32-NEXT: .cfi_offset a0, -24
+; RV32-NEXT: .cfi_offset a1, -28
+; RV32-NEXT: .cfi_offset a2, -32
+; RV32-NEXT: .cfi_offset a3, -36
+; RV32-NEXT: .cfi_offset a4, -40
+; RV32-NEXT: .cfi_offset a5, -44
+; RV32-NEXT: .cfi_offset a6, -48
+; RV32-NEXT: .cfi_offset a7, -52
+; RV32-NEXT: .cfi_offset t3, -56
+; RV32-NEXT: .cfi_offset t4, -60
+; RV32-NEXT: .cfi_offset t5, -64
+; RV32-NEXT: .cfi_offset t6, -68
+; RV32-NEXT: addi s0, sp, 80
+; RV32-NEXT: .cfi_def_cfa s0, 0
+; RV32-NEXT: call callee
+; RV32-NEXT: .cfi_def_cfa sp, 80
+; RV32-NEXT: lw ra, 76(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t0, 72(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t1, 68(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t2, 64(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 60(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a0, 56(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a1, 52(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a2, 48(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a3, 44(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a4, 40(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a5, 36(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a6, 32(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a7, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t3, 24(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t4, 20(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t5, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t6, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore ra
+; RV32-NEXT: .cfi_restore t0
+; RV32-NEXT: .cfi_restore t1
+; RV32-NEXT: .cfi_restore t2
+; RV32-NEXT: .cfi_restore s0
+; RV32-NEXT: .cfi_restore a0
+; RV32-NEXT: .cfi_restore a1
+; RV32-NEXT: .cfi_restore a2
+; RV32-NEXT: .cfi_restore a3
+; RV32-NEXT: .cfi_restore a4
+; RV32-NEXT: .cfi_restore a5
+; RV32-NEXT: .cfi_restore a6
+; RV32-NEXT: .cfi_restore a7
+; RV32-NEXT: .cfi_restore t3
+; RV32-NEXT: .cfi_restore t4
+; RV32-NEXT: .cfi_restore t5
+; RV32-NEXT: .cfi_restore t6
+; RV32-NEXT: addi sp, sp, 80
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: stack_swap_caller_fp:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: addi sp, sp, -144
+; RV64-NEXT: .cfi_def_cfa_offset 144
+; RV64-NEXT: sd ra, 136(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t0, 128(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t1, 120(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t2, 112(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s0, 104(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a0, 96(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a1, 88(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a2, 80(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a3, 72(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a4, 64(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a5, 56(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a6, 48(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a7, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t3, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t4, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t5, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t6, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset ra, -8
+; RV64-NEXT: .cfi_offset t0, -16
+; RV64-NEXT: .cfi_offset t1, -24
+; RV64-NEXT: .cfi_offset t2, -32
+; RV64-NEXT: .cfi_offset s0, -40
+; RV64-NEXT: .cfi_offset a0, -48
+; RV64-NEXT: .cfi_offset a1, -56
+; RV64-NEXT: .cfi_offset a2, -64
+; RV64-NEXT: .cfi_offset a3, -72
+; RV64-NEXT: .cfi_offset a4, -80
+; RV64-NEXT: .cfi_offset a5, -88
+; RV64-NEXT: .cfi_offset a6, -96
+; RV64-NEXT: .cfi_offset a7, -104
+; RV64-NEXT: .cfi_offset t3, -112
+; RV64-NEXT: .cfi_offset t4, -120
+; RV64-NEXT: .cfi_offset t5, -128
+; RV64-NEXT: .cfi_offset t6, -136
+; RV64-NEXT: addi s0, sp, 144
+; RV64-NEXT: .cfi_def_cfa s0, 0
+; RV64-NEXT: call callee
+; RV64-NEXT: .cfi_def_cfa sp, 144
+; RV64-NEXT: ld ra, 136(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t0, 128(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t1, 120(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t2, 112(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 104(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a0, 96(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a1, 88(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a2, 80(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a3, 72(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a4, 64(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a5, 56(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a6, 48(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a7, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t3, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t4, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t5, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t6, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore ra
+; RV64-NEXT: .cfi_restore t0
+; RV64-NEXT: .cfi_restore t1
+; RV64-NEXT: .cfi_restore t2
+; RV64-NEXT: .cfi_restore s0
+; RV64-NEXT: .cfi_restore a0
+; RV64-NEXT: .cfi_restore a1
+; RV64-NEXT: .cfi_restore a2
+; RV64-NEXT: .cfi_restore a3
+; RV64-NEXT: .cfi_restore a4
+; RV64-NEXT: .cfi_restore a5
+; RV64-NEXT: .cfi_restore a6
+; RV64-NEXT: .cfi_restore a7
+; RV64-NEXT: .cfi_restore t3
+; RV64-NEXT: .cfi_restore t4
+; RV64-NEXT: .cfi_restore t5
+; RV64-NEXT: .cfi_restore t6
+; RV64-NEXT: addi sp, sp, 144
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ call void @callee()
+ ret void
+}
+
+define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
+; RV32-LABEL: preeemptible_caller:
+; RV32: # %bb.0:
+; RV32-NEXT: addi sp, sp, -80
+; RV32-NEXT: .cfi_def_cfa_offset 80
+; RV32-NEXT: sw s0, 12(sp)
+; RV32-NEXT: sw s1, 8(sp)
+; RV32-NEXT: csrr s0, mcause
+; RV32-NEXT: csrr s1, mepc
+; RV32-NEXT: csrsi mstatus, 8
+; RV32-NEXT: sw ra, 76(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t0, 72(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t1, 68(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t2, 64(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a0, 60(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a1, 56(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a2, 52(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a3, 48(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a4, 44(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a5, 40(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a6, 36(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a7, 32(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t3, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t4, 24(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t5, 20(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t6, 16(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset ra, -4
+; RV32-NEXT: .cfi_offset t0, -8
+; RV32-NEXT: .cfi_offset t1, -12
+; RV32-NEXT: .cfi_offset t2, -16
+; RV32-NEXT: .cfi_offset a0, -20
+; RV32-NEXT: .cfi_offset a1, -24
+; RV32-NEXT: .cfi_offset a2, -28
+; RV32-NEXT: .cfi_offset a3, -32
+; RV32-NEXT: .cfi_offset a4, -36
+; RV32-NEXT: .cfi_offset a5, -40
+; RV32-NEXT: .cfi_offset a6, -44
+; RV32-NEXT: .cfi_offset a7, -48
+; RV32-NEXT: .cfi_offset t3, -52
+; RV32-NEXT: .cfi_offset t4, -56
+; RV32-NEXT: .cfi_offset t5, -60
+; RV32-NEXT: .cfi_offset t6, -64
+; RV32-NEXT: call callee
+; RV32-NEXT: lw ra, 76(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t0, 72(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t1, 68(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t2, 64(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a0, 60(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a1, 56(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a2, 52(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a3, 48(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a4, 44(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a5, 40(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a6, 36(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a7, 32(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t3, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t4, 24(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t5, 20(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t6, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore ra
+; RV32-NEXT: .cfi_restore t0
+; RV32-NEXT: .cfi_restore t1
+; RV32-NEXT: .cfi_restore t2
+; RV32-NEXT: .cfi_restore a0
+; RV32-NEXT: .cfi_restore a1
+; RV32-NEXT: .cfi_restore a2
+; RV32-NEXT: .cfi_restore a3
+; RV32-NEXT: .cfi_restore a4
+; RV32-NEXT: .cfi_restore a5
+; RV32-NEXT: .cfi_restore a6
+; RV32-NEXT: .cfi_restore a7
+; RV32-NEXT: .cfi_restore t3
+; RV32-NEXT: .cfi_restore t4
+; RV32-NEXT: .cfi_restore t5
+; RV32-NEXT: .cfi_restore t6
+; RV32-NEXT: csrci mstatus, 8
+; RV32-NEXT: csrw mepc, s1
+; RV32-NEXT: csrw mcause, s0
+; RV32-NEXT: lw s1, 8(sp)
+; RV32-NEXT: lw s0, 12(sp)
+; RV32-NEXT: addi sp, sp, 80
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: mret
+;
+; RV64-LABEL: preeemptible_caller:
+; RV64: # %bb.0:
+; RV64-NEXT: addi sp, sp, -144
+; RV64-NEXT: .cfi_def_cfa_offset 144
+; RV64-NEXT: sd s0, 8(sp)
+; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: csrr s0, mcause
+; RV64-NEXT: csrr s1, mepc
+; RV64-NEXT: csrsi mstatus, 8
+; RV64-NEXT: sd ra, 136(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t0, 128(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t1, 120(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t2, 112(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a0, 104(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a1, 96(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a2, 88(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a3, 80(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a4, 72(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a5, 64(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a6, 56(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a7, 48(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t3, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t4, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t5, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t6, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset ra, -8
+; RV64-NEXT: .cfi_offset t0, -16
+; RV64-NEXT: .cfi_offset t1, -24
+; RV64-NEXT: .cfi_offset t2, -32
+; RV64-NEXT: .cfi_offset a0, -40
+; RV64-NEXT: .cfi_offset a1, -48
+; RV64-NEXT: .cfi_offset a2, -56
+; RV64-NEXT: .cfi_offset a3, -64
+; RV64-NEXT: .cfi_offset a4, -72
+; RV64-NEXT: .cfi_offset a5, -80
+; RV64-NEXT: .cfi_offset a6, -88
+; RV64-NEXT: .cfi_offset a7, -96
+; RV64-NEXT: .cfi_offset t3, -104
+; RV64-NEXT: .cfi_offset t4, -112
+; RV64-NEXT: .cfi_offset t5, -120
+; RV64-NEXT: .cfi_offset t6, -128
+; RV64-NEXT: call callee
+; RV64-NEXT: ld ra, 136(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t0, 128(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t1, 120(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t2, 112(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a0, 104(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a1, 96(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a2, 88(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a3, 80(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a4, 72(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a5, 64(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a6, 56(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a7, 48(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t3, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t4, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t5, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t6, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore ra
+; RV64-NEXT: .cfi_restore t0
+; RV64-NEXT: .cfi_restore t1
+; RV64-NEXT: .cfi_restore t2
+; RV64-NEXT: .cfi_restore a0
+; RV64-NEXT: .cfi_restore a1
+; RV64-NEXT: .cfi_restore a2
+; RV64-NEXT: .cfi_restore a3
+; RV64-NEXT: .cfi_restore a4
+; RV64-NEXT: .cfi_restore a5
+; RV64-NEXT: .cfi_restore a6
+; RV64-NEXT: .cfi_restore a7
+; RV64-NEXT: .cfi_restore t3
+; RV64-NEXT: .cfi_restore t4
+; RV64-NEXT: .cfi_restore t5
+; RV64-NEXT: .cfi_restore t6
+; RV64-NEXT: csrci mstatus, 8
+; RV64-NEXT: csrw mepc, s1
+; RV64-NEXT: csrw mcause, s0
+; RV64-NEXT: ld s1, 0(sp)
+; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: addi sp, sp, 144
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: mret
+ call void @callee()
+ ret void
+}
+
+define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
+; RV32-LABEL: both_caller:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: addi sp, sp, -80
+; RV32-NEXT: .cfi_def_cfa_offset 80
+; RV32-NEXT: sw s0, 12(sp)
+; RV32-NEXT: sw s1, 8(sp)
+; RV32-NEXT: csrr s0, mcause
+; RV32-NEXT: csrr s1, mepc
+; RV32-NEXT: csrsi mstatus, 8
+; RV32-NEXT: sw ra, 76(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t0, 72(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t1, 68(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t2, 64(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a0, 60(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a1, 56(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a2, 52(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a3, 48(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a4, 44(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a5, 40(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a6, 36(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a7, 32(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t3, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t4, 24(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t5, 20(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t6, 16(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset ra, -4
+; RV32-NEXT: .cfi_offset t0, -8
+; RV32-NEXT: .cfi_offset t1, -12
+; RV32-NEXT: .cfi_offset t2, -16
+; RV32-NEXT: .cfi_offset a0, -20
+; RV32-NEXT: .cfi_offset a1, -24
+; RV32-NEXT: .cfi_offset a2, -28
+; RV32-NEXT: .cfi_offset a3, -32
+; RV32-NEXT: .cfi_offset a4, -36
+; RV32-NEXT: .cfi_offset a5, -40
+; RV32-NEXT: .cfi_offset a6, -44
+; RV32-NEXT: .cfi_offset a7, -48
+; RV32-NEXT: .cfi_offset t3, -52
+; RV32-NEXT: .cfi_offset t4, -56
+; RV32-NEXT: .cfi_offset t5, -60
+; RV32-NEXT: .cfi_offset t6, -64
+; RV32-NEXT: call callee
+; RV32-NEXT: lw ra, 76(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t0, 72(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t1, 68(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t2, 64(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a0, 60(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a1, 56(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a2, 52(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a3, 48(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a4, 44(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a5, 40(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a6, 36(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a7, 32(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t3, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t4, 24(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t5, 20(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t6, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore ra
+; RV32-NEXT: .cfi_restore t0
+; RV32-NEXT: .cfi_restore t1
+; RV32-NEXT: .cfi_restore t2
+; RV32-NEXT: .cfi_restore a0
+; RV32-NEXT: .cfi_restore a1
+; RV32-NEXT: .cfi_restore a2
+; RV32-NEXT: .cfi_restore a3
+; RV32-NEXT: .cfi_restore a4
+; RV32-NEXT: .cfi_restore a5
+; RV32-NEXT: .cfi_restore a6
+; RV32-NEXT: .cfi_restore a7
+; RV32-NEXT: .cfi_restore t3
+; RV32-NEXT: .cfi_restore t4
+; RV32-NEXT: .cfi_restore t5
+; RV32-NEXT: .cfi_restore t6
+; RV32-NEXT: csrci mstatus, 8
+; RV32-NEXT: csrw mepc, s1
+; RV32-NEXT: csrw mcause, s0
+; RV32-NEXT: lw s1, 8(sp)
+; RV32-NEXT: lw s0, 12(sp)
+; RV32-NEXT: addi sp, sp, 80
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: both_caller:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: addi sp, sp, -144
+; RV64-NEXT: .cfi_def_cfa_offset 144
+; RV64-NEXT: sd s0, 8(sp)
+; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: csrr s0, mcause
+; RV64-NEXT: csrr s1, mepc
+; RV64-NEXT: csrsi mstatus, 8
+; RV64-NEXT: sd ra, 136(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t0, 128(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t1, 120(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t2, 112(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a0, 104(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a1, 96(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a2, 88(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a3, 80(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a4, 72(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a5, 64(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a6, 56(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a7, 48(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t3, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t4, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t5, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t6, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset ra, -8
+; RV64-NEXT: .cfi_offset t0, -16
+; RV64-NEXT: .cfi_offset t1, -24
+; RV64-NEXT: .cfi_offset t2, -32
+; RV64-NEXT: .cfi_offset a0, -40
+; RV64-NEXT: .cfi_offset a1, -48
+; RV64-NEXT: .cfi_offset a2, -56
+; RV64-NEXT: .cfi_offset a3, -64
+; RV64-NEXT: .cfi_offset a4, -72
+; RV64-NEXT: .cfi_offset a5, -80
+; RV64-NEXT: .cfi_offset a6, -88
+; RV64-NEXT: .cfi_offset a7, -96
+; RV64-NEXT: .cfi_offset t3, -104
+; RV64-NEXT: .cfi_offset t4, -112
+; RV64-NEXT: .cfi_offset t5, -120
+; RV64-NEXT: .cfi_offset t6, -128
+; RV64-NEXT: call callee
+; RV64-NEXT: ld ra, 136(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t0, 128(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t1, 120(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t2, 112(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a0, 104(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a1, 96(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a2, 88(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a3, 80(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a4, 72(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a5, 64(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a6, 56(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a7, 48(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t3, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t4, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t5, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t6, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore ra
+; RV64-NEXT: .cfi_restore t0
+; RV64-NEXT: .cfi_restore t1
+; RV64-NEXT: .cfi_restore t2
+; RV64-NEXT: .cfi_restore a0
+; RV64-NEXT: .cfi_restore a1
+; RV64-NEXT: .cfi_restore a2
+; RV64-NEXT: .cfi_restore a3
+; RV64-NEXT: .cfi_restore a4
+; RV64-NEXT: .cfi_restore a5
+; RV64-NEXT: .cfi_restore a6
+; RV64-NEXT: .cfi_restore a7
+; RV64-NEXT: .cfi_restore t3
+; RV64-NEXT: .cfi_restore t4
+; RV64-NEXT: .cfi_restore t5
+; RV64-NEXT: .cfi_restore t6
+; RV64-NEXT: csrci mstatus, 8
+; RV64-NEXT: csrw mepc, s1
+; RV64-NEXT: csrw mcause, s0
+; RV64-NEXT: ld s1, 0(sp)
+; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: addi sp, sp, 144
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ call void @callee()
+ ret void
+}
+
+define void @stack_swap_clobber() "interrupt"="SiFive-CLIC-stack-swap" {
+; RV32-LABEL: stack_swap_clobber:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: .cfi_def_cfa_offset 16
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset s0, -4
+; RV32-NEXT: .cfi_offset s1, -8
+; RV32-NEXT: #APP
+; RV32-NEXT: #NO_APP
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore s0
+; RV32-NEXT: .cfi_restore s1
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: stack_swap_clobber:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: addi sp, sp, -16
+; RV64-NEXT: .cfi_def_cfa_offset 16
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset s0, -8
+; RV64-NEXT: .cfi_offset s1, -16
+; RV64-NEXT: #APP
+; RV64-NEXT: #NO_APP
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore s0
+; RV64-NEXT: .cfi_restore s1
+; RV64-NEXT: addi sp, sp, 16
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ call void asm sideeffect "", "~{x8},~{x9}"() #4
+ ret void
+}
+
+define void @stack_swap_clobber_fp() "interrupt"="SiFive-CLIC-stack-swap" "frame-pointer"="all" {
+; RV32-LABEL: stack_swap_clobber_fp:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: .cfi_def_cfa_offset 16
+; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s0, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 4(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset ra, -4
+; RV32-NEXT: .cfi_offset s0, -8
+; RV32-NEXT: .cfi_offset s1, -12
+; RV32-NEXT: addi s0, sp, 16
+; RV32-NEXT: .cfi_def_cfa s0, 0
+; RV32-NEXT: #APP
+; RV32-NEXT: #NO_APP
+; RV32-NEXT: .cfi_def_cfa sp, 16
+; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 4(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore ra
+; RV32-NEXT: .cfi_restore s0
+; RV32-NEXT: .cfi_restore s1
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: stack_swap_clobber_fp:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: addi sp, sp, -32
+; RV64-NEXT: .cfi_def_cfa_offset 32
+; RV64-NEXT: sd ra, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s0, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset ra, -8
+; RV64-NEXT: .cfi_offset s0, -16
+; RV64-NEXT: .cfi_offset s1, -24
+; RV64-NEXT: addi s0, sp, 32
+; RV64-NEXT: .cfi_def_cfa s0, 0
+; RV64-NEXT: #APP
+; RV64-NEXT: #NO_APP
+; RV64-NEXT: .cfi_def_cfa sp, 32
+; RV64-NEXT: ld ra, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore ra
+; RV64-NEXT: .cfi_restore s0
+; RV64-NEXT: .cfi_restore s1
+; RV64-NEXT: addi sp, sp, 32
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ call void asm sideeffect "", "~{x8},~{x9}"() #4
+ ret void
+}
+
+define void @preemptible_clobber() "interrupt"="SiFive-CLIC-preemptible" {
+; RV32-LABEL: preemptible_clobber:
+; RV32: # %bb.0:
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: .cfi_def_cfa_offset 16
+; RV32-NEXT: sw s0, 4(sp)
+; RV32-NEXT: sw s1, 0(sp)
+; RV32-NEXT: csrr s0, mcause
+; RV32-NEXT: csrr s1, mepc
+; RV32-NEXT: csrsi mstatus, 8
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset s0, -4
+; RV32-NEXT: .cfi_offset s1, -8
+; RV32-NEXT: #APP
+; RV32-NEXT: #NO_APP
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore s0
+; RV32-NEXT: .cfi_restore s1
+; RV32-NEXT: csrci mstatus, 8
+; RV32-NEXT: csrw mepc, s1
+; RV32-NEXT: csrw mcause, s0
+; RV32-NEXT: lw s1, 0(sp)
+; RV32-NEXT: lw s0, 4(sp)
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: mret
+;
+; RV64-LABEL: preemptible_clobber:
+; RV64: # %bb.0:
+; RV64-NEXT: addi sp, sp, -32
+; RV64-NEXT: .cfi_def_cfa_offset 32
+; RV64-NEXT: sd s0, 8(sp)
+; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: csrr s0, mcause
+; RV64-NEXT: csrr s1, mepc
+; RV64-NEXT: csrsi mstatus, 8
+; RV64-NEXT: sd s0, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset s0, -8
+; RV64-NEXT: .cfi_offset s1, -16
+; RV64-NEXT: #APP
+; RV64-NEXT: #NO_APP
+; RV64-NEXT: ld s0, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore s0
+; RV64-NEXT: .cfi_restore s1
+; RV64-NEXT: csrci mstatus, 8
+; RV64-NEXT: csrw mepc, s1
+; RV64-NEXT: csrw mcause, s0
+; RV64-NEXT: ld s1, 0(sp)
+; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: addi sp, sp, 32
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: mret
+ call void asm sideeffect "", "~{x8},~{x9}"() #4
+ ret void
+}
+
+define void @both_clobber() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
+; RV32-LABEL: both_clobber:
+; RV32: # %bb.0:
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: .cfi_def_cfa_offset 16
+; RV32-NEXT: sw s0, 4(sp)
+; RV32-NEXT: sw s1, 0(sp)
+; RV32-NEXT: csrr s0, mcause
+; RV32-NEXT: csrr s1, mepc
+; RV32-NEXT: csrsi mstatus, 8
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset s0, -4
+; RV32-NEXT: .cfi_offset s1, -8
+; RV32-NEXT: #APP
+; RV32-NEXT: #NO_APP
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: .cfi_restore s0
+; RV32-NEXT: .cfi_restore s1
+; RV32-NEXT: csrci mstatus, 8
+; RV32-NEXT: csrw mepc, s1
+; RV32-NEXT: csrw mcause, s0
+; RV32-NEXT: lw s1, 0(sp)
+; RV32-NEXT: lw s0, 4(sp)
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: .cfi_def_cfa_offset 0
+; RV32-NEXT: csrrw sp, mscratchcsw, sp
+; RV32-NEXT: mret
+;
+; RV64-LABEL: both_clobber:
+; RV64: # %bb.0:
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: addi sp, sp, -32
+; RV64-NEXT: .cfi_def_cfa_offset 32
+; RV64-NEXT: sd s0, 8(sp)
+; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: csrr s0, mcause
+; RV64-NEXT: csrr s1, mepc
+; RV64-NEXT: csrsi mstatus, 8
+; RV64-NEXT: sd s0, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset s0, -8
+; RV64-NEXT: .cfi_offset s1, -16
+; RV64-NEXT: #APP
+; RV64-NEXT: #NO_APP
+; RV64-NEXT: ld s0, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: .cfi_restore s0
+; RV64-NEXT: .cfi_restore s1
+; RV64-NEXT: csrci mstatus, 8
+; RV64-NEXT: csrw mepc, s1
+; RV64-NEXT: csrw mcause, s0
+; RV64-NEXT: ld s1, 0(sp)
+; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: addi sp, sp, 32
+; RV64-NEXT: .cfi_def_cfa_offset 0
+; RV64-NEXT: csrrw sp, mscratchcsw, sp
+; RV64-NEXT: mret
+ call void asm sideeffect "", "~{x8},~{x9}"() #4
+ ret void
+}
>From 52d85b53dc1a4a6891dfd5006f6de3bc7c32378b Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Fri, 21 Mar 2025 15:35:13 -0700
Subject: [PATCH 03/10] Fix Clang SiFive Interrupts Test
---
clang/test/Sema/riscv-interrupt-attr-sifive.c | 62 +++++++++++--------
1 file changed, 35 insertions(+), 27 deletions(-)
diff --git a/clang/test/Sema/riscv-interrupt-attr-sifive.c b/clang/test/Sema/riscv-interrupt-attr-sifive.c
index 792d72df07563..9b41e38664d8d 100644
--- a/clang/test/Sema/riscv-interrupt-attr-sifive.c
+++ b/clang/test/Sema/riscv-interrupt-attr-sifive.c
@@ -1,7 +1,11 @@
-// RUN: %clang_cc1 -triple riscv32-unknown-elf -emit-llvm -DCHECK_IR < %s| FileCheck %s
-// RUN: %clang_cc1 -triple riscv64-unknown-elf -emit-llvm -DCHECK_IR < %s| FileCheck %s
-// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -verify -fsyntax-only
-// RUN: %clang_cc1 %s -triple riscv64-unknown-elf -verify -fsyntax-only
+// RUN: %clang_cc1 -triple riscv32-unknown-elf -target-feature +experimental-xsfmclic -emit-llvm -DCHECK_IR < %s| FileCheck %s
+// RUN: %clang_cc1 -triple riscv64-unknown-elf -target-feature +experimental-xsfmclic -emit-llvm -DCHECK_IR < %s| FileCheck %s
+// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -target-feature +experimental-xsfmclic -verify=enabled,both -fsyntax-only
+// RUN: %clang_cc1 %s -triple riscv64-unknown-elf -target-feature +experimental-xsfmclic -verify=enabled,both -fsyntax-only
+// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -verify=disabled,both -fsyntax-only
+// RUN: %clang_cc1 %s -triple riscv64-unknown-elf -verify=disabled,both -fsyntax-only
+// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -target-feature -experimental-xsfmclic -verify=disabled,both -fsyntax-only
+// RUN: %clang_cc1 %s -triple riscv64-unknown-elf -target-feature -experimental-xsfmclic -verify=disabled,both -fsyntax-only
#if defined(CHECK_IR)
// CHECK-LABEL: @foo_stack_swap() #0
@@ -61,38 +65,42 @@ void foo_machine_preemptible(void) {}
// CHECK: "interrupt"="SiFive-CLIC-preemptible-stack-swap"
#else
-__attribute__((interrupt("SiFive-CLIC-stack-swap"))) void foo15(void);
-__attribute__((interrupt("SiFive-CLIC-stack-swap", "SiFive-CLIC-stack-swap"))) void foo15(void);
-__attribute__((interrupt("SiFive-CLIC-stack-swap", "machine"))) void foo15(void);
-__attribute__((interrupt("machine", "SiFive-CLIC-stack-swap"))) void foo15(void);
+__attribute__((interrupt("SiFive-CLIC-stack-swap"))) void foo15(void); // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-stack-swap' requires extension 'XSfmclic'}}
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "SiFive-CLIC-stack-swap"))) void foo15(void); // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-stack-swap' requires extension 'XSfmclic'}}
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "machine"))) void foo15(void); // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-stack-swap' requires extension 'XSfmclic'}}
+__attribute__((interrupt("machine", "SiFive-CLIC-stack-swap"))) void foo15(void); // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-stack-swap' requires extension 'XSfmclic'}}
-__attribute__((interrupt("SiFive-CLIC-preemptible"))) void foo15(void);
-__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-preemptible"))) void foo15(void);
-__attribute__((interrupt("SiFive-CLIC-preemptible", "machine"))) void foo15(void);
-__attribute__((interrupt("machine", "SiFive-CLIC-preemptible"))) void foo15(void);
+__attribute__((interrupt("SiFive-CLIC-preemptible"))) void foo15(void); // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-preemptible' requires extension 'XSfmclic'}}
+__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-preemptible"))) void foo15(void); // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-preemptible' requires extension 'XSfmclic'}}
+__attribute__((interrupt("SiFive-CLIC-preemptible", "machine"))) void foo15(void); // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-preemptible' requires extension 'XSfmclic'}}
+__attribute__((interrupt("machine", "SiFive-CLIC-preemptible"))) void foo15(void); // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-preemptible' requires extension 'XSfmclic'}}
-__attribute__((interrupt("SiFive-CLIC-stack-swap"))) void foo16(void) {}
-__attribute__((interrupt("SiFive-CLIC-stack-swap", "SiFive-CLIC-stack-swap"))) void foo17(void) {}
-__attribute__((interrupt("SiFive-CLIC-stack-swap", "machine"))) void foo18(void) {}
-__attribute__((interrupt("machine", "SiFive-CLIC-stack-swap"))) void foo19(void) {}
+__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-stack-swap"))) void foo16(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-preemptible' requires extension 'XSfmclic'}}
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "SiFive-CLIC-preemptible"))) void foo17(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-stack-swap' requires extension 'XSfmclic'}}
-__attribute__((interrupt("SiFive-CLIC-preemptible"))) void foo20(void) {}
-__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-preemptible"))) void foo21(void) {}
-__attribute__((interrupt("SiFive-CLIC-preemptible", "machine"))) void foo22(void) {}
-__attribute__((interrupt("machine", "SiFive-CLIC-preemptible"))) void foo23(void) {}
+__attribute__((interrupt("machine", "machine", "SiFive-CLIC-preemptible"))) void foo24(void) {} // both-error {{'interrupt' attribute takes no more than 2 arguments}}
-__attribute__((interrupt("machine", "machine", "SiFive-CLIC-preemptible"))) void foo24(void) {} // expected-error {{'interrupt' attribute takes no more than 2 arguments}}
+__attribute__((interrupt("SiFive-CLIC-preemptible", "supervisor"))) void foo27(void) {} // both-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
-__attribute__((interrupt("SiFive-CLIC-preemptible", "supervisor"))) void foo27(void) {} // expected-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
+__attribute__((interrupt("supervisor", "SiFive-CLIC-stack-swap"))) void foo28(void) {} // both-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
-__attribute__((interrupt("supervisor", "SiFive-CLIC-stack-swap"))) void foo28(void) {} // expected-error {{RISC-V 'interrupt' attribute contains invalid combination of interrupt types}}
+__attribute__((interrupt("SiFive-CLIC-stack-swap", 1))) void foo29(void) {} // both-error {{expected string literal as argument of 'interrupt' attribute}}
-__attribute__((interrupt("SiFive-CLIC-stack-swap", 1))) void foo29(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
+__attribute__((interrupt(1, "SiFive-CLIC-stack-swap"))) void foo30(void) {} // both-error {{expected string literal as argument of 'interrupt' attribute}}
-__attribute__((interrupt(1, "SiFive-CLIC-stack-swap"))) void foo30(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
+__attribute__((interrupt("SiFive-CLIC-stack-swap", "foo"))) void foo31(void) {} // both-warning {{'interrupt' attribute argument not supported: "foo"}}
-__attribute__((interrupt("SiFive-CLIC-stack-swap", "foo"))) void foo31(void) {} // expected-warning {{'interrupt' attribute argument not supported: "foo"}}
+__attribute__((interrupt("foo", "SiFive-CLIC-stack-swap"))) void foo32(void) {} // both-warning {{'interrupt' attribute argument not supported: "foo"}}
-__attribute__((interrupt("foo", "SiFive-CLIC-stack-swap"))) void foo32(void) {} // expected-warning {{'interrupt' attribute argument not supported: "foo"}}
+
+__attribute__((target("arch=+xsfmclic"))) __attribute__((interrupt("SiFive-CLIC-preemptible"))) void foo_sfmclic_preemptible(void) {}
+__attribute__((target("arch=+xsfmclic"))) __attribute__((interrupt("SiFive-CLIC-stack-swap"))) void foo_sfmclic_stack_swap(void) {}
+__attribute__((target("arch=+xsfmclic"))) __attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-stack-swap"))) void foo_sfmclic_both(void) {}
+
+// The attribute order is important, the interrupt attribute must come after the
+// target attribute
+__attribute__((interrupt("SiFive-CLIC-preemptible"))) __attribute__((target("arch=+xsfmclic"))) void foo_preemptible_sfmclic(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-preemptible' requires extension 'XSfmclic'}}
+__attribute__((interrupt("SiFive-CLIC-stack-swap"))) __attribute__((target("arch=+xsfmclic"))) void fooc_stack_swap_sfmclic(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-stack-swap' requires extension 'XSfmclic'}}
+__attribute__((interrupt("SiFive-CLIC-preemptible", "SiFive-CLIC-stack-swap"))) __attribute__((target("arch=+xsfmclic"))) void foo_both_sfmclic(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'SiFive-CLIC-preemptible' requires extension 'XSfmclic'}}
#endif
>From ad1b9e0fed547f404612fbc48a0378aaca71c744 Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Fri, 21 Mar 2025 15:36:19 -0700
Subject: [PATCH 04/10] Fix xsf*clic test names
---
llvm/test/MC/RISCV/{rvsfmclic-invalid.s => xsfmclic-invalid.s} | 0
llvm/test/MC/RISCV/{rvsfmclic-valid.s => xsfmclic-valid.s} | 0
llvm/test/MC/RISCV/{rvsfsclic-invalid.s => xsfsclic-invalid.s} | 0
llvm/test/MC/RISCV/{rvsfsclic-valid.s => xsfsclic-valid.s} | 0
4 files changed, 0 insertions(+), 0 deletions(-)
rename llvm/test/MC/RISCV/{rvsfmclic-invalid.s => xsfmclic-invalid.s} (100%)
rename llvm/test/MC/RISCV/{rvsfmclic-valid.s => xsfmclic-valid.s} (100%)
rename llvm/test/MC/RISCV/{rvsfsclic-invalid.s => xsfsclic-invalid.s} (100%)
rename llvm/test/MC/RISCV/{rvsfsclic-valid.s => xsfsclic-valid.s} (100%)
diff --git a/llvm/test/MC/RISCV/rvsfmclic-invalid.s b/llvm/test/MC/RISCV/xsfmclic-invalid.s
similarity index 100%
rename from llvm/test/MC/RISCV/rvsfmclic-invalid.s
rename to llvm/test/MC/RISCV/xsfmclic-invalid.s
diff --git a/llvm/test/MC/RISCV/rvsfmclic-valid.s b/llvm/test/MC/RISCV/xsfmclic-valid.s
similarity index 100%
rename from llvm/test/MC/RISCV/rvsfmclic-valid.s
rename to llvm/test/MC/RISCV/xsfmclic-valid.s
diff --git a/llvm/test/MC/RISCV/rvsfsclic-invalid.s b/llvm/test/MC/RISCV/xsfsclic-invalid.s
similarity index 100%
rename from llvm/test/MC/RISCV/rvsfsclic-invalid.s
rename to llvm/test/MC/RISCV/xsfsclic-invalid.s
diff --git a/llvm/test/MC/RISCV/rvsfsclic-valid.s b/llvm/test/MC/RISCV/xsfsclic-valid.s
similarity index 100%
rename from llvm/test/MC/RISCV/rvsfsclic-valid.s
rename to llvm/test/MC/RISCV/xsfsclic-valid.s
>From e6def5853075ff931499eab51c6d65bd62df5f0b Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Fri, 21 Mar 2025 17:25:34 -0700
Subject: [PATCH 05/10] Remove PrologEpilog Debug Code
---
llvm/lib/CodeGen/PrologEpilogInserter.cpp | 28 -----------------------
1 file changed, 28 deletions(-)
diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index bd60faab0911a..9b852c0fd49cf 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -230,16 +230,10 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
// with stack arguments.
TFI->spillFPBP(MF);
- LLVM_DEBUG(llvm::dbgs() << "Before calculateCallFrameInfo \n");
- LLVM_DEBUG(MF.dump());
-
// Calculate the MaxCallFrameSize value for the function's frame
// information. Also eliminates call frame pseudo instructions.
calculateCallFrameInfo(MF);
- LLVM_DEBUG(llvm::dbgs() << "Before calculateSaveRestoreBlocks \n");
- LLVM_DEBUG(MF.dump());
-
// Determine placement of CSR spill/restore code and prolog/epilog code:
// place all spills in the entry block, all restores in return blocks.
calculateSaveRestoreBlocks(MF);
@@ -249,29 +243,17 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
for (MachineBasicBlock *SaveBlock : SaveBlocks)
stashEntryDbgValues(*SaveBlock, EntryDbgValues);
- LLVM_DEBUG(llvm::dbgs() << "Before spillCalleeSavedRegs \n");
- LLVM_DEBUG(MF.dump());
-
// Handle CSR spilling and restoring, for targets that need it.
if (MF.getTarget().usesPhysRegsForValues())
spillCalleeSavedRegs(MF);
- LLVM_DEBUG(llvm::dbgs() << "Before processFunctionBeforeFrameFinalized \n");
- LLVM_DEBUG(MF.dump());
-
// Allow the target machine to make final modifications to the function
// before the frame layout is finalized.
TFI->processFunctionBeforeFrameFinalized(MF, RS);
- LLVM_DEBUG(llvm::dbgs() << "Before calculateFrameObjectOffsets \n");
- LLVM_DEBUG(MF.dump());
-
// Calculate actual frame offsets for all abstract stack objects...
calculateFrameObjectOffsets(MF);
- LLVM_DEBUG(llvm::dbgs() << "Before insertPrologEpilogCode \n");
- LLVM_DEBUG(MF.dump());
-
// Add prolog and epilog code to the function. This function is required
// to align the stack frame as necessary for any stack variables or
// called functions. Because of this, calculateCalleeSavedRegisters()
@@ -284,17 +266,10 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
for (auto &I : EntryDbgValues)
I.first->insert(I.first->begin(), I.second.begin(), I.second.end());
- LLVM_DEBUG(
- llvm::dbgs() << "Before processFunctionBeforeFrameIndicesReplaced \n");
- LLVM_DEBUG(MF.dump());
-
// Allow the target machine to make final modifications to the function
// before the frame layout is finalized.
TFI->processFunctionBeforeFrameIndicesReplaced(MF, RS);
- LLVM_DEBUG(llvm::dbgs() << "Before frame index resolution \n");
- LLVM_DEBUG(MF.dump());
-
// Replace all MO_FrameIndex operands with physical register references
// and actual offsets.
if (TFI->needsFrameIndexResolution(MF)) {
@@ -309,9 +284,6 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
replaceFrameIndices(MF);
}
- LLVM_DEBUG(llvm::dbgs() << "After frame index resolution \n");
- LLVM_DEBUG(MF.dump());
-
// If register scavenging is needed, as we've enabled doing it as a
// post-pass, scavenge the virtual registers that frame index elimination
// inserted.
>From fffa624a726115897548b673ad31926b4f256160 Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Fri, 21 Mar 2025 17:29:54 -0700
Subject: [PATCH 06/10] Fix Function Name Cases
---
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 50375a37957ad..a13126f37a089 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -268,7 +268,7 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
}
// Insert instruction to swap mscratchsw with sp
-static void emitSifiveCLICStackSwap(MachineFunction &MF, MachineBasicBlock &MBB,
+static void emitSiFiveCLICStackSwap(MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
const DebugLoc &DL) {
auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
@@ -302,7 +302,7 @@ createSiFivePreemptibleInterruptFrameEntries(MachineFunction &MF,
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
+ // `emitSiFiveCLICPreemptibleSaves`. This is in addition to any other stack
// objects we might have for X8 and X9, as they might be saved twice.
for (int I = 0; I < 2; ++I) {
int FI = MFI.CreateStackObject(TRI.getSpillSize(RC), TRI.getSpillAlign(RC),
@@ -311,7 +311,7 @@ createSiFivePreemptibleInterruptFrameEntries(MachineFunction &MF,
}
}
-static void emitSifiveCLICPreemptibleSaves(MachineFunction &MF,
+static void emitSiFiveCLICPreemptibleSaves(MachineFunction &MF,
MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
const DebugLoc &DL) {
@@ -363,7 +363,7 @@ static void emitSifiveCLICPreemptibleSaves(MachineFunction &MF,
.setMIFlag(MachineInstr::FrameSetup);
}
-static void emitSifiveCLICPreemptibleRestores(MachineFunction &MF,
+static void emitSiFiveCLICPreemptibleRestores(MachineFunction &MF,
MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
const DebugLoc &DL) {
@@ -386,7 +386,7 @@ static void emitSifiveCLICPreemptibleRestores(MachineFunction &MF,
// 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`.
+ // value stored in `emitSiFiveCLICPreemptibleSaves`.
BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRW))
.addReg(RISCV::X0)
.addImm(RISCVSysReg::mepc)
@@ -399,7 +399,7 @@ static void emitSifiveCLICPreemptibleRestores(MachineFunction &MF,
.setMIFlag(MachineInstr::FrameSetup);
// X8 and X9 need to be restored to their values on function entry, which we
- // saved onto the stack in `emitSifiveCLICPreemptibleSaves`.
+ // saved onto the stack in `emitSiFiveCLICPreemptibleSaves`.
TII->loadRegFromStackSlot(MBB, MBBI, RISCV::X9,
RVFI->getInterruptCSRFrameIndex(1),
&RISCV::GPRRegClass, STI.getRegisterInfo(),
@@ -1016,7 +1016,7 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
return;
// SiFive CLIC needs to swap `sp` into `mscratchcsw`
- emitSifiveCLICStackSwap(MF, MBB, MBBI, DL);
+ emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL);
// Emit prologue for shadow call stack.
emitSCSPrologue(MF, MBB, MBBI, DL);
@@ -1138,7 +1138,7 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
NeedProbe, ProbeSize, DynAllocation);
// Save SiFive CLIC CSRs into Stack
- emitSifiveCLICPreemptibleSaves(MF, MBB, MBBI, DL);
+ 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
@@ -1456,7 +1456,7 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF,
}
}
- emitSifiveCLICPreemptibleRestores(MF, MBB, MBBI, DL);
+ emitSiFiveCLICPreemptibleRestores(MF, MBB, MBBI, DL);
// Deallocate stack if StackSize isn't a zero yet
if (StackSize != 0)
@@ -1466,7 +1466,7 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF,
emitSCSEpilogue(MF, MBB, MBBI, DL);
//
- emitSifiveCLICStackSwap(MF, MBB, MBBI, DL);
+ emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL);
}
StackOffset
>From c27f1b07f43b5b580845e111c5b7b7e1b2bedbba Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Fri, 21 Mar 2025 17:34:35 -0700
Subject: [PATCH 07/10] Correctly mark Stack Object as for a Spill
---
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 2 +-
.../CodeGen/RISCV/sifive-interrupt-attr.ll | 96 +++++++++----------
2 files changed, 49 insertions(+), 49 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index a13126f37a089..c3667f427ae40 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -306,7 +306,7 @@ createSiFivePreemptibleInterruptFrameEntries(MachineFunction &MF,
// objects we might have for X8 and X9, as they might be saved twice.
for (int I = 0; I < 2; ++I) {
int FI = MFI.CreateStackObject(TRI.getSpillSize(RC), TRI.getSpillAlign(RC),
- false);
+ true);
RVFI.pushInterruptCSRFrameIndex(FI);
}
}
diff --git a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
index 9485827ba179e..a5b8142450f38 100644
--- a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
+++ b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
@@ -78,16 +78,16 @@ define void @preemptible_empty() "interrupt"="SiFive-CLIC-preemptible" {
; RV32: # %bb.0:
; RV32-NEXT: addi sp, sp, -16
; RV32-NEXT: .cfi_def_cfa_offset 16
-; RV32-NEXT: sw s0, 12(sp)
-; RV32-NEXT: sw s1, 8(sp)
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 8(sp)
-; RV32-NEXT: lw s0, 12(sp)
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 16
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: mret
@@ -96,16 +96,16 @@ define void @preemptible_empty() "interrupt"="SiFive-CLIC-preemptible" {
; RV64: # %bb.0:
; RV64-NEXT: addi sp, sp, -16
; RV64-NEXT: .cfi_def_cfa_offset 16
-; RV64-NEXT: sd s0, 8(sp)
-; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp)
-; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 16
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: mret
@@ -118,16 +118,16 @@ define void @both_empty() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV32-NEXT: csrrw sp, mscratchcsw, sp
; RV32-NEXT: addi sp, sp, -16
; RV32-NEXT: .cfi_def_cfa_offset 16
-; RV32-NEXT: sw s0, 12(sp)
-; RV32-NEXT: sw s1, 8(sp)
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 8(sp)
-; RV32-NEXT: lw s0, 12(sp)
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 16
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: csrrw sp, mscratchcsw, sp
@@ -138,16 +138,16 @@ define void @both_empty() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV64-NEXT: csrrw sp, mscratchcsw, sp
; RV64-NEXT: addi sp, sp, -16
; RV64-NEXT: .cfi_def_cfa_offset 16
-; RV64-NEXT: sd s0, 8(sp)
-; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp)
-; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 16
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: csrrw sp, mscratchcsw, sp
@@ -484,8 +484,8 @@ define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
; RV32: # %bb.0:
; RV32-NEXT: addi sp, sp, -80
; RV32-NEXT: .cfi_def_cfa_offset 80
-; RV32-NEXT: sw s0, 12(sp)
-; RV32-NEXT: sw s1, 8(sp)
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
@@ -557,8 +557,8 @@ define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 8(sp)
-; RV32-NEXT: lw s0, 12(sp)
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 80
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: mret
@@ -567,8 +567,8 @@ define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
; RV64: # %bb.0:
; RV64-NEXT: addi sp, sp, -144
; RV64-NEXT: .cfi_def_cfa_offset 144
-; RV64-NEXT: sd s0, 8(sp)
-; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
@@ -640,8 +640,8 @@ define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp)
-; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 144
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: mret
@@ -655,8 +655,8 @@ define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV32-NEXT: csrrw sp, mscratchcsw, sp
; RV32-NEXT: addi sp, sp, -80
; RV32-NEXT: .cfi_def_cfa_offset 80
-; RV32-NEXT: sw s0, 12(sp)
-; RV32-NEXT: sw s1, 8(sp)
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
@@ -728,8 +728,8 @@ define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 8(sp)
-; RV32-NEXT: lw s0, 12(sp)
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 80
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: csrrw sp, mscratchcsw, sp
@@ -740,8 +740,8 @@ define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV64-NEXT: csrrw sp, mscratchcsw, sp
; RV64-NEXT: addi sp, sp, -144
; RV64-NEXT: .cfi_def_cfa_offset 144
-; RV64-NEXT: sd s0, 8(sp)
-; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
@@ -813,8 +813,8 @@ define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp)
-; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 144
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: csrrw sp, mscratchcsw, sp
@@ -930,8 +930,8 @@ define void @preemptible_clobber() "interrupt"="SiFive-CLIC-preemptible" {
; RV32: # %bb.0:
; RV32-NEXT: addi sp, sp, -16
; RV32-NEXT: .cfi_def_cfa_offset 16
-; RV32-NEXT: sw s0, 4(sp)
-; RV32-NEXT: sw s1, 0(sp)
+; RV32-NEXT: sw s0, 4(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 0(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
@@ -948,8 +948,8 @@ define void @preemptible_clobber() "interrupt"="SiFive-CLIC-preemptible" {
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 0(sp)
-; RV32-NEXT: lw s0, 4(sp)
+; RV32-NEXT: lw s1, 0(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 4(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 16
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: mret
@@ -958,8 +958,8 @@ define void @preemptible_clobber() "interrupt"="SiFive-CLIC-preemptible" {
; RV64: # %bb.0:
; RV64-NEXT: addi sp, sp, -32
; RV64-NEXT: .cfi_def_cfa_offset 32
-; RV64-NEXT: sd s0, 8(sp)
-; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
@@ -976,8 +976,8 @@ define void @preemptible_clobber() "interrupt"="SiFive-CLIC-preemptible" {
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp)
-; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 32
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: mret
@@ -991,8 +991,8 @@ define void @both_clobber() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV32-NEXT: csrrw sp, mscratchcsw, sp
; RV32-NEXT: addi sp, sp, -16
; RV32-NEXT: .cfi_def_cfa_offset 16
-; RV32-NEXT: sw s0, 4(sp)
-; RV32-NEXT: sw s1, 0(sp)
+; RV32-NEXT: sw s0, 4(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 0(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
@@ -1009,8 +1009,8 @@ define void @both_clobber() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 0(sp)
-; RV32-NEXT: lw s0, 4(sp)
+; RV32-NEXT: lw s1, 0(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 4(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 16
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: csrrw sp, mscratchcsw, sp
@@ -1021,8 +1021,8 @@ define void @both_clobber() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV64-NEXT: csrrw sp, mscratchcsw, sp
; RV64-NEXT: addi sp, sp, -32
; RV64-NEXT: .cfi_def_cfa_offset 32
-; RV64-NEXT: sd s0, 8(sp)
-; RV64-NEXT: sd s1, 0(sp)
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
@@ -1039,8 +1039,8 @@ define void @both_clobber() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp)
-; RV64-NEXT: ld s0, 8(sp)
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 32
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: csrrw sp, mscratchcsw, sp
>From ac40958375558073fd23d4165374b1e88e0d91c6 Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Fri, 21 Mar 2025 18:08:42 -0700
Subject: [PATCH 08/10] Include Interrupt Callee-Saves in Min/MaxCSRFrameIndex
---
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 13 +-
.../CodeGen/RISCV/sifive-interrupt-attr.ll | 496 +++++++++---------
2 files changed, 260 insertions(+), 249 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index c3667f427ae40..0575129d13221 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -2021,11 +2021,22 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
MachineFunction &MF, const TargetRegisterInfo *TRI,
std::vector<CalleeSavedInfo> &CSI, unsigned &MinCSFrameIndex,
unsigned &MaxCSFrameIndex) const {
+ auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+ // Preemptible Interrupts have two additional Callee-save Frame Indexes,
+ // not tracked by `CSI`.
+ if (RVFI->isSiFivePreemptibleInterrupt(MF)) {
+ for (int I = 0; I < 2; ++I) {
+ int FI = RVFI->getInterruptCSRFrameIndex(I);
+ MinCSFrameIndex = std::min<unsigned>(MinCSFrameIndex, FI);
+ MaxCSFrameIndex = std::max<unsigned>(MaxCSFrameIndex, FI);
+ }
+ }
+
// Early exit if no callee saved registers are modified!
if (CSI.empty())
return true;
- auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
if (RVFI->useQCIInterrupt(MF)) {
RVFI->setQCIInterruptStackSize(QCIInterruptPushAmount);
} else if (RVFI->isPushable(MF)) {
diff --git a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
index a5b8142450f38..d2732bcd8bffd 100644
--- a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
+++ b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
@@ -484,60 +484,60 @@ define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
; RV32: # %bb.0:
; RV32-NEXT: addi sp, sp, -80
; RV32-NEXT: .cfi_def_cfa_offset 80
-; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s0, 76(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 72(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
-; RV32-NEXT: sw ra, 76(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t0, 72(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t1, 68(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t2, 64(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a0, 60(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a1, 56(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a2, 52(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a3, 48(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a4, 44(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a5, 40(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a6, 36(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a7, 32(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t3, 28(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t4, 24(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t5, 20(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t6, 16(sp) # 4-byte Folded Spill
-; RV32-NEXT: .cfi_offset ra, -4
-; RV32-NEXT: .cfi_offset t0, -8
-; RV32-NEXT: .cfi_offset t1, -12
-; RV32-NEXT: .cfi_offset t2, -16
-; RV32-NEXT: .cfi_offset a0, -20
-; RV32-NEXT: .cfi_offset a1, -24
-; RV32-NEXT: .cfi_offset a2, -28
-; RV32-NEXT: .cfi_offset a3, -32
-; RV32-NEXT: .cfi_offset a4, -36
-; RV32-NEXT: .cfi_offset a5, -40
-; RV32-NEXT: .cfi_offset a6, -44
-; RV32-NEXT: .cfi_offset a7, -48
-; RV32-NEXT: .cfi_offset t3, -52
-; RV32-NEXT: .cfi_offset t4, -56
-; RV32-NEXT: .cfi_offset t5, -60
-; RV32-NEXT: .cfi_offset t6, -64
+; RV32-NEXT: sw ra, 68(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t0, 64(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t1, 60(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t2, 56(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a0, 52(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a1, 48(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a2, 44(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a3, 40(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a4, 36(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a5, 32(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a6, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a7, 24(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t3, 20(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t4, 16(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t5, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t6, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset ra, -12
+; RV32-NEXT: .cfi_offset t0, -16
+; RV32-NEXT: .cfi_offset t1, -20
+; RV32-NEXT: .cfi_offset t2, -24
+; RV32-NEXT: .cfi_offset a0, -28
+; RV32-NEXT: .cfi_offset a1, -32
+; RV32-NEXT: .cfi_offset a2, -36
+; RV32-NEXT: .cfi_offset a3, -40
+; RV32-NEXT: .cfi_offset a4, -44
+; RV32-NEXT: .cfi_offset a5, -48
+; RV32-NEXT: .cfi_offset a6, -52
+; RV32-NEXT: .cfi_offset a7, -56
+; RV32-NEXT: .cfi_offset t3, -60
+; RV32-NEXT: .cfi_offset t4, -64
+; RV32-NEXT: .cfi_offset t5, -68
+; RV32-NEXT: .cfi_offset t6, -72
; RV32-NEXT: call callee
-; RV32-NEXT: lw ra, 76(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t0, 72(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t1, 68(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t2, 64(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a0, 60(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a1, 56(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a2, 52(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a3, 48(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a4, 44(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a5, 40(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a6, 36(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a7, 32(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t3, 28(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t4, 24(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t5, 20(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t6, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw ra, 68(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t0, 64(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t1, 60(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t2, 56(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a0, 52(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a1, 48(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a2, 44(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a3, 40(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a4, 36(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a5, 32(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a6, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a7, 24(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t3, 20(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t4, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t5, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t6, 8(sp) # 4-byte Folded Reload
; RV32-NEXT: .cfi_restore ra
; RV32-NEXT: .cfi_restore t0
; RV32-NEXT: .cfi_restore t1
@@ -557,8 +557,8 @@ define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 72(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 76(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 80
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: mret
@@ -567,60 +567,60 @@ define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
; RV64: # %bb.0:
; RV64-NEXT: addi sp, sp, -144
; RV64-NEXT: .cfi_def_cfa_offset 144
-; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s0, 136(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 128(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
-; RV64-NEXT: sd ra, 136(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t0, 128(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t1, 120(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t2, 112(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a0, 104(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a1, 96(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a2, 88(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a3, 80(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a4, 72(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a5, 64(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a6, 56(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a7, 48(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t3, 40(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t4, 32(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t5, 24(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t6, 16(sp) # 8-byte Folded Spill
-; RV64-NEXT: .cfi_offset ra, -8
-; RV64-NEXT: .cfi_offset t0, -16
-; RV64-NEXT: .cfi_offset t1, -24
-; RV64-NEXT: .cfi_offset t2, -32
-; RV64-NEXT: .cfi_offset a0, -40
-; RV64-NEXT: .cfi_offset a1, -48
-; RV64-NEXT: .cfi_offset a2, -56
-; RV64-NEXT: .cfi_offset a3, -64
-; RV64-NEXT: .cfi_offset a4, -72
-; RV64-NEXT: .cfi_offset a5, -80
-; RV64-NEXT: .cfi_offset a6, -88
-; RV64-NEXT: .cfi_offset a7, -96
-; RV64-NEXT: .cfi_offset t3, -104
-; RV64-NEXT: .cfi_offset t4, -112
-; RV64-NEXT: .cfi_offset t5, -120
-; RV64-NEXT: .cfi_offset t6, -128
+; RV64-NEXT: sd ra, 120(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t0, 112(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t1, 104(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t2, 96(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a0, 88(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a1, 80(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a2, 72(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a3, 64(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a4, 56(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a5, 48(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a6, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a7, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t3, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t4, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t5, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t6, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset ra, -24
+; RV64-NEXT: .cfi_offset t0, -32
+; RV64-NEXT: .cfi_offset t1, -40
+; RV64-NEXT: .cfi_offset t2, -48
+; RV64-NEXT: .cfi_offset a0, -56
+; RV64-NEXT: .cfi_offset a1, -64
+; RV64-NEXT: .cfi_offset a2, -72
+; RV64-NEXT: .cfi_offset a3, -80
+; RV64-NEXT: .cfi_offset a4, -88
+; RV64-NEXT: .cfi_offset a5, -96
+; RV64-NEXT: .cfi_offset a6, -104
+; RV64-NEXT: .cfi_offset a7, -112
+; RV64-NEXT: .cfi_offset t3, -120
+; RV64-NEXT: .cfi_offset t4, -128
+; RV64-NEXT: .cfi_offset t5, -136
+; RV64-NEXT: .cfi_offset t6, -144
; RV64-NEXT: call callee
-; RV64-NEXT: ld ra, 136(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t0, 128(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t1, 120(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t2, 112(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a0, 104(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a1, 96(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a2, 88(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a3, 80(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a4, 72(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a5, 64(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a6, 56(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a7, 48(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t3, 40(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t4, 32(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t5, 24(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t6, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld ra, 120(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t0, 112(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t1, 104(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t2, 96(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a0, 88(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a1, 80(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a2, 72(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a3, 64(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a4, 56(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a5, 48(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a6, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a7, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t3, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t4, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t5, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t6, 0(sp) # 8-byte Folded Reload
; RV64-NEXT: .cfi_restore ra
; RV64-NEXT: .cfi_restore t0
; RV64-NEXT: .cfi_restore t1
@@ -640,8 +640,8 @@ define void @preeemptible_caller() "interrupt"="SiFive-CLIC-preemptible" {
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 128(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 136(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 144
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: mret
@@ -655,60 +655,60 @@ define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV32-NEXT: csrrw sp, mscratchcsw, sp
; RV32-NEXT: addi sp, sp, -80
; RV32-NEXT: .cfi_def_cfa_offset 80
-; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s0, 76(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 72(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
-; RV32-NEXT: sw ra, 76(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t0, 72(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t1, 68(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t2, 64(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a0, 60(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a1, 56(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a2, 52(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a3, 48(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a4, 44(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a5, 40(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a6, 36(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw a7, 32(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t3, 28(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t4, 24(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t5, 20(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw t6, 16(sp) # 4-byte Folded Spill
-; RV32-NEXT: .cfi_offset ra, -4
-; RV32-NEXT: .cfi_offset t0, -8
-; RV32-NEXT: .cfi_offset t1, -12
-; RV32-NEXT: .cfi_offset t2, -16
-; RV32-NEXT: .cfi_offset a0, -20
-; RV32-NEXT: .cfi_offset a1, -24
-; RV32-NEXT: .cfi_offset a2, -28
-; RV32-NEXT: .cfi_offset a3, -32
-; RV32-NEXT: .cfi_offset a4, -36
-; RV32-NEXT: .cfi_offset a5, -40
-; RV32-NEXT: .cfi_offset a6, -44
-; RV32-NEXT: .cfi_offset a7, -48
-; RV32-NEXT: .cfi_offset t3, -52
-; RV32-NEXT: .cfi_offset t4, -56
-; RV32-NEXT: .cfi_offset t5, -60
-; RV32-NEXT: .cfi_offset t6, -64
+; RV32-NEXT: sw ra, 68(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t0, 64(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t1, 60(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t2, 56(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a0, 52(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a1, 48(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a2, 44(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a3, 40(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a4, 36(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a5, 32(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a6, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw a7, 24(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t3, 20(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t4, 16(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t5, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw t6, 8(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset ra, -12
+; RV32-NEXT: .cfi_offset t0, -16
+; RV32-NEXT: .cfi_offset t1, -20
+; RV32-NEXT: .cfi_offset t2, -24
+; RV32-NEXT: .cfi_offset a0, -28
+; RV32-NEXT: .cfi_offset a1, -32
+; RV32-NEXT: .cfi_offset a2, -36
+; RV32-NEXT: .cfi_offset a3, -40
+; RV32-NEXT: .cfi_offset a4, -44
+; RV32-NEXT: .cfi_offset a5, -48
+; RV32-NEXT: .cfi_offset a6, -52
+; RV32-NEXT: .cfi_offset a7, -56
+; RV32-NEXT: .cfi_offset t3, -60
+; RV32-NEXT: .cfi_offset t4, -64
+; RV32-NEXT: .cfi_offset t5, -68
+; RV32-NEXT: .cfi_offset t6, -72
; RV32-NEXT: call callee
-; RV32-NEXT: lw ra, 76(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t0, 72(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t1, 68(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t2, 64(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a0, 60(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a1, 56(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a2, 52(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a3, 48(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a4, 44(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a5, 40(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a6, 36(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw a7, 32(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t3, 28(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t4, 24(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t5, 20(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw t6, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw ra, 68(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t0, 64(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t1, 60(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t2, 56(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a0, 52(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a1, 48(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a2, 44(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a3, 40(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a4, 36(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a5, 32(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a6, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw a7, 24(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t3, 20(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t4, 16(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t5, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw t6, 8(sp) # 4-byte Folded Reload
; RV32-NEXT: .cfi_restore ra
; RV32-NEXT: .cfi_restore t0
; RV32-NEXT: .cfi_restore t1
@@ -728,8 +728,8 @@ define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 72(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 76(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 80
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: csrrw sp, mscratchcsw, sp
@@ -740,60 +740,60 @@ define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV64-NEXT: csrrw sp, mscratchcsw, sp
; RV64-NEXT: addi sp, sp, -144
; RV64-NEXT: .cfi_def_cfa_offset 144
-; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s0, 136(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 128(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
-; RV64-NEXT: sd ra, 136(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t0, 128(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t1, 120(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t2, 112(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a0, 104(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a1, 96(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a2, 88(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a3, 80(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a4, 72(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a5, 64(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a6, 56(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd a7, 48(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t3, 40(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t4, 32(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t5, 24(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd t6, 16(sp) # 8-byte Folded Spill
-; RV64-NEXT: .cfi_offset ra, -8
-; RV64-NEXT: .cfi_offset t0, -16
-; RV64-NEXT: .cfi_offset t1, -24
-; RV64-NEXT: .cfi_offset t2, -32
-; RV64-NEXT: .cfi_offset a0, -40
-; RV64-NEXT: .cfi_offset a1, -48
-; RV64-NEXT: .cfi_offset a2, -56
-; RV64-NEXT: .cfi_offset a3, -64
-; RV64-NEXT: .cfi_offset a4, -72
-; RV64-NEXT: .cfi_offset a5, -80
-; RV64-NEXT: .cfi_offset a6, -88
-; RV64-NEXT: .cfi_offset a7, -96
-; RV64-NEXT: .cfi_offset t3, -104
-; RV64-NEXT: .cfi_offset t4, -112
-; RV64-NEXT: .cfi_offset t5, -120
-; RV64-NEXT: .cfi_offset t6, -128
+; RV64-NEXT: sd ra, 120(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t0, 112(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t1, 104(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t2, 96(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a0, 88(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a1, 80(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a2, 72(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a3, 64(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a4, 56(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a5, 48(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a6, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd a7, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t3, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t4, 16(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t5, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd t6, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset ra, -24
+; RV64-NEXT: .cfi_offset t0, -32
+; RV64-NEXT: .cfi_offset t1, -40
+; RV64-NEXT: .cfi_offset t2, -48
+; RV64-NEXT: .cfi_offset a0, -56
+; RV64-NEXT: .cfi_offset a1, -64
+; RV64-NEXT: .cfi_offset a2, -72
+; RV64-NEXT: .cfi_offset a3, -80
+; RV64-NEXT: .cfi_offset a4, -88
+; RV64-NEXT: .cfi_offset a5, -96
+; RV64-NEXT: .cfi_offset a6, -104
+; RV64-NEXT: .cfi_offset a7, -112
+; RV64-NEXT: .cfi_offset t3, -120
+; RV64-NEXT: .cfi_offset t4, -128
+; RV64-NEXT: .cfi_offset t5, -136
+; RV64-NEXT: .cfi_offset t6, -144
; RV64-NEXT: call callee
-; RV64-NEXT: ld ra, 136(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t0, 128(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t1, 120(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t2, 112(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a0, 104(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a1, 96(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a2, 88(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a3, 80(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a4, 72(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a5, 64(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a6, 56(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld a7, 48(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t3, 40(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t4, 32(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t5, 24(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld t6, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld ra, 120(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t0, 112(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t1, 104(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t2, 96(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a0, 88(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a1, 80(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a2, 72(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a3, 64(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a4, 56(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a5, 48(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a6, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld a7, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t3, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t4, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t5, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld t6, 0(sp) # 8-byte Folded Reload
; RV64-NEXT: .cfi_restore ra
; RV64-NEXT: .cfi_restore t0
; RV64-NEXT: .cfi_restore t1
@@ -813,8 +813,8 @@ define void @both_caller() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 128(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 136(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 144
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: csrrw sp, mscratchcsw, sp
@@ -930,26 +930,26 @@ define void @preemptible_clobber() "interrupt"="SiFive-CLIC-preemptible" {
; RV32: # %bb.0:
; RV32-NEXT: addi sp, sp, -16
; RV32-NEXT: .cfi_def_cfa_offset 16
-; RV32-NEXT: sw s0, 4(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw s1, 0(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
-; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
-; RV32-NEXT: .cfi_offset s0, -4
-; RV32-NEXT: .cfi_offset s1, -8
+; RV32-NEXT: sw s0, 4(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 0(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset s0, -12
+; RV32-NEXT: .cfi_offset s1, -16
; RV32-NEXT: #APP
; RV32-NEXT: #NO_APP
-; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 4(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 0(sp) # 4-byte Folded Reload
; RV32-NEXT: .cfi_restore s0
; RV32-NEXT: .cfi_restore s1
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 0(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw s0, 4(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 16
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: mret
@@ -958,26 +958,26 @@ define void @preemptible_clobber() "interrupt"="SiFive-CLIC-preemptible" {
; RV64: # %bb.0:
; RV64-NEXT: addi sp, sp, -32
; RV64-NEXT: .cfi_def_cfa_offset 32
-; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s0, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 16(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
-; RV64-NEXT: sd s0, 24(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd s1, 16(sp) # 8-byte Folded Spill
-; RV64-NEXT: .cfi_offset s0, -8
-; RV64-NEXT: .cfi_offset s1, -16
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset s0, -24
+; RV64-NEXT: .cfi_offset s1, -32
; RV64-NEXT: #APP
; RV64-NEXT: #NO_APP
-; RV64-NEXT: ld s0, 24(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld s1, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
; RV64-NEXT: .cfi_restore s0
; RV64-NEXT: .cfi_restore s1
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 24(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 32
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: mret
@@ -991,26 +991,26 @@ define void @both_clobber() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV32-NEXT: csrrw sp, mscratchcsw, sp
; RV32-NEXT: addi sp, sp, -16
; RV32-NEXT: .cfi_def_cfa_offset 16
-; RV32-NEXT: sw s0, 4(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw s1, 0(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
; RV32-NEXT: csrr s0, mcause
; RV32-NEXT: csrr s1, mepc
; RV32-NEXT: csrsi mstatus, 8
-; RV32-NEXT: sw s0, 12(sp) # 4-byte Folded Spill
-; RV32-NEXT: sw s1, 8(sp) # 4-byte Folded Spill
-; RV32-NEXT: .cfi_offset s0, -4
-; RV32-NEXT: .cfi_offset s1, -8
+; RV32-NEXT: sw s0, 4(sp) # 4-byte Folded Spill
+; RV32-NEXT: sw s1, 0(sp) # 4-byte Folded Spill
+; RV32-NEXT: .cfi_offset s0, -12
+; RV32-NEXT: .cfi_offset s1, -16
; RV32-NEXT: #APP
; RV32-NEXT: #NO_APP
-; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 4(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 0(sp) # 4-byte Folded Reload
; RV32-NEXT: .cfi_restore s0
; RV32-NEXT: .cfi_restore s1
; RV32-NEXT: csrci mstatus, 8
; RV32-NEXT: csrw mepc, s1
; RV32-NEXT: csrw mcause, s0
-; RV32-NEXT: lw s1, 0(sp) # 4-byte Folded Reload
-; RV32-NEXT: lw s0, 4(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s1, 8(sp) # 4-byte Folded Reload
+; RV32-NEXT: lw s0, 12(sp) # 4-byte Folded Reload
; RV32-NEXT: addi sp, sp, 16
; RV32-NEXT: .cfi_def_cfa_offset 0
; RV32-NEXT: csrrw sp, mscratchcsw, sp
@@ -1021,26 +1021,26 @@ define void @both_clobber() "interrupt"="SiFive-CLIC-preemptible-stack-swap" {
; RV64-NEXT: csrrw sp, mscratchcsw, sp
; RV64-NEXT: addi sp, sp, -32
; RV64-NEXT: .cfi_def_cfa_offset 32
-; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s0, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 16(sp) # 8-byte Folded Spill
; RV64-NEXT: csrr s0, mcause
; RV64-NEXT: csrr s1, mepc
; RV64-NEXT: csrsi mstatus, 8
-; RV64-NEXT: sd s0, 24(sp) # 8-byte Folded Spill
-; RV64-NEXT: sd s1, 16(sp) # 8-byte Folded Spill
-; RV64-NEXT: .cfi_offset s0, -8
-; RV64-NEXT: .cfi_offset s1, -16
+; RV64-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
+; RV64-NEXT: .cfi_offset s0, -24
+; RV64-NEXT: .cfi_offset s1, -32
; RV64-NEXT: #APP
; RV64-NEXT: #NO_APP
-; RV64-NEXT: ld s0, 24(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld s1, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
; RV64-NEXT: .cfi_restore s0
; RV64-NEXT: .cfi_restore s1
; RV64-NEXT: csrci mstatus, 8
; RV64-NEXT: csrw mepc, s1
; RV64-NEXT: csrw mcause, s0
-; RV64-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
-; RV64-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s1, 16(sp) # 8-byte Folded Reload
+; RV64-NEXT: ld s0, 24(sp) # 8-byte Folded Reload
; RV64-NEXT: addi sp, sp, 32
; RV64-NEXT: .cfi_def_cfa_offset 0
; RV64-NEXT: csrrw sp, mscratchcsw, sp
>From 18206fe8b93a7fd0a56610e52804e97c161038ce Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Mon, 24 Mar 2025 10:29:28 -0700
Subject: [PATCH 09/10] Release Notes
---
clang/docs/ReleaseNotes.rst | 6 ++++++
llvm/docs/ReleaseNotes.md | 4 ++++
2 files changed, 10 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9b0680c68b83a..6911bc3d0c8de 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -414,6 +414,12 @@ RISC-V Support
^^^^^^^^^^^^^^
- Add support for `-mtune=generic-ooo` (a generic out-of-order model).
+- Adds support for `__attribute__((interrupt("SiFive-CLIC-preemptible")))` and
+ `__attribute__((interrupt("SiFive-CLIC-stack-swap")))`. The former
+ automatically saves some interrupt CSRs before re-enabling interrupts in the
+ function prolog, the latter swaps `sp` with the value in a CSR before it is
+ used or modified. These two can also be combined, and can be combined with
+ `interrupt("machine")`.
CUDA/HIP Language Changes
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 30d66b4a77a71..8d94e4b469515 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -132,6 +132,10 @@ Changes to the RISC-V Backend
extension.
* Adds assembler support for the 'Zclsd` (Compressed Load/Store Pair Instructions)
extension.
+* Adds experimental assembler support for SiFive CLIC CSRs, under the names
+ `Zsfmclic` for the M-mode registers and `Zsfsclic` for the S-mode registers.
+* Adds Support for SiFive CLIC interrupt attributes, which automate writing CLIC
+ interrupt handlers without using inline assembly.
Changes to the WebAssembly Backend
----------------------------------
>From b3e4b1c1173b5d1750fbee79f8b12b7aebf1a081 Mon Sep 17 00:00:00 2001
From: Sam Elliott <quic_aelliott at quicinc.com>
Date: Wed, 26 Mar 2025 16:40:19 -0700
Subject: [PATCH 10/10] Fix Features test
---
llvm/test/CodeGen/RISCV/features-info.ll | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/test/CodeGen/RISCV/features-info.ll b/llvm/test/CodeGen/RISCV/features-info.ll
index 5d0b7e0e256e3..063d87feab385 100644
--- a/llvm/test/CodeGen/RISCV/features-info.ll
+++ b/llvm/test/CodeGen/RISCV/features-info.ll
@@ -41,6 +41,8 @@
; CHECK-NEXT: experimental-xqcisync - 'Xqcisync' (Qualcomm uC Sync Delay Extension).
; CHECK-NEXT: experimental-xrivosvisni - 'XRivosVisni' (Rivos Vector Integer Small New).
; CHECK-NEXT: experimental-xrivosvizip - 'XRivosVizip' (Rivos Vector Register Zips).
+; CHECK-NEXT: experimental-xsfmclic - 'XSfmclic' (SiFive CLIC Machine-mode CSRs).
+; CHECK-NEXT: experimental-xsfsclic - 'XSfsclic' (SiFive CLIC Supervisor-mode CSRs).
; CHECK-NEXT: experimental-zalasr - 'Zalasr' (Load-Acquire and Store-Release Instructions).
; CHECK-NEXT: experimental-zicfilp - 'Zicfilp' (Landing pad).
; CHECK-NEXT: experimental-zicfiss - 'Zicfiss' (Shadow stack).
More information about the llvm-commits
mailing list