Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -1794,7 +1794,7 @@ 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 @@ 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 @@ ``__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 Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2355,6 +2355,12 @@ /// 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 @@ 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. Index: clang/include/clang/AST/ComputeDependence.h =================================================================== --- clang/include/clang/AST/ComputeDependence.h +++ clang/include/clang/AST/ComputeDependence.h @@ -78,6 +78,7 @@ class CXXFoldExpr; class TypeTraitExpr; class ConceptSpecializationExpr; +class SYCLUniqueStableNameExpr; class PredefinedExpr; class CallExpr; class OffsetOfExpr; @@ -165,6 +166,7 @@ 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); Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -2039,6 +2039,64 @@ } }; +// 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 { Index: clang/include/clang/AST/JSONNodeDumper.h =================================================================== --- clang/include/clang/AST/JSONNodeDumper.h +++ clang/include/clang/AST/JSONNodeDumper.h @@ -263,6 +263,7 @@ 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); Index: clang/include/clang/AST/Mangle.h =================================================================== --- clang/include/clang/AST/Mangle.h +++ clang/include/clang/AST/Mangle.h @@ -107,9 +107,6 @@ 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 @@ 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 @@ 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 { Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -2651,6 +2651,9 @@ 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, {}) Index: clang/include/clang/AST/TextNodeDumper.h =================================================================== --- clang/include/clang/AST/TextNodeDumper.h +++ clang/include/clang/AST/TextNodeDumper.h @@ -249,6 +249,7 @@ 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); Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6352,6 +6352,11 @@ 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">, Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -444,6 +444,8 @@ bool hasWasmExceptions() const { return getExceptionHandling() == ExceptionHandlingKind::Wasm; } + + bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; } }; /// Floating point control options Index: clang/include/clang/Basic/StmtNodes.td =================================================================== --- clang/include/clang/Basic/StmtNodes.td +++ clang/include/clang/Basic/StmtNodes.td @@ -57,6 +57,7 @@ // Expressions def Expr : StmtNode; def PredefinedExpr : StmtNode; +def SYCLUniqueStableNameExpr : StmtNode; def DeclRefExpr : StmtNode; def IntegerLiteral : StmtNode; def FixedPointLiteral : StmtNode; Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ clang/include/clang/Basic/TokenKinds.def @@ -694,11 +694,12 @@ 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) Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -1800,6 +1800,7 @@ ExprResult ParsePostfixExpressionSuffix(ExprResult LHS); ExprResult ParseUnaryExprOrTypeTraitExpression(); ExprResult ParseBuiltinPrimaryExpression(); + ExprResult ParseSYCLUniqueStableNameExpression(); ExprResult ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, bool &isCastExpr, Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -911,6 +911,10 @@ 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 @@ 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); Index: clang/include/clang/Serialization/ASTBitCodes.h =================================================================== --- clang/include/clang/Serialization/ASTBitCodes.h +++ clang/include/clang/Serialization/ASTBitCodes.h @@ -1960,6 +1960,9 @@ // FixedPointLiteral EXPR_FIXEDPOINT_LITERAL, + + // SYCLUniqueStableNameExpr + EXPR_SYCL_UNIQUE_STABLE_NAME, }; /// The kinds of designators that can occur in a Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -2458,7 +2458,7 @@ // 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 @@ 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 @@ 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(); +} Index: clang/lib/AST/ComputeDependence.cpp =================================================================== --- clang/lib/AST/ComputeDependence.cpp +++ clang/lib/AST/ComputeDependence.cpp @@ -556,6 +556,10 @@ 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; Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -504,6 +504,70 @@ 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 @@ case SourceLocExprClass: case ConceptSpecializationExprClass: case RequiresExprClass: + case SYCLUniqueStableNameExprClass: // These never have a side-effect. return false; Index: clang/lib/AST/ExprClassification.cpp =================================================================== --- clang/lib/AST/ExprClassification.cpp +++ clang/lib/AST/ExprClassification.cpp @@ -433,6 +433,9 @@ 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"); Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -8669,6 +8669,26 @@ 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 @@ 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 @@ case Expr::CoawaitExprClass: case Expr::DependentCoawaitExprClass: case Expr::CoyieldExprClass: + case Expr::SYCLUniqueStableNameExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::InitListExprClass: { Index: clang/lib/AST/ItaniumMangle.cpp =================================================================== --- clang/lib/AST/ItaniumMangle.cpp +++ clang/lib/AST/ItaniumMangle.cpp @@ -125,14 +125,16 @@ 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 @@ 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 @@ return Name; } + DiscriminatorOverrideTy getDiscriminatorOverride() const override { + return DiscriminatorOverride; + } + /// @} }; @@ -1515,7 +1518,9 @@ // ::= * + // # 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 @@ // 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 @@ 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 @@ Mangler.mangleLambdaSig(Lambda); } +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) { - return new ItaniumMangleContextImpl(Context, Diags); +ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags, + DiscriminatorOverrideTy DiscriminatorOverride) { + return new ItaniumMangleContextImpl(Context, Diags, DiscriminatorOverride); } Index: clang/lib/AST/JSONNodeDumper.cpp =================================================================== --- clang/lib/AST/JSONNodeDumper.cpp +++ clang/lib/AST/JSONNodeDumper.cpp @@ -1164,6 +1164,12 @@ } } +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())); } Index: clang/lib/AST/StmtPrinter.cpp =================================================================== --- clang/lib/AST/StmtPrinter.cpp +++ clang/lib/AST/StmtPrinter.cpp @@ -1081,6 +1081,13 @@ 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()); } Index: clang/lib/AST/StmtProfile.cpp =================================================================== --- clang/lib/AST/StmtProfile.cpp +++ clang/lib/AST/StmtProfile.cpp @@ -1190,6 +1190,12 @@ } } +void StmtProfiler::VisitSYCLUniqueStableNameExpr( + const SYCLUniqueStableNameExpr *S) { + VisitExpr(S); + VisitType(S->getTypeSourceInfo()->getType()); +} + void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) { VisitExpr(S); ID.AddInteger(S->getIdentKind()); Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -1018,6 +1018,11 @@ OS << " isFreeIvar"; } +void TextNodeDumper::VisitSYCLUniqueStableNameExpr( + const SYCLUniqueStableNameExpr *Node) { + dumpType(Node->getTypeSourceInfo()->getType()); +} + void TextNodeDumper::VisitPredefinedExpr(const PredefinedExpr *Node) { OS << " " << PredefinedExpr::getIdentKindName(Node->getIdentKind()); } Index: clang/lib/Basic/IdentifierTable.cpp =================================================================== --- clang/lib/Basic/IdentifierTable.cpp +++ clang/lib/Basic/IdentifierTable.cpp @@ -107,8 +107,9 @@ 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 @@ 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; } Index: clang/lib/CodeGen/CGCUDANV.cpp =================================================================== --- clang/lib/CodeGen/CGCUDANV.cpp +++ clang/lib/CodeGen/CGCUDANV.cpp @@ -191,12 +191,27 @@ 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 @@ 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 { Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/lib/CodeGen/CGExprScalar.cpp @@ -486,6 +486,8 @@ 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 @@ 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) { Index: clang/lib/Parse/ParseExpr.cpp =================================================================== --- clang/lib/Parse/ParseExpr.cpp +++ clang/lib/Parse/ParseExpr.cpp @@ -1469,6 +1469,9 @@ 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 @@ 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. /// Index: clang/lib/Sema/SemaExceptionSpec.cpp =================================================================== --- clang/lib/Sema/SemaExceptionSpec.cpp +++ clang/lib/Sema/SemaExceptionSpec.cpp @@ -1575,6 +1575,8 @@ return mergeCanThrow(CT, canThrow(TS->getTryBody())); } + case Stmt::SYCLUniqueStableNameExprClass: + return CT_Cannot; case Stmt::NoStmtClass: llvm_unreachable("Invalid class for statement"); } Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -3506,6 +3506,28 @@ 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; Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -461,11 +461,15 @@ 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; Index: clang/lib/Sema/SemaSYCL.cpp =================================================================== --- clang/lib/Sema/SemaSYCL.cpp +++ 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 @@ 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); +} Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -548,6 +548,35 @@ 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 @@ 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 Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -2399,6 +2399,19 @@ 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 @@ 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) { Index: clang/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- clang/lib/Serialization/ASTReaderStmt.cpp +++ clang/lib/Serialization/ASTReaderStmt.cpp @@ -581,6 +581,16 @@ 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 @@ /*StorageKind=*/Record[ASTStmtReader::NumExprFields])); break; + case EXPR_SYCL_UNIQUE_STABLE_NAME: + S = SYCLUniqueStableNameExpr::CreateEmpty(Context); + break; + case EXPR_PREDEFINED: S = PredefinedExpr::CreateEmpty( Context, Index: clang/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- clang/lib/Serialization/ASTWriterStmt.cpp +++ clang/lib/Serialization/ASTWriterStmt.cpp @@ -580,6 +580,17 @@ 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); Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1419,6 +1419,7 @@ case Stmt::OMPArraySectionExprClass: case Stmt::OMPArrayShapingExprClass: case Stmt::OMPIteratorExprClass: + case Stmt::SYCLUniqueStableNameExprClass: case Stmt::TypeTraitExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; Index: clang/test/AST/ast-print-sycl-unique-stable-name.cpp =================================================================== --- /dev/null +++ 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())); Index: clang/test/CodeGenSYCL/unique_stable_name.cpp =================================================================== --- /dev/null +++ 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)*)) Index: clang/test/ParserSYCL/unique_stable_name.cpp =================================================================== --- /dev/null +++ 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(); +} Index: clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp =================================================================== --- /dev/null +++ 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); +} Index: clang/test/SemaSYCL/unique_stable_name.cpp =================================================================== --- /dev/null +++ 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