From fb448ac6e526434a85226aca31ee0fed3b8cb426 Mon Sep 17 00:00:00 2001 From: Fraser Cormack Date: Mon, 5 Aug 2024 17:22:12 +0100 Subject: [PATCH 1/5] [SYCL] Start to move attribute handling to SemaSYCL This better reflects how Sema is organized upstream, following recent restructuring. Doing all of it at once (moving *all* SYCL-related code out of SemaDeclAttr) results in an unwieldy diff, interleaving additions and deletions in an unreadable way. Thus the patch is split up into parts for better reviewability. --- clang/include/clang/Sema/Sema.h | 80 - clang/include/clang/Sema/SemaSYCL.h | 129 ++ clang/lib/Sema/SemaDeclAttr.cpp | 2005 +---------------- clang/lib/Sema/SemaSYCL.cpp | 1796 ++++++++++++++- clang/lib/Sema/SemaStmtAttr.cpp | 19 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 68 +- 6 files changed, 2068 insertions(+), 2029 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 666987ffd69e6..ee973895a8573 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3936,159 +3936,79 @@ class Sema final : public SemaBase { void redelayDiagnostics(sema::DelayedDiagnosticPool &pool); - void AddSYCLIntelBankBitsAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size); - bool AnyWorkGroupSizesDiffer(const Expr *LHSXDim, const Expr *LHSYDim, - const Expr *LHSZDim, const Expr *RHSXDim, - const Expr *RHSYDim, const Expr *RHSZDim); - bool AllWorkGroupSizesSame(const Expr *LHSXDim, const Expr *LHSYDim, - const Expr *LHSZDim, const Expr *RHSXDim, - const Expr *RHSYDim, const Expr *RHSZDim); - void AddSYCLWorkGroupSizeHintAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, Expr *ZDim); SYCLWorkGroupSizeHintAttr * MergeSYCLWorkGroupSizeHintAttr(Decl *D, const SYCLWorkGroupSizeHintAttr &A); - void AddIntelReqdSubGroupSize(Decl *D, const AttributeCommonInfo &CI, - Expr *E); IntelReqdSubGroupSizeAttr * MergeIntelReqdSubGroupSizeAttr(Decl *D, const IntelReqdSubGroupSizeAttr &A); IntelNamedSubGroupSizeAttr * MergeIntelNamedSubGroupSizeAttr(Decl *D, const IntelNamedSubGroupSizeAttr &A); - void AddSYCLIntelNumSimdWorkItemsAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); SYCLIntelNumSimdWorkItemsAttr * MergeSYCLIntelNumSimdWorkItemsAttr(Decl *D, const SYCLIntelNumSimdWorkItemsAttr &A); - void AddSYCLIntelESimdVectorizeAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); SYCLIntelESimdVectorizeAttr * MergeSYCLIntelESimdVectorizeAttr(Decl *D, const SYCLIntelESimdVectorizeAttr &A); - void AddSYCLIntelSchedulerTargetFmaxMhzAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E); SYCLIntelSchedulerTargetFmaxMhzAttr *MergeSYCLIntelSchedulerTargetFmaxMhzAttr( Decl *D, const SYCLIntelSchedulerTargetFmaxMhzAttr &A); - void AddSYCLIntelNoGlobalWorkOffsetAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E); SYCLIntelNoGlobalWorkOffsetAttr *MergeSYCLIntelNoGlobalWorkOffsetAttr( Decl *D, const SYCLIntelNoGlobalWorkOffsetAttr &A); - void AddSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); SYCLIntelLoopFuseAttr * MergeSYCLIntelLoopFuseAttr(Decl *D, const SYCLIntelLoopFuseAttr &A); - void AddSYCLIntelPrivateCopiesAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); - void AddSYCLIntelMaxReplicatesAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); SYCLIntelMaxReplicatesAttr * MergeSYCLIntelMaxReplicatesAttr(Decl *D, const SYCLIntelMaxReplicatesAttr &A); - void AddSYCLIntelForcePow2DepthAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); SYCLIntelForcePow2DepthAttr * MergeSYCLIntelForcePow2DepthAttr(Decl *D, const SYCLIntelForcePow2DepthAttr &A); - void AddSYCLIntelInitiationIntervalAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E); SYCLIntelInitiationIntervalAttr *MergeSYCLIntelInitiationIntervalAttr( Decl *D, const SYCLIntelInitiationIntervalAttr &A); SYCLIntelMaxConcurrencyAttr * MergeSYCLIntelMaxConcurrencyAttr(Decl *D, const SYCLIntelMaxConcurrencyAttr &A); - void AddSYCLIntelMaxGlobalWorkDimAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); SYCLIntelMaxGlobalWorkDimAttr * MergeSYCLIntelMaxGlobalWorkDimAttr(Decl *D, const SYCLIntelMaxGlobalWorkDimAttr &A); - void AddSYCLIntelMinWorkGroupsPerComputeUnitAttr( - Decl *D, const AttributeCommonInfo &CI, Expr *E); SYCLIntelMinWorkGroupsPerComputeUnitAttr * MergeSYCLIntelMinWorkGroupsPerComputeUnitAttr( Decl *D, const SYCLIntelMinWorkGroupsPerComputeUnitAttr &A); - void AddSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( - Decl *D, const AttributeCommonInfo &CI, Expr *E); SYCLIntelMaxWorkGroupsPerMultiprocessorAttr * MergeSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( Decl *D, const SYCLIntelMaxWorkGroupsPerMultiprocessorAttr &A); - void AddSYCLIntelBankWidthAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); SYCLIntelBankWidthAttr * MergeSYCLIntelBankWidthAttr(Decl *D, const SYCLIntelBankWidthAttr &A); - void AddSYCLIntelNumBanksAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); SYCLIntelNumBanksAttr * MergeSYCLIntelNumBanksAttr(Decl *D, const SYCLIntelNumBanksAttr &A); SYCLDeviceHasAttr *MergeSYCLDeviceHasAttr(Decl *D, const SYCLDeviceHasAttr &A); - void AddSYCLDeviceHasAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size); SYCLUsesAspectsAttr *MergeSYCLUsesAspectsAttr(Decl *D, const SYCLUsesAspectsAttr &A); - void AddSYCLUsesAspectsAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size); bool CheckMaxAllowedWorkGroupSize(const Expr *RWGSXDim, const Expr *RWGSYDim, const Expr *RWGSZDim, const Expr *MWGSXDim, const Expr *MWGSYDim, const Expr *MWGSZDim); - void AddSYCLIntelMaxWorkGroupSizeAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, Expr *ZDim); SYCLIntelMaxWorkGroupSizeAttr * MergeSYCLIntelMaxWorkGroupSizeAttr(Decl *D, const SYCLIntelMaxWorkGroupSizeAttr &A); void CheckSYCLAddIRAttributesFunctionAttrConflicts(Decl *D); SYCLAddIRAttributesFunctionAttr *MergeSYCLAddIRAttributesFunctionAttr( Decl *D, const SYCLAddIRAttributesFunctionAttr &A); - void AddSYCLAddIRAttributesFunctionAttr(Decl *D, - const AttributeCommonInfo &CI, - MutableArrayRef Args); SYCLAddIRAttributesKernelParameterAttr * MergeSYCLAddIRAttributesKernelParameterAttr( Decl *D, const SYCLAddIRAttributesKernelParameterAttr &A); - void AddSYCLAddIRAttributesKernelParameterAttr(Decl *D, - const AttributeCommonInfo &CI, - MutableArrayRef Args); SYCLAddIRAttributesGlobalVariableAttr * MergeSYCLAddIRAttributesGlobalVariableAttr( Decl *D, const SYCLAddIRAttributesGlobalVariableAttr &A); - void AddSYCLAddIRAttributesGlobalVariableAttr(Decl *D, - const AttributeCommonInfo &CI, - MutableArrayRef Args); SYCLAddIRAnnotationsMemberAttr * MergeSYCLAddIRAnnotationsMemberAttr(Decl *D, const SYCLAddIRAnnotationsMemberAttr &A); - void AddSYCLAddIRAnnotationsMemberAttr(Decl *D, const AttributeCommonInfo &CI, - MutableArrayRef Args); - void AddSYCLReqdWorkGroupSizeAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, Expr *ZDim); SYCLReqdWorkGroupSizeAttr * MergeSYCLReqdWorkGroupSizeAttr(Decl *D, const SYCLReqdWorkGroupSizeAttr &A); SYCLTypeAttr *MergeSYCLTypeAttr(Decl *D, const AttributeCommonInfo &CI, SYCLTypeAttr::SYCLType TypeName); - /// Emit a diagnostic about the given attribute having a deprecated name, and - /// also emit a fixit hint to generate the new attribute name. - void DiagnoseDeprecatedAttribute(const ParsedAttr &A, StringRef NewScope, - StringRef NewName); - - /// Diagnoses an attribute in the 'intelfpga' namespace and suggests using - /// the attribute in the 'intel' namespace instead. - void CheckDeprecatedSYCLAttributeSpelling(const ParsedAttr &A, - StringRef NewName = ""); - - /// addSYCLIntelPipeIOAttr - Adds a pipe I/O attribute to a particular - /// declaration. - void addSYCLIntelPipeIOAttr(Decl *D, const AttributeCommonInfo &CI, Expr *ID); SYCLIntelPipeIOAttr *MergeSYCLIntelPipeIOAttr(Decl *D, const SYCLIntelPipeIOAttr &A); - /// AddSYCLIntelMaxConcurrencyAttr - Adds a max_concurrency attribute to a - /// particular declaration. - void AddSYCLIntelMaxConcurrencyAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E); - bool CheckCountedByAttr(Scope *Scope, const FieldDecl *FD); /// Check if IdxExpr is a valid parameter index for a function or /// instance method D. May output an error. diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h index f285dbfb0baa8..fd707efebdf14 100644 --- a/clang/include/clang/Sema/SemaSYCL.h +++ b/clang/include/clang/Sema/SemaSYCL.h @@ -16,9 +16,11 @@ #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/Type.h" +#include "clang/Basic/Cuda.h" #include "clang/Basic/SourceLocation.h" #include "clang/Sema/Ownership.h" #include "clang/Sema/SemaBase.h" +#include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SetVector.h" @@ -215,6 +217,27 @@ class SYCLIntegrationFooter { void emitSpecIDName(raw_ostream &O, const VarDecl *VD); }; +// Handles max_global_work_dim. +// Returns a OneArgResult value; EqualToOne means all argument values are +// equal to one, NotEqualToOne means at least one argument value is not +// equal to one, and Unknown means that at least one of the argument values +// could not be determined. +enum class OneArgResult { Unknown, EqualToOne, NotEqualToOne }; +static inline OneArgResult areAllArgsOne(const Expr *Args[], size_t Count) { + for (size_t Idx = 0; Idx < Count; ++Idx) { + const Expr *Arg = Args[Idx]; + // Optional arguments are considered trivially one. + if (!Arg) + return OneArgResult::EqualToOne; + const auto *CE = dyn_cast(Args[Idx]); + if (!CE) + return OneArgResult::Unknown; + if (CE->getResultAsAPSInt() != 1) + return OneArgResult::NotEqualToOne; + } + return OneArgResult::EqualToOne; +} + class SemaSYCL : public SemaBase { private: // We store SYCL Kernels here and handle separately -- which is a hack. @@ -397,6 +420,112 @@ class SemaSYCL : public SemaBase { ParsedType ParsedTy); void handleKernelAttr(Decl *D, const ParsedAttr &AL); + + static CudaArch getCudaArch(const TargetInfo &TI); + static bool hasDependentExpr(Expr **Exprs, const size_t ExprsSize); + + /// Emit a diagnostic about the given attribute having a deprecated name, and + /// also emit a fixit hint to generate the new attribute name. + void diagnoseDeprecatedAttribute(const ParsedAttr &A, StringRef NewScope, + StringRef NewName); + + /// Diagnoses an attribute in the 'intelfpga' namespace and suggests using + /// the attribute in the 'intel' namespace instead. + void checkDeprecatedSYCLAttributeSpelling(const ParsedAttr &A, + StringRef NewName = ""); + + bool checkValidFPGAMemoryAttributesVar(Decl *D); + + bool checkWorkGroupSize(const Expr *NSWIValue, const Expr *RWGSXDim, + const Expr *RWGSYDim, const Expr *RWGSZDim); + bool checkMaxAllowedWorkGroupSize(const Expr *RWGSXDim, const Expr *RWGSYDim, + const Expr *RWGSZDim, const Expr *MWGSXDim, + const Expr *MWGSYDim, const Expr *MWGSZDim); + bool areInvalidWorkGroupSizeAttrs(const Expr *MGValue, const Expr *XDim, + const Expr *YDim, const Expr *ZDim); + + // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr or + // ReqdWorkGroupSizeAttr, check to see if they hold equal values + // (1, 1, 1). Returns true if diagnosed. + template + bool checkWorkGroupSizeAttrExpr(Decl *D, const AttributeCommonInfo &AL) { + if (const auto *A = D->getAttr()) { + const Expr *Args[3] = {A->getXDim(), A->getYDim(), A->getZDim()}; + if (OneArgResult::NotEqualToOne == areAllArgsOne(Args, 3)) { + Diag(A->getLocation(), diag::err_sycl_x_y_z_arguments_must_be_one) + << A << AL; + return true; + } + } + return false; + } + + bool anyWorkGroupSizesDiffer(const Expr *LHSXDim, const Expr *LHSYDim, + const Expr *LHSZDim, const Expr *RHSXDim, + const Expr *RHSYDim, const Expr *RHSZDim); + bool allWorkGroupSizesSame(const Expr *LHSXDim, const Expr *LHSYDim, + const Expr *LHSZDim, const Expr *RHSXDim, + const Expr *RHSYDim, const Expr *RHSZDim); + + void addSYCLIntelPipeIOAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E); + void addSYCLDeviceHasAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size); + void addSYCLUsesAspectsAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size); + void addSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelNumSimdWorkItemsAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelSchedulerTargetFmaxMhzAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelNoGlobalWorkOffsetAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelMaxGlobalWorkDimAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelMinWorkGroupsPerComputeUnitAttr( + Decl *D, const AttributeCommonInfo &CI, Expr *E); + void addSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( + Decl *D, const AttributeCommonInfo &CI, Expr *E); + void addSYCLIntelMaxConcurrencyAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelPrivateCopiesAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelMaxReplicatesAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelInitiationIntervalAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelESimdVectorizeAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLAddIRAttributesFunctionAttr(Decl *D, + const AttributeCommonInfo &CI, + MutableArrayRef Args); + void addSYCLAddIRAttributesKernelParameterAttr(Decl *D, + const AttributeCommonInfo &CI, + MutableArrayRef Args); + void addSYCLAddIRAttributesGlobalVariableAttr(Decl *D, + const AttributeCommonInfo &CI, + MutableArrayRef Args); + void addSYCLAddIRAnnotationsMemberAttr(Decl *D, const AttributeCommonInfo &CI, + MutableArrayRef Args); + void addSYCLWorkGroupSizeHintAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, Expr *ZDim); + void addSYCLReqdWorkGroupSizeAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, Expr *ZDim); + void addSYCLIntelMaxWorkGroupSizeAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, Expr *ZDim); + void addSYCLIntelForcePow2DepthAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelBankWidthAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelNumBanksAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + void addSYCLIntelBankBitsAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size); + void addIntelReqdSubGroupSize(Decl *D, const AttributeCommonInfo &CI, + Expr *E); }; } // namespace clang diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 166da8245b763..778dd9525052d 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -50,7 +50,6 @@ #include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaHLSL.h" #include "clang/Sema/SemaInternal.h" -#include "clang/Sema/SemaSYCL.h" #include "clang/Sema/SemaM68k.h" #include "clang/Sema/SemaMIPS.h" #include "clang/Sema/SemaMSP430.h" @@ -117,81 +116,6 @@ static bool checkPositiveIntArgument(Sema &S, const AttrInfo &AI, const Expr *Ex return true; } -void Sema::DiagnoseDeprecatedAttribute(const ParsedAttr &A, StringRef NewScope, - StringRef NewName) { - assert((!NewName.empty() || !NewScope.empty()) && - "Deprecated attribute with no new scope or name?"); - Diag(A.getLoc(), diag::warn_attribute_spelling_deprecated) - << "'" + A.getNormalizedFullName() + "'"; - - FixItHint Fix; - std::string NewFullName; - if (NewScope.empty() && !NewName.empty()) { - // Only have a new name. - Fix = FixItHint::CreateReplacement(A.getLoc(), NewName); - NewFullName = - ((A.hasScope() ? A.getScopeName()->getName() : StringRef("")) + - "::" + NewName) - .str(); - } else if (NewName.empty() && !NewScope.empty()) { - // Only have a new scope. - Fix = FixItHint::CreateReplacement(A.getScopeLoc(), NewScope); - NewFullName = (NewScope + "::" + A.getAttrName()->getName()).str(); - } else { - // Have both a new name and a new scope. - NewFullName = (NewScope + "::" + NewName).str(); - Fix = FixItHint::CreateReplacement(A.getRange(), NewFullName); - } - - Diag(A.getLoc(), diag::note_spelling_suggestion) - << "'" + NewFullName + "'" << Fix; -} - -void Sema::CheckDeprecatedSYCLAttributeSpelling(const ParsedAttr &A, - StringRef NewName) { - // Additionally, diagnose the old [[intel::ii]] spelling. - if (A.getKind() == ParsedAttr::AT_SYCLIntelInitiationInterval && - A.getAttrName()->isStr("ii")) { - DiagnoseDeprecatedAttribute(A, "intel", "initiation_interval"); - return; - } - - // Diagnose SYCL 2017 spellings in later SYCL modes. - if (LangOpts.getSYCLVersion() > LangOptions::SYCL_2017) { - // All attributes in the cl vendor namespace are deprecated in favor of a - // name in the sycl namespace as of SYCL 2020. - if (A.hasScope() && A.getScopeName()->isStr("cl")) { - DiagnoseDeprecatedAttribute(A, "sycl", NewName); - return; - } - - // All GNU-style spellings are deprecated in favor of a C++-style spelling. - if (A.getSyntax() == ParsedAttr::AS_GNU) { - // Note: we cannot suggest an automatic fix-it because GNU-style - // spellings can appear in locations that are not valid for a C++-style - // spelling, and the attribute could be part of an attribute list within - // a single __attribute__ specifier. Just tell the user it's deprecated - // manually. - // - // This currently assumes that the GNU-style spelling is the same as the - // SYCL 2020 spelling (sans the vendor namespace). - Diag(A.getLoc(), diag::warn_attribute_spelling_deprecated) - << "'" + A.getNormalizedFullName() + "'"; - Diag(A.getLoc(), diag::note_spelling_suggestion) - << "'[[sycl::" + A.getNormalizedFullName() + "]]'"; - return; - } - } - - // Diagnose SYCL 2020 spellings used in earlier SYCL modes as being an - // extension. - if (LangOpts.getSYCLVersion() == LangOptions::SYCL_2017 && A.hasScope() && - A.getScopeName()->isStr("sycl")) { - Diag(A.getLoc(), diag::ext_sycl_2020_attr_spelling) << A; - return; - } -} - /// Check if IdxExpr is a valid parameter index for a function or /// instance method D. May output an error. /// @@ -2938,114 +2862,6 @@ static void handleWorkGroupSize(Sema &S, Decl *D, const ParsedAttr &AL) { WorkGroupAttr(S.Context, AL, WGSize[0], WGSize[1], WGSize[2])); } -// Returns a DupArgResult value; Same means the args have the same value, -// Different means the args do not have the same value, and Unknown means that -// the args cannot (yet) be compared. -enum class DupArgResult { Unknown, Same, Different }; -static DupArgResult AreArgValuesIdentical(const Expr *LHS, const Expr *RHS) { - // If both operands are nullptr they are unspecified and are considered the - // same. - if (!LHS && !RHS) - return DupArgResult::Same; - - // Otherwise, if either operand is nullptr they are considered different. - if (!LHS || !RHS) - return DupArgResult::Different; - - // Otherwise, if either operand is still value dependent, we can't test - // anything. - const auto *LHSCE = dyn_cast(LHS); - const auto *RHSCE = dyn_cast(RHS); - if (!LHSCE || !RHSCE) - return DupArgResult::Unknown; - - // Otherwise, test that the values. - return LHSCE->getResultAsAPSInt() == RHSCE->getResultAsAPSInt() - ? DupArgResult::Same - : DupArgResult::Different; -} - -// Returns true if any of the specified dimensions (X,Y,Z) differ between the -// arguments. -bool Sema::AnyWorkGroupSizesDiffer(const Expr *LHSXDim, const Expr *LHSYDim, - const Expr *LHSZDim, const Expr *RHSXDim, - const Expr *RHSYDim, const Expr *RHSZDim) { - DupArgResult Results[] = {AreArgValuesIdentical(LHSXDim, RHSXDim), - AreArgValuesIdentical(LHSYDim, RHSYDim), - AreArgValuesIdentical(LHSZDim, RHSZDim)}; - return llvm::is_contained(Results, DupArgResult::Different); -} - -// Returns true if all of the specified dimensions (X,Y,Z) are the same between -// the arguments. -bool Sema::AllWorkGroupSizesSame(const Expr *LHSXDim, const Expr *LHSYDim, - const Expr *LHSZDim, const Expr *RHSXDim, - const Expr *RHSYDim, const Expr *RHSZDim) { - DupArgResult Results[] = {AreArgValuesIdentical(LHSXDim, RHSXDim), - AreArgValuesIdentical(LHSYDim, RHSYDim), - AreArgValuesIdentical(LHSZDim, RHSZDim)}; - return llvm::all_of(Results, - [](DupArgResult V) { return V == DupArgResult::Same; }); -} - -void Sema::AddSYCLWorkGroupSizeHintAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, Expr *ZDim) { - // Returns nullptr if diagnosing, otherwise returns the original expression - // or the original expression converted to a constant expression. - auto CheckAndConvertArg = [&](Expr *E) -> std::optional { - // We can only check if the expression is not value dependent. - if (E && !E->isValueDependent()) { - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return std::nullopt; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return std::nullopt; - } - } - - return E; - }; - - // Check all three argument values, and if any are bad, bail out. This will - // convert the given expressions into constant expressions when possible. - std::optional XDimConvert = CheckAndConvertArg(XDim); - std::optional YDimConvert = CheckAndConvertArg(YDim); - std::optional ZDimConvert = CheckAndConvertArg(ZDim); - if (!XDimConvert || !YDimConvert || !ZDimConvert) - return; - XDim = XDimConvert.value(); - YDim = YDimConvert.value(); - ZDim = ZDimConvert.value(); - - // If the attribute was already applied with different arguments, then - // diagnose the second attribute as a duplicate and don't add it. - if (const auto *Existing = D->getAttr()) { - // If any of the results are known to be different, we can diagnose at this - // point and drop the attribute. - if (AnyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(Existing->getLoc(), diag::note_previous_attribute); - return; - } - // If all of the results are known to be the same, we can silently drop the - // attribute. Otherwise, we have to add the attribute and resolve its - // differences later. - if (AllWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) - return; - } - - D->addAttr(::new (Context) - SYCLWorkGroupSizeHintAttr(Context, CI, XDim, YDim, ZDim)); -} - SYCLWorkGroupSizeHintAttr * Sema::MergeSYCLWorkGroupSizeHintAttr(Decl *D, const SYCLWorkGroupSizeHintAttr &A) { @@ -3053,9 +2869,9 @@ Sema::MergeSYCLWorkGroupSizeHintAttr(Decl *D, if (const auto *DeclAttr = D->getAttr()) { // If any of the results are known to be different, we can diagnose at this // point and drop the attribute. - if (AnyWorkGroupSizesDiffer(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), A.getXDim(), A.getYDim(), - A.getZDim())) { + if (SYCL().anyWorkGroupSizesDiffer(DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim(), A.getXDim(), + A.getYDim(), A.getZDim())) { Diag(DeclAttr->getLoc(), diag::warn_duplicate_attribute) << &A; Diag(A.getLoc(), diag::note_previous_attribute); return nullptr; @@ -3063,9 +2879,9 @@ Sema::MergeSYCLWorkGroupSizeHintAttr(Decl *D, // If all of the results are known to be the same, we can silently drop the // attribute. Otherwise, we have to add the attribute and resolve its // differences later. - if (AllWorkGroupSizesSame(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), A.getXDim(), A.getYDim(), - A.getZDim())) + if (SYCL().allWorkGroupSizesSame(DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim(), A.getXDim(), + A.getYDim(), A.getZDim())) return nullptr; } return ::new (Context) SYCLWorkGroupSizeHintAttr(Context, A, A.getXDim(), @@ -3075,7 +2891,7 @@ Sema::MergeSYCLWorkGroupSizeHintAttr(Decl *D, // Handles SYCL work_group_size_hint. static void handleSYCLWorkGroupSizeHint(Sema &S, Decl *D, const ParsedAttr &AL) { - S.CheckDeprecatedSYCLAttributeSpelling(AL); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(AL); // __attribute__((work_group_size_hint) requires exactly three arguments. if (AL.getSyntax() == ParsedAttr::AS_GNU || !AL.hasScope() || @@ -3089,7 +2905,7 @@ static void handleSYCLWorkGroupSizeHint(Sema &S, Decl *D, Expr *XDimExpr = NumArgs > 0 ? AL.getArgAsExpr(0) : nullptr; Expr *YDimExpr = NumArgs > 1 ? AL.getArgAsExpr(1) : nullptr; Expr *ZDimExpr = NumArgs > 2 ? AL.getArgAsExpr(2) : nullptr; - S.AddSYCLWorkGroupSizeHintAttr(D, AL, XDimExpr, YDimExpr, ZDimExpr); + S.SYCL().addSYCLWorkGroupSizeHintAttr(D, AL, XDimExpr, YDimExpr, ZDimExpr); } static void handleWorkGroupSizeHint(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -3100,203 +2916,15 @@ static void handleWorkGroupSizeHint(Sema &S, Decl *D, const ParsedAttr &AL) { handleWorkGroupSize(S, D, AL); } -// Checks correctness of mutual usage of different work_group_size attributes: -// reqd_work_group_size, max_work_group_size, and max_global_work_dim. -// -// If [[intel::max_work_group_size(X, Y, Z)]] or -// [[sycl::reqd_work_group_size(X, Y, Z)]] or -// [[cl::reqd_work_group_size(X, Y, Z)]] -// or __attribute__((reqd_work_group_size)) attribute is specified on a -// declaration along with [[intel::max_global_work_dim()]] attribute, check to -// see if all arguments of 'max_work_group_size' or different spellings of -// 'reqd_work_group_size' attribute hold value 1 in case the argument of -// [[intel::max_global_work_dim()]] attribute value equals to 0. -static bool InvalidWorkGroupSizeAttrs(Sema &S, const Expr *MGValue, - const Expr *XDim, const Expr *YDim, - const Expr *ZDim) { - // If any of the operand is still value dependent, we can't test anything. - const auto *MGValueExpr = dyn_cast(MGValue); - const auto *XDimExpr = dyn_cast(XDim); - - if (!MGValueExpr || !XDimExpr) - return false; - - // Y and Z may be optional so we allow them to be null and consider them - // dependent if the original epxression was not null while the result of the - // cast is. - const auto *YDimExpr = dyn_cast_or_null(YDim); - const auto *ZDimExpr = dyn_cast_or_null(ZDim); - - if ((!YDimExpr && YDim) || (!ZDimExpr && ZDim)) - return false; - - // Otherwise, check if the attribute values are equal to one. - // Y and Z dimensions are optional and are considered trivially 1 if - // unspecified. - return (MGValueExpr->getResultAsAPSInt() == 0 && - (XDimExpr->getResultAsAPSInt() != 1 || - (YDimExpr && YDimExpr->getResultAsAPSInt() != 1) || - (ZDimExpr && ZDimExpr->getResultAsAPSInt() != 1))); -} - -// Checks correctness of mutual usage of different work_group_size attributes: -// reqd_work_group_size and max_work_group_size. -// -// If the 'reqd_work_group_size' attribute is specified on a declaration along -// with 'max_work_group_size' attribute, check to see if values of -// 'reqd_work_group_size' attribute arguments are equal to or less than values -// of 'max_work_group_size' attribute arguments. -// -// The arguments to reqd_work_group_size are ordered based on which index -// increments the fastest. In OpenCL, the first argument is the index that -// increments the fastest, and in SYCL, the last argument is the index that -// increments the fastest. -// -// __attribute__((reqd_work_group_size)) follows the OpenCL rules in OpenCL -// mode. All spellings of reqd_work_group_size attribute (regardless of -// syntax used) follow the SYCL rules when in SYCL mode. -bool Sema::CheckMaxAllowedWorkGroupSize( - const Expr *RWGSXDim, const Expr *RWGSYDim, const Expr *RWGSZDim, - const Expr *MWGSXDim, const Expr *MWGSYDim, const Expr *MWGSZDim) { - // If any of the operand is still value dependent, we can't test anything. - const auto *RWGSXDimExpr = dyn_cast(RWGSXDim); - const auto *MWGSXDimExpr = dyn_cast(MWGSXDim); - const auto *MWGSYDimExpr = dyn_cast(MWGSYDim); - const auto *MWGSZDimExpr = dyn_cast(MWGSZDim); - - if (!RWGSXDimExpr || !MWGSXDimExpr || !MWGSYDimExpr || !MWGSZDimExpr) - return false; - - // Y and Z may be optional so we allow them to be null and consider them - // dependent if the original epxression was not null while the result of the - // cast is. - const auto *RWGSYDimExpr = dyn_cast_or_null(RWGSYDim); - const auto *RWGSZDimExpr = dyn_cast_or_null(RWGSZDim); - - if ((!RWGSYDimExpr && RWGSYDim) || (!RWGSZDimExpr && RWGSZDim)) - return false; - - // SYCL reorders arguments based on the dimensionality. - // If we only have the X-dimension, there is no change to the expressions, - // otherwise the last specified dimension acts as the first dimension in the - // work-group size. - const ConstantExpr *FirstRWGDimExpr = RWGSXDimExpr; - const ConstantExpr *SecondRWGDimExpr = RWGSYDimExpr; - const ConstantExpr *ThirdRWGDimExpr = RWGSZDimExpr; - if (getLangOpts().SYCLIsDevice && RWGSYDim) - std::swap(FirstRWGDimExpr, RWGSZDim ? ThirdRWGDimExpr : SecondRWGDimExpr); - - // Check if values of 'reqd_work_group_size' attribute arguments are greater - // than values of 'max_work_group_size' attribute arguments. - bool CheckFirstArgument = - FirstRWGDimExpr->getResultAsAPSInt().getZExtValue() > - MWGSZDimExpr->getResultAsAPSInt().getZExtValue(); - - bool CheckSecondArgument = - SecondRWGDimExpr && SecondRWGDimExpr->getResultAsAPSInt().getZExtValue() > - MWGSYDimExpr->getResultAsAPSInt().getZExtValue(); - - bool CheckThirdArgument = - ThirdRWGDimExpr && ThirdRWGDimExpr->getResultAsAPSInt().getZExtValue() > - MWGSXDimExpr->getResultAsAPSInt().getZExtValue(); - - return CheckFirstArgument || CheckSecondArgument || CheckThirdArgument; -} - -void Sema::AddSYCLIntelMaxWorkGroupSizeAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, - Expr *ZDim) { - // Returns nullptr if diagnosing, otherwise returns the original expression - // or the original expression converted to a constant expression. - auto CheckAndConvertArg = [&](Expr *E) -> Expr * { - // Check if the expression is not value dependent. - if (!E->isValueDependent()) { - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return nullptr; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return nullptr; - } - } - return E; - }; - - // Check all three argument values, and if any are bad, bail out. This will - // convert the given expressions into constant expressions when possible. - XDim = CheckAndConvertArg(XDim); - YDim = CheckAndConvertArg(YDim); - ZDim = CheckAndConvertArg(ZDim); - if (!XDim || !YDim || !ZDim) - return; - - // If the 'max_work_group_size' attribute is specified on a declaration along - // with 'reqd_work_group_size' attribute, check to see if values of - // 'reqd_work_group_size' attribute arguments are equal to or less than values - // of 'max_work_group_size' attribute arguments. - // - // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments - // are greater than values of 'max_work_group_size' attribute arguments. - if (const auto *DeclAttr = D->getAttr()) { - if (CheckMaxAllowedWorkGroupSize(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), XDim, YDim, ZDim)) { - Diag(CI.getLoc(), diag::err_conflicting_sycl_function_attributes) - << CI << DeclAttr; - Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); - return; - } - } - - // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr, check to see if - // the attribute holds values equal to (1, 1, 1) in case the value of - // SYCLIntelMaxGlobalWorkDimAttr equals to 0. - if (const auto *DeclAttr = D->getAttr()) { - if (InvalidWorkGroupSizeAttrs(*this, DeclAttr->getValue(), XDim, YDim, - ZDim)) { - Diag(CI.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) - << CI << DeclAttr; - return; - } - } - - // If the attribute was already applied with different arguments, then - // diagnose the second attribute as a duplicate and don't add it. - if (const auto *Existing = D->getAttr()) { - // If any of the results are known to be different, we can diagnose at this - // point and drop the attribute. - if (AnyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(Existing->getLoc(), diag::note_previous_attribute); - return; - } - // If all of the results are known to be the same, we can silently drop the - // attribute. Otherwise, we have to add the attribute and resolve its - // differences later. - if (AllWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) - return; - } - - D->addAttr(::new (Context) - SYCLIntelMaxWorkGroupSizeAttr(Context, CI, XDim, YDim, ZDim)); -} - SYCLIntelMaxWorkGroupSizeAttr *Sema::MergeSYCLIntelMaxWorkGroupSizeAttr( Decl *D, const SYCLIntelMaxWorkGroupSizeAttr &A) { // Check to see if there's a duplicate attribute already applied. if (const auto *DeclAttr = D->getAttr()) { // If any of the results are known to be different, we can diagnose at this // point and drop the attribute. - if (AnyWorkGroupSizesDiffer(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), A.getXDim(), A.getYDim(), - A.getZDim())) { + if (SYCL().anyWorkGroupSizesDiffer(DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim(), A.getXDim(), + A.getYDim(), A.getZDim())) { Diag(DeclAttr->getLoc(), diag::warn_duplicate_attribute) << &A; Diag(A.getLoc(), diag::note_previous_attribute); return nullptr; @@ -3304,9 +2932,9 @@ SYCLIntelMaxWorkGroupSizeAttr *Sema::MergeSYCLIntelMaxWorkGroupSizeAttr( // If all of the results are known to be the same, we can silently drop the // attribute. Otherwise, we have to add the attribute and resolve its // differences later. - if (AllWorkGroupSizesSame(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), A.getXDim(), A.getYDim(), - A.getZDim())) + if (SYCL().allWorkGroupSizesSame(DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim(), A.getXDim(), + A.getYDim(), A.getZDim())) return nullptr; } @@ -3318,9 +2946,9 @@ SYCLIntelMaxWorkGroupSizeAttr *Sema::MergeSYCLIntelMaxWorkGroupSizeAttr( // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments // are greater than values of 'max_work_group_size' attribute arguments. if (const auto *DeclAttr = D->getAttr()) { - if (CheckMaxAllowedWorkGroupSize(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), A.getXDim(), - A.getYDim(), A.getZDim())) { + if (SYCL().checkMaxAllowedWorkGroupSize( + DeclAttr->getXDim(), DeclAttr->getYDim(), DeclAttr->getZDim(), + A.getXDim(), A.getYDim(), A.getZDim())) { Diag(DeclAttr->getLoc(), diag::err_conflicting_sycl_function_attributes) << DeclAttr << &A; Diag(A.getLoc(), diag::note_conflicting_attribute); @@ -3332,8 +2960,8 @@ SYCLIntelMaxWorkGroupSizeAttr *Sema::MergeSYCLIntelMaxWorkGroupSizeAttr( // the attribute holds values equal to (1, 1, 1) in case the value of // SYCLIntelMaxGlobalWorkDimAttr equals to 0. if (const auto *DeclAttr = D->getAttr()) { - if (InvalidWorkGroupSizeAttrs(*this, DeclAttr->getValue(), A.getXDim(), - A.getYDim(), A.getZDim())) { + if (SYCL().areInvalidWorkGroupSizeAttrs(DeclAttr->getValue(), A.getXDim(), + A.getYDim(), A.getZDim())) { Diag(A.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) << &A << DeclAttr; return nullptr; @@ -3347,165 +2975,23 @@ SYCLIntelMaxWorkGroupSizeAttr *Sema::MergeSYCLIntelMaxWorkGroupSizeAttr( // Handles max_work_group_size attribute. static void handleSYCLIntelMaxWorkGroupSize(Sema &S, Decl *D, const ParsedAttr &AL) { - S.AddSYCLIntelMaxWorkGroupSizeAttr(D, AL, AL.getArgAsExpr(0), - AL.getArgAsExpr(1), AL.getArgAsExpr(2)); + S.SYCL().addSYCLIntelMaxWorkGroupSizeAttr( + D, AL, AL.getArgAsExpr(0), AL.getArgAsExpr(1), AL.getArgAsExpr(2)); } // Handles min_work_groups_per_cu attribute. static void handleSYCLIntelMinWorkGroupsPerComputeUnit(Sema &S, Decl *D, const ParsedAttr &AL) { - S.AddSYCLIntelMinWorkGroupsPerComputeUnitAttr(D, AL, AL.getArgAsExpr(0)); + S.SYCL().addSYCLIntelMinWorkGroupsPerComputeUnitAttr(D, AL, + AL.getArgAsExpr(0)); } // Handles max_work_groups_per_mp attribute. static void handleSYCLIntelMaxWorkGroupsPerMultiprocessor(Sema &S, Decl *D, const ParsedAttr &AL) { - S.AddSYCLIntelMaxWorkGroupsPerMultiprocessorAttr(D, AL, AL.getArgAsExpr(0)); -} - -// Handles reqd_work_group_size. -// If the 'reqd_work_group_size' attribute is specified on a declaration along -// with 'num_simd_work_items' attribute, the required work group size specified -// by 'num_simd_work_items' attribute must evenly divide the index that -// increments fastest in the 'reqd_work_group_size' attribute. -// -// The arguments to reqd_work_group_size are ordered based on which index -// increments the fastest. In OpenCL, the first argument is the index that -// increments the fastest, and in SYCL, the last argument is the index that -// increments the fastest. -// -// __attribute__((reqd_work_group_size)) follows the OpenCL rules in OpenCL -// mode. All spellings of reqd_work_group_size attribute (regardless of -// syntax used) follow the SYCL rules when in SYCL mode. -static bool CheckWorkGroupSize(Sema &S, const Expr *NSWIValue, - const Expr *RWGSXDim, const Expr *RWGSYDim, - const Expr *RWGSZDim) { - // If any of the operand is still value dependent, we can't test anything. - const auto *NSWIValueExpr = dyn_cast(NSWIValue); - const auto *RWGSXDimExpr = dyn_cast(RWGSXDim); - - if (!NSWIValueExpr || !RWGSXDimExpr) - return false; - - // Y and Z may be optional so we allow them to be null and consider them - // dependent if the original epxression was not null while the result of the - // cast is. - const auto *RWGSYDimExpr = dyn_cast_or_null(RWGSYDim); - const auto *RWGSZDimExpr = dyn_cast_or_null(RWGSZDim); - - if ((!RWGSYDimExpr && RWGSYDim) || (!RWGSZDimExpr && RWGSZDim)) - return false; - - // Otherwise, check which argument increments the fastest. - const ConstantExpr *LastRWGSDimExpr = - RWGSZDim ? RWGSZDimExpr : (RWGSYDim ? RWGSYDimExpr : RWGSXDimExpr); - unsigned WorkGroupSize = LastRWGSDimExpr->getResultAsAPSInt().getZExtValue(); - - // Check if the required work group size specified by 'num_simd_work_items' - // attribute evenly divides the index that increments fastest in the - // 'reqd_work_group_size' attribute. - return WorkGroupSize % NSWIValueExpr->getResultAsAPSInt().getZExtValue() != 0; -} - -void Sema::AddSYCLReqdWorkGroupSizeAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, Expr *ZDim) { - // Returns nullptr if diagnosing, otherwise returns the original expression - // or the original expression converted to a constant expression. - auto CheckAndConvertArg = [&](Expr *E) -> std::optional { - // Check if the expression is not value dependent. - if (E && !E->isValueDependent()) { - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return std::nullopt; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return std::nullopt; - } - } - return E; - }; - - // Check all three argument values, and if any are bad, bail out. This will - // convert the given expressions into constant expressions when possible. - std::optional XDimConvert = CheckAndConvertArg(XDim); - std::optional YDimConvert = CheckAndConvertArg(YDim); - std::optional ZDimConvert = CheckAndConvertArg(ZDim); - if (!XDimConvert || !YDimConvert || !ZDimConvert) - return; - XDim = XDimConvert.value(); - YDim = YDimConvert.value(); - ZDim = ZDimConvert.value(); - - // If the declaration has a ReqdWorkGroupSizeAttr, check to see if - // the attribute holds values equal to (1, 1, 1) in case the value of - // SYCLIntelMaxGlobalWorkDimAttr equals to 0. - if (const auto *DeclAttr = D->getAttr()) { - if (InvalidWorkGroupSizeAttrs(*this, DeclAttr->getValue(), XDim, YDim, - ZDim)) { - Diag(CI.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) - << CI << DeclAttr; - } - } - - // If the 'max_work_group_size' attribute is specified on a declaration along - // with 'reqd_work_group_size' attribute, check to see if values of - // 'reqd_work_group_size' attribute arguments are equal to or less than values - // of 'max_work_group_size' attribute arguments. - // - // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments - // are greater than values of 'max_work_group_size' attribute arguments. - if (const auto *DeclAttr = D->getAttr()) { - if (CheckMaxAllowedWorkGroupSize(XDim, YDim, ZDim, DeclAttr->getXDim(), - DeclAttr->getYDim(), - DeclAttr->getZDim())) { - Diag(CI.getLoc(), diag::err_conflicting_sycl_function_attributes) - << CI << DeclAttr; - Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); - return; - } - } - - // If the 'reqd_work_group_size' attribute is specified on a declaration - // along with 'num_simd_work_items' attribute, the required work group size - // specified by 'num_simd_work_items' attribute must evenly divide the index - // that increments fastest in the 'reqd_work_group_size' attribute. - if (const auto *DeclAttr = D->getAttr()) { - if (CheckWorkGroupSize(*this, DeclAttr->getValue(), XDim, YDim, ZDim)) { - Diag(DeclAttr->getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) - << DeclAttr << CI; - Diag(CI.getLoc(), diag::note_conflicting_attribute); - return; - } - } - - // If the attribute was already applied with different arguments, then - // diagnose the second attribute as a duplicate and don't add it. - if (const auto *Existing = D->getAttr()) { - // If any of the results are known to be different, we can diagnose at this - // point and drop the attribute. - if (AnyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) { - Diag(CI.getLoc(), diag::err_duplicate_attribute) << CI; - Diag(Existing->getLoc(), diag::note_previous_attribute); - return; - } - - // If all of the results are known to be the same, we can silently drop the - // attribute. Otherwise, we have to add the attribute and resolve its - // differences later. - if (AllWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) - return; - } - - D->addAttr(::new (Context) - SYCLReqdWorkGroupSizeAttr(Context, CI, XDim, YDim, ZDim)); + S.SYCL().addSYCLIntelMaxWorkGroupsPerMultiprocessorAttr(D, AL, + AL.getArgAsExpr(0)); } SYCLReqdWorkGroupSizeAttr * @@ -3515,8 +3001,8 @@ Sema::MergeSYCLReqdWorkGroupSizeAttr(Decl *D, // attribute holds values equal to (1, 1, 1) in case the value of // SYCLIntelMaxGlobalWorkDimAttr equals to 0. if (const auto *DeclAttr = D->getAttr()) { - if (InvalidWorkGroupSizeAttrs(*this, DeclAttr->getValue(), A.getXDim(), - A.getYDim(), A.getZDim())) { + if (SYCL().areInvalidWorkGroupSizeAttrs(DeclAttr->getValue(), A.getXDim(), + A.getYDim(), A.getZDim())) { Diag(A.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) << &A << DeclAttr; return nullptr; @@ -3531,9 +3017,9 @@ Sema::MergeSYCLReqdWorkGroupSizeAttr(Decl *D, // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments // are greater than values of 'max_work_group_size' attribute arguments. if (const auto *DeclAttr = D->getAttr()) { - if (CheckMaxAllowedWorkGroupSize(A.getXDim(), A.getYDim(), A.getZDim(), - DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim())) { + if (SYCL().checkMaxAllowedWorkGroupSize( + A.getXDim(), A.getYDim(), A.getZDim(), DeclAttr->getXDim(), + DeclAttr->getYDim(), DeclAttr->getZDim())) { Diag(DeclAttr->getLoc(), diag::err_conflicting_sycl_function_attributes) << DeclAttr << &A; Diag(A.getLoc(), diag::note_conflicting_attribute); @@ -3546,8 +3032,8 @@ Sema::MergeSYCLReqdWorkGroupSizeAttr(Decl *D, // specified by 'num_simd_work_items' attribute must evenly divide the index // that increments fastest in the 'reqd_work_group_size' attribute. if (const auto *DeclAttr = D->getAttr()) { - if (CheckWorkGroupSize(*this, DeclAttr->getValue(), A.getXDim(), - A.getYDim(), A.getZDim())) { + if (SYCL().checkWorkGroupSize(DeclAttr->getValue(), A.getXDim(), + A.getYDim(), A.getZDim())) { Diag(DeclAttr->getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) << DeclAttr << &A; Diag(A.getLoc(), diag::note_conflicting_attribute); @@ -3559,9 +3045,9 @@ Sema::MergeSYCLReqdWorkGroupSizeAttr(Decl *D, if (const auto *DeclAttr = D->getAttr()) { // If any of the results are known to be different, we can diagnose at this // point and drop the attribute. - if (AnyWorkGroupSizesDiffer(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), A.getXDim(), A.getYDim(), - A.getZDim())) { + if (SYCL().anyWorkGroupSizesDiffer(DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim(), A.getXDim(), + A.getYDim(), A.getZDim())) { Diag(DeclAttr->getLoc(), diag::err_duplicate_attribute) << &A; Diag(A.getLoc(), diag::note_previous_attribute); return nullptr; @@ -3570,9 +3056,9 @@ Sema::MergeSYCLReqdWorkGroupSizeAttr(Decl *D, // If all of the results are known to be the same, we can silently drop the // attribute. Otherwise, we have to add the attribute and resolve its // differences later. - if (AllWorkGroupSizesSame(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), A.getXDim(), A.getYDim(), - A.getZDim())) + if (SYCL().allWorkGroupSizesSame(DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim(), A.getXDim(), + A.getYDim(), A.getZDim())) return nullptr; } @@ -3581,7 +3067,7 @@ Sema::MergeSYCLReqdWorkGroupSizeAttr(Decl *D, } static void handleSYCLReqdWorkGroupSize(Sema &S, Decl *D, const ParsedAttr &AL){ - S.CheckDeprecatedSYCLAttributeSpelling(AL); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(AL); // __attribute__((reqd_work_group_size)) and [[cl::reqd_work_group_size]] // all require exactly three arguments. @@ -3598,7 +3084,7 @@ static void handleSYCLReqdWorkGroupSize(Sema &S, Decl *D, const ParsedAttr &AL){ Expr *XDimExpr = NumArgs > 0 ? AL.getArgAsExpr(0) : nullptr; Expr *YDimExpr = NumArgs > 1 ? AL.getArgAsExpr(1) : nullptr; Expr *ZDimExpr = NumArgs > 2 ? AL.getArgAsExpr(2) : nullptr; - S.AddSYCLReqdWorkGroupSizeAttr(D, AL, XDimExpr, YDimExpr, ZDimExpr); + S.SYCL().addSYCLReqdWorkGroupSizeAttr(D, AL, XDimExpr, YDimExpr, ZDimExpr); } static void handleReqdWorkGroupSize(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -3609,65 +3095,6 @@ static void handleReqdWorkGroupSize(Sema &S, Decl *D, const ParsedAttr &AL) { handleWorkGroupSize(S, D, AL); } -void Sema::AddIntelReqdSubGroupSize(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - auto &TI = Context.getTargetInfo(); - if (TI.getTriple().isNVPTX() && ArgVal != 32) - Diag(E->getExprLoc(), diag::warn_reqd_sub_group_attribute_n) - << ArgVal.getSExtValue() << TI.getTriple().getArchName() << 32; - if (TI.getTriple().isAMDGPU()) { - const auto HasWaveFrontSize64 = - TI.getTargetOpts().FeatureMap["wavefrontsize64"]; - const auto HasWaveFrontSize32 = - TI.getTargetOpts().FeatureMap["wavefrontsize32"]; - - // CDNA supports only 64 wave front size, for those GPUs allow subgroup - // size of 64. Some GPUs support both 32 and 64, for those (and the rest) - // only allow 32. Warn on incompatible sizes. - const auto SupportedWaveFrontSize = - HasWaveFrontSize64 && !HasWaveFrontSize32 ? 64 : 32; - if (ArgVal != SupportedWaveFrontSize) - Diag(E->getExprLoc(), diag::warn_reqd_sub_group_attribute_n) - << ArgVal.getSExtValue() << TI.getTriple().getArchName() - << SupportedWaveFrontSize; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) IntelReqdSubGroupSizeAttr(Context, CI, E)); -} - IntelReqdSubGroupSizeAttr * Sema::MergeIntelReqdSubGroupSizeAttr(Decl *D, const IntelReqdSubGroupSizeAttr &A) { @@ -3691,10 +3118,10 @@ Sema::MergeIntelReqdSubGroupSizeAttr(Decl *D, static void handleIntelReqdSubGroupSize(Sema &S, Decl *D, const ParsedAttr &AL) { - S.CheckDeprecatedSYCLAttributeSpelling(AL); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(AL); Expr *E = AL.getArgAsExpr(0); - S.AddIntelReqdSubGroupSize(D, AL, E); + S.SYCL().addIntelReqdSubGroupSize(D, AL, E); } IntelNamedSubGroupSizeAttr * @@ -3734,60 +3161,6 @@ static void handleIntelNamedSubGroupSize(Sema &S, Decl *D, D->addAttr(IntelNamedSubGroupSizeAttr::Create(S.Context, SizeType, AL)); } -void Sema::AddSYCLIntelNumSimdWorkItemsAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - - // If the 'reqd_work_group_size' attribute is specified on a declaration - // along with 'num_simd_work_items' attribute, the required work group size - // specified by 'num_simd_work_items' attribute must evenly divide the index - // that increments fastest in the 'reqd_work_group_size' attribute. - if (const auto *DeclAttr = D->getAttr()) { - if (CheckWorkGroupSize(*this, E, DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim())) { - Diag(CI.getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) - << CI << DeclAttr; - Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); - return; - } - } - } - - D->addAttr(::new (Context) SYCLIntelNumSimdWorkItemsAttr(Context, CI, E)); -} - SYCLIntelNumSimdWorkItemsAttr *Sema::MergeSYCLIntelNumSimdWorkItemsAttr( Decl *D, const SYCLIntelNumSimdWorkItemsAttr &A) { // Check to see if there's a duplicate attribute with different values @@ -3810,8 +3183,8 @@ SYCLIntelNumSimdWorkItemsAttr *Sema::MergeSYCLIntelNumSimdWorkItemsAttr( // specified by 'num_simd_work_items' attribute must evenly divide the index // that increments fastest in the 'reqd_work_group_size' attribute. if (const auto *DeclAttr = D->getAttr()) { - if (CheckWorkGroupSize(*this, A.getValue(), DeclAttr->getXDim(), - DeclAttr->getYDim(), DeclAttr->getZDim())) { + if (SYCL().checkWorkGroupSize(A.getValue(), DeclAttr->getXDim(), + DeclAttr->getYDim(), DeclAttr->getZDim())) { Diag(A.getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) << &A << DeclAttr; Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); @@ -3826,7 +3199,7 @@ SYCLIntelNumSimdWorkItemsAttr *Sema::MergeSYCLIntelNumSimdWorkItemsAttr( static void handleSYCLIntelNumSimdWorkItemsAttr(Sema &S, Decl *D, const ParsedAttr &A) { Expr *E = A.getArgAsExpr(0); - S.AddSYCLIntelNumSimdWorkItemsAttr(D, A, E); + S.SYCL().addSYCLIntelNumSimdWorkItemsAttr(D, A, E); } // Handles use_stall_enable_clusters @@ -3836,47 +3209,6 @@ static void handleSYCLIntelUseStallEnableClustersAttr(Sema &S, Decl *D, SYCLIntelUseStallEnableClustersAttr(S.Context, A)); } -// Handles initiation_interval attribute. -void Sema::AddSYCLIntelInitiationIntervalAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = - D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getNExpr())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) - SYCLIntelInitiationIntervalAttr(Context, CI, E)); -} - SYCLIntelInitiationIntervalAttr * Sema::MergeSYCLIntelInitiationIntervalAttr( Decl *D, const SYCLIntelInitiationIntervalAttr &A) { @@ -3902,51 +3234,9 @@ Sema::MergeSYCLIntelInitiationIntervalAttr( static void handleSYCLIntelInitiationIntervalAttr(Sema &S, Decl *D, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); - - S.AddSYCLIntelInitiationIntervalAttr(D, A, A.getArgAsExpr(0)); -} - -// Handle scheduler_target_fmax_mhz -void Sema::AddSYCLIntelSchedulerTargetFmaxMhzAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = - D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); - D->addAttr(::new (Context) - SYCLIntelSchedulerTargetFmaxMhzAttr(Context, CI, E)); + S.SYCL().addSYCLIntelInitiationIntervalAttr(D, A, A.getArgAsExpr(0)); } SYCLIntelSchedulerTargetFmaxMhzAttr * @@ -3975,257 +3265,46 @@ Sema::MergeSYCLIntelSchedulerTargetFmaxMhzAttr( static void handleSYCLIntelSchedulerTargetFmaxMhzAttr(Sema &S, Decl *D, const ParsedAttr &AL) { Expr *E = AL.getArgAsExpr(0); - S.AddSYCLIntelSchedulerTargetFmaxMhzAttr(D, AL, E); -} - -// Handles max_global_work_dim. -// Returns a OneArgResult value; EqualToOne means all argument values are -// equal to one, NotEqualToOne means at least one argument value is not -// equal to one, and Unknown means that at least one of the argument values -// could not be determined. -enum class OneArgResult { Unknown, EqualToOne, NotEqualToOne }; -static OneArgResult AreAllArgsOne(const Expr *Args[], size_t Count) { - - for (size_t Idx = 0; Idx < Count; ++Idx) { - const Expr *Arg = Args[Idx]; - // Optional arguments are considered trivially one. - if (!Arg) - return OneArgResult::EqualToOne; - const auto *CE = dyn_cast(Args[Idx]); - if (!CE) - return OneArgResult::Unknown; - if (CE->getResultAsAPSInt() != 1) - return OneArgResult::NotEqualToOne; - } - return OneArgResult::EqualToOne; -} - -// If the declaration has a SYCLIntelMaxWorkGroupSizeAttr or -// ReqdWorkGroupSizeAttr, check to see if they hold equal values -// (1, 1, 1). Returns true if diagnosed. -template -static bool checkWorkGroupSizeAttrExpr(Sema &S, Decl *D, - const AttributeCommonInfo &AL) { - if (const auto *A = D->getAttr()) { - const Expr *Args[3] = {A->getXDim(), A->getYDim(), A->getZDim()}; - if (OneArgResult::NotEqualToOne == AreAllArgsOne(Args, 3)) { - S.Diag(A->getLocation(), diag::err_sycl_x_y_z_arguments_must_be_one) - << A << AL; - return true; - } - } - return false; + S.SYCL().addSYCLIntelSchedulerTargetFmaxMhzAttr(D, AL, E); } -void Sema::AddSYCLIntelMaxGlobalWorkDimAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute must be in the range [0, 3]. - if (ArgVal < 0 || ArgVal > 3) { - Diag(E->getBeginLoc(), diag::err_attribute_argument_out_of_range) - << CI << 0 << 3 << E->getSourceRange(); - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); +SYCLIntelMaxGlobalWorkDimAttr *Sema::MergeSYCLIntelMaxGlobalWorkDimAttr( + Decl *D, const SYCLIntelMaxGlobalWorkDimAttr &A) { + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (const auto *MergeExpr = dyn_cast(A.getValue())) { + if (DeclExpr->getResultAsAPSInt() != MergeExpr->getResultAsAPSInt()) { + Diag(DeclAttr->getLoc(), diag::warn_duplicate_attribute) << &A; + Diag(A.getLoc(), diag::note_previous_attribute); } - // Drop the duplicate attribute. - return; + // Do not add a duplicate attribute. + return nullptr; } } + } - // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr or - // SYCLReqdWorkGroupSizeAttr, check to see if the attribute holds values - // equal to (1, 1, 1) in case the value of SYCLIntelMaxGlobalWorkDimAttr - // equals to 0. - if (ArgVal == 0) { - if (checkWorkGroupSizeAttrExpr(*this, D, - CI) || - checkWorkGroupSizeAttrExpr(*this, D, CI)) - return; - } + // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr or + // SYCLReqdWorkGroupSizeAttr, check to see if the attribute holds values equal + // to (1, 1, 1) in case the value of SYCLIntelMaxGlobalWorkDimAttr equals to + // 0. + const auto *MergeExpr = dyn_cast(A.getValue()); + if (MergeExpr && MergeExpr->getResultAsAPSInt() == 0) { + if (SYCL().checkWorkGroupSizeAttrExpr(D, + A) || + SYCL().checkWorkGroupSizeAttrExpr(D, A)) + return nullptr; } - D->addAttr(::new (Context) SYCLIntelMaxGlobalWorkDimAttr(Context, CI, E)); -} - -// Check that the value is a non-negative integer constant that can fit in -// 32-bits. Issue correct error message and return false on failure. -bool static check32BitInt(const Expr *E, Sema &S, llvm::APSInt &I, - const AttributeCommonInfo &CI) { - if (!I.isIntN(32)) { - S.Diag(E->getExprLoc(), diag::err_ice_too_large) - << llvm::toString(I, 10, false) << 32 << /* Unsigned */ 1; - return false; - } - - if (I.isSigned() && I.isNegative()) { - S.Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /* Non-negative */ 1; - return false; - } - - return true; -} - -void Sema::AddSYCLIntelMinWorkGroupsPerComputeUnitAttr( - Decl *D, const AttributeCommonInfo &CI, Expr *E) { - if (Context.getLangOpts().SYCLIsDevice && - !Context.getTargetInfo().getTriple().isNVPTX()) { - Diag(E->getBeginLoc(), diag::warn_launch_bounds_is_cuda_specific) - << CI << E->getSourceRange(); - return; - } - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - if (!check32BitInt(E, *this, ArgVal, CI)) - return; - E = Res.get(); - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = - D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) - SYCLIntelMinWorkGroupsPerComputeUnitAttr(Context, CI, E)); -} - -// Helper to get CudaArch. -static CudaArch getCudaArch(const TargetInfo &TI) { - if (!TI.getTriple().isNVPTX()) - llvm_unreachable("getCudaArch is only valid for NVPTX triple"); - auto &TO = TI.getTargetOpts(); - return StringToCudaArch(TO.CPU); -} - -void Sema::AddSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( - Decl *D, const AttributeCommonInfo &CI, Expr *E) { - auto &TI = Context.getTargetInfo(); - if (Context.getLangOpts().SYCLIsDevice) { - if (!TI.getTriple().isNVPTX()) { - Diag(E->getBeginLoc(), diag::warn_launch_bounds_is_cuda_specific) - << CI << E->getSourceRange(); - return; - } - - // Feature '.maxclusterrank' requires .target sm_90 or higher. - auto SM = getCudaArch(TI); - if (SM == CudaArch::UNKNOWN || SM < CudaArch::SM_90) { - Diag(E->getBeginLoc(), diag::warn_cuda_maxclusterrank_sm_90) - << CudaArchToString(SM) << CI << E->getSourceRange(); - return; - } - } - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - if (!check32BitInt(E, *this, ArgVal, CI)) - return; - E = Res.get(); - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = - D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) - SYCLIntelMaxWorkGroupsPerMultiprocessorAttr(Context, CI, E)); -} - -SYCLIntelMaxGlobalWorkDimAttr *Sema::MergeSYCLIntelMaxGlobalWorkDimAttr( - Decl *D, const SYCLIntelMaxGlobalWorkDimAttr &A) { - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (const auto *MergeExpr = dyn_cast(A.getValue())) { - if (DeclExpr->getResultAsAPSInt() != MergeExpr->getResultAsAPSInt()) { - Diag(DeclAttr->getLoc(), diag::warn_duplicate_attribute) << &A; - Diag(A.getLoc(), diag::note_previous_attribute); - } - // Do not add a duplicate attribute. - return nullptr; - } - } - } - - // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr or - // SYCLReqdWorkGroupSizeAttr, check to see if the attribute holds values equal - // to (1, 1, 1) in case the value of SYCLIntelMaxGlobalWorkDimAttr equals to - // 0. - const auto *MergeExpr = dyn_cast(A.getValue()); - if (MergeExpr && MergeExpr->getResultAsAPSInt() == 0) { - if (checkWorkGroupSizeAttrExpr(*this, D, - A) || - checkWorkGroupSizeAttrExpr(*this, D, A)) - return nullptr; - } - - return ::new (Context) - SYCLIntelMaxGlobalWorkDimAttr(Context, A, A.getValue()); + return ::new (Context) + SYCLIntelMaxGlobalWorkDimAttr(Context, A, A.getValue()); } static void handleSYCLIntelMaxGlobalWorkDimAttr(Sema &S, Decl *D, const ParsedAttr &AL) { Expr *E = AL.getArgAsExpr(0); - S.AddSYCLIntelMaxGlobalWorkDimAttr(D, AL, E); + S.SYCL().addSYCLIntelMaxGlobalWorkDimAttr(D, AL, E); } SYCLIntelMinWorkGroupsPerComputeUnitAttr * @@ -4274,56 +3353,6 @@ Sema::MergeSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( SYCLIntelMaxWorkGroupsPerMultiprocessorAttr(Context, A, A.getValue()); } -// Handles [[intel::loop_fuse]] and [[intel::loop_fuse_independent]]. -void Sema::AddSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // [[intel::loop_fuse]] and [[intel::loop_fuse_independent]] are - // incompatible. - // FIXME: If additional spellings are provided for this attribute, - // this code will do the wrong thing. - if (DeclAttr->getAttributeSpellingListIndex() != - CI.getAttributeSpellingListIndex()) { - Diag(CI.getLoc(), diag::err_attributes_are_not_compatible) - << CI << DeclAttr << CI.isRegularKeywordAttribute(); - Diag(DeclAttr->getLocation(), diag::note_conflicting_attribute); - return; - } - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) SYCLIntelLoopFuseAttr(Context, CI, E)); -} - SYCLIntelLoopFuseAttr * Sema::MergeSYCLIntelLoopFuseAttr(Decl *D, const SYCLIntelLoopFuseAttr &A) { // Check to see if there's a duplicate attribute with different values @@ -4362,7 +3391,7 @@ static void handleSYCLIntelLoopFuseAttr(Sema &S, Decl *D, const ParsedAttr &A) { : IntegerLiteral::Create(S.Context, llvm::APInt(32, 1), S.Context.IntTy, A.getLoc()); - S.AddSYCLIntelLoopFuseAttr(D, A, E); + S.SYCL().addSYCLIntelLoopFuseAttr(D, A, E); } static void handleVecTypeHint(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -6218,45 +5247,6 @@ static void handleSYCLRegisterNumAttr(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(::new (S.Context) SYCLRegisterNumAttr(S.Context, AL, RegNo)); } -void Sema::AddSYCLIntelESimdVectorizeAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - if (ArgVal != 8 && ArgVal != 16 && ArgVal != 32) { - Diag(E->getExprLoc(), diag::err_sycl_esimd_vectorize_unsupported_value) - << CI; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) SYCLIntelESimdVectorizeAttr(Context, CI, E)); -} - SYCLIntelESimdVectorizeAttr * Sema::MergeSYCLIntelESimdVectorizeAttr(Decl *D, const SYCLIntelESimdVectorizeAttr &A) { @@ -6279,10 +5269,10 @@ Sema::MergeSYCLIntelESimdVectorizeAttr(Decl *D, static void handleSYCLIntelESimdVectorizeAttr(Sema &S, Decl *D, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); Expr *E = A.getArgAsExpr(0); - S.AddSYCLIntelESimdVectorizeAttr(D, A, E); + S.SYCL().addSYCLIntelESimdVectorizeAttr(D, A, E); } static void handleConstantAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -6865,7 +5855,7 @@ Sema::CreateLaunchBoundsAttr(const AttributeCommonInfo &CI, Expr *MaxThreads, if (MaxBlocks) { // '.maxclusterrank' ptx directive requires .target sm_90 or higher. - auto SM = getCudaArch(Context.getTargetInfo()); + auto SM = SYCL().getCudaArch(Context.getTargetInfo()); if (SM == CudaArch::UNKNOWN || SM < CudaArch::SM_90) { Diag(MaxBlocks->getBeginLoc(), diag::warn_cuda_maxclusterrank_sm_90) << CudaArchToString(SM) << CI << MaxBlocks->getSourceRange(); @@ -6970,78 +5960,6 @@ static bool checkForDuplicateAttribute(Sema &S, Decl *D, return false; } -// Checks if FPGA memory attributes apply on valid variables. -// Returns true if an error occured. -static bool CheckValidFPGAMemoryAttributesVar(Sema &S, Decl *D) { - // Check for SYCL device compilation context. - if (!S.Context.getLangOpts().SYCLIsDevice) { - return false; - } - - const auto *VD = dyn_cast(D); - if (!VD) - return false; - - // Exclude implicit parameters and non-type template parameters. - if (VD->getKind() == Decl::ImplicitParam || - VD->getKind() == Decl::NonTypeTemplateParm) - return false; - - // Check for non-static data member. - if (isa(D)) - return false; - - // Check for SYCL device global attribute decoration. - if (S.SYCL().isTypeDecoratedWithDeclAttribute( - VD->getType())) - return false; - - // Check for constant variables and variables in the OpenCL constant - // address space. - if (VD->getType().isConstQualified() || - VD->getType().getAddressSpace() == LangAS::opencl_constant) - return false; - - // Check for static storage class or local storage. - if (VD->getStorageClass() == SC_Static || VD->hasLocalStorage()) - return false; - - return true; -} - -void Sema::AddSYCLIntelNoGlobalWorkOffsetAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) SYCLIntelNoGlobalWorkOffsetAttr(Context, CI, E)); -} - SYCLIntelNoGlobalWorkOffsetAttr *Sema::MergeSYCLIntelNoGlobalWorkOffsetAttr( Decl *D, const SYCLIntelNoGlobalWorkOffsetAttr &A) { // Check to see if there's a duplicate attribute with different values @@ -7070,7 +5988,7 @@ static void handleSYCLIntelNoGlobalWorkOffsetAttr(Sema &S, Decl *D, : IntegerLiteral::Create(S.Context, llvm::APInt(32, 1), S.Context.IntTy, A.getLoc()); - S.AddSYCLIntelNoGlobalWorkOffsetAttr(D, A, E); + S.SYCL().addSYCLIntelNoGlobalWorkOffsetAttr(D, A, E); } /// Handle the [[intel::singlepump]] attribute. @@ -7091,7 +6009,7 @@ static void handleSYCLIntelSinglePumpAttr(Sema &S, Decl *D, // static variables, non-static data members, and device_global variables // for the device compilation. if ((D->getKind() == Decl::ParmVar) || - CheckValidFPGAMemoryAttributesVar(S, D)) { + S.SYCL().checkValidFPGAMemoryAttributesVar(D)) { S.Diag(AL.getLoc(), diag::err_fpga_attribute_incorrect_variable) << AL << /*agent memory arguments*/ 0; return; @@ -7124,7 +6042,7 @@ static void handleSYCLIntelDoublePumpAttr(Sema &S, Decl *D, // static variables, non-static data members, and device_global variables // for the device compilation. if ((D->getKind() == Decl::ParmVar) || - CheckValidFPGAMemoryAttributesVar(S, D)) { + S.SYCL().checkValidFPGAMemoryAttributesVar(D)) { S.Diag(AL.getLoc(), diag::err_fpga_attribute_incorrect_variable) << AL << /*agent memory arguments*/ 0; return; @@ -7177,7 +6095,7 @@ static void handleSYCLIntelMemoryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Check attribute applies to field, constant variables, local variables, // static variables, agent memory arguments, non-static data members, // and device_global variables for the device compilation. - if (CheckValidFPGAMemoryAttributesVar(S, D)) { + if (S.SYCL().checkValidFPGAMemoryAttributesVar(D)) { S.Diag(AL.getLoc(), diag::err_fpga_attribute_incorrect_variable) << AL << /*agent memory arguments*/ 1; return; @@ -7206,7 +6124,7 @@ static void handleSYCLIntelRegisterAttr(Sema &S, Decl *D, // static variables, non-static data members, and device_global variables // for the device compilation. if ((D->getKind() == Decl::ParmVar) || - CheckValidFPGAMemoryAttributesVar(S, D)) { + S.SYCL().checkValidFPGAMemoryAttributesVar(D)) { S.Diag(A.getLoc(), diag::err_fpga_attribute_incorrect_variable) << A << /*agent memory arguments*/ 0; return; @@ -7215,73 +6133,6 @@ static void handleSYCLIntelRegisterAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) SYCLIntelRegisterAttr(S.Context, A)); } -/// Handle the [[intel::bankwidth]] and [[intel::numbanks]] attributes. -/// These require a single constant power of two greater than zero. -/// These are incompatible with the register attribute. -/// The numbanks and bank_bits attributes are related. If bank_bits exists -/// when handling numbanks they are checked for consistency. - -void Sema::AddSYCLIntelBankWidthAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - - // This attribute requires a single constant power of two greater than zero. - if (!ArgVal.isPowerOf2()) { - Diag(E->getExprLoc(), diag::err_attribute_argument_not_power_of_two) - << CI; - return; - } - - // Check attribute applies to field, constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (CheckValidFPGAMemoryAttributesVar(*this, D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - // If the declaration does not have an [[intel::fpga_memory]] - // attribute, this creates one as an implicit attribute. - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) SYCLIntelBankWidthAttr(Context, CI, E)); -} - SYCLIntelBankWidthAttr * Sema::MergeSYCLIntelBankWidthAttr(Decl *D, const SYCLIntelBankWidthAttr &A) { // Check to see if there's a duplicate attribute with different values @@ -7302,83 +6153,7 @@ Sema::MergeSYCLIntelBankWidthAttr(Decl *D, const SYCLIntelBankWidthAttr &A) { static void handleSYCLIntelBankWidthAttr(Sema &S, Decl *D, const ParsedAttr &A) { - S.AddSYCLIntelBankWidthAttr(D, A, A.getArgAsExpr(0)); -} - -void Sema::AddSYCLIntelNumBanksAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - - // This attribute requires a single constant power of two greater than zero. - if (!ArgVal.isPowerOf2()) { - Diag(E->getExprLoc(), diag::err_attribute_argument_not_power_of_two) - << CI; - return; - } - - // Check or add the related BankBits attribute. - if (auto *BBA = D->getAttr()) { - unsigned NumBankBits = BBA->args_size(); - if (NumBankBits != ArgVal.ceilLogBase2()) { - Diag(E->getExprLoc(), diag::err_bankbits_numbanks_conflicting) << CI; - return; - } - } - - // Check attribute applies to constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (CheckValidFPGAMemoryAttributesVar(*this, D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - // If the declaration does not have an [[intel::fpga_memory]] - // attribute, this creates one as an implicit attribute. - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - // We are adding a user NumBanks attribute, drop any implicit default. - if (auto *NBA = D->getAttr()) { - if (NBA->isImplicit()) - D->dropAttr(); - } - - D->addAttr(::new (Context) SYCLIntelNumBanksAttr(Context, CI, E)); + S.SYCL().addSYCLIntelBankWidthAttr(D, A, A.getArgAsExpr(0)); } SYCLIntelNumBanksAttr * @@ -7400,7 +6175,7 @@ Sema::MergeSYCLIntelNumBanksAttr(Decl *D, const SYCLIntelNumBanksAttr &A) { } static void handleSYCLIntelNumBanksAttr(Sema &S, Decl *D, const ParsedAttr &A) { - S.AddSYCLIntelNumBanksAttr(D, A, A.getArgAsExpr(0)); + S.SYCL().addSYCLIntelNumBanksAttr(D, A, A.getArgAsExpr(0)); } static void handleIntelSimpleDualPortAttr(Sema &S, Decl *D, @@ -7419,10 +6194,10 @@ static void handleIntelSimpleDualPortAttr(Sema &S, Decl *D, // Check attribute applies to field, constant variables, local variables, // static variables, agent memory arguments, non-static data members, // and device_global variables for the device compilation. - if (CheckValidFPGAMemoryAttributesVar(S, D)) { - S.Diag(AL.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << AL << /*agent memory arguments*/ 1; - return; + if (S.SYCL().checkValidFPGAMemoryAttributesVar(D)) { + S.Diag(AL.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << AL << /*agent memory arguments*/ 1; + return; } if (!D->hasAttr()) @@ -7433,59 +6208,6 @@ static void handleIntelSimpleDualPortAttr(Sema &S, Decl *D, SYCLIntelSimpleDualPortAttr(S.Context, AL)); } -void Sema::AddSYCLIntelMaxReplicatesAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - - // Check attribute applies to field, constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (CheckValidFPGAMemoryAttributesVar(*this, D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - // If the declaration does not have an [[intel::fpga_memory]] - // attribute, this creates one as an implicit attribute. - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) SYCLIntelMaxReplicatesAttr(Context, CI, E)); -} - SYCLIntelMaxReplicatesAttr * Sema::MergeSYCLIntelMaxReplicatesAttr(Decl *D, const SYCLIntelMaxReplicatesAttr &A) { @@ -7509,7 +6231,7 @@ Sema::MergeSYCLIntelMaxReplicatesAttr(Decl *D, static void handleSYCLIntelMaxReplicatesAttr(Sema &S, Decl *D, const ParsedAttr &A) { - S.AddSYCLIntelMaxReplicatesAttr(D, A, A.getArgAsExpr(0)); + S.SYCL().addSYCLIntelMaxReplicatesAttr(D, A, A.getArgAsExpr(0)); } /// Handle the merge attribute. @@ -7547,7 +6269,7 @@ static void handleSYCLIntelMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // static variables, non-static data members, and device_global variables // for the device compilation. if ((D->getKind() == Decl::ParmVar) || - CheckValidFPGAMemoryAttributesVar(S, D)) { + S.SYCL().checkValidFPGAMemoryAttributesVar(D)) { S.Diag(AL.getLoc(), diag::err_fpga_attribute_incorrect_variable) << AL << /*agent memory arguments*/ 0; return; @@ -7580,199 +6302,12 @@ static void handleSYCLIntelBankBitsAttr(Sema &S, Decl *D, const ParsedAttr &A) { Args.push_back(A.getArgAsExpr(I)); } - S.AddSYCLIntelBankBitsAttr(D, A, Args.data(), Args.size()); -} - -void Sema::AddSYCLIntelBankBitsAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size) { - SYCLIntelBankBitsAttr TmpAttr(Context, CI, Exprs, Size); - SmallVector Args; - SmallVector Values; - bool ListIsValueDep = false; - for (auto *E : TmpAttr.args()) { - llvm::APSInt Value(32, /*IsUnsigned=*/false); - Expr::EvalResult Result; - ListIsValueDep = ListIsValueDep || E->isValueDependent(); - if (!E->isValueDependent()) { - ExprResult ICE = VerifyIntegerConstantExpression(E, &Value); - if (ICE.isInvalid()) - return; - if (!Value.isNonNegative()) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - E = ICE.get(); - } - Args.push_back(E); - Values.push_back(Value.getExtValue()); - } - - // Check that the list is consecutive. - if (!ListIsValueDep && Values.size() > 1) { - bool ListIsAscending = Values[0] < Values[1]; - for (int I = 0, E = Values.size() - 1; I < E; ++I) { - if (Values[I + 1] != Values[I] + (ListIsAscending ? 1 : -1)) { - Diag(CI.getLoc(), diag::err_bankbits_non_consecutive) << &TmpAttr; - return; - } - } - } - - // Check or add the related numbanks attribute. - if (auto *NBA = D->getAttr()) { - Expr *E = NBA->getValue(); - if (!E->isValueDependent()) { - Expr::EvalResult Result; - E->EvaluateAsInt(Result, Context); - llvm::APSInt Value = Result.Val.getInt(); - if (Args.size() != Value.ceilLogBase2()) { - Diag(TmpAttr.getLoc(), diag::err_bankbits_numbanks_conflicting); - return; - } - } - } else { - llvm::APInt Num(32, (unsigned)(1 << Args.size())); - Expr *NBE = - IntegerLiteral::Create(Context, Num, Context.IntTy, SourceLocation()); - D->addAttr(SYCLIntelNumBanksAttr::CreateImplicit(Context, NBE)); - } - - // Check attribute applies to field, constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (CheckValidFPGAMemoryAttributesVar(*this, D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) - SYCLIntelBankBitsAttr(Context, CI, Args.data(), Args.size())); -} - -void Sema::AddSYCLIntelPrivateCopiesAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - - // Check attribute applies to field as well as const variables, non-static - // local variables, non-static data members, and device_global variables. - // for the device compilation. - if (const auto *VD = dyn_cast(D)) { - if (Context.getLangOpts().SYCLIsDevice && - (!(isa(D) || - (VD->getKind() != Decl::ImplicitParam && - VD->getKind() != Decl::NonTypeTemplateParm && - VD->getKind() != Decl::ParmVar && - (VD->hasLocalStorage() || - SYCL().isTypeDecoratedWithDeclAttribute( - VD->getType())))))) { - Diag(CI.getLoc(), diag::err_fpga_attribute_invalid_decl) << CI; - return; - } - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - // If the declaration does not have [[intel::fpga_memory]] - // attribute, this creates default implicit memory. - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) SYCLIntelPrivateCopiesAttr(Context, CI, E)); + S.SYCL().addSYCLIntelBankBitsAttr(D, A, Args.data(), Args.size()); } static void handleSYCLIntelPrivateCopiesAttr(Sema &S, Decl *D, const ParsedAttr &A) { - S.AddSYCLIntelPrivateCopiesAttr(D, A, A.getArgAsExpr(0)); -} - -void Sema::AddSYCLIntelForcePow2DepthAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute accepts values 0 and 1 only. - if (ArgVal < 0 || ArgVal > 1) { - Diag(E->getBeginLoc(), diag::err_attribute_argument_is_not_valid) << CI; - return; - } - - // Check attribute applies to field, constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (CheckValidFPGAMemoryAttributesVar(*this, D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // If there is no mismatch, drop any duplicate attributes. - return; - } - } - } - - // If the declaration does not have an [[intel::fpga_memory]] - // attribute, this creates one as an implicit attribute. - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) SYCLIntelForcePow2DepthAttr(Context, CI, E)); + S.SYCL().addSYCLIntelPrivateCopiesAttr(D, A, A.getArgAsExpr(0)); } SYCLIntelForcePow2DepthAttr * @@ -7798,7 +6333,7 @@ Sema::MergeSYCLIntelForcePow2DepthAttr(Decl *D, static void handleSYCLIntelForcePow2DepthAttr(Sema &S, Decl *D, const ParsedAttr &A) { - S.AddSYCLIntelForcePow2DepthAttr(D, A, A.getArgAsExpr(0)); + S.SYCL().addSYCLIntelForcePow2DepthAttr(D, A, A.getArgAsExpr(0)); } static void handleXRayLogArgsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -7833,57 +6368,6 @@ static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D, PatchableFunctionEntryAttr(S.Context, AL, Count, Offset)); } -void Sema::addSYCLIntelPipeIOAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - VarDecl *VD = cast(D); - QualType Ty = VD->getType(); - // TODO: Applicable only on pipe storages. Currently they are defined - // as structures inside of SYCL headers. Add a check for pipe_storage_t - // when it is ready. - if (!Ty->isStructureType()) { - Diag(CI.getLoc(), diag::err_attribute_wrong_decl_type_str) - << CI << CI.isRegularKeywordAttribute() - << "SYCL pipe storage declaration"; - return; - } - - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getID())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) SYCLIntelPipeIOAttr(Context, CI, E)); -} - SYCLIntelPipeIOAttr * Sema::MergeSYCLIntelPipeIOAttr(Decl *D, const SYCLIntelPipeIOAttr &A) { // Check to see if there's a duplicate attribute with different values @@ -7907,7 +6391,7 @@ Sema::MergeSYCLIntelPipeIOAttr(Decl *D, const SYCLIntelPipeIOAttr &A) { static void handleSYCLIntelPipeIOAttr(Sema &S, Decl *D, const ParsedAttr &A) { Expr *E = A.getArgAsExpr(0); - S.addSYCLIntelPipeIOAttr(D, A, E); + S.SYCL().addSYCLIntelPipeIOAttr(D, A, E); } SYCLIntelMaxConcurrencyAttr *Sema::MergeSYCLIntelMaxConcurrencyAttr( @@ -7930,160 +6414,10 @@ SYCLIntelMaxConcurrencyAttr *Sema::MergeSYCLIntelMaxConcurrencyAttr( return ::new (Context) SYCLIntelMaxConcurrencyAttr(Context, A, A.getNExpr()); } -void Sema::AddSYCLIntelMaxConcurrencyAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - llvm::APSInt ArgVal; - ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getNExpr())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) SYCLIntelMaxConcurrencyAttr(Context, CI, E)); -} - static void handleSYCLIntelMaxConcurrencyAttr(Sema &S, Decl *D, const ParsedAttr &A) { Expr *E = A.getArgAsExpr(0); - S.AddSYCLIntelMaxConcurrencyAttr(D, A, E); -} - -// Checks if an expression is a valid filter list for an add_ir_attributes_* -// attribute. Returns true if an error occured. -static bool checkAddIRAttributesFilterListExpr(Expr *FilterListArg, Sema &S, - const AttributeCommonInfo &CI) { - const auto *FilterListE = cast(FilterListArg); - for (const Expr *FilterElemE : FilterListE->inits()) - if (!isa(FilterElemE)) - return S.Diag(FilterElemE->getBeginLoc(), - diag::err_sycl_add_ir_attribute_invalid_filter) - << CI; - return false; -} - -// Returns true if a type is either an array of char or a pointer to char. -static bool isAddIRAttributesValidStringType(QualType T) { - if (!T->isArrayType() && !T->isPointerType()) - return false; - QualType ElemT = T->isArrayType() - ? cast(T.getTypePtr())->getElementType() - : T->getPointeeType(); - return ElemT.isConstQualified() && ElemT->isCharType(); -} - -// Checks if an expression is a valid attribute name for an add_ir_attributes_* -// attribute. Returns true if an error occured. -static bool checkAddIRAttributesNameExpr(Expr *NameArg, Sema &S, - const AttributeCommonInfo &CI) { - // Only strings and const char * are valid name arguments. - if (isAddIRAttributesValidStringType(NameArg->getType())) - return false; - - return S.Diag(NameArg->getBeginLoc(), - diag::err_sycl_add_ir_attribute_invalid_name) - << CI; -} - -// Checks if an expression is a valid attribute value for an add_ir_attributes_* -// attribute. Returns true if an error occured. -static bool checkAddIRAttributesValueExpr(Expr *ValArg, Sema &S, - const AttributeCommonInfo &CI) { - QualType ValType = ValArg->getType(); - if (isAddIRAttributesValidStringType(ValType) || ValType->isNullPtrType() || - ValType->isIntegralOrEnumerationType() || ValType->isFloatingType()) - return false; - - return S.Diag(ValArg->getBeginLoc(), - diag::err_sycl_add_ir_attribute_invalid_value) - << CI; -} - -// Checks and evaluates arguments of an add_ir_attributes_* attribute. Returns -// true if an error occured. -static bool evaluateAddIRAttributesArgs(Expr **Args, size_t ArgsSize, Sema &S, - const AttributeCommonInfo &CI) { - ASTContext &Context = S.getASTContext(); - - // Check filter list if it is the first argument. - bool HasFilter = ArgsSize && isa(Args[0]); - if (HasFilter && checkAddIRAttributesFilterListExpr(Args[0], S, CI)) - return true; - - llvm::SmallVector Notes; - bool HasDependentArg = false; - for (unsigned I = HasFilter; I < ArgsSize; I++) { - Expr *&E = Args[I]; - - if (isa(E)) - return S.Diag(E->getBeginLoc(), - diag::err_sycl_add_ir_attr_filter_list_invalid_arg) - << CI; - - if (E->isValueDependent() || E->isTypeDependent()) { - HasDependentArg = true; - continue; - } - - Expr::EvalResult Eval; - Eval.Diag = &Notes; - if (!E->EvaluateAsConstantExpr(Eval, Context) || !Notes.empty()) { - S.Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type) - << CI << (I + 1) << AANT_ArgumentConstantExpr; - for (auto &Note : Notes) - S.Diag(Note.first, Note.second); - return true; - } - assert(Eval.Val.hasValue()); - E = ConstantExpr::Create(Context, E, Eval.Val); - } - - // If there are no dependent expressions, check for expected number of args. - if (!HasDependentArg && ArgsSize && (ArgsSize - HasFilter) & 1) - return S.Diag(CI.getLoc(), diag::err_sycl_add_ir_attribute_must_have_pairs) - << CI; - - // If there are no dependent expressions, check argument types. - // First half of the arguments are names, the second half are values. - unsigned MidArg = (ArgsSize - HasFilter) / 2 + HasFilter; - if (!HasDependentArg) { - for (unsigned I = HasFilter; I < ArgsSize; ++I) { - if ((I < MidArg && checkAddIRAttributesNameExpr(Args[I], S, CI)) || - (I >= MidArg && checkAddIRAttributesValueExpr(Args[I], S, CI))) - return true; - } - } - return false; -} - -static bool hasDependentExpr(Expr **Exprs, const size_t ExprsSize) { - return std::any_of(Exprs, Exprs + ExprsSize, [](const Expr *E) { - return E->isValueDependent() || E->isTypeDependent(); - }); + S.SYCL().addSYCLIntelMaxConcurrencyAttr(D, A, E); } static bool hasSameSYCLAddIRAttributes( @@ -8103,8 +6437,9 @@ static bool checkSYCLAddIRAttributesMergeability(const AddIRAttrT &NewAttr, ASTContext &Context = S.getASTContext(); // If there are no dependent argument expressions and the filters or the // attributes are different, then fail due to differing duplicates. - if (!hasDependentExpr(NewAttr.args_begin(), NewAttr.args_size()) && - !hasDependentExpr(ExistingAttr.args_begin(), ExistingAttr.args_size()) && + if (!S.SYCL().hasDependentExpr(NewAttr.args_begin(), NewAttr.args_size()) && + !S.SYCL().hasDependentExpr(ExistingAttr.args_begin(), + ExistingAttr.args_size()) && (NewAttr.getAttributeFilter() != ExistingAttr.getAttributeFilter() || !hasSameSYCLAddIRAttributes( NewAttr.getAttributeNameValuePairs(Context), @@ -8122,8 +6457,8 @@ void Sema::CheckSYCLAddIRAttributesFunctionAttrConflicts(Decl *D) { // If there is no such attribute there is nothing to check. If there are // dependent arguments we cannot know the actual number of arguments so we // defer the check. - if (!AddIRFuncAttr || - hasDependentExpr(AddIRFuncAttr->args_begin(), AddIRFuncAttr->args_size())) + if (!AddIRFuncAttr || SYCL().hasDependentExpr(AddIRFuncAttr->args_begin(), + AddIRFuncAttr->args_size())) return; // If there are no name-value pairs in the attribute it will not have an @@ -8164,54 +6499,6 @@ SYCLAddIRAttributesFunctionAttr *Sema::MergeSYCLAddIRAttributesFunctionAttr( return A.clone(Context); } -void Sema::AddSYCLAddIRAttributesFunctionAttr(Decl *D, - const AttributeCommonInfo &CI, - MutableArrayRef Args) { - if (const auto *FuncD = dyn_cast(D)) { - if (FuncD->isDefaulted()) { - Diag(CI.getLoc(), diag::err_disallow_attribute_on_func) << CI << 0; - return; - } - if (FuncD->isDeleted()) { - Diag(CI.getLoc(), diag::err_disallow_attribute_on_func) << CI << 1; - return; - } - } - - auto *Attr = SYCLAddIRAttributesFunctionAttr::Create(Context, Args.data(), - Args.size(), CI); - if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, - CI)) - return; - D->addAttr(Attr); - - // There are compile-time SYCL properties which we would like to turn into - // attributes to enable compiler diagnostics. - // At the moment the only such property is related to virtual functions and - // it is turned into sycl_device attribute. This is a tiny optimization to - // avoid deep dive into the attribute if we already know that a declaration - // is a device declaration. It may have to be removed later if/when we add - // handling of more compile-time properties here. - if (D->hasAttr()) - return; - - // SYCL Headers use template magic to pass key=value pairs to the attribute - // and we should make sure that all template instantiations are done before - // accessing attribute arguments. - if (hasDependentExpr(Attr->args_begin(), Attr->args_size())) - return; - - SmallVector, 4> Pairs = - Attr->getFilteredAttributeNameValuePairs(Context); - - for (const auto &[Key, Value] : Pairs) { - if (Key == "indirectly-callable") { - D->addAttr(SYCLDeviceAttr::CreateImplicit(Context)); - break; - } - } -} - static void handleSYCLAddIRAttributesFunctionAttr(Sema &S, Decl *D, const ParsedAttr &A) { llvm::SmallVector Args; @@ -8221,7 +6508,7 @@ static void handleSYCLAddIRAttributesFunctionAttr(Sema &S, Decl *D, Args.push_back(A.getArgAsExpr(I)); } - S.AddSYCLAddIRAttributesFunctionAttr(D, A, Args); + S.SYCL().addSYCLAddIRAttributesFunctionAttr(D, A, Args); } SYCLAddIRAttributesKernelParameterAttr * @@ -8235,16 +6522,6 @@ Sema::MergeSYCLAddIRAttributesKernelParameterAttr( return A.clone(Context); } -void Sema::AddSYCLAddIRAttributesKernelParameterAttr( - Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { - auto *Attr = SYCLAddIRAttributesKernelParameterAttr::Create( - Context, Args.data(), Args.size(), CI); - if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, - CI)) - return; - D->addAttr(Attr); -} - static void handleSYCLAddIRAttributesKernelParameterAttr(Sema &S, Decl *D, const ParsedAttr &A) { llvm::SmallVector Args; @@ -8254,7 +6531,7 @@ static void handleSYCLAddIRAttributesKernelParameterAttr(Sema &S, Decl *D, Args.push_back(A.getArgAsExpr(I)); } - S.AddSYCLAddIRAttributesKernelParameterAttr(D, A, Args); + S.SYCL().addSYCLAddIRAttributesKernelParameterAttr(D, A, Args); } SYCLAddIRAttributesGlobalVariableAttr * @@ -8268,16 +6545,6 @@ Sema::MergeSYCLAddIRAttributesGlobalVariableAttr( return A.clone(Context); } -void Sema::AddSYCLAddIRAttributesGlobalVariableAttr( - Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { - auto *Attr = SYCLAddIRAttributesGlobalVariableAttr::Create( - Context, Args.data(), Args.size(), CI); - if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, - CI)) - return; - D->addAttr(Attr); -} - static void handleSYCLAddIRAttributesGlobalVariableAttr(Sema &S, Decl *D, const ParsedAttr &A) { llvm::SmallVector Args; @@ -8287,7 +6554,7 @@ static void handleSYCLAddIRAttributesGlobalVariableAttr(Sema &S, Decl *D, Args.push_back(A.getArgAsExpr(I)); } - S.AddSYCLAddIRAttributesGlobalVariableAttr(D, A, Args); + S.SYCL().addSYCLAddIRAttributesGlobalVariableAttr(D, A, Args); } SYCLAddIRAnnotationsMemberAttr *Sema::MergeSYCLAddIRAnnotationsMemberAttr( @@ -8299,17 +6566,6 @@ SYCLAddIRAnnotationsMemberAttr *Sema::MergeSYCLAddIRAnnotationsMemberAttr( return A.clone(Context); } -void Sema::AddSYCLAddIRAnnotationsMemberAttr(Decl *D, - const AttributeCommonInfo &CI, - MutableArrayRef Args) { - auto *Attr = SYCLAddIRAnnotationsMemberAttr::Create(Context, Args.data(), - Args.size(), CI); - if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, - CI)) - return; - D->addAttr(Attr); -} - static void handleSYCLAddIRAnnotationsMemberAttr(Sema &S, Decl *D, const ParsedAttr &A) { llvm::SmallVector Args; @@ -8319,7 +6575,7 @@ static void handleSYCLAddIRAnnotationsMemberAttr(Sema &S, Decl *D, Args.push_back(A.getArgAsExpr(I)); } - S.AddSYCLAddIRAnnotationsMemberAttr(D, A, Args); + S.SYCL().addSYCLAddIRAnnotationsMemberAttr(D, A, Args); } static bool SYCLAliasValid(ASTContext &Context, unsigned BuiltinID) { @@ -9113,17 +7369,6 @@ static void handleFunctionReturnThunksAttr(Sema &S, Decl *D, D->addAttr(FunctionReturnThunksAttr::Create(S.Context, Kind, AL)); } -bool isDeviceAspectType(const QualType Ty) { - const EnumType *ET = Ty->getAs(); - if (!ET) - return false; - - if (const auto *Attr = ET->getDecl()->getAttr()) - return Attr->getType() == SYCLTypeAttr::aspect; - - return false; -} - SYCLDeviceHasAttr *Sema::MergeSYCLDeviceHasAttr(Decl *D, const SYCLDeviceHasAttr &A) { if (const auto *ExistingAttr = D->getAttr()) { @@ -9139,24 +7384,6 @@ SYCLDeviceHasAttr *Sema::MergeSYCLDeviceHasAttr(Decl *D, SYCLDeviceHasAttr(Context, A, Args.data(), Args.size()); } -void Sema::AddSYCLDeviceHasAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size) { - - SYCLDeviceHasAttr TmpAttr(Context, CI, Exprs, Size); - SmallVector Aspects; - for (auto *E : TmpAttr.aspects()) - if (!isa(E) && !isDeviceAspectType(E->getType())) - Diag(E->getExprLoc(), diag::err_sycl_invalid_aspect_argument) << CI; - - if (const auto *ExistingAttr = D->getAttr()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute_exact) << CI; - Diag(ExistingAttr->getLoc(), diag::note_previous_attribute); - return; - } - - D->addAttr(::new (Context) SYCLDeviceHasAttr(Context, CI, Exprs, Size)); -} - static void handleSYCLDeviceHasAttr(Sema &S, Decl *D, const ParsedAttr &A) { // Ignore the attribute if compiling for the host side because aspects may not // be marked properly for such compilation @@ -9167,7 +7394,7 @@ static void handleSYCLDeviceHasAttr(Sema &S, Decl *D, const ParsedAttr &A) { for (unsigned I = 0; I < A.getNumArgs(); ++I) Args.push_back(A.getArgAsExpr(I)); - S.AddSYCLDeviceHasAttr(D, A, Args.data(), Args.size()); + S.SYCL().addSYCLDeviceHasAttr(D, A, Args.data(), Args.size()); } SYCLUsesAspectsAttr * @@ -9185,24 +7412,6 @@ Sema::MergeSYCLUsesAspectsAttr(Decl *D, const SYCLUsesAspectsAttr &A) { SYCLUsesAspectsAttr(Context, A, Args.data(), Args.size()); } -void Sema::AddSYCLUsesAspectsAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size) { - - SYCLUsesAspectsAttr TmpAttr(Context, CI, Exprs, Size); - SmallVector Aspects; - for (auto *E : TmpAttr.aspects()) - if (!isDeviceAspectType(E->getType())) - Diag(E->getExprLoc(), diag::err_sycl_invalid_aspect_argument) << CI; - - if (const auto *ExistingAttr = D->getAttr()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute_exact) << CI; - Diag(ExistingAttr->getLoc(), diag::note_previous_attribute); - return; - } - - D->addAttr(::new (Context) SYCLUsesAspectsAttr(Context, CI, Exprs, Size)); -} - static void handleSYCLUsesAspectsAttr(Sema &S, Decl *D, const ParsedAttr &A) { // Ignore the attribute if compiling for the host because aspects may not be // marked properly for such compilation @@ -9213,7 +7422,7 @@ static void handleSYCLUsesAspectsAttr(Sema &S, Decl *D, const ParsedAttr &A) { for (unsigned I = 0; I < A.getNumArgs(); ++I) Args.push_back(A.getArgAsExpr(I)); - S.AddSYCLUsesAspectsAttr(D, A, Args.data(), Args.size()); + S.SYCL().addSYCLUsesAspectsAttr(D, A, Args.data(), Args.size()); } static void handleAvailableOnlyInDefaultEvalMethod(Sema &S, Decl *D, diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 75e0d5fd9eb9f..b2816b7875307 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -23,8 +23,8 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" -#include "clang/Sema/Initialization.h" #include "clang/Sema/Attr.h" +#include "clang/Sema/Initialization.h" #include "clang/Sema/ParsedAttr.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/APSInt.h" @@ -5338,9 +5338,9 @@ static void PropagateAndDiagnoseDeviceAttr( case attr::Kind::SYCLReqdWorkGroupSize: { auto *RWGSA = cast(A); if (auto *Existing = SYCLKernel->getAttr()) { - if (S.SemaRef.AnyWorkGroupSizesDiffer( - Existing->getXDim(), Existing->getYDim(), Existing->getZDim(), - RWGSA->getXDim(), RWGSA->getYDim(), RWGSA->getZDim())) { + if (S.anyWorkGroupSizesDiffer(Existing->getXDim(), Existing->getYDim(), + Existing->getZDim(), RWGSA->getXDim(), + RWGSA->getYDim(), RWGSA->getZDim())) { S.Diag(SYCLKernel->getLocation(), diag::err_conflicting_sycl_kernel_attributes); S.Diag(Existing->getLocation(), diag::note_conflicting_attribute); @@ -5349,7 +5349,7 @@ static void PropagateAndDiagnoseDeviceAttr( } } else if (auto *Existing = SYCLKernel->getAttr()) { - if (S.SemaRef.CheckMaxAllowedWorkGroupSize( + if (S.checkMaxAllowedWorkGroupSize( RWGSA->getXDim(), RWGSA->getYDim(), RWGSA->getZDim(), Existing->getXDim(), Existing->getYDim(), Existing->getZDim())) { S.Diag(SYCLKernel->getLocation(), @@ -5368,9 +5368,9 @@ static void PropagateAndDiagnoseDeviceAttr( case attr::Kind::SYCLWorkGroupSizeHint: { auto *WGSH = cast(A); if (auto *Existing = SYCLKernel->getAttr()) { - if (S.SemaRef.AnyWorkGroupSizesDiffer( - Existing->getXDim(), Existing->getYDim(), Existing->getZDim(), - WGSH->getXDim(), WGSH->getYDim(), WGSH->getZDim())) { + if (S.anyWorkGroupSizesDiffer(Existing->getXDim(), Existing->getYDim(), + Existing->getZDim(), WGSH->getXDim(), + WGSH->getYDim(), WGSH->getZDim())) { S.Diag(SYCLKernel->getLocation(), diag::err_conflicting_sycl_kernel_attributes); S.Diag(Existing->getLocation(), diag::note_conflicting_attribute); @@ -5384,7 +5384,7 @@ static void PropagateAndDiagnoseDeviceAttr( case attr::Kind::SYCLIntelMaxWorkGroupSize: { auto *SIMWGSA = cast(A); if (auto *Existing = SYCLKernel->getAttr()) { - if (S.SemaRef.CheckMaxAllowedWorkGroupSize( + if (S.checkMaxAllowedWorkGroupSize( Existing->getXDim(), Existing->getYDim(), Existing->getZDim(), SIMWGSA->getXDim(), SIMWGSA->getYDim(), SIMWGSA->getZDim())) { S.Diag(SYCLKernel->getLocation(), @@ -6879,3 +6879,1781 @@ void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) { handleSimpleAttribute(*this, D, AL); } + +// Returns a DupArgResult value; Same means the args have the same value, +// Different means the args do not have the same value, and Unknown means that +// the args cannot (yet) be compared. +enum class DupArgResult { Unknown, Same, Different }; +static DupArgResult areArgValuesIdentical(const Expr *LHS, const Expr *RHS) { + // If both operands are nullptr they are unspecified and are considered the + // same. + if (!LHS && !RHS) + return DupArgResult::Same; + + // Otherwise, if either operand is nullptr they are considered different. + if (!LHS || !RHS) + return DupArgResult::Different; + + // Otherwise, if either operand is still value dependent, we can't test + // anything. + const auto *LHSCE = dyn_cast(LHS); + const auto *RHSCE = dyn_cast(RHS); + if (!LHSCE || !RHSCE) + return DupArgResult::Unknown; + + // Otherwise, test that the values. + return LHSCE->getResultAsAPSInt() == RHSCE->getResultAsAPSInt() + ? DupArgResult::Same + : DupArgResult::Different; +} + +// Returns true if any of the specified dimensions (X,Y,Z) differ between the +// arguments. +bool SemaSYCL::anyWorkGroupSizesDiffer(const Expr *LHSXDim, const Expr *LHSYDim, + const Expr *LHSZDim, const Expr *RHSXDim, + const Expr *RHSYDim, + const Expr *RHSZDim) { + DupArgResult Results[] = {areArgValuesIdentical(LHSXDim, RHSXDim), + areArgValuesIdentical(LHSYDim, RHSYDim), + areArgValuesIdentical(LHSZDim, RHSZDim)}; + return llvm::is_contained(Results, DupArgResult::Different); +} + +// Returns true if all of the specified dimensions (X,Y,Z) are the same between +// the arguments. +bool SemaSYCL::allWorkGroupSizesSame(const Expr *LHSXDim, const Expr *LHSYDim, + const Expr *LHSZDim, const Expr *RHSXDim, + const Expr *RHSYDim, const Expr *RHSZDim) { + DupArgResult Results[] = {areArgValuesIdentical(LHSXDim, RHSXDim), + areArgValuesIdentical(LHSYDim, RHSYDim), + areArgValuesIdentical(LHSZDim, RHSZDim)}; + return llvm::all_of(Results, + [](DupArgResult V) { return V == DupArgResult::Same; }); +} + +// Helper to get CudaArch. +CudaArch SemaSYCL::getCudaArch(const TargetInfo &TI) { + if (!TI.getTriple().isNVPTX()) + llvm_unreachable("getCudaArch is only valid for NVPTX triple"); + auto &TO = TI.getTargetOpts(); + return StringToCudaArch(TO.CPU); +} + +bool SemaSYCL::hasDependentExpr(Expr **Exprs, const size_t ExprsSize) { + return std::any_of(Exprs, Exprs + ExprsSize, [](const Expr *E) { + return E->isValueDependent() || E->isTypeDependent(); + }); +} + +void SemaSYCL::checkDeprecatedSYCLAttributeSpelling(const ParsedAttr &A, + StringRef NewName) { + // Additionally, diagnose the old [[intel::ii]] spelling. + if (A.getKind() == ParsedAttr::AT_SYCLIntelInitiationInterval && + A.getAttrName()->isStr("ii")) { + diagnoseDeprecatedAttribute(A, "intel", "initiation_interval"); + return; + } + + // Diagnose SYCL 2017 spellings in later SYCL modes. + if (getLangOpts().getSYCLVersion() > LangOptions::SYCL_2017) { + // All attributes in the cl vendor namespace are deprecated in favor of a + // name in the sycl namespace as of SYCL 2020. + if (A.hasScope() && A.getScopeName()->isStr("cl")) { + diagnoseDeprecatedAttribute(A, "sycl", NewName); + return; + } + + // All GNU-style spellings are deprecated in favor of a C++-style spelling. + if (A.getSyntax() == ParsedAttr::AS_GNU) { + // Note: we cannot suggest an automatic fix-it because GNU-style + // spellings can appear in locations that are not valid for a C++-style + // spelling, and the attribute could be part of an attribute list within + // a single __attribute__ specifier. Just tell the user it's deprecated + // manually. + // + // This currently assumes that the GNU-style spelling is the same as the + // SYCL 2020 spelling (sans the vendor namespace). + Diag(A.getLoc(), diag::warn_attribute_spelling_deprecated) + << "'" + A.getNormalizedFullName() + "'"; + Diag(A.getLoc(), diag::note_spelling_suggestion) + << "'[[sycl::" + A.getNormalizedFullName() + "]]'"; + return; + } + } + + // Diagnose SYCL 2020 spellings used in earlier SYCL modes as being an + // extension. + if (getLangOpts().getSYCLVersion() == LangOptions::SYCL_2017 && + A.hasScope() && A.getScopeName()->isStr("sycl")) { + Diag(A.getLoc(), diag::ext_sycl_2020_attr_spelling) << A; + return; + } +} + +void SemaSYCL::diagnoseDeprecatedAttribute(const ParsedAttr &A, + StringRef NewScope, + StringRef NewName) { + assert((!NewName.empty() || !NewScope.empty()) && + "Deprecated attribute with no new scope or name?"); + Diag(A.getLoc(), diag::warn_attribute_spelling_deprecated) + << "'" + A.getNormalizedFullName() + "'"; + + FixItHint Fix; + std::string NewFullName; + if (NewScope.empty() && !NewName.empty()) { + // Only have a new name. + Fix = FixItHint::CreateReplacement(A.getLoc(), NewName); + NewFullName = + ((A.hasScope() ? A.getScopeName()->getName() : StringRef("")) + + "::" + NewName) + .str(); + } else if (NewName.empty() && !NewScope.empty()) { + // Only have a new scope. + Fix = FixItHint::CreateReplacement(A.getScopeLoc(), NewScope); + NewFullName = (NewScope + "::" + A.getAttrName()->getName()).str(); + } else { + // Have both a new name and a new scope. + NewFullName = (NewScope + "::" + NewName).str(); + Fix = FixItHint::CreateReplacement(A.getRange(), NewFullName); + } + + Diag(A.getLoc(), diag::note_spelling_suggestion) + << "'" + NewFullName + "'" << Fix; +} + +// Checks if FPGA memory attributes apply on valid variables. +// Returns true if an error occured. +bool SemaSYCL::checkValidFPGAMemoryAttributesVar(Decl *D) { + // Check for SYCL device compilation context. + if (!getLangOpts().SYCLIsDevice) { + return false; + } + + const auto *VD = dyn_cast(D); + if (!VD) + return false; + + // Exclude implicit parameters and non-type template parameters. + if (VD->getKind() == Decl::ImplicitParam || + VD->getKind() == Decl::NonTypeTemplateParm) + return false; + + // Check for non-static data member. + if (isa(D)) + return false; + + // Check for SYCL device global attribute decoration. + if (isTypeDecoratedWithDeclAttribute(VD->getType())) + return false; + + // Check for constant variables and variables in the OpenCL constant + // address space. + if (VD->getType().isConstQualified() || + VD->getType().getAddressSpace() == LangAS::opencl_constant) + return false; + + // Check for static storage class or local storage. + if (VD->getStorageClass() == SC_Static || VD->hasLocalStorage()) + return false; + + return true; +} + +// Handles reqd_work_group_size. +// If the 'reqd_work_group_size' attribute is specified on a declaration along +// with 'num_simd_work_items' attribute, the required work group size specified +// by 'num_simd_work_items' attribute must evenly divide the index that +// increments fastest in the 'reqd_work_group_size' attribute. +// +// The arguments to reqd_work_group_size are ordered based on which index +// increments the fastest. In OpenCL, the first argument is the index that +// increments the fastest, and in SYCL, the last argument is the index that +// increments the fastest. +// +// __attribute__((reqd_work_group_size)) follows the OpenCL rules in OpenCL +// mode. All spellings of reqd_work_group_size attribute (regardless of +// syntax used) follow the SYCL rules when in SYCL mode. +bool SemaSYCL::checkWorkGroupSize(const Expr *NSWIValue, const Expr *RWGSXDim, + const Expr *RWGSYDim, const Expr *RWGSZDim) { + // If any of the operand is still value dependent, we can't test anything. + const auto *NSWIValueExpr = dyn_cast(NSWIValue); + const auto *RWGSXDimExpr = dyn_cast(RWGSXDim); + + if (!NSWIValueExpr || !RWGSXDimExpr) + return false; + + // Y and Z may be optional so we allow them to be null and consider them + // dependent if the original epxression was not null while the result of the + // cast is. + const auto *RWGSYDimExpr = dyn_cast_or_null(RWGSYDim); + const auto *RWGSZDimExpr = dyn_cast_or_null(RWGSZDim); + + if ((!RWGSYDimExpr && RWGSYDim) || (!RWGSZDimExpr && RWGSZDim)) + return false; + + // Otherwise, check which argument increments the fastest. + const ConstantExpr *LastRWGSDimExpr = + RWGSZDim ? RWGSZDimExpr : (RWGSYDim ? RWGSYDimExpr : RWGSXDimExpr); + unsigned WorkGroupSize = LastRWGSDimExpr->getResultAsAPSInt().getZExtValue(); + + // Check if the required work group size specified by 'num_simd_work_items' + // attribute evenly divides the index that increments fastest in the + // 'reqd_work_group_size' attribute. + return WorkGroupSize % NSWIValueExpr->getResultAsAPSInt().getZExtValue() != 0; +} + +// Checks correctness of mutual usage of different work_group_size attributes: +// reqd_work_group_size and max_work_group_size. +// +// If the 'reqd_work_group_size' attribute is specified on a declaration along +// with 'max_work_group_size' attribute, check to see if values of +// 'reqd_work_group_size' attribute arguments are equal to or less than values +// of 'max_work_group_size' attribute arguments. +// +// The arguments to reqd_work_group_size are ordered based on which index +// increments the fastest. In OpenCL, the first argument is the index that +// increments the fastest, and in SYCL, the last argument is the index that +// increments the fastest. +// +// __attribute__((reqd_work_group_size)) follows the OpenCL rules in OpenCL +// mode. All spellings of reqd_work_group_size attribute (regardless of +// syntax used) follow the SYCL rules when in SYCL mode. +bool SemaSYCL::checkMaxAllowedWorkGroupSize( + const Expr *RWGSXDim, const Expr *RWGSYDim, const Expr *RWGSZDim, + const Expr *MWGSXDim, const Expr *MWGSYDim, const Expr *MWGSZDim) { + // If any of the operand is still value dependent, we can't test anything. + const auto *RWGSXDimExpr = dyn_cast(RWGSXDim); + const auto *MWGSXDimExpr = dyn_cast(MWGSXDim); + const auto *MWGSYDimExpr = dyn_cast(MWGSYDim); + const auto *MWGSZDimExpr = dyn_cast(MWGSZDim); + + if (!RWGSXDimExpr || !MWGSXDimExpr || !MWGSYDimExpr || !MWGSZDimExpr) + return false; + + // Y and Z may be optional so we allow them to be null and consider them + // dependent if the original epxression was not null while the result of the + // cast is. + const auto *RWGSYDimExpr = dyn_cast_or_null(RWGSYDim); + const auto *RWGSZDimExpr = dyn_cast_or_null(RWGSZDim); + + if ((!RWGSYDimExpr && RWGSYDim) || (!RWGSZDimExpr && RWGSZDim)) + return false; + + // SYCL reorders arguments based on the dimensionality. + // If we only have the X-dimension, there is no change to the expressions, + // otherwise the last specified dimension acts as the first dimension in the + // work-group size. + const ConstantExpr *FirstRWGDimExpr = RWGSXDimExpr; + const ConstantExpr *SecondRWGDimExpr = RWGSYDimExpr; + const ConstantExpr *ThirdRWGDimExpr = RWGSZDimExpr; + if (getLangOpts().SYCLIsDevice && RWGSYDim) + std::swap(FirstRWGDimExpr, RWGSZDim ? ThirdRWGDimExpr : SecondRWGDimExpr); + + // Check if values of 'reqd_work_group_size' attribute arguments are greater + // than values of 'max_work_group_size' attribute arguments. + bool CheckFirstArgument = + FirstRWGDimExpr->getResultAsAPSInt().getZExtValue() > + MWGSZDimExpr->getResultAsAPSInt().getZExtValue(); + + bool CheckSecondArgument = + SecondRWGDimExpr && SecondRWGDimExpr->getResultAsAPSInt().getZExtValue() > + MWGSYDimExpr->getResultAsAPSInt().getZExtValue(); + + bool CheckThirdArgument = + ThirdRWGDimExpr && ThirdRWGDimExpr->getResultAsAPSInt().getZExtValue() > + MWGSXDimExpr->getResultAsAPSInt().getZExtValue(); + + return CheckFirstArgument || CheckSecondArgument || CheckThirdArgument; +} + +// Checks correctness of mutual usage of different work_group_size attributes: +// reqd_work_group_size, max_work_group_size, and max_global_work_dim. +// +// If [[intel::max_work_group_size(X, Y, Z)]] or +// [[sycl::reqd_work_group_size(X, Y, Z)]] or +// [[cl::reqd_work_group_size(X, Y, Z)]] +// or __attribute__((reqd_work_group_size)) attribute is specified on a +// declaration along with [[intel::max_global_work_dim()]] attribute, check to +// see if all arguments of 'max_work_group_size' or different spellings of +// 'reqd_work_group_size' attribute hold value 1 in case the argument of +// [[intel::max_global_work_dim()]] attribute value equals to 0. +bool SemaSYCL::areInvalidWorkGroupSizeAttrs(const Expr *MGValue, + const Expr *XDim, const Expr *YDim, + const Expr *ZDim) { + // If any of the operand is still value dependent, we can't test anything. + const auto *MGValueExpr = dyn_cast(MGValue); + const auto *XDimExpr = dyn_cast(XDim); + + if (!MGValueExpr || !XDimExpr) + return false; + + // Y and Z may be optional so we allow them to be null and consider them + // dependent if the original epxression was not null while the result of the + // cast is. + const auto *YDimExpr = dyn_cast_or_null(YDim); + const auto *ZDimExpr = dyn_cast_or_null(ZDim); + + if ((!YDimExpr && YDim) || (!ZDimExpr && ZDim)) + return false; + + // Otherwise, check if the attribute values are equal to one. + // Y and Z dimensions are optional and are considered trivially 1 if + // unspecified. + return (MGValueExpr->getResultAsAPSInt() == 0 && + (XDimExpr->getResultAsAPSInt() != 1 || + (YDimExpr && YDimExpr->getResultAsAPSInt() != 1) || + (ZDimExpr && ZDimExpr->getResultAsAPSInt() != 1))); +} + +void SemaSYCL::addSYCLIntelForcePow2DepthAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute accepts values 0 and 1 only. + if (ArgVal < 0 || ArgVal > 1) { + Diag(E->getBeginLoc(), diag::err_attribute_argument_is_not_valid) << CI; + return; + } + + // Check attribute applies to field, constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // If there is no mismatch, drop any duplicate attributes. + return; + } + } + } + + // If the declaration does not have an [[intel::fpga_memory]] + // attribute, this creates one as an implicit attribute. + ASTContext &Context = getASTContext(); + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) SYCLIntelForcePow2DepthAttr(Context, CI, E)); +} + +/// Handle the [[intel::bankwidth]] and [[intel::numbanks]] attributes. +/// These require a single constant power of two greater than zero. +/// These are incompatible with the register attribute. +/// The numbanks and bank_bits attributes are related. If bank_bits exists +/// when handling numbanks they are checked for consistency. +void SemaSYCL::addSYCLIntelBankWidthAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + // This attribute requires a single constant power of two greater than zero. + if (!ArgVal.isPowerOf2()) { + Diag(E->getExprLoc(), diag::err_attribute_argument_not_power_of_two) + << CI; + return; + } + + // Check attribute applies to field, constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + // If the declaration does not have an [[intel::fpga_memory]] + // attribute, this creates one as an implicit attribute. + ASTContext &Context = getASTContext(); + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) SYCLIntelBankWidthAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelNumBanksAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + // This attribute requires a single constant power of two greater than zero. + if (!ArgVal.isPowerOf2()) { + Diag(E->getExprLoc(), diag::err_attribute_argument_not_power_of_two) + << CI; + return; + } + + // Check or add the related BankBits attribute. + if (auto *BBA = D->getAttr()) { + unsigned NumBankBits = BBA->args_size(); + if (NumBankBits != ArgVal.ceilLogBase2()) { + Diag(E->getExprLoc(), diag::err_bankbits_numbanks_conflicting) << CI; + return; + } + } + + // Check attribute applies to constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + // If the declaration does not have an [[intel::fpga_memory]] + // attribute, this creates one as an implicit attribute. + ASTContext &Context = getASTContext(); + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + // We are adding a user NumBanks attribute, drop any implicit default. + if (auto *NBA = D->getAttr()) { + if (NBA->isImplicit()) + D->dropAttr(); + } + + D->addAttr(::new (Context) SYCLIntelNumBanksAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelBankBitsAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size) { + ASTContext &Context = getASTContext(); + SYCLIntelBankBitsAttr TmpAttr(Context, CI, Exprs, Size); + SmallVector Args; + SmallVector Values; + bool ListIsValueDep = false; + for (auto *E : TmpAttr.args()) { + llvm::APSInt Value(32, /*IsUnsigned=*/false); + Expr::EvalResult Result; + ListIsValueDep = ListIsValueDep || E->isValueDependent(); + if (!E->isValueDependent()) { + ExprResult ICE = SemaRef.VerifyIntegerConstantExpression(E, &Value); + if (ICE.isInvalid()) + return; + if (!Value.isNonNegative()) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + E = ICE.get(); + } + Args.push_back(E); + Values.push_back(Value.getExtValue()); + } + + // Check that the list is consecutive. + if (!ListIsValueDep && Values.size() > 1) { + bool ListIsAscending = Values[0] < Values[1]; + for (int I = 0, E = Values.size() - 1; I < E; ++I) { + if (Values[I + 1] != Values[I] + (ListIsAscending ? 1 : -1)) { + Diag(CI.getLoc(), diag::err_bankbits_non_consecutive) << &TmpAttr; + return; + } + } + } + + // Check or add the related numbanks attribute. + if (auto *NBA = D->getAttr()) { + Expr *E = NBA->getValue(); + if (!E->isValueDependent()) { + Expr::EvalResult Result; + E->EvaluateAsInt(Result, Context); + llvm::APSInt Value = Result.Val.getInt(); + if (Args.size() != Value.ceilLogBase2()) { + Diag(TmpAttr.getLoc(), diag::err_bankbits_numbanks_conflicting); + return; + } + } + } else { + llvm::APInt Num(32, (unsigned)(1 << Args.size())); + Expr *NBE = + IntegerLiteral::Create(Context, Num, Context.IntTy, SourceLocation()); + D->addAttr(SYCLIntelNumBanksAttr::CreateImplicit(Context, NBE)); + } + + // Check attribute applies to field, constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) + SYCLIntelBankBitsAttr(Context, CI, Args.data(), Args.size())); +} + +bool isDeviceAspectType(const QualType Ty) { + const EnumType *ET = Ty->getAs(); + if (!ET) + return false; + + if (const auto *Attr = ET->getDecl()->getAttr()) + return Attr->getType() == SYCLTypeAttr::aspect; + + return false; +} + +void SemaSYCL::addSYCLDeviceHasAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size) { + ASTContext &Context = getASTContext(); + SYCLDeviceHasAttr TmpAttr(Context, CI, Exprs, Size); + SmallVector Aspects; + for (auto *E : TmpAttr.aspects()) + if (!isa(E) && !isDeviceAspectType(E->getType())) + Diag(E->getExprLoc(), diag::err_sycl_invalid_aspect_argument) << CI; + + if (const auto *ExistingAttr = D->getAttr()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute_exact) << CI; + Diag(ExistingAttr->getLoc(), diag::note_previous_attribute); + return; + } + + D->addAttr(::new (Context) SYCLDeviceHasAttr(Context, CI, Exprs, Size)); +} + +void SemaSYCL::addSYCLUsesAspectsAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size) { + ASTContext &Context = getASTContext(); + SYCLUsesAspectsAttr TmpAttr(Context, CI, Exprs, Size); + SmallVector Aspects; + for (auto *E : TmpAttr.aspects()) + if (!isDeviceAspectType(E->getType())) + Diag(E->getExprLoc(), diag::err_sycl_invalid_aspect_argument) << CI; + + if (const auto *ExistingAttr = D->getAttr()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute_exact) << CI; + Diag(ExistingAttr->getLoc(), diag::note_previous_attribute); + return; + } + + D->addAttr(::new (Context) SYCLUsesAspectsAttr(Context, CI, Exprs, Size)); +} + +void SemaSYCL::addSYCLIntelPipeIOAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + VarDecl *VD = cast(D); + QualType Ty = VD->getType(); + // TODO: Applicable only on pipe storages. Currently they are defined + // as structures inside of SYCL headers. Add a check for pipe_storage_t + // when it is ready. + if (!Ty->isStructureType()) { + Diag(CI.getLoc(), diag::err_attribute_wrong_decl_type_str) + << CI << CI.isRegularKeywordAttribute() + << "SYCL pipe storage declaration"; + return; + } + + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getID())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelPipeIOAttr(Context, CI, E)); +} + +// Handles [[intel::loop_fuse]] and [[intel::loop_fuse_independent]]. +void SemaSYCL::addSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // [[intel::loop_fuse]] and [[intel::loop_fuse_independent]] are + // incompatible. + // FIXME: If additional spellings are provided for this attribute, + // this code will do the wrong thing. + if (DeclAttr->getAttributeSpellingListIndex() != + CI.getAttributeSpellingListIndex()) { + Diag(CI.getLoc(), diag::err_attributes_are_not_compatible) + << CI << DeclAttr << CI.isRegularKeywordAttribute(); + Diag(DeclAttr->getLocation(), diag::note_conflicting_attribute); + return; + } + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelLoopFuseAttr(Context, CI, E)); +} + +void SemaSYCL::addIntelReqdSubGroupSize(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + ASTContext &Context = getASTContext(); + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + auto &TI = Context.getTargetInfo(); + if (TI.getTriple().isNVPTX() && ArgVal != 32) + Diag(E->getExprLoc(), diag::warn_reqd_sub_group_attribute_n) + << ArgVal.getSExtValue() << TI.getTriple().getArchName() << 32; + if (TI.getTriple().isAMDGPU()) { + const auto HasWaveFrontSize64 = + TI.getTargetOpts().FeatureMap["wavefrontsize64"]; + const auto HasWaveFrontSize32 = + TI.getTargetOpts().FeatureMap["wavefrontsize32"]; + + // CDNA supports only 64 wave front size, for those GPUs allow subgroup + // size of 64. Some GPUs support both 32 and 64, for those (and the rest) + // only allow 32. Warn on incompatible sizes. + const auto SupportedWaveFrontSize = + HasWaveFrontSize64 && !HasWaveFrontSize32 ? 64 : 32; + if (ArgVal != SupportedWaveFrontSize) + Diag(E->getExprLoc(), diag::warn_reqd_sub_group_attribute_n) + << ArgVal.getSExtValue() << TI.getTriple().getArchName() + << SupportedWaveFrontSize; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + D->addAttr(::new (Context) IntelReqdSubGroupSizeAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelNumSimdWorkItemsAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + + // If the 'reqd_work_group_size' attribute is specified on a declaration + // along with 'num_simd_work_items' attribute, the required work group size + // specified by 'num_simd_work_items' attribute must evenly divide the index + // that increments fastest in the 'reqd_work_group_size' attribute. + if (const auto *DeclAttr = D->getAttr()) { + if (checkWorkGroupSize(E, DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim())) { + Diag(CI.getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) + << CI << DeclAttr; + Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelNumSimdWorkItemsAttr(Context, CI, E)); +} + +// Handle scheduler_target_fmax_mhz +void SemaSYCL::addSYCLIntelSchedulerTargetFmaxMhzAttr( + Decl *D, const AttributeCommonInfo &CI, Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = + D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) + SYCLIntelSchedulerTargetFmaxMhzAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelNoGlobalWorkOffsetAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelNoGlobalWorkOffsetAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelMaxGlobalWorkDimAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute must be in the range [0, 3]. + if (ArgVal < 0 || ArgVal > 3) { + Diag(E->getBeginLoc(), diag::err_attribute_argument_out_of_range) + << CI << 0 << 3 << E->getSourceRange(); + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + + // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr or + // SYCLReqdWorkGroupSizeAttr, check to see if the attribute holds values + // equal to (1, 1, 1) in case the value of SYCLIntelMaxGlobalWorkDimAttr + // equals to 0. + if (ArgVal == 0) { + if (checkWorkGroupSizeAttrExpr(D, CI) || + checkWorkGroupSizeAttrExpr(D, CI)) + return; + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelMaxGlobalWorkDimAttr(Context, CI, E)); +} + +// Check that the value is a non-negative integer constant that can fit in +// 32-bits. Issue correct error message and return false on failure. +bool static check32BitInt(const Expr *E, SemaSYCL &S, llvm::APSInt &I, + const AttributeCommonInfo &CI) { + if (!I.isIntN(32)) { + S.Diag(E->getExprLoc(), diag::err_ice_too_large) + << llvm::toString(I, 10, false) << 32 << /* Unsigned */ 1; + return false; + } + + if (I.isSigned() && I.isNegative()) { + S.Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /* Non-negative */ 1; + return false; + } + + return true; +} + +void SemaSYCL::addSYCLIntelMinWorkGroupsPerComputeUnitAttr( + Decl *D, const AttributeCommonInfo &CI, Expr *E) { + ASTContext &Context = getASTContext(); + if (getLangOpts().SYCLIsDevice && + !Context.getTargetInfo().getTriple().isNVPTX()) { + Diag(E->getBeginLoc(), diag::warn_launch_bounds_is_cuda_specific) + << CI << E->getSourceRange(); + return; + } + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + if (!check32BitInt(E, *this, ArgVal, CI)) + return; + E = Res.get(); + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = + D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + D->addAttr(::new (Context) + SYCLIntelMinWorkGroupsPerComputeUnitAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( + Decl *D, const AttributeCommonInfo &CI, Expr *E) { + ASTContext &Context = getASTContext(); + auto &TI = Context.getTargetInfo(); + if (Context.getLangOpts().SYCLIsDevice) { + if (!TI.getTriple().isNVPTX()) { + Diag(E->getBeginLoc(), diag::warn_launch_bounds_is_cuda_specific) + << CI << E->getSourceRange(); + return; + } + + // Feature '.maxclusterrank' requires .target sm_90 or higher. + auto SM = getCudaArch(TI); + if (SM == CudaArch::UNKNOWN || SM < CudaArch::SM_90) { + Diag(E->getBeginLoc(), diag::warn_cuda_maxclusterrank_sm_90) + << CudaArchToString(SM) << CI << E->getSourceRange(); + return; + } + } + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + if (!check32BitInt(E, *this, ArgVal, CI)) + return; + E = Res.get(); + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = + D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + D->addAttr(::new (Context) + SYCLIntelMaxWorkGroupsPerMultiprocessorAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelMaxConcurrencyAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getNExpr())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelMaxConcurrencyAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelPrivateCopiesAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + ASTContext &Context = getASTContext(); + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + + // Check attribute applies to field as well as const variables, non-static + // local variables, non-static data members, and device_global variables. + // for the device compilation. + if (const auto *VD = dyn_cast(D)) { + if (Context.getLangOpts().SYCLIsDevice && + (!(isa(D) || + (VD->getKind() != Decl::ImplicitParam && + VD->getKind() != Decl::NonTypeTemplateParm && + VD->getKind() != Decl::ParmVar && + (VD->hasLocalStorage() || + isTypeDecoratedWithDeclAttribute( + VD->getType())))))) { + Diag(CI.getLoc(), diag::err_fpga_attribute_invalid_decl) << CI; + return; + } + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + // If the declaration does not have [[intel::fpga_memory]] + // attribute, this creates default implicit memory. + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) SYCLIntelPrivateCopiesAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelMaxReplicatesAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + // Check attribute applies to field, constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + // If the declaration does not have an [[intel::fpga_memory]] + // attribute, this creates one as an implicit attribute. + ASTContext &Context = getASTContext(); + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) SYCLIntelMaxReplicatesAttr(Context, CI, E)); +} + +// Handles initiation_interval attribute. +void SemaSYCL::addSYCLIntelInitiationIntervalAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getNExpr())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelInitiationIntervalAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelESimdVectorizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + if (ArgVal != 8 && ArgVal != 16 && ArgVal != 32) { + Diag(E->getExprLoc(), diag::err_sycl_esimd_vectorize_unsupported_value) + << CI; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelESimdVectorizeAttr(Context, CI, E)); +} + +// Checks if an expression is a valid filter list for an add_ir_attributes_* +// attribute. Returns true if an error occured. +static bool checkAddIRAttributesFilterListExpr(Expr *FilterListArg, SemaSYCL &S, + const AttributeCommonInfo &CI) { + const auto *FilterListE = cast(FilterListArg); + for (const Expr *FilterElemE : FilterListE->inits()) + if (!isa(FilterElemE)) + return S.Diag(FilterElemE->getBeginLoc(), + diag::err_sycl_add_ir_attribute_invalid_filter) + << CI; + return false; +} + +// Returns true if a type is either an array of char or a pointer to char. +static bool isAddIRAttributesValidStringType(QualType T) { + if (!T->isArrayType() && !T->isPointerType()) + return false; + QualType ElemT = T->isArrayType() + ? cast(T.getTypePtr())->getElementType() + : T->getPointeeType(); + return ElemT.isConstQualified() && ElemT->isCharType(); +} + +// Checks if an expression is a valid attribute value for an add_ir_attributes_* +// attribute. Returns true if an error occured. +static bool checkAddIRAttributesValueExpr(Expr *ValArg, SemaSYCL &S, + const AttributeCommonInfo &CI) { + QualType ValType = ValArg->getType(); + if (isAddIRAttributesValidStringType(ValType) || ValType->isNullPtrType() || + ValType->isIntegralOrEnumerationType() || ValType->isFloatingType()) + return false; + + return S.Diag(ValArg->getBeginLoc(), + diag::err_sycl_add_ir_attribute_invalid_value) + << CI; +} + +// Checks if an expression is a valid attribute name for an add_ir_attributes_* +// attribute. Returns true if an error occured. +static bool checkAddIRAttributesNameExpr(Expr *NameArg, SemaSYCL &S, + const AttributeCommonInfo &CI) { + // Only strings and const char * are valid name arguments. + if (isAddIRAttributesValidStringType(NameArg->getType())) + return false; + + return S.Diag(NameArg->getBeginLoc(), + diag::err_sycl_add_ir_attribute_invalid_name) + << CI; +} + +// Checks and evaluates arguments of an add_ir_attributes_* attribute. Returns +// true if an error occured. +static bool evaluateAddIRAttributesArgs(Expr **Args, size_t ArgsSize, + SemaSYCL &S, + const AttributeCommonInfo &CI) { + ASTContext &Context = S.getASTContext(); + + // Check filter list if it is the first argument. + bool HasFilter = ArgsSize && isa(Args[0]); + if (HasFilter && checkAddIRAttributesFilterListExpr(Args[0], S, CI)) + return true; + + llvm::SmallVector Notes; + bool HasDependentArg = false; + for (unsigned I = HasFilter; I < ArgsSize; I++) { + Expr *&E = Args[I]; + + if (isa(E)) + return S.Diag(E->getBeginLoc(), + diag::err_sycl_add_ir_attr_filter_list_invalid_arg) + << CI; + + if (E->isValueDependent() || E->isTypeDependent()) { + HasDependentArg = true; + continue; + } + + Expr::EvalResult Eval; + Eval.Diag = &Notes; + if (!E->EvaluateAsConstantExpr(Eval, Context) || !Notes.empty()) { + S.Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type) + << CI << (I + 1) << AANT_ArgumentConstantExpr; + for (auto &Note : Notes) + S.Diag(Note.first, Note.second); + return true; + } + assert(Eval.Val.hasValue()); + E = ConstantExpr::Create(Context, E, Eval.Val); + } + + // If there are no dependent expressions, check for expected number of args. + if (!HasDependentArg && ArgsSize && (ArgsSize - HasFilter) & 1) + return S.Diag(CI.getLoc(), diag::err_sycl_add_ir_attribute_must_have_pairs) + << CI; + + // If there are no dependent expressions, check argument types. + // First half of the arguments are names, the second half are values. + unsigned MidArg = (ArgsSize - HasFilter) / 2 + HasFilter; + if (!HasDependentArg) { + for (unsigned I = HasFilter; I < ArgsSize; ++I) { + if ((I < MidArg && checkAddIRAttributesNameExpr(Args[I], S, CI)) || + (I >= MidArg && checkAddIRAttributesValueExpr(Args[I], S, CI))) + return true; + } + } + return false; +} + +void SemaSYCL::addSYCLAddIRAttributesFunctionAttr( + Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { + if (const auto *FuncD = dyn_cast(D)) { + if (FuncD->isDefaulted()) { + Diag(CI.getLoc(), diag::err_disallow_attribute_on_func) << CI << 0; + return; + } + if (FuncD->isDeleted()) { + Diag(CI.getLoc(), diag::err_disallow_attribute_on_func) << CI << 1; + return; + } + } + + ASTContext &Context = getASTContext(); + auto *Attr = SYCLAddIRAttributesFunctionAttr::Create(Context, Args.data(), + Args.size(), CI); + if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, + CI)) + return; + D->addAttr(Attr); + + // There are compile-time SYCL properties which we would like to turn into + // attributes to enable compiler diagnostics. + // At the moment the only such property is related to virtual functions and + // it is turned into sycl_device attribute. This is a tiny optimization to + // avoid deep dive into the attribute if we already know that a declaration + // is a device declaration. It may have to be removed later if/when we add + // handling of more compile-time properties here. + if (D->hasAttr()) + return; + + // SYCL Headers use template magic to pass key=value pairs to the attribute + // and we should make sure that all template instantiations are done before + // accessing attribute arguments. + if (hasDependentExpr(Attr->args_begin(), Attr->args_size())) + return; + + SmallVector, 4> Pairs = + Attr->getFilteredAttributeNameValuePairs(Context); + + for (const auto &[Key, Value] : Pairs) { + if (Key == "indirectly-callable") { + D->addAttr(SYCLDeviceAttr::CreateImplicit(Context)); + break; + } + } +} + +void SemaSYCL::addSYCLAddIRAttributesKernelParameterAttr( + Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { + ASTContext &Context = getASTContext(); + auto *Attr = SYCLAddIRAttributesKernelParameterAttr::Create( + Context, Args.data(), Args.size(), CI); + if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, + CI)) + return; + D->addAttr(Attr); +} + +void SemaSYCL::addSYCLAddIRAttributesGlobalVariableAttr( + Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { + ASTContext &Context = getASTContext(); + auto *Attr = SYCLAddIRAttributesGlobalVariableAttr::Create( + Context, Args.data(), Args.size(), CI); + if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, + CI)) + return; + D->addAttr(Attr); +} + +void SemaSYCL::addSYCLAddIRAnnotationsMemberAttr(Decl *D, + const AttributeCommonInfo &CI, + MutableArrayRef Args) { + ASTContext &Context = getASTContext(); + auto *Attr = SYCLAddIRAnnotationsMemberAttr::Create(Context, Args.data(), + Args.size(), CI); + if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, + CI)) + return; + D->addAttr(Attr); +} + +void SemaSYCL::addSYCLWorkGroupSizeHintAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, + Expr *ZDim) { + // Returns nullptr if diagnosing, otherwise returns the original expression + // or the original expression converted to a constant expression. + auto CheckAndConvertArg = [&](Expr *E) -> std::optional { + // We can only check if the expression is not value dependent. + if (E && !E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return std::nullopt; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return std::nullopt; + } + } + + return E; + }; + + // Check all three argument values, and if any are bad, bail out. This will + // convert the given expressions into constant expressions when possible. + std::optional XDimConvert = CheckAndConvertArg(XDim); + std::optional YDimConvert = CheckAndConvertArg(YDim); + std::optional ZDimConvert = CheckAndConvertArg(ZDim); + if (!XDimConvert || !YDimConvert || !ZDimConvert) + return; + XDim = XDimConvert.value(); + YDim = YDimConvert.value(); + ZDim = ZDimConvert.value(); + + // If the attribute was already applied with different arguments, then + // diagnose the second attribute as a duplicate and don't add it. + if (const auto *Existing = D->getAttr()) { + // If any of the results are known to be different, we can diagnose at this + // point and drop the attribute. + if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(Existing->getLoc(), diag::note_previous_attribute); + return; + } + // If all of the results are known to be the same, we can silently drop the + // attribute. Otherwise, we have to add the attribute and resolve its + // differences later. + if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) + return; + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) + SYCLWorkGroupSizeHintAttr(Context, CI, XDim, YDim, ZDim)); +} + +void SemaSYCL::addSYCLIntelMaxWorkGroupSizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, + Expr *ZDim) { + // Returns nullptr if diagnosing, otherwise returns the original expression + // or the original expression converted to a constant expression. + auto CheckAndConvertArg = [&](Expr *E) -> Expr * { + // Check if the expression is not value dependent. + if (!E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return nullptr; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return nullptr; + } + } + return E; + }; + + // Check all three argument values, and if any are bad, bail out. This will + // convert the given expressions into constant expressions when possible. + XDim = CheckAndConvertArg(XDim); + YDim = CheckAndConvertArg(YDim); + ZDim = CheckAndConvertArg(ZDim); + if (!XDim || !YDim || !ZDim) + return; + + // If the 'max_work_group_size' attribute is specified on a declaration along + // with 'reqd_work_group_size' attribute, check to see if values of + // 'reqd_work_group_size' attribute arguments are equal to or less than values + // of 'max_work_group_size' attribute arguments. + // + // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments + // are greater than values of 'max_work_group_size' attribute arguments. + if (const auto *DeclAttr = D->getAttr()) { + if (checkMaxAllowedWorkGroupSize(DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim(), XDim, YDim, ZDim)) { + Diag(CI.getLoc(), diag::err_conflicting_sycl_function_attributes) + << CI << DeclAttr; + Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); + return; + } + } + + // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr, check to see if + // the attribute holds values equal to (1, 1, 1) in case the value of + // SYCLIntelMaxGlobalWorkDimAttr equals to 0. + if (const auto *DeclAttr = D->getAttr()) { + if (areInvalidWorkGroupSizeAttrs(DeclAttr->getValue(), XDim, YDim, ZDim)) { + Diag(CI.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) + << CI << DeclAttr; + return; + } + } + + // If the attribute was already applied with different arguments, then + // diagnose the second attribute as a duplicate and don't add it. + if (const auto *Existing = D->getAttr()) { + // If any of the results are known to be different, we can diagnose at this + // point and drop the attribute. + if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(Existing->getLoc(), diag::note_previous_attribute); + return; + } + // If all of the results are known to be the same, we can silently drop the + // attribute. Otherwise, we have to add the attribute and resolve its + // differences later. + if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) + return; + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) + SYCLIntelMaxWorkGroupSizeAttr(Context, CI, XDim, YDim, ZDim)); +} + +void SemaSYCL::addSYCLReqdWorkGroupSizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, + Expr *ZDim) { + // Returns nullptr if diagnosing, otherwise returns the original expression + // or the original expression converted to a constant expression. + auto CheckAndConvertArg = [&](Expr *E) -> std::optional { + // Check if the expression is not value dependent. + if (E && !E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return std::nullopt; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return std::nullopt; + } + } + return E; + }; + + // Check all three argument values, and if any are bad, bail out. This will + // convert the given expressions into constant expressions when possible. + std::optional XDimConvert = CheckAndConvertArg(XDim); + std::optional YDimConvert = CheckAndConvertArg(YDim); + std::optional ZDimConvert = CheckAndConvertArg(ZDim); + if (!XDimConvert || !YDimConvert || !ZDimConvert) + return; + XDim = XDimConvert.value(); + YDim = YDimConvert.value(); + ZDim = ZDimConvert.value(); + + // If the declaration has a ReqdWorkGroupSizeAttr, check to see if + // the attribute holds values equal to (1, 1, 1) in case the value of + // SYCLIntelMaxGlobalWorkDimAttr equals to 0. + if (const auto *DeclAttr = D->getAttr()) { + if (areInvalidWorkGroupSizeAttrs(DeclAttr->getValue(), XDim, YDim, ZDim)) { + Diag(CI.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) + << CI << DeclAttr; + } + } + + // If the 'max_work_group_size' attribute is specified on a declaration along + // with 'reqd_work_group_size' attribute, check to see if values of + // 'reqd_work_group_size' attribute arguments are equal to or less than values + // of 'max_work_group_size' attribute arguments. + // + // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments + // are greater than values of 'max_work_group_size' attribute arguments. + if (const auto *DeclAttr = D->getAttr()) { + if (checkMaxAllowedWorkGroupSize(XDim, YDim, ZDim, DeclAttr->getXDim(), + DeclAttr->getYDim(), + DeclAttr->getZDim())) { + Diag(CI.getLoc(), diag::err_conflicting_sycl_function_attributes) + << CI << DeclAttr; + Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); + return; + } + } + + // If the 'reqd_work_group_size' attribute is specified on a declaration + // along with 'num_simd_work_items' attribute, the required work group size + // specified by 'num_simd_work_items' attribute must evenly divide the index + // that increments fastest in the 'reqd_work_group_size' attribute. + if (const auto *DeclAttr = D->getAttr()) { + if (checkWorkGroupSize(DeclAttr->getValue(), XDim, YDim, ZDim)) { + Diag(DeclAttr->getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) + << DeclAttr << CI; + Diag(CI.getLoc(), diag::note_conflicting_attribute); + return; + } + } + + // If the attribute was already applied with different arguments, then + // diagnose the second attribute as a duplicate and don't add it. + if (const auto *Existing = D->getAttr()) { + // If any of the results are known to be different, we can diagnose at this + // point and drop the attribute. + if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) { + Diag(CI.getLoc(), diag::err_duplicate_attribute) << CI; + Diag(Existing->getLoc(), diag::note_previous_attribute); + return; + } + + // If all of the results are known to be the same, we can silently drop the + // attribute. Otherwise, we have to add the attribute and resolve its + // differences later. + if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) + return; + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) + SYCLReqdWorkGroupSizeAttr(Context, CI, XDim, YDim, ZDim)); +} diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index dcee4506e99f1..e4d9a78682ad7 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -18,6 +18,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" +#include "clang/Sema/SemaSYCL.h" #include "llvm/ADT/StringExtras.h" #include @@ -97,7 +98,7 @@ Sema::BuildSYCLIntelMaxConcurrencyAttr(const AttributeCommonInfo &CI, static Attr *handleSYCLIntelMaxConcurrencyAttr(Sema &S, Stmt *St, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); Expr *E = A.getArgAsExpr(0); return S.BuildSYCLIntelMaxConcurrencyAttr(A, E); @@ -126,7 +127,7 @@ Sema::BuildSYCLIntelInitiationIntervalAttr(const AttributeCommonInfo &CI, static Attr *handleSYCLIntelInitiationIntervalAttr(Sema &S, Stmt *St, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); Expr *E = A.getArgAsExpr(0); return S.BuildSYCLIntelInitiationIntervalAttr(A, E); @@ -154,7 +155,7 @@ Sema::BuildSYCLIntelMaxInterleavingAttr(const AttributeCommonInfo &CI, static Attr *handleSYCLIntelMaxInterleavingAttr(Sema &S, Stmt *St, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); Expr *E = A.getArgAsExpr(0); return S.BuildSYCLIntelMaxInterleavingAttr(A, E); @@ -183,7 +184,7 @@ Sema::BuildSYCLIntelLoopCoalesceAttr(const AttributeCommonInfo &CI, static Attr *handleSYCLIntelLoopCoalesceAttr(Sema &S, Stmt *St, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); Expr *E = A.isArgExpr(0) ? A.getArgAsExpr(0) : nullptr; return S.BuildSYCLIntelLoopCoalesceAttr(A, E); @@ -212,7 +213,7 @@ Sema::BuildSYCLIntelSpeculatedIterationsAttr(const AttributeCommonInfo &CI, static Attr *handleSYCLIntelSpeculatedIterationsAttr(Sema &S, Stmt *St, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); Expr *E = A.getArgAsExpr(0); return S.BuildSYCLIntelSpeculatedIterationsAttr(A, E); @@ -220,7 +221,7 @@ static Attr *handleSYCLIntelSpeculatedIterationsAttr(Sema &S, Stmt *St, static Attr *handleSYCLIntelDisableLoopPipeliningAttr(Sema &S, Stmt *, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); return new (S.Context) SYCLIntelDisableLoopPipeliningAttr(S.Context, A); } @@ -384,7 +385,7 @@ CheckRedundantSYCLIntelIVDepAttrs(Sema &S, ArrayRef Attrs) { static Attr *handleIntelIVDepAttr(Sema &S, Stmt *St, const ParsedAttr &A) { unsigned NumArgs = A.getNumArgs(); - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); return S.BuildSYCLIntelIVDepAttr( A, NumArgs >= 1 ? A.getArgAsExpr(0) : nullptr, @@ -444,7 +445,7 @@ Sema::BuildSYCLIntelLoopCountAttr(const AttributeCommonInfo &CI, Expr *E) { static Attr *handleSYCLIntelLoopCountAttr(Sema &S, Stmt *St, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); Expr *E = A.getArgAsExpr(0); return S.BuildSYCLIntelLoopCountAttr(A, E); @@ -478,7 +479,7 @@ Sema::BuildSYCLIntelMaxReinvocationDelayAttr(const AttributeCommonInfo &CI, static Attr * handleSYCLIntelMaxReinvocationDelayAttr(Sema &S, Stmt *St, const ParsedAttr &A) { - S.CheckDeprecatedSYCLAttributeSpelling(A); + S.SYCL().checkDeprecatedSYCLAttributeSpelling(A); Expr *E = A.getArgAsExpr(0); return S.BuildSYCLIntelMaxReinvocationDelayAttr(A, E); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 8f56f48a37563..93999b2f0b11b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -643,7 +643,8 @@ static void instantiateSYCLIntelForcePow2DepthAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(Attr->getValue(), TemplateArgs); if (!Result.isInvalid()) - return S.AddSYCLIntelForcePow2DepthAttr(New, *Attr, Result.getAs()); + return S.SYCL().addSYCLIntelForcePow2DepthAttr(New, *Attr, + Result.getAs()); } static void instantiateSYCLIntelBankWidthAttr( @@ -653,7 +654,7 @@ static void instantiateSYCLIntelBankWidthAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(Attr->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelBankWidthAttr(New, *Attr, Result.getAs()); + S.SYCL().addSYCLIntelBankWidthAttr(New, *Attr, Result.getAs()); } static void instantiateSYCLIntelNumBanksAttr( @@ -663,7 +664,7 @@ static void instantiateSYCLIntelNumBanksAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(Attr->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelNumBanksAttr(New, *Attr, Result.getAs()); + S.SYCL().addSYCLIntelNumBanksAttr(New, *Attr, Result.getAs()); } static void instantiateSYCLIntelBankBitsAttr( @@ -678,7 +679,7 @@ static void instantiateSYCLIntelBankBitsAttr( return; Args.push_back(Result.getAs()); } - S.AddSYCLIntelBankBitsAttr(New, *Attr, Args.data(), Args.size()); + S.SYCL().addSYCLIntelBankBitsAttr(New, *Attr, Args.data(), Args.size()); } static void @@ -691,7 +692,7 @@ instantiateSYCLDeviceHasAttr(Sema &S, if (S.SubstExprs(ArrayRef(Attr->aspects_begin(), Attr->aspects_end()), /*IsCall=*/false, TemplateArgs, Args)) return; - S.AddSYCLDeviceHasAttr(New, *Attr, Args.data(), Args.size()); + S.SYCL().addSYCLDeviceHasAttr(New, *Attr, Args.data(), Args.size()); } static void instantiateSYCLUsesAspectsAttr( @@ -700,13 +701,13 @@ static void instantiateSYCLUsesAspectsAttr( EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); SmallVector Args; - for (auto I : Attr->aspects()) { + for (auto *I : Attr->aspects()) { ExprResult Result = S.SubstExpr(I, TemplateArgs); if (Result.isInvalid()) return; Args.push_back(Result.getAs()); } - S.AddSYCLUsesAspectsAttr(New, *Attr, Args.data(), Args.size()); + S.SYCL().addSYCLUsesAspectsAttr(New, *Attr, Args.data(), Args.size()); } static void instantiateSYCLIntelPipeIOAttr( @@ -717,7 +718,7 @@ static void instantiateSYCLIntelPipeIOAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(Attr->getID(), TemplateArgs); if (!Result.isInvalid()) - S.addSYCLIntelPipeIOAttr(New, *Attr, Result.getAs()); + S.SYCL().addSYCLIntelPipeIOAttr(New, *Attr, Result.getAs()); } static void instantiateSYCLIntelLoopFuseAttr( @@ -727,7 +728,7 @@ static void instantiateSYCLIntelLoopFuseAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(Attr->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelLoopFuseAttr(New, *Attr, Result.getAs()); + S.SYCL().addSYCLIntelLoopFuseAttr(New, *Attr, Result.getAs()); } static void instantiateIntelReqdSubGroupSize( @@ -737,7 +738,7 @@ static void instantiateIntelReqdSubGroupSize( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddIntelReqdSubGroupSize(New, *A, Result.getAs()); + S.SYCL().addIntelReqdSubGroupSize(New, *A, Result.getAs()); } static void instantiateSYCLIntelNumSimdWorkItemsAttr( @@ -747,7 +748,7 @@ static void instantiateSYCLIntelNumSimdWorkItemsAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelNumSimdWorkItemsAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelNumSimdWorkItemsAttr(New, *A, Result.getAs()); } static void instantiateSYCLIntelSchedulerTargetFmaxMhzAttr( @@ -757,7 +758,8 @@ static void instantiateSYCLIntelSchedulerTargetFmaxMhzAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelSchedulerTargetFmaxMhzAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelSchedulerTargetFmaxMhzAttr(New, *A, + Result.getAs()); } static void instantiateSYCLIntelNoGlobalWorkOffsetAttr( @@ -767,7 +769,7 @@ static void instantiateSYCLIntelNoGlobalWorkOffsetAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelNoGlobalWorkOffsetAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelNoGlobalWorkOffsetAttr(New, *A, Result.getAs()); } static void instantiateSYCLIntelMaxGlobalWorkDimAttr( @@ -777,7 +779,7 @@ static void instantiateSYCLIntelMaxGlobalWorkDimAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelMaxGlobalWorkDimAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelMaxGlobalWorkDimAttr(New, *A, Result.getAs()); } static void instantiateSYCLIntelMinWorkGroupsPerComputeUnitAttr( @@ -787,8 +789,8 @@ static void instantiateSYCLIntelMinWorkGroupsPerComputeUnitAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelMinWorkGroupsPerComputeUnitAttr(New, *A, - Result.getAs()); + S.SYCL().addSYCLIntelMinWorkGroupsPerComputeUnitAttr(New, *A, + Result.getAs()); } static void instantiateSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( @@ -798,8 +800,8 @@ static void instantiateSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelMaxWorkGroupsPerMultiprocessorAttr(New, *A, - Result.getAs()); + S.SYCL().addSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( + New, *A, Result.getAs()); } static void instantiateSYCLIntelMaxConcurrencyAttr( @@ -809,7 +811,7 @@ static void instantiateSYCLIntelMaxConcurrencyAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getNExpr(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelMaxConcurrencyAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelMaxConcurrencyAttr(New, *A, Result.getAs()); } static void instantiateSYCLIntelPrivateCopiesAttr( @@ -819,7 +821,7 @@ static void instantiateSYCLIntelPrivateCopiesAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelPrivateCopiesAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelPrivateCopiesAttr(New, *A, Result.getAs()); } static void instantiateSYCLIntelMaxReplicatesAttr( @@ -829,7 +831,7 @@ static void instantiateSYCLIntelMaxReplicatesAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelMaxReplicatesAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelMaxReplicatesAttr(New, *A, Result.getAs()); } static void instantiateSYCLIntelInitiationIntervalAttr( @@ -839,7 +841,7 @@ static void instantiateSYCLIntelInitiationIntervalAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getNExpr(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelInitiationIntervalAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelInitiationIntervalAttr(New, *A, Result.getAs()); } static void instantiateSYCLIntelESimdVectorizeAttr( @@ -849,7 +851,7 @@ static void instantiateSYCLIntelESimdVectorizeAttr( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.AddSYCLIntelESimdVectorizeAttr(New, *A, Result.getAs()); + S.SYCL().addSYCLIntelESimdVectorizeAttr(New, *A, Result.getAs()); } static void instantiateSYCLAddIRAttributesFunctionAttr( @@ -861,7 +863,7 @@ static void instantiateSYCLAddIRAttributesFunctionAttr( if (S.SubstExprs(ArrayRef(A->args_begin(), A->args_end()), /*IsCall=*/false, TemplateArgs, Args)) return; - S.AddSYCLAddIRAttributesFunctionAttr(New, *A, Args); + S.SYCL().addSYCLAddIRAttributesFunctionAttr(New, *A, Args); } static void instantiateSYCLAddIRAttributesKernelParameterAttr( @@ -873,7 +875,7 @@ static void instantiateSYCLAddIRAttributesKernelParameterAttr( if (S.SubstExprs(ArrayRef(A->args().begin(), A->args().end()), /*IsCall=*/false, TemplateArgs, Args)) return; - S.AddSYCLAddIRAttributesKernelParameterAttr(New, *A, Args); + S.SYCL().addSYCLAddIRAttributesKernelParameterAttr(New, *A, Args); } static void instantiateSYCLAddIRAttributesGlobalVariableAttr( @@ -885,7 +887,7 @@ static void instantiateSYCLAddIRAttributesGlobalVariableAttr( if (S.SubstExprs(ArrayRef(A->args().begin(), A->args().end()), /*IsCall=*/false, TemplateArgs, Args)) return; - S.AddSYCLAddIRAttributesGlobalVariableAttr(New, *A, Args); + S.SYCL().addSYCLAddIRAttributesGlobalVariableAttr(New, *A, Args); } static void instantiateSYCLAddIRAnnotationsMemberAttr( @@ -897,7 +899,7 @@ static void instantiateSYCLAddIRAnnotationsMemberAttr( if (S.SubstExprs(ArrayRef(A->args().begin(), A->args().end()), /*IsCall=*/false, TemplateArgs, Args)) return; - S.AddSYCLAddIRAnnotationsMemberAttr(New, *A, Args); + S.SYCL().addSYCLAddIRAnnotationsMemberAttr(New, *A, Args); } static void instantiateSYCLWorkGroupSizeHintAttr( @@ -915,8 +917,8 @@ static void instantiateSYCLWorkGroupSizeHintAttr( if (ZResult.isInvalid()) return; - S.AddSYCLWorkGroupSizeHintAttr(New, *A, XResult.get(), YResult.get(), - ZResult.get()); + S.SYCL().addSYCLWorkGroupSizeHintAttr(New, *A, XResult.get(), YResult.get(), + ZResult.get()); } static void instantiateSYCLIntelMaxWorkGroupSizeAttr( @@ -934,8 +936,8 @@ static void instantiateSYCLIntelMaxWorkGroupSizeAttr( if (ZResult.isInvalid()) return; - S.AddSYCLIntelMaxWorkGroupSizeAttr(New, *A, XResult.get(), YResult.get(), - ZResult.get()); + S.SYCL().addSYCLIntelMaxWorkGroupSizeAttr(New, *A, XResult.get(), + YResult.get(), ZResult.get()); } static void instantiateSYCLReqdWorkGroupSizeAttr( @@ -953,8 +955,8 @@ static void instantiateSYCLReqdWorkGroupSizeAttr( if (ZResult.isInvalid()) return; - S.AddSYCLReqdWorkGroupSizeAttr(New, *A, XResult.get(), YResult.get(), - ZResult.get()); + S.SYCL().addSYCLReqdWorkGroupSizeAttr(New, *A, XResult.get(), YResult.get(), + ZResult.get()); } // This doesn't take any template parameters, but we have a custom action that From 9a3cc74e594550ab1108f781462b4241d58e193a Mon Sep 17 00:00:00 2001 From: Fraser Cormack Date: Mon, 5 Aug 2024 18:06:50 +0100 Subject: [PATCH 2/5] rename function --- clang/include/clang/Sema/SemaSYCL.h | 4 ++-- clang/lib/Sema/SemaDeclAttr.cpp | 2 +- clang/lib/Sema/SemaSYCL.cpp | 5 +++-- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h index fd707efebdf14..f864f2ec1bc64 100644 --- a/clang/include/clang/Sema/SemaSYCL.h +++ b/clang/include/clang/Sema/SemaSYCL.h @@ -524,8 +524,8 @@ class SemaSYCL : public SemaBase { Expr *E); void addSYCLIntelBankBitsAttr(Decl *D, const AttributeCommonInfo &CI, Expr **Exprs, unsigned Size); - void addIntelReqdSubGroupSize(Decl *D, const AttributeCommonInfo &CI, - Expr *E); + void addIntelReqdSubGroupSizeAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); }; } // namespace clang diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 778dd9525052d..a37460d1f7b94 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3121,7 +3121,7 @@ static void handleIntelReqdSubGroupSize(Sema &S, Decl *D, S.SYCL().checkDeprecatedSYCLAttributeSpelling(AL); Expr *E = AL.getArgAsExpr(0); - S.SYCL().addIntelReqdSubGroupSize(D, AL, E); + S.SYCL().addIntelReqdSubGroupSizeAttr(D, AL, E); } IntelNamedSubGroupSizeAttr * diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index b2816b7875307..5890e25a182c0 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -7627,8 +7627,9 @@ void SemaSYCL::addSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, D->addAttr(::new (Context) SYCLIntelLoopFuseAttr(Context, CI, E)); } -void SemaSYCL::addIntelReqdSubGroupSize(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { +void SemaSYCL::addIntelReqdSubGroupSizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { ASTContext &Context = getASTContext(); if (!E->isValueDependent()) { // Validate that we have an integer constant expression and then store the diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 93999b2f0b11b..eeb3be647b8be 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -738,7 +738,7 @@ static void instantiateIntelReqdSubGroupSize( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); if (!Result.isInvalid()) - S.SYCL().addIntelReqdSubGroupSize(New, *A, Result.getAs()); + S.SYCL().addIntelReqdSubGroupSizeAttr(New, *A, Result.getAs()); } static void instantiateSYCLIntelNumSimdWorkItemsAttr( From 2f2190cdd73213d6ca2e55475285382588df98dc Mon Sep 17 00:00:00 2001 From: Fraser Cormack Date: Tue, 6 Aug 2024 15:05:00 +0100 Subject: [PATCH 3/5] move to separate file --- clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/SemaSYCL.cpp | 1831 -------------------------- clang/lib/Sema/SemaSYCLAttrDecl.cpp | 1849 +++++++++++++++++++++++++++ 3 files changed, 1850 insertions(+), 1831 deletions(-) create mode 100644 clang/lib/Sema/SemaSYCLAttrDecl.cpp diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index c5142187a8e0a..2d05ded840efc 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -86,6 +86,7 @@ add_clang_library(clangSema SemaStmtAsm.cpp SemaStmtAttr.cpp SemaSYCL.cpp + SemaSYCLAttrDecl.cpp SemaSwift.cpp SemaSystemZ.cpp SemaTemplate.cpp diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 70c5b0a54b60d..3ec9b3685880c 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -6839,1834 +6839,3 @@ ExprResult SemaSYCL::ActOnUniqueStableNameExpr(SourceLocation OpLoc, return BuildUniqueStableNameExpr(OpLoc, LParen, RParen, TSI); } - -void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) { - // The 'sycl_kernel' attribute applies only to function templates. - const auto *FD = cast(D); - const FunctionTemplateDecl *FT = FD->getDescribedFunctionTemplate(); - assert(FT && "Function template is expected"); - - // Function template must have at least two template parameters so it - // can be used in OpenCL kernel generation. - const TemplateParameterList *TL = FT->getTemplateParameters(); - if (TL->size() < 2) { - Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_template_params); - return; - } - - // The first two template parameters must be typenames. - for (unsigned I = 0; I < 2 && I < TL->size(); ++I) { - const NamedDecl *TParam = TL->getParam(I); - if (isa(TParam)) { - Diag(FT->getLocation(), - diag::warn_sycl_kernel_invalid_template_param_type); - return; - } - } - - // Function must have at least one parameter. - if (getFunctionOrMethodNumParams(D) < 1) { - Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_function_params); - return; - } - - // Function must return void. - QualType RetTy = getFunctionOrMethodResultType(D); - if (!RetTy->isVoidType()) { - Diag(FT->getLocation(), diag::warn_sycl_kernel_return_type); - return; - } - - handleSimpleAttribute(*this, D, AL); -} - -// Returns a DupArgResult value; Same means the args have the same value, -// Different means the args do not have the same value, and Unknown means that -// the args cannot (yet) be compared. -enum class DupArgResult { Unknown, Same, Different }; -static DupArgResult areArgValuesIdentical(const Expr *LHS, const Expr *RHS) { - // If both operands are nullptr they are unspecified and are considered the - // same. - if (!LHS && !RHS) - return DupArgResult::Same; - - // Otherwise, if either operand is nullptr they are considered different. - if (!LHS || !RHS) - return DupArgResult::Different; - - // Otherwise, if either operand is still value dependent, we can't test - // anything. - const auto *LHSCE = dyn_cast(LHS); - const auto *RHSCE = dyn_cast(RHS); - if (!LHSCE || !RHSCE) - return DupArgResult::Unknown; - - // Otherwise, test that the values. - return LHSCE->getResultAsAPSInt() == RHSCE->getResultAsAPSInt() - ? DupArgResult::Same - : DupArgResult::Different; -} - -// Returns true if any of the specified dimensions (X,Y,Z) differ between the -// arguments. -bool SemaSYCL::anyWorkGroupSizesDiffer(const Expr *LHSXDim, const Expr *LHSYDim, - const Expr *LHSZDim, const Expr *RHSXDim, - const Expr *RHSYDim, - const Expr *RHSZDim) { - DupArgResult Results[] = {areArgValuesIdentical(LHSXDim, RHSXDim), - areArgValuesIdentical(LHSYDim, RHSYDim), - areArgValuesIdentical(LHSZDim, RHSZDim)}; - return llvm::is_contained(Results, DupArgResult::Different); -} - -// Returns true if all of the specified dimensions (X,Y,Z) are the same between -// the arguments. -bool SemaSYCL::allWorkGroupSizesSame(const Expr *LHSXDim, const Expr *LHSYDim, - const Expr *LHSZDim, const Expr *RHSXDim, - const Expr *RHSYDim, const Expr *RHSZDim) { - DupArgResult Results[] = {areArgValuesIdentical(LHSXDim, RHSXDim), - areArgValuesIdentical(LHSYDim, RHSYDim), - areArgValuesIdentical(LHSZDim, RHSZDim)}; - return llvm::all_of(Results, - [](DupArgResult V) { return V == DupArgResult::Same; }); -} - -// Helper to get CudaArch. -CudaArch SemaSYCL::getCudaArch(const TargetInfo &TI) { - if (!TI.getTriple().isNVPTX()) - llvm_unreachable("getCudaArch is only valid for NVPTX triple"); - auto &TO = TI.getTargetOpts(); - return StringToCudaArch(TO.CPU); -} - -bool SemaSYCL::hasDependentExpr(Expr **Exprs, const size_t ExprsSize) { - return std::any_of(Exprs, Exprs + ExprsSize, [](const Expr *E) { - return E->isValueDependent() || E->isTypeDependent(); - }); -} - -void SemaSYCL::checkDeprecatedSYCLAttributeSpelling(const ParsedAttr &A, - StringRef NewName) { - // Additionally, diagnose the old [[intel::ii]] spelling. - if (A.getKind() == ParsedAttr::AT_SYCLIntelInitiationInterval && - A.getAttrName()->isStr("ii")) { - diagnoseDeprecatedAttribute(A, "intel", "initiation_interval"); - return; - } - - // Diagnose SYCL 2017 spellings in later SYCL modes. - if (getLangOpts().getSYCLVersion() > LangOptions::SYCL_2017) { - // All attributes in the cl vendor namespace are deprecated in favor of a - // name in the sycl namespace as of SYCL 2020. - if (A.hasScope() && A.getScopeName()->isStr("cl")) { - diagnoseDeprecatedAttribute(A, "sycl", NewName); - return; - } - - // All GNU-style spellings are deprecated in favor of a C++-style spelling. - if (A.getSyntax() == ParsedAttr::AS_GNU) { - // Note: we cannot suggest an automatic fix-it because GNU-style - // spellings can appear in locations that are not valid for a C++-style - // spelling, and the attribute could be part of an attribute list within - // a single __attribute__ specifier. Just tell the user it's deprecated - // manually. - // - // This currently assumes that the GNU-style spelling is the same as the - // SYCL 2020 spelling (sans the vendor namespace). - Diag(A.getLoc(), diag::warn_attribute_spelling_deprecated) - << "'" + A.getNormalizedFullName() + "'"; - Diag(A.getLoc(), diag::note_spelling_suggestion) - << "'[[sycl::" + A.getNormalizedFullName() + "]]'"; - return; - } - } - - // Diagnose SYCL 2020 spellings used in earlier SYCL modes as being an - // extension. - if (getLangOpts().getSYCLVersion() == LangOptions::SYCL_2017 && - A.hasScope() && A.getScopeName()->isStr("sycl")) { - Diag(A.getLoc(), diag::ext_sycl_2020_attr_spelling) << A; - return; - } -} - -void SemaSYCL::diagnoseDeprecatedAttribute(const ParsedAttr &A, - StringRef NewScope, - StringRef NewName) { - assert((!NewName.empty() || !NewScope.empty()) && - "Deprecated attribute with no new scope or name?"); - Diag(A.getLoc(), diag::warn_attribute_spelling_deprecated) - << "'" + A.getNormalizedFullName() + "'"; - - FixItHint Fix; - std::string NewFullName; - if (NewScope.empty() && !NewName.empty()) { - // Only have a new name. - Fix = FixItHint::CreateReplacement(A.getLoc(), NewName); - NewFullName = - ((A.hasScope() ? A.getScopeName()->getName() : StringRef("")) + - "::" + NewName) - .str(); - } else if (NewName.empty() && !NewScope.empty()) { - // Only have a new scope. - Fix = FixItHint::CreateReplacement(A.getScopeLoc(), NewScope); - NewFullName = (NewScope + "::" + A.getAttrName()->getName()).str(); - } else { - // Have both a new name and a new scope. - NewFullName = (NewScope + "::" + NewName).str(); - Fix = FixItHint::CreateReplacement(A.getRange(), NewFullName); - } - - Diag(A.getLoc(), diag::note_spelling_suggestion) - << "'" + NewFullName + "'" << Fix; -} - -// Checks if FPGA memory attributes apply on valid variables. -// Returns true if an error occured. -bool SemaSYCL::checkValidFPGAMemoryAttributesVar(Decl *D) { - // Check for SYCL device compilation context. - if (!getLangOpts().SYCLIsDevice) { - return false; - } - - const auto *VD = dyn_cast(D); - if (!VD) - return false; - - // Exclude implicit parameters and non-type template parameters. - if (VD->getKind() == Decl::ImplicitParam || - VD->getKind() == Decl::NonTypeTemplateParm) - return false; - - // Check for non-static data member. - if (isa(D)) - return false; - - // Check for SYCL device global attribute decoration. - if (isTypeDecoratedWithDeclAttribute(VD->getType())) - return false; - - // Check for constant variables and variables in the OpenCL constant - // address space. - if (VD->getType().isConstQualified() || - VD->getType().getAddressSpace() == LangAS::opencl_constant) - return false; - - // Check for static storage class or local storage. - if (VD->getStorageClass() == SC_Static || VD->hasLocalStorage()) - return false; - - return true; -} - -// Handles reqd_work_group_size. -// If the 'reqd_work_group_size' attribute is specified on a declaration along -// with 'num_simd_work_items' attribute, the required work group size specified -// by 'num_simd_work_items' attribute must evenly divide the index that -// increments fastest in the 'reqd_work_group_size' attribute. -// -// The arguments to reqd_work_group_size are ordered based on which index -// increments the fastest. In OpenCL, the first argument is the index that -// increments the fastest, and in SYCL, the last argument is the index that -// increments the fastest. -// -// __attribute__((reqd_work_group_size)) follows the OpenCL rules in OpenCL -// mode. All spellings of reqd_work_group_size attribute (regardless of -// syntax used) follow the SYCL rules when in SYCL mode. -bool SemaSYCL::checkWorkGroupSize(const Expr *NSWIValue, const Expr *RWGSXDim, - const Expr *RWGSYDim, const Expr *RWGSZDim) { - // If any of the operand is still value dependent, we can't test anything. - const auto *NSWIValueExpr = dyn_cast(NSWIValue); - const auto *RWGSXDimExpr = dyn_cast(RWGSXDim); - - if (!NSWIValueExpr || !RWGSXDimExpr) - return false; - - // Y and Z may be optional so we allow them to be null and consider them - // dependent if the original epxression was not null while the result of the - // cast is. - const auto *RWGSYDimExpr = dyn_cast_or_null(RWGSYDim); - const auto *RWGSZDimExpr = dyn_cast_or_null(RWGSZDim); - - if ((!RWGSYDimExpr && RWGSYDim) || (!RWGSZDimExpr && RWGSZDim)) - return false; - - // Otherwise, check which argument increments the fastest. - const ConstantExpr *LastRWGSDimExpr = - RWGSZDim ? RWGSZDimExpr : (RWGSYDim ? RWGSYDimExpr : RWGSXDimExpr); - unsigned WorkGroupSize = LastRWGSDimExpr->getResultAsAPSInt().getZExtValue(); - - // Check if the required work group size specified by 'num_simd_work_items' - // attribute evenly divides the index that increments fastest in the - // 'reqd_work_group_size' attribute. - return WorkGroupSize % NSWIValueExpr->getResultAsAPSInt().getZExtValue() != 0; -} - -// Checks correctness of mutual usage of different work_group_size attributes: -// reqd_work_group_size and max_work_group_size. -// -// If the 'reqd_work_group_size' attribute is specified on a declaration along -// with 'max_work_group_size' attribute, check to see if values of -// 'reqd_work_group_size' attribute arguments are equal to or less than values -// of 'max_work_group_size' attribute arguments. -// -// The arguments to reqd_work_group_size are ordered based on which index -// increments the fastest. In OpenCL, the first argument is the index that -// increments the fastest, and in SYCL, the last argument is the index that -// increments the fastest. -// -// __attribute__((reqd_work_group_size)) follows the OpenCL rules in OpenCL -// mode. All spellings of reqd_work_group_size attribute (regardless of -// syntax used) follow the SYCL rules when in SYCL mode. -bool SemaSYCL::checkMaxAllowedWorkGroupSize( - const Expr *RWGSXDim, const Expr *RWGSYDim, const Expr *RWGSZDim, - const Expr *MWGSXDim, const Expr *MWGSYDim, const Expr *MWGSZDim) { - // If any of the operand is still value dependent, we can't test anything. - const auto *RWGSXDimExpr = dyn_cast(RWGSXDim); - const auto *MWGSXDimExpr = dyn_cast(MWGSXDim); - const auto *MWGSYDimExpr = dyn_cast(MWGSYDim); - const auto *MWGSZDimExpr = dyn_cast(MWGSZDim); - - if (!RWGSXDimExpr || !MWGSXDimExpr || !MWGSYDimExpr || !MWGSZDimExpr) - return false; - - // Y and Z may be optional so we allow them to be null and consider them - // dependent if the original epxression was not null while the result of the - // cast is. - const auto *RWGSYDimExpr = dyn_cast_or_null(RWGSYDim); - const auto *RWGSZDimExpr = dyn_cast_or_null(RWGSZDim); - - if ((!RWGSYDimExpr && RWGSYDim) || (!RWGSZDimExpr && RWGSZDim)) - return false; - - // SYCL reorders arguments based on the dimensionality. - // If we only have the X-dimension, there is no change to the expressions, - // otherwise the last specified dimension acts as the first dimension in the - // work-group size. - const ConstantExpr *FirstRWGDimExpr = RWGSXDimExpr; - const ConstantExpr *SecondRWGDimExpr = RWGSYDimExpr; - const ConstantExpr *ThirdRWGDimExpr = RWGSZDimExpr; - if (getLangOpts().SYCLIsDevice && RWGSYDim) - std::swap(FirstRWGDimExpr, RWGSZDim ? ThirdRWGDimExpr : SecondRWGDimExpr); - - // Check if values of 'reqd_work_group_size' attribute arguments are greater - // than values of 'max_work_group_size' attribute arguments. - bool CheckFirstArgument = - FirstRWGDimExpr->getResultAsAPSInt().getZExtValue() > - MWGSZDimExpr->getResultAsAPSInt().getZExtValue(); - - bool CheckSecondArgument = - SecondRWGDimExpr && SecondRWGDimExpr->getResultAsAPSInt().getZExtValue() > - MWGSYDimExpr->getResultAsAPSInt().getZExtValue(); - - bool CheckThirdArgument = - ThirdRWGDimExpr && ThirdRWGDimExpr->getResultAsAPSInt().getZExtValue() > - MWGSXDimExpr->getResultAsAPSInt().getZExtValue(); - - return CheckFirstArgument || CheckSecondArgument || CheckThirdArgument; -} - -// Checks correctness of mutual usage of different work_group_size attributes: -// reqd_work_group_size, max_work_group_size, and max_global_work_dim. -// -// If [[intel::max_work_group_size(X, Y, Z)]] or -// [[sycl::reqd_work_group_size(X, Y, Z)]] or -// [[cl::reqd_work_group_size(X, Y, Z)]] -// or __attribute__((reqd_work_group_size)) attribute is specified on a -// declaration along with [[intel::max_global_work_dim()]] attribute, check to -// see if all arguments of 'max_work_group_size' or different spellings of -// 'reqd_work_group_size' attribute hold value 1 in case the argument of -// [[intel::max_global_work_dim()]] attribute value equals to 0. -bool SemaSYCL::areInvalidWorkGroupSizeAttrs(const Expr *MGValue, - const Expr *XDim, const Expr *YDim, - const Expr *ZDim) { - // If any of the operand is still value dependent, we can't test anything. - const auto *MGValueExpr = dyn_cast(MGValue); - const auto *XDimExpr = dyn_cast(XDim); - - if (!MGValueExpr || !XDimExpr) - return false; - - // Y and Z may be optional so we allow them to be null and consider them - // dependent if the original epxression was not null while the result of the - // cast is. - const auto *YDimExpr = dyn_cast_or_null(YDim); - const auto *ZDimExpr = dyn_cast_or_null(ZDim); - - if ((!YDimExpr && YDim) || (!ZDimExpr && ZDim)) - return false; - - // Otherwise, check if the attribute values are equal to one. - // Y and Z dimensions are optional and are considered trivially 1 if - // unspecified. - return (MGValueExpr->getResultAsAPSInt() == 0 && - (XDimExpr->getResultAsAPSInt() != 1 || - (YDimExpr && YDimExpr->getResultAsAPSInt() != 1) || - (ZDimExpr && ZDimExpr->getResultAsAPSInt() != 1))); -} - -void SemaSYCL::addSYCLIntelForcePow2DepthAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute accepts values 0 and 1 only. - if (ArgVal < 0 || ArgVal > 1) { - Diag(E->getBeginLoc(), diag::err_attribute_argument_is_not_valid) << CI; - return; - } - - // Check attribute applies to field, constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (checkValidFPGAMemoryAttributesVar(D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // If there is no mismatch, drop any duplicate attributes. - return; - } - } - } - - // If the declaration does not have an [[intel::fpga_memory]] - // attribute, this creates one as an implicit attribute. - ASTContext &Context = getASTContext(); - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) SYCLIntelForcePow2DepthAttr(Context, CI, E)); -} - -/// Handle the [[intel::bankwidth]] and [[intel::numbanks]] attributes. -/// These require a single constant power of two greater than zero. -/// These are incompatible with the register attribute. -/// The numbanks and bank_bits attributes are related. If bank_bits exists -/// when handling numbanks they are checked for consistency. -void SemaSYCL::addSYCLIntelBankWidthAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - - // This attribute requires a single constant power of two greater than zero. - if (!ArgVal.isPowerOf2()) { - Diag(E->getExprLoc(), diag::err_attribute_argument_not_power_of_two) - << CI; - return; - } - - // Check attribute applies to field, constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (checkValidFPGAMemoryAttributesVar(D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - // If the declaration does not have an [[intel::fpga_memory]] - // attribute, this creates one as an implicit attribute. - ASTContext &Context = getASTContext(); - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) SYCLIntelBankWidthAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelNumBanksAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - - // This attribute requires a single constant power of two greater than zero. - if (!ArgVal.isPowerOf2()) { - Diag(E->getExprLoc(), diag::err_attribute_argument_not_power_of_two) - << CI; - return; - } - - // Check or add the related BankBits attribute. - if (auto *BBA = D->getAttr()) { - unsigned NumBankBits = BBA->args_size(); - if (NumBankBits != ArgVal.ceilLogBase2()) { - Diag(E->getExprLoc(), diag::err_bankbits_numbanks_conflicting) << CI; - return; - } - } - - // Check attribute applies to constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (checkValidFPGAMemoryAttributesVar(D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - // If the declaration does not have an [[intel::fpga_memory]] - // attribute, this creates one as an implicit attribute. - ASTContext &Context = getASTContext(); - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - // We are adding a user NumBanks attribute, drop any implicit default. - if (auto *NBA = D->getAttr()) { - if (NBA->isImplicit()) - D->dropAttr(); - } - - D->addAttr(::new (Context) SYCLIntelNumBanksAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelBankBitsAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size) { - ASTContext &Context = getASTContext(); - SYCLIntelBankBitsAttr TmpAttr(Context, CI, Exprs, Size); - SmallVector Args; - SmallVector Values; - bool ListIsValueDep = false; - for (auto *E : TmpAttr.args()) { - llvm::APSInt Value(32, /*IsUnsigned=*/false); - Expr::EvalResult Result; - ListIsValueDep = ListIsValueDep || E->isValueDependent(); - if (!E->isValueDependent()) { - ExprResult ICE = SemaRef.VerifyIntegerConstantExpression(E, &Value); - if (ICE.isInvalid()) - return; - if (!Value.isNonNegative()) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - E = ICE.get(); - } - Args.push_back(E); - Values.push_back(Value.getExtValue()); - } - - // Check that the list is consecutive. - if (!ListIsValueDep && Values.size() > 1) { - bool ListIsAscending = Values[0] < Values[1]; - for (int I = 0, E = Values.size() - 1; I < E; ++I) { - if (Values[I + 1] != Values[I] + (ListIsAscending ? 1 : -1)) { - Diag(CI.getLoc(), diag::err_bankbits_non_consecutive) << &TmpAttr; - return; - } - } - } - - // Check or add the related numbanks attribute. - if (auto *NBA = D->getAttr()) { - Expr *E = NBA->getValue(); - if (!E->isValueDependent()) { - Expr::EvalResult Result; - E->EvaluateAsInt(Result, Context); - llvm::APSInt Value = Result.Val.getInt(); - if (Args.size() != Value.ceilLogBase2()) { - Diag(TmpAttr.getLoc(), diag::err_bankbits_numbanks_conflicting); - return; - } - } - } else { - llvm::APInt Num(32, (unsigned)(1 << Args.size())); - Expr *NBE = - IntegerLiteral::Create(Context, Num, Context.IntTy, SourceLocation()); - D->addAttr(SYCLIntelNumBanksAttr::CreateImplicit(Context, NBE)); - } - - // Check attribute applies to field, constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (checkValidFPGAMemoryAttributesVar(D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) - SYCLIntelBankBitsAttr(Context, CI, Args.data(), Args.size())); -} - -bool isDeviceAspectType(const QualType Ty) { - const EnumType *ET = Ty->getAs(); - if (!ET) - return false; - - if (const auto *Attr = ET->getDecl()->getAttr()) - return Attr->getType() == SYCLTypeAttr::aspect; - - return false; -} - -void SemaSYCL::addSYCLDeviceHasAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size) { - ASTContext &Context = getASTContext(); - SYCLDeviceHasAttr TmpAttr(Context, CI, Exprs, Size); - SmallVector Aspects; - for (auto *E : TmpAttr.aspects()) - if (!isa(E) && !isDeviceAspectType(E->getType())) - Diag(E->getExprLoc(), diag::err_sycl_invalid_aspect_argument) << CI; - - if (const auto *ExistingAttr = D->getAttr()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute_exact) << CI; - Diag(ExistingAttr->getLoc(), diag::note_previous_attribute); - return; - } - - D->addAttr(::new (Context) SYCLDeviceHasAttr(Context, CI, Exprs, Size)); -} - -void SemaSYCL::addSYCLUsesAspectsAttr(Decl *D, const AttributeCommonInfo &CI, - Expr **Exprs, unsigned Size) { - ASTContext &Context = getASTContext(); - SYCLUsesAspectsAttr TmpAttr(Context, CI, Exprs, Size); - SmallVector Aspects; - for (auto *E : TmpAttr.aspects()) - if (!isDeviceAspectType(E->getType())) - Diag(E->getExprLoc(), diag::err_sycl_invalid_aspect_argument) << CI; - - if (const auto *ExistingAttr = D->getAttr()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute_exact) << CI; - Diag(ExistingAttr->getLoc(), diag::note_previous_attribute); - return; - } - - D->addAttr(::new (Context) SYCLUsesAspectsAttr(Context, CI, Exprs, Size)); -} - -void SemaSYCL::addSYCLIntelPipeIOAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - VarDecl *VD = cast(D); - QualType Ty = VD->getType(); - // TODO: Applicable only on pipe storages. Currently they are defined - // as structures inside of SYCL headers. Add a check for pipe_storage_t - // when it is ready. - if (!Ty->isStructureType()) { - Diag(CI.getLoc(), diag::err_attribute_wrong_decl_type_str) - << CI << CI.isRegularKeywordAttribute() - << "SYCL pipe storage declaration"; - return; - } - - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getID())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) SYCLIntelPipeIOAttr(Context, CI, E)); -} - -// Handles [[intel::loop_fuse]] and [[intel::loop_fuse_independent]]. -void SemaSYCL::addSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // [[intel::loop_fuse]] and [[intel::loop_fuse_independent]] are - // incompatible. - // FIXME: If additional spellings are provided for this attribute, - // this code will do the wrong thing. - if (DeclAttr->getAttributeSpellingListIndex() != - CI.getAttributeSpellingListIndex()) { - Diag(CI.getLoc(), diag::err_attributes_are_not_compatible) - << CI << DeclAttr << CI.isRegularKeywordAttribute(); - Diag(DeclAttr->getLocation(), diag::note_conflicting_attribute); - return; - } - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) SYCLIntelLoopFuseAttr(Context, CI, E)); -} - -void SemaSYCL::addIntelReqdSubGroupSizeAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - ASTContext &Context = getASTContext(); - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - auto &TI = Context.getTargetInfo(); - if (TI.getTriple().isNVPTX() && ArgVal != 32) - Diag(E->getExprLoc(), diag::warn_reqd_sub_group_attribute_n) - << ArgVal.getSExtValue() << TI.getTriple().getArchName() << 32; - if (TI.getTriple().isAMDGPU()) { - const auto HasWaveFrontSize64 = - TI.getTargetOpts().FeatureMap["wavefrontsize64"]; - const auto HasWaveFrontSize32 = - TI.getTargetOpts().FeatureMap["wavefrontsize32"]; - - // CDNA supports only 64 wave front size, for those GPUs allow subgroup - // size of 64. Some GPUs support both 32 and 64, for those (and the rest) - // only allow 32. Warn on incompatible sizes. - const auto SupportedWaveFrontSize = - HasWaveFrontSize64 && !HasWaveFrontSize32 ? 64 : 32; - if (ArgVal != SupportedWaveFrontSize) - Diag(E->getExprLoc(), diag::warn_reqd_sub_group_attribute_n) - << ArgVal.getSExtValue() << TI.getTriple().getArchName() - << SupportedWaveFrontSize; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) IntelReqdSubGroupSizeAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelNumSimdWorkItemsAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - - // If the 'reqd_work_group_size' attribute is specified on a declaration - // along with 'num_simd_work_items' attribute, the required work group size - // specified by 'num_simd_work_items' attribute must evenly divide the index - // that increments fastest in the 'reqd_work_group_size' attribute. - if (const auto *DeclAttr = D->getAttr()) { - if (checkWorkGroupSize(E, DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim())) { - Diag(CI.getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) - << CI << DeclAttr; - Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); - return; - } - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) SYCLIntelNumSimdWorkItemsAttr(Context, CI, E)); -} - -// Handle scheduler_target_fmax_mhz -void SemaSYCL::addSYCLIntelSchedulerTargetFmaxMhzAttr( - Decl *D, const AttributeCommonInfo &CI, Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = - D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) - SYCLIntelSchedulerTargetFmaxMhzAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelNoGlobalWorkOffsetAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) SYCLIntelNoGlobalWorkOffsetAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelMaxGlobalWorkDimAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute must be in the range [0, 3]. - if (ArgVal < 0 || ArgVal > 3) { - Diag(E->getBeginLoc(), diag::err_attribute_argument_out_of_range) - << CI << 0 << 3 << E->getSourceRange(); - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - - // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr or - // SYCLReqdWorkGroupSizeAttr, check to see if the attribute holds values - // equal to (1, 1, 1) in case the value of SYCLIntelMaxGlobalWorkDimAttr - // equals to 0. - if (ArgVal == 0) { - if (checkWorkGroupSizeAttrExpr(D, CI) || - checkWorkGroupSizeAttrExpr(D, CI)) - return; - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) SYCLIntelMaxGlobalWorkDimAttr(Context, CI, E)); -} - -// Check that the value is a non-negative integer constant that can fit in -// 32-bits. Issue correct error message and return false on failure. -bool static check32BitInt(const Expr *E, SemaSYCL &S, llvm::APSInt &I, - const AttributeCommonInfo &CI) { - if (!I.isIntN(32)) { - S.Diag(E->getExprLoc(), diag::err_ice_too_large) - << llvm::toString(I, 10, false) << 32 << /* Unsigned */ 1; - return false; - } - - if (I.isSigned() && I.isNegative()) { - S.Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /* Non-negative */ 1; - return false; - } - - return true; -} - -void SemaSYCL::addSYCLIntelMinWorkGroupsPerComputeUnitAttr( - Decl *D, const AttributeCommonInfo &CI, Expr *E) { - ASTContext &Context = getASTContext(); - if (getLangOpts().SYCLIsDevice) { - if (!Context.getTargetInfo().getTriple().isNVPTX()) { - Diag(E->getBeginLoc(), diag::warn_launch_bounds_is_cuda_specific) - << CI << E->getSourceRange(); - return; - } - - if (!D->hasAttr()) { - Diag(CI.getLoc(), diag::warn_launch_bounds_missing_attr) << CI << 0; - return; - } - } - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - if (!check32BitInt(E, *this, ArgVal, CI)) - return; - E = Res.get(); - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = - D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) - SYCLIntelMinWorkGroupsPerComputeUnitAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( - Decl *D, const AttributeCommonInfo &CI, Expr *E) { - ASTContext &Context = getASTContext(); - auto &TI = Context.getTargetInfo(); - if (Context.getLangOpts().SYCLIsDevice) { - if (!TI.getTriple().isNVPTX()) { - Diag(E->getBeginLoc(), diag::warn_launch_bounds_is_cuda_specific) - << CI << E->getSourceRange(); - return; - } - - // Feature '.maxclusterrank' requires .target sm_90 or higher. - auto SM = getCudaArch(TI); - if (SM == CudaArch::UNKNOWN || SM < CudaArch::SM_90) { - Diag(E->getBeginLoc(), diag::warn_cuda_maxclusterrank_sm_90) - << CudaArchToString(SM) << CI << E->getSourceRange(); - return; - } - - if (!D->hasAttr() || - !D->hasAttr()) { - Diag(CI.getLoc(), diag::warn_launch_bounds_missing_attr) << CI << 1; - return; - } - } - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - if (!check32BitInt(E, *this, ArgVal, CI)) - return; - E = Res.get(); - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = - D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - D->addAttr(::new (Context) - SYCLIntelMaxWorkGroupsPerMultiprocessorAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelMaxConcurrencyAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getNExpr())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) SYCLIntelMaxConcurrencyAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelPrivateCopiesAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - ASTContext &Context = getASTContext(); - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - // This attribute requires a non-negative value. - if (ArgVal < 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*non-negative*/ 1; - return; - } - - // Check attribute applies to field as well as const variables, non-static - // local variables, non-static data members, and device_global variables. - // for the device compilation. - if (const auto *VD = dyn_cast(D)) { - if (Context.getLangOpts().SYCLIsDevice && - (!(isa(D) || - (VD->getKind() != Decl::ImplicitParam && - VD->getKind() != Decl::NonTypeTemplateParm && - VD->getKind() != Decl::ParmVar && - (VD->hasLocalStorage() || - isTypeDecoratedWithDeclAttribute( - VD->getType())))))) { - Diag(CI.getLoc(), diag::err_fpga_attribute_invalid_decl) << CI; - return; - } - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - // If the declaration does not have [[intel::fpga_memory]] - // attribute, this creates default implicit memory. - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) SYCLIntelPrivateCopiesAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelMaxReplicatesAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - - // Check attribute applies to field, constant variables, local variables, - // static variables, agent memory arguments, non-static data members, - // and device_global variables for the device compilation. - if (checkValidFPGAMemoryAttributesVar(D)) { - Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) - << CI << /*agent memory arguments*/ 1; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - // If the declaration does not have an [[intel::fpga_memory]] - // attribute, this creates one as an implicit attribute. - ASTContext &Context = getASTContext(); - if (!D->hasAttr()) - D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( - Context, SYCLIntelMemoryAttr::Default)); - - D->addAttr(::new (Context) SYCLIntelMaxReplicatesAttr(Context, CI, E)); -} - -// Handles initiation_interval attribute. -void SemaSYCL::addSYCLIntelInitiationIntervalAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return; - } - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getNExpr())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) SYCLIntelInitiationIntervalAttr(Context, CI, E)); -} - -void SemaSYCL::addSYCLIntelESimdVectorizeAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *E) { - if (!E->isValueDependent()) { - // Validate that we have an integer constant expression and then store the - // converted constant expression into the semantic attribute so that we - // don't have to evaluate it again later. - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return; - E = Res.get(); - - if (ArgVal != 8 && ArgVal != 16 && ArgVal != 32) { - Diag(E->getExprLoc(), diag::err_sycl_esimd_vectorize_unsupported_value) - << CI; - return; - } - - // Check to see if there's a duplicate attribute with different values - // already applied to the declaration. - if (const auto *DeclAttr = D->getAttr()) { - // If the other attribute argument is instantiation dependent, we won't - // have converted it to a constant expression yet and thus we test - // whether this is a null pointer. - if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { - if (ArgVal != DeclExpr->getResultAsAPSInt()) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(DeclAttr->getLoc(), diag::note_previous_attribute); - } - // Drop the duplicate attribute. - return; - } - } - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) SYCLIntelESimdVectorizeAttr(Context, CI, E)); -} - -// Checks if an expression is a valid filter list for an add_ir_attributes_* -// attribute. Returns true if an error occured. -static bool checkAddIRAttributesFilterListExpr(Expr *FilterListArg, SemaSYCL &S, - const AttributeCommonInfo &CI) { - const auto *FilterListE = cast(FilterListArg); - for (const Expr *FilterElemE : FilterListE->inits()) - if (!isa(FilterElemE)) - return S.Diag(FilterElemE->getBeginLoc(), - diag::err_sycl_add_ir_attribute_invalid_filter) - << CI; - return false; -} - -// Returns true if a type is either an array of char or a pointer to char. -static bool isAddIRAttributesValidStringType(QualType T) { - if (!T->isArrayType() && !T->isPointerType()) - return false; - QualType ElemT = T->isArrayType() - ? cast(T.getTypePtr())->getElementType() - : T->getPointeeType(); - return ElemT.isConstQualified() && ElemT->isCharType(); -} - -// Checks if an expression is a valid attribute value for an add_ir_attributes_* -// attribute. Returns true if an error occured. -static bool checkAddIRAttributesValueExpr(Expr *ValArg, SemaSYCL &S, - const AttributeCommonInfo &CI) { - QualType ValType = ValArg->getType(); - if (isAddIRAttributesValidStringType(ValType) || ValType->isNullPtrType() || - ValType->isIntegralOrEnumerationType() || ValType->isFloatingType()) - return false; - - return S.Diag(ValArg->getBeginLoc(), - diag::err_sycl_add_ir_attribute_invalid_value) - << CI; -} - -// Checks if an expression is a valid attribute name for an add_ir_attributes_* -// attribute. Returns true if an error occured. -static bool checkAddIRAttributesNameExpr(Expr *NameArg, SemaSYCL &S, - const AttributeCommonInfo &CI) { - // Only strings and const char * are valid name arguments. - if (isAddIRAttributesValidStringType(NameArg->getType())) - return false; - - return S.Diag(NameArg->getBeginLoc(), - diag::err_sycl_add_ir_attribute_invalid_name) - << CI; -} - -// Checks and evaluates arguments of an add_ir_attributes_* attribute. Returns -// true if an error occured. -static bool evaluateAddIRAttributesArgs(Expr **Args, size_t ArgsSize, - SemaSYCL &S, - const AttributeCommonInfo &CI) { - ASTContext &Context = S.getASTContext(); - - // Check filter list if it is the first argument. - bool HasFilter = ArgsSize && isa(Args[0]); - if (HasFilter && checkAddIRAttributesFilterListExpr(Args[0], S, CI)) - return true; - - llvm::SmallVector Notes; - bool HasDependentArg = false; - for (unsigned I = HasFilter; I < ArgsSize; I++) { - Expr *&E = Args[I]; - - if (isa(E)) - return S.Diag(E->getBeginLoc(), - diag::err_sycl_add_ir_attr_filter_list_invalid_arg) - << CI; - - if (E->isValueDependent() || E->isTypeDependent()) { - HasDependentArg = true; - continue; - } - - Expr::EvalResult Eval; - Eval.Diag = &Notes; - if (!E->EvaluateAsConstantExpr(Eval, Context) || !Notes.empty()) { - S.Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type) - << CI << (I + 1) << AANT_ArgumentConstantExpr; - for (auto &Note : Notes) - S.Diag(Note.first, Note.second); - return true; - } - assert(Eval.Val.hasValue()); - E = ConstantExpr::Create(Context, E, Eval.Val); - } - - // If there are no dependent expressions, check for expected number of args. - if (!HasDependentArg && ArgsSize && (ArgsSize - HasFilter) & 1) - return S.Diag(CI.getLoc(), diag::err_sycl_add_ir_attribute_must_have_pairs) - << CI; - - // If there are no dependent expressions, check argument types. - // First half of the arguments are names, the second half are values. - unsigned MidArg = (ArgsSize - HasFilter) / 2 + HasFilter; - if (!HasDependentArg) { - for (unsigned I = HasFilter; I < ArgsSize; ++I) { - if ((I < MidArg && checkAddIRAttributesNameExpr(Args[I], S, CI)) || - (I >= MidArg && checkAddIRAttributesValueExpr(Args[I], S, CI))) - return true; - } - } - return false; -} - -void SemaSYCL::addSYCLAddIRAttributesFunctionAttr( - Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { - if (const auto *FuncD = dyn_cast(D)) { - if (FuncD->isDefaulted()) { - Diag(CI.getLoc(), diag::err_disallow_attribute_on_func) << CI << 0; - return; - } - if (FuncD->isDeleted()) { - Diag(CI.getLoc(), diag::err_disallow_attribute_on_func) << CI << 1; - return; - } - } - - ASTContext &Context = getASTContext(); - auto *Attr = SYCLAddIRAttributesFunctionAttr::Create(Context, Args.data(), - Args.size(), CI); - if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, - CI)) - return; - D->addAttr(Attr); - - // There are compile-time SYCL properties which we would like to turn into - // attributes to enable compiler diagnostics. - // At the moment the only such property is related to virtual functions and - // it is turned into sycl_device attribute. This is a tiny optimization to - // avoid deep dive into the attribute if we already know that a declaration - // is a device declaration. It may have to be removed later if/when we add - // handling of more compile-time properties here. - if (D->hasAttr()) - return; - - // SYCL Headers use template magic to pass key=value pairs to the attribute - // and we should make sure that all template instantiations are done before - // accessing attribute arguments. - if (hasDependentExpr(Attr->args_begin(), Attr->args_size())) - return; - - SmallVector, 4> Pairs = - Attr->getFilteredAttributeNameValuePairs(Context); - - for (const auto &[Key, Value] : Pairs) { - if (Key == "indirectly-callable") { - D->addAttr(SYCLDeviceAttr::CreateImplicit(Context)); - break; - } - } -} - -void SemaSYCL::addSYCLAddIRAttributesKernelParameterAttr( - Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { - ASTContext &Context = getASTContext(); - auto *Attr = SYCLAddIRAttributesKernelParameterAttr::Create( - Context, Args.data(), Args.size(), CI); - if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, - CI)) - return; - D->addAttr(Attr); -} - -void SemaSYCL::addSYCLAddIRAttributesGlobalVariableAttr( - Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { - ASTContext &Context = getASTContext(); - auto *Attr = SYCLAddIRAttributesGlobalVariableAttr::Create( - Context, Args.data(), Args.size(), CI); - if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, - CI)) - return; - D->addAttr(Attr); -} - -void SemaSYCL::addSYCLAddIRAnnotationsMemberAttr(Decl *D, - const AttributeCommonInfo &CI, - MutableArrayRef Args) { - ASTContext &Context = getASTContext(); - auto *Attr = SYCLAddIRAnnotationsMemberAttr::Create(Context, Args.data(), - Args.size(), CI); - if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, - CI)) - return; - D->addAttr(Attr); -} - -void SemaSYCL::addSYCLWorkGroupSizeHintAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, - Expr *ZDim) { - // Returns nullptr if diagnosing, otherwise returns the original expression - // or the original expression converted to a constant expression. - auto CheckAndConvertArg = [&](Expr *E) -> std::optional { - // We can only check if the expression is not value dependent. - if (E && !E->isValueDependent()) { - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return std::nullopt; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return std::nullopt; - } - } - - return E; - }; - - // Check all three argument values, and if any are bad, bail out. This will - // convert the given expressions into constant expressions when possible. - std::optional XDimConvert = CheckAndConvertArg(XDim); - std::optional YDimConvert = CheckAndConvertArg(YDim); - std::optional ZDimConvert = CheckAndConvertArg(ZDim); - if (!XDimConvert || !YDimConvert || !ZDimConvert) - return; - XDim = XDimConvert.value(); - YDim = YDimConvert.value(); - ZDim = ZDimConvert.value(); - - // If the attribute was already applied with different arguments, then - // diagnose the second attribute as a duplicate and don't add it. - if (const auto *Existing = D->getAttr()) { - // If any of the results are known to be different, we can diagnose at this - // point and drop the attribute. - if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(Existing->getLoc(), diag::note_previous_attribute); - return; - } - // If all of the results are known to be the same, we can silently drop the - // attribute. Otherwise, we have to add the attribute and resolve its - // differences later. - if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) - return; - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) - SYCLWorkGroupSizeHintAttr(Context, CI, XDim, YDim, ZDim)); -} - -void SemaSYCL::addSYCLIntelMaxWorkGroupSizeAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, - Expr *ZDim) { - // Returns nullptr if diagnosing, otherwise returns the original expression - // or the original expression converted to a constant expression. - auto CheckAndConvertArg = [&](Expr *E) -> Expr * { - // Check if the expression is not value dependent. - if (!E->isValueDependent()) { - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return nullptr; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return nullptr; - } - } - return E; - }; - - // Check all three argument values, and if any are bad, bail out. This will - // convert the given expressions into constant expressions when possible. - XDim = CheckAndConvertArg(XDim); - YDim = CheckAndConvertArg(YDim); - ZDim = CheckAndConvertArg(ZDim); - if (!XDim || !YDim || !ZDim) - return; - - // If the 'max_work_group_size' attribute is specified on a declaration along - // with 'reqd_work_group_size' attribute, check to see if values of - // 'reqd_work_group_size' attribute arguments are equal to or less than values - // of 'max_work_group_size' attribute arguments. - // - // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments - // are greater than values of 'max_work_group_size' attribute arguments. - if (const auto *DeclAttr = D->getAttr()) { - if (checkMaxAllowedWorkGroupSize(DeclAttr->getXDim(), DeclAttr->getYDim(), - DeclAttr->getZDim(), XDim, YDim, ZDim)) { - Diag(CI.getLoc(), diag::err_conflicting_sycl_function_attributes) - << CI << DeclAttr; - Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); - return; - } - } - - // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr, check to see if - // the attribute holds values equal to (1, 1, 1) in case the value of - // SYCLIntelMaxGlobalWorkDimAttr equals to 0. - if (const auto *DeclAttr = D->getAttr()) { - if (areInvalidWorkGroupSizeAttrs(DeclAttr->getValue(), XDim, YDim, ZDim)) { - Diag(CI.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) - << CI << DeclAttr; - return; - } - } - - // If the attribute was already applied with different arguments, then - // diagnose the second attribute as a duplicate and don't add it. - if (const auto *Existing = D->getAttr()) { - // If any of the results are known to be different, we can diagnose at this - // point and drop the attribute. - if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) { - Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; - Diag(Existing->getLoc(), diag::note_previous_attribute); - return; - } - // If all of the results are known to be the same, we can silently drop the - // attribute. Otherwise, we have to add the attribute and resolve its - // differences later. - if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) - return; - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) - SYCLIntelMaxWorkGroupSizeAttr(Context, CI, XDim, YDim, ZDim)); -} - -void SemaSYCL::addSYCLReqdWorkGroupSizeAttr(Decl *D, - const AttributeCommonInfo &CI, - Expr *XDim, Expr *YDim, - Expr *ZDim) { - // Returns nullptr if diagnosing, otherwise returns the original expression - // or the original expression converted to a constant expression. - auto CheckAndConvertArg = [&](Expr *E) -> std::optional { - // Check if the expression is not value dependent. - if (E && !E->isValueDependent()) { - llvm::APSInt ArgVal; - ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); - if (Res.isInvalid()) - return std::nullopt; - E = Res.get(); - - // This attribute requires a strictly positive value. - if (ArgVal <= 0) { - Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) - << CI << /*positive*/ 0; - return std::nullopt; - } - } - return E; - }; - - // Check all three argument values, and if any are bad, bail out. This will - // convert the given expressions into constant expressions when possible. - std::optional XDimConvert = CheckAndConvertArg(XDim); - std::optional YDimConvert = CheckAndConvertArg(YDim); - std::optional ZDimConvert = CheckAndConvertArg(ZDim); - if (!XDimConvert || !YDimConvert || !ZDimConvert) - return; - XDim = XDimConvert.value(); - YDim = YDimConvert.value(); - ZDim = ZDimConvert.value(); - - // If the declaration has a ReqdWorkGroupSizeAttr, check to see if - // the attribute holds values equal to (1, 1, 1) in case the value of - // SYCLIntelMaxGlobalWorkDimAttr equals to 0. - if (const auto *DeclAttr = D->getAttr()) { - if (areInvalidWorkGroupSizeAttrs(DeclAttr->getValue(), XDim, YDim, ZDim)) { - Diag(CI.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) - << CI << DeclAttr; - } - } - - // If the 'max_work_group_size' attribute is specified on a declaration along - // with 'reqd_work_group_size' attribute, check to see if values of - // 'reqd_work_group_size' attribute arguments are equal to or less than values - // of 'max_work_group_size' attribute arguments. - // - // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments - // are greater than values of 'max_work_group_size' attribute arguments. - if (const auto *DeclAttr = D->getAttr()) { - if (checkMaxAllowedWorkGroupSize(XDim, YDim, ZDim, DeclAttr->getXDim(), - DeclAttr->getYDim(), - DeclAttr->getZDim())) { - Diag(CI.getLoc(), diag::err_conflicting_sycl_function_attributes) - << CI << DeclAttr; - Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); - return; - } - } - - // If the 'reqd_work_group_size' attribute is specified on a declaration - // along with 'num_simd_work_items' attribute, the required work group size - // specified by 'num_simd_work_items' attribute must evenly divide the index - // that increments fastest in the 'reqd_work_group_size' attribute. - if (const auto *DeclAttr = D->getAttr()) { - if (checkWorkGroupSize(DeclAttr->getValue(), XDim, YDim, ZDim)) { - Diag(DeclAttr->getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) - << DeclAttr << CI; - Diag(CI.getLoc(), diag::note_conflicting_attribute); - return; - } - } - - // If the attribute was already applied with different arguments, then - // diagnose the second attribute as a duplicate and don't add it. - if (const auto *Existing = D->getAttr()) { - // If any of the results are known to be different, we can diagnose at this - // point and drop the attribute. - if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) { - Diag(CI.getLoc(), diag::err_duplicate_attribute) << CI; - Diag(Existing->getLoc(), diag::note_previous_attribute); - return; - } - - // If all of the results are known to be the same, we can silently drop the - // attribute. Otherwise, we have to add the attribute and resolve its - // differences later. - if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), - Existing->getYDim(), Existing->getZDim())) - return; - } - - ASTContext &Context = getASTContext(); - D->addAttr(::new (Context) - SYCLReqdWorkGroupSizeAttr(Context, CI, XDim, YDim, ZDim)); -} diff --git a/clang/lib/Sema/SemaSYCLAttrDecl.cpp b/clang/lib/Sema/SemaSYCLAttrDecl.cpp new file mode 100644 index 0000000000000..8c1eec3adc689 --- /dev/null +++ b/clang/lib/Sema/SemaSYCLAttrDecl.cpp @@ -0,0 +1,1849 @@ +//===- SemaSYCLAttrDecl.cpp - Semantic Analysis for SYCL 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 +// +//===----------------------------------------------------------------------===// +// This implements Semantic Analysis for SYCL attributes. +//===----------------------------------------------------------------------===// + +#include "clang/Basic/TargetInfo.h" +#include "clang/Sema/Attr.h" +#include "clang/Sema/Initialization.h" +#include "clang/Sema/ParsedAttr.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaSYCL.h" + +using namespace clang; + +void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) { + // The 'sycl_kernel' attribute applies only to function templates. + const auto *FD = cast(D); + const FunctionTemplateDecl *FT = FD->getDescribedFunctionTemplate(); + assert(FT && "Function template is expected"); + + // Function template must have at least two template parameters so it + // can be used in OpenCL kernel generation. + const TemplateParameterList *TL = FT->getTemplateParameters(); + if (TL->size() < 2) { + Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_template_params); + return; + } + + // The first two template parameters must be typenames. + for (unsigned I = 0; I < 2 && I < TL->size(); ++I) { + const NamedDecl *TParam = TL->getParam(I); + if (isa(TParam)) { + Diag(FT->getLocation(), + diag::warn_sycl_kernel_invalid_template_param_type); + return; + } + } + + // Function must have at least one parameter. + if (getFunctionOrMethodNumParams(D) < 1) { + Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_function_params); + return; + } + + // Function must return void. + QualType RetTy = getFunctionOrMethodResultType(D); + if (!RetTy->isVoidType()) { + Diag(FT->getLocation(), diag::warn_sycl_kernel_return_type); + return; + } + + handleSimpleAttribute(*this, D, AL); +} + +// Returns a DupArgResult value; Same means the args have the same value, +// Different means the args do not have the same value, and Unknown means that +// the args cannot (yet) be compared. +enum class DupArgResult { Unknown, Same, Different }; +static DupArgResult areArgValuesIdentical(const Expr *LHS, const Expr *RHS) { + // If both operands are nullptr they are unspecified and are considered the + // same. + if (!LHS && !RHS) + return DupArgResult::Same; + + // Otherwise, if either operand is nullptr they are considered different. + if (!LHS || !RHS) + return DupArgResult::Different; + + // Otherwise, if either operand is still value dependent, we can't test + // anything. + const auto *LHSCE = dyn_cast(LHS); + const auto *RHSCE = dyn_cast(RHS); + if (!LHSCE || !RHSCE) + return DupArgResult::Unknown; + + // Otherwise, test that the values. + return LHSCE->getResultAsAPSInt() == RHSCE->getResultAsAPSInt() + ? DupArgResult::Same + : DupArgResult::Different; +} + +// Returns true if any of the specified dimensions (X,Y,Z) differ between the +// arguments. +bool SemaSYCL::anyWorkGroupSizesDiffer(const Expr *LHSXDim, const Expr *LHSYDim, + const Expr *LHSZDim, const Expr *RHSXDim, + const Expr *RHSYDim, + const Expr *RHSZDim) { + DupArgResult Results[] = {areArgValuesIdentical(LHSXDim, RHSXDim), + areArgValuesIdentical(LHSYDim, RHSYDim), + areArgValuesIdentical(LHSZDim, RHSZDim)}; + return llvm::is_contained(Results, DupArgResult::Different); +} + +// Returns true if all of the specified dimensions (X,Y,Z) are the same between +// the arguments. +bool SemaSYCL::allWorkGroupSizesSame(const Expr *LHSXDim, const Expr *LHSYDim, + const Expr *LHSZDim, const Expr *RHSXDim, + const Expr *RHSYDim, const Expr *RHSZDim) { + DupArgResult Results[] = {areArgValuesIdentical(LHSXDim, RHSXDim), + areArgValuesIdentical(LHSYDim, RHSYDim), + areArgValuesIdentical(LHSZDim, RHSZDim)}; + return llvm::all_of(Results, + [](DupArgResult V) { return V == DupArgResult::Same; }); +} + +// Helper to get CudaArch. +CudaArch SemaSYCL::getCudaArch(const TargetInfo &TI) { + if (!TI.getTriple().isNVPTX()) + llvm_unreachable("getCudaArch is only valid for NVPTX triple"); + auto &TO = TI.getTargetOpts(); + return StringToCudaArch(TO.CPU); +} + +bool SemaSYCL::hasDependentExpr(Expr **Exprs, const size_t ExprsSize) { + return std::any_of(Exprs, Exprs + ExprsSize, [](const Expr *E) { + return E->isValueDependent() || E->isTypeDependent(); + }); +} + +void SemaSYCL::checkDeprecatedSYCLAttributeSpelling(const ParsedAttr &A, + StringRef NewName) { + // Additionally, diagnose the old [[intel::ii]] spelling. + if (A.getKind() == ParsedAttr::AT_SYCLIntelInitiationInterval && + A.getAttrName()->isStr("ii")) { + diagnoseDeprecatedAttribute(A, "intel", "initiation_interval"); + return; + } + + // Diagnose SYCL 2017 spellings in later SYCL modes. + if (getLangOpts().getSYCLVersion() > LangOptions::SYCL_2017) { + // All attributes in the cl vendor namespace are deprecated in favor of a + // name in the sycl namespace as of SYCL 2020. + if (A.hasScope() && A.getScopeName()->isStr("cl")) { + diagnoseDeprecatedAttribute(A, "sycl", NewName); + return; + } + + // All GNU-style spellings are deprecated in favor of a C++-style spelling. + if (A.getSyntax() == ParsedAttr::AS_GNU) { + // Note: we cannot suggest an automatic fix-it because GNU-style + // spellings can appear in locations that are not valid for a C++-style + // spelling, and the attribute could be part of an attribute list within + // a single __attribute__ specifier. Just tell the user it's deprecated + // manually. + // + // This currently assumes that the GNU-style spelling is the same as the + // SYCL 2020 spelling (sans the vendor namespace). + Diag(A.getLoc(), diag::warn_attribute_spelling_deprecated) + << "'" + A.getNormalizedFullName() + "'"; + Diag(A.getLoc(), diag::note_spelling_suggestion) + << "'[[sycl::" + A.getNormalizedFullName() + "]]'"; + return; + } + } + + // Diagnose SYCL 2020 spellings used in earlier SYCL modes as being an + // extension. + if (getLangOpts().getSYCLVersion() == LangOptions::SYCL_2017 && + A.hasScope() && A.getScopeName()->isStr("sycl")) { + Diag(A.getLoc(), diag::ext_sycl_2020_attr_spelling) << A; + return; + } +} + +void SemaSYCL::diagnoseDeprecatedAttribute(const ParsedAttr &A, + StringRef NewScope, + StringRef NewName) { + assert((!NewName.empty() || !NewScope.empty()) && + "Deprecated attribute with no new scope or name?"); + Diag(A.getLoc(), diag::warn_attribute_spelling_deprecated) + << "'" + A.getNormalizedFullName() + "'"; + + FixItHint Fix; + std::string NewFullName; + if (NewScope.empty() && !NewName.empty()) { + // Only have a new name. + Fix = FixItHint::CreateReplacement(A.getLoc(), NewName); + NewFullName = + ((A.hasScope() ? A.getScopeName()->getName() : StringRef("")) + + "::" + NewName) + .str(); + } else if (NewName.empty() && !NewScope.empty()) { + // Only have a new scope. + Fix = FixItHint::CreateReplacement(A.getScopeLoc(), NewScope); + NewFullName = (NewScope + "::" + A.getAttrName()->getName()).str(); + } else { + // Have both a new name and a new scope. + NewFullName = (NewScope + "::" + NewName).str(); + Fix = FixItHint::CreateReplacement(A.getRange(), NewFullName); + } + + Diag(A.getLoc(), diag::note_spelling_suggestion) + << "'" + NewFullName + "'" << Fix; +} + +// Checks if FPGA memory attributes apply on valid variables. +// Returns true if an error occured. +bool SemaSYCL::checkValidFPGAMemoryAttributesVar(Decl *D) { + // Check for SYCL device compilation context. + if (!getLangOpts().SYCLIsDevice) { + return false; + } + + const auto *VD = dyn_cast(D); + if (!VD) + return false; + + // Exclude implicit parameters and non-type template parameters. + if (VD->getKind() == Decl::ImplicitParam || + VD->getKind() == Decl::NonTypeTemplateParm) + return false; + + // Check for non-static data member. + if (isa(D)) + return false; + + // Check for SYCL device global attribute decoration. + if (isTypeDecoratedWithDeclAttribute(VD->getType())) + return false; + + // Check for constant variables and variables in the OpenCL constant + // address space. + if (VD->getType().isConstQualified() || + VD->getType().getAddressSpace() == LangAS::opencl_constant) + return false; + + // Check for static storage class or local storage. + if (VD->getStorageClass() == SC_Static || VD->hasLocalStorage()) + return false; + + return true; +} + +// Handles reqd_work_group_size. +// If the 'reqd_work_group_size' attribute is specified on a declaration along +// with 'num_simd_work_items' attribute, the required work group size specified +// by 'num_simd_work_items' attribute must evenly divide the index that +// increments fastest in the 'reqd_work_group_size' attribute. +// +// The arguments to reqd_work_group_size are ordered based on which index +// increments the fastest. In OpenCL, the first argument is the index that +// increments the fastest, and in SYCL, the last argument is the index that +// increments the fastest. +// +// __attribute__((reqd_work_group_size)) follows the OpenCL rules in OpenCL +// mode. All spellings of reqd_work_group_size attribute (regardless of +// syntax used) follow the SYCL rules when in SYCL mode. +bool SemaSYCL::checkWorkGroupSize(const Expr *NSWIValue, const Expr *RWGSXDim, + const Expr *RWGSYDim, const Expr *RWGSZDim) { + // If any of the operand is still value dependent, we can't test anything. + const auto *NSWIValueExpr = dyn_cast(NSWIValue); + const auto *RWGSXDimExpr = dyn_cast(RWGSXDim); + + if (!NSWIValueExpr || !RWGSXDimExpr) + return false; + + // Y and Z may be optional so we allow them to be null and consider them + // dependent if the original epxression was not null while the result of the + // cast is. + const auto *RWGSYDimExpr = dyn_cast_or_null(RWGSYDim); + const auto *RWGSZDimExpr = dyn_cast_or_null(RWGSZDim); + + if ((!RWGSYDimExpr && RWGSYDim) || (!RWGSZDimExpr && RWGSZDim)) + return false; + + // Otherwise, check which argument increments the fastest. + const ConstantExpr *LastRWGSDimExpr = + RWGSZDim ? RWGSZDimExpr : (RWGSYDim ? RWGSYDimExpr : RWGSXDimExpr); + unsigned WorkGroupSize = LastRWGSDimExpr->getResultAsAPSInt().getZExtValue(); + + // Check if the required work group size specified by 'num_simd_work_items' + // attribute evenly divides the index that increments fastest in the + // 'reqd_work_group_size' attribute. + return WorkGroupSize % NSWIValueExpr->getResultAsAPSInt().getZExtValue() != 0; +} + +// Checks correctness of mutual usage of different work_group_size attributes: +// reqd_work_group_size and max_work_group_size. +// +// If the 'reqd_work_group_size' attribute is specified on a declaration along +// with 'max_work_group_size' attribute, check to see if values of +// 'reqd_work_group_size' attribute arguments are equal to or less than values +// of 'max_work_group_size' attribute arguments. +// +// The arguments to reqd_work_group_size are ordered based on which index +// increments the fastest. In OpenCL, the first argument is the index that +// increments the fastest, and in SYCL, the last argument is the index that +// increments the fastest. +// +// __attribute__((reqd_work_group_size)) follows the OpenCL rules in OpenCL +// mode. All spellings of reqd_work_group_size attribute (regardless of +// syntax used) follow the SYCL rules when in SYCL mode. +bool SemaSYCL::checkMaxAllowedWorkGroupSize( + const Expr *RWGSXDim, const Expr *RWGSYDim, const Expr *RWGSZDim, + const Expr *MWGSXDim, const Expr *MWGSYDim, const Expr *MWGSZDim) { + // If any of the operand is still value dependent, we can't test anything. + const auto *RWGSXDimExpr = dyn_cast(RWGSXDim); + const auto *MWGSXDimExpr = dyn_cast(MWGSXDim); + const auto *MWGSYDimExpr = dyn_cast(MWGSYDim); + const auto *MWGSZDimExpr = dyn_cast(MWGSZDim); + + if (!RWGSXDimExpr || !MWGSXDimExpr || !MWGSYDimExpr || !MWGSZDimExpr) + return false; + + // Y and Z may be optional so we allow them to be null and consider them + // dependent if the original epxression was not null while the result of the + // cast is. + const auto *RWGSYDimExpr = dyn_cast_or_null(RWGSYDim); + const auto *RWGSZDimExpr = dyn_cast_or_null(RWGSZDim); + + if ((!RWGSYDimExpr && RWGSYDim) || (!RWGSZDimExpr && RWGSZDim)) + return false; + + // SYCL reorders arguments based on the dimensionality. + // If we only have the X-dimension, there is no change to the expressions, + // otherwise the last specified dimension acts as the first dimension in the + // work-group size. + const ConstantExpr *FirstRWGDimExpr = RWGSXDimExpr; + const ConstantExpr *SecondRWGDimExpr = RWGSYDimExpr; + const ConstantExpr *ThirdRWGDimExpr = RWGSZDimExpr; + if (getLangOpts().SYCLIsDevice && RWGSYDim) + std::swap(FirstRWGDimExpr, RWGSZDim ? ThirdRWGDimExpr : SecondRWGDimExpr); + + // Check if values of 'reqd_work_group_size' attribute arguments are greater + // than values of 'max_work_group_size' attribute arguments. + bool CheckFirstArgument = + FirstRWGDimExpr->getResultAsAPSInt().getZExtValue() > + MWGSZDimExpr->getResultAsAPSInt().getZExtValue(); + + bool CheckSecondArgument = + SecondRWGDimExpr && SecondRWGDimExpr->getResultAsAPSInt().getZExtValue() > + MWGSYDimExpr->getResultAsAPSInt().getZExtValue(); + + bool CheckThirdArgument = + ThirdRWGDimExpr && ThirdRWGDimExpr->getResultAsAPSInt().getZExtValue() > + MWGSXDimExpr->getResultAsAPSInt().getZExtValue(); + + return CheckFirstArgument || CheckSecondArgument || CheckThirdArgument; +} + +// Checks correctness of mutual usage of different work_group_size attributes: +// reqd_work_group_size, max_work_group_size, and max_global_work_dim. +// +// If [[intel::max_work_group_size(X, Y, Z)]] or +// [[sycl::reqd_work_group_size(X, Y, Z)]] or +// [[cl::reqd_work_group_size(X, Y, Z)]] +// or __attribute__((reqd_work_group_size)) attribute is specified on a +// declaration along with [[intel::max_global_work_dim()]] attribute, check to +// see if all arguments of 'max_work_group_size' or different spellings of +// 'reqd_work_group_size' attribute hold value 1 in case the argument of +// [[intel::max_global_work_dim()]] attribute value equals to 0. +bool SemaSYCL::areInvalidWorkGroupSizeAttrs(const Expr *MGValue, + const Expr *XDim, const Expr *YDim, + const Expr *ZDim) { + // If any of the operand is still value dependent, we can't test anything. + const auto *MGValueExpr = dyn_cast(MGValue); + const auto *XDimExpr = dyn_cast(XDim); + + if (!MGValueExpr || !XDimExpr) + return false; + + // Y and Z may be optional so we allow them to be null and consider them + // dependent if the original epxression was not null while the result of the + // cast is. + const auto *YDimExpr = dyn_cast_or_null(YDim); + const auto *ZDimExpr = dyn_cast_or_null(ZDim); + + if ((!YDimExpr && YDim) || (!ZDimExpr && ZDim)) + return false; + + // Otherwise, check if the attribute values are equal to one. + // Y and Z dimensions are optional and are considered trivially 1 if + // unspecified. + return (MGValueExpr->getResultAsAPSInt() == 0 && + (XDimExpr->getResultAsAPSInt() != 1 || + (YDimExpr && YDimExpr->getResultAsAPSInt() != 1) || + (ZDimExpr && ZDimExpr->getResultAsAPSInt() != 1))); +} + +void SemaSYCL::addSYCLIntelForcePow2DepthAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute accepts values 0 and 1 only. + if (ArgVal < 0 || ArgVal > 1) { + Diag(E->getBeginLoc(), diag::err_attribute_argument_is_not_valid) << CI; + return; + } + + // Check attribute applies to field, constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // If there is no mismatch, drop any duplicate attributes. + return; + } + } + } + + // If the declaration does not have an [[intel::fpga_memory]] + // attribute, this creates one as an implicit attribute. + ASTContext &Context = getASTContext(); + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) SYCLIntelForcePow2DepthAttr(Context, CI, E)); +} + +/// Handle the [[intel::bankwidth]] and [[intel::numbanks]] attributes. +/// These require a single constant power of two greater than zero. +/// These are incompatible with the register attribute. +/// The numbanks and bank_bits attributes are related. If bank_bits exists +/// when handling numbanks they are checked for consistency. +void SemaSYCL::addSYCLIntelBankWidthAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + // This attribute requires a single constant power of two greater than zero. + if (!ArgVal.isPowerOf2()) { + Diag(E->getExprLoc(), diag::err_attribute_argument_not_power_of_two) + << CI; + return; + } + + // Check attribute applies to field, constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + // If the declaration does not have an [[intel::fpga_memory]] + // attribute, this creates one as an implicit attribute. + ASTContext &Context = getASTContext(); + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) SYCLIntelBankWidthAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelNumBanksAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + // This attribute requires a single constant power of two greater than zero. + if (!ArgVal.isPowerOf2()) { + Diag(E->getExprLoc(), diag::err_attribute_argument_not_power_of_two) + << CI; + return; + } + + // Check or add the related BankBits attribute. + if (auto *BBA = D->getAttr()) { + unsigned NumBankBits = BBA->args_size(); + if (NumBankBits != ArgVal.ceilLogBase2()) { + Diag(E->getExprLoc(), diag::err_bankbits_numbanks_conflicting) << CI; + return; + } + } + + // Check attribute applies to constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + // If the declaration does not have an [[intel::fpga_memory]] + // attribute, this creates one as an implicit attribute. + ASTContext &Context = getASTContext(); + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + // We are adding a user NumBanks attribute, drop any implicit default. + if (auto *NBA = D->getAttr()) { + if (NBA->isImplicit()) + D->dropAttr(); + } + + D->addAttr(::new (Context) SYCLIntelNumBanksAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelBankBitsAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size) { + ASTContext &Context = getASTContext(); + SYCLIntelBankBitsAttr TmpAttr(Context, CI, Exprs, Size); + SmallVector Args; + SmallVector Values; + bool ListIsValueDep = false; + for (auto *E : TmpAttr.args()) { + llvm::APSInt Value(32, /*IsUnsigned=*/false); + Expr::EvalResult Result; + ListIsValueDep = ListIsValueDep || E->isValueDependent(); + if (!E->isValueDependent()) { + ExprResult ICE = SemaRef.VerifyIntegerConstantExpression(E, &Value); + if (ICE.isInvalid()) + return; + if (!Value.isNonNegative()) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + E = ICE.get(); + } + Args.push_back(E); + Values.push_back(Value.getExtValue()); + } + + // Check that the list is consecutive. + if (!ListIsValueDep && Values.size() > 1) { + bool ListIsAscending = Values[0] < Values[1]; + for (int I = 0, E = Values.size() - 1; I < E; ++I) { + if (Values[I + 1] != Values[I] + (ListIsAscending ? 1 : -1)) { + Diag(CI.getLoc(), diag::err_bankbits_non_consecutive) << &TmpAttr; + return; + } + } + } + + // Check or add the related numbanks attribute. + if (auto *NBA = D->getAttr()) { + Expr *E = NBA->getValue(); + if (!E->isValueDependent()) { + Expr::EvalResult Result; + E->EvaluateAsInt(Result, Context); + llvm::APSInt Value = Result.Val.getInt(); + if (Args.size() != Value.ceilLogBase2()) { + Diag(TmpAttr.getLoc(), diag::err_bankbits_numbanks_conflicting); + return; + } + } + } else { + llvm::APInt Num(32, (unsigned)(1 << Args.size())); + Expr *NBE = + IntegerLiteral::Create(Context, Num, Context.IntTy, SourceLocation()); + D->addAttr(SYCLIntelNumBanksAttr::CreateImplicit(Context, NBE)); + } + + // Check attribute applies to field, constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) + SYCLIntelBankBitsAttr(Context, CI, Args.data(), Args.size())); +} + +bool isDeviceAspectType(const QualType Ty) { + const EnumType *ET = Ty->getAs(); + if (!ET) + return false; + + if (const auto *Attr = ET->getDecl()->getAttr()) + return Attr->getType() == SYCLTypeAttr::aspect; + + return false; +} + +void SemaSYCL::addSYCLDeviceHasAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size) { + ASTContext &Context = getASTContext(); + SYCLDeviceHasAttr TmpAttr(Context, CI, Exprs, Size); + SmallVector Aspects; + for (auto *E : TmpAttr.aspects()) + if (!isa(E) && !isDeviceAspectType(E->getType())) + Diag(E->getExprLoc(), diag::err_sycl_invalid_aspect_argument) << CI; + + if (const auto *ExistingAttr = D->getAttr()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute_exact) << CI; + Diag(ExistingAttr->getLoc(), diag::note_previous_attribute); + return; + } + + D->addAttr(::new (Context) SYCLDeviceHasAttr(Context, CI, Exprs, Size)); +} + +void SemaSYCL::addSYCLUsesAspectsAttr(Decl *D, const AttributeCommonInfo &CI, + Expr **Exprs, unsigned Size) { + ASTContext &Context = getASTContext(); + SYCLUsesAspectsAttr TmpAttr(Context, CI, Exprs, Size); + SmallVector Aspects; + for (auto *E : TmpAttr.aspects()) + if (!isDeviceAspectType(E->getType())) + Diag(E->getExprLoc(), diag::err_sycl_invalid_aspect_argument) << CI; + + if (const auto *ExistingAttr = D->getAttr()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute_exact) << CI; + Diag(ExistingAttr->getLoc(), diag::note_previous_attribute); + return; + } + + D->addAttr(::new (Context) SYCLUsesAspectsAttr(Context, CI, Exprs, Size)); +} + +void SemaSYCL::addSYCLIntelPipeIOAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + VarDecl *VD = cast(D); + QualType Ty = VD->getType(); + // TODO: Applicable only on pipe storages. Currently they are defined + // as structures inside of SYCL headers. Add a check for pipe_storage_t + // when it is ready. + if (!Ty->isStructureType()) { + Diag(CI.getLoc(), diag::err_attribute_wrong_decl_type_str) + << CI << CI.isRegularKeywordAttribute() + << "SYCL pipe storage declaration"; + return; + } + + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getID())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelPipeIOAttr(Context, CI, E)); +} + +// Handles [[intel::loop_fuse]] and [[intel::loop_fuse_independent]]. +void SemaSYCL::addSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // [[intel::loop_fuse]] and [[intel::loop_fuse_independent]] are + // incompatible. + // FIXME: If additional spellings are provided for this attribute, + // this code will do the wrong thing. + if (DeclAttr->getAttributeSpellingListIndex() != + CI.getAttributeSpellingListIndex()) { + Diag(CI.getLoc(), diag::err_attributes_are_not_compatible) + << CI << DeclAttr << CI.isRegularKeywordAttribute(); + Diag(DeclAttr->getLocation(), diag::note_conflicting_attribute); + return; + } + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelLoopFuseAttr(Context, CI, E)); +} + +void SemaSYCL::addIntelReqdSubGroupSizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + ASTContext &Context = getASTContext(); + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + auto &TI = Context.getTargetInfo(); + if (TI.getTriple().isNVPTX() && ArgVal != 32) + Diag(E->getExprLoc(), diag::warn_reqd_sub_group_attribute_n) + << ArgVal.getSExtValue() << TI.getTriple().getArchName() << 32; + if (TI.getTriple().isAMDGPU()) { + const auto HasWaveFrontSize64 = + TI.getTargetOpts().FeatureMap["wavefrontsize64"]; + const auto HasWaveFrontSize32 = + TI.getTargetOpts().FeatureMap["wavefrontsize32"]; + + // CDNA supports only 64 wave front size, for those GPUs allow subgroup + // size of 64. Some GPUs support both 32 and 64, for those (and the rest) + // only allow 32. Warn on incompatible sizes. + const auto SupportedWaveFrontSize = + HasWaveFrontSize64 && !HasWaveFrontSize32 ? 64 : 32; + if (ArgVal != SupportedWaveFrontSize) + Diag(E->getExprLoc(), diag::warn_reqd_sub_group_attribute_n) + << ArgVal.getSExtValue() << TI.getTriple().getArchName() + << SupportedWaveFrontSize; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + D->addAttr(::new (Context) IntelReqdSubGroupSizeAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelNumSimdWorkItemsAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + + // If the 'reqd_work_group_size' attribute is specified on a declaration + // along with 'num_simd_work_items' attribute, the required work group size + // specified by 'num_simd_work_items' attribute must evenly divide the index + // that increments fastest in the 'reqd_work_group_size' attribute. + if (const auto *DeclAttr = D->getAttr()) { + if (checkWorkGroupSize(E, DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim())) { + Diag(CI.getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) + << CI << DeclAttr; + Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelNumSimdWorkItemsAttr(Context, CI, E)); +} + +// Handle scheduler_target_fmax_mhz +void SemaSYCL::addSYCLIntelSchedulerTargetFmaxMhzAttr( + Decl *D, const AttributeCommonInfo &CI, Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = + D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) + SYCLIntelSchedulerTargetFmaxMhzAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelNoGlobalWorkOffsetAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelNoGlobalWorkOffsetAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelMaxGlobalWorkDimAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute must be in the range [0, 3]. + if (ArgVal < 0 || ArgVal > 3) { + Diag(E->getBeginLoc(), diag::err_attribute_argument_out_of_range) + << CI << 0 << 3 << E->getSourceRange(); + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + + // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr or + // SYCLReqdWorkGroupSizeAttr, check to see if the attribute holds values + // equal to (1, 1, 1) in case the value of SYCLIntelMaxGlobalWorkDimAttr + // equals to 0. + if (ArgVal == 0) { + if (checkWorkGroupSizeAttrExpr(D, CI) || + checkWorkGroupSizeAttrExpr(D, CI)) + return; + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelMaxGlobalWorkDimAttr(Context, CI, E)); +} + +// Check that the value is a non-negative integer constant that can fit in +// 32-bits. Issue correct error message and return false on failure. +bool static check32BitInt(const Expr *E, SemaSYCL &S, llvm::APSInt &I, + const AttributeCommonInfo &CI) { + if (!I.isIntN(32)) { + S.Diag(E->getExprLoc(), diag::err_ice_too_large) + << llvm::toString(I, 10, false) << 32 << /* Unsigned */ 1; + return false; + } + + if (I.isSigned() && I.isNegative()) { + S.Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /* Non-negative */ 1; + return false; + } + + return true; +} + +void SemaSYCL::addSYCLIntelMinWorkGroupsPerComputeUnitAttr( + Decl *D, const AttributeCommonInfo &CI, Expr *E) { + ASTContext &Context = getASTContext(); + if (getLangOpts().SYCLIsDevice) { + if (!Context.getTargetInfo().getTriple().isNVPTX()) { + Diag(E->getBeginLoc(), diag::warn_launch_bounds_is_cuda_specific) + << CI << E->getSourceRange(); + return; + } + + if (!D->hasAttr()) { + Diag(CI.getLoc(), diag::warn_launch_bounds_missing_attr) << CI << 0; + return; + } + } + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + if (!check32BitInt(E, *this, ArgVal, CI)) + return; + E = Res.get(); + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = + D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + D->addAttr(::new (Context) + SYCLIntelMinWorkGroupsPerComputeUnitAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelMaxWorkGroupsPerMultiprocessorAttr( + Decl *D, const AttributeCommonInfo &CI, Expr *E) { + ASTContext &Context = getASTContext(); + auto &TI = Context.getTargetInfo(); + if (Context.getLangOpts().SYCLIsDevice) { + if (!TI.getTriple().isNVPTX()) { + Diag(E->getBeginLoc(), diag::warn_launch_bounds_is_cuda_specific) + << CI << E->getSourceRange(); + return; + } + + // Feature '.maxclusterrank' requires .target sm_90 or higher. + auto SM = getCudaArch(TI); + if (SM == CudaArch::UNKNOWN || SM < CudaArch::SM_90) { + Diag(E->getBeginLoc(), diag::warn_cuda_maxclusterrank_sm_90) + << CudaArchToString(SM) << CI << E->getSourceRange(); + return; + } + + if (!D->hasAttr() || + !D->hasAttr()) { + Diag(CI.getLoc(), diag::warn_launch_bounds_missing_attr) << CI << 1; + return; + } + } + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + if (!check32BitInt(E, *this, ArgVal, CI)) + return; + E = Res.get(); + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = + D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + D->addAttr(::new (Context) + SYCLIntelMaxWorkGroupsPerMultiprocessorAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelMaxConcurrencyAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getNExpr())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelMaxConcurrencyAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelPrivateCopiesAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + ASTContext &Context = getASTContext(); + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + // This attribute requires a non-negative value. + if (ArgVal < 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*non-negative*/ 1; + return; + } + + // Check attribute applies to field as well as const variables, non-static + // local variables, non-static data members, and device_global variables. + // for the device compilation. + if (const auto *VD = dyn_cast(D)) { + if (Context.getLangOpts().SYCLIsDevice && + (!(isa(D) || + (VD->getKind() != Decl::ImplicitParam && + VD->getKind() != Decl::NonTypeTemplateParm && + VD->getKind() != Decl::ParmVar && + (VD->hasLocalStorage() || + isTypeDecoratedWithDeclAttribute( + VD->getType())))))) { + Diag(CI.getLoc(), diag::err_fpga_attribute_invalid_decl) << CI; + return; + } + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + // If the declaration does not have [[intel::fpga_memory]] + // attribute, this creates default implicit memory. + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) SYCLIntelPrivateCopiesAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelMaxReplicatesAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + // Check attribute applies to field, constant variables, local variables, + // static variables, agent memory arguments, non-static data members, + // and device_global variables for the device compilation. + if (checkValidFPGAMemoryAttributesVar(D)) { + Diag(CI.getLoc(), diag::err_fpga_attribute_incorrect_variable) + << CI << /*agent memory arguments*/ 1; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + // If the declaration does not have an [[intel::fpga_memory]] + // attribute, this creates one as an implicit attribute. + ASTContext &Context = getASTContext(); + if (!D->hasAttr()) + D->addAttr(SYCLIntelMemoryAttr::CreateImplicit( + Context, SYCLIntelMemoryAttr::Default)); + + D->addAttr(::new (Context) SYCLIntelMaxReplicatesAttr(Context, CI, E)); +} + +// Handles initiation_interval attribute. +void SemaSYCL::addSYCLIntelInitiationIntervalAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getNExpr())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelInitiationIntervalAttr(Context, CI, E)); +} + +void SemaSYCL::addSYCLIntelESimdVectorizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + if (ArgVal != 8 && ArgVal != 16 && ArgVal != 32) { + Diag(E->getExprLoc(), diag::err_sycl_esimd_vectorize_unsupported_value) + << CI; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) SYCLIntelESimdVectorizeAttr(Context, CI, E)); +} + +// Checks if an expression is a valid filter list for an add_ir_attributes_* +// attribute. Returns true if an error occured. +static bool checkAddIRAttributesFilterListExpr(Expr *FilterListArg, SemaSYCL &S, + const AttributeCommonInfo &CI) { + const auto *FilterListE = cast(FilterListArg); + for (const Expr *FilterElemE : FilterListE->inits()) + if (!isa(FilterElemE)) + return S.Diag(FilterElemE->getBeginLoc(), + diag::err_sycl_add_ir_attribute_invalid_filter) + << CI; + return false; +} + +// Returns true if a type is either an array of char or a pointer to char. +static bool isAddIRAttributesValidStringType(QualType T) { + if (!T->isArrayType() && !T->isPointerType()) + return false; + QualType ElemT = T->isArrayType() + ? cast(T.getTypePtr())->getElementType() + : T->getPointeeType(); + return ElemT.isConstQualified() && ElemT->isCharType(); +} + +// Checks if an expression is a valid attribute value for an add_ir_attributes_* +// attribute. Returns true if an error occured. +static bool checkAddIRAttributesValueExpr(Expr *ValArg, SemaSYCL &S, + const AttributeCommonInfo &CI) { + QualType ValType = ValArg->getType(); + if (isAddIRAttributesValidStringType(ValType) || ValType->isNullPtrType() || + ValType->isIntegralOrEnumerationType() || ValType->isFloatingType()) + return false; + + return S.Diag(ValArg->getBeginLoc(), + diag::err_sycl_add_ir_attribute_invalid_value) + << CI; +} + +// Checks if an expression is a valid attribute name for an add_ir_attributes_* +// attribute. Returns true if an error occured. +static bool checkAddIRAttributesNameExpr(Expr *NameArg, SemaSYCL &S, + const AttributeCommonInfo &CI) { + // Only strings and const char * are valid name arguments. + if (isAddIRAttributesValidStringType(NameArg->getType())) + return false; + + return S.Diag(NameArg->getBeginLoc(), + diag::err_sycl_add_ir_attribute_invalid_name) + << CI; +} + +// Checks and evaluates arguments of an add_ir_attributes_* attribute. Returns +// true if an error occured. +static bool evaluateAddIRAttributesArgs(Expr **Args, size_t ArgsSize, + SemaSYCL &S, + const AttributeCommonInfo &CI) { + ASTContext &Context = S.getASTContext(); + + // Check filter list if it is the first argument. + bool HasFilter = ArgsSize && isa(Args[0]); + if (HasFilter && checkAddIRAttributesFilterListExpr(Args[0], S, CI)) + return true; + + llvm::SmallVector Notes; + bool HasDependentArg = false; + for (unsigned I = HasFilter; I < ArgsSize; I++) { + Expr *&E = Args[I]; + + if (isa(E)) + return S.Diag(E->getBeginLoc(), + diag::err_sycl_add_ir_attr_filter_list_invalid_arg) + << CI; + + if (E->isValueDependent() || E->isTypeDependent()) { + HasDependentArg = true; + continue; + } + + Expr::EvalResult Eval; + Eval.Diag = &Notes; + if (!E->EvaluateAsConstantExpr(Eval, Context) || !Notes.empty()) { + S.Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type) + << CI << (I + 1) << AANT_ArgumentConstantExpr; + for (auto &Note : Notes) + S.Diag(Note.first, Note.second); + return true; + } + assert(Eval.Val.hasValue()); + E = ConstantExpr::Create(Context, E, Eval.Val); + } + + // If there are no dependent expressions, check for expected number of args. + if (!HasDependentArg && ArgsSize && (ArgsSize - HasFilter) & 1) + return S.Diag(CI.getLoc(), diag::err_sycl_add_ir_attribute_must_have_pairs) + << CI; + + // If there are no dependent expressions, check argument types. + // First half of the arguments are names, the second half are values. + unsigned MidArg = (ArgsSize - HasFilter) / 2 + HasFilter; + if (!HasDependentArg) { + for (unsigned I = HasFilter; I < ArgsSize; ++I) { + if ((I < MidArg && checkAddIRAttributesNameExpr(Args[I], S, CI)) || + (I >= MidArg && checkAddIRAttributesValueExpr(Args[I], S, CI))) + return true; + } + } + return false; +} + +void SemaSYCL::addSYCLAddIRAttributesFunctionAttr( + Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { + if (const auto *FuncD = dyn_cast(D)) { + if (FuncD->isDefaulted()) { + Diag(CI.getLoc(), diag::err_disallow_attribute_on_func) << CI << 0; + return; + } + if (FuncD->isDeleted()) { + Diag(CI.getLoc(), diag::err_disallow_attribute_on_func) << CI << 1; + return; + } + } + + ASTContext &Context = getASTContext(); + auto *Attr = SYCLAddIRAttributesFunctionAttr::Create(Context, Args.data(), + Args.size(), CI); + if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, + CI)) + return; + D->addAttr(Attr); + + // There are compile-time SYCL properties which we would like to turn into + // attributes to enable compiler diagnostics. + // At the moment the only such property is related to virtual functions and + // it is turned into sycl_device attribute. This is a tiny optimization to + // avoid deep dive into the attribute if we already know that a declaration + // is a device declaration. It may have to be removed later if/when we add + // handling of more compile-time properties here. + if (D->hasAttr()) + return; + + // SYCL Headers use template magic to pass key=value pairs to the attribute + // and we should make sure that all template instantiations are done before + // accessing attribute arguments. + if (hasDependentExpr(Attr->args_begin(), Attr->args_size())) + return; + + SmallVector, 4> Pairs = + Attr->getFilteredAttributeNameValuePairs(Context); + + for (const auto &[Key, Value] : Pairs) { + if (Key == "indirectly-callable") { + D->addAttr(SYCLDeviceAttr::CreateImplicit(Context)); + break; + } + } +} + +void SemaSYCL::addSYCLAddIRAttributesKernelParameterAttr( + Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { + ASTContext &Context = getASTContext(); + auto *Attr = SYCLAddIRAttributesKernelParameterAttr::Create( + Context, Args.data(), Args.size(), CI); + if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, + CI)) + return; + D->addAttr(Attr); +} + +void SemaSYCL::addSYCLAddIRAttributesGlobalVariableAttr( + Decl *D, const AttributeCommonInfo &CI, MutableArrayRef Args) { + ASTContext &Context = getASTContext(); + auto *Attr = SYCLAddIRAttributesGlobalVariableAttr::Create( + Context, Args.data(), Args.size(), CI); + if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, + CI)) + return; + D->addAttr(Attr); +} + +void SemaSYCL::addSYCLAddIRAnnotationsMemberAttr(Decl *D, + const AttributeCommonInfo &CI, + MutableArrayRef Args) { + ASTContext &Context = getASTContext(); + auto *Attr = SYCLAddIRAnnotationsMemberAttr::Create(Context, Args.data(), + Args.size(), CI); + if (evaluateAddIRAttributesArgs(Attr->args_begin(), Attr->args_size(), *this, + CI)) + return; + D->addAttr(Attr); +} + +void SemaSYCL::addSYCLWorkGroupSizeHintAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, + Expr *ZDim) { + // Returns nullptr if diagnosing, otherwise returns the original expression + // or the original expression converted to a constant expression. + auto CheckAndConvertArg = [&](Expr *E) -> std::optional { + // We can only check if the expression is not value dependent. + if (E && !E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return std::nullopt; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return std::nullopt; + } + } + + return E; + }; + + // Check all three argument values, and if any are bad, bail out. This will + // convert the given expressions into constant expressions when possible. + std::optional XDimConvert = CheckAndConvertArg(XDim); + std::optional YDimConvert = CheckAndConvertArg(YDim); + std::optional ZDimConvert = CheckAndConvertArg(ZDim); + if (!XDimConvert || !YDimConvert || !ZDimConvert) + return; + XDim = XDimConvert.value(); + YDim = YDimConvert.value(); + ZDim = ZDimConvert.value(); + + // If the attribute was already applied with different arguments, then + // diagnose the second attribute as a duplicate and don't add it. + if (const auto *Existing = D->getAttr()) { + // If any of the results are known to be different, we can diagnose at this + // point and drop the attribute. + if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(Existing->getLoc(), diag::note_previous_attribute); + return; + } + // If all of the results are known to be the same, we can silently drop the + // attribute. Otherwise, we have to add the attribute and resolve its + // differences later. + if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) + return; + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) + SYCLWorkGroupSizeHintAttr(Context, CI, XDim, YDim, ZDim)); +} + +void SemaSYCL::addSYCLIntelMaxWorkGroupSizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, + Expr *ZDim) { + // Returns nullptr if diagnosing, otherwise returns the original expression + // or the original expression converted to a constant expression. + auto CheckAndConvertArg = [&](Expr *E) -> Expr * { + // Check if the expression is not value dependent. + if (!E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return nullptr; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return nullptr; + } + } + return E; + }; + + // Check all three argument values, and if any are bad, bail out. This will + // convert the given expressions into constant expressions when possible. + XDim = CheckAndConvertArg(XDim); + YDim = CheckAndConvertArg(YDim); + ZDim = CheckAndConvertArg(ZDim); + if (!XDim || !YDim || !ZDim) + return; + + // If the 'max_work_group_size' attribute is specified on a declaration along + // with 'reqd_work_group_size' attribute, check to see if values of + // 'reqd_work_group_size' attribute arguments are equal to or less than values + // of 'max_work_group_size' attribute arguments. + // + // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments + // are greater than values of 'max_work_group_size' attribute arguments. + if (const auto *DeclAttr = D->getAttr()) { + if (checkMaxAllowedWorkGroupSize(DeclAttr->getXDim(), DeclAttr->getYDim(), + DeclAttr->getZDim(), XDim, YDim, ZDim)) { + Diag(CI.getLoc(), diag::err_conflicting_sycl_function_attributes) + << CI << DeclAttr; + Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); + return; + } + } + + // If the declaration has a SYCLIntelMaxWorkGroupSizeAttr, check to see if + // the attribute holds values equal to (1, 1, 1) in case the value of + // SYCLIntelMaxGlobalWorkDimAttr equals to 0. + if (const auto *DeclAttr = D->getAttr()) { + if (areInvalidWorkGroupSizeAttrs(DeclAttr->getValue(), XDim, YDim, ZDim)) { + Diag(CI.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) + << CI << DeclAttr; + return; + } + } + + // If the attribute was already applied with different arguments, then + // diagnose the second attribute as a duplicate and don't add it. + if (const auto *Existing = D->getAttr()) { + // If any of the results are known to be different, we can diagnose at this + // point and drop the attribute. + if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(Existing->getLoc(), diag::note_previous_attribute); + return; + } + // If all of the results are known to be the same, we can silently drop the + // attribute. Otherwise, we have to add the attribute and resolve its + // differences later. + if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) + return; + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) + SYCLIntelMaxWorkGroupSizeAttr(Context, CI, XDim, YDim, ZDim)); +} + +void SemaSYCL::addSYCLReqdWorkGroupSizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *XDim, Expr *YDim, + Expr *ZDim) { + // Returns nullptr if diagnosing, otherwise returns the original expression + // or the original expression converted to a constant expression. + auto CheckAndConvertArg = [&](Expr *E) -> std::optional { + // Check if the expression is not value dependent. + if (E && !E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = SemaRef.VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return std::nullopt; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return std::nullopt; + } + } + return E; + }; + + // Check all three argument values, and if any are bad, bail out. This will + // convert the given expressions into constant expressions when possible. + std::optional XDimConvert = CheckAndConvertArg(XDim); + std::optional YDimConvert = CheckAndConvertArg(YDim); + std::optional ZDimConvert = CheckAndConvertArg(ZDim); + if (!XDimConvert || !YDimConvert || !ZDimConvert) + return; + XDim = XDimConvert.value(); + YDim = YDimConvert.value(); + ZDim = ZDimConvert.value(); + + // If the declaration has a ReqdWorkGroupSizeAttr, check to see if + // the attribute holds values equal to (1, 1, 1) in case the value of + // SYCLIntelMaxGlobalWorkDimAttr equals to 0. + if (const auto *DeclAttr = D->getAttr()) { + if (areInvalidWorkGroupSizeAttrs(DeclAttr->getValue(), XDim, YDim, ZDim)) { + Diag(CI.getLoc(), diag::err_sycl_x_y_z_arguments_must_be_one) + << CI << DeclAttr; + } + } + + // If the 'max_work_group_size' attribute is specified on a declaration along + // with 'reqd_work_group_size' attribute, check to see if values of + // 'reqd_work_group_size' attribute arguments are equal to or less than values + // of 'max_work_group_size' attribute arguments. + // + // We emit diagnostic if values of 'reqd_work_group_size' attribute arguments + // are greater than values of 'max_work_group_size' attribute arguments. + if (const auto *DeclAttr = D->getAttr()) { + if (checkMaxAllowedWorkGroupSize(XDim, YDim, ZDim, DeclAttr->getXDim(), + DeclAttr->getYDim(), + DeclAttr->getZDim())) { + Diag(CI.getLoc(), diag::err_conflicting_sycl_function_attributes) + << CI << DeclAttr; + Diag(DeclAttr->getLoc(), diag::note_conflicting_attribute); + return; + } + } + + // If the 'reqd_work_group_size' attribute is specified on a declaration + // along with 'num_simd_work_items' attribute, the required work group size + // specified by 'num_simd_work_items' attribute must evenly divide the index + // that increments fastest in the 'reqd_work_group_size' attribute. + if (const auto *DeclAttr = D->getAttr()) { + if (checkWorkGroupSize(DeclAttr->getValue(), XDim, YDim, ZDim)) { + Diag(DeclAttr->getLoc(), diag::err_sycl_num_kernel_wrong_reqd_wg_size) + << DeclAttr << CI; + Diag(CI.getLoc(), diag::note_conflicting_attribute); + return; + } + } + + // If the attribute was already applied with different arguments, then + // diagnose the second attribute as a duplicate and don't add it. + if (const auto *Existing = D->getAttr()) { + // If any of the results are known to be different, we can diagnose at this + // point and drop the attribute. + if (anyWorkGroupSizesDiffer(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) { + Diag(CI.getLoc(), diag::err_duplicate_attribute) << CI; + Diag(Existing->getLoc(), diag::note_previous_attribute); + return; + } + + // If all of the results are known to be the same, we can silently drop the + // attribute. Otherwise, we have to add the attribute and resolve its + // differences later. + if (allWorkGroupSizesSame(XDim, YDim, ZDim, Existing->getXDim(), + Existing->getYDim(), Existing->getZDim())) + return; + } + + ASTContext &Context = getASTContext(); + D->addAttr(::new (Context) + SYCLReqdWorkGroupSizeAttr(Context, CI, XDim, YDim, ZDim)); +} From 89b3181402560a89e7e6a53acc042042f9ae6b87 Mon Sep 17 00:00:00 2001 From: Fraser Cormack Date: Wed, 14 Aug 2024 15:57:55 +0100 Subject: [PATCH 4/5] rename file --- clang/lib/Sema/CMakeLists.txt | 2 +- clang/lib/Sema/{SemaSYCLAttrDecl.cpp => SemaSYCLDeclAttr.cpp} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename clang/lib/Sema/{SemaSYCLAttrDecl.cpp => SemaSYCLDeclAttr.cpp} (99%) diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index f602011460c21..e6a2d132f9618 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -87,7 +87,7 @@ add_clang_library(clangSema SemaStmtAsm.cpp SemaStmtAttr.cpp SemaSYCL.cpp - SemaSYCLAttrDecl.cpp + SemaSYCLDeclAttr.cpp SemaSwift.cpp SemaSystemZ.cpp SemaTemplate.cpp diff --git a/clang/lib/Sema/SemaSYCLAttrDecl.cpp b/clang/lib/Sema/SemaSYCLDeclAttr.cpp similarity index 99% rename from clang/lib/Sema/SemaSYCLAttrDecl.cpp rename to clang/lib/Sema/SemaSYCLDeclAttr.cpp index 5d86be8d6393c..5069fe2e0ff70 100644 --- a/clang/lib/Sema/SemaSYCLAttrDecl.cpp +++ b/clang/lib/Sema/SemaSYCLDeclAttr.cpp @@ -1,4 +1,4 @@ -//===- SemaSYCLAttrDecl.cpp - Semantic Analysis for SYCL attributes -------===// +//===- SemaSYCLDeclAttr.cpp - Semantic Analysis for SYCL attributes -------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. From 814170216d9b6dd21efe2fcb6fef9d9041fcc682 Mon Sep 17 00:00:00 2001 From: Fraser Cormack Date: Thu, 15 Aug 2024 10:35:19 +0100 Subject: [PATCH 5/5] fix comparison --- clang/lib/Sema/SemaSYCLDeclAttr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaSYCLDeclAttr.cpp b/clang/lib/Sema/SemaSYCLDeclAttr.cpp index eac5780bd90f4..d8b616ea7b48d 100644 --- a/clang/lib/Sema/SemaSYCLDeclAttr.cpp +++ b/clang/lib/Sema/SemaSYCLDeclAttr.cpp @@ -132,7 +132,7 @@ void SemaSYCL::checkDeprecatedSYCLAttributeSpelling(const ParsedAttr &A, } // Diagnose SYCL 2020 spellings in later SYCL modes. - if (getLangOpts().getSYCLVersion() > LangOptions::SYCL_2020) { + if (getLangOpts().getSYCLVersion() >= LangOptions::SYCL_2020) { // All attributes in the cl vendor namespace are deprecated in favor of a // name in the sycl namespace as of SYCL 2020. if (A.hasScope() && A.getScopeName()->isStr("cl")) {