From eba69b59d1a30dead07da2c279c8ecfd2b62ba9f Mon Sep 17 00:00:00 2001 From: Erich Keane Date: Fri, 23 Apr 2021 08:22:35 -0700 Subject: [PATCH] Reimplement __builtin_unique_stable_name- The original version of this was reverted, and @rjmcall provided some advice to architect a new solution. This is that solution. This implements a builtin to provide a unique name that is stable across compilations of this TU for the purposes of implementing the library component of the unnamed kernel feature of SYCL. It does this by running the Itanium mangler with a few modifications. Because it is somewhat common to wrap non-kernel-related lambdas in macros that aren't present on the device (such as for logging), this uniquely generates an ID for all lambdas involved in the naming of a kernel. It uses the lambda-mangling number to do this, except replaces this with its own number (starting at 10000 for readabililty reasons) for lambdas used to name a kernel. Additionally, this implements itself as constexpr with a slight catch: if a name would be invalidated by the use of this lambda in a later kernel invocation, it is diagnosed as an error (see the Sema tests). Differential Revision: https://reviews.llvm.org/D103112 --- clang/docs/LanguageExtensions.rst | 38 +++- clang/include/clang/AST/ASTContext.h | 23 ++ clang/include/clang/AST/ComputeDependence.h | 2 + clang/include/clang/AST/Expr.h | 58 +++++ clang/include/clang/AST/JSONNodeDumper.h | 1 + clang/include/clang/AST/Mangle.h | 11 +- clang/include/clang/AST/RecursiveASTVisitor.h | 3 + clang/include/clang/AST/TextNodeDumper.h | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 5 + clang/include/clang/Basic/LangOptions.h | 2 + clang/include/clang/Basic/StmtNodes.td | 1 + clang/include/clang/Basic/TokenKinds.def | 11 +- clang/include/clang/Parse/Parser.h | 1 + clang/include/clang/Sema/Sema.h | 13 ++ .../include/clang/Serialization/ASTBitCodes.h | 3 + clang/lib/AST/ASTContext.cpp | 115 +++++++++- clang/lib/AST/ComputeDependence.cpp | 4 + clang/lib/AST/Expr.cpp | 65 ++++++ clang/lib/AST/ExprClassification.cpp | 3 + clang/lib/AST/ExprConstant.cpp | 24 +- clang/lib/AST/ItaniumMangle.cpp | 55 +++-- clang/lib/AST/JSONNodeDumper.cpp | 6 + clang/lib/AST/StmtPrinter.cpp | 7 + clang/lib/AST/StmtProfile.cpp | 6 + clang/lib/AST/TextNodeDumper.cpp | 5 + clang/lib/Basic/IdentifierTable.cpp | 5 +- clang/lib/CodeGen/CGCUDANV.cpp | 27 ++- clang/lib/CodeGen/CGExprScalar.cpp | 21 ++ clang/lib/Parse/ParseExpr.cpp | 30 +++ clang/lib/Sema/SemaExceptionSpec.cpp | 2 + clang/lib/Sema/SemaExpr.cpp | 22 ++ clang/lib/Sema/SemaLambda.cpp | 6 +- clang/lib/Sema/SemaSYCL.cpp | 33 +++ .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 34 +++ clang/lib/Sema/TreeTransform.h | 31 +++ clang/lib/Serialization/ASTReaderStmt.cpp | 14 ++ clang/lib/Serialization/ASTWriterStmt.cpp | 11 + clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + .../AST/ast-print-sycl-unique-stable-name.cpp | 28 +++ clang/test/CodeGenSYCL/unique_stable_name.cpp | 159 +++++++++++++ clang/test/ParserSYCL/unique_stable_name.cpp | 43 ++++ .../unique_stable_name_sycl_only.cpp | 9 + clang/test/SemaSYCL/unique_stable_name.cpp | 215 ++++++++++++++++++ clang/tools/libclang/CXCursor.cpp | 1 + 44 files changed, 1117 insertions(+), 38 deletions(-) create mode 100644 clang/test/AST/ast-print-sycl-unique-stable-name.cpp create mode 100644 clang/test/CodeGenSYCL/unique_stable_name.cpp create mode 100644 clang/test/ParserSYCL/unique_stable_name.cpp create mode 100644 clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp create mode 100644 clang/test/SemaSYCL/unique_stable_name.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index eb9fe0c0ac0d..129e73345b2a 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1794,7 +1794,7 @@ correctly in any circumstances. It can be used if: metaprogramming algorithms to be able to specify/detect types generically. - the generated kernel binary does not contain indirect calls because they - are eliminated using compiler optimizations e.g. devirtualization. + are eliminated using compiler optimizations e.g. devirtualization. - the selected target supports the function pointer like functionality e.g. most CPU targets. @@ -2404,6 +2404,40 @@ argument. int *pb =__builtin_preserve_access_index(&v->c[3].b); __builtin_preserve_access_index(v->j); +``__builtin_sycl_unique_stable_name`` +------------------------------------- + +``__builtin_sycl_unique_stable_name()`` is a builtin that takes a type and +produces a string literal containing a unique name for the type that is stable +across split compilations, mainly to support SYCL/Data Parallel C++ language. + +In cases where the split compilation needs to share a unique token for a type +across the boundary (such as in an offloading situation), this name can be used +for lookup purposes, such as in the SYCL Integration Header. + +The value of this builtin is computed entirely at compile time, so it can be +used in constant expressions. This value encodes lambda functions based on a +stable numbering order in which they appear in their local declaration contexts. +Once this builtin is evaluated in a constexpr context, it is erroneous to use +it in an instantiation which changes its value. + +In order to produce the unique name, the current implementation of the bultin +uses Itanium mangling even if the host compilation uses a different name +mangling scheme at runtime. The mangler marks all the lambdas required to name +the SYCL kernel and emits a stable local ordering of the respective lambdas, +starting from ``10000``. The initial value of ``10000`` serves as an obvious +differentiator from ordinary lambda mangling numbers but does not serve any +other purpose and may change in the future. The resulting pattern is +demanglable. When non-lambda types are passed to the builtin, the mangler emits +their usual pattern without any special treatment. + +**Syntax**: + +.. code-block:: c + + // Computes a unique stable name for the given type. + constexpr const char * __builtin_sycl_unique_stable_name( type-id ); + Multiprecision Arithmetic Builtins ---------------------------------- @@ -2598,7 +2632,7 @@ Guaranteed inlined copy ``__builtin_memcpy_inline`` has been designed as a building block for efficient ``memcpy`` implementations. It is identical to ``__builtin_memcpy`` but also guarantees not to call any external functions. See LLVM IR `llvm.memcpy.inline -`_ intrinsic +`_ intrinsic for more information. This is useful to implement a custom version of ``memcpy``, implement a diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index be6170564fcd..db6d263a5e15 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2355,6 +2355,12 @@ public: /// If \p T is null pointer, assume the target in ASTContext. MangleContext *createMangleContext(const TargetInfo *T = nullptr); + /// Creates a device mangle context to correctly mangle lambdas in a mixed + /// architecture compile by setting the lambda mangling number source to the + /// DeviceLambdaManglingNumber. Currently this asserts that the TargetInfo + /// (from the AuxTargetInfo) is a an itanium target. + MangleContext *createDeviceMangleContext(const TargetInfo &T); + void DeepCollectObjCIvars(const ObjCInterfaceDecl *OI, bool leafClass, SmallVectorImpl &Ivars) const; @@ -3158,10 +3164,27 @@ public: StringRef getCUIDHash() const; + void AddSYCLKernelNamingDecl(const CXXRecordDecl *RD); + bool IsSYCLKernelNamingDecl(const NamedDecl *RD) const; + unsigned GetSYCLKernelNamingIndex(const NamedDecl *RD) const; + /// A SourceLocation to store whether we have evaluated a kernel name already, + /// and where it happened. If so, we need to diagnose an illegal use of the + /// builtin. + llvm::MapVector + SYCLUniqueStableNameEvaluatedValues; + private: /// All OMPTraitInfo objects live in this collection, one per /// `pragma omp [begin] declare variant` directive. SmallVector, 4> OMPTraitInfoVector; + + /// A list of the (right now just lambda decls) declarations required to + /// name all the SYCL kernels in the translation unit, so that we can get the + /// correct kernel name, as well as implement + /// __builtin_sycl_unique_stable_name. + llvm::DenseMap> + SYCLKernelNamingTypes; }; /// Insertion operator for diagnostics. diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 04e8e2c7d2cc..7dde42ee71ba 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -78,6 +78,7 @@ class MaterializeTemporaryExpr; class CXXFoldExpr; class TypeTraitExpr; class ConceptSpecializationExpr; +class SYCLUniqueStableNameExpr; class PredefinedExpr; class CallExpr; class OffsetOfExpr; @@ -165,6 +166,7 @@ ExprDependence computeDependence(TypeTraitExpr *E); ExprDependence computeDependence(ConceptSpecializationExpr *E, bool ValueDependent); +ExprDependence computeDependence(SYCLUniqueStableNameExpr *E); ExprDependence computeDependence(PredefinedExpr *E); ExprDependence computeDependence(CallExpr *E, llvm::ArrayRef PreArgs); ExprDependence computeDependence(OffsetOfExpr *E); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 589199dbcdca..55ee96feae98 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2039,6 +2039,64 @@ public: } }; +// This represents a use of the __builtin_sycl_unique_stable_name, which takes a +// type-id, and at CodeGen time emits a unique string representation of the +// type in a way that permits us to properly encode information about the SYCL +// kernels. +class SYCLUniqueStableNameExpr final : public Expr { + friend class ASTStmtReader; + SourceLocation OpLoc, LParen, RParen; + TypeSourceInfo *TypeInfo; + + SYCLUniqueStableNameExpr(EmptyShell Empty, QualType ResultTy); + SYCLUniqueStableNameExpr(SourceLocation OpLoc, SourceLocation LParen, + SourceLocation RParen, QualType ResultTy, + TypeSourceInfo *TSI); + + void setTypeSourceInfo(TypeSourceInfo *Ty) { TypeInfo = Ty; } + + void setLocation(SourceLocation L) { OpLoc = L; } + void setLParenLocation(SourceLocation L) { LParen = L; } + void setRParenLocation(SourceLocation L) { RParen = L; } + +public: + TypeSourceInfo *getTypeSourceInfo() { return TypeInfo; } + + const TypeSourceInfo *getTypeSourceInfo() const { return TypeInfo; } + + static SYCLUniqueStableNameExpr * + Create(const ASTContext &Ctx, SourceLocation OpLoc, SourceLocation LParen, + SourceLocation RParen, TypeSourceInfo *TSI); + + static SYCLUniqueStableNameExpr *CreateEmpty(const ASTContext &Ctx); + + SourceLocation getBeginLoc() const { return getLocation(); } + SourceLocation getEndLoc() const { return RParen; } + SourceLocation getLocation() const { return OpLoc; } + SourceLocation getLParenLocation() const { return LParen; } + SourceLocation getRParenLocation() const { return RParen; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == SYCLUniqueStableNameExprClass; + } + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + // Convenience function to generate the name of the currently stored type. + std::string ComputeName(ASTContext &Context) const; + + // Get the generated name of the type. Note that this only works after all + // kernels have been instantiated. + static std::string ComputeName(ASTContext &Context, QualType Ty); +}; + /// ParenExpr - This represents a parethesized expression, e.g. "(1)". This /// AST node is only formed if full location information is requested. class ParenExpr : public Expr { diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index 4e7162992418..a8b731a7cd31 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -263,6 +263,7 @@ public: void VisitBlockDecl(const BlockDecl *D); void VisitDeclRefExpr(const DeclRefExpr *DRE); + void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E); void VisitPredefinedExpr(const PredefinedExpr *PE); void VisitUnaryOperator(const UnaryOperator *UO); void VisitBinaryOperator(const BinaryOperator *BO); diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index dc620ec10ff9..7d02f08e0120 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -107,9 +107,6 @@ public: virtual bool shouldMangleCXXName(const NamedDecl *D) = 0; virtual bool shouldMangleStringLiteral(const StringLiteral *SL) = 0; - virtual bool isDeviceMangleContext() const { return false; } - virtual void setDeviceMangleContext(bool) {} - virtual bool isUniqueInternalLinkageDecl(const NamedDecl *ND) { return false; } @@ -173,6 +170,8 @@ public: class ItaniumMangleContext : public MangleContext { public: + using DiscriminatorOverrideTy = + llvm::Optional (*)(ASTContext &, const NamedDecl *); explicit ItaniumMangleContext(ASTContext &C, DiagnosticsEngine &D) : MangleContext(C, D, MK_Itanium) {} @@ -195,12 +194,18 @@ public: virtual void mangleDynamicStermFinalizer(const VarDecl *D, raw_ostream &) = 0; + // This has to live here, otherwise the CXXNameMangler won't have access to + // it. + virtual DiscriminatorOverrideTy getDiscriminatorOverride() const = 0; static bool classof(const MangleContext *C) { return C->getKind() == MK_Itanium; } static ItaniumMangleContext *create(ASTContext &Context, DiagnosticsEngine &Diags); + static ItaniumMangleContext *create(ASTContext &Context, + DiagnosticsEngine &Diags, + DiscriminatorOverrideTy Discriminator); }; class MicrosoftMangleContext : public MangleContext { diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 4770a2fd42a2..b3ea6f7817ef 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2651,6 +2651,9 @@ DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, { DEF_TRAVERSE_STMT(ObjCAvailabilityCheckExpr, {}) DEF_TRAVERSE_STMT(ParenExpr, {}) DEF_TRAVERSE_STMT(ParenListExpr, {}) +DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, { + TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc())); +}) DEF_TRAVERSE_STMT(PredefinedExpr, {}) DEF_TRAVERSE_STMT(ShuffleVectorExpr, {}) DEF_TRAVERSE_STMT(ConvertVectorExpr, {}) diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 0955c494a74a..e3aa94d5a1e8 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -249,6 +249,7 @@ public: void VisitCastExpr(const CastExpr *Node); void VisitImplicitCastExpr(const ImplicitCastExpr *Node); void VisitDeclRefExpr(const DeclRefExpr *Node); + void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *Node); void VisitPredefinedExpr(const PredefinedExpr *Node); void VisitCharacterLiteral(const CharacterLiteral *Node); void VisitIntegerLiteral(const IntegerLiteral *Node); diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5d0f5dcbfc35..4b653b8b4737 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6352,6 +6352,11 @@ def warn_pointer_arith_null_ptr : Warning< def warn_gnu_null_ptr_arith : Warning< "arithmetic on a null pointer treated as a cast from integer to pointer is a GNU extension">, InGroup, DefaultIgnore; +def err_kernel_invalidates_sycl_unique_stable_name + : Error<"kernel instantiation changes the result of an evaluated " + "'__builtin_sycl_unique_stable_name'">; +def note_sycl_unique_stable_name_evaluated_here + : Note<"'__builtin_sycl_unique_stable_name' evaluated here">; def warn_floatingpoint_eq : Warning< "comparing floating point with == or != is unsafe">, diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 7fd4bf0eb206..f5975d89a299 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -444,6 +444,8 @@ public: bool hasWasmExceptions() const { return getExceptionHandling() == ExceptionHandlingKind::Wasm; } + + bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; } }; /// Floating point control options diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index ecaf7b027e77..6134e60f3541 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -57,6 +57,7 @@ def CoreturnStmt : StmtNode; // Expressions def Expr : StmtNode; def PredefinedExpr : StmtNode; +def SYCLUniqueStableNameExpr : StmtNode; def DeclRefExpr : StmtNode; def IntegerLiteral : StmtNode; def FixedPointLiteral : StmtNode; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index be258c95d2fa..fd9909628b3b 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -696,11 +696,12 @@ ALIAS("_declspec" , __declspec , KEYMS) ALIAS("_pascal" , __pascal , KEYBORLAND) // Clang Extensions. -KEYWORD(__builtin_convertvector , KEYALL) -ALIAS("__char16_t" , char16_t , KEYCXX) -ALIAS("__char32_t" , char32_t , KEYCXX) -KEYWORD(__builtin_bit_cast , KEYALL) -KEYWORD(__builtin_available , KEYALL) +KEYWORD(__builtin_convertvector , KEYALL) +ALIAS("__char16_t" , char16_t , KEYCXX) +ALIAS("__char32_t" , char32_t , KEYCXX) +KEYWORD(__builtin_bit_cast , KEYALL) +KEYWORD(__builtin_available , KEYALL) +KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL) // Clang-specific keywords enabled only in testing. TESTING_KEYWORD(__unknown_anytype , KEYALL) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 213f7fb3dc56..e3e6509b025e 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1800,6 +1800,7 @@ private: ExprResult ParsePostfixExpressionSuffix(ExprResult LHS); ExprResult ParseUnaryExprOrTypeTraitExpression(); ExprResult ParseBuiltinPrimaryExpression(); + ExprResult ParseSYCLUniqueStableNameExpression(); ExprResult ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, bool &isCastExpr, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 706293fa929c..07518b37eb98 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -911,6 +911,10 @@ public: OpaqueParser = P; } + // Does the work necessary to deal with a SYCL kernel lambda. At the moment, + // this just marks the list of lambdas required to name the kernel. + void AddSYCLKernelLambda(const FunctionDecl *FD); + class DelayedDiagnostics; class DelayedDiagnosticsState { @@ -5190,6 +5194,15 @@ public: ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind); ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val); + ExprResult BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, + TypeSourceInfo *TSI); + ExprResult ActOnSYCLUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, + ParsedType ParsedTy); + bool CheckLoopHintExpr(Expr *E, SourceLocation Loc); ExprResult ActOnNumericConstant(const Token &Tok, Scope *UDLScope = nullptr); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 5e5a73e73092..83a6f6a2c440 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1960,6 +1960,9 @@ enum StmtCode { // FixedPointLiteral EXPR_FIXEDPOINT_LITERAL, + + // SYCLUniqueStableNameExpr + EXPR_SYCL_UNIQUE_STABLE_NAME, }; /// The kinds of designators that can occur in a diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 47d64a5fa48e..75656496a946 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2458,7 +2458,7 @@ unsigned ASTContext::getPreferredTypeAlign(const Type *T) const { // The preferred alignment of member pointers is that of a pointer. if (T->isMemberPointerType()) return getPreferredTypeAlign(getPointerDiffType().getTypePtr()); - + if (!Target->allowsLargerPreferedTypeAlignment()) return ABIAlign; @@ -11075,6 +11075,33 @@ MangleContext *ASTContext::createMangleContext(const TargetInfo *T) { llvm_unreachable("Unsupported ABI"); } +MangleContext *ASTContext::createDeviceMangleContext(const TargetInfo &T) { + assert(T.getCXXABI().getKind() != TargetCXXABI::Microsoft && + "Device mangle context does not support Microsoft mangling."); + switch (T.getCXXABI().getKind()) { + case TargetCXXABI::AppleARM64: + case TargetCXXABI::Fuchsia: + case TargetCXXABI::GenericAArch64: + case TargetCXXABI::GenericItanium: + case TargetCXXABI::GenericARM: + case TargetCXXABI::GenericMIPS: + case TargetCXXABI::iOS: + case TargetCXXABI::WebAssembly: + case TargetCXXABI::WatchOS: + case TargetCXXABI::XL: + return ItaniumMangleContext::create( + *this, getDiagnostics(), + [](ASTContext &, const NamedDecl *ND) -> llvm::Optional { + if (const auto *RD = dyn_cast(ND)) + return RD->getDeviceLambdaManglingNumber(); + return llvm::None; + }); + case TargetCXXABI::Microsoft: + return MicrosoftMangleContext::create(*this, getDiagnostics()); + } + llvm_unreachable("Unsupported ABI"); +} + CXXABI::~CXXABI() = default; size_t ASTContext::getSideTableAllocatedMemory() const { @@ -11648,3 +11675,89 @@ StringRef ASTContext::getCUIDHash() const { CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true); return CUIDHash; } + +// Get the closest named parent, so we can order the sycl naming decls somewhere +// that mangling is meaningful. +static const DeclContext *GetNamedParent(const CXXRecordDecl *RD) { + const DeclContext *DC = RD->getDeclContext(); + + while (!isa(DC)) + DC = DC->getParent(); + return DC; +} + +void ASTContext::AddSYCLKernelNamingDecl(const CXXRecordDecl *RD) { + assert(getLangOpts().isSYCL() && "Only valid for SYCL programs"); + RD = RD->getCanonicalDecl(); + const DeclContext *DC = GetNamedParent(RD); + + assert(RD->getLocation().isValid() && + "Invalid location on kernel naming decl"); + + (void)SYCLKernelNamingTypes[DC].insert(RD); +} + +bool ASTContext::IsSYCLKernelNamingDecl(const NamedDecl *ND) const { + assert(getLangOpts().isSYCL() && "Only valid for SYCL programs"); + const auto *RD = dyn_cast(ND); + if (!RD) + return false; + RD = RD->getCanonicalDecl(); + const DeclContext *DC = GetNamedParent(RD); + + auto Itr = SYCLKernelNamingTypes.find(DC); + + if (Itr == SYCLKernelNamingTypes.end()) + return false; + + return Itr->getSecond().count(RD); +} + +// Filters the Decls list to those that share the lambda mangling with the +// passed RD. +static void FilterSYCLKernelNamingDecls( + ASTContext &Ctx, const CXXRecordDecl *RD, + llvm::SmallVectorImpl &Decls) { + static std::unique_ptr Mangler{ + ItaniumMangleContext::create(Ctx, Ctx.getDiagnostics())}; + + llvm::SmallString<128> LambdaSig; + llvm::raw_svector_ostream Out(LambdaSig); + Mangler->mangleLambdaSig(RD, Out); + + llvm::erase_if(Decls, [&LambdaSig](const CXXRecordDecl *LocalRD) { + llvm::SmallString<128> LocalLambdaSig; + llvm::raw_svector_ostream LocalOut(LocalLambdaSig); + Mangler->mangleLambdaSig(LocalRD, LocalOut); + return LambdaSig != LocalLambdaSig; + }); +} + +unsigned ASTContext::GetSYCLKernelNamingIndex(const NamedDecl *ND) const { + assert(getLangOpts().isSYCL() && "Only valid for SYCL programs"); + assert(IsSYCLKernelNamingDecl(ND) && + "Lambda not involved in mangling asked for a naming index?"); + + const CXXRecordDecl *RD = cast(ND)->getCanonicalDecl(); + const DeclContext *DC = GetNamedParent(RD); + + auto Itr = SYCLKernelNamingTypes.find(DC); + assert(Itr != SYCLKernelNamingTypes.end() && "Not a valid DeclContext?"); + + const llvm::SmallPtrSet &Set = Itr->getSecond(); + + llvm::SmallVector Decls{Set.begin(), Set.end()}; + + // If we are in an itanium situation, the mangling-numbers for a lambda depend + // on the mangled signature, so sort by that. Only TargetCXXABI::Microsoft + // doesn't use the itanium mangler, and just sets the lambda mangling number + // incrementally, with no consideration to the signature. + if (Target->getCXXABI().getKind() != TargetCXXABI::Microsoft) + FilterSYCLKernelNamingDecls(const_cast(*this), RD, Decls); + + llvm::sort(Decls, [](const CXXRecordDecl *LHS, const CXXRecordDecl *RHS) { + return LHS->getLambdaManglingNumber() < RHS->getLambdaManglingNumber(); + }); + + return llvm::find(Decls, RD) - Decls.begin(); +} diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 4026fdc76fd6..1a5d2f7075fb 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -556,6 +556,10 @@ ExprDependence clang::computeDependence(RecoveryExpr *E) { return D; } +ExprDependence clang::computeDependence(SYCLUniqueStableNameExpr *E) { + return toExprDependence(E->getTypeSourceInfo()->getType()->getDependence()); +} + ExprDependence clang::computeDependence(PredefinedExpr *E) { return toExprDependence(E->getType()->getDependence()) & ~ExprDependence::UnexpandedPack; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index fe2cdc91fb27..49e1305beea9 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -504,6 +504,70 @@ SourceLocation DeclRefExpr::getEndLoc() const { return getNameInfo().getEndLoc(); } +SYCLUniqueStableNameExpr::SYCLUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, + QualType ResultTy, + TypeSourceInfo *TSI) + : Expr(SYCLUniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary), + OpLoc(OpLoc), LParen(LParen), RParen(RParen) { + setTypeSourceInfo(TSI); + setDependence(computeDependence(this)); +} + +SYCLUniqueStableNameExpr::SYCLUniqueStableNameExpr(EmptyShell Empty, + QualType ResultTy) + : Expr(SYCLUniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary) {} + +SYCLUniqueStableNameExpr * +SYCLUniqueStableNameExpr::Create(const ASTContext &Ctx, SourceLocation OpLoc, + SourceLocation LParen, SourceLocation RParen, + TypeSourceInfo *TSI) { + QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst()); + return new (Ctx) + SYCLUniqueStableNameExpr(OpLoc, LParen, RParen, ResultTy, TSI); +} + +SYCLUniqueStableNameExpr * +SYCLUniqueStableNameExpr::CreateEmpty(const ASTContext &Ctx) { + QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst()); + return new (Ctx) SYCLUniqueStableNameExpr(EmptyShell(), ResultTy); +} + +std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context) const { + return SYCLUniqueStableNameExpr::ComputeName(Context, + getTypeSourceInfo()->getType()); +} + +std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context, + QualType Ty) { + auto MangleCallback = [](ASTContext &Ctx, + const NamedDecl *ND) -> llvm::Optional { + // This replaces the 'lambda number' in the mangling with a unique number + // based on its order in the declaration. To provide some level of visual + // notability (actual uniqueness from normal lambdas isn't necessary, as + // these are used differently), we add 10,000 to the number. + // For example: + // _ZTSZ3foovEUlvE10005_ + // Demangles to: typeinfo name for foo()::'lambda10005'() + // Note that the mangler subtracts 2, since with normal lambdas the lambda + // mangling number '0' is an anonymous struct mangle, and '1' is omitted. + // So 10,002 results in the first number being 10,000. + if (Ctx.IsSYCLKernelNamingDecl(ND)) + return 10'002 + Ctx.GetSYCLKernelNamingIndex(ND); + return llvm::None; + }; + std::unique_ptr Ctx{ItaniumMangleContext::create( + Context, Context.getDiagnostics(), MangleCallback)}; + + std::string Buffer; + Buffer.reserve(128); + llvm::raw_string_ostream Out(Buffer); + Ctx->mangleTypeName(Ty, Out); + + return Out.str(); +} + PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK, StringLiteral *SL) : Expr(PredefinedExprClass, FNTy, VK_LValue, OK_Ordinary) { @@ -3381,6 +3445,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case SourceLocExprClass: case ConceptSpecializationExprClass: case RequiresExprClass: + case SYCLUniqueStableNameExprClass: // These never have a side-effect. return false; diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 0286c730ce4e..dd7c4a4cbe00 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -433,6 +433,9 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CoawaitExprClass: case Expr::CoyieldExprClass: return ClassifyInternal(Ctx, cast(E)->getResumeExpr()); + case Expr::SYCLUniqueStableNameExprClass: + return Cl::CL_PRValue; + break; } llvm_unreachable("unhandled expression kind in classification"); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 17bfed5d435f..17598cdcb8b5 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8669,6 +8669,26 @@ public: return true; } + bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E) { + std::string ResultStr = E->ComputeName(Info.Ctx); + + Info.Ctx.SYCLUniqueStableNameEvaluatedValues[E] = ResultStr; + + QualType CharTy = Info.Ctx.CharTy.withConst(); + APInt Size(Info.Ctx.getTypeSize(Info.Ctx.getSizeType()), + ResultStr.size() + 1); + QualType ArrayTy = Info.Ctx.getConstantArrayType(CharTy, Size, nullptr, + ArrayType::Normal, 0); + + StringLiteral *SL = + StringLiteral::Create(Info.Ctx, ResultStr, StringLiteral::Ascii, + /*Pascal*/ false, ArrayTy, E->getLocation()); + + evaluateLValue(SL, Result); + Result.addArray(Info, E, cast(ArrayTy)); + return true; + } + // FIXME: Missing: @protocol, @selector }; } // end anonymous namespace @@ -10367,7 +10387,8 @@ namespace { Result = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue()); - if (!Result.hasArrayFiller()) return true; + if (!Result.hasArrayFiller()) + return true; // Zero-initialize all elements. LValue Subobject = This; @@ -15154,6 +15175,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CoawaitExprClass: case Expr::DependentCoawaitExprClass: case Expr::CoyieldExprClass: + case Expr::SYCLUniqueStableNameExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::InitListExprClass: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 2a3a94f58a55..94e404a5f38a 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -125,14 +125,16 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext { typedef std::pair DiscriminatorKeyTy; llvm::DenseMap Discriminator; llvm::DenseMap Uniquifier; + const DiscriminatorOverrideTy DiscriminatorOverride = nullptr; - bool IsDevCtx = false; bool NeedsUniqueInternalLinkageNames = false; public: - explicit ItaniumMangleContextImpl(ASTContext &Context, - DiagnosticsEngine &Diags) - : ItaniumMangleContext(Context, Diags) {} + explicit ItaniumMangleContextImpl( + ASTContext &Context, DiagnosticsEngine &Diags, + DiscriminatorOverrideTy DiscriminatorOverride) + : ItaniumMangleContext(Context, Diags), + DiscriminatorOverride(DiscriminatorOverride) {} /// @name Mangler Entry Points /// @{ @@ -147,9 +149,6 @@ public: NeedsUniqueInternalLinkageNames = true; } - bool isDeviceMangleContext() const override { return IsDevCtx; } - void setDeviceMangleContext(bool IsDev) override { IsDevCtx = IsDev; } - void mangleCXXName(GlobalDecl GD, raw_ostream &) override; void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, raw_ostream &) override; @@ -246,6 +245,10 @@ public: return Name; } + DiscriminatorOverrideTy getDiscriminatorOverride() const override { + return DiscriminatorOverride; + } + /// @} }; @@ -1515,7 +1518,9 @@ void CXXNameMangler::mangleUnqualifiedName(GlobalDecl GD, // ::= * + // # Parameter types or 'v' for 'void'. if (const CXXRecordDecl *Record = dyn_cast(TD)) { - if (Record->isLambda() && Record->getLambdaManglingNumber()) { + if (Record->isLambda() && (Record->getLambdaManglingNumber() || + Context.getDiscriminatorOverride()( + Context.getASTContext(), Record))) { assert(!AdditionalAbiTags && "Lambda type cannot have additional abi tags"); mangleLambda(Record); @@ -1953,9 +1958,11 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { // if the host-side CXX ABI has different numbering for lambda. In such case, // if the mangle context is that device-side one, use the device-side lambda // mangling number for this lambda. - unsigned Number = Context.isDeviceMangleContext() - ? Lambda->getDeviceLambdaManglingNumber() - : Lambda->getLambdaManglingNumber(); + llvm::Optional DeviceNumber = + Context.getDiscriminatorOverride()(Context.getASTContext(), Lambda); + unsigned Number = DeviceNumber.hasValue() ? *DeviceNumber + : Lambda->getLambdaManglingNumber(); + assert(Number > 0 && "Lambda should be mangled as an unnamed class"); if (Number > 1) mangleNumber(Number - 2); @@ -5026,6 +5033,16 @@ recurse: Out << "v18co_yield"; mangleExpression(cast(E)->getOperand()); break; + case Expr::SYCLUniqueStableNameExprClass: { + const auto *USN = cast(E); + NotPrimaryExpr(); + + Out << "u33__builtin_sycl_unique_stable_name"; + mangleType(USN->getTypeSourceInfo()->getType()); + + Out << "E"; + break; + } } if (AsTemplateArg && !IsPrimaryExpr) @@ -6378,7 +6395,17 @@ void ItaniumMangleContextImpl::mangleLambdaSig(const CXXRecordDecl *Lambda, Mangler.mangleLambdaSig(Lambda); } -ItaniumMangleContext * -ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) { - return new ItaniumMangleContextImpl(Context, Diags); +ItaniumMangleContext *ItaniumMangleContext::create(ASTContext &Context, + DiagnosticsEngine &Diags) { + return new ItaniumMangleContextImpl( + Context, Diags, + [](ASTContext &, const NamedDecl *) -> llvm::Optional { + return llvm::None; + }); +} + +ItaniumMangleContext * +ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags, + DiscriminatorOverrideTy DiscriminatorOverride) { + return new ItaniumMangleContextImpl(Context, Diags, DiscriminatorOverride); } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 95e60f8b19a6..5d5388880e67 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1164,6 +1164,12 @@ void JSONNodeDumper::VisitDeclRefExpr(const DeclRefExpr *DRE) { } } +void JSONNodeDumper::VisitSYCLUniqueStableNameExpr( + const SYCLUniqueStableNameExpr *E) { + JOS.attribute("typeSourceInfo", + createQualType(E->getTypeSourceInfo()->getType())); +} + void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) { JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind())); } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 58afc020f457..3e275744931a 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1081,6 +1081,13 @@ void StmtPrinter::VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *Node) { OS << "]"; } +void StmtPrinter::VisitSYCLUniqueStableNameExpr( + SYCLUniqueStableNameExpr *Node) { + OS << "__builtin_sycl_unique_stable_name("; + Node->getTypeSourceInfo()->getType().print(OS, Policy); + OS << ")"; +} + void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) { OS << PredefinedExpr::getIdentKindName(Node->getIdentKind()); } diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index fc1f8a532724..dc91043b44a6 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1190,6 +1190,12 @@ void StmtProfiler::VisitDeclRefExpr(const DeclRefExpr *S) { } } +void StmtProfiler::VisitSYCLUniqueStableNameExpr( + const SYCLUniqueStableNameExpr *S) { + VisitExpr(S); + VisitType(S->getTypeSourceInfo()->getType()); +} + void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) { VisitExpr(S); ID.AddInteger(S->getIdentKind()); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index ce5089ff5ece..cb5b4230a5ab 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1018,6 +1018,11 @@ void TextNodeDumper::VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node) { OS << " isFreeIvar"; } +void TextNodeDumper::VisitSYCLUniqueStableNameExpr( + const SYCLUniqueStableNameExpr *Node) { + dumpType(Node->getTypeSourceInfo()->getType()); +} + void TextNodeDumper::VisitPredefinedExpr(const PredefinedExpr *Node) { OS << " " << PredefinedExpr::getIdentKindName(Node->getIdentKind()); } diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index 3c18219c91c9..dafacaf88502 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -107,8 +107,9 @@ namespace { KEYCXX20 = 0x200000, KEYOPENCLCXX = 0x400000, KEYMSCOMPAT = 0x800000, + KEYSYCL = 0x1000000, KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20, - KEYALL = (0xffffff & ~KEYNOMS18 & + KEYALL = (0xfffffff & ~KEYNOMS18 & ~KEYNOOPENCL) // KEYNOMS18 and KEYNOOPENCL are used to exclude. }; @@ -155,6 +156,8 @@ static KeywordStatus getKeywordStatus(const LangOptions &LangOpts, if (LangOpts.CPlusPlus && (Flags & KEYALLCXX)) return KS_Future; if (LangOpts.CPlusPlus && !LangOpts.CPlusPlus20 && (Flags & CHAR8SUPPORT)) return KS_Future; + if (LangOpts.isSYCL() && (Flags & KEYSYCL)) + return KS_Enabled; return KS_Disabled; } diff --git a/clang/lib/CodeGen/CGCUDANV.cpp b/clang/lib/CodeGen/CGCUDANV.cpp index 995b6a0b5fec..bfd2c16c1282 100644 --- a/clang/lib/CodeGen/CGCUDANV.cpp +++ b/clang/lib/CodeGen/CGCUDANV.cpp @@ -191,12 +191,27 @@ CGNVCUDARuntime::addUnderscoredPrefixToName(StringRef FuncName) const { return ((Twine("__cuda") + Twine(FuncName)).str()); } +static std::unique_ptr InitDeviceMC(CodeGenModule &CGM) { + // If the host and device have different C++ ABIs, mark it as the device + // mangle context so that the mangling needs to retrieve the additional + // device lambda mangling number instead of the regular host one. + if (CGM.getContext().getAuxTargetInfo() && + CGM.getContext().getTargetInfo().getCXXABI().isMicrosoft() && + CGM.getContext().getAuxTargetInfo()->getCXXABI().isItaniumFamily()) { + return std::unique_ptr( + CGM.getContext().createDeviceMangleContext( + *CGM.getContext().getAuxTargetInfo())); + } + + return std::unique_ptr(CGM.getContext().createMangleContext( + CGM.getContext().getAuxTargetInfo())); +} + CGNVCUDARuntime::CGNVCUDARuntime(CodeGenModule &CGM) : CGCUDARuntime(CGM), Context(CGM.getLLVMContext()), TheModule(CGM.getModule()), RelocatableDeviceCode(CGM.getLangOpts().GPURelocatableDeviceCode), - DeviceMC(CGM.getContext().createMangleContext( - CGM.getContext().getAuxTargetInfo())) { + DeviceMC(InitDeviceMC(CGM)) { CodeGen::CodeGenTypes &Types = CGM.getTypes(); ASTContext &Ctx = CGM.getContext(); @@ -207,14 +222,6 @@ CGNVCUDARuntime::CGNVCUDARuntime(CodeGenModule &CGM) CharPtrTy = llvm::PointerType::getUnqual(Types.ConvertType(Ctx.CharTy)); VoidPtrTy = cast(Types.ConvertType(Ctx.VoidPtrTy)); VoidPtrPtrTy = VoidPtrTy->getPointerTo(); - if (CGM.getContext().getAuxTargetInfo()) { - // If the host and device have different C++ ABIs, mark it as the device - // mangle context so that the mangling needs to retrieve the additonal - // device lambda mangling number instead of the regular host one. - DeviceMC->setDeviceMangleContext( - CGM.getContext().getTargetInfo().getCXXABI().isMicrosoft() && - CGM.getContext().getAuxTargetInfo()->getCXXABI().isItaniumFamily()); - } } llvm::FunctionCallee CGNVCUDARuntime::getSetupArgumentFn() const { diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 89ab9abc7dad..d67d6abe93c9 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -486,6 +486,8 @@ public: return CGF.EmitPseudoObjectRValue(E).getScalarVal(); } + Value *VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E); + Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) { if (E->isGLValue()) return EmitLoadOfLValue(CGF.getOrCreateOpaqueLValueMapping(E), @@ -1581,6 +1583,25 @@ Value *ScalarExprEmitter::VisitExpr(Expr *E) { return llvm::UndefValue::get(CGF.ConvertType(E->getType())); } +Value * +ScalarExprEmitter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) { + ASTContext &Context = CGF.getContext(); + llvm::Optional GlobalAS = + Context.getTargetInfo().getConstantAddressSpace(); + llvm::Constant *GlobalConstStr = Builder.CreateGlobalStringPtr( + E->ComputeName(Context), "__usn_str", + static_cast(GlobalAS.getValueOr(LangAS::Default))); + + unsigned ExprAS = Context.getTargetAddressSpace(E->getType()); + + if (GlobalConstStr->getType()->getPointerAddressSpace() == ExprAS) + return GlobalConstStr; + + llvm::Type *EltTy = GlobalConstStr->getType()->getPointerElementType(); + llvm::PointerType *NewPtrTy = llvm::PointerType::get(EltTy, ExprAS); + return Builder.CreateAddrSpaceCast(GlobalConstStr, NewPtrTy, "usn_addr_cast"); +} + Value *ScalarExprEmitter::VisitShuffleVectorExpr(ShuffleVectorExpr *E) { // Vector Mask Case if (E->getNumSubExprs() == 2) { diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index c2b47f6375b8..22f3b7624c45 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1469,6 +1469,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw_this: Res = ParseCXXThis(); break; + case tok::kw___builtin_sycl_unique_stable_name: + Res = ParseSYCLUniqueStableNameExpression(); + break; case tok::annot_typename: if (isStartOfObjCClassMessageMissingOpenBracket()) { @@ -2324,6 +2327,33 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, return Operand; } +/// Parse a __builtin_sycl_unique_stable_name expression. Accepts a type-id as +/// a parameter. +ExprResult Parser::ParseSYCLUniqueStableNameExpression() { + assert(Tok.is(tok::kw___builtin_sycl_unique_stable_name) && + "Not __bulitin_sycl_unique_stable_name"); + + SourceLocation OpLoc = ConsumeToken(); + BalancedDelimiterTracker T(*this, tok::l_paren); + + // __builtin_sycl_unique_stable_name expressions are always parenthesized. + if (T.expectAndConsume(diag::err_expected_lparen_after, + "__builtin_sycl_unique_stable_name")) + return ExprError(); + + TypeResult Ty = ParseTypeName(); + + if (Ty.isInvalid()) { + T.skipToEnd(); + return ExprError(); + } + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnSYCLUniqueStableNameExpr(OpLoc, T.getOpenLocation(), + T.getCloseLocation(), Ty.get()); +} /// Parse a sizeof or alignof expression. /// diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index f0b9e6a5bc3b..fd6a4940c174 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1575,6 +1575,8 @@ CanThrowResult Sema::canThrow(const Stmt *S) { return mergeCanThrow(CT, canThrow(TS->getTryBody())); } + case Stmt::SYCLUniqueStableNameExprClass: + return CT_Cannot; case Stmt::NoStmtClass: llvm_unreachable("Invalid class for statement"); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 5361eb37fafc..02421cbc4317 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3506,6 +3506,28 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc, return PredefinedExpr::Create(Context, Loc, ResTy, IK, SL); } +ExprResult Sema::BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, + TypeSourceInfo *TSI) { + return SYCLUniqueStableNameExpr::Create(Context, OpLoc, LParen, RParen, TSI); +} + +ExprResult Sema::ActOnSYCLUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, + ParsedType ParsedTy) { + TypeSourceInfo *TSI = nullptr; + QualType Ty = GetTypeFromParser(ParsedTy, &TSI); + + if (Ty.isNull()) + return ExprError(); + if (!TSI) + TSI = Context.getTrivialTypeSourceInfo(Ty, LParen); + + return BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, TSI); +} + ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) { PredefinedExpr::IdentKind IK; diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 1c07732fe8aa..cf5e20f7b6c1 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -461,11 +461,15 @@ void Sema::handleLambdaNumbering( std::tie(MCtx, ManglingContextDecl) = getCurrentMangleNumberContext(Class->getDeclContext()); bool HasKnownInternalLinkage = false; - if (!MCtx && getLangOpts().CUDA) { + if (!MCtx && (getLangOpts().CUDA || getLangOpts().SYCLIsDevice || + getLangOpts().SYCLIsHost)) { // Force lambda numbering in CUDA/HIP as we need to name lambdas following // ODR. Both device- and host-compilation need to have a consistent naming // on kernel functions. As lambdas are potential part of these `__global__` // function names, they needs numbering following ODR. + // Also force for SYCL, since we need this for the + // __builtin_sycl_unique_stable_name implementation, which depends on lambda + // mangling. MCtx = getMangleNumberingContext(Class, ManglingContextDecl); assert(MCtx && "Retrieving mangle numbering context failed!"); HasKnownInternalLinkage = true; diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index af35052ee1e3..3b48a53efc0d 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -8,6 +8,7 @@ // This implements Semantic Analysis for SYCL constructs. //===----------------------------------------------------------------------===// +#include "clang/AST/Mangle.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" @@ -47,3 +48,35 @@ bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) { return DiagKind != SemaDiagnosticBuilder::K_Immediate && DiagKind != SemaDiagnosticBuilder::K_ImmediateWithCallStack; } + +// The SYCL kernel's 'object type' used for diagnostics and naming/mangling is +// the first parameter to a sycl_kernel labeled function template. In SYCL1.2.1, +// this was passed by value, and in SYCL2020, it is passed by reference. +static QualType GetSYCLKernelObjectType(const FunctionDecl *KernelCaller) { + assert(KernelCaller->getNumParams() > 0 && "Insufficient kernel parameters"); + QualType KernelParamTy = KernelCaller->getParamDecl(0)->getType(); + + // SYCL 2020 kernels are passed by reference. + if (KernelParamTy->isReferenceType()) + return KernelParamTy->getPointeeType(); + + // SYCL 1.2.1 + return KernelParamTy; +} + +void Sema::AddSYCLKernelLambda(const FunctionDecl *FD) { + auto MangleCallback = [](ASTContext &Ctx, + const NamedDecl *ND) -> llvm::Optional { + if (const auto *RD = dyn_cast(ND)) + Ctx.AddSYCLKernelNamingDecl(RD); + // We always want to go into the lambda mangling (skipping the unnamed + // struct version), so make sure we return a value here. + return 1; + }; + + QualType Ty = GetSYCLKernelObjectType(FD); + std::unique_ptr Ctx{ItaniumMangleContext::create( + Context, Context.getDiagnostics(), MangleCallback)}; + llvm::raw_null_ostream Out; + Ctx->mangleTypeName(Ty, Out); +} diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index cbd29701496b..7611700d014b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -548,6 +548,35 @@ static void instantiateDependentAMDGPUWavesPerEUAttr( S.addAMDGPUWavesPerEUAttr(New, Attr, MinExpr, MaxExpr); } +// This doesn't take any template parameters, but we have a custom action that +// needs to happen when the kernel itself is instantiated. We need to run the +// ItaniumMangler to mark the names required to name this kernel. +static void instantiateDependentSYCLKernelAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const SYCLKernelAttr &Attr, Decl *New) { + // Functions cannot be partially specialized, so if we are being instantiated, + // we are obviously a complete specialization. Since this attribute is only + // valid on function template declarations, we know that this is a full + // instantiation of a kernel. + S.AddSYCLKernelLambda(cast(New)); + + // Evaluate whether this would change any of the already evaluated + // __builtin_sycl_unique_stable_name values. + for (auto &Itr : S.Context.SYCLUniqueStableNameEvaluatedValues) { + const std::string &CurName = Itr.first->ComputeName(S.Context); + if (Itr.second != CurName) { + S.Diag(New->getLocation(), + diag::err_kernel_invalidates_sycl_unique_stable_name); + S.Diag(Itr.first->getLocation(), + diag::note_sycl_unique_stable_name_evaluated_here); + // Update this so future diagnostics work correctly. + Itr.second = CurName; + } + } + + New->addAttr(Attr.clone(S.getASTContext())); +} + /// Determine whether the attribute A might be relevent to the declaration D. /// If not, we can skip instantiating it. The attribute may or may not have /// been instantiated yet. @@ -723,6 +752,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, continue; } + if (auto *A = dyn_cast(TmplAttr)) { + instantiateDependentSYCLKernelAttr(*this, TemplateArgs, *A, New); + continue; + } + assert(!TmplAttr->isPackExpansion()); if (TmplAttr->isLateParsed() && LateAttrs) { // Late parsed attributes must be instantiated and attached after the diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index e4bbc1cf4df6..80b798702fdd 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2399,6 +2399,19 @@ public: return SEHFinallyStmt::Create(getSema().getASTContext(), Loc, Block); } + ExprResult RebuildSYCLUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, Expr *E) { + return getSema().BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, E); + } + + ExprResult RebuildSYCLUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, + TypeSourceInfo *TSI) { + return getSema().BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, TSI); + } + /// Build a new predefined expression. /// /// By default, performs semantic analysis to build the new expression. @@ -10172,6 +10185,24 @@ TreeTransform::TransformConstantExpr(ConstantExpr *E) { return TransformExpr(E->getSubExpr()); } +template +ExprResult TreeTransform::TransformSYCLUniqueStableNameExpr( + SYCLUniqueStableNameExpr *E) { + if (!E->isTypeDependent()) + return E; + + TypeSourceInfo *NewT = getDerived().TransformType(E->getTypeSourceInfo()); + + if (!NewT) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && E->getTypeSourceInfo() == NewT) + return E; + + return getDerived().RebuildSYCLUniqueStableNameExpr( + E->getLocation(), E->getLParenLocation(), E->getRParenLocation(), NewT); +} + template ExprResult TreeTransform::TransformPredefinedExpr(PredefinedExpr *E) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 04adbabc9b89..c1f66709c851 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -581,6 +581,16 @@ void ASTStmtReader::VisitConstantExpr(ConstantExpr *E) { E->setSubExpr(Record.readSubExpr()); } +void ASTStmtReader::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) { + VisitExpr(E); + + E->setLocation(readSourceLocation()); + E->setLParenLocation(readSourceLocation()); + E->setRParenLocation(readSourceLocation()); + + E->setTypeSourceInfo(Record.readTypeSourceInfo()); +} + void ASTStmtReader::VisitPredefinedExpr(PredefinedExpr *E) { VisitExpr(E); bool HasFunctionName = Record.readInt(); @@ -2802,6 +2812,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { /*StorageKind=*/Record[ASTStmtReader::NumExprFields])); break; + case EXPR_SYCL_UNIQUE_STABLE_NAME: + S = SYCLUniqueStableNameExpr::CreateEmpty(Context); + break; + case EXPR_PREDEFINED: S = PredefinedExpr::CreateEmpty( Context, diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 017de0db357b..10be9b894c27 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -580,6 +580,17 @@ void ASTStmtWriter::VisitConstantExpr(ConstantExpr *E) { Code = serialization::EXPR_CONSTANT; } +void ASTStmtWriter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) { + VisitExpr(E); + + Record.AddSourceLocation(E->getLocation()); + Record.AddSourceLocation(E->getLParenLocation()); + Record.AddSourceLocation(E->getRParenLocation()); + Record.AddTypeSourceInfo(E->getTypeSourceInfo()); + + Code = serialization::EXPR_SYCL_UNIQUE_STABLE_NAME; +} + void ASTStmtWriter::VisitPredefinedExpr(PredefinedExpr *E) { VisitExpr(E); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index ccfc84bd7a58..d40161f289f8 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1419,6 +1419,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPArraySectionExprClass: case Stmt::OMPArrayShapingExprClass: case Stmt::OMPIteratorExprClass: + case Stmt::SYCLUniqueStableNameExprClass: case Stmt::TypeTraitExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; diff --git a/clang/test/AST/ast-print-sycl-unique-stable-name.cpp b/clang/test/AST/ast-print-sycl-unique-stable-name.cpp new file mode 100644 index 000000000000..3f49ea9ee733 --- /dev/null +++ b/clang/test/AST/ast-print-sycl-unique-stable-name.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -ast-print -fsycl-is-device %s -o - -triple spir64-sycldevice | FileCheck %s + +template +void WrappedInTemplate(T t) { + (void)__builtin_sycl_unique_stable_name(T); + (void)__builtin_sycl_unique_stable_name(typename T::type); + (void)__builtin_sycl_unique_stable_name(decltype(t.foo())); +} + +struct Type { + using type = int; + + double foo(); +}; + +void use() { + WrappedInTemplate(Type{}); +} + +// CHECK: template void WrappedInTemplate(T t) +// CHECK: __builtin_sycl_unique_stable_name(T); +// CHECK: __builtin_sycl_unique_stable_name(typename T::type); +// CHECK: __builtin_sycl_unique_stable_name(decltype(t.foo())); + +// CHECK: template<> void WrappedInTemplate(Type t) +// CHECK: __builtin_sycl_unique_stable_name(Type); +// CHECK: __builtin_sycl_unique_stable_name(typename Type::type); +// CHECK: __builtin_sycl_unique_stable_name(decltype(t.foo())); diff --git a/clang/test/CodeGenSYCL/unique_stable_name.cpp b/clang/test/CodeGenSYCL/unique_stable_name.cpp new file mode 100644 index 000000000000..380ddaeadc69 --- /dev/null +++ b/clang/test/CodeGenSYCL/unique_stable_name.cpp @@ -0,0 +1,159 @@ +// RUN: %clang_cc1 -triple spir64-unknown-unknown-sycldevice -fsycl-is-device -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s +// CHECK: @[[LAMBDA_KERNEL3:[^\w]+]] = private unnamed_addr constant [[LAMBDA_K3_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ4mainEUlPZ4mainEUlvE10000_E10000_\00" +// CHECK: @[[INT1:[^\w]+]] = private unnamed_addr constant [[INT_SIZE:\[[0-9]+ x i8\]]] c"_ZTSi\00" +// CHECK: @[[STRING:[^\w]+]] = private unnamed_addr constant [[STRING_SIZE:\[[0-9]+ x i8\]]] c"_ZTSAppL_ZZ4mainE1jE_i\00", +// CHECK: @[[INT2:[^\w]+]] = private unnamed_addr constant [[INT_SIZE]] c"_ZTSi\00" +// CHECK: @[[LAMBDA_X:[^\w]+]] = private unnamed_addr constant [[LAMBDA_X_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE_\00" +// CHECK: @[[MACRO_X:[^\w]+]] = private unnamed_addr constant [[MACRO_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE0_\00" +// CHECK: @[[MACRO_Y:[^\w]+]] = private unnamed_addr constant [[MACRO_SIZE]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE1_\00" +// CHECK: @{{.*}} = private unnamed_addr constant [36 x i8] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE2_\00", align 1 +// CHECK: @{{.*}} = private unnamed_addr constant [36 x i8] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE3_\00", align 1 +// CHECK: @[[MACRO_MACRO_X:[^\w]+]] = private unnamed_addr constant [[MACRO_MACRO_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE4_\00" +// CHECK: @[[MACRO_MACRO_Y:[^\w]+]] = private unnamed_addr constant [[MACRO_MACRO_SIZE]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE5_\00" +// CHECK: @[[INT3:[^\w]+]] = private unnamed_addr constant [[INT_SIZE]] c"_ZTSi\00" +// CHECK: @[[LAMBDA:[^\w]+]] = private unnamed_addr constant [[LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE_\00" +// CHECK: @[[LAMBDA_IN_DEP_INT:[^\w]+]] = private unnamed_addr constant [[DEP_INT_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ28lambda_in_dependent_functionIiEvvEUlvE_\00", +// CHECK: @[[LAMBDA_IN_DEP_X:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ28lambda_in_dependent_functionIZZ4mainENKUlvE10001_clEvEUlvE_EvvEUlvE_\00", +// CHECK: @[[LAMBDA_NO_DEP:[^\w]+]] = private unnamed_addr constant [[NO_DEP_LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ13lambda_no_depIidEvT_T0_EUlidE_\00", +// CHECK: @[[LAMBDA_TWO_DEP:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA1_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ14lambda_two_depIZZ4mainENKUlvE10001_clEvEUliE_ZZ4mainENKS0_clEvEUldE_EvvEUlvE_\00", +// CHECK: @[[LAMBDA_TWO_DEP2:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA2_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ14lambda_two_depIZZ4mainENKUlvE10001_clEvEUldE_ZZ4mainENKS0_clEvEUliE_EvvEUlvE_\00", + +extern "C" void puts(const char *) {} + +template +void template_param() { + puts(__builtin_sycl_unique_stable_name(T)); +} + +template +void lambda_in_dependent_function() { + auto y = [] {}; + puts(__builtin_sycl_unique_stable_name(decltype(y))); +} + +template +void lambda_two_dep() { + auto z = [] {}; + puts(__builtin_sycl_unique_stable_name(decltype(z))); +} + +template +void lambda_no_dep(Tw a, Tz b) { + auto p = [](Tw a, Tz b) { return ((Tz)a + b); }; + puts(__builtin_sycl_unique_stable_name(decltype(p))); +} + +#define DEF_IN_MACRO() \ + auto MACRO_X = []() {}; \ + auto MACRO_Y = []() {}; \ + puts(__builtin_sycl_unique_stable_name(decltype(MACRO_X))); \ + puts(__builtin_sycl_unique_stable_name(decltype(MACRO_Y))); + +#define MACRO_CALLS_MACRO() \ + { DEF_IN_MACRO(); } \ + { DEF_IN_MACRO(); } + +template +auto func() -> decltype(__builtin_sycl_unique_stable_name(decltype(Ty::str))); + +struct Derp { + static constexpr const char str[] = "derp derp derp"; +}; + +template +[[clang::sycl_kernel]] void kernel_single_task(KernelType kernelFunc) { + kernelFunc(); +} + +int main() { + kernel_single_task(func); + // CHECK: call spir_func void @_Z18kernel_single_taskIZ4mainE7kernel2PFPKcvEEvT0_(i8 addrspace(4)* ()* @_Z4funcI4DerpEDTu33__builtin_sycl_unique_stable_nameDtsrT_3strEEEv) + + auto l1 = []() { return 1; }; + auto l2 = [](decltype(l1) *l = nullptr) { return 2; }; + kernel_single_task(l2); + puts(__builtin_sycl_unique_stable_name(decltype(l2))); + // CHECK: call spir_func void @_Z18kernel_single_taskIZ4mainE7kernel3Z4mainEUlPZ4mainEUlvE_E_EvT0_ + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[LAMBDA_K3_SIZE]], [[LAMBDA_K3_SIZE]]* @[[LAMBDA_KERNEL3]], i32 0, i32 0) to i8 addrspace(4)*)) + + constexpr const char str[] = "lalala"; + static_assert(__builtin_strcmp(__builtin_sycl_unique_stable_name(decltype(str)), "_ZTSA7_Kc\0") == 0, "unexpected mangling"); + + int i = 0; + puts(__builtin_sycl_unique_stable_name(decltype(i++))); + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT1]], i32 0, i32 0) to i8 addrspace(4)*)) + + // FIXME: Ensure that j is incremented because VLAs are terrible. + int j = 55; + puts(__builtin_sycl_unique_stable_name(int[++j])); + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[STRING_SIZE]], [[STRING_SIZE]]* @[[STRING]], i32 0, i32 0) to i8 addrspace(4)*)) + + // CHECK: define internal spir_func void @_Z18kernel_single_taskIZ4mainE7kernel2PFPKcvEEvT0_ + // CHECK: declare spir_func i8 addrspace(4)* @_Z4funcI4DerpEDTu33__builtin_sycl_unique_stable_nameDtsrT_3strEEEv + // CHECK: define internal spir_func void @_Z18kernel_single_taskIZ4mainE7kernel3Z4mainEUlPZ4mainEUlvE_E_EvT0_ + // CHECK: define internal spir_func void @_Z18kernel_single_taskIZ4mainE6kernelZ4mainEUlvE0_EvT0_ + + kernel_single_task( + []() { + puts(__builtin_sycl_unique_stable_name(int)); + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT2]], i32 0, i32 0) to i8 addrspace(4)*)) + + auto x = []() {}; + puts(__builtin_sycl_unique_stable_name(decltype(x))); + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[LAMBDA_X_SIZE]], [[LAMBDA_X_SIZE]]* @[[LAMBDA_X]], i32 0, i32 0) to i8 addrspace(4)*)) + + DEF_IN_MACRO(); + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[MACRO_SIZE]], [[MACRO_SIZE]]* @[[MACRO_X]], i32 0, i32 0) to i8 addrspace(4)*)) + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[MACRO_SIZE]], [[MACRO_SIZE]]* @[[MACRO_Y]], i32 0, i32 0) to i8 addrspace(4)*)) + + MACRO_CALLS_MACRO(); + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[MACRO_MACRO_SIZE]], [[MACRO_MACRO_SIZE]]* @[[MACRO_MACRO_X]], i32 0, i32 0) to i8 addrspace(4)*)) + // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[MACRO_MACRO_SIZE]], [[MACRO_MACRO_SIZE]]* @[[MACRO_MACRO_Y]], i32 0, i32 0) to i8 addrspace(4)*)) + + template_param(); + // CHECK: call spir_func void @_Z14template_paramIiEvv + + template_param(); + // CHECK: call spir_func void @_Z14template_paramIZZ4mainENKUlvE0_clEvEUlvE_Evv + + lambda_in_dependent_function(); + // CHECK: call spir_func void @_Z28lambda_in_dependent_functionIiEvv + + lambda_in_dependent_function(); + // CHECK: call spir_func void @_Z28lambda_in_dependent_functionIZZ4mainENKUlvE0_clEvEUlvE_Evv + + lambda_no_dep(3, 5.5); + // CHECK: call spir_func void @_Z13lambda_no_depIidEvT_T0_(i32 3, double 5.500000e+00) + + int a = 5; + double b = 10.7; + auto y = [](int a) { return a; }; + auto z = [](double b) { return b; }; + lambda_two_dep(); + // CHECK: call spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUliE_ZZ4mainENKS0_clEvEUldE_Evv + + lambda_two_dep(); + // CHECK: call spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUldE_ZZ4mainENKS0_clEvEUliE_Evv + }); +} + +// CHECK: define linkonce_odr spir_func void @_Z14template_paramIiEvv +// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT3]], i32 0, i32 0) to i8 addrspace(4)*)) + +// CHECK: define internal spir_func void @_Z14template_paramIZZ4mainENKUlvE0_clEvEUlvE_Evv +// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[LAMBDA_SIZE]], [[LAMBDA_SIZE]]* @[[LAMBDA]], i32 0, i32 0) to i8 addrspace(4)*)) + +// CHECK: define linkonce_odr spir_func void @_Z28lambda_in_dependent_functionIiEvv +// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_INT_SIZE]], [[DEP_INT_SIZE]]* @[[LAMBDA_IN_DEP_INT]], i32 0, i32 0) to i8 addrspace(4)*)) + +// CHECK: define internal spir_func void @_Z28lambda_in_dependent_functionIZZ4mainENKUlvE0_clEvEUlvE_Evv +// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA_SIZE]], [[DEP_LAMBDA_SIZE]]* @[[LAMBDA_IN_DEP_X]], i32 0, i32 0) to i8 addrspace(4)*)) + +// CHECK: define linkonce_odr spir_func void @_Z13lambda_no_depIidEvT_T0_(i32 %a, double %b) +// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[NO_DEP_LAMBDA_SIZE]], [[NO_DEP_LAMBDA_SIZE]]* @[[LAMBDA_NO_DEP]], i32 0, i32 0) to i8 addrspace(4)*)) + +// CHECK: define internal spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUliE_ZZ4mainENKS0_clEvEUldE_Evv +// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA1_SIZE]], [[DEP_LAMBDA1_SIZE]]* @[[LAMBDA_TWO_DEP]], i32 0, i32 0) to i8 addrspace(4)*)) + +// CHECK: define internal spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUldE_ZZ4mainENKS0_clEvEUliE_Evv +// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA2_SIZE]], [[DEP_LAMBDA2_SIZE]]* @[[LAMBDA_TWO_DEP2]], i32 0, i32 0) to i8 addrspace(4)*)) diff --git a/clang/test/ParserSYCL/unique_stable_name.cpp b/clang/test/ParserSYCL/unique_stable_name.cpp new file mode 100644 index 000000000000..aa50f54ab213 --- /dev/null +++ b/clang/test/ParserSYCL/unique_stable_name.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify -Wno-unused %s + +namespace NS { +using good = double; +} + +void f(int var) { + // expected-error@+1{{expected '(' after '__builtin_sycl_unique_stable_name'}} + __builtin_sycl_unique_stable_name int; // Correct usage is __builtin_sycl_unique_stable_name(int); + + // expected-error@+1{{expected '(' after '__builtin_sycl_unique_stable_name'}} + __builtin_sycl_unique_stable_name{int}; // Correct usage is __builtin_sycl_unique_stable_name(int); + + // expected-error@+2{{expected ')'}} + // expected-note@+1{{to match this '('}} + __builtin_sycl_unique_stable_name(int; // Missing paren before semicolon + + // expected-error@+2{{expected ')'}} + // expected-note@+1{{to match this '('}} + __builtin_sycl_unique_stable_name(int, float); // Missing paren before comma + + // expected-error@+1{{unknown type name 'var'}} + __builtin_sycl_unique_stable_name(var); + __builtin_sycl_unique_stable_name(NS::good); + + // expected-error@+1{{expected a type}} + __builtin_sycl_unique_stable_name(for (int i = 0; i < 10; ++i) {}) + __builtin_sycl_unique_stable_name({ + (for (int i = 0; i < 10; ++i){})}) +} + +template +void f2() { + __builtin_sycl_unique_stable_name(typename T::good_type); +} + +struct S { + class good_type {}; +}; + +void use() { + f2(); +} diff --git a/clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp b/clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp new file mode 100644 index 000000000000..5f2a2e3313df --- /dev/null +++ b/clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify=notsycl -Wno-unused %s +// RUN: %clang_cc1 -fsyntax-only -fsycl-is-host -verify=sycl -Wno-unused %s +// RUN: %clang_cc1 -fsyntax-only -fsycl-is-device -verify=sycl -Wno-unused %s + +// sycl-no-diagnostics +void foo() { + // notsycl-error@+1{{expected '(' for function-style cast or type construction}} + __builtin_sycl_unique_stable_name(int); +} diff --git a/clang/test/SemaSYCL/unique_stable_name.cpp b/clang/test/SemaSYCL/unique_stable_name.cpp new file mode 100644 index 000000000000..f86000fca4e2 --- /dev/null +++ b/clang/test/SemaSYCL/unique_stable_name.cpp @@ -0,0 +1,215 @@ +// RUN: %clang_cc1 %s -std=c++17 -triple x86_64-pc-windows-msvc -fsycl-is-device -verify -fsyntax-only -Wno-unused +// RUN: %clang_cc1 %s -std=c++17 -triple x86_64-linux-gnu -fsycl-is-device -verify -fsyntax-only -Wno-unused + +template +[[clang::sycl_kernel]] void kernel_single_task(KernelType kernelFunc) { // #kernelSingleTask + kernelFunc(); +} + +// kernel1 - expect error +// The current function is named with a lambda (i.e., takes a lambda as a +// template parameter. Call the builtin on the current function then it is +// passed to a kernel. Test that passing the given function to the unique +// stable name builtin and then to the kernel throws an error because the +// latter causes its name mangling to change. +template +void kernel1func(const Func &F1) { + constexpr const char *F1_output = __builtin_sycl_unique_stable_name(Func); // #USN_F1 + // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}} + // expected-note@#kernel1func_call{{in instantiation of function template specialization}} + // expected-note@#USN_F1{{'__builtin_sycl_unique_stable_name' evaluated here}} + // expected-note@+1{{in instantiation of function template specialization}} + kernel_single_task(F1); // #kernel1_call +} + +void callkernel1() { + kernel1func([]() {}); // #kernel1func_call +} + +// kernel2 - expect error +// The current function is named with a lambda (i.e., takes a lambda as a +// template parameter). Call the builtin on the given function, +// then an empty lambda is passed to kernel. +// Test that passing the given function to the unique stable name builtin and +// then passing a different lambda to the kernel still throws an error because +// the calling context is part of naming the kernel. Even though the given +// function (F2) is not passed to the kernel, its mangling changes due to +// kernel call with the unrelated lambda. +template +void kernel2func(const Func &F2) { + constexpr const char *F2_output = __builtin_sycl_unique_stable_name(Func); // #USN_F2 + // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}} + // expected-note@#kernel2func_call{{in instantiation of function template specialization}} + // expected-note@#USN_F2{{'__builtin_sycl_unique_stable_name' evaluated here}} + // expected-note@+1{{in instantiation of function template specialization}} + kernel_single_task([]() {}); +} + +void callkernel2() { + kernel2func([]() {}); // #kernel2func_call +} + +template