Author: Sam Elliott Date: 2025-04-25T17:12:27-07:00 New Revision: cfc5baf6e6fc083fdbb584534a3fb8ea289752d2
URL: https://github.com/llvm/llvm-project/commit/cfc5baf6e6fc083fdbb584534a3fb8ea289752d2 DIFF: https://github.com/llvm/llvm-project/commit/cfc5baf6e6fc083fdbb584534a3fb8ea289752d2.diff LOG: [RISCV] SiFive CLIC Support (#132481) 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 `sf.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. This Change also adds support for the following two experimental extensions, which only contain CSRs: - XSfsclic - for SiFive's CLIC Supervisor-Mode CSRs - XSfmclic - for SiFive's CLIC Machine-Mode CSRs The latter is needed for interrupt support. The CFI information for this implementation is not correct, but I'd prefer to correct this in a follow-up. While it's unlikely anyone wants to unwind through a handler, the CFI information is also used by debuggers so it would be good to get it right. Co-authored-by: Ana Pazos <apa...@quicinc.com> Added: clang/test/Sema/riscv-interrupt-attr-sifive.c llvm/test/CodeGen/RISCV/sifive-interrupt-attr-err.ll llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll llvm/test/MC/RISCV/xsfmclic-invalid.s llvm/test/MC/RISCV/xsfmclic-valid.s llvm/test/MC/RISCV/xsfsclic-invalid.s llvm/test/MC/RISCV/xsfsclic-valid.s Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/CodeGen/Targets/RISCV.cpp clang/lib/Sema/SemaRISCV.cpp clang/test/Driver/print-supported-extensions-riscv.c clang/test/Sema/riscv-interrupt-attr-qci.c clang/test/Sema/riscv-interrupt-attr.c llvm/docs/ReleaseNotes.md llvm/lib/Target/RISCV/RISCVFeatures.td llvm/lib/Target/RISCV/RISCVFrameLowering.cpp llvm/lib/Target/RISCV/RISCVISelLowering.cpp llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h llvm/lib/Target/RISCV/RISCVSystemOperands.td llvm/test/CodeGen/RISCV/features-info.ll llvm/unittests/TargetParser/RISCVISAInfoTest.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6ecb97825ab8d..3724c8cbc70fe 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -634,6 +634,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")`. - Adds support for `__attribute__((interrupt("qci-nest")))` and `__attribute__((interrupt("qci-nonest")))`. These use instructions from diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index d48aed5b73cf5..dcdcff8c46fe2 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2252,10 +2252,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 98468034e71a8..6e2dbe5655189 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2900,8 +2900,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 @@ -2912,6 +2913,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 diff erent 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. @@ -2921,6 +2931,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 7090cbe7acbe6..4c96142e28134 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12763,7 +12763,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 diff erent 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 b9f843b1920a1..f0beedada7306 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/Driver/print-supported-extensions-riscv.c b/clang/test/Driver/print-supported-extensions-riscv.c index 6fb5825f83e1f..3c5a8c3ee0411 100644 --- a/clang/test/Driver/print-supported-extensions-riscv.c +++ b/clang/test/Driver/print-supported-extensions-riscv.c @@ -219,6 +219,8 @@ // CHECK-NEXT: xqcisync 0.2 'Xqcisync' (Qualcomm uC Sync Delay 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/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..9b41e38664d8d --- /dev/null +++ b/clang/test/Sema/riscv-interrupt-attr-sifive.c @@ -0,0 +1,106 @@ +// 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 +// 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); // 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); // 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-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("machine", "machine", "SiFive-CLIC-preemptible"))) void foo24(void) {} // both-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("supervisor", "SiFive-CLIC-stack-swap"))) void foo28(void) {} // both-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(1, "SiFive-CLIC-stack-swap"))) void foo30(void) {} // both-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("foo", "SiFive-CLIC-stack-swap"))) void foo32(void) {} // both-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 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/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index 6fb206e4df188..fdab02eee9123 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -167,11 +167,14 @@ Changes to the RISC-V Backend * Adds assembler support for ``.option exact``, which disables automatic compression, and branch and linker relaxation. This can be disabled with ``.option noexact``, which is also the default. -<<<<<<< HEAD * `-mcpu=xiangshan-kunminghu` was added. * `-mcpu=andes-n45` and `-mcpu=andes-nx45` were added. * `-mcpu=andes-a45` and `-mcpu=andes-ax45` were added. * Adds support for the 'Ziccamoc` (Main Memory Supports Atomics in Zacas) extension, which was introduced as an optional extension of the RISC-V Profiles specification. +* 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 ---------------------------------- diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td index 0ed8cd5aa6de2..b3bb1ad3d25ca 100644 --- a/llvm/lib/Target/RISCV/RISCVFeatures.td +++ b/llvm/lib/Target/RISCV/RISCVFeatures.td @@ -1257,6 +1257,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/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp index f09e3cb20a665..8d8e4bf6358f3 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" @@ -27,6 +28,8 @@ #include <algorithm> +#define DEBUG_TYPE "riscv-frame" + using namespace llvm; static Align getABIStackAlignment(RISCVABI::ABI ABI) { @@ -200,6 +203,149 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB, CFIInstBuilder(MBB, MI, MachineInstr::FrameDestroy).buildRestore(SCSPReg); } +// 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::sf_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), + true); + 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::mepc) + .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 @@ -762,6 +908,9 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, if (MF.getFunction().getCallingConv() == CallingConv::GHC) return; + // SiFive CLIC needs to swap `sp` into `sf.mscratchcsw` + emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL); + // Emit prologue for shadow call stack. emitSCSPrologue(MF, MBB, MBBI, DL); @@ -871,6 +1020,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 @@ -1159,12 +1311,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); + + // SiFive CLIC needs to swap `sf.mscratchcsw` into `sp` + emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL); } StackOffset @@ -1357,6 +1514,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> @@ -1716,11 +1876,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/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 4bcefae8aed03..c72a016c5333b 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -21423,15 +21423,28 @@ 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", + /*gen_crash_diag=*/false); + + const TargetFrameLowering *TFI = Subtarget.getFrameLowering(); + if (Kind.starts_with("SiFive-CLIC-preemptible") && TFI->hasFP(MF)) + report_fatal_error("'SiFive-CLIC-preemptible' interrupt kinds cannot " + "have a frame pointer", + /*gen_crash_diag=*/false); } 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/lib/Target/RISCV/RISCVSystemOperands.td b/llvm/lib/Target/RISCV/RISCVSystemOperands.td index 79ec8134733e8..280a2a446d28a 100644 --- a/llvm/lib/Target/RISCV/RISCVSystemOperands.td +++ b/llvm/lib/Target/RISCV/RISCVSystemOperands.td @@ -496,6 +496,24 @@ def : SysReg<"minstretcfgh", 0x722>; // Vendor CSRs //===----------------------------------------------- +// XSfmclic +let FeaturesRequired = [{ {RISCV::FeatureVendorXSfmclic} }] in { +def : SysReg<"sf.mtvt", 0x307>; +def : SysReg<"sf.mnxti", 0x345>; +def : SysReg<"sf.mintstatus", 0x346>; +def : SysReg<"sf.mscratchcsw", 0x348>; +def : SysReg<"sf.mscratchcswl", 0x349>; +} + +// XSfsclic +let FeaturesRequired = [{ {RISCV::FeatureVendorXSfsclic} }] in { +def : SysReg<"sf.stvt", 0x107>; +def : SysReg<"sf.snxti", 0x145>; +def : SysReg<"sf.sintstatus", 0x146>; +def : SysReg<"sf.sscratchcsw", 0x148>; +def : SysReg<"sf.sscratchcswl", 0x149>; +} + // Xqciint let FeaturesRequired = [{ {RISCV::FeatureVendorXqciint} }], isRV32Only = 1 in { def : SysReg<"qc.mmcr", 0x7C0>; diff --git a/llvm/test/CodeGen/RISCV/features-info.ll b/llvm/test/CodeGen/RISCV/features-info.ll index 75ee7d1f324c4..1e5af28fe6ade 100644 --- a/llvm/test/CodeGen/RISCV/features-info.ll +++ b/llvm/test/CodeGen/RISCV/features-info.ll @@ -42,6 +42,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). diff --git a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr-err.ll b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr-err.ll new file mode 100644 index 0000000000000..ccc11b74f78e7 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr-err.ll @@ -0,0 +1,12 @@ +; RUN: not llc -mtriple riscv32-unknown-elf -mattr=+experimental-xsfmclic -o - %s 2>&1 \ +; RUN: | FileCheck %s +; RUN: not llc -mtriple riscv64-unknown-elf -mattr=+experimental-xsfmclic -o - %s 2>&1 \ +; RUN: | FileCheck %s + +;; Test that these report fatal errors. + +; CHECK: LLVM ERROR: 'SiFive-CLIC-preemptible' interrupt kinds cannot have a frame pointer + +define void @preemptible() "interrupt"="SiFive-CLIC-preemptible" "frame-pointer"="all" { + ret void +} diff --git a/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll b/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll new file mode 100644 index 0000000000000..fe1b3f977a781 --- /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 \ +; RUN: | FileCheck %s --check-prefix=RV32 +; RUN: llc -mtriple riscv64-unknown-elf -mattr=+experimental-xsfmclic -o - %s \ +; RUN: | FileCheck %s --check-prefix=RV64 + +; Test Handling of the SiFive-CLIC interrupt attributes. +; +; "stack-swap" means that sp should be swapped into `sf.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, sf.mscratchcsw, sp +; RV32-NEXT: csrrw sp, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: stack_swap_empty: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.mscratchcsw, sp +; RV64-NEXT: csrrw sp, sf.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, sf.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, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: stack_swap_empty_fp: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.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, sf.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) # 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) # 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 +; +; 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) # 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) # 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 + ret void +} + +define void @both_empty() "interrupt"="SiFive-CLIC-preemptible-stack-swap" { +; RV32-LABEL: both_empty: +; RV32: # %bb.0: +; RV32-NEXT: csrrw sp, sf.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: 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) # 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, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: both_empty: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.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: 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) # 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, sf.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, sf.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, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: stack_swap_caller: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.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, sf.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, sf.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, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: stack_swap_caller_fp: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.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, sf.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, 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, 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, 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 +; 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, 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 +; +; RV64-LABEL: preeemptible_caller: +; RV64: # %bb.0: +; RV64-NEXT: addi sp, sp, -144 +; RV64-NEXT: .cfi_def_cfa_offset 144 +; 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, 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, 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: csrci mstatus, 8 +; RV64-NEXT: csrw mepc, s1 +; RV64-NEXT: csrw mcause, s0 +; 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 + 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, sf.mscratchcsw, sp +; RV32-NEXT: addi sp, sp, -80 +; RV32-NEXT: .cfi_def_cfa_offset 80 +; 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, 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, 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 +; 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, 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, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: both_caller: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.mscratchcsw, sp +; RV64-NEXT: addi sp, sp, -144 +; RV64-NEXT: .cfi_def_cfa_offset 144 +; 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, 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, 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: csrci mstatus, 8 +; RV64-NEXT: csrw mepc, s1 +; RV64-NEXT: csrw mcause, s0 +; 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, sf.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, sf.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, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: stack_swap_clobber: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.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, sf.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, sf.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, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: stack_swap_clobber_fp: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.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, sf.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, 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, 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, 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, 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 +; +; RV64-LABEL: preemptible_clobber: +; RV64: # %bb.0: +; RV64-NEXT: addi sp, sp, -32 +; RV64-NEXT: .cfi_def_cfa_offset 32 +; 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, 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, 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, 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 + 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, sf.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: csrr s0, mcause +; RV32-NEXT: csrr s1, mepc +; RV32-NEXT: csrsi mstatus, 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, 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, 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, sf.mscratchcsw, sp +; RV32-NEXT: mret +; +; RV64-LABEL: both_clobber: +; RV64: # %bb.0: +; RV64-NEXT: csrrw sp, sf.mscratchcsw, sp +; RV64-NEXT: addi sp, sp, -32 +; RV64-NEXT: .cfi_def_cfa_offset 32 +; 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, 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, 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, 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, sf.mscratchcsw, sp +; RV64-NEXT: mret + call void asm sideeffect "", "~{x8},~{x9}"() #4 + ret void +} diff --git a/llvm/test/MC/RISCV/xsfmclic-invalid.s b/llvm/test/MC/RISCV/xsfmclic-invalid.s new file mode 100644 index 0000000000000..dc8cc0b6c3543 --- /dev/null +++ b/llvm/test/MC/RISCV/xsfmclic-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, sf.mtvt, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.mtvt' requires 'experimental-xsfmclic' to be enabled + +csrrs t1, sf.mnxti, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.mnxti' requires 'experimental-xsfmclic' to be enabled + +csrrs t1, sf.mintstatus, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.mintstatus' requires 'experimental-xsfmclic' to be enabled + +csrrs t1, sf.mscratchcsw, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.mscratchcsw' requires 'experimental-xsfmclic' to be enabled + +csrrs t1, sf.mscratchcswl, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.mscratchcswl' requires 'experimental-xsfmclic' to be enabled diff --git a/llvm/test/MC/RISCV/xsfmclic-valid.s b/llvm/test/MC/RISCV/xsfmclic-valid.s new file mode 100644 index 0000000000000..c639c90c958af --- /dev/null +++ b/llvm/test/MC/RISCV/xsfmclic-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, sf.mtvt, zero +# CHECK-ENC: encoding: [0x73,0x23,0x70,0x30] +csrrs t1, sf.mtvt, zero +# CHECK-INST: csrrs t2, sf.mtvt, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x70,0x30] +csrrs t2, 0x307, zero + +# CHECK-INST: csrrs t1, sf.mnxti, zero +# CHECK-ENC: encoding: [0x73,0x23,0x50,0x34] +csrrs t1, sf.mnxti, zero +# CHECK-INST: csrrs t2, sf.mnxti, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x50,0x34] +csrrs t2, 0x345, zero + +# CHECK-INST: csrrs t1, sf.mintstatus, zero +# CHECK-ENC: encoding: [0x73,0x23,0x60,0x34] +csrrs t1, sf.mintstatus, zero +# CHECK-INST: csrrs t2, sf.mintstatus, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x60,0x34] +csrrs t2, 0x346, zero + +# CHECK-INST: csrrs t1, sf.mscratchcsw, zero +# CHECK-ENC: encoding: [0x73,0x23,0x80,0x34] +csrrs t1, sf.mscratchcsw, zero +# CHECK-INST: csrrs t2, sf.mscratchcsw, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x80,0x34] +csrrs t2, 0x348, zero + +# CHECK-INST: csrrs t1, sf.mscratchcswl, zero +# CHECK-ENC: encoding: [0x73,0x23,0x90,0x34] +csrrs t1, sf.mscratchcswl, zero +# CHECK-INST: csrrs t2, sf.mscratchcswl, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x90,0x34] +csrrs t2, 0x349, zero diff --git a/llvm/test/MC/RISCV/xsfsclic-invalid.s b/llvm/test/MC/RISCV/xsfsclic-invalid.s new file mode 100644 index 0000000000000..c4acfdfb8dcff --- /dev/null +++ b/llvm/test/MC/RISCV/xsfsclic-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, sf.stvt, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.stvt' requires 'experimental-xsfsclic' to be enabled + +csrrs t1, sf.snxti, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.snxti' requires 'experimental-xsfsclic' to be enabled + +csrrs t1, sf.sintstatus, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.sintstatus' requires 'experimental-xsfsclic' to be enabled + +csrrs t1, sf.sscratchcsw, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.sscratchcsw' requires 'experimental-xsfsclic' to be enabled + +csrrs t1, sf.sscratchcswl, zero +// CHECK-FEATURE: :[[@LINE-1]]:11: error: system register 'sf.sscratchcswl' requires 'experimental-xsfsclic' to be enabled diff --git a/llvm/test/MC/RISCV/xsfsclic-valid.s b/llvm/test/MC/RISCV/xsfsclic-valid.s new file mode 100644 index 0000000000000..c62e2366dfcad --- /dev/null +++ b/llvm/test/MC/RISCV/xsfsclic-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, sf.stvt, zero +# CHECK-ENC: encoding: [0x73,0x23,0x70,0x10] +csrrs t1, sf.stvt, zero +# CHECK-INST: csrrs t2, sf.stvt, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x70,0x10] +csrrs t2, 0x107, zero + +# CHECK-INST: csrrs t1, sf.snxti, zero +# CHECK-ENC: encoding: [0x73,0x23,0x50,0x14] +csrrs t1, sf.snxti, zero +# CHECK-INST: csrrs t2, sf.snxti, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x50,0x14] +csrrs t2, 0x145, zero + +# CHECK-INST: csrrs t1, sf.sintstatus, zero +# CHECK-ENC: encoding: [0x73,0x23,0x60,0x14] +csrrs t1, sf.sintstatus, zero +# CHECK-INST: csrrs t2, sf.sintstatus, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x60,0x14] +csrrs t2, 0x146, zero + +# CHECK-INST: csrrs t1, sf.sscratchcsw, zero +# CHECK-ENC: encoding: [0x73,0x23,0x80,0x14] +csrrs t1, sf.sscratchcsw, zero +# CHECK-INST: csrrs t2, sf.sscratchcsw, zero +# CHECK-ENC: encoding: [0xf3,0x23,0x80,0x14] +csrrs t2, 0x148, zero + +# CHECK-INST: csrrs t1, sf.sscratchcswl, zero +# CHECK-ENC: encoding: [0x73,0x23,0x90,0x14] +csrrs t1, sf.sscratchcswl, zero +# CHECK-INST: csrrs t2, sf.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 71c3b9635a8be..625c79abd04f4 100644 --- a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp +++ b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp @@ -1182,6 +1182,8 @@ Experimental extensions xqcisync 0.2 xrivosvisni 0.1 xrivosvizip 0.1 + xsfmclic 0.1 + xsfsclic 0.1 Supported Profiles rva20s64 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits