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/5] [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/5] 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) { >From adf3a3697be13384f6bc0320b1ec8b10df1c1903 Mon Sep 17 00:00:00 2001 From: Brandon Wu <[email protected]> Date: Sun, 12 Jul 2026 23:21:14 -0700 Subject: [PATCH 3/5] fixup! negative test --- clang/test/Sema/sifive-xsfmm-func-attr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/test/Sema/sifive-xsfmm-func-attr.c b/clang/test/Sema/sifive-xsfmm-func-attr.c index c9f84b7847f47..afeb4f1a7d654 100644 --- a/clang/test/Sema/sifive-xsfmm-func-attr.c +++ b/clang/test/Sema/sifive-xsfmm-func-attr.c @@ -140,3 +140,6 @@ void invalid_predefined_lib_function(void) __riscv_in("xsfmm") { // 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'}} } + +void invalid_state(void) __riscv_new("12345") { // expected-error {{unknown state '12345'}} +} >From 5ecac9a82c504914718b7a8bb944d811571a6efd Mon Sep 17 00:00:00 2001 From: Brandon Wu <[email protected]> Date: Wed, 12 Aug 2026 12:15:52 +0800 Subject: [PATCH 4/5] fixup! reject calls in attributed funcs --- clang/lib/CodeGen/CGCall.cpp | 23 +++++ llvm/lib/Target/RISCV/CMakeLists.txt | 1 + llvm/lib/Target/RISCV/RISCV.h | 3 + llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 4 + llvm/lib/Target/RISCV/RISCVStateAttributes.h | 29 ++++++ llvm/lib/Target/RISCV/RISCVStateCheck.cpp | 92 +++++++++++++++++++ llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 3 + .../Target/RISCV/RISCVTargetTransformInfo.cpp | 12 +++ .../Target/RISCV/RISCVTargetTransformInfo.h | 3 + llvm/test/CodeGen/RISCV/O0-pipeline.ll | 1 + llvm/test/CodeGen/RISCV/O3-pipeline.ll | 1 + llvm/test/CodeGen/RISCV/riscv-state-check.ll | 30 ++++++ 12 files changed, 202 insertions(+) create mode 100644 llvm/lib/Target/RISCV/RISCVStateAttributes.h create mode 100644 llvm/lib/Target/RISCV/RISCVStateCheck.cpp create mode 100644 llvm/test/CodeGen/RISCV/riscv-state-check.ll diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 82b374e50fd41..abbe7efc4d6ad 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2045,6 +2045,29 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx, FuncAttrs.addAttribute("aarch64_out_zt0"); if (FunctionType::getArmZT0State(SMEBits) == FunctionType::ARM_InOut) FuncAttrs.addAttribute("aarch64_inout_zt0"); + + unsigned RISCVBits = FPT->getRISCVAttributes(); + switch (FunctionType::getRISCVXsfmmState(RISCVBits)) { + case FunctionType::RISCVNone: + break; + case FunctionType::RISCVIn: + FuncAttrs.addAttribute("riscv_in"); + break; + case FunctionType::RISCVOut: + FuncAttrs.addAttribute("riscv_out"); + break; + case FunctionType::RISCVInOut: + FuncAttrs.addAttribute("riscv_inout"); + break; + case FunctionType::RISCVPreserves: + FuncAttrs.addAttribute("riscv_preserves"); + break; + case FunctionType::RISCVNew: + FuncAttrs.addAttribute("riscv_new"); + break; + default: + llvm_unreachable("Unimplemented RISC-V attribute type"); + } } static void AddAttributesFromOMPAssumes(llvm::AttrBuilder &FuncAttrs, diff --git a/llvm/lib/Target/RISCV/CMakeLists.txt b/llvm/lib/Target/RISCV/CMakeLists.txt index 4a1a21cc9b5cd..03c96a722bde1 100644 --- a/llvm/lib/Target/RISCV/CMakeLists.txt +++ b/llvm/lib/Target/RISCV/CMakeLists.txt @@ -65,6 +65,7 @@ add_llvm_target(RISCVCodeGen RISCVRegisterInfo.cpp RISCVSelectionDAGInfo.cpp RISCVSubtarget.cpp + RISCVStateCheck.cpp RISCVTargetMachine.cpp RISCVTargetObjectFile.cpp RISCVTargetTransformInfo.cpp diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h index 929a8d8f17b4f..6860599420ac7 100644 --- a/llvm/lib/Target/RISCV/RISCV.h +++ b/llvm/lib/Target/RISCV/RISCV.h @@ -134,6 +134,9 @@ FunctionPass *createRISCVVMV0EliminationPass(); void initializeRISCVVMV0EliminationPass(PassRegistry &); void initializeRISCVAsmPrinterPass(PassRegistry &); + +FunctionPass *createRISCVStateCheckPass(); +void initializeRISCVStateCheckPass(PassRegistry &); } // namespace llvm #endif diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index b31a3a7760d3b..ab7cb0eb297a8 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -14,6 +14,7 @@ #include "MCTargetDesc/RISCVBaseInfo.h" #include "MCTargetDesc/RISCVMatInt.h" #include "RISCV.h" +#include "RISCVStateAttributes.h" #include "RISCVMachineFunctionInfo.h" #include "RISCVSubtarget.h" #include "llvm/ADT/STLExtras.h" @@ -3625,6 +3626,9 @@ bool RISCVInstrInfo::isFunctionSafeToOutlineFrom( if (F.hasSection()) return false; + if (RISCVState::hasAttribute(F)) + return false; + // It's safe to outline from MF. return true; } diff --git a/llvm/lib/Target/RISCV/RISCVStateAttributes.h b/llvm/lib/Target/RISCV/RISCVStateAttributes.h new file mode 100644 index 0000000000000..e755190b34156 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVStateAttributes.h @@ -0,0 +1,29 @@ +//=-- RISCVStateAttributes.h - Helper for interpreting RISC-V attributes -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVSTATEATTRIBUTES_H +#define LLVM_LIB_TARGET_RISCV_RISCVSTATEATTRIBUTES_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Function.h" + +namespace llvm { +namespace RISCVState { + +inline constexpr StringLiteral Attributes[] = { + "riscv_in", "riscv_out", "riscv_inout", "riscv_preserves", "riscv_new"}; + +inline bool hasAttribute(const Function &F) { + return any_of(Attributes, [&F](StringRef A) { return F.hasFnAttribute(A); }); +} + +} // namespace RISCVState +} // namespace llvm + +#endif // LLVM_LIB_TARGET_RISCV_RISCVSTATEATTRIBUTES_H diff --git a/llvm/lib/Target/RISCV/RISCVStateCheck.cpp b/llvm/lib/Target/RISCV/RISCVStateCheck.cpp new file mode 100644 index 0000000000000..c9ab99e39ac2b --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVStateCheck.cpp @@ -0,0 +1,92 @@ +//======-- RISCVStateCheck.cpp - Helper for checking RISC-V attributes -======// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +#include "RISCV.h" +#include "RISCVStateAttributes.h" +#include "llvm/CodeGen/MachineFunctionPass.h" + +using namespace llvm; + +#define RISCV_STATE_CHECK_NAME "RISC-V architecture state check" + +namespace { +class RISCVStateCheck : public MachineFunctionPass { +public: + static char ID; + + RISCVStateCheck() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + StringRef getPassName() const override { return RISCV_STATE_CHECK_NAME; } +}; +} // namespace + +char RISCVStateCheck::ID = 0; + +INITIALIZE_PASS(RISCVStateCheck, "riscv-state-check", + RISCV_STATE_CHECK_NAME, false, true) + +static const MachineOperand *getCalleeSymbol(const MachineInstr &MI) { + for (const MachineOperand &MO : MI.operands()) + if (MO.isGlobal() || MO.isSymbol()) + return &MO; + return nullptr; +} + +bool RISCVStateCheck::runOnMachineFunction(MachineFunction &MF) { + const Function &F = MF.getFunction(); + if (!RISCVState::hasAttribute(F)) + return false; + + for (const MachineBasicBlock &MBB : MF) { + for (const MachineInstr &MI : MBB) { + if (!MI.isCall()) + continue; + + // There might be save/restore libcalls generated during frame lowering + // that only touch GPRs, in that case we can just skip it. + if (MI.getFlag(MachineInstr::FrameSetup) || + MI.getFlag(MachineInstr::FrameDestroy)) + continue; + + const MachineOperand *Callee = getCalleeSymbol(MI); + if (!Callee) + continue; + + std::string Name; + if (Callee->isSymbol()) { + Name = Callee->getSymbolName(); + } else { + const GlobalValue *GV = Callee->getGlobal(); + const auto *CalleeFn = dyn_cast<Function>(GV); + // Skip if this function is attributed which is already checked at + // frontend. + if (CalleeFn && RISCVState::hasAttribute(*CalleeFn)) + continue; + Name = GV->getName().str(); + } + + std::string Message = "cannot emit call to '" + Name + + "' from an RISC-V attributed function."; + reportFatalUsageError(MF.getName() + ": " + Message); + } + } + + return false; +} + +FunctionPass *llvm::createRISCVStateCheckPass() { + return new RISCVStateCheck(); +} diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp index e13012a94711d..37ba888dcaabd 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -145,6 +145,7 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() { initializeRISCVDAGToDAGISelLegacyPass(*PR); initializeRISCVMoveMergePass(*PR); initializeRISCVPushPopOptPass(*PR); + initializeRISCVStateCheckPass(*PR); initializeRISCVIndirectBranchTrackingPass(*PR); initializeRISCVLoadStoreOptPass(*PR); initializeRISCVPreAllocZilsdOptPass(*PR); @@ -592,6 +593,8 @@ void RISCVPassConfig::addPreEmitPass() { } void RISCVPassConfig::addPreEmitPass2() { + addPass(createRISCVStateCheckPass()); + if (TM->getOptLevel() != CodeGenOptLevel::None) { addPass(createRISCVMoveMergePass()); // Schedule PushPop Optimization before expansion of Pseudo instruction, diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp index 07a962f7dd03d..5a1f1c8260990 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp @@ -3701,6 +3701,18 @@ bool RISCVTTIImpl::shouldCopyAttributeWhenOutliningFrom( return BaseT::shouldCopyAttributeWhenOutliningFrom(Caller, Attr); } +bool RISCVTTIImpl::areInlineCompatible(const Function *Caller, + const Function *Callee) const { + // riscv_new is the only function that can be called in an non-attributed + // function, we need to prevent inlining this kind of function in case any + // compiler generated non-attributed call in attributed function is inlined so + // it passes the check silently. + if (Callee->hasFnAttribute("riscv_new")) + return false; + + return BaseT::areInlineCompatible(Caller, Callee); +} + std::optional<Instruction *> RISCVTTIImpl::instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const { // If all operands of a vmv.v.x are constant, fold a bitcast(vmv.v.x) to scale diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h index 2b43b93daa6c7..d8fa376c1f074 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h @@ -550,6 +550,9 @@ class RISCVTTIImpl final : public BasicTTIImplBase<RISCVTTIImpl> { shouldCopyAttributeWhenOutliningFrom(const Function *Caller, const Attribute &Attr) const override; + bool areInlineCompatible(const Function *Caller, + const Function *Callee) const override; + std::optional<Instruction *> instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const override; }; diff --git a/llvm/test/CodeGen/RISCV/O0-pipeline.ll b/llvm/test/CodeGen/RISCV/O0-pipeline.ll index 847a8bd96c6d6..d8585bcf07e11 100644 --- a/llvm/test/CodeGen/RISCV/O0-pipeline.ll +++ b/llvm/test/CodeGen/RISCV/O0-pipeline.ll @@ -75,6 +75,7 @@ ; CHECK-NEXT: Lazy Machine Block Frequency Analysis ; CHECK-NEXT: Machine Optimization Remark Emitter ; CHECK-NEXT: Stack Frame Layout Analysis +; CHECK-NEXT: RISC-V architecture state check ; CHECK-NEXT: RISC-V pseudo instruction expansion pass ; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass ; CHECK-NEXT: Unpack machine instruction bundles diff --git a/llvm/test/CodeGen/RISCV/O3-pipeline.ll b/llvm/test/CodeGen/RISCV/O3-pipeline.ll index 149764ffedf9e..8c1fc4de50730 100644 --- a/llvm/test/CodeGen/RISCV/O3-pipeline.ll +++ b/llvm/test/CodeGen/RISCV/O3-pipeline.ll @@ -219,6 +219,7 @@ ; CHECK-NEXT: Lazy Machine Block Frequency Analysis ; CHECK-NEXT: Machine Optimization Remark Emitter ; CHECK-NEXT: Stack Frame Layout Analysis +; CHECK-NEXT: RISC-V architecture state check ; CHECK-NEXT: RISC-V Zcmp move merging pass ; CHECK-NEXT: RISC-V Zcmp Push/Pop optimization pass ; CHECK-NEXT: RISC-V pseudo instruction expansion pass diff --git a/llvm/test/CodeGen/RISCV/riscv-state-check.ll b/llvm/test/CodeGen/RISCV/riscv-state-check.ll new file mode 100644 index 0000000000000..660767d2bcc4b --- /dev/null +++ b/llvm/test/CodeGen/RISCV/riscv-state-check.ll @@ -0,0 +1,30 @@ +; RUN: not llc -mtriple=riscv64 -mattr=+xsfmmbase,+save-restore -o /dev/null < %s 2>&1 \ +; RUN: | FileCheck %s --implicit-check-not=error: + +declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1) +; CHECK: error: libgcc_call: cannot emit call to '__divdi3' from an RISC-V attributed function. +define i64 @libgcc_call(i64 %a, i64 %b) "riscv_inout" { + %d = sdiv i64 %a, %b + ret i64 %d +} + +; CHECK: error: memcpy_call: cannot emit call to 'memcpy' from an RISC-V attributed function. +define void @memcpy_call(ptr %d, ptr %s) "riscv_in" { + call void @llvm.memcpy.p0.p0.i64(ptr %d, ptr %s, i64 1024, i1 false) + ret void +} + +declare void @extern_func() +; CHECK: error: extern_call: cannot emit call to 'extern_func' from an RISC-V attributed function. +define void @extern_call() "riscv_in" { + tail call void @extern_func() + ret void +} + +declare void @preserves(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64) "riscv_preserves" +; spill lib calls should be legal, e.g. __riscv_save_4, __riscv_save_5 +define void @legal(i64 %a) "riscv_in" { + call void @preserves(i64 %a, i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, i64 8, i64 9, i64 10, i64 11) + call void @preserves(i64 %a, i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, i64 8, i64 9, i64 10, i64 11) + ret void +} >From 35c4b0f030155f9b605a558eb59c764b6d65d23d Mon Sep 17 00:00:00 2001 From: Brandon Wu <[email protected]> Date: Mon, 17 Aug 2026 12:41:59 +0800 Subject: [PATCH 5/5] fixup! clang format --- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 2 +- llvm/lib/Target/RISCV/RISCVStateCheck.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index ab7cb0eb297a8..f7a1c100e3132 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -14,8 +14,8 @@ #include "MCTargetDesc/RISCVBaseInfo.h" #include "MCTargetDesc/RISCVMatInt.h" #include "RISCV.h" -#include "RISCVStateAttributes.h" #include "RISCVMachineFunctionInfo.h" +#include "RISCVStateAttributes.h" #include "RISCVSubtarget.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" diff --git a/llvm/lib/Target/RISCV/RISCVStateCheck.cpp b/llvm/lib/Target/RISCV/RISCVStateCheck.cpp index c9ab99e39ac2b..d8450786c4107 100644 --- a/llvm/lib/Target/RISCV/RISCVStateCheck.cpp +++ b/llvm/lib/Target/RISCV/RISCVStateCheck.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// - #include "RISCV.h" #include "RISCVStateAttributes.h" #include "llvm/CodeGen/MachineFunctionPass.h" @@ -35,8 +34,8 @@ class RISCVStateCheck : public MachineFunctionPass { char RISCVStateCheck::ID = 0; -INITIALIZE_PASS(RISCVStateCheck, "riscv-state-check", - RISCV_STATE_CHECK_NAME, false, true) +INITIALIZE_PASS(RISCVStateCheck, "riscv-state-check", RISCV_STATE_CHECK_NAME, + false, true) static const MachineOperand *getCalleeSymbol(const MachineInstr &MI) { for (const MachineOperand &MO : MI.operands()) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
