[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 <apazos@quicinc.com>
This commit is contained in:
Sam Elliott 2025-04-25 17:12:27 -07:00 committed by GitHub
parent 5137587fb1
commit cfc5baf6e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 1809 additions and 99 deletions

View File

@ -634,6 +634,12 @@ RISC-V Support
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
- Add support for `-mtune=generic-ooo` (a generic out-of-order model). - 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 - Adds support for `__attribute__((interrupt("qci-nest")))` and
`__attribute__((interrupt("qci-nonest")))`. These use instructions from `__attribute__((interrupt("qci-nonest")))`. These use instructions from

View File

@ -2252,10 +2252,23 @@ def NoMicroMips : InheritableAttr, TargetSpecificAttr<TargetMips32> {
def RISCVInterrupt : InheritableAttr, TargetSpecificAttr<TargetRISCV> { def RISCVInterrupt : InheritableAttr, TargetSpecificAttr<TargetRISCV> {
let Spellings = [GCC<"interrupt">]; let Spellings = [GCC<"interrupt">];
let Subjects = SubjectList<[Function]>; let Subjects = SubjectList<[Function]>;
let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true, let Args = [VariadicEnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
["supervisor", "machine", "qci-nest", "qci-nonest"], [
["supervisor", "machine", "qcinest", "qcinonest"], "supervisor",
1>]; "machine",
"qci-nest",
"qci-nonest",
"SiFive-CLIC-preemptible",
"SiFive-CLIC-stack-swap",
],
[
"supervisor",
"machine",
"qcinest",
"qcinonest",
"SiFiveCLICPreemptible",
"SiFiveCLICStackSwap",
]>];
let ParseKind = "Interrupt"; let ParseKind = "Interrupt";
let Documentation = [RISCVInterruptDocs]; let Documentation = [RISCVInterruptDocs];
} }

View File

@ -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 the backend to generate appropriate function entry/exit code so that it can be
used directly as an interrupt service routine. used directly as an interrupt service routine.
Permissible values for this parameter are ``supervisor``, ``machine``, Permissible values for this parameter are ``machine``, ``supervisor``,
``qci-nest`` and ``qci-nonest``. If there is no parameter, then it defaults to ``qci-nest``, ``qci-nonest``, ``SiFive-CLIC-preemptible``, and
``SiFive-CLIC-stack-swap``. If there is no parameter, then it defaults to
``machine``. ``machine``.
The ``qci-nest`` and ``qci-nonest`` values require Qualcomm's Xqciint extension 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 begin the interrupt handler. Both of these will use ``qc.c.mileaveret`` to
restore the state and return to the previous context. restore the state and return to the previous context.
The ``SiFive-CLIC-preemptible`` and ``SiFive-CLIC-stack-swap`` values are used
for machine-mode interrupts. For ``SiFive-CLIC-preemptible`` interrupts, the
values of ``mcause`` and ``mepc`` are saved onto the stack, and interrupts are
re-enabled. For ``SiFive-CLIC-stack-swap`` interrupts, the stack pointer is
swapped with ``mscratch`` before its first use and after its last use.
The SiFive CLIC values may be combined with each other and with the ``machine``
attribute value. Any other combination of different values is not allowed.
Repeated interrupt attribute on the same declaration will cause a warning Repeated interrupt attribute on the same declaration will cause a warning
to be emitted. In case of repeated declarations, the last one prevails. 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 The RISC-V Instruction Set Manual Volume II: Privileged Architecture
Version 1.10. Version 1.10.
https://github.com/quic/riscv-unified-db/releases/tag/Xqci-0.7 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
}]; }];
} }

View File

@ -12763,7 +12763,9 @@ def err_riscv_builtin_invalid_lmul : Error<
def err_riscv_type_requires_extension : Error< def err_riscv_type_requires_extension : Error<
"RISC-V type %0 requires the '%1' extension">; "RISC-V type %0 requires the '%1' extension">;
def err_riscv_attribute_interrupt_requires_extension : Error< 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< 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">; "'std::source_location::__impl' was not found; it must be defined before '__builtin_source_location' is called">;

View File

@ -829,16 +829,39 @@ public:
if (!Attr) if (!Attr)
return; return;
const char *Kind; StringRef Kind = "machine";
switch (Attr->getInterrupt()) { bool HasSiFiveCLICPreemptible = false;
case RISCVInterruptAttr::supervisor: Kind = "supervisor"; break; bool HasSiFiveCLICStackSwap = false;
case RISCVInterruptAttr::machine: Kind = "machine"; break; for (RISCVInterruptAttr::InterruptType type : Attr->interrupt()) {
case RISCVInterruptAttr::qcinest: switch (type) {
Kind = "qci-nest"; case RISCVInterruptAttr::machine:
break; // Do not update `Kind` because `Kind` is already "machine", or the
case RISCVInterruptAttr::qcinonest: // kinds also contains SiFive types which need to be applied.
Kind = "qci-nonest"; break;
break; case RISCVInterruptAttr::supervisor:
Kind = "supervisor";
break;
case RISCVInterruptAttr::qcinest:
Kind = "qci-nest";
break;
case RISCVInterruptAttr::qcinonest:
Kind = "qci-nonest";
break;
// There are three different LLVM IR attribute values for SiFive CLIC
// interrupt kinds, one for each kind and one extra for their combination.
case RISCVInterruptAttr::SiFiveCLICPreemptible: {
HasSiFiveCLICPreemptible = true;
Kind = HasSiFiveCLICStackSwap ? "SiFive-CLIC-preemptible-stack-swap"
: "SiFive-CLIC-preemptible";
break;
}
case RISCVInterruptAttr::SiFiveCLICStackSwap: {
HasSiFiveCLICStackSwap = true;
Kind = HasSiFiveCLICPreemptible ? "SiFive-CLIC-preemptible-stack-swap"
: "SiFive-CLIC-stack-swap";
break;
}
}
} }
Fn->addFnAttr("interrupt", Kind); Fn->addFnAttr("interrupt", Kind);

View File

@ -13,6 +13,7 @@
#include "clang/Sema/SemaRISCV.h" #include "clang/Sema/SemaRISCV.h"
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h" #include "clang/AST/Attr.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/Decl.h" #include "clang/AST/Decl.h"
#include "clang/Basic/Builtins.h" #include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetBuiltins.h"
@ -1453,25 +1454,14 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
return; 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: // Semantic checks for a function with the 'interrupt' attribute:
// - Must be a function. // - Must be a function.
// - Must have no parameters. // - Must have no parameters.
// - Must have the 'void' return type. // - Must have the 'void' return type.
// - The attribute itself must either have no argument or one of the // - The attribute itself must have at most 2 arguments
// valid interrupt types, see [RISCVInterruptDocs]. // - 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) { if (D->getFunctionType() == nullptr) {
Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
@ -1491,35 +1481,105 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
return; return;
} }
RISCVInterruptAttr::InterruptType Kind; if (!AL.checkAtMostNumArgs(SemaRef, 2))
if (!RISCVInterruptAttr::ConvertStrToInterruptType(Str, Kind)) { return;
Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
<< AL << Str << ArgLoc; 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 (!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;
}
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; return;
} }
switch (Kind) { if (HasUnaryType && HasSiFiveCLICType) {
default: Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_invalid_combination);
break; return;
case RISCVInterruptAttr::InterruptType::qcinest:
case RISCVInterruptAttr::InterruptType::qcinonest: {
const TargetInfo &TI = getASTContext().getTargetInfo();
llvm::StringMap<bool> FunctionFeatureMap;
getASTContext().getFunctionFeatureMap(FunctionFeatureMap,
dyn_cast<FunctionDecl>(D));
if (!TI.hasFeature("experimental-xqciint") &&
!FunctionFeatureMap.lookup("experimental-xqciint")) {
Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_requires_extension)
<< Str << "Xqciint";
return;
}
break;
} }
// "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()) for (RISCVInterruptAttr::InterruptType Type : Types) {
RISCVInterruptAttr(getASTContext(), AL, Kind)); 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) { bool SemaRISCV::isAliasValid(unsigned BuiltinID, StringRef AliasName) {

View File

@ -219,6 +219,8 @@
// CHECK-NEXT: xqcisync 0.2 'Xqcisync' (Qualcomm uC Sync Delay Extension) // CHECK-NEXT: xqcisync 0.2 'Xqcisync' (Qualcomm uC Sync Delay Extension)
// CHECK-NEXT: xrivosvisni 0.1 'XRivosVisni' (Rivos Vector Integer Small New) // CHECK-NEXT: xrivosvisni 0.1 'XRivosVisni' (Rivos Vector Integer Small New)
// CHECK-NEXT: xrivosvizip 0.1 'XRivosVizip' (Rivos Vector Register Zips) // 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-EMPTY:
// CHECK-NEXT: Supported Profiles // CHECK-NEXT: Supported Profiles
// CHECK-NEXT: rva20s64 // CHECK-NEXT: rva20s64

View File

@ -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 -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 -verify=disabled,both -fsyntax-only
// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -target-feature -experimental-xqciint -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"))) __attribute__((interrupt("qci-nest")))
void foo_nest_interrupt(void) {} 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 // CHECK: ret void
__attribute__((interrupt("qci-nonest"))) __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: attributes #0
// CHECK: "interrupt"="qci-nest" // CHECK: "interrupt"="qci-nest"
@ -22,18 +32,23 @@ void foo_nonnest_interrupt(void) {}
// CHECK: "interrupt"="qci-nonest" // CHECK: "interrupt"="qci-nonest"
#else #else
// Test for QCI extension's interrupt attribute support // 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(1))) void foo1(void) {} // both-error {{expected string literal as argument of 'interrupt' attribute}}
__attribute__((interrupt("qci-noest"))) void foo_nonest1(void) {} // both-warning {{'interrupt' attribute argument not supported: qci-noest}} __attribute__((interrupt("qci-nonest", 1))) void foo_nonest2(void) {} // both-error {{expected string literal as argument of 'interrupt' attribute}}
__attribute__((interrupt(1))) void foo_nest2(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-nest", "qci-nonest"))) void foo1(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}} __attribute__((interrupt("qci-est"))) void foo_nest3(void) {} // both-warning {{'interrupt' attribute argument not supported: "qci-est"}}
__attribute__((interrupt("qci-nonest", "qci-nest"))) void foo2(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}} __attribute__((interrupt("qci-noest"))) void foo_nonest3(void) {} // both-warning {{'interrupt' attribute argument not supported: "qci-noest"}}
__attribute__((interrupt("", "qci-nonest"))) void foo3(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}} __attribute__((interrupt("", "qci-nonest"))) void foo_nonest4(void) {} // both-warning {{'interrupt' attribute argument not supported: ""}}
__attribute__((interrupt("", "qci-nest"))) void foo4(void) {} // both-error {{'interrupt' attribute takes no more than 1 argument}} __attribute__((interrupt("", "qci-nest"))) void foo_nest4(void) {} // both-warning {{'interrupt' attribute argument not supported: ""}}
__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-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'}}
__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'}}
// This tests the errors for the qci interrupts when using // This tests the errors for the qci interrupts when using
// `__attribute__((target(...)))` - but they fail on RV64, because you cannot // `__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 // The attribute order is important, the interrupt attribute must come after the
// target attribute // 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-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-nonest"))) __attribute__((target("arch=+xqciint"))) void foo_nonest_xqciint2(void) {} // disabled-error {{RISC-V 'interrupt' attribute 'qci-nonest' requires extension 'Xqciint'}}
#endif #endif
#endif #endif

View File

@ -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

View File

@ -16,37 +16,48 @@ __attribute__((interrupt())) void foo_default(void) {}
// CHECK-LABEL: @foo_default2() #1 // CHECK-LABEL: @foo_default2() #1
// CHECK: ret void // CHECK: ret void
__attribute__((interrupt())) void foo_default2(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: attributes #0
// CHECK: "interrupt"="supervisor" // CHECK: "interrupt"="supervisor"
// CHECK: attributes #1 // CHECK: attributes #1
// CHECK: "interrupt"="machine" // CHECK: "interrupt"="machine"
#else #else
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}} \ __attribute__((interrupt("machine"), interrupt("supervisor"))) void foo6(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \
// expected-note {{repeated RISC-V 'interrupt' attribute is here}} // expected-note {{repeated RISC-V 'interrupt' attribute is here}}
__attribute__((interrupt, interrupt)) void foo7(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \ __attribute__((interrupt, interrupt)) void foo7(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \
// expected-note {{repeated RISC-V 'interrupt' attribute is here}} // 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)) int foo3(void) {return 0;} // expected-warning {{RISC-V 'interrupt' attribute only applies to functions that have a 'void' return type}}
__attribute__((interrupt())) void foo5(int a) {} // expected-warning {{RISC-V 'interrupt' attribute only applies to functions that have no parameters}}
__attribute__((interrupt("machine", "supervisor", "machine"))) void foo15(void) {} // expected-error {{'interrupt' attribute takes no more than 2 arguments}}
__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(""))) 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("supervisor"))) void foo9(void);
__attribute__((interrupt("machine"))) 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("machine"))) void foo12(void) {}
__attribute__((interrupt())) void foo13(void) {} __attribute__((interrupt())) void foo13(void) {}
__attribute__((interrupt)) void foo14(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 #endif

View File

@ -167,11 +167,14 @@ Changes to the RISC-V Backend
* Adds assembler support for ``.option exact``, which disables automatic compression, * Adds assembler support for ``.option exact``, which disables automatic compression,
and branch and linker relaxation. This can be disabled with ``.option noexact``, and branch and linker relaxation. This can be disabled with ``.option noexact``,
which is also the default. which is also the default.
<<<<<<< HEAD
* `-mcpu=xiangshan-kunminghu` was added. * `-mcpu=xiangshan-kunminghu` was added.
* `-mcpu=andes-n45` and `-mcpu=andes-nx45` were added. * `-mcpu=andes-n45` and `-mcpu=andes-nx45` were added.
* `-mcpu=andes-a45` and `-mcpu=andes-ax45` 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 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 Changes to the WebAssembly Backend
---------------------------------- ----------------------------------

View File

@ -1257,6 +1257,14 @@ def HasVendorXSfcease
AssemblerPredicate<(all_of FeatureVendorXSfcease), AssemblerPredicate<(all_of FeatureVendorXSfcease),
"'XSfcease' (SiFive sf.cease Instruction)">; "'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 // Core-V Extensions
def FeatureVendorXCVelw def FeatureVendorXCVelw

View File

@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "RISCVFrameLowering.h" #include "RISCVFrameLowering.h"
#include "MCTargetDesc/RISCVBaseInfo.h"
#include "RISCVMachineFunctionInfo.h" #include "RISCVMachineFunctionInfo.h"
#include "RISCVSubtarget.h" #include "RISCVSubtarget.h"
#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/Dwarf.h"
@ -27,6 +28,8 @@
#include <algorithm> #include <algorithm>
#define DEBUG_TYPE "riscv-frame"
using namespace llvm; using namespace llvm;
static Align getABIStackAlignment(RISCVABI::ABI ABI) { static Align getABIStackAlignment(RISCVABI::ABI ABI) {
@ -200,6 +203,149 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
CFIInstBuilder(MBB, MI, MachineInstr::FrameDestroy).buildRestore(SCSPReg); 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 // 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 // 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 // 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) if (MF.getFunction().getCallingConv() == CallingConv::GHC)
return; return;
// SiFive CLIC needs to swap `sp` into `sf.mscratchcsw`
emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL);
// Emit prologue for shadow call stack. // Emit prologue for shadow call stack.
emitSCSPrologue(MF, MBB, MBBI, DL); emitSCSPrologue(MF, MBB, MBBI, DL);
@ -871,6 +1020,9 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
allocateStack(MBB, MBBI, MF, StackSize, RealStackSize, /*EmitCFI=*/true, allocateStack(MBB, MBBI, MF, StackSize, RealStackSize, /*EmitCFI=*/true,
NeedProbe, ProbeSize, DynAllocation); 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 // 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 // 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 // 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 // Deallocate stack if StackSize isn't a zero yet
if (StackSize != 0) if (StackSize != 0)
deallocateStack(MF, MBB, MBBI, DL, StackSize, RealStackSize - StackSize); deallocateStack(MF, MBB, MBBI, DL, StackSize, RealStackSize - StackSize);
// Emit epilogue for shadow call stack. // Emit epilogue for shadow call stack.
emitSCSEpilogue(MF, MBB, MBBI, DL); emitSCSEpilogue(MF, MBB, MBBI, DL);
// SiFive CLIC needs to swap `sf.mscratchcsw` into `sp`
emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL);
} }
StackOffset StackOffset
@ -1357,6 +1514,9 @@ void RISCVFrameLowering::determineCalleeSaves(MachineFunction &MF,
auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
if (RVFI->isPushable(MF) && SavedRegs.test(RISCV::X26)) if (RVFI->isPushable(MF) && SavedRegs.test(RISCV::X26))
SavedRegs.set(RISCV::X27); SavedRegs.set(RISCV::X27);
// SiFive Preemptible Interrupt Handlers need additional frame entries
createSiFivePreemptibleInterruptFrameEntries(MF, *RVFI);
} }
std::pair<int64_t, Align> std::pair<int64_t, Align>
@ -1716,11 +1876,22 @@ bool RISCVFrameLowering::assignCalleeSavedSpillSlots(
MachineFunction &MF, const TargetRegisterInfo *TRI, MachineFunction &MF, const TargetRegisterInfo *TRI,
std::vector<CalleeSavedInfo> &CSI, unsigned &MinCSFrameIndex, std::vector<CalleeSavedInfo> &CSI, unsigned &MinCSFrameIndex,
unsigned &MaxCSFrameIndex) const { 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! // Early exit if no callee saved registers are modified!
if (CSI.empty()) if (CSI.empty())
return true; return true;
auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
if (RVFI->useQCIInterrupt(MF)) { if (RVFI->useQCIInterrupt(MF)) {
RVFI->setQCIInterruptStackSize(QCIInterruptPushAmount); RVFI->setQCIInterruptStackSize(QCIInterruptPushAmount);
} else if (RVFI->isPushable(MF)) { } else if (RVFI->isPushable(MF)) {

View File

@ -21423,15 +21423,28 @@ SDValue RISCVTargetLowering::LowerFormalArguments(
"supervisor", "supervisor",
"qci-nest", "qci-nest",
"qci-nonest", "qci-nonest",
"SiFive-CLIC-preemptible",
"SiFive-CLIC-stack-swap",
"SiFive-CLIC-preemptible-stack-swap",
}; };
if (llvm::find(SupportedInterruptKinds, Kind) == if (llvm::find(SupportedInterruptKinds, Kind) ==
std::end(SupportedInterruptKinds)) std::end(SupportedInterruptKinds))
report_fatal_error( report_fatal_error(
"Function interrupt attribute argument not supported!"); "Function interrupt attribute argument not supported!");
if ((Kind == "qci-nest" || Kind == "qci-nonest") && if (Kind.starts_with("qci-") && !Subtarget.hasVendorXqciint())
!Subtarget.hasVendorXqciint())
report_fatal_error("'qci-*' interrupt kinds require Xqciint extension"); 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()); EVT PtrVT = getPointerTy(DAG.getDataLayout());

View File

@ -67,12 +67,17 @@ RISCVMachineFunctionInfo::getInterruptStackKind(
StringRef InterruptVal = StringRef InterruptVal =
MF.getFunction().getFnAttribute("interrupt").getValueAsString(); 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) { void yaml::RISCVMachineFunctionInfo::mappingImpl(yaml::IO &YamlIO) {
@ -87,6 +92,10 @@ RISCVMachineFunctionInfo::getPushPopKind(const MachineFunction &MF) const {
if (VarArgsSaveSize != 0) if (VarArgsSaveSize != 0)
return PushPopKind::None; 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. // Zcmp is not compatible with the frame pointer convention.
if (MF.getSubtarget<RISCVSubtarget>().hasStdExtZcmp() && if (MF.getSubtarget<RISCVSubtarget>().hasStdExtZcmp() &&
!MF.getTarget().Options.DisableFramePointerElim(MF)) !MF.getTarget().Options.DisableFramePointerElim(MF))

View File

@ -78,6 +78,9 @@ private:
/// Size of any opaque stack adjustment due to QCI Interrupt instructions. /// Size of any opaque stack adjustment due to QCI Interrupt instructions.
unsigned QCIInterruptStackSize = 0; unsigned QCIInterruptStackSize = 0;
/// Store Frame Indexes for Interrupt-Related CSR Spills.
SmallVector<int, 2> InterruptCSRFrameIndexes;
int64_t StackProbeSize = 0; int64_t StackProbeSize = 0;
/// Does it probe the stack for a dynamic allocation? /// Does it probe the stack for a dynamic allocation?
@ -153,7 +156,14 @@ public:
unsigned getRVPushStackSize() const { return RVPushStackSize; } unsigned getRVPushStackSize() const { return RVPushStackSize; }
void setRVPushStackSize(unsigned Size) { RVPushStackSize = Size; } 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; InterruptStackKind getInterruptStackKind(const MachineFunction &MF) const;
@ -166,6 +176,32 @@ public:
unsigned getQCIInterruptStackSize() const { return QCIInterruptStackSize; } unsigned getQCIInterruptStackSize() const { return QCIInterruptStackSize; }
void setQCIInterruptStackSize(unsigned Size) { QCIInterruptStackSize = Size; } 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 // Some Stack Management Variants automatically update FP in a frame-pointer
// convention compatible way - which means we don't need to manually update // 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 // the FP, but we still need to emit the correct CFI information for

View File

@ -496,6 +496,24 @@ def : SysReg<"minstretcfgh", 0x722>;
// Vendor CSRs // 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 // Xqciint
let FeaturesRequired = [{ {RISCV::FeatureVendorXqciint} }], isRV32Only = 1 in { let FeaturesRequired = [{ {RISCV::FeatureVendorXqciint} }], isRV32Only = 1 in {
def : SysReg<"qc.mmcr", 0x7C0>; def : SysReg<"qc.mmcr", 0x7C0>;

View File

@ -42,6 +42,8 @@
; CHECK-NEXT: experimental-xqcisync - 'Xqcisync' (Qualcomm uC Sync Delay Extension). ; CHECK-NEXT: experimental-xqcisync - 'Xqcisync' (Qualcomm uC Sync Delay Extension).
; CHECK-NEXT: experimental-xrivosvisni - 'XRivosVisni' (Rivos Vector Integer Small New). ; CHECK-NEXT: experimental-xrivosvisni - 'XRivosVisni' (Rivos Vector Integer Small New).
; CHECK-NEXT: experimental-xrivosvizip - 'XRivosVizip' (Rivos Vector Register Zips). ; 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-zalasr - 'Zalasr' (Load-Acquire and Store-Release Instructions).
; CHECK-NEXT: experimental-zicfilp - 'Zicfilp' (Landing pad). ; CHECK-NEXT: experimental-zicfilp - 'Zicfilp' (Landing pad).
; CHECK-NEXT: experimental-zicfiss - 'Zicfiss' (Shadow stack). ; CHECK-NEXT: experimental-zicfiss - 'Zicfiss' (Shadow stack).

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1182,6 +1182,8 @@ Experimental extensions
xqcisync 0.2 xqcisync 0.2
xrivosvisni 0.1 xrivosvisni 0.1
xrivosvizip 0.1 xrivosvizip 0.1
xsfmclic 0.1
xsfsclic 0.1
Supported Profiles Supported Profiles
rva20s64 rva20s64