From b5a034e771d0e4d7d8e71fc545b230d98e5a1f42 Mon Sep 17 00:00:00 2001 From: Erich Keane Date: Wed, 18 Mar 2020 13:45:58 -0700 Subject: [PATCH] [SYCL] Implement __builtin_unique_stable_name. In order to support non-user-named kernels, SYCL needs some way in the integration headers to name the kernel object themselves. Initially, the design considered just RTTI naming of the lambdas, this results in a quite unstable situation in light of some device/host macros. Additionally, this ends up needing to use RTTI, which is a burden on the implementation and typically unsupported. Instead, we've introduced a builtin, __builtin_unique_stable_name, which takes a type or expression, and results in a constexpr constant character array that uniquely represents the type (or type of the expression) being passed to it. The implementation accomplishes that simply by using a slightly modified version of the Itanium Mangling. The one exception is when mangling lambdas, instead of appending the index of the lambda in the function, it appends the macro-expansion back-trace of the lambda itself in the form LINE->COL[~LINE->COL...]. Differential Revision: https://reviews.llvm.org/D76620 --- clang/docs/LanguageExtensions.rst | 24 ++++++++ clang/include/clang/AST/Expr.h | 68 +++++++++++++++++++++- clang/include/clang/AST/Mangle.h | 10 +++- clang/include/clang/Basic/TokenKinds.def | 11 ++-- clang/include/clang/Parse/Parser.h | 1 + clang/include/clang/Sema/Sema.h | 9 +++ clang/lib/AST/Expr.cpp | 81 +++++++++++++++++++++++++-- clang/lib/AST/ItaniumMangle.cpp | 52 +++++++++++++++-- clang/lib/Parse/ParseExpr.cpp | 41 +++++++++++++- clang/lib/Parse/ParseTentative.cpp | 1 + clang/lib/Sema/SemaExpr.cpp | 64 +++++++++++++++++++++ clang/lib/Sema/SemaTemplateInstantiate.cpp | 36 ++++++++++++ clang/test/CodeGenSYCL/unique-stable-name.cpp | 77 +++++++++++++++++++++++++ clang/test/ParserSYCL/unique-stable-name.cpp | 33 +++++++++++ 14 files changed, 489 insertions(+), 19 deletions(-) create mode 100644 clang/test/CodeGenSYCL/unique-stable-name.cpp create mode 100644 clang/test/ParserSYCL/unique-stable-name.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index f9511dc..558ce7d 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2187,6 +2187,30 @@ argument. int *pb =__builtin_preserve_access_index(&v->c[3].b); __builtin_preserve_access_index(v->j); +``__builtin_unique_stable_name`` +------------------------ + +``__builtin_unique_stable_name()`` is a builtin that takes a type or expression and +produces a string literal containing a unique name for the type (or type of the +expression) that is stable across split compilations. + +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. + +This builtin is superior to RTTI for this purpose for two reasons. First, this +value is computed entirely at compile time, so it can be used in constant +expressions. Second, this value encodes lambda functions based on line-number +rather than the order in which it appears in a function. This is valuable +because it is stable in cases where an unrelated lambda is introduced +conditionally in the same function. + +The current implementation of this builtin uses a slightly modified Itanium +Mangler to produce the unique name. The lambda ordinal is replaced with one or +more line/column pairs in the format ``LINE->COL``, separated with a ``~`` +character. Typically, only one pair will be included, however in the case of +macro expansions the entire macro expansion stack is expressed. + Multiprecision Arithmetic Builtins ---------------------------------- diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index e27d7f0..fa49182 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1896,13 +1896,17 @@ public: /// [C99 6.4.2.2] - A predefined identifier such as __func__. class PredefinedExpr final : public Expr, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend class ASTStmtReader; friend TrailingObjects; // PredefinedExpr is optionally followed by a single trailing // "Stmt *" for the predefined identifier. It is present if and only if // hasFunctionName() is true and is always a "StringLiteral *". + // It can also be followed by a Expr* in the case of a + // __builtin_unique_stable_name with an expression, or TypeSourceInfo * if + // __builtin_unique_stable_name with a type. public: enum IdentKind { @@ -1915,12 +1919,18 @@ public: PrettyFunction, /// The same as PrettyFunction, except that the /// 'virtual' keyword is omitted for virtual member functions. - PrettyFunctionNoVirtual + PrettyFunctionNoVirtual, + UniqueStableNameType, + UniqueStableNameExpr, }; private: PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK, StringLiteral *SL); + PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK, + TypeSourceInfo *Info); + PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK, + Expr *E); explicit PredefinedExpr(EmptyShell Empty, bool HasFunctionName); @@ -1933,10 +1943,39 @@ private: *getTrailingObjects() = SL; } + void setTypeSourceInfo(TypeSourceInfo *Info) { + assert(!hasFunctionName() && getIdentKind() == UniqueStableNameType && + "TypeSourceInfo only valid for UniqueStableName of a Type"); + *getTrailingObjects() = Info; + } + + void setExpr(Expr *E) { + assert(!hasFunctionName() && getIdentKind() == UniqueStableNameExpr && + "TypeSourceInfo only valid for UniqueStableName of n Expression."); + *getTrailingObjects() = E; + } + + size_t numTrailingObjects(OverloadToken) const { + return hasFunctionName(); + } + + size_t numTrailingObjects(OverloadToken) const { + return getIdentKind() == UniqueStableNameType && !hasFunctionName(); + } + size_t numTrailingObjects(OverloadToken) const { + return getIdentKind() == UniqueStableNameExpr && !hasFunctionName(); + } + public: /// Create a PredefinedExpr. static PredefinedExpr *Create(const ASTContext &Ctx, SourceLocation L, QualType FNTy, IdentKind IK, StringLiteral *SL); + static PredefinedExpr *Create(const ASTContext &Ctx, SourceLocation L, + QualType FNTy, IdentKind IK, StringLiteral *SL, + TypeSourceInfo *Info); + static PredefinedExpr *Create(const ASTContext &Ctx, SourceLocation L, + QualType FNTy, IdentKind IK, StringLiteral *SL, + Expr *E); /// Create an empty PredefinedExpr. static PredefinedExpr *CreateEmpty(const ASTContext &Ctx, @@ -1961,8 +2000,33 @@ public: : nullptr; } + TypeSourceInfo *getTypeSourceInfo() { + assert(!hasFunctionName() && getIdentKind() == UniqueStableNameType && + "TypeSourceInfo only valid for UniqueStableName of a Type"); + return *getTrailingObjects(); + } + + const TypeSourceInfo *getTypeSourceInfo() const { + assert(!hasFunctionName() && getIdentKind() == UniqueStableNameType && + "TypeSourceInfo only valid for UniqueStableName of a Type"); + return *getTrailingObjects(); + } + + Expr *getExpr() { + assert(!hasFunctionName() && getIdentKind() == UniqueStableNameExpr && + "TypeSourceInfo only valid for UniqueStableName of n Expression."); + return *getTrailingObjects(); + } + + const Expr *getExpr() const { + assert(!hasFunctionName() && getIdentKind() == UniqueStableNameExpr && + "TypeSourceInfo only valid for UniqueStableName of n Expression."); + return *getTrailingObjects(); + } + static StringRef getIdentKindName(IdentKind IK); static std::string ComputeName(IdentKind IK, const Decl *CurrentDecl); + static std::string ComputeName(ASTContext &Ctx, IdentKind IK, const QualType Ty); SourceLocation getBeginLoc() const { return getLocation(); } SourceLocation getEndLoc() const { return getLocation(); } diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index 39e4f23..2cbe6e3 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -148,9 +148,14 @@ public: }; class ItaniumMangleContext : public MangleContext { + bool IsUniqueNameMangler = false; public: explicit ItaniumMangleContext(ASTContext &C, DiagnosticsEngine &D) : MangleContext(C, D, MK_Itanium) {} + explicit ItaniumMangleContext(ASTContext &C, DiagnosticsEngine &D, + bool IsUniqueNameMangler) + : MangleContext(C, D, MK_Itanium), + IsUniqueNameMangler(IsUniqueNameMangler) {} virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0; virtual void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) = 0; @@ -169,12 +174,15 @@ public: virtual void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) = 0; + bool isUniqueNameMangler() { return IsUniqueNameMangler; } + static bool classof(const MangleContext *C) { return C->getKind() == MK_Itanium; } static ItaniumMangleContext *create(ASTContext &Context, - DiagnosticsEngine &Diags); + DiagnosticsEngine &Diags, + bool IsUniqueNameMangler = false); }; class MicrosoftMangleContext : public MangleContext { diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index fec029a..3b1062e 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -674,11 +674,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_unique_stable_name, KEYALL) // 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 69db747..8ba701c 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1754,6 +1754,7 @@ private: ExprResult ParsePostfixExpressionSuffix(ExprResult LHS); ExprResult ParseUnaryExprOrTypeTraitExpression(); ExprResult ParseBuiltinPrimaryExpression(); + ExprResult ParseUniqueStableNameExpression(); ExprResult ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, bool &isCastExpr, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4eaf09a..841c030 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4817,6 +4817,15 @@ public: ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind); ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val); + ExprResult BuildUniqueStableName(SourceLocation Loc, TypeSourceInfo *Operand); + ExprResult BuildUniqueStableName(SourceLocation Loc, Expr *E); + ExprResult ActOnUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, ParsedType Ty); + ExprResult ActOnUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation LParen, + SourceLocation RParen, Expr *Operand); + bool CheckLoopHintExpr(Expr *E, SourceLocation Loc); ExprResult ActOnNumericConstant(const Token &Tok, Scope *UDLScope = nullptr); diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index b964eb6..b603d2a 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -507,6 +507,34 @@ PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK, setDependence(computeDependence(this)); } +PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FnTy, IdentKind IK, + TypeSourceInfo *Info) + : Expr(PredefinedExprClass, FnTy, VK_LValue, OK_Ordinary) { + PredefinedExprBits.Kind = IK; + assert((getIdentKind() == IK) && + "IdentKind do not fit in PredefinedExprBitFields!"); + assert(IK == UniqueStableNameType && + "Constructor only valid with UniqueStableNameType"); + PredefinedExprBits.HasFunctionName = false; + PredefinedExprBits.Loc = L; + setTypeSourceInfo(Info); + setDependence(computeDependence(this)); +} + +PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FnTy, IdentKind IK, + Expr *Info) + : Expr(PredefinedExprClass, FnTy, VK_LValue, OK_Ordinary) { + PredefinedExprBits.Kind = IK; + assert((getIdentKind() == IK) && + "IdentKind do not fit in PredefinedExprBitFields!"); + assert(IK == UniqueStableNameExpr && + "Constructor only valid with UniqueStableNameExpr"); + PredefinedExprBits.HasFunctionName = false; + PredefinedExprBits.Loc = L; + setExpr(Info); + setDependence(computeDependence(this)); +} + PredefinedExpr::PredefinedExpr(EmptyShell Empty, bool HasFunctionName) : Expr(PredefinedExprClass, Empty) { PredefinedExprBits.HasFunctionName = HasFunctionName; @@ -516,15 +544,44 @@ PredefinedExpr *PredefinedExpr::Create(const ASTContext &Ctx, SourceLocation L, QualType FNTy, IdentKind IK, StringLiteral *SL) { bool HasFunctionName = SL != nullptr; - void *Mem = Ctx.Allocate(totalSizeToAlloc(HasFunctionName), - alignof(PredefinedExpr)); + void *Mem = Ctx.Allocate( + totalSizeToAlloc(HasFunctionName, 0, 0), + alignof(PredefinedExpr)); return new (Mem) PredefinedExpr(L, FNTy, IK, SL); } +PredefinedExpr *PredefinedExpr::Create(const ASTContext &Ctx, SourceLocation L, + QualType FNTy, IdentKind IK, + StringLiteral *SL, + TypeSourceInfo *Info) { + assert(IK == UniqueStableNameType && "Only valid with UniqueStableNameType"); + bool HasFunctionName = SL != nullptr; + void *Mem = Ctx.Allocate(totalSizeToAlloc( + HasFunctionName, 0, !HasFunctionName), + alignof(PredefinedExpr)); + if (HasFunctionName) + return new (Mem) PredefinedExpr(L, FNTy, IK, SL); + return new (Mem) PredefinedExpr(L, FNTy, IK, Info); +} + +PredefinedExpr *PredefinedExpr::Create(const ASTContext &Ctx, SourceLocation L, + QualType FNTy, IdentKind IK, + StringLiteral *SL, Expr *E) { + assert(IK == UniqueStableNameExpr && "Only valid with UniqueStableNameExpr"); + bool HasFunctionName = SL != nullptr; + void *Mem = Ctx.Allocate(totalSizeToAlloc( + HasFunctionName, !HasFunctionName, 0), + alignof(PredefinedExpr)); + if (HasFunctionName) + return new (Mem) PredefinedExpr(L, FNTy, IK, SL); + return new (Mem) PredefinedExpr(L, FNTy, IK, E); +} + PredefinedExpr *PredefinedExpr::CreateEmpty(const ASTContext &Ctx, bool HasFunctionName) { - void *Mem = Ctx.Allocate(totalSizeToAlloc(HasFunctionName), - alignof(PredefinedExpr)); + void *Mem = Ctx.Allocate( + totalSizeToAlloc(HasFunctionName, 0, 0), + alignof(PredefinedExpr)); return new (Mem) PredefinedExpr(EmptyShell(), HasFunctionName); } @@ -544,12 +601,28 @@ StringRef PredefinedExpr::getIdentKindName(PredefinedExpr::IdentKind IK) { return "__FUNCSIG__"; case LFuncSig: return "L__FUNCSIG__"; + case UniqueStableNameType: + case UniqueStableNameExpr: + return "__builtin_unique_stable_name"; case PrettyFunctionNoVirtual: break; } llvm_unreachable("Unknown ident kind for PredefinedExpr"); } +std::string PredefinedExpr::ComputeName(ASTContext &Context, IdentKind IK, + QualType Ty) { + std::unique_ptr Ctx{ItaniumMangleContext::create( + Context, Context.getDiagnostics(), /*IsUniqueNameMangler*/ true)}; + + Ty = Ty.getCanonicalType(); + + SmallString<256> Buffer; + llvm::raw_svector_ostream Out(Buffer); + Ctx->mangleTypeName(Ty, Out); + return std::string(Buffer.str()); +} + // FIXME: Maybe this should use DeclPrinter with a special "print predefined // expr" policy instead. std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index da723ed..9c1e6b5 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -127,8 +127,9 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext { public: explicit ItaniumMangleContextImpl(ASTContext &Context, - DiagnosticsEngine &Diags) - : ItaniumMangleContext(Context, Diags) {} + DiagnosticsEngine &Diags, + bool IsUniqueNameMangler) + : ItaniumMangleContext(Context, Diags, IsUniqueNameMangler) {} /// @name Mangler Entry Points /// @{ @@ -1393,7 +1394,8 @@ 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.isUniqueNameMangler())) { assert(!AdditionalAbiTags && "Lambda type cannot have additional abi tags"); mangleLambda(Record); @@ -1768,6 +1770,37 @@ void CXXNameMangler::mangleTemplateParamDecl(const NamedDecl *Decl) { } } +// Handles the __builtin_unique_stable_name feature for lambdas. Instead of the +// ordinal of the lambda in its mangling, this does line/column to uniquely and +// reliably identify the lambda. Additionally, macro expansions are expressed +// as well to prevent macros causing duplicates. +static void mangleUniqueNameLambda(CXXNameMangler &Mangler, SourceManager &SM, + raw_ostream &Out, + const CXXRecordDecl *Lambda) { + SourceLocation Loc = Lambda->getLocation(); + + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + Mangler.mangleNumber(PLoc.getLine()); + Out << "->"; + Mangler.mangleNumber(PLoc.getColumn()); + + while(Loc.isMacroID()) { + SourceLocation SLToPrint = Loc; + if (SM.isMacroArgExpansion(Loc)) + SLToPrint = SM.getImmediateExpansionRange(Loc).getBegin(); + + PLoc = SM.getPresumedLoc(SM.getSpellingLoc(SLToPrint)); + Out << "~"; + Mangler.mangleNumber(PLoc.getLine()); + Out << "->"; + Mangler.mangleNumber(PLoc.getColumn()); + + Loc = SM.getImmediateMacroCallerLoc(Loc); + if (Loc.isFileID()) + Loc = SM.getImmediateMacroCallerLoc(SLToPrint); + } +} + void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { // If the context of a closure type is an initializer for a class member // (static or nonstatic), it is encoded in a qualified name with a final @@ -1798,6 +1831,12 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { mangleLambdaSig(Lambda); Out << "E"; + if (Context.isUniqueNameMangler()) { + mangleUniqueNameLambda( + *this, Context.getASTContext().getSourceManager(), Out, Lambda); + return; + } + // The number is omitted for the first closure type with a given // in a given context; it is n-2 for the nth closure type // (in lexical order) with that same and context. @@ -5203,7 +5242,8 @@ 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, + bool IsUniqueNameMangler) { + return new ItaniumMangleContextImpl(Context, Diags, IsUniqueNameMangler); } diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 096e295..bcd5679 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1450,7 +1450,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw_this: Res = ParseCXXThis(); break; - + case tok::kw___builtin_unique_stable_name: + Res = ParseUniqueStableNameExpression(); + break; case tok::annot_typename: if (isStartOfObjCClassMessageMissingOpenBracket()) { ParsedType Type = getTypeAnnotation(Tok); @@ -2222,6 +2224,43 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, } +ExprResult Parser::ParseUniqueStableNameExpression() { + assert(Tok.is(tok::kw___builtin_unique_stable_name) && + "Not __bulitin_unique_stable_name"); + + SourceLocation OpLoc = ConsumeToken(); + BalancedDelimiterTracker T(*this, tok::l_paren); + + // typeid expressions are always parenthesized. + if (T.expectAndConsume(diag::err_expected_lparen_after, + "__builtin_unique_stable_name")) + return ExprError(); + + if (isTypeIdInParens()) { + TypeResult Ty = ParseTypeName(); + T.consumeClose(); + + if (Ty.isInvalid()) + return ExprError(); + + return Actions.ActOnUniqueStableNameExpr(OpLoc, T.getOpenLocation(), + T.getCloseLocation(), Ty.get()); + } + + EnterExpressionEvaluationContext Unevaluated( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + ExprResult Result = ParseExpression(); + + if (Result.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return Result; + } + + T.consumeClose(); + return Actions.ActOnUniqueStableNameExpr(OpLoc, T.getOpenLocation(), + T.getCloseLocation(), Result.get()); +} + /// Parse a sizeof or alignof expression. /// /// \verbatim diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 75cc7c2..bca7d02 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1119,6 +1119,7 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { case tok::kw_L__FUNCSIG__: case tok::kw___PRETTY_FUNCTION__: case tok::kw___uuidof: + case tok::kw___builtin_unique_stable_name: #define TYPE_TRAIT(N,Spelling,K) \ case tok::kw_##Spelling: #include "clang/Basic/TokenKinds.def" diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 373ae97..7cb12ec 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3373,6 +3373,70 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc, return PredefinedExpr::Create(Context, Loc, ResTy, IK, SL); } +static std::pair +GetUniqueStableNameInfo(ASTContext &Context, QualType OpType, + SourceLocation OpLoc, PredefinedExpr::IdentKind K) { + std::pair Result{{}, nullptr}; + + if (OpType->isDependentType()) { + Result.first = Context.DependentTy; + return Result; + } + + std::string Str = PredefinedExpr::ComputeName(Context, K, OpType); + llvm::APInt Length(32, Str.length() + 1); + Result.first = + Context.adjustStringLiteralBaseType(Context.CharTy.withConst()); + Result.first = Context.getConstantArrayType( + Result.first, Length, nullptr, ArrayType::Normal, /*IndexTypeQuals*/ 0); + Result.second = StringLiteral::Create(Context, Str, StringLiteral::Ascii, + /*Pascal*/ false, Result.first, OpLoc); + return Result; +} + +ExprResult Sema::BuildUniqueStableName(SourceLocation OpLoc, + TypeSourceInfo *Operand) { + QualType ResultTy; + StringLiteral *SL; + std::tie(ResultTy, SL) = GetUniqueStableNameInfo( + Context, Operand->getType(), OpLoc, PredefinedExpr::UniqueStableNameType); + + return PredefinedExpr::Create(Context, OpLoc, ResultTy, + PredefinedExpr::UniqueStableNameType, SL, + Operand); +} + +ExprResult Sema::BuildUniqueStableName(SourceLocation OpLoc, + Expr *E) { + QualType ResultTy; + StringLiteral *SL; + std::tie(ResultTy, SL) = GetUniqueStableNameInfo( + Context, E->getType(), OpLoc, PredefinedExpr::UniqueStableNameExpr); + + return PredefinedExpr::Create(Context, OpLoc, ResultTy, + PredefinedExpr::UniqueStableNameExpr, SL, E); +} + +ExprResult Sema::ActOnUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation L, SourceLocation R, + ParsedType Ty) { + TypeSourceInfo *TInfo = nullptr; + QualType T = GetTypeFromParser(Ty, &TInfo); + + if (T.isNull()) + return ExprError(); + if (!TInfo) + TInfo = Context.getTrivialTypeSourceInfo(T, OpLoc); + + return BuildUniqueStableName(OpLoc, TInfo); +} + +ExprResult Sema::ActOnUniqueStableNameExpr(SourceLocation OpLoc, + SourceLocation L, SourceLocation R, + Expr *E) { + return BuildUniqueStableName(OpLoc, E); +} + ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) { PredefinedExpr::IdentKind IK; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index a601b92..e2b3ebb 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1385,11 +1385,47 @@ TemplateName TemplateInstantiator::TransformTemplateName( AllowInjectedClassName); } +static ExprResult TransformUniqueStableName(TemplateInstantiator &TI, + PredefinedExpr *E) { + if (E->getIdentKind() == PredefinedExpr::UniqueStableNameType) { + TypeSourceInfo *Info = + TI.getDerived().TransformType(E->getTypeSourceInfo()); + + if (!Info) + return ExprError(); + + if (!TI.getDerived().AlwaysRebuild() && Info == E->getTypeSourceInfo()) + return E; + + return TI.getSema().BuildUniqueStableName(E->getLocation(), Info); + } + + if (E->getIdentKind() == PredefinedExpr::UniqueStableNameExpr) { + EnterExpressionEvaluationContext Unevaluated( + TI.getSema(), Sema::ExpressionEvaluationContext::Unevaluated); + ExprResult SubExpr = TI.getDerived().TransformExpr(E->getExpr()); + + if (SubExpr.isInvalid()) + return ExprError(); + + if (!TI.getDerived().AlwaysRebuild() && SubExpr.get() == E->getExpr()) + return E; + + return TI.getSema().BuildUniqueStableName(E->getLocation(), SubExpr.get()); + } + + llvm_unreachable("Only valid for UniqueStableNameType/Expr"); +} + ExprResult TemplateInstantiator::TransformPredefinedExpr(PredefinedExpr *E) { if (!E->isTypeDependent()) return E; + if (E->getIdentKind() == PredefinedExpr::UniqueStableNameType || + E->getIdentKind() == PredefinedExpr::UniqueStableNameExpr) + return TransformUniqueStableName(*this, E); + return getSema().BuildPredefinedExpr(E->getLocation(), E->getIdentKind()); } diff --git a/clang/test/CodeGenSYCL/unique-stable-name.cpp b/clang/test/CodeGenSYCL/unique-stable-name.cpp new file mode 100644 index 0000000..b54c17b --- /dev/null +++ b/clang/test/CodeGenSYCL/unique-stable-name.cpp @@ -0,0 +1,77 @@ +// RUN: %clang_cc1 -triple spir64-unknown-unknown-sycldevice -fsycl -fsycl-is-device -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s +// CHECK: @[[INT:[^\w]+]] = private unnamed_addr constant [[INT_SIZE:\[[0-9]+ x i8\]]] c"_ZTSi\00" +// CHECK: @[[LAMBDA_X:[^\w]+]] = private unnamed_addr constant [[LAMBDA_X_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE42->5clEvEUlvE46->16\00" +// CHECK: @[[MACRO_X:[^\w]+]] = private unnamed_addr constant [[MACRO_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE42->5clEvEUlvE52->7~28->18\00" +// CHECK: @[[MACRO_Y:[^\w]+]] = private unnamed_addr constant [[MACRO_SIZE]] c"_ZTSZZ4mainENKUlvE42->5clEvEUlvE52->7~28->41\00" +// CHECK: @[[MACRO_MACRO_X:[^\w]+]] = private unnamed_addr constant [[MACRO_MACRO_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE42->5clEvEUlvE55->7~28->18~33->4\00" +// CHECK: @[[MACRO_MACRO_Y:[^\w]+]] = private unnamed_addr constant [[MACRO_MACRO_SIZE]] c"_ZTSZZ4mainENKUlvE42->5clEvEUlvE55->7~28->41~33->4\00" +// CHECK: @[[LAMBDA_IN_DEP_INT:[^\w]+]] = private unnamed_addr constant [[DEP_INT_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ28lambda_in_dependent_functionIiEvvEUlvE23->12\00", +// CHECK: @[[LAMBDA_IN_DEP_X:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ28lambda_in_dependent_functionIZZ4mainENKUlvE42->5clEvEUlvE46->16EvvEUlvE23->12\00", + +extern "C" void printf(const char *) {} + +template +void template_param() { + printf(__builtin_unique_stable_name(T)); +} + +template +T getT() { return T{}; } + +template +void lambda_in_dependent_function() { + auto y = [] {}; + printf(__builtin_unique_stable_name(y)); +} + +#define DEF_IN_MACRO() \ + auto MACRO_X = []() {};auto MACRO_Y = []() {}; \ + printf(__builtin_unique_stable_name(MACRO_X)); \ + printf(__builtin_unique_stable_name(MACRO_Y)); + +#define MACRO_CALLS_MACRO() \ + {DEF_IN_MACRO();}{DEF_IN_MACRO();} + +template +[[clang::sycl_kernel]] void kernel_single_task(KernelType kernelFunc) { + kernelFunc(); +} + +int main() { + kernel_single_task( + []() { + printf(__builtin_unique_stable_name(int)); + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT]] + + auto x = [](){}; + printf(__builtin_unique_stable_name(x)); + printf(__builtin_unique_stable_name(decltype(x))); + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[LAMBDA_X_SIZE]], [[LAMBDA_X_SIZE]]* @[[LAMBDA_X]] + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[LAMBDA_X_SIZE]], [[LAMBDA_X_SIZE]]* @[[LAMBDA_X]] + + DEF_IN_MACRO(); + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[MACRO_SIZE]], [[MACRO_SIZE]]* @[[MACRO_X]] + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[MACRO_SIZE]], [[MACRO_SIZE]]* @[[MACRO_Y]] + MACRO_CALLS_MACRO(); + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[MACRO_MACRO_SIZE]], [[MACRO_MACRO_SIZE]]* @[[MACRO_MACRO_X]] + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[MACRO_MACRO_SIZE]], [[MACRO_MACRO_SIZE]]* @[[MACRO_MACRO_Y]] + + template_param(); + // CHECK: define linkonce_odr spir_func void @_Z14template_paramIiEvv + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT]] + + template_param(); + // CHECK: define internal spir_func void @"_Z14template_paramIZZ4mainENK3 + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[LAMBDA_X_SIZE]], [[LAMBDA_X_SIZE]]* @[[LAMBDA_X]] + + lambda_in_dependent_function(); + // CHECK: define linkonce_odr spir_func void @_Z28lambda_in_dependent_functionIiEvv + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[DEP_INT_SIZE]], [[DEP_INT_SIZE]]* @[[LAMBDA_IN_DEP_INT]] + + lambda_in_dependent_function(); + // CHECK: define internal spir_func void @"_Z28lambda_in_dependent_functionIZZ4mainENK3$_0clEvEUlvE_Evv + // CHECK: call spir_func void @printf(i8* getelementptr inbounds ([[DEP_LAMBDA_SIZE]], [[DEP_LAMBDA_SIZE]]* @[[LAMBDA_IN_DEP_X]] + + }); +} + diff --git a/clang/test/ParserSYCL/unique-stable-name.cpp b/clang/test/ParserSYCL/unique-stable-name.cpp new file mode 100644 index 0000000..d1f1304 --- /dev/null +++ b/clang/test/ParserSYCL/unique-stable-name.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused %s + +namespace NS{}; + +void f(int var) { + // expected-error@+1{{expected '(' after '__builtin_unique_stable_name'}} + __builtin_unique_stable_name int; + // expected-error@+1{{expected '(' after '__builtin_unique_stable_name'}} + __builtin_unique_stable_name {int}; + + __builtin_unique_stable_name(var); + // expected-error@+1{{use of undeclared identifier 'bad_var'}} + __builtin_unique_stable_name(bad_var); + // expected-error@+1{{use of undeclared identifier 'bad'}} + __builtin_unique_stable_name(bad::type); + // expected-error@+1{{no member named 'still_bad' in namespace 'NS'}} + __builtin_unique_stable_name(NS::still_bad); +} + +template +void f2() { + // expected-error@+1{{no member named 'bad_val' in 'S'}} + __builtin_unique_stable_name(T::bad_val); + // expected-error@+1{{no type named 'bad_type' in 'S'}} + __builtin_unique_stable_name(typename T::bad_type); +} + +struct S{}; + +void use() { + // expected-note@+1{{in instantiation of}} + f2(); +} -- 2.7.4