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
