https://github.com/4vtomat updated 
https://github.com/llvm/llvm-project/pull/206260

>From 1867ae4b9f3d2e901829a7061f2dbfa51526c2c6 Mon Sep 17 00:00:00 2001
From: Brandon Wu <[email protected]>
Date: Mon, 4 May 2026 15:27:57 +0900
Subject: [PATCH 1/2] [RISCV][clang] Support XSfmm ABI attributes

This patch introduces a new attribute keyword to describe the
architecture state for RISC-V, currently this is used by XSfmm matrix
state, detail usage is described in SiFive documentation: 
https://www.sifive.com/document-file/xsfmm-matrix-extensions-specification

We add five new function type attributes:
  1. __riscv_in("xsfmm"): function reads from matrix state
  2. __riscv_out("xsfmm"): function writes to matrix state
  3. __riscv_inout("xsfmm"): function reads and writes matrix state
  4. __riscv_preserves("xsfmm"): function doesn't read or write state
  5. __riscv_new("xsfmm"): function initiates new matrix state

TODO: Support __xsfmm_preserves statement attribute. We should allow
non user-defined functions that are xsfmm unaware and is proved to not reading
or writing the state, e.g. libc, libm, etc. One of the approach is
providing statement attribute, e.g. __xsfmm_preserves
printf("hello\n");, to make sema checking recognize and be aware of this
call.
---
 clang/include/clang/AST/TypeBase.h            | 102 ++++++++++++-
 clang/include/clang/Basic/Attr.td             |  35 +++++
 clang/include/clang/Basic/AttrDocs.td         |  67 +++++++++
 .../clang/Basic/DiagnosticSemaKinds.td        |   9 ++
 clang/lib/AST/ASTContext.cpp                  |   6 +-
 clang/lib/AST/Type.cpp                        |  18 +++
 clang/lib/AST/TypePrinter.cpp                 |   5 +
 clang/lib/Sema/SemaChecking.cpp               | 100 ++++++++++++
 clang/lib/Sema/SemaType.cpp                   | 102 +++++++++++++
 clang/test/Sema/sifive-xsfmm-func-attr.c      | 142 ++++++++++++++++++
 10 files changed, 577 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/Sema/sifive-xsfmm-func-attr.c

diff --git a/clang/include/clang/AST/TypeBase.h 
b/clang/include/clang/AST/TypeBase.h
index c9658775f0470..9654bdf34a196 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -4816,14 +4816,17 @@ class FunctionType : public Type {
     LLVM_PREFERRED_TYPE(bool)
     unsigned HasArmTypeAttributes : 1;
 
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned HasRISCVTypeAttributes : 1;
+
     LLVM_PREFERRED_TYPE(bool)
     unsigned EffectsHaveConditions : 1;
     unsigned NumFunctionEffects : 4;
 
     FunctionTypeExtraBitfields()
         : NumExceptionType(0), HasExtraAttributeInfo(false),
-          HasArmTypeAttributes(false), EffectsHaveConditions(false),
-          NumFunctionEffects(0) {}
+          HasArmTypeAttributes(false), HasRISCVTypeAttributes(false),
+          EffectsHaveConditions(false), NumFunctionEffects(0) {}
   };
 
   /// A holder for extra information from attributes which aren't part of an
@@ -4869,6 +4872,46 @@ class FunctionType : public Type {
     ARM_InOut = 4,
   };
 
+  /// RISC-V type attributes for states.
+  enum RISCVTypeAttributes : uint8_t {
+    RISCVNormalFunction = 0,
+
+    // Describes the value of the xsfmm tile state using RISCVStateValue.
+    // Each tile gets 3 bits to store its state.
+    RISCVXsfmmShift = 0,
+    RISCVXsfmmMask = 0b111 << RISCVXsfmmShift,
+
+    // Currently using 3 bits for xsfmm
+    RISCVAttributeMask = 0b111
+  };
+
+  enum RISCVStateValue : unsigned {
+    RISCVNone = 0,
+    RISCVIn = 1,
+    RISCVOut = 2,
+    RISCVInOut = 3,
+    RISCVPreserves = 4,
+    RISCVNew = 5,
+  };
+
+  static const char *ToRISCVStateString(RISCVStateValue St) {
+    switch (St) {
+    case RISCVNone:
+      return "none";
+    case RISCVIn:
+      return "__riscv_in";
+    case RISCVOut:
+      return "__riscv_out";
+    case RISCVInOut:
+      return "__riscv_inout";
+    case RISCVPreserves:
+      return "__riscv_preserves";
+    case RISCVNew:
+      return "__riscv_new";
+    }
+    llvm_unreachable("Invalid RISCV state value");
+  }
+
   static ArmStateValue getArmZAState(unsigned AttrBits) {
     return static_cast<ArmStateValue>((AttrBits & SME_ZAMask) >> SME_ZAShift);
   }
@@ -4877,6 +4920,11 @@ class FunctionType : public Type {
     return static_cast<ArmStateValue>((AttrBits & SME_ZT0Mask) >> 
SME_ZT0Shift);
   }
 
+  static RISCVStateValue getRISCVXsfmmState(unsigned AttrBits) {
+    return static_cast<RISCVStateValue>((AttrBits & RISCVXsfmmMask) >>
+                                        RISCVXsfmmShift);
+  }
+
   /// A holder for Arm type attributes as described in the Arm C/C++
   /// Language extensions which are not particularly common to all
   /// types and therefore accounted separately from FunctionTypeBitfields.
@@ -4889,6 +4937,13 @@ class FunctionType : public Type {
     FunctionTypeArmAttributes() : AArch64SMEAttributes(SME_NormalFunction) {}
   };
 
+  struct alignas(void *) FunctionTypeRISCVAttributes {
+    LLVM_PREFERRED_TYPE(RISCVTypeAttributes)
+    unsigned RISCVAttributes : 3;
+
+    FunctionTypeRISCVAttributes() : RISCVAttributes(RISCVNormalFunction) {}
+  };
+
 protected:
   FunctionType(TypeClass tc, QualType res, QualType Canonical,
                TypeDependence Dependence, ExtInfo Info)
@@ -5366,9 +5421,11 @@ class FunctionProtoType final
           FunctionProtoType, QualType, SourceLocation,
           FunctionType::FunctionTypeExtraBitfields,
           FunctionType::FunctionTypeExtraAttributeInfo,
-          FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
-          Expr *, FunctionDecl *, FunctionType::ExtParameterInfo, Qualifiers,
-          FunctionEffect, EffectConditionExpr> {
+          FunctionType::FunctionTypeArmAttributes,
+          FunctionType::FunctionTypeRISCVAttributes,
+          FunctionType::ExceptionType, Expr *, FunctionDecl *,
+          FunctionType::ExtParameterInfo, Qualifiers, FunctionEffect,
+          EffectConditionExpr> {
   friend class ASTContext; // ASTContext creates these.
   friend TrailingObjects;
 
@@ -5471,14 +5528,18 @@ class FunctionProtoType final
     unsigned CFIUncheckedCallee : 1;
     LLVM_PREFERRED_TYPE(AArch64SMETypeAttributes)
     unsigned AArch64SMEAttributes : 9;
+    LLVM_PREFERRED_TYPE(RISCVTypeAttributes)
+    unsigned RISCVAttributes : 3;
 
     ExtProtoInfo()
         : Variadic(false), HasTrailingReturn(false), CFIUncheckedCallee(false),
-          AArch64SMEAttributes(SME_NormalFunction) {}
+          AArch64SMEAttributes(SME_NormalFunction),
+          RISCVAttributes(RISCVNormalFunction) {}
 
     ExtProtoInfo(CallingConv CC)
         : ExtInfo(CC), Variadic(false), HasTrailingReturn(false),
-          CFIUncheckedCallee(false), AArch64SMEAttributes(SME_NormalFunction) 
{}
+          CFIUncheckedCallee(false), AArch64SMEAttributes(SME_NormalFunction),
+          RISCVAttributes(RISCVNormalFunction) {}
 
     ExtProtoInfo withExceptionSpec(const ExceptionSpecInfo &ESI) {
       ExtProtoInfo Result(*this);
@@ -5495,6 +5556,7 @@ class FunctionProtoType final
     bool requiresFunctionProtoTypeExtraBitfields() const {
       return ExceptionSpec.Type == EST_Dynamic ||
              requiresFunctionProtoTypeArmAttributes() ||
+             requiresFunctionProtoTypeRISCVAttributes() ||
              requiresFunctionProtoTypeExtraAttributeInfo() ||
              !FunctionEffects.empty();
     }
@@ -5513,6 +5575,14 @@ class FunctionProtoType final
       else
         AArch64SMEAttributes &= ~Kind;
     }
+
+    bool requiresFunctionProtoTypeRISCVAttributes() const {
+      return RISCVAttributes != RISCVNormalFunction;
+    }
+
+    void setRISCVAttribute(RISCVTypeAttributes Kind) {
+      RISCVAttributes |= Kind;
+    }
   };
 
 private:
@@ -5528,6 +5598,11 @@ class FunctionProtoType final
     return hasArmTypeAttributes();
   }
 
+  unsigned
+  numTrailingObjects(OverloadToken<FunctionTypeRISCVAttributes>) const {
+    return hasRISCVTypeAttributes();
+  }
+
   unsigned numTrailingObjects(OverloadToken<FunctionTypeExtraBitfields>) const 
{
     return hasExtraBitfields();
   }
@@ -5641,6 +5716,12 @@ class FunctionProtoType final
                ->HasArmTypeAttributes;
   }
 
+  bool hasRISCVTypeAttributes() const {
+    return FunctionTypeBits.HasExtraBitfields &&
+           getTrailingObjects<FunctionTypeExtraBitfields>()
+               ->HasRISCVTypeAttributes;
+  }
+
   bool hasExtQualifiers() const {
     return FunctionTypeBits.HasExtQuals;
   }
@@ -5670,6 +5751,7 @@ class FunctionProtoType final
     EPI.ExtParameterInfos = getExtParameterInfosOrNull();
     EPI.ExtraAttributeInfo = getExtraAttributeInfo();
     EPI.AArch64SMEAttributes = getAArch64SMEAttributes();
+    EPI.RISCVAttributes = getRISCVAttributes();
     EPI.FunctionEffects = getFunctionEffects();
     return EPI;
   }
@@ -5872,6 +5954,12 @@ class FunctionProtoType final
         ->AArch64SMEAttributes;
   }
 
+  unsigned getRISCVAttributes() const {
+    if (!hasRISCVTypeAttributes())
+      return RISCVNormalFunction;
+    return getTrailingObjects<FunctionTypeRISCVAttributes>()->RISCVAttributes;
+  }
+
   ExtParameterInfo getExtParameterInfo(unsigned I) const {
     assert(I < getNumParams() && "parameter index out of range");
     if (hasExtParameterInfos())
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index a222092cd42cf..9e78094640b33 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3583,6 +3583,41 @@ def RISCVVLSCC: DeclOrTypeAttr, 
TargetSpecificAttr<TargetRISCV> {
  let Documentation = [RISCVVLSCCDocs];
 }
 
+def RISCVIn : TypeAttr, TargetSpecificAttr<TargetRISCV> {
+  let Spellings = [RegularKeyword<"__riscv_in">];
+  let Args = [VariadicStringArgument<"InArgs">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [RISCVInDocs];
+}
+
+def RISCVOut : TypeAttr, TargetSpecificAttr<TargetRISCV> {
+  let Spellings = [RegularKeyword<"__riscv_out">];
+  let Args = [VariadicStringArgument<"OutArgs">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [RISCVOutDocs];
+}
+
+def RISCVInOut : TypeAttr, TargetSpecificAttr<TargetRISCV> {
+  let Spellings = [RegularKeyword<"__riscv_inout">];
+  let Args = [VariadicStringArgument<"InOutArgs">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [RISCVInOutDocs];
+}
+
+def RISCVPreserves : TypeAttr, TargetSpecificAttr<TargetRISCV> {
+  let Spellings = [RegularKeyword<"__riscv_preserves">];
+  let Args = [VariadicStringArgument<"PreserveArgs">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [RISCVPreservesDocs];
+}
+
+def RISCVNew : TypeAttr, TargetSpecificAttr<TargetRISCV> {
+  let Spellings = [RegularKeyword<"__riscv_new">];
+  let Args = [VariadicStringArgument<"NewArgs">];
+  let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+  let Documentation = [RISCVNewDocs];
+}
+
 def Target : InheritableAttr {
   let Spellings = [GCC<"target">];
   let Args = [StringArgument<"featuresStr">];
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 04362de2d5be2..c86891c3c5cea 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6787,6 +6787,73 @@ the ABI. This variant aims to pass fixed-length vectors 
via vector registers,
 if possible, rather than through general-purpose registers.}];
 }
 
+def RISCVInDocs : Documentation {
+  let Category = DocCatFunction;
+  let Heading = "__riscv_in";
+  let Content = [{
+The ``__riscv_in(S)`` attribute indicates that a function reads from state S.
+This attribute is usually used in RISC-V matrix extensions to specify that the
+function will read from a matrix state passed as arguments.
+This helps compiler checks to prevent common programming errors that could lead
+to undefined behavior, data corruption, or incorrect computation results when
+working with matrix operations.
+  }];
+}
+
+def RISCVOutDocs : Documentation {
+  let Category = DocCatFunction;
+  let Heading = "__riscv_out";
+  let Content = [{
+The ``__riscv_out(S)`` attribute indicates that a function writes to state S.
+This attribute is usually used in RISC-V matrix extensions to specify that the
+function will write to a matrix state passed as arguments.
+This helps compiler checks to prevent common programming errors that could lead
+to undefined behavior, data corruption, or incorrect computation results when
+working with matrix operations.
+  }];
+}
+
+def RISCVInOutDocs : Documentation {
+  let Category = DocCatFunction;
+  let Heading = "__riscv_inout";
+  let Content = [{
+The ``__riscv_inout(S)`` attribute indicates that a function reads from and
+writes to state S.
+This attribute is usually used in RISC-V matrix extensions to specify that the
+function will read from and write to a matrix state passed as arguments.
+This helps compiler checks to prevent common programming errors that could lead
+to undefined behavior, data corruption, or incorrect computation results when
+working with matrix operations.
+  }];
+}
+
+def RISCVPreservesDocs : Documentation {
+  let Category = DocCatFunction;
+  let Heading = "__riscv_preserves";
+  let Content = [{
+The ``__riscv_preserves(S)`` attribute indicates that a function neither reads
+from nor writes to state S.
+This attribute is usually used in RISC-V matrix extensions to specify that the
+function will not read from or write to a matrix state passed as arguments.
+This helps compiler checks to prevent common programming errors that could lead
+to undefined behavior, data corruption, or incorrect computation results when
+working with matrix operations.
+  }];
+}
+
+def RISCVNewDocs : Documentation {
+  let Category = DocCatFunction;
+  let Heading = "__riscv_new";
+  let Content = [{
+The ``__riscv_new(S)`` attribute indicates that a function initiates a new 
state.
+This attribute is usually used in RISC-V matrix extensions to specify that the
+function will initiate a new matrix state passed as arguments.
+This helps compiler checks to prevent common programming errors that could lead
+to undefined behavior, data corruption, or incorrect computation results when
+working with matrix operations.
+  }];
+}
+
 def PreferredNameDocs : Documentation {
   let Category = DocCatDecl;
   let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f7fba8df1e4d7..42a55dcdf0128 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13619,6 +13619,15 @@ def err_riscv_attribute_interrupt_requires_extension : 
Error<
 def err_riscv_attribute_interrupt_invalid_combination : Error<
   "RISC-V 'interrupt' attribute contains invalid combination of interrupt 
types">;
 def err_riscv_builtin_invalid_twiden : Error<"RISC-V XSfmm twiden must be 1, 2 
or 4">;
+// RISC-V errors
+def err_riscv_call_invalid_features : Error<
+  "call to an attributed function requires %0">;
+def err_missing_riscv_state : Error<"missing state for %0">;
+def err_unknown_riscv_state : Error<"unknown state '%0'">;
+def err_conflicting_attributes_riscv_state : Error<
+  "conflicting attribute. Description: %0">;
+def err_mutually_exclusive_attributes_riscv_state : Error<
+  "mutually exclusive attributes for state '%0'">;
 
 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/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index abf0cd5e18c2b..c02b43c947072 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5161,12 +5161,14 @@ QualType ASTContext::getFunctionTypeInternal(
   size_t Size = FunctionProtoType::totalSizeToAlloc<
       QualType, SourceLocation, FunctionType::FunctionTypeExtraBitfields,
       FunctionType::FunctionTypeExtraAttributeInfo,
-      FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
+      FunctionType::FunctionTypeArmAttributes,
+      FunctionType::FunctionTypeRISCVAttributes, FunctionType::ExceptionType,
       Expr *, FunctionDecl *, FunctionProtoType::ExtParameterInfo, Qualifiers,
       FunctionEffect, EffectConditionExpr>(
       NumArgs, EPI.Variadic, EPI.requiresFunctionProtoTypeExtraBitfields(),
       EPI.requiresFunctionProtoTypeExtraAttributeInfo(),
-      EPI.requiresFunctionProtoTypeArmAttributes(), ESH.NumExceptionType,
+      EPI.requiresFunctionProtoTypeArmAttributes(),
+      EPI.requiresFunctionProtoTypeRISCVAttributes(), ESH.NumExceptionType,
       ESH.NumExprPtr, ESH.NumFunctionDeclPtr,
       EPI.ExtParameterInfos ? NumArgs : 0,
       EPI.TypeQuals.hasNonFastQualifiers() ? 1 : 0, EPI.FunctionEffects.size(),
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index b7bef40ca89f3..f704f748f7055 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3819,6 +3819,15 @@ FunctionProtoType::FunctionProtoType(QualType result, 
ArrayRef<QualType> params,
     ExtraBits.HasArmTypeAttributes = true;
   }
 
+  if (epi.requiresFunctionProtoTypeRISCVAttributes()) {
+    auto &RISCVTypeAttrs = *getTrailingObjects<FunctionTypeRISCVAttributes>();
+    RISCVTypeAttrs = FunctionTypeRISCVAttributes();
+
+    // Also set the bit in FunctionTypeExtraBitfields
+    auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>();
+    ExtraBits.HasRISCVTypeAttributes = true;
+  }
+
   // Fill in the trailing argument array.
   auto *argSlot = getTrailingObjects<QualType>();
   for (unsigned i = 0; i != getNumParams(); ++i) {
@@ -3835,6 +3844,14 @@ FunctionProtoType::FunctionProtoType(QualType result, 
ArrayRef<QualType> params,
     ArmTypeAttrs.AArch64SMEAttributes = epi.AArch64SMEAttributes;
   }
 
+  // Propagate the RISC-V state attributes.
+  if (epi.RISCVAttributes != RISCVNormalFunction) {
+    auto &RISCVTypeAttrs = *getTrailingObjects<FunctionTypeRISCVAttributes>();
+    assert(epi.RISCVAttributes <= RISCVAttributeMask &&
+           "Not enough bits to encode RISC-V attributes");
+    RISCVTypeAttrs.RISCVAttributes = epi.RISCVAttributes;
+  }
+
   // Fill in the exception type array if present.
   if (getExceptionSpecType() == EST_Dynamic) {
     auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>();
@@ -4071,6 +4088,7 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID 
&ID, QualType Result,
   ID.AddInteger((EffectCount << 3) | (HasConds << 2) |
                 (epi.AArch64SMEAttributes << 1) | epi.HasTrailingReturn);
   ID.AddInteger(epi.CFIUncheckedCallee);
+  ID.AddInteger(epi.RISCVAttributes);
 
   for (unsigned Idx = 0; Idx != EffectCount; ++Idx) {
     ID.AddInteger(epi.FunctionEffects.Effects[Idx].toOpaqueInt32());
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index e8fbffb9f954d..04d990d028694 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2060,6 +2060,11 @@ void TypePrinter::printAttributedAfter(const 
AttributedType *T,
   case attr::ArmOut:
   case attr::ArmInOut:
   case attr::ArmPreserves:
+  case attr::RISCVIn:
+  case attr::RISCVOut:
+  case attr::RISCVInOut:
+  case attr::RISCVPreserves:
+  case attr::RISCVNew:
   case attr::NonBlocking:
   case attr::NonAllocating:
   case attr::Blocking:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index ec4a9037f5c23..13a15f4bb0b83 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -4536,6 +4536,106 @@ void Sema::checkCall(NamedDecl *FDecl, const 
FunctionProtoType *Proto,
         Diag(Loc, diag::note_sme_use_preserves_za);
       }
     }
+
+    // Check if there's any conflicting call for every state, it should not be
+    // any conflict if caller and callee are in different state.
+
+    if (CallerFD &&
+        (!FD || !FD->getBuiltinID() ||
+         Context.BuiltinInfo.isLibFunction(FD->getBuiltinID()) ||
+         Context.BuiltinInfo.isPredefinedLibFunction(FD->getBuiltinID()))) {
+      QualType CallerType = CallerFD->getType();
+      if (!CallerType.isNull()) {
+        if (const auto *FPT = CallerType->getAs<FunctionProtoType>()) {
+          FunctionProtoType::ExtProtoInfo CallerExtInfo =
+              FPT->getExtProtoInfo();
+          llvm::StringMap<bool> CallerFeatureMap;
+          if (CallerExtInfo.RISCVAttributes & FunctionType::RISCVAttributeMask)
+            Context.getFunctionFeatureMap(CallerFeatureMap, CallerFD);
+          // tuple(CallerAttr, CalleeAttr, required feature)
+          const std::tuple<FunctionType::RISCVStateValue,
+                           FunctionType::RISCVStateValue, StringRef>
+              RISCVStateInfo[] = {
+                  {FunctionType::getRISCVXsfmmState(
+                       CallerExtInfo.RISCVAttributes),
+                   FunctionType::getRISCVXsfmmState(ExtInfo.RISCVAttributes),
+                   "xsfmmbase"}};
+          for (auto [CallerAttr, CalleeAttr, RequiredFeature] :
+               RISCVStateInfo) {
+            // If both caller and callee are not attributed, then we're fine.
+            if (CallerAttr == FunctionType::RISCVNone &&
+                CalleeAttr == FunctionType::RISCVNone)
+              continue;
+
+            if (!Context.getTargetInfo().hasFeature(RequiredFeature) &&
+                !CallerFeatureMap.lookup(RequiredFeature)) {
+              // check if corresponding attributes are enabled.
+              Diag(Loc, diag::err_riscv_call_invalid_features)
+                  << RequiredFeature;
+              continue;
+            }
+
+            switch (CallerAttr) {
+            case FunctionType::RISCVNone:
+              if (CalleeAttr != FunctionType::RISCVNew) {
+                // Check limitation:
+                // 1. Only __riscv_new function can be called in non-attributed
+                // function.
+                Diag(Loc, diag::err_conflicting_attributes_riscv_state)
+                    << "Only __riscv_new function can be called in "
+                       "non-attributed function.";
+              }
+              break;
+            case FunctionType::RISCVIn:
+              if (CalleeAttr != FunctionType::RISCVIn &&
+                  CalleeAttr != FunctionType::RISCVPreserves) {
+                // 2. Function with __riscv_in can only call __riscv_in and
+                // __riscv_preserves function.
+                Diag(Loc, diag::err_conflicting_attributes_riscv_state)
+                    << "Function with __riscv_in can only call __riscv_in and "
+                       "__riscv_preserves function.";
+              }
+              break;
+            case FunctionType::RISCVOut:
+              if (CalleeAttr != FunctionType::RISCVIn &&
+                  CalleeAttr != FunctionType::RISCVOut &&
+                  CalleeAttr != FunctionType::RISCVPreserves) {
+                // 3. Function with __riscv_out can only call
+                // __riscv_in, __riscv_out and __riscv_preserves function.
+                Diag(Loc, diag::err_conflicting_attributes_riscv_state)
+                    << "Function with __riscv_out can only call "
+                       "__riscv_in, __riscv_out and __riscv_preserves "
+                       "function.";
+              }
+              break;
+            case FunctionType::RISCVPreserves:
+              if (CalleeAttr != FunctionType::RISCVPreserves) {
+                // 4. Function with __riscv_preserves can only call
+                // __riscv_preserves function.
+                Diag(Loc, diag::err_conflicting_attributes_riscv_state)
+                    << "Function with __riscv_preserves can only call "
+                       "__riscv_preserves function.";
+              }
+              break;
+            case FunctionType::RISCVNew:
+            case FunctionType::RISCVInOut:
+              if (CalleeAttr == FunctionType::RISCVNone ||
+                  CalleeAttr == FunctionType::RISCVNew) {
+                // Handle remainings: __riscv_new, __riscv_inout
+                // 5. Function with attribute can only call function with
+                // attribute(except for __riscv_new), i.e. __riscv_new and
+                // non-attributed function can not be called in attributed
+                // function.
+                Diag(Loc, diag::err_conflicting_attributes_riscv_state)
+                    << "__riscv_new and non-attributed function can not be "
+                       "called in attributed function.";
+              }
+              break;
+            }
+          }
+        }
+      }
+    }
   }
 
   if (FDecl && FDecl->hasAttr<AllocAlignAttr>()) {
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d2bb312feadc1..b31c900c1a362 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -166,6 +166,11 @@ static void diagnoseBadTypeAttribute(Sema &S, const 
ParsedAttr &attr,
   case ParsedAttr::AT_ArmAgnostic:                                             
\
   case ParsedAttr::AT_AnyX86NoCallerSavedRegisters:                            
\
   case ParsedAttr::AT_AnyX86NoCfCheck:                                         
\
+  case ParsedAttr::AT_RISCVIn:                                                 
\
+  case ParsedAttr::AT_RISCVOut:                                                
\
+  case ParsedAttr::AT_RISCVInOut:                                              
\
+  case ParsedAttr::AT_RISCVPreserves:                                          
\
+  case ParsedAttr::AT_RISCVNew:                                                
\
     CALLING_CONV_ATTRS_CASELIST
 
 // Microsoft-specific type qualifiers.
@@ -8041,6 +8046,51 @@ static bool handleArmStateAttribute(Sema &S,
   return false;
 }
 
+static bool handleRISCVStateAttribute(Sema &S,
+                                      FunctionProtoType::ExtProtoInfo &EPI,
+                                      ParsedAttr &Attr,
+                                      FunctionType::RISCVStateValue State) {
+  if (!Attr.getNumArgs()) {
+    S.Diag(Attr.getLoc(), diag::err_missing_riscv_state) << Attr;
+    Attr.setInvalid();
+    return true;
+  }
+
+  for (unsigned I = 0; I < Attr.getNumArgs(); ++I) {
+    StringRef StateName;
+    SourceLocation LiteralLoc;
+    if (!S.checkStringLiteralArgumentAttr(Attr, I, StateName, &LiteralLoc))
+      return true;
+
+    unsigned Shift;
+    FunctionType::RISCVStateValue ExistingState;
+
+    // Determine which tile state this is and get its shift/mask
+    if (StateName == "xsfmm") {
+      Shift = FunctionType::RISCVXsfmmShift;
+      ExistingState = FunctionType::getRISCVXsfmmState(EPI.RISCVAttributes);
+    } else {
+      S.Diag(LiteralLoc, diag::err_unknown_riscv_state) << StateName;
+      Attr.setInvalid();
+      return true;
+    }
+
+    // __riscv_in, __riscv_out, __riscv_inout, __riscv_preserves, and
+    // __riscv_new are all mutually exclusive for the same state,
+    // so check if there are conflicting attributes.
+    if (ExistingState != FunctionType::RISCVNone && ExistingState != State) {
+      S.Diag(LiteralLoc, diag::err_mutually_exclusive_attributes_riscv_state)
+          << StateName;
+      Attr.setInvalid();
+      return true;
+    }
+
+    EPI.setRISCVAttribute(
+        (FunctionType::RISCVTypeAttributes)((State << Shift)));
+  }
+  return false;
+}
+
 /// Process an individual function attribute.  Returns true to
 /// indicate that the attribute was handled, false if it wasn't.
 static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr 
&attr,
@@ -8293,6 +8343,58 @@ static bool handleFunctionTypeAttr(TypeProcessingState 
&state, ParsedAttr &attr,
     return true;
   }
 
+  if (attr.getKind() == ParsedAttr::AT_RISCVIn ||
+      attr.getKind() == ParsedAttr::AT_RISCVOut ||
+      attr.getKind() == ParsedAttr::AT_RISCVInOut ||
+      attr.getKind() == ParsedAttr::AT_RISCVPreserves ||
+      attr.getKind() == ParsedAttr::AT_RISCVNew) {
+    if (S.CheckAttrTarget(attr))
+      return true;
+
+    if (!unwrapped.isFunctionType())
+      return false;
+
+    const auto *FnTy = unwrapped.get()->getAs<FunctionProtoType>();
+    if (!FnTy) {
+      S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type)
+          << attr << attr.isRegularKeywordAttribute()
+          << ExpectedFunctionWithProtoType;
+      attr.setInvalid();
+      return false;
+    }
+
+    FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo();
+    switch (attr.getKind()) {
+    case ParsedAttr::AT_RISCVIn:
+      if (handleRISCVStateAttribute(S, EPI, attr, FunctionType::RISCVIn))
+        return true;
+      break;
+    case ParsedAttr::AT_RISCVOut:
+      if (handleRISCVStateAttribute(S, EPI, attr, FunctionType::RISCVOut))
+        return true;
+      break;
+    case ParsedAttr::AT_RISCVInOut:
+      if (handleRISCVStateAttribute(S, EPI, attr, FunctionType::RISCVInOut))
+        return true;
+      break;
+    case ParsedAttr::AT_RISCVPreserves:
+      if (handleRISCVStateAttribute(S, EPI, attr, 
FunctionType::RISCVPreserves))
+        return true;
+      break;
+    case ParsedAttr::AT_RISCVNew:
+      if (handleRISCVStateAttribute(S, EPI, attr, FunctionType::RISCVNew))
+        return true;
+      break;
+    default:
+      llvm_unreachable("Unsupported attribute");
+    }
+
+    QualType newtype = S.Context.getFunctionType(FnTy->getReturnType(),
+                                                 FnTy->getParamTypes(), EPI);
+    type = unwrapped.wrap(S, newtype->getAs<FunctionType>());
+    return true;
+  }
+
   if (attr.getKind() == ParsedAttr::AT_NoThrow) {
     // Delay if this is not a function type.
     if (!unwrapped.isFunctionType())
diff --git a/clang/test/Sema/sifive-xsfmm-func-attr.c 
b/clang/test/Sema/sifive-xsfmm-func-attr.c
new file mode 100644
index 0000000000000..c9f84b7847f47
--- /dev/null
+++ b/clang/test/Sema/sifive-xsfmm-func-attr.c
@@ -0,0 +1,142 @@
+// RUN: %clang_cc1 -triple riscv64-none-linux-gnu -target-feature +xsfmmbase 
-Wno-error=implicit-function-declaration -fsyntax-only -verify %s
+
+#include <riscv_vector.h>
+
+int xsfmm_in_callee(void) __riscv_in("xsfmm");
+int xsfmm_out_callee(void) __riscv_out("xsfmm");
+int xsfmm_inout_callee(void) __riscv_inout("xsfmm");
+int xsfmm_preserves_callee(void) __riscv_preserves("xsfmm");
+int xsfmm_new_callee(void) __riscv_new("xsfmm");
+
+void valid_new(void) {
+  xsfmm_new_callee();
+}
+
+void valid_preserves_in_in(void) __riscv_in("xsfmm") {
+  xsfmm_preserves_callee();
+}
+
+void valid_in_in_in(void) __riscv_in("xsfmm") {
+  xsfmm_in_callee();
+}
+
+void valid_in_in_out(void) __riscv_out("xsfmm") {
+  xsfmm_in_callee();
+}
+
+void valid_out_in_out(void) __riscv_out("xsfmm") {
+  xsfmm_out_callee();
+}
+
+void valid_preserves_in_out(void) __riscv_out("xsfmm") {
+  xsfmm_preserves_callee();
+}
+
+void valid_preserves_in_preserves(void) __riscv_preserves("xsfmm") {
+  xsfmm_preserves_callee();
+}
+
+vint32m1_t valid_in_intrinsic(vint32m1_t v, unsigned vl) __riscv_in("xsfmm") {
+  unsigned avl = __riscv_vsetvl_e32m1(vl);
+  return __riscv_vadd(v, v, avl);
+}
+
+vint32m1_t valid_out_intrinsic(vint32m1_t v, unsigned vl) __riscv_out("xsfmm") 
{
+  unsigned avl = __riscv_vsetvl_e32m1(vl);
+  return __riscv_vadd(v, v, avl);
+}
+
+vint32m1_t valid_inout_intrinsic(vint32m1_t v, unsigned vl) 
__riscv_inout("xsfmm") {
+  unsigned avl = __riscv_vsetvl_e32m1(vl);
+  return __riscv_vadd(v, v, avl);
+}
+
+vint32m1_t valid_preserves_intrinsic(vint32m1_t v, unsigned vl) 
__riscv_preserves("xsfmm") {
+  unsigned avl = __riscv_vsetvl_e32m1(vl);
+  return __riscv_vadd(v, v, avl);
+}
+
+vint32m1_t valid_new_intrinsic(vint32m1_t v, unsigned vl) __riscv_new("xsfmm") 
{
+  unsigned avl = __riscv_vsetvl_e32m1(vl);
+  return __riscv_vadd(v, v, avl);
+}
+
+void invalid_mutual_exclusive1(void) __riscv_in("xsfmm") __riscv_out("xsfmm") 
{ // expected-error {{mutually exclusive attributes for state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive2(void) __riscv_in("xsfmm") 
__riscv_inout("xsfmm") { // expected-error {{mutually exclusive attributes for 
state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive3(void) __riscv_in("xsfmm") 
__riscv_preserves("xsfmm") { // expected-error {{mutually exclusive attributes 
for state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive4(void) __riscv_in("xsfmm") __riscv_new("xsfmm") 
{ // expected-error {{mutually exclusive attributes for state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive5(void) __riscv_out("xsfmm") 
__riscv_inout("xsfmm") { // expected-error {{mutually exclusive attributes for 
state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive6(void) __riscv_out("xsfmm") 
__riscv_preserves("xsfmm") { // expected-error {{mutually exclusive attributes 
for state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive7(void) __riscv_out("xsfmm") __riscv_new("xsfmm") 
{ // expected-error {{mutually exclusive attributes for state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive8(void) __riscv_inout("xsfmm") 
__riscv_preserves("xsfmm") { // expected-error {{mutually exclusive attributes 
for state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive9(void) __riscv_inout("xsfmm") 
__riscv_new("xsfmm") { // expected-error {{mutually exclusive attributes for 
state 'xsfmm'}}
+}
+
+void invalid_mutual_exclusive10(void) __riscv_preserves("xsfmm") 
__riscv_new("xsfmm") { // expected-error {{mutually exclusive attributes for 
state 'xsfmm'}}
+}
+
+void invalid_in_in_preserves(void) __riscv_preserves("xsfmm") {
+  xsfmm_in_callee(); // expected-error {{conflicting attribute. Description: 
Function with __riscv_preserves can only call __riscv_preserves function.}}
+}
+
+void invalid_out_in_preserves(void) __riscv_preserves("xsfmm") {
+  xsfmm_out_callee(); // expected-error {{conflicting attribute. Description: 
Function with __riscv_preserves can only call __riscv_preserves function.}}
+}
+
+void invalid_inout_in_preserves(void) __riscv_preserves("xsfmm") {
+  xsfmm_inout_callee(); // expected-error {{conflicting attribute. 
Description: Function with __riscv_preserves can only call __riscv_preserves 
function.}}
+}
+
+void invalid_new_in_preserves(void) __riscv_preserves("xsfmm") {
+  xsfmm_new_callee(); // expected-error {{conflicting attribute. Description: 
Function with __riscv_preserves can only call __riscv_preserves function.}}
+}
+
+void invalid_out_in_in(void) __riscv_in("xsfmm") {
+  xsfmm_out_callee(); // expected-error {{conflicting attribute. Description: 
Function with __riscv_in can only call __riscv_in and __riscv_preserves 
function.}}
+}
+
+void invalid_inout_in_in(void) __riscv_in("xsfmm") {
+  xsfmm_inout_callee(); // expected-error {{conflicting attribute. 
Description: Function with __riscv_in can only call __riscv_in and 
__riscv_preserves function.}}
+}
+
+void invalid_new_in_in(void) __riscv_in("xsfmm") {
+  xsfmm_new_callee(); // expected-error {{conflicting attribute. Description: 
Function with __riscv_in can only call __riscv_in and __riscv_preserves 
function.}}
+}
+
+void invalid_inout_in_out(void) __riscv_out("xsfmm") {
+  xsfmm_inout_callee(); // expected-error {{conflicting attribute. 
Description: Function with __riscv_out can only call __riscv_in, __riscv_out 
and __riscv_preserves function.}}
+}
+
+void invalid_new_in_out(void) __riscv_out("xsfmm") {
+  xsfmm_new_callee(); // expected-error {{conflicting attribute. Description: 
Function with __riscv_out can only call __riscv_in, __riscv_out and 
__riscv_preserves function.}}
+}
+
+void invalid_new_in_new(void) __riscv_new("xsfmm") {
+  xsfmm_new_callee(); // expected-error {{conflicting attribute. Description: 
__riscv_new and non-attributed function can not be called in attributed 
function.}}
+}
+
+void invalid_lib_function_intrinsic(void) __riscv_in("xsfmm") {
+  __builtin_memcpy(NULL, NULL, 0); // expected-error {{conflicting attribute. 
Description: Function with __riscv_in can only call __riscv_in and 
__riscv_preserves function.}}
+}
+
+void invalid_predefined_lib_function(void) __riscv_in("xsfmm") {
+  memcpy(NULL, NULL, 0); // expected-error {{conflicting attribute. 
Description: Function with __riscv_in can only call __riscv_in and 
__riscv_preserves function.}}
+  // expected-warning@-1 {{call to undeclared library function 'memcpy' with 
type 'void *(void *, const void *, __size_t)' (aka 'void *(void *, const void 
*, unsigned long)'); ISO C99 and later do not support implicit function 
declarations}}
+  // expected-note@-2 {{include the header <string.h> or explicitly provide a 
declaration for 'memcpy'}}
+}

>From 798abc343071f6f7259ac9f893f4f51fa2b97818 Mon Sep 17 00:00:00 2001
From: Brandon Wu <[email protected]>
Date: Wed, 8 Jul 2026 19:25:39 -0700
Subject: [PATCH 2/2] fixup! wording

---
 clang/include/clang/AST/TypeBase.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/TypeBase.h 
b/clang/include/clang/AST/TypeBase.h
index 9654bdf34a196..96c66f67b797c 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -4909,7 +4909,7 @@ class FunctionType : public Type {
     case RISCVNew:
       return "__riscv_new";
     }
-    llvm_unreachable("Invalid RISCV state value");
+    llvm_unreachable("Invalid RISC-V state value");
   }
 
   static ArmStateValue getArmZAState(unsigned AttrBits) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to