Skip to content

[RISCV] SiFive CLIC Support #132481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Apr 26, 2025
Merged

[RISCV] SiFive CLIC Support #132481

merged 20 commits into from
Apr 26, 2025

Conversation

lenary
Copy link
Member

@lenary lenary commented Mar 21, 2025

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.

lenary and others added 2 commits March 21, 2025 12:09
This Change adds support for two SiFive vendor attributes in clang:
- "SiFive-CLIC-preemptible"
- "SiFive-CLIC-stack-swap"

These can be given together, and can be combined with "machine", but
cannot be combined with any other interrupt attribute values.

These are handled primarily in RISCVFrameLowering:
- "SiFive-CLIC-stack-swap" entails swapping `sp` with `mscratchcsw` at
  function entry and exit, which holds the trap stack pointer.
- "SiFive-CLIC-preemptible" entails saving `mcause` and `mepc` before
  re-enabling interrupts using `mstatus`. To save these, `s0` and `s1`
  are first spilled to the stack, and then the values are read into
  these registers. If these registers are used in the function, their
  values will be spilled a second time onto the stack with the generic
  callee-saved-register handling. At the end of the function interrupts
  are disabled again before `mepc` and `mcause` are restored.

The CFI information for this implementation is not correct.

Co-authored-by: Ana Pazos <[email protected]>
@lenary lenary requested review from kito-cheng, apazos and topperc March 21, 2025 22:17
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:RISC-V clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. mc Machine (object) code labels Mar 21, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 21, 2025

@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: Sam Elliott (lenary)

Changes

This Change adds support for two SiFive vendor attributes in clang:

  • "SiFive-CLIC-preemptible"
  • "SiFive-CLIC-stack-swap"

These can be given together, and can be combined with "machine", but
cannot be combined with any other interrupt attribute values.

These are handled primarily in RISCVFrameLowering:

  • "SiFive-CLIC-stack-swap" entails swapping sp with mscratchcsw at
    function entry and exit, which holds the trap stack pointer.
  • "SiFive-CLIC-preemptible" entails saving mcause and mepc before
    re-enabling interrupts using mstatus. To save these, s0 and s1
    are first spilled to the stack, and then the values are read into
    these registers. If these registers are used in the function, their
    values will be spilled a second time onto the stack with the generic
    callee-saved-register handling. At the end of the function interrupts
    are disabled again before mepc and mcause are restored.

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.


Patch is 96.10 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132481.diff

22 Files Affected:

  • (modified) clang/include/clang/Basic/Attr.td (+17-4)
  • (modified) clang/include/clang/Basic/AttrDocs.td (+13-2)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3-1)
  • (modified) clang/lib/CodeGen/Targets/RISCV.cpp (+33-10)
  • (modified) clang/lib/Sema/SemaRISCV.cpp (+96-36)
  • (modified) clang/test/Driver/print-supported-extensions-riscv.c (+2)
  • (modified) clang/test/Sema/riscv-interrupt-attr-qci.c (+32-17)
  • (added) clang/test/Sema/riscv-interrupt-attr-sifive.c (+98)
  • (modified) clang/test/Sema/riscv-interrupt-attr.c (+31-15)
  • (modified) llvm/lib/CodeGen/PrologEpilogInserter.cpp (+28)
  • (modified) llvm/lib/Target/RISCV/RISCVFeatures.td (+8)
  • (modified) llvm/lib/Target/RISCV/RISCVFrameLowering.cpp (+160)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+8-2)
  • (modified) llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp (+14-5)
  • (modified) llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h (+37-1)
  • (modified) llvm/lib/Target/RISCV/RISCVSystemOperands.td (+23)
  • (added) llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll (+1050)
  • (added) llvm/test/MC/RISCV/rvsfmclic-invalid.s (+20)
  • (added) llvm/test/MC/RISCV/rvsfmclic-valid.s (+46)
  • (added) llvm/test/MC/RISCV/rvsfsclic-invalid.s (+20)
  • (added) llvm/test/MC/RISCV/rvsfsclic-valid.s (+46)
  • (modified) llvm/unittests/TargetParser/RISCVISAInfoTest.cpp (+2)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b8cd3475bb88a..8f1f5baef24a2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2224,10 +2224,23 @@ def NoMicroMips : InheritableAttr, TargetSpecificAttr<TargetMips32> {
 def RISCVInterrupt : InheritableAttr, TargetSpecificAttr<TargetRISCV> {
   let Spellings = [GCC<"interrupt">];
   let Subjects = SubjectList<[Function]>;
-  let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
-                           ["supervisor", "machine", "qci-nest", "qci-nonest"],
-                           ["supervisor", "machine", "qcinest", "qcinonest"],
-                           1>];
+  let Args = [VariadicEnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
+                                   [
+                                     "supervisor",
+                                     "machine",
+                                     "qci-nest",
+                                     "qci-nonest",
+                                     "SiFive-CLIC-preemptible",
+                                     "SiFive-CLIC-stack-swap",
+                                   ],
+                                   [
+                                     "supervisor",
+                                     "machine",
+                                     "qcinest",
+                                     "qcinonest",
+                                     "SiFiveCLICPreemptible",
+                                     "SiFiveCLICStackSwap",
+                                   ]>];
   let ParseKind = "Interrupt";
   let Documentation = [RISCVInterruptDocs];
 }
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 34e7ff9612859..552cb7f45a7d2 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -2863,8 +2863,9 @@ targets. This attribute may be attached to a function definition and instructs
 the backend to generate appropriate function entry/exit code so that it can be
 used directly as an interrupt service routine.
 
-Permissible values for this parameter are ``supervisor``, ``machine``,
-``qci-nest`` and ``qci-nonest``. If there is no parameter, then it defaults to
+Permissible values for this parameter are ``machine``, ``supervisor``,
+``qci-nest``, ``qci-nonest``, ``SiFive-CLIC-preemptible``, and
+``SiFive-CLIC-stack-swap``. If there is no parameter, then it defaults to
 ``machine``.
 
 The ``qci-nest`` and ``qci-nonest`` values require Qualcomm's Xqciint extension
@@ -2875,6 +2876,15 @@ restore interrupt state to the stack -- the ``qci-nest`` value will use
 begin the interrupt handler. Both of these will use ``qc.c.mileaveret`` to
 restore the state and return to the previous context.
 
+The ``SiFive-CLIC-preemptible`` and ``SiFive-CLIC-stack-swap`` values are used
+for machine-mode interrupts. For ``SiFive-CLIC-preemptible`` interrupts, the
+values of ``mcause`` and ``mepc`` are saved onto the stack, and interrupts are
+re-enabled. For ``SiFive-CLIC-stack-swap`` interrupts, the stack pointer is
+swapped with ``mscratch`` before its first use and after its last use.
+
+The SiFive CLIC values may be combined with each other and with the ``machine``
+attribute value. Any other combination of different values is not allowed.
+
 Repeated interrupt attribute on the same declaration will cause a warning
 to be emitted. In case of repeated declarations, the last one prevails.
 
@@ -2884,6 +2894,7 @@ https://riscv.org/specifications/privileged-isa/
 The RISC-V Instruction Set Manual Volume II: Privileged Architecture
 Version 1.10.
 https://github.com/quic/riscv-unified-db/releases/tag/Xqci-0.7
+https://sifive.cdn.prismic.io/sifive/d1984d2b-c9b9-4c91-8de0-d68a5e64fa0f_sifive-interrupt-cookbook-v1p2.pdf
   }];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1536a3b8c920a..0c587d8b677d5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12705,7 +12705,9 @@ def err_riscv_builtin_invalid_lmul : Error<
 def err_riscv_type_requires_extension : Error<
   "RISC-V type %0 requires the '%1' extension">;
 def err_riscv_attribute_interrupt_requires_extension : Error<
-  "RISC-V interrupt attribute '%0' requires extension '%1'">;
+  "RISC-V 'interrupt' attribute '%0' requires extension '%1'">;
+def err_riscv_attribute_interrupt_invalid_combination : Error<
+  "RISC-V 'interrupt' attribute contains invalid combination of interrupt types">;
 
 def err_std_source_location_impl_not_found : Error<
   "'std::source_location::__impl' was not found; it must be defined before '__builtin_source_location' is called">;
diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp
index 5aa10ba41f5ed..14d4cee7c61d3 100644
--- a/clang/lib/CodeGen/Targets/RISCV.cpp
+++ b/clang/lib/CodeGen/Targets/RISCV.cpp
@@ -829,16 +829,39 @@ class RISCVTargetCodeGenInfo : public TargetCodeGenInfo {
     if (!Attr)
       return;
 
-    const char *Kind;
-    switch (Attr->getInterrupt()) {
-    case RISCVInterruptAttr::supervisor: Kind = "supervisor"; break;
-    case RISCVInterruptAttr::machine: Kind = "machine"; break;
-    case RISCVInterruptAttr::qcinest:
-      Kind = "qci-nest";
-      break;
-    case RISCVInterruptAttr::qcinonest:
-      Kind = "qci-nonest";
-      break;
+    StringRef Kind = "machine";
+    bool HasSiFiveCLICPreemptible = false;
+    bool HasSiFiveCLICStackSwap = false;
+    for (RISCVInterruptAttr::InterruptType type : Attr->interrupt()) {
+      switch (type) {
+      case RISCVInterruptAttr::machine:
+        // Do not update `Kind` because `Kind` is already "machine", or the
+        // kinds also contains SiFive types which need to be applied.
+        break;
+      case RISCVInterruptAttr::supervisor:
+        Kind = "supervisor";
+        break;
+      case RISCVInterruptAttr::qcinest:
+        Kind = "qci-nest";
+        break;
+      case RISCVInterruptAttr::qcinonest:
+        Kind = "qci-nonest";
+        break;
+      // There are three different LLVM IR attribute values for SiFive CLIC
+      // interrupt kinds, one for each kind and one extra for their combination.
+      case RISCVInterruptAttr::SiFiveCLICPreemptible: {
+        HasSiFiveCLICPreemptible = true;
+        Kind = HasSiFiveCLICStackSwap ? "SiFive-CLIC-preemptible-stack-swap"
+                                      : "SiFive-CLIC-preemptible";
+        break;
+      }
+      case RISCVInterruptAttr::SiFiveCLICStackSwap: {
+        HasSiFiveCLICStackSwap = true;
+        Kind = HasSiFiveCLICPreemptible ? "SiFive-CLIC-preemptible-stack-swap"
+                                        : "SiFive-CLIC-stack-swap";
+        break;
+      }
+      }
     }
 
     Fn->addFnAttr("interrupt", Kind);
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index f23827d566610..25ff1e99498ec 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -13,6 +13,7 @@
 #include "clang/Sema/SemaRISCV.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Attr.h"
+#include "clang/AST/Attrs.inc"
 #include "clang/AST/Decl.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/TargetBuiltins.h"
@@ -1453,25 +1454,14 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
     return;
   }
 
-  // Check the attribute argument. Argument is optional.
-  if (!AL.checkAtMostNumArgs(SemaRef, 1))
-    return;
-
-  StringRef Str;
-  SourceLocation ArgLoc;
-
-  // 'machine'is the default interrupt mode.
-  if (AL.getNumArgs() == 0)
-    Str = "machine";
-  else if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
-    return;
-
   // Semantic checks for a function with the 'interrupt' attribute:
   // - Must be a function.
   // - Must have no parameters.
   // - Must have the 'void' return type.
-  // - The attribute itself must either have no argument or one of the
-  //   valid interrupt types, see [RISCVInterruptDocs].
+  // - The attribute itself must have at most 2 arguments
+  // - The attribute arguments must be string literals, and valid choices.
+  // - The attribute arguments must be a valid combination
+  // - The current target must support the right extensions for the combination.
 
   if (D->getFunctionType() == nullptr) {
     Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
@@ -1491,35 +1481,105 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
     return;
   }
 
-  RISCVInterruptAttr::InterruptType Kind;
-  if (!RISCVInterruptAttr::ConvertStrToInterruptType(Str, Kind)) {
-    Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
-        << AL << Str << ArgLoc;
+  if (!AL.checkAtMostNumArgs(SemaRef, 2))
     return;
-  }
 
-  switch (Kind) {
-  default:
-    break;
-  case RISCVInterruptAttr::InterruptType::qcinest:
-  case RISCVInterruptAttr::InterruptType::qcinonest: {
-    const TargetInfo &TI = getASTContext().getTargetInfo();
-    llvm::StringMap<bool> FunctionFeatureMap;
-    getASTContext().getFunctionFeatureMap(FunctionFeatureMap,
-                                          dyn_cast<FunctionDecl>(D));
+  bool HasSiFiveCLICType = false;
+  bool HasUnaryType = false;
+
+  SmallSet<RISCVInterruptAttr::InterruptType, 2> Types;
+  for (unsigned ArgIndex = 0; ArgIndex < AL.getNumArgs(); ++ArgIndex) {
+    RISCVInterruptAttr::InterruptType Type;
+    StringRef TypeString;
+    SourceLocation Loc;
 
-    if (!TI.hasFeature("experimental-xqciint") &&
-        !FunctionFeatureMap.lookup("experimental-xqciint")) {
-      Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_requires_extension)
-          << Str << "Xqciint";
+    if (!SemaRef.checkStringLiteralArgumentAttr(AL, ArgIndex, TypeString, &Loc))
+      return;
+
+    if (!RISCVInterruptAttr::ConvertStrToInterruptType(TypeString, Type)) {
+      std::string TypeLiteral = ("\"" + TypeString + "\"").str();
+      Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
+          << AL << TypeLiteral << Loc;
       return;
     }
-    break;
+
+    switch (Type) {
+    case RISCVInterruptAttr::machine:
+      // "machine" could be combined with the SiFive CLIC types, or could be
+      // just "machine".
+      break;
+    case RISCVInterruptAttr::SiFiveCLICPreemptible:
+    case RISCVInterruptAttr::SiFiveCLICStackSwap:
+      // SiFive-CLIC types can be combined with each other and "machine"
+      HasSiFiveCLICType = true;
+      break;
+    case RISCVInterruptAttr::supervisor:
+    case RISCVInterruptAttr::qcinest:
+    case RISCVInterruptAttr::qcinonest:
+      // "supervisor" and "qci-(no)nest" cannot be combined with any other types
+      HasUnaryType = true;
+      break;
+    }
+
+    Types.insert(Type);
+  }
+
+  if (HasUnaryType && Types.size() > 1) {
+    Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_invalid_combination);
+    return;
   }
+
+  if (HasUnaryType && HasSiFiveCLICType) {
+    Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_invalid_combination);
+    return;
+  }
+
+  // "machine" is the default, if nothing is specified.
+  if (AL.getNumArgs() == 0)
+    Types.insert(RISCVInterruptAttr::machine);
+
+  const TargetInfo &TI = getASTContext().getTargetInfo();
+  llvm::StringMap<bool> FunctionFeatureMap;
+  getASTContext().getFunctionFeatureMap(FunctionFeatureMap,
+                                        dyn_cast<FunctionDecl>(D));
+
+  auto HasFeature = [&](StringRef FeatureName) -> bool {
+    return TI.hasFeature(FeatureName) || FunctionFeatureMap.lookup(FeatureName);
   };
 
-  D->addAttr(::new (getASTContext())
-                 RISCVInterruptAttr(getASTContext(), AL, Kind));
+  for (RISCVInterruptAttr::InterruptType Type : Types) {
+    switch (Type) {
+    // The QCI interrupt types require Xqciint
+    case RISCVInterruptAttr::qcinest:
+    case RISCVInterruptAttr::qcinonest: {
+      if (!HasFeature("experimental-xqciint")) {
+        Diag(AL.getLoc(),
+             diag::err_riscv_attribute_interrupt_requires_extension)
+            << RISCVInterruptAttr::ConvertInterruptTypeToStr(Type) << "Xqciint";
+        return;
+      }
+    } break;
+    // The SiFive CLIC interrupt types require Xsfmclic
+    case RISCVInterruptAttr::SiFiveCLICPreemptible:
+    case RISCVInterruptAttr::SiFiveCLICStackSwap: {
+      if (!HasFeature("experimental-xsfmclic")) {
+        Diag(AL.getLoc(),
+             diag::err_riscv_attribute_interrupt_requires_extension)
+            << RISCVInterruptAttr::ConvertInterruptTypeToStr(Type)
+            << "XSfmclic";
+        return;
+      }
+    } break;
+    default:
+      break;
+    }
+  }
+
+  SmallVector<RISCVInterruptAttr::InterruptType, 2> TypesVec(Types.begin(),
+                                                             Types.end());
+
+  D->addAttr(::new (getASTContext()) RISCVInterruptAttr(
+      getASTContext(), AL, TypesVec.data(), TypesVec.size()));
 }
 
 bool SemaRISCV::isAliasValid(unsigned BuiltinID, StringRef AliasName) {
diff --git a/clang/test/Driver/print-supported-extensions-riscv.c b/clang/test/Driver/print-supported-extensions-riscv.c
index 35de2820ef84f..ca695221dee9d 100644
--- a/clang/test/Driver/print-supported-extensions-riscv.c
+++ b/clang/test/Driver/print-supported-extensions-riscv.c
@@ -213,6 +213,8 @@
 // CHECK-NEXT:     xqcisls              0.2       'Xqcisls' (Qualcomm uC Scaled Load Store Extension)
 // CHECK-NEXT:     xrivosvisni          0.1       'XRivosVisni' (Rivos Vector Integer Small New)
 // CHECK-NEXT:     xrivosvizip          0.1       'XRivosVizip' (Rivos Vector Register Zips)
+// CHECK-NEXT:     xsfmclic             0.1       'XSfmclic' (SiFive CLIC Machine-mode CSRs)
+// CHECK-NEXT:     xsfsclic             0.1       'XSfsclic' (SiFive CLIC Supervisor-mode CSRs)
 // CHECK-EMPTY:
 // CHECK-NEXT: Supported Profiles
 // CHECK-NEXT:     rva20s64
diff --git a/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...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Mar 21, 2025

@llvm/pr-subscribers-mc

Author: Sam Elliott (lenary)

Changes

This Change adds support for two SiFive vendor attributes in clang:

  • "SiFive-CLIC-preemptible"
  • "SiFive-CLIC-stack-swap"

These can be given together, and can be combined with "machine", but
cannot be combined with any other interrupt attribute values.

These are handled primarily in RISCVFrameLowering:

  • "SiFive-CLIC-stack-swap" entails swapping sp with mscratchcsw at
    function entry and exit, which holds the trap stack pointer.
  • "SiFive-CLIC-preemptible" entails saving mcause and mepc before
    re-enabling interrupts using mstatus. To save these, s0 and s1
    are first spilled to the stack, and then the values are read into
    these registers. If these registers are used in the function, their
    values will be spilled a second time onto the stack with the generic
    callee-saved-register handling. At the end of the function interrupts
    are disabled again before mepc and mcause are restored.

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.


Patch is 96.10 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132481.diff

22 Files Affected:

  • (modified) clang/include/clang/Basic/Attr.td (+17-4)
  • (modified) clang/include/clang/Basic/AttrDocs.td (+13-2)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3-1)
  • (modified) clang/lib/CodeGen/Targets/RISCV.cpp (+33-10)
  • (modified) clang/lib/Sema/SemaRISCV.cpp (+96-36)
  • (modified) clang/test/Driver/print-supported-extensions-riscv.c (+2)
  • (modified) clang/test/Sema/riscv-interrupt-attr-qci.c (+32-17)
  • (added) clang/test/Sema/riscv-interrupt-attr-sifive.c (+98)
  • (modified) clang/test/Sema/riscv-interrupt-attr.c (+31-15)
  • (modified) llvm/lib/CodeGen/PrologEpilogInserter.cpp (+28)
  • (modified) llvm/lib/Target/RISCV/RISCVFeatures.td (+8)
  • (modified) llvm/lib/Target/RISCV/RISCVFrameLowering.cpp (+160)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+8-2)
  • (modified) llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp (+14-5)
  • (modified) llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h (+37-1)
  • (modified) llvm/lib/Target/RISCV/RISCVSystemOperands.td (+23)
  • (added) llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll (+1050)
  • (added) llvm/test/MC/RISCV/rvsfmclic-invalid.s (+20)
  • (added) llvm/test/MC/RISCV/rvsfmclic-valid.s (+46)
  • (added) llvm/test/MC/RISCV/rvsfsclic-invalid.s (+20)
  • (added) llvm/test/MC/RISCV/rvsfsclic-valid.s (+46)
  • (modified) llvm/unittests/TargetParser/RISCVISAInfoTest.cpp (+2)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b8cd3475bb88a..8f1f5baef24a2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2224,10 +2224,23 @@ def NoMicroMips : InheritableAttr, TargetSpecificAttr<TargetMips32> {
 def RISCVInterrupt : InheritableAttr, TargetSpecificAttr<TargetRISCV> {
   let Spellings = [GCC<"interrupt">];
   let Subjects = SubjectList<[Function]>;
-  let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
-                           ["supervisor", "machine", "qci-nest", "qci-nonest"],
-                           ["supervisor", "machine", "qcinest", "qcinonest"],
-                           1>];
+  let Args = [VariadicEnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
+                                   [
+                                     "supervisor",
+                                     "machine",
+                                     "qci-nest",
+                                     "qci-nonest",
+                                     "SiFive-CLIC-preemptible",
+                                     "SiFive-CLIC-stack-swap",
+                                   ],
+                                   [
+                                     "supervisor",
+                                     "machine",
+                                     "qcinest",
+                                     "qcinonest",
+                                     "SiFiveCLICPreemptible",
+                                     "SiFiveCLICStackSwap",
+                                   ]>];
   let ParseKind = "Interrupt";
   let Documentation = [RISCVInterruptDocs];
 }
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 34e7ff9612859..552cb7f45a7d2 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -2863,8 +2863,9 @@ targets. This attribute may be attached to a function definition and instructs
 the backend to generate appropriate function entry/exit code so that it can be
 used directly as an interrupt service routine.
 
-Permissible values for this parameter are ``supervisor``, ``machine``,
-``qci-nest`` and ``qci-nonest``. If there is no parameter, then it defaults to
+Permissible values for this parameter are ``machine``, ``supervisor``,
+``qci-nest``, ``qci-nonest``, ``SiFive-CLIC-preemptible``, and
+``SiFive-CLIC-stack-swap``. If there is no parameter, then it defaults to
 ``machine``.
 
 The ``qci-nest`` and ``qci-nonest`` values require Qualcomm's Xqciint extension
@@ -2875,6 +2876,15 @@ restore interrupt state to the stack -- the ``qci-nest`` value will use
 begin the interrupt handler. Both of these will use ``qc.c.mileaveret`` to
 restore the state and return to the previous context.
 
+The ``SiFive-CLIC-preemptible`` and ``SiFive-CLIC-stack-swap`` values are used
+for machine-mode interrupts. For ``SiFive-CLIC-preemptible`` interrupts, the
+values of ``mcause`` and ``mepc`` are saved onto the stack, and interrupts are
+re-enabled. For ``SiFive-CLIC-stack-swap`` interrupts, the stack pointer is
+swapped with ``mscratch`` before its first use and after its last use.
+
+The SiFive CLIC values may be combined with each other and with the ``machine``
+attribute value. Any other combination of different values is not allowed.
+
 Repeated interrupt attribute on the same declaration will cause a warning
 to be emitted. In case of repeated declarations, the last one prevails.
 
@@ -2884,6 +2894,7 @@ https://riscv.org/specifications/privileged-isa/
 The RISC-V Instruction Set Manual Volume II: Privileged Architecture
 Version 1.10.
 https://github.com/quic/riscv-unified-db/releases/tag/Xqci-0.7
+https://sifive.cdn.prismic.io/sifive/d1984d2b-c9b9-4c91-8de0-d68a5e64fa0f_sifive-interrupt-cookbook-v1p2.pdf
   }];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1536a3b8c920a..0c587d8b677d5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12705,7 +12705,9 @@ def err_riscv_builtin_invalid_lmul : Error<
 def err_riscv_type_requires_extension : Error<
   "RISC-V type %0 requires the '%1' extension">;
 def err_riscv_attribute_interrupt_requires_extension : Error<
-  "RISC-V interrupt attribute '%0' requires extension '%1'">;
+  "RISC-V 'interrupt' attribute '%0' requires extension '%1'">;
+def err_riscv_attribute_interrupt_invalid_combination : Error<
+  "RISC-V 'interrupt' attribute contains invalid combination of interrupt types">;
 
 def err_std_source_location_impl_not_found : Error<
   "'std::source_location::__impl' was not found; it must be defined before '__builtin_source_location' is called">;
diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp
index 5aa10ba41f5ed..14d4cee7c61d3 100644
--- a/clang/lib/CodeGen/Targets/RISCV.cpp
+++ b/clang/lib/CodeGen/Targets/RISCV.cpp
@@ -829,16 +829,39 @@ class RISCVTargetCodeGenInfo : public TargetCodeGenInfo {
     if (!Attr)
       return;
 
-    const char *Kind;
-    switch (Attr->getInterrupt()) {
-    case RISCVInterruptAttr::supervisor: Kind = "supervisor"; break;
-    case RISCVInterruptAttr::machine: Kind = "machine"; break;
-    case RISCVInterruptAttr::qcinest:
-      Kind = "qci-nest";
-      break;
-    case RISCVInterruptAttr::qcinonest:
-      Kind = "qci-nonest";
-      break;
+    StringRef Kind = "machine";
+    bool HasSiFiveCLICPreemptible = false;
+    bool HasSiFiveCLICStackSwap = false;
+    for (RISCVInterruptAttr::InterruptType type : Attr->interrupt()) {
+      switch (type) {
+      case RISCVInterruptAttr::machine:
+        // Do not update `Kind` because `Kind` is already "machine", or the
+        // kinds also contains SiFive types which need to be applied.
+        break;
+      case RISCVInterruptAttr::supervisor:
+        Kind = "supervisor";
+        break;
+      case RISCVInterruptAttr::qcinest:
+        Kind = "qci-nest";
+        break;
+      case RISCVInterruptAttr::qcinonest:
+        Kind = "qci-nonest";
+        break;
+      // There are three different LLVM IR attribute values for SiFive CLIC
+      // interrupt kinds, one for each kind and one extra for their combination.
+      case RISCVInterruptAttr::SiFiveCLICPreemptible: {
+        HasSiFiveCLICPreemptible = true;
+        Kind = HasSiFiveCLICStackSwap ? "SiFive-CLIC-preemptible-stack-swap"
+                                      : "SiFive-CLIC-preemptible";
+        break;
+      }
+      case RISCVInterruptAttr::SiFiveCLICStackSwap: {
+        HasSiFiveCLICStackSwap = true;
+        Kind = HasSiFiveCLICPreemptible ? "SiFive-CLIC-preemptible-stack-swap"
+                                        : "SiFive-CLIC-stack-swap";
+        break;
+      }
+      }
     }
 
     Fn->addFnAttr("interrupt", Kind);
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index f23827d566610..25ff1e99498ec 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -13,6 +13,7 @@
 #include "clang/Sema/SemaRISCV.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Attr.h"
+#include "clang/AST/Attrs.inc"
 #include "clang/AST/Decl.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/TargetBuiltins.h"
@@ -1453,25 +1454,14 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
     return;
   }
 
-  // Check the attribute argument. Argument is optional.
-  if (!AL.checkAtMostNumArgs(SemaRef, 1))
-    return;
-
-  StringRef Str;
-  SourceLocation ArgLoc;
-
-  // 'machine'is the default interrupt mode.
-  if (AL.getNumArgs() == 0)
-    Str = "machine";
-  else if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
-    return;
-
   // Semantic checks for a function with the 'interrupt' attribute:
   // - Must be a function.
   // - Must have no parameters.
   // - Must have the 'void' return type.
-  // - The attribute itself must either have no argument or one of the
-  //   valid interrupt types, see [RISCVInterruptDocs].
+  // - The attribute itself must have at most 2 arguments
+  // - The attribute arguments must be string literals, and valid choices.
+  // - The attribute arguments must be a valid combination
+  // - The current target must support the right extensions for the combination.
 
   if (D->getFunctionType() == nullptr) {
     Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
@@ -1491,35 +1481,105 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
     return;
   }
 
-  RISCVInterruptAttr::InterruptType Kind;
-  if (!RISCVInterruptAttr::ConvertStrToInterruptType(Str, Kind)) {
-    Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
-        << AL << Str << ArgLoc;
+  if (!AL.checkAtMostNumArgs(SemaRef, 2))
     return;
-  }
 
-  switch (Kind) {
-  default:
-    break;
-  case RISCVInterruptAttr::InterruptType::qcinest:
-  case RISCVInterruptAttr::InterruptType::qcinonest: {
-    const TargetInfo &TI = getASTContext().getTargetInfo();
-    llvm::StringMap<bool> FunctionFeatureMap;
-    getASTContext().getFunctionFeatureMap(FunctionFeatureMap,
-                                          dyn_cast<FunctionDecl>(D));
+  bool HasSiFiveCLICType = false;
+  bool HasUnaryType = false;
+
+  SmallSet<RISCVInterruptAttr::InterruptType, 2> Types;
+  for (unsigned ArgIndex = 0; ArgIndex < AL.getNumArgs(); ++ArgIndex) {
+    RISCVInterruptAttr::InterruptType Type;
+    StringRef TypeString;
+    SourceLocation Loc;
 
-    if (!TI.hasFeature("experimental-xqciint") &&
-        !FunctionFeatureMap.lookup("experimental-xqciint")) {
-      Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_requires_extension)
-          << Str << "Xqciint";
+    if (!SemaRef.checkStringLiteralArgumentAttr(AL, ArgIndex, TypeString, &Loc))
+      return;
+
+    if (!RISCVInterruptAttr::ConvertStrToInterruptType(TypeString, Type)) {
+      std::string TypeLiteral = ("\"" + TypeString + "\"").str();
+      Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
+          << AL << TypeLiteral << Loc;
       return;
     }
-    break;
+
+    switch (Type) {
+    case RISCVInterruptAttr::machine:
+      // "machine" could be combined with the SiFive CLIC types, or could be
+      // just "machine".
+      break;
+    case RISCVInterruptAttr::SiFiveCLICPreemptible:
+    case RISCVInterruptAttr::SiFiveCLICStackSwap:
+      // SiFive-CLIC types can be combined with each other and "machine"
+      HasSiFiveCLICType = true;
+      break;
+    case RISCVInterruptAttr::supervisor:
+    case RISCVInterruptAttr::qcinest:
+    case RISCVInterruptAttr::qcinonest:
+      // "supervisor" and "qci-(no)nest" cannot be combined with any other types
+      HasUnaryType = true;
+      break;
+    }
+
+    Types.insert(Type);
+  }
+
+  if (HasUnaryType && Types.size() > 1) {
+    Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_invalid_combination);
+    return;
   }
+
+  if (HasUnaryType && HasSiFiveCLICType) {
+    Diag(AL.getLoc(), diag::err_riscv_attribute_interrupt_invalid_combination);
+    return;
+  }
+
+  // "machine" is the default, if nothing is specified.
+  if (AL.getNumArgs() == 0)
+    Types.insert(RISCVInterruptAttr::machine);
+
+  const TargetInfo &TI = getASTContext().getTargetInfo();
+  llvm::StringMap<bool> FunctionFeatureMap;
+  getASTContext().getFunctionFeatureMap(FunctionFeatureMap,
+                                        dyn_cast<FunctionDecl>(D));
+
+  auto HasFeature = [&](StringRef FeatureName) -> bool {
+    return TI.hasFeature(FeatureName) || FunctionFeatureMap.lookup(FeatureName);
   };
 
-  D->addAttr(::new (getASTContext())
-                 RISCVInterruptAttr(getASTContext(), AL, Kind));
+  for (RISCVInterruptAttr::InterruptType Type : Types) {
+    switch (Type) {
+    // The QCI interrupt types require Xqciint
+    case RISCVInterruptAttr::qcinest:
+    case RISCVInterruptAttr::qcinonest: {
+      if (!HasFeature("experimental-xqciint")) {
+        Diag(AL.getLoc(),
+             diag::err_riscv_attribute_interrupt_requires_extension)
+            << RISCVInterruptAttr::ConvertInterruptTypeToStr(Type) << "Xqciint";
+        return;
+      }
+    } break;
+    // The SiFive CLIC interrupt types require Xsfmclic
+    case RISCVInterruptAttr::SiFiveCLICPreemptible:
+    case RISCVInterruptAttr::SiFiveCLICStackSwap: {
+      if (!HasFeature("experimental-xsfmclic")) {
+        Diag(AL.getLoc(),
+             diag::err_riscv_attribute_interrupt_requires_extension)
+            << RISCVInterruptAttr::ConvertInterruptTypeToStr(Type)
+            << "XSfmclic";
+        return;
+      }
+    } break;
+    default:
+      break;
+    }
+  }
+
+  SmallVector<RISCVInterruptAttr::InterruptType, 2> TypesVec(Types.begin(),
+                                                             Types.end());
+
+  D->addAttr(::new (getASTContext()) RISCVInterruptAttr(
+      getASTContext(), AL, TypesVec.data(), TypesVec.size()));
 }
 
 bool SemaRISCV::isAliasValid(unsigned BuiltinID, StringRef AliasName) {
diff --git a/clang/test/Driver/print-supported-extensions-riscv.c b/clang/test/Driver/print-supported-extensions-riscv.c
index 35de2820ef84f..ca695221dee9d 100644
--- a/clang/test/Driver/print-supported-extensions-riscv.c
+++ b/clang/test/Driver/print-supported-extensions-riscv.c
@@ -213,6 +213,8 @@
 // CHECK-NEXT:     xqcisls              0.2       'Xqcisls' (Qualcomm uC Scaled Load Store Extension)
 // CHECK-NEXT:     xrivosvisni          0.1       'XRivosVisni' (Rivos Vector Integer Small New)
 // CHECK-NEXT:     xrivosvizip          0.1       'XRivosVizip' (Rivos Vector Register Zips)
+// CHECK-NEXT:     xsfmclic             0.1       'XSfmclic' (SiFive CLIC Machine-mode CSRs)
+// CHECK-NEXT:     xsfsclic             0.1       'XSfsclic' (SiFive CLIC Supervisor-mode CSRs)
 // CHECK-EMPTY:
 // CHECK-NEXT: Supported Profiles
 // CHECK-NEXT:     rva20s64
diff --git a/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...
[truncated]

@jrtc27
Copy link
Collaborator

jrtc27 commented Mar 21, 2025

Is there a reason why these aren't lowercase like the rest?

@lenary
Copy link
Member Author

lenary commented Mar 21, 2025

Is there a reason why these aren't lowercase like the rest?

They match the existing names documented in https://starfivetech.com/uploads/sifive-interrupt-cookbook-v1p2.pdf - we could check them in clang case-insensitively, though.

@lenary
Copy link
Member Author

lenary commented Mar 21, 2025

For Info, the last time this was proposed was in 2020: https://reviews.llvm.org/D79521 - at that time, no vendor extensions had been accepted upstream, and there was not yet a policy for upstream supporting vendor extensions.

Comment on lines 490 to 505
let FeaturesRequired = [{ {RISCV::FeatureVendorXSfmclic} }] in {
def : SysReg<"mtvt", 0x307>;
def : SysReg<"mnxti", 0x345>;
def : SysReg<"mintstatus", 0x346>;
def : SysReg<"mscratchcsw", 0x348>;
def : SysReg<"mscratchcswl", 0x349>;
}

// XSfsclic
let FeaturesRequired = [{ {RISCV::FeatureVendorXSfsclic} }] in {
def : SysReg<"stvt", 0x107>;
def : SysReg<"snxti", 0x145>;
def : SysReg<"sintstatus", 0x146>;
def : SysReg<"sscratchcsw", 0x148>;
def : SysReg<"sscratchcswl", 0x149>;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am little hesitate here, we should have sf. prefix here in theory....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to add this, I didn't because I presumed there exists legacy code which uses these names. If you want, I could add the prefixes to these, and then add a copy as deprecated aliases?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the call, I think we've concluded to go ahead with prefixed versions at least in this PR. We can loop back to discuss separately if there's demand to support the unprefixed form, but ideally we can do sf.* and if people need the old version for downstream users they can carry the alias downstream.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this on the RISC-V LLVM Sync-up today. I will prefix these, and not add deprecated aliases.

@lenary
Copy link
Member Author

lenary commented Apr 10, 2025

@kito-cheng sorted, we now hard error (without a crash trace) if you try to combine frame pointers and preemptible interrupts

Copy link
Member

@kito-cheng kito-cheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :)

@lenary
Copy link
Member Author

lenary commented Apr 25, 2025

I've resolved the merge conflict so I can merge this, now I'm back at work after some travel. I'll let some pre-commit checks run before I finally hit merge though.

.setMIFlag(MachineInstr::FrameSetup);
BuildMI(MBB, MBBI, DL, TII->get(RISCV::CSRRS))
.addReg(RISCV::X9, RegState::Define)
.addImm(RISCVSysReg::lookupSysRegByName("MEPC")->Encoding)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use RISCVSysReg::mepc here to avoid the table lookup?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I thought I had got these. Yes definitely.

@lenary lenary merged commit cfc5baf into llvm:main Apr 26, 2025
10 of 12 checks passed
@lenary lenary deleted the pr/riscv-sifive-interrupts branch April 26, 2025 00:12
@llvm-ci
Copy link
Collaborator

llvm-ci commented Apr 26, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-expensive-checks-debian running on gribozavr4 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/16/builds/17991

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/RISCV/sifive-interrupt-attr.ll' FAILED ********************
Exit Code: 2

Command Output (stderr):
--
/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/llc -mtriple riscv32-unknown-elf -mattr=+experimental-xsfmclic -o - /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll    | /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll --check-prefix=RV32 # RUN: at line 2
+ /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/llc -mtriple riscv32-unknown-elf -mattr=+experimental-xsfmclic -o - /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll
+ /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll --check-prefix=RV32

# After Prologue/Epilogue Insertion & Frame Finalization
# Machine code for function preemptible_empty: NoPHIs, TracksLiveness, NoVRegs, TiedOpsRewritten, TracksDebugUserValues
Frame Objects:
  fi#0: size=4, align=4, at location [SP-4]
  fi#1: size=4, align=4, at location [SP-8]

bb.0 (%ir-block.0):
  $x2 = frame-setup ADDI $x2, -16
  frame-setup CFI_INSTRUCTION def_cfa_offset 16
  frame-setup SW killed $x8, $x2, 12 :: (store (s32) into %stack.0)
  frame-setup SW killed $x9, $x2, 8 :: (store (s32) into %stack.1)
  $x8 = frame-setup CSRRS 834, $x0
  $x9 = frame-setup CSRRS 833, $x0
  frame-setup CSRRSI $x0, 768, 8
  frame-setup CSRRCI $x0, 768, 8
  frame-setup CSRRW $x0, 833, killed $x9
  frame-setup CSRRW $x0, 834, killed $x8
  $x9 = frame-setup LW $x2, 8 :: (load (s32) from %stack.1)
  $x8 = frame-setup LW $x2, 12 :: (load (s32) from %stack.0)
  $x2 = frame-destroy ADDI $x2, 16
  frame-destroy CFI_INSTRUCTION def_cfa_offset 0
  MRET

# End machine code for function preemptible_empty.

*** Bad machine code: Explicit definition marked as use ***
- function:    preemptible_empty
- basic block: %bb.0  (0xa8170f0)
- instruction: frame-setup CSRRSI $x0, 768, 8
- operand 0:   $x0

*** Bad machine code: Explicit definition marked as use ***
- function:    preemptible_empty
- basic block: %bb.0  (0xa8170f0)
- instruction: frame-setup CSRRCI $x0, 768, 8
- operand 0:   $x0

*** Bad machine code: Explicit definition marked as use ***
- function:    preemptible_empty
- basic block: %bb.0  (0xa8170f0)
- instruction: frame-setup CSRRW $x0, 833, killed $x9
...

@llvm-ci
Copy link
Collaborator

llvm-ci commented Apr 26, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-expensive-checks-ubuntu running on as-builder-4 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/187/builds/5614

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/RISCV/sifive-interrupt-attr.ll' FAILED ********************
Exit Code: 2

Command Output (stderr):
--
/home/buildbot/worker/as-builder-4/ramdisk/expensive-checks/build/bin/llc -mtriple riscv32-unknown-elf -mattr=+experimental-xsfmclic -o - /home/buildbot/worker/as-builder-4/ramdisk/expensive-checks/llvm-project/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll    | /home/buildbot/worker/as-builder-4/ramdisk/expensive-checks/build/bin/FileCheck /home/buildbot/worker/as-builder-4/ramdisk/expensive-checks/llvm-project/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll --check-prefix=RV32 # RUN: at line 2
+ /home/buildbot/worker/as-builder-4/ramdisk/expensive-checks/build/bin/FileCheck /home/buildbot/worker/as-builder-4/ramdisk/expensive-checks/llvm-project/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll --check-prefix=RV32
+ /home/buildbot/worker/as-builder-4/ramdisk/expensive-checks/build/bin/llc -mtriple riscv32-unknown-elf -mattr=+experimental-xsfmclic -o - /home/buildbot/worker/as-builder-4/ramdisk/expensive-checks/llvm-project/llvm/test/CodeGen/RISCV/sifive-interrupt-attr.ll

# After Prologue/Epilogue Insertion & Frame Finalization
# Machine code for function preemptible_empty: NoPHIs, TracksLiveness, NoVRegs, TiedOpsRewritten, TracksDebugUserValues
Frame Objects:
  fi#0: size=4, align=4, at location [SP-4]
  fi#1: size=4, align=4, at location [SP-8]

bb.0 (%ir-block.0):
  $x2 = frame-setup ADDI $x2, -16
  frame-setup CFI_INSTRUCTION def_cfa_offset 16
  frame-setup SW killed $x8, $x2, 12 :: (store (s32) into %stack.0)
  frame-setup SW killed $x9, $x2, 8 :: (store (s32) into %stack.1)
  $x8 = frame-setup CSRRS 834, $x0
  $x9 = frame-setup CSRRS 833, $x0
  frame-setup CSRRSI $x0, 768, 8
  frame-setup CSRRCI $x0, 768, 8
  frame-setup CSRRW $x0, 833, killed $x9
  frame-setup CSRRW $x0, 834, killed $x8
  $x9 = frame-setup LW $x2, 8 :: (load (s32) from %stack.1)
  $x8 = frame-setup LW $x2, 12 :: (load (s32) from %stack.0)
  $x2 = frame-destroy ADDI $x2, 16
  frame-destroy CFI_INSTRUCTION def_cfa_offset 0
  MRET

# End machine code for function preemptible_empty.

*** Bad machine code: Explicit definition marked as use ***
- function:    preemptible_empty
- basic block: %bb.0  (0x558ec9f45d80)
- instruction: frame-setup CSRRSI $x0, 768, 8
- operand 0:   $x0

*** Bad machine code: Explicit definition marked as use ***
- function:    preemptible_empty
- basic block: %bb.0  (0x558ec9f45d80)
- instruction: frame-setup CSRRCI $x0, 768, 8
- operand 0:   $x0

*** Bad machine code: Explicit definition marked as use ***
- function:    preemptible_empty
- basic block: %bb.0  (0x558ec9f45d80)
- instruction: frame-setup CSRRW $x0, 833, killed $x9
...

jyli0116 pushed a commit to jyli0116/llvm-project that referenced this pull request Apr 28, 2025
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 <[email protected]>
@vvereschaka
Copy link
Contributor

@lenary,
any progress with fixing sifive-interrupt-attr.ll test on the expensive check builders?

@lenary
Copy link
Member Author

lenary commented Apr 28, 2025

Sorry! I will look at it today. I'll push an xfail in the meantime, to get it green again.

@lenary
Copy link
Member Author

lenary commented Apr 28, 2025

I pushed an xfail as 31bd7a507152 and will look into the failures now.

@vvereschaka
Copy link
Contributor

Thank you @lenary!

lenary added a commit to lenary/llvm-project that referenced this pull request Apr 28, 2025
The expensive checks bots found issues with llvm#132481, due to not setting
defs/uses correctly. In 31bd7a5 I added verify flags, so that the
failure is reproduced without requiring expensive checks, and xfailed
the test.

This change:
- Ensures that registers are correctly marked as defs/uses.
- Removes the xfail.
- Leaves the tests with `-verify-machineinstrs` which should have been
  present originally.
lenary added a commit that referenced this pull request Apr 29, 2025
The expensive checks bots found issues with #132481, due to not setting
defs/uses correctly. In 31bd7a5 I added verify flags, so that the
failure is reproduced without requiring expensive checks, and xfailed
the test.

This change:
- Ensures that registers are correctly marked as defs/uses.
- Removes the xfail.
- Leaves the tests with `-verify-machineinstrs` which should have been
present originally.
gizmondo pushed a commit to gizmondo/llvm-project that referenced this pull request Apr 29, 2025
The expensive checks bots found issues with llvm#132481, due to not setting
defs/uses correctly. In 31bd7a5 I added verify flags, so that the
failure is reproduced without requiring expensive checks, and xfailed
the test.

This change:
- Ensures that registers are correctly marked as defs/uses.
- Removes the xfail.
- Leaves the tests with `-verify-machineinstrs` which should have been
present originally.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
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 <[email protected]>
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
The expensive checks bots found issues with llvm#132481, due to not setting
defs/uses correctly. In 31bd7a5 I added verify flags, so that the
failure is reproduced without requiring expensive checks, and xfailed
the test.

This change:
- Ensures that registers are correctly marked as defs/uses.
- Removes the xfail.
- Leaves the tests with `-verify-machineinstrs` which should have been
present originally.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
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 <[email protected]>
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
The expensive checks bots found issues with llvm#132481, due to not setting
defs/uses correctly. In 31bd7a5 I added verify flags, so that the
failure is reproduced without requiring expensive checks, and xfailed
the test.

This change:
- Ensures that registers are correctly marked as defs/uses.
- Removes the xfail.
- Leaves the tests with `-verify-machineinstrs` which should have been
present originally.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
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 <[email protected]>
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
The expensive checks bots found issues with llvm#132481, due to not setting
defs/uses correctly. In 31bd7a5 I added verify flags, so that the
failure is reproduced without requiring expensive checks, and xfailed
the test.

This change:
- Ensures that registers are correctly marked as defs/uses.
- Removes the xfail.
- Leaves the tests with `-verify-machineinstrs` which should have been
present originally.
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
The expensive checks bots found issues with llvm#132481, due to not setting
defs/uses correctly. In 31bd7a5 I added verify flags, so that the
failure is reproduced without requiring expensive checks, and xfailed
the test.

This change:
- Ensures that registers are correctly marked as defs/uses.
- Removes the xfail.
- Leaves the tests with `-verify-machineinstrs` which should have been
present originally.
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request May 9, 2025
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 <[email protected]>
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request May 9, 2025
The expensive checks bots found issues with llvm#132481, due to not setting
defs/uses correctly. In 31bd7a5 I added verify flags, so that the
failure is reproduced without requiring expensive checks, and xfailed
the test.

This change:
- Ensures that registers are correctly marked as defs/uses.
- Removes the xfail.
- Leaves the tests with `-verify-machineinstrs` which should have been
present originally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:RISC-V clang:codegen IR generation bugs: mangling, exceptions, etc. clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants