[clang] eba69b5 - Reimplement __builtin_unique_stable_name-

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Thu May 27 07:12:27 PDT 2021


Author: Erich Keane
Date: 2021-05-27T07:12:20-07:00
New Revision: eba69b59d1a30dead07da2c279c8ecfd2b62ba9f

URL: https://github.com/llvm/llvm-project/commit/eba69b59d1a30dead07da2c279c8ecfd2b62ba9f
DIFF: https://github.com/llvm/llvm-project/commit/eba69b59d1a30dead07da2c279c8ecfd2b62ba9f.diff

LOG: Reimplement __builtin_unique_stable_name-

The original version of this was reverted, and @rjmcall provided some
advice to architect a new solution.  This is that solution.

This implements a builtin to provide a unique name that is stable across
compilations of this TU for the purposes of implementing the library
component of the unnamed kernel feature of SYCL.  It does this by
running the Itanium mangler with a few modifications.

Because it is somewhat common to wrap non-kernel-related lambdas in
macros that aren't present on the device (such as for logging), this
uniquely generates an ID for all lambdas involved in the naming of a
kernel. It uses the lambda-mangling number to do this, except replaces
this with its own number (starting at 10000 for readabililty reasons)
for lambdas used to name a kernel.

Additionally, this implements itself as constexpr with a slight catch:
if a name would be invalidated by the use of this lambda in a later
kernel invocation, it is diagnosed as an error (see the Sema tests).

Differential Revision: https://reviews.llvm.org/D103112

Added: 
    clang/test/AST/ast-print-sycl-unique-stable-name.cpp
    clang/test/CodeGenSYCL/unique_stable_name.cpp
    clang/test/ParserSYCL/unique_stable_name.cpp
    clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp
    clang/test/SemaSYCL/unique_stable_name.cpp

Modified: 
    clang/docs/LanguageExtensions.rst
    clang/include/clang/AST/ASTContext.h
    clang/include/clang/AST/ComputeDependence.h
    clang/include/clang/AST/Expr.h
    clang/include/clang/AST/JSONNodeDumper.h
    clang/include/clang/AST/Mangle.h
    clang/include/clang/AST/RecursiveASTVisitor.h
    clang/include/clang/AST/TextNodeDumper.h
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/LangOptions.h
    clang/include/clang/Basic/StmtNodes.td
    clang/include/clang/Basic/TokenKinds.def
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Serialization/ASTBitCodes.h
    clang/lib/AST/ASTContext.cpp
    clang/lib/AST/ComputeDependence.cpp
    clang/lib/AST/Expr.cpp
    clang/lib/AST/ExprClassification.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/lib/AST/ItaniumMangle.cpp
    clang/lib/AST/JSONNodeDumper.cpp
    clang/lib/AST/StmtPrinter.cpp
    clang/lib/AST/StmtProfile.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/lib/Basic/IdentifierTable.cpp
    clang/lib/CodeGen/CGCUDANV.cpp
    clang/lib/CodeGen/CGExprScalar.cpp
    clang/lib/Parse/ParseExpr.cpp
    clang/lib/Sema/SemaExceptionSpec.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaLambda.cpp
    clang/lib/Sema/SemaSYCL.cpp
    clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
    clang/lib/Sema/TreeTransform.h
    clang/lib/Serialization/ASTReaderStmt.cpp
    clang/lib/Serialization/ASTWriterStmt.cpp
    clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
    clang/tools/libclang/CXCursor.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index eb9fe0c0ac0d7..129e73345b2a2 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1794,7 +1794,7 @@ correctly in any circumstances. It can be used if:
   metaprogramming algorithms to be able to specify/detect types generically.
 
 - the generated kernel binary does not contain indirect calls because they
-  are eliminated using compiler optimizations e.g. devirtualization. 
+  are eliminated using compiler optimizations e.g. devirtualization.
 
 - the selected target supports the function pointer like functionality e.g.
   most CPU targets.
@@ -2404,6 +2404,40 @@ argument.
   int *pb =__builtin_preserve_access_index(&v->c[3].b);
   __builtin_preserve_access_index(v->j);
 
+``__builtin_sycl_unique_stable_name``
+-------------------------------------
+
+``__builtin_sycl_unique_stable_name()`` is a builtin that takes a type and
+produces a string literal containing a unique name for the type that is stable
+across split compilations, mainly to support SYCL/Data Parallel C++ language.
+
+In cases where the split compilation needs to share a unique token for a type
+across the boundary (such as in an offloading situation), this name can be used
+for lookup purposes, such as in the SYCL Integration Header.
+
+The value of this builtin is computed entirely at compile time, so it can be
+used in constant expressions. This value encodes lambda functions based on a
+stable numbering order in which they appear in their local declaration contexts.
+Once this builtin is evaluated in a constexpr context, it is erroneous to use
+it in an instantiation which changes its value.
+
+In order to produce the unique name, the current implementation of the bultin
+uses Itanium mangling even if the host compilation uses a 
diff erent 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
+
diff erentiator from ordinary lambda mangling numbers but does not serve any
+other purpose and may change in the future. The resulting pattern is
+demanglable. When non-lambda types are passed to the builtin, the mangler emits
+their usual pattern without any special treatment.
+
+**Syntax**:
+
+.. code-block:: c
+
+  // Computes a unique stable name for the given type.
+  constexpr const char * __builtin_sycl_unique_stable_name( type-id );
+
 Multiprecision Arithmetic Builtins
 ----------------------------------
 
@@ -2598,7 +2632,7 @@ Guaranteed inlined copy
 ``__builtin_memcpy_inline`` has been designed as a building block for efficient
 ``memcpy`` implementations. It is identical to ``__builtin_memcpy`` but also
 guarantees not to call any external functions. See LLVM IR `llvm.memcpy.inline
-<https://llvm.org/docs/LangRef.html#llvm-memcpy-inline-intrinsic>`_ intrinsic 
+<https://llvm.org/docs/LangRef.html#llvm-memcpy-inline-intrinsic>`_ intrinsic
 for more information.
 
 This is useful to implement a custom version of ``memcpy``, implement a

diff  --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index be6170564fcd0..db6d263a5e15c 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2355,6 +2355,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// 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<const ObjCIvarDecl*> &Ivars) const;
 
@@ -3158,10 +3164,27 @@ OPT_LIST(V)
 
   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<const SYCLUniqueStableNameExpr *, std::string>
+      SYCLUniqueStableNameEvaluatedValues;
+
 private:
   /// All OMPTraitInfo objects live in this collection, one per
   /// `pragma omp [begin] declare variant` directive.
   SmallVector<std::unique_ptr<OMPTraitInfo>, 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<const DeclContext *,
+                 llvm::SmallPtrSet<const CXXRecordDecl *, 4>>
+      SYCLKernelNamingTypes;
 };
 
 /// Insertion operator for diagnostics.

diff  --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h
index 04e8e2c7d2ccb..7dde42ee71ba0 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -78,6 +78,7 @@ class MaterializeTemporaryExpr;
 class CXXFoldExpr;
 class TypeTraitExpr;
 class ConceptSpecializationExpr;
+class SYCLUniqueStableNameExpr;
 class PredefinedExpr;
 class CallExpr;
 class OffsetOfExpr;
@@ -165,6 +166,7 @@ ExprDependence computeDependence(TypeTraitExpr *E);
 ExprDependence computeDependence(ConceptSpecializationExpr *E,
                                  bool ValueDependent);
 
+ExprDependence computeDependence(SYCLUniqueStableNameExpr *E);
 ExprDependence computeDependence(PredefinedExpr *E);
 ExprDependence computeDependence(CallExpr *E, llvm::ArrayRef<Expr *> PreArgs);
 ExprDependence computeDependence(OffsetOfExpr *E);

diff  --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 589199dbcdcaf..55ee96feae98c 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -2039,6 +2039,64 @@ class PredefinedExpr final
   }
 };
 
+// This represents a use of the __builtin_sycl_unique_stable_name, which takes a
+// type-id, and at CodeGen time emits a unique string representation of the
+// type in a way that permits us to properly encode information about the SYCL
+// kernels.
+class SYCLUniqueStableNameExpr final : public Expr {
+  friend class ASTStmtReader;
+  SourceLocation OpLoc, LParen, RParen;
+  TypeSourceInfo *TypeInfo;
+
+  SYCLUniqueStableNameExpr(EmptyShell Empty, QualType ResultTy);
+  SYCLUniqueStableNameExpr(SourceLocation OpLoc, SourceLocation LParen,
+                           SourceLocation RParen, QualType ResultTy,
+                           TypeSourceInfo *TSI);
+
+  void setTypeSourceInfo(TypeSourceInfo *Ty) { TypeInfo = Ty; }
+
+  void setLocation(SourceLocation L) { OpLoc = L; }
+  void setLParenLocation(SourceLocation L) { LParen = L; }
+  void setRParenLocation(SourceLocation L) { RParen = L; }
+
+public:
+  TypeSourceInfo *getTypeSourceInfo() { return TypeInfo; }
+
+  const TypeSourceInfo *getTypeSourceInfo() const { return TypeInfo; }
+
+  static SYCLUniqueStableNameExpr *
+  Create(const ASTContext &Ctx, SourceLocation OpLoc, SourceLocation LParen,
+         SourceLocation RParen, TypeSourceInfo *TSI);
+
+  static SYCLUniqueStableNameExpr *CreateEmpty(const ASTContext &Ctx);
+
+  SourceLocation getBeginLoc() const { return getLocation(); }
+  SourceLocation getEndLoc() const { return RParen; }
+  SourceLocation getLocation() const { return OpLoc; }
+  SourceLocation getLParenLocation() const { return LParen; }
+  SourceLocation getRParenLocation() const { return RParen; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == SYCLUniqueStableNameExprClass;
+  }
+
+  // Iterators
+  child_range children() {
+    return child_range(child_iterator(), child_iterator());
+  }
+
+  const_child_range children() const {
+    return const_child_range(const_child_iterator(), const_child_iterator());
+  }
+
+  // Convenience function to generate the name of the currently stored type.
+  std::string ComputeName(ASTContext &Context) const;
+
+  // Get the generated name of the type.  Note that this only works after all
+  // kernels have been instantiated.
+  static std::string ComputeName(ASTContext &Context, QualType Ty);
+};
+
 /// ParenExpr - This represents a parethesized expression, e.g. "(1)".  This
 /// AST node is only formed if full location information is requested.
 class ParenExpr : public Expr {

diff  --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h
index 4e7162992418a..a8b731a7cd31d 100644
--- a/clang/include/clang/AST/JSONNodeDumper.h
+++ b/clang/include/clang/AST/JSONNodeDumper.h
@@ -263,6 +263,7 @@ class JSONNodeDumper
   void VisitBlockDecl(const BlockDecl *D);
 
   void VisitDeclRefExpr(const DeclRefExpr *DRE);
+  void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E);
   void VisitPredefinedExpr(const PredefinedExpr *PE);
   void VisitUnaryOperator(const UnaryOperator *UO);
   void VisitBinaryOperator(const BinaryOperator *BO);

diff  --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h
index dc620ec10ff9e..7d02f08e0120c 100644
--- a/clang/include/clang/AST/Mangle.h
+++ b/clang/include/clang/AST/Mangle.h
@@ -107,9 +107,6 @@ class MangleContext {
   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 MangleContext {
 
 class ItaniumMangleContext : public MangleContext {
 public:
+  using DiscriminatorOverrideTy =
+      llvm::Optional<unsigned> (*)(ASTContext &, const NamedDecl *);
   explicit ItaniumMangleContext(ASTContext &C, DiagnosticsEngine &D)
       : MangleContext(C, D, MK_Itanium) {}
 
@@ -195,12 +194,18 @@ class ItaniumMangleContext : public MangleContext {
 
   virtual void mangleDynamicStermFinalizer(const VarDecl *D, raw_ostream &) = 0;
 
+  // This has to live here, otherwise the CXXNameMangler won't have access to
+  // it.
+  virtual DiscriminatorOverrideTy getDiscriminatorOverride() const = 0;
   static bool classof(const MangleContext *C) {
     return C->getKind() == MK_Itanium;
   }
 
   static ItaniumMangleContext *create(ASTContext &Context,
                                       DiagnosticsEngine &Diags);
+  static ItaniumMangleContext *create(ASTContext &Context,
+                                      DiagnosticsEngine &Diags,
+                                      DiscriminatorOverrideTy Discriminator);
 };
 
 class MicrosoftMangleContext : public MangleContext {

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 4770a2fd42a2a..b3ea6f7817ef9 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2651,6 +2651,9 @@ DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, {
 DEF_TRAVERSE_STMT(ObjCAvailabilityCheckExpr, {})
 DEF_TRAVERSE_STMT(ParenExpr, {})
 DEF_TRAVERSE_STMT(ParenListExpr, {})
+DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
 DEF_TRAVERSE_STMT(PredefinedExpr, {})
 DEF_TRAVERSE_STMT(ShuffleVectorExpr, {})
 DEF_TRAVERSE_STMT(ConvertVectorExpr, {})

diff  --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 0955c494a74ad..e3aa94d5a1e83 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -249,6 +249,7 @@ class TextNodeDumper
   void VisitCastExpr(const CastExpr *Node);
   void VisitImplicitCastExpr(const ImplicitCastExpr *Node);
   void VisitDeclRefExpr(const DeclRefExpr *Node);
+  void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *Node);
   void VisitPredefinedExpr(const PredefinedExpr *Node);
   void VisitCharacterLiteral(const CharacterLiteral *Node);
   void VisitIntegerLiteral(const IntegerLiteral *Node);

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5d0f5dcbfc359..4b653b8b47370 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6352,6 +6352,11 @@ def warn_pointer_arith_null_ptr : Warning<
 def warn_gnu_null_ptr_arith : Warning<
   "arithmetic on a null pointer treated as a cast from integer to pointer is a GNU extension">,
   InGroup<NullPointerArithmetic>, DefaultIgnore;
+def err_kernel_invalidates_sycl_unique_stable_name
+    : Error<"kernel instantiation changes the result of an evaluated "
+            "'__builtin_sycl_unique_stable_name'">;
+def note_sycl_unique_stable_name_evaluated_here
+    : Note<"'__builtin_sycl_unique_stable_name' evaluated here">;
 
 def warn_floatingpoint_eq : Warning<
   "comparing floating point with == or != is unsafe">,

diff  --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 7fd4bf0eb2065..f5975d89a299e 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -444,6 +444,8 @@ class LangOptions : public LangOptionsBase {
   bool hasWasmExceptions() const {
     return getExceptionHandling() == ExceptionHandlingKind::Wasm;
   }
+
+  bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; }
 };
 
 /// Floating point control options

diff  --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index ecaf7b027e778..6134e60f35414 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -57,6 +57,7 @@ def CoreturnStmt : StmtNode<Stmt>;
 // Expressions
 def Expr : StmtNode<ValueStmt, 1>;
 def PredefinedExpr : StmtNode<Expr>;
+def SYCLUniqueStableNameExpr : StmtNode<Expr>;
 def DeclRefExpr : StmtNode<Expr>;
 def IntegerLiteral : StmtNode<Expr>;
 def FixedPointLiteral : StmtNode<Expr>;

diff  --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index be258c95d2fac..fd9909628b3b7 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -696,11 +696,12 @@ ALIAS("_declspec"        , __declspec , KEYMS)
 ALIAS("_pascal"      , __pascal   , KEYBORLAND)
 
 // Clang Extensions.
-KEYWORD(__builtin_convertvector     , KEYALL)
-ALIAS("__char16_t"   , char16_t     , KEYCXX)
-ALIAS("__char32_t"   , char32_t     , KEYCXX)
-KEYWORD(__builtin_bit_cast          , KEYALL)
-KEYWORD(__builtin_available         , KEYALL)
+KEYWORD(__builtin_convertvector          , KEYALL)
+ALIAS("__char16_t"   , char16_t          , KEYCXX)
+ALIAS("__char32_t"   , char32_t          , KEYCXX)
+KEYWORD(__builtin_bit_cast               , KEYALL)
+KEYWORD(__builtin_available              , KEYALL)
+KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL)
 
 // Clang-specific keywords enabled only in testing.
 TESTING_KEYWORD(__unknown_anytype , KEYALL)

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 213f7fb3dc56f..e3e6509b025e5 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1800,6 +1800,7 @@ class Parser : public CodeCompletionHandler {
   ExprResult ParsePostfixExpressionSuffix(ExprResult LHS);
   ExprResult ParseUnaryExprOrTypeTraitExpression();
   ExprResult ParseBuiltinPrimaryExpression();
+  ExprResult ParseSYCLUniqueStableNameExpression();
 
   ExprResult ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
                                                      bool &isCastExpr,

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 706293fa929ce..07518b37eb98a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -911,6 +911,10 @@ class Sema final {
     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 @@ class Sema final {
   ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind);
   ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val);
 
+  ExprResult BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen,
+                                           TypeSourceInfo *TSI);
+  ExprResult ActOnSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen,
+                                           ParsedType ParsedTy);
+
   bool CheckLoopHintExpr(Expr *E, SourceLocation Loc);
 
   ExprResult ActOnNumericConstant(const Token &Tok, Scope *UDLScope = nullptr);

diff  --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 5e5a73e730924..83a6f6a2c4402 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1960,6 +1960,9 @@ enum StmtCode {
 
   // FixedPointLiteral
   EXPR_FIXEDPOINT_LITERAL,
+
+  // SYCLUniqueStableNameExpr
+  EXPR_SYCL_UNIQUE_STABLE_NAME,
 };
 
 /// The kinds of designators that can occur in a

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 47d64a5fa48ea..75656496a9467 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2458,7 +2458,7 @@ unsigned ASTContext::getPreferredTypeAlign(const Type *T) const {
   // The preferred alignment of member pointers is that of a pointer.
   if (T->isMemberPointerType())
     return getPreferredTypeAlign(getPointerDiffType().getTypePtr());
- 
+
   if (!Target->allowsLargerPreferedTypeAlignment())
     return ABIAlign;
 
@@ -11075,6 +11075,33 @@ MangleContext *ASTContext::createMangleContext(const TargetInfo *T) {
   llvm_unreachable("Unsupported ABI");
 }
 
+MangleContext *ASTContext::createDeviceMangleContext(const TargetInfo &T) {
+  assert(T.getCXXABI().getKind() != TargetCXXABI::Microsoft &&
+         "Device mangle context does not support Microsoft mangling.");
+  switch (T.getCXXABI().getKind()) {
+  case TargetCXXABI::AppleARM64:
+  case TargetCXXABI::Fuchsia:
+  case TargetCXXABI::GenericAArch64:
+  case TargetCXXABI::GenericItanium:
+  case TargetCXXABI::GenericARM:
+  case TargetCXXABI::GenericMIPS:
+  case TargetCXXABI::iOS:
+  case TargetCXXABI::WebAssembly:
+  case TargetCXXABI::WatchOS:
+  case TargetCXXABI::XL:
+    return ItaniumMangleContext::create(
+        *this, getDiagnostics(),
+        [](ASTContext &, const NamedDecl *ND) -> llvm::Optional<unsigned> {
+          if (const auto *RD = dyn_cast<CXXRecordDecl>(ND))
+            return RD->getDeviceLambdaManglingNumber();
+          return llvm::None;
+        });
+  case TargetCXXABI::Microsoft:
+    return MicrosoftMangleContext::create(*this, getDiagnostics());
+  }
+  llvm_unreachable("Unsupported ABI");
+}
+
 CXXABI::~CXXABI() = default;
 
 size_t ASTContext::getSideTableAllocatedMemory() const {
@@ -11648,3 +11675,89 @@ StringRef ASTContext::getCUIDHash() const {
   CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true);
   return CUIDHash;
 }
+
+// Get the closest named parent, so we can order the sycl naming decls somewhere
+// that mangling is meaningful.
+static const DeclContext *GetNamedParent(const CXXRecordDecl *RD) {
+  const DeclContext *DC = RD->getDeclContext();
+
+  while (!isa<NamedDecl, TranslationUnitDecl>(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<CXXRecordDecl>(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<const CXXRecordDecl *> &Decls) {
+  static std::unique_ptr<ItaniumMangleContext> 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<CXXRecordDecl>(ND)->getCanonicalDecl();
+  const DeclContext *DC = GetNamedParent(RD);
+
+  auto Itr = SYCLKernelNamingTypes.find(DC);
+  assert(Itr != SYCLKernelNamingTypes.end() && "Not a valid DeclContext?");
+
+  const llvm::SmallPtrSet<const CXXRecordDecl *, 4> &Set = Itr->getSecond();
+
+  llvm::SmallVector<const CXXRecordDecl *> 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<ASTContext &>(*this), RD, Decls);
+
+  llvm::sort(Decls, [](const CXXRecordDecl *LHS, const CXXRecordDecl *RHS) {
+    return LHS->getLambdaManglingNumber() < RHS->getLambdaManglingNumber();
+  });
+
+  return llvm::find(Decls, RD) - Decls.begin();
+}

diff  --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 4026fdc76fd6f..1a5d2f7075fb5 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -556,6 +556,10 @@ ExprDependence clang::computeDependence(RecoveryExpr *E) {
   return D;
 }
 
+ExprDependence clang::computeDependence(SYCLUniqueStableNameExpr *E) {
+  return toExprDependence(E->getTypeSourceInfo()->getType()->getDependence());
+}
+
 ExprDependence clang::computeDependence(PredefinedExpr *E) {
   return toExprDependence(E->getType()->getDependence()) &
          ~ExprDependence::UnexpandedPack;

diff  --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index fe2cdc91fb27c..49e1305beea97 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -504,6 +504,70 @@ SourceLocation DeclRefExpr::getEndLoc() const {
   return getNameInfo().getEndLoc();
 }
 
+SYCLUniqueStableNameExpr::SYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                                   SourceLocation LParen,
+                                                   SourceLocation RParen,
+                                                   QualType ResultTy,
+                                                   TypeSourceInfo *TSI)
+    : Expr(SYCLUniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary),
+      OpLoc(OpLoc), LParen(LParen), RParen(RParen) {
+  setTypeSourceInfo(TSI);
+  setDependence(computeDependence(this));
+}
+
+SYCLUniqueStableNameExpr::SYCLUniqueStableNameExpr(EmptyShell Empty,
+                                                   QualType ResultTy)
+    : Expr(SYCLUniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary) {}
+
+SYCLUniqueStableNameExpr *
+SYCLUniqueStableNameExpr::Create(const ASTContext &Ctx, SourceLocation OpLoc,
+                                 SourceLocation LParen, SourceLocation RParen,
+                                 TypeSourceInfo *TSI) {
+  QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  return new (Ctx)
+      SYCLUniqueStableNameExpr(OpLoc, LParen, RParen, ResultTy, TSI);
+}
+
+SYCLUniqueStableNameExpr *
+SYCLUniqueStableNameExpr::CreateEmpty(const ASTContext &Ctx) {
+  QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  return new (Ctx) SYCLUniqueStableNameExpr(EmptyShell(), ResultTy);
+}
+
+std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context) const {
+  return SYCLUniqueStableNameExpr::ComputeName(Context,
+                                               getTypeSourceInfo()->getType());
+}
+
+std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context,
+                                                  QualType Ty) {
+  auto MangleCallback = [](ASTContext &Ctx,
+                           const NamedDecl *ND) -> llvm::Optional<unsigned> {
+    // 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 
diff erently), 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<MangleContext> Ctx{ItaniumMangleContext::create(
+      Context, Context.getDiagnostics(), MangleCallback)};
+
+  std::string Buffer;
+  Buffer.reserve(128);
+  llvm::raw_string_ostream Out(Buffer);
+  Ctx->mangleTypeName(Ty, Out);
+
+  return Out.str();
+}
+
 PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK,
                                StringLiteral *SL)
     : Expr(PredefinedExprClass, FNTy, VK_LValue, OK_Ordinary) {
@@ -3381,6 +3445,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case SourceLocExprClass:
   case ConceptSpecializationExprClass:
   case RequiresExprClass:
+  case SYCLUniqueStableNameExprClass:
     // These never have a side-effect.
     return false;
 

diff  --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index 0286c730ce4e3..dd7c4a4cbe00b 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -433,6 +433,9 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::CoawaitExprClass:
   case Expr::CoyieldExprClass:
     return ClassifyInternal(Ctx, cast<CoroutineSuspendExpr>(E)->getResumeExpr());
+  case Expr::SYCLUniqueStableNameExprClass:
+    return Cl::CL_PRValue;
+    break;
   }
 
   llvm_unreachable("unhandled expression kind in classification");

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 17bfed5d435fd..17598cdcb8b58 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8669,6 +8669,26 @@ class PointerExprEvaluator
     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<ConstantArrayType>(ArrayTy));
+    return true;
+  }
+
   // FIXME: Missing: @protocol, @selector
 };
 } // end anonymous namespace
@@ -10367,7 +10387,8 @@ namespace {
 
       Result = APValue(APValue::UninitArray(), 0,
                        CAT->getSize().getZExtValue());
-      if (!Result.hasArrayFiller()) return true;
+      if (!Result.hasArrayFiller())
+        return true;
 
       // Zero-initialize all elements.
       LValue Subobject = This;
@@ -15154,6 +15175,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
   case Expr::CoawaitExprClass:
   case Expr::DependentCoawaitExprClass:
   case Expr::CoyieldExprClass:
+  case Expr::SYCLUniqueStableNameExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
 
   case Expr::InitListExprClass: {

diff  --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 2a3a94f58a557..94e404a5f38af 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -125,14 +125,16 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext {
   typedef std::pair<const DeclContext*, IdentifierInfo*> DiscriminatorKeyTy;
   llvm::DenseMap<DiscriminatorKeyTy, unsigned> Discriminator;
   llvm::DenseMap<const NamedDecl*, unsigned> 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 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext {
     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 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext {
     return Name;
   }
 
+  DiscriminatorOverrideTy getDiscriminatorOverride() const override {
+    return DiscriminatorOverride;
+  }
+
   /// @}
 };
 
@@ -1515,7 +1518,9 @@ void CXXNameMangler::mangleUnqualifiedName(GlobalDecl GD,
     // <lambda-sig> ::= <template-param-decl>* <parameter-type>+
     //     # Parameter types or 'v' for 'void'.
     if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(TD)) {
-      if (Record->isLambda() && Record->getLambdaManglingNumber()) {
+      if (Record->isLambda() && (Record->getLambdaManglingNumber() ||
+                                 Context.getDiscriminatorOverride()(
+                                     Context.getASTContext(), Record))) {
         assert(!AdditionalAbiTags &&
                "Lambda type cannot have additional abi tags");
         mangleLambda(Record);
@@ -1953,9 +1958,11 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) {
   // if the host-side CXX ABI has 
diff erent 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<unsigned> 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 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
     Out << "v18co_yield";
     mangleExpression(cast<CoawaitExpr>(E)->getOperand());
     break;
+  case Expr::SYCLUniqueStableNameExprClass: {
+    const auto *USN = cast<SYCLUniqueStableNameExpr>(E);
+    NotPrimaryExpr();
+
+    Out << "u33__builtin_sycl_unique_stable_name";
+    mangleType(USN->getTypeSourceInfo()->getType());
+
+    Out << "E";
+    break;
+  }
   }
 
   if (AsTemplateArg && !IsPrimaryExpr)
@@ -6378,7 +6395,17 @@ void ItaniumMangleContextImpl::mangleLambdaSig(const CXXRecordDecl *Lambda,
   Mangler.mangleLambdaSig(Lambda);
 }
 
+ItaniumMangleContext *ItaniumMangleContext::create(ASTContext &Context,
+                                                   DiagnosticsEngine &Diags) {
+  return new ItaniumMangleContextImpl(
+      Context, Diags,
+      [](ASTContext &, const NamedDecl *) -> llvm::Optional<unsigned> {
+        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);
 }

diff  --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index 95e60f8b19a6a..5d5388880e675 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1164,6 +1164,12 @@ void JSONNodeDumper::VisitDeclRefExpr(const DeclRefExpr *DRE) {
   }
 }
 
+void JSONNodeDumper::VisitSYCLUniqueStableNameExpr(
+    const SYCLUniqueStableNameExpr *E) {
+  JOS.attribute("typeSourceInfo",
+                createQualType(E->getTypeSourceInfo()->getType()));
+}
+
 void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) {
   JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind()));
 }

diff  --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 58afc020f457e..3e275744931a5 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1081,6 +1081,13 @@ void StmtPrinter::VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *Node) {
   OS << "]";
 }
 
+void StmtPrinter::VisitSYCLUniqueStableNameExpr(
+    SYCLUniqueStableNameExpr *Node) {
+  OS << "__builtin_sycl_unique_stable_name(";
+  Node->getTypeSourceInfo()->getType().print(OS, Policy);
+  OS << ")";
+}
+
 void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) {
   OS << PredefinedExpr::getIdentKindName(Node->getIdentKind());
 }

diff  --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index fc1f8a5327244..dc91043b44a67 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -1190,6 +1190,12 @@ void StmtProfiler::VisitDeclRefExpr(const DeclRefExpr *S) {
   }
 }
 
+void StmtProfiler::VisitSYCLUniqueStableNameExpr(
+    const SYCLUniqueStableNameExpr *S) {
+  VisitExpr(S);
+  VisitType(S->getTypeSourceInfo()->getType());
+}
+
 void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) {
   VisitExpr(S);
   ID.AddInteger(S->getIdentKind());

diff  --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index ce5089ff5ecee..cb5b4230a5ab9 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1018,6 +1018,11 @@ void TextNodeDumper::VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node) {
     OS << " isFreeIvar";
 }
 
+void TextNodeDumper::VisitSYCLUniqueStableNameExpr(
+    const SYCLUniqueStableNameExpr *Node) {
+  dumpType(Node->getTypeSourceInfo()->getType());
+}
+
 void TextNodeDumper::VisitPredefinedExpr(const PredefinedExpr *Node) {
   OS << " " << PredefinedExpr::getIdentKindName(Node->getIdentKind());
 }

diff  --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 3c18219c91c9b..dafacaf88502b 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -107,8 +107,9 @@ namespace {
     KEYCXX20      = 0x200000,
     KEYOPENCLCXX  = 0x400000,
     KEYMSCOMPAT   = 0x800000,
+    KEYSYCL       = 0x1000000,
     KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
-    KEYALL = (0xffffff & ~KEYNOMS18 &
+    KEYALL = (0xfffffff & ~KEYNOMS18 &
               ~KEYNOOPENCL) // KEYNOMS18 and KEYNOOPENCL are used to exclude.
   };
 
@@ -155,6 +156,8 @@ static KeywordStatus getKeywordStatus(const LangOptions &LangOpts,
   if (LangOpts.CPlusPlus && (Flags & KEYALLCXX)) return KS_Future;
   if (LangOpts.CPlusPlus && !LangOpts.CPlusPlus20 && (Flags & CHAR8SUPPORT))
     return KS_Future;
+  if (LangOpts.isSYCL() && (Flags & KEYSYCL))
+    return KS_Enabled;
   return KS_Disabled;
 }
 

diff  --git a/clang/lib/CodeGen/CGCUDANV.cpp b/clang/lib/CodeGen/CGCUDANV.cpp
index 995b6a0b5fec6..bfd2c16c12827 100644
--- a/clang/lib/CodeGen/CGCUDANV.cpp
+++ b/clang/lib/CodeGen/CGCUDANV.cpp
@@ -191,12 +191,27 @@ CGNVCUDARuntime::addUnderscoredPrefixToName(StringRef FuncName) const {
   return ((Twine("__cuda") + Twine(FuncName)).str());
 }
 
+static std::unique_ptr<MangleContext> InitDeviceMC(CodeGenModule &CGM) {
+  // If the host and device have 
diff erent 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<MangleContext>(
+        CGM.getContext().createDeviceMangleContext(
+            *CGM.getContext().getAuxTargetInfo()));
+  }
+
+  return std::unique_ptr<MangleContext>(CGM.getContext().createMangleContext(
+      CGM.getContext().getAuxTargetInfo()));
+}
+
 CGNVCUDARuntime::CGNVCUDARuntime(CodeGenModule &CGM)
     : CGCUDARuntime(CGM), Context(CGM.getLLVMContext()),
       TheModule(CGM.getModule()),
       RelocatableDeviceCode(CGM.getLangOpts().GPURelocatableDeviceCode),
-      DeviceMC(CGM.getContext().createMangleContext(
-          CGM.getContext().getAuxTargetInfo())) {
+      DeviceMC(InitDeviceMC(CGM)) {
   CodeGen::CodeGenTypes &Types = CGM.getTypes();
   ASTContext &Ctx = CGM.getContext();
 
@@ -207,14 +222,6 @@ CGNVCUDARuntime::CGNVCUDARuntime(CodeGenModule &CGM)
   CharPtrTy = llvm::PointerType::getUnqual(Types.ConvertType(Ctx.CharTy));
   VoidPtrTy = cast<llvm::PointerType>(Types.ConvertType(Ctx.VoidPtrTy));
   VoidPtrPtrTy = VoidPtrTy->getPointerTo();
-  if (CGM.getContext().getAuxTargetInfo()) {
-    // If the host and device have 
diff erent C++ ABIs, mark it as the device
-    // mangle context so that the mangling needs to retrieve the additonal
-    // device lambda mangling number instead of the regular host one.
-    DeviceMC->setDeviceMangleContext(
-        CGM.getContext().getTargetInfo().getCXXABI().isMicrosoft() &&
-        CGM.getContext().getAuxTargetInfo()->getCXXABI().isItaniumFamily());
-  }
 }
 
 llvm::FunctionCallee CGNVCUDARuntime::getSetupArgumentFn() const {

diff  --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 89ab9abc7dadb..d67d6abe93c99 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -486,6 +486,8 @@ class ScalarExprEmitter
     return CGF.EmitPseudoObjectRValue(E).getScalarVal();
   }
 
+  Value *VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E);
+
   Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) {
     if (E->isGLValue())
       return EmitLoadOfLValue(CGF.getOrCreateOpaqueLValueMapping(E),
@@ -1581,6 +1583,25 @@ Value *ScalarExprEmitter::VisitExpr(Expr *E) {
   return llvm::UndefValue::get(CGF.ConvertType(E->getType()));
 }
 
+Value *
+ScalarExprEmitter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
+  ASTContext &Context = CGF.getContext();
+  llvm::Optional<LangAS> GlobalAS =
+      Context.getTargetInfo().getConstantAddressSpace();
+  llvm::Constant *GlobalConstStr = Builder.CreateGlobalStringPtr(
+      E->ComputeName(Context), "__usn_str",
+      static_cast<unsigned>(GlobalAS.getValueOr(LangAS::Default)));
+
+  unsigned ExprAS = Context.getTargetAddressSpace(E->getType());
+
+  if (GlobalConstStr->getType()->getPointerAddressSpace() == ExprAS)
+    return GlobalConstStr;
+
+  llvm::Type *EltTy = GlobalConstStr->getType()->getPointerElementType();
+  llvm::PointerType *NewPtrTy = llvm::PointerType::get(EltTy, ExprAS);
+  return Builder.CreateAddrSpaceCast(GlobalConstStr, NewPtrTy, "usn_addr_cast");
+}
+
 Value *ScalarExprEmitter::VisitShuffleVectorExpr(ShuffleVectorExpr *E) {
   // Vector Mask Case
   if (E->getNumSubExprs() == 2) {

diff  --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index c2b47f6375b84..22f3b7624c45b 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1469,6 +1469,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
   case tok::kw_this:
     Res = ParseCXXThis();
     break;
+  case tok::kw___builtin_sycl_unique_stable_name:
+    Res = ParseSYCLUniqueStableNameExpression();
+    break;
 
   case tok::annot_typename:
     if (isStartOfObjCClassMessageMissingOpenBracket()) {
@@ -2324,6 +2327,33 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
   return Operand;
 }
 
+/// Parse a __builtin_sycl_unique_stable_name expression.  Accepts a type-id as
+/// a parameter.
+ExprResult Parser::ParseSYCLUniqueStableNameExpression() {
+  assert(Tok.is(tok::kw___builtin_sycl_unique_stable_name) &&
+         "Not __bulitin_sycl_unique_stable_name");
+
+  SourceLocation OpLoc = ConsumeToken();
+  BalancedDelimiterTracker T(*this, tok::l_paren);
+
+  // __builtin_sycl_unique_stable_name expressions are always parenthesized.
+  if (T.expectAndConsume(diag::err_expected_lparen_after,
+                         "__builtin_sycl_unique_stable_name"))
+    return ExprError();
+
+  TypeResult Ty = ParseTypeName();
+
+  if (Ty.isInvalid()) {
+    T.skipToEnd();
+    return ExprError();
+  }
+
+  if (T.consumeClose())
+    return ExprError();
+
+  return Actions.ActOnSYCLUniqueStableNameExpr(OpLoc, T.getOpenLocation(),
+                                               T.getCloseLocation(), Ty.get());
+}
 
 /// Parse a sizeof or alignof expression.
 ///

diff  --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index f0b9e6a5bc3ba..fd6a4940c174f 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1575,6 +1575,8 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     return mergeCanThrow(CT, canThrow(TS->getTryBody()));
   }
 
+  case Stmt::SYCLUniqueStableNameExprClass:
+    return CT_Cannot;
   case Stmt::NoStmtClass:
     llvm_unreachable("Invalid class for statement");
   }

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 5361eb37fafce..02421cbc4317d 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -3506,6 +3506,28 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc,
   return PredefinedExpr::Create(Context, Loc, ResTy, IK, SL);
 }
 
+ExprResult Sema::BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                               SourceLocation LParen,
+                                               SourceLocation RParen,
+                                               TypeSourceInfo *TSI) {
+  return SYCLUniqueStableNameExpr::Create(Context, OpLoc, LParen, RParen, TSI);
+}
+
+ExprResult Sema::ActOnSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                               SourceLocation LParen,
+                                               SourceLocation RParen,
+                                               ParsedType ParsedTy) {
+  TypeSourceInfo *TSI = nullptr;
+  QualType Ty = GetTypeFromParser(ParsedTy, &TSI);
+
+  if (Ty.isNull())
+    return ExprError();
+  if (!TSI)
+    TSI = Context.getTrivialTypeSourceInfo(Ty, LParen);
+
+  return BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
+}
+
 ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) {
   PredefinedExpr::IdentKind IK;
 

diff  --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 1c07732fe8aa4..cf5e20f7b6c1e 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -461,11 +461,15 @@ void Sema::handleLambdaNumbering(
   std::tie(MCtx, ManglingContextDecl) =
       getCurrentMangleNumberContext(Class->getDeclContext());
   bool HasKnownInternalLinkage = false;
-  if (!MCtx && getLangOpts().CUDA) {
+  if (!MCtx && (getLangOpts().CUDA || getLangOpts().SYCLIsDevice ||
+                getLangOpts().SYCLIsHost)) {
     // Force lambda numbering in CUDA/HIP as we need to name lambdas following
     // ODR. Both device- and host-compilation need to have a consistent naming
     // on kernel functions. As lambdas are potential part of these `__global__`
     // function names, they needs numbering following ODR.
+    // Also force for SYCL, since we need this for the
+    // __builtin_sycl_unique_stable_name implementation, which depends on lambda
+    // mangling.
     MCtx = getMangleNumberingContext(Class, ManglingContextDecl);
     assert(MCtx && "Retrieving mangle numbering context failed!");
     HasKnownInternalLinkage = true;

diff  --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index af35052ee1e3e..3b48a53efc0d1 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -8,6 +8,7 @@
 // This implements Semantic Analysis for SYCL constructs.
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/Mangle.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaDiagnostic.h"
 
@@ -47,3 +48,35 @@ bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) {
   return DiagKind != SemaDiagnosticBuilder::K_Immediate &&
          DiagKind != SemaDiagnosticBuilder::K_ImmediateWithCallStack;
 }
+
+// The SYCL kernel's 'object type' used for diagnostics and naming/mangling is
+// the first parameter to a sycl_kernel labeled function template. In SYCL1.2.1,
+// this was passed by value, and in SYCL2020, it is passed by reference.
+static QualType GetSYCLKernelObjectType(const FunctionDecl *KernelCaller) {
+  assert(KernelCaller->getNumParams() > 0 && "Insufficient kernel parameters");
+  QualType KernelParamTy = KernelCaller->getParamDecl(0)->getType();
+
+  // SYCL 2020 kernels are passed by reference.
+  if (KernelParamTy->isReferenceType())
+    return KernelParamTy->getPointeeType();
+
+  // SYCL 1.2.1
+  return KernelParamTy;
+}
+
+void Sema::AddSYCLKernelLambda(const FunctionDecl *FD) {
+  auto MangleCallback = [](ASTContext &Ctx,
+                           const NamedDecl *ND) -> llvm::Optional<unsigned> {
+    if (const auto *RD = dyn_cast<CXXRecordDecl>(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<MangleContext> Ctx{ItaniumMangleContext::create(
+      Context, Context.getDiagnostics(), MangleCallback)};
+  llvm::raw_null_ostream Out;
+  Ctx->mangleTypeName(Ty, Out);
+}

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index cbd29701496b5..7611700d014bd 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -548,6 +548,35 @@ static void instantiateDependentAMDGPUWavesPerEUAttr(
   S.addAMDGPUWavesPerEUAttr(New, Attr, MinExpr, MaxExpr);
 }
 
+// This doesn't take any template parameters, but we have a custom action that
+// needs to happen when the kernel itself is instantiated. We need to run the
+// ItaniumMangler to mark the names required to name this kernel.
+static void instantiateDependentSYCLKernelAttr(
+    Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
+    const SYCLKernelAttr &Attr, Decl *New) {
+  // Functions cannot be partially specialized, so if we are being instantiated,
+  // we are obviously a complete specialization. Since this attribute is only
+  // valid on function template declarations, we know that this is a full
+  // instantiation of a kernel.
+  S.AddSYCLKernelLambda(cast<FunctionDecl>(New));
+
+  // Evaluate whether this would change any of the already evaluated
+  // __builtin_sycl_unique_stable_name values.
+  for (auto &Itr : S.Context.SYCLUniqueStableNameEvaluatedValues) {
+    const std::string &CurName = Itr.first->ComputeName(S.Context);
+    if (Itr.second != CurName) {
+      S.Diag(New->getLocation(),
+             diag::err_kernel_invalidates_sycl_unique_stable_name);
+      S.Diag(Itr.first->getLocation(),
+             diag::note_sycl_unique_stable_name_evaluated_here);
+      // Update this so future diagnostics work correctly.
+      Itr.second = CurName;
+    }
+  }
+
+  New->addAttr(Attr.clone(S.getASTContext()));
+}
+
 /// Determine whether the attribute A might be relevent to the declaration D.
 /// If not, we can skip instantiating it. The attribute may or may not have
 /// been instantiated yet.
@@ -723,6 +752,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
       continue;
     }
 
+    if (auto *A = dyn_cast<SYCLKernelAttr>(TmplAttr)) {
+      instantiateDependentSYCLKernelAttr(*this, TemplateArgs, *A, New);
+      continue;
+    }
+
     assert(!TmplAttr->isPackExpansion());
     if (TmplAttr->isLateParsed() && LateAttrs) {
       // Late parsed attributes must be instantiated and attached after the

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e4bbc1cf4df63..80b798702fdd3 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -2399,6 +2399,19 @@ class TreeTransform {
     return SEHFinallyStmt::Create(getSema().getASTContext(), Loc, Block);
   }
 
+  ExprResult RebuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                             SourceLocation LParen,
+                                             SourceLocation RParen, Expr *E) {
+    return getSema().BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, E);
+  }
+
+  ExprResult RebuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                             SourceLocation LParen,
+                                             SourceLocation RParen,
+                                             TypeSourceInfo *TSI) {
+    return getSema().BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
+  }
+
   /// Build a new predefined expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -10172,6 +10185,24 @@ TreeTransform<Derived>::TransformConstantExpr(ConstantExpr *E) {
   return TransformExpr(E->getSubExpr());
 }
 
+template <typename Derived>
+ExprResult TreeTransform<Derived>::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<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformPredefinedExpr(PredefinedExpr *E) {

diff  --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 04adbabc9b89b..c1f66709c8515 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -581,6 +581,16 @@ void ASTStmtReader::VisitConstantExpr(ConstantExpr *E) {
   E->setSubExpr(Record.readSubExpr());
 }
 
+void ASTStmtReader::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
+  VisitExpr(E);
+
+  E->setLocation(readSourceLocation());
+  E->setLParenLocation(readSourceLocation());
+  E->setRParenLocation(readSourceLocation());
+
+  E->setTypeSourceInfo(Record.readTypeSourceInfo());
+}
+
 void ASTStmtReader::VisitPredefinedExpr(PredefinedExpr *E) {
   VisitExpr(E);
   bool HasFunctionName = Record.readInt();
@@ -2802,6 +2812,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
                        /*StorageKind=*/Record[ASTStmtReader::NumExprFields]));
       break;
 
+    case EXPR_SYCL_UNIQUE_STABLE_NAME:
+      S = SYCLUniqueStableNameExpr::CreateEmpty(Context);
+      break;
+
     case EXPR_PREDEFINED:
       S = PredefinedExpr::CreateEmpty(
           Context,

diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 017de0db357b5..10be9b894c277 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -580,6 +580,17 @@ void ASTStmtWriter::VisitConstantExpr(ConstantExpr *E) {
   Code = serialization::EXPR_CONSTANT;
 }
 
+void ASTStmtWriter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
+  VisitExpr(E);
+
+  Record.AddSourceLocation(E->getLocation());
+  Record.AddSourceLocation(E->getLParenLocation());
+  Record.AddSourceLocation(E->getRParenLocation());
+  Record.AddTypeSourceInfo(E->getTypeSourceInfo());
+
+  Code = serialization::EXPR_SYCL_UNIQUE_STABLE_NAME;
+}
+
 void ASTStmtWriter::VisitPredefinedExpr(PredefinedExpr *E) {
   VisitExpr(E);
 

diff  --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index ccfc84bd7a58a..d40161f289f88 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1419,6 +1419,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::OMPArraySectionExprClass:
     case Stmt::OMPArrayShapingExprClass:
     case Stmt::OMPIteratorExprClass:
+    case Stmt::SYCLUniqueStableNameExprClass:
     case Stmt::TypeTraitExprClass: {
       Bldr.takeNodes(Pred);
       ExplodedNodeSet preVisit;

diff  --git a/clang/test/AST/ast-print-sycl-unique-stable-name.cpp b/clang/test/AST/ast-print-sycl-unique-stable-name.cpp
new file mode 100644
index 0000000000000..3f49ea9ee733c
--- /dev/null
+++ b/clang/test/AST/ast-print-sycl-unique-stable-name.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -ast-print -fsycl-is-device %s -o - -triple spir64-sycldevice | FileCheck %s
+
+template <typename T>
+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 <typename T> 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>(Type t)
+// CHECK: __builtin_sycl_unique_stable_name(Type);
+// CHECK: __builtin_sycl_unique_stable_name(typename Type::type);
+// CHECK: __builtin_sycl_unique_stable_name(decltype(t.foo()));

diff  --git a/clang/test/CodeGenSYCL/unique_stable_name.cpp b/clang/test/CodeGenSYCL/unique_stable_name.cpp
new file mode 100644
index 0000000000000..380ddaeadc69c
--- /dev/null
+++ b/clang/test/CodeGenSYCL/unique_stable_name.cpp
@@ -0,0 +1,159 @@
+// RUN: %clang_cc1 -triple spir64-unknown-unknown-sycldevice -fsycl-is-device -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s
+// CHECK: @[[LAMBDA_KERNEL3:[^\w]+]] = private unnamed_addr constant [[LAMBDA_K3_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ4mainEUlPZ4mainEUlvE10000_E10000_\00"
+// CHECK: @[[INT1:[^\w]+]] = private unnamed_addr constant [[INT_SIZE:\[[0-9]+ x i8\]]] c"_ZTSi\00"
+// CHECK: @[[STRING:[^\w]+]] = private unnamed_addr constant [[STRING_SIZE:\[[0-9]+ x i8\]]] c"_ZTSAppL_ZZ4mainE1jE_i\00",
+// CHECK: @[[INT2:[^\w]+]] = private unnamed_addr constant [[INT_SIZE]] c"_ZTSi\00"
+// CHECK: @[[LAMBDA_X:[^\w]+]] = private unnamed_addr constant [[LAMBDA_X_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE_\00"
+// CHECK: @[[MACRO_X:[^\w]+]] = private unnamed_addr constant [[MACRO_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE0_\00"
+// CHECK: @[[MACRO_Y:[^\w]+]] =  private unnamed_addr constant [[MACRO_SIZE]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE1_\00"
+// CHECK: @{{.*}} = private unnamed_addr constant [36 x i8] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE2_\00", align 1
+// CHECK: @{{.*}} = private unnamed_addr constant [36 x i8] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE3_\00", align 1
+// CHECK: @[[MACRO_MACRO_X:[^\w]+]] = private unnamed_addr constant [[MACRO_MACRO_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE4_\00"
+// CHECK: @[[MACRO_MACRO_Y:[^\w]+]] = private unnamed_addr constant [[MACRO_MACRO_SIZE]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE5_\00"
+// CHECK: @[[INT3:[^\w]+]] = private unnamed_addr constant [[INT_SIZE]] c"_ZTSi\00"
+// CHECK: @[[LAMBDA:[^\w]+]] = private unnamed_addr constant [[LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZZ4mainENKUlvE10001_clEvEUlvE_\00"
+// CHECK: @[[LAMBDA_IN_DEP_INT:[^\w]+]] = private unnamed_addr constant [[DEP_INT_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ28lambda_in_dependent_functionIiEvvEUlvE_\00",
+// CHECK: @[[LAMBDA_IN_DEP_X:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ28lambda_in_dependent_functionIZZ4mainENKUlvE10001_clEvEUlvE_EvvEUlvE_\00",
+// CHECK: @[[LAMBDA_NO_DEP:[^\w]+]] = private unnamed_addr constant [[NO_DEP_LAMBDA_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ13lambda_no_depIidEvT_T0_EUlidE_\00",
+// CHECK: @[[LAMBDA_TWO_DEP:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA1_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ14lambda_two_depIZZ4mainENKUlvE10001_clEvEUliE_ZZ4mainENKS0_clEvEUldE_EvvEUlvE_\00",
+// CHECK: @[[LAMBDA_TWO_DEP2:[^\w]+]] = private unnamed_addr constant [[DEP_LAMBDA2_SIZE:\[[0-9]+ x i8\]]] c"_ZTSZ14lambda_two_depIZZ4mainENKUlvE10001_clEvEUldE_ZZ4mainENKS0_clEvEUliE_EvvEUlvE_\00",
+
+extern "C" void puts(const char *) {}
+
+template <typename T>
+void template_param() {
+  puts(__builtin_sycl_unique_stable_name(T));
+}
+
+template <typename T>
+void lambda_in_dependent_function() {
+  auto y = [] {};
+  puts(__builtin_sycl_unique_stable_name(decltype(y)));
+}
+
+template <typename Tw, typename Tz>
+void lambda_two_dep() {
+  auto z = [] {};
+  puts(__builtin_sycl_unique_stable_name(decltype(z)));
+}
+
+template <typename Tw, typename Tz>
+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 <typename Ty>
+auto func() -> decltype(__builtin_sycl_unique_stable_name(decltype(Ty::str)));
+
+struct Derp {
+  static constexpr const char str[] = "derp derp derp";
+};
+
+template <typename KernelName, typename KernelType>
+[[clang::sycl_kernel]] void kernel_single_task(KernelType kernelFunc) {
+  kernelFunc();
+}
+
+int main() {
+  kernel_single_task<class kernel2>(func<Derp>);
+  // 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<class kernel3>(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<class kernel>(
+      []() {
+        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<int>();
+        // CHECK: call spir_func void @_Z14template_paramIiEvv
+
+        template_param<decltype(x)>();
+        // CHECK: call spir_func void @_Z14template_paramIZZ4mainENKUlvE0_clEvEUlvE_Evv
+
+        lambda_in_dependent_function<int>();
+        // CHECK: call spir_func void @_Z28lambda_in_dependent_functionIiEvv
+
+        lambda_in_dependent_function<decltype(x)>();
+        // CHECK: call spir_func void @_Z28lambda_in_dependent_functionIZZ4mainENKUlvE0_clEvEUlvE_Evv
+
+        lambda_no_dep<int, double>(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<decltype(y), decltype(z)>();
+        // CHECK: call spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUliE_ZZ4mainENKS0_clEvEUldE_Evv
+
+        lambda_two_dep<decltype(z), decltype(y)>();
+        // CHECK: call spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUldE_ZZ4mainENKS0_clEvEUliE_Evv
+      });
+}
+
+// CHECK: define linkonce_odr spir_func void @_Z14template_paramIiEvv
+// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[INT_SIZE]], [[INT_SIZE]]* @[[INT3]], i32 0, i32 0) to i8 addrspace(4)*))
+
+// CHECK: define internal spir_func void @_Z14template_paramIZZ4mainENKUlvE0_clEvEUlvE_Evv
+// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[LAMBDA_SIZE]], [[LAMBDA_SIZE]]* @[[LAMBDA]], i32 0, i32 0) to i8 addrspace(4)*))
+
+// CHECK: define linkonce_odr spir_func void @_Z28lambda_in_dependent_functionIiEvv
+// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_INT_SIZE]], [[DEP_INT_SIZE]]* @[[LAMBDA_IN_DEP_INT]], i32 0, i32 0) to i8 addrspace(4)*))
+
+// CHECK: define internal spir_func void @_Z28lambda_in_dependent_functionIZZ4mainENKUlvE0_clEvEUlvE_Evv
+// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA_SIZE]], [[DEP_LAMBDA_SIZE]]* @[[LAMBDA_IN_DEP_X]], i32 0, i32 0) to i8 addrspace(4)*))
+
+// CHECK: define linkonce_odr spir_func void @_Z13lambda_no_depIidEvT_T0_(i32 %a, double %b)
+// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[NO_DEP_LAMBDA_SIZE]], [[NO_DEP_LAMBDA_SIZE]]* @[[LAMBDA_NO_DEP]], i32 0, i32 0) to i8 addrspace(4)*))
+
+// CHECK: define internal spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUliE_ZZ4mainENKS0_clEvEUldE_Evv
+// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA1_SIZE]], [[DEP_LAMBDA1_SIZE]]* @[[LAMBDA_TWO_DEP]], i32 0, i32 0) to i8 addrspace(4)*))
+
+// CHECK: define internal spir_func void @_Z14lambda_two_depIZZ4mainENKUlvE0_clEvEUldE_ZZ4mainENKS0_clEvEUliE_Evv
+// CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[DEP_LAMBDA2_SIZE]], [[DEP_LAMBDA2_SIZE]]* @[[LAMBDA_TWO_DEP2]], i32 0, i32 0) to i8 addrspace(4)*))

diff  --git a/clang/test/ParserSYCL/unique_stable_name.cpp b/clang/test/ParserSYCL/unique_stable_name.cpp
new file mode 100644
index 0000000000000..aa50f54ab2137
--- /dev/null
+++ b/clang/test/ParserSYCL/unique_stable_name.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify -Wno-unused %s
+
+namespace NS {
+using good = double;
+}
+
+void f(int var) {
+  // expected-error at +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 at +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 at +2{{expected ')'}}
+  // expected-note at +1{{to match this '('}}
+  __builtin_sycl_unique_stable_name(int; // Missing paren before semicolon
+
+  // expected-error at +2{{expected ')'}}
+  // expected-note at +1{{to match this '('}}
+  __builtin_sycl_unique_stable_name(int, float); // Missing paren before comma
+
+  // expected-error at +1{{unknown type name 'var'}}
+  __builtin_sycl_unique_stable_name(var);
+  __builtin_sycl_unique_stable_name(NS::good);
+
+  // expected-error at +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 <typename T>
+void f2() {
+  __builtin_sycl_unique_stable_name(typename T::good_type);
+}
+
+struct S {
+  class good_type {};
+};
+
+void use() {
+  f2<S>();
+}

diff  --git a/clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp b/clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp
new file mode 100644
index 0000000000000..5f2a2e3313df5
--- /dev/null
+++ b/clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=notsycl -Wno-unused %s
+// RUN: %clang_cc1 -fsyntax-only -fsycl-is-host -verify=sycl -Wno-unused %s
+// RUN: %clang_cc1 -fsyntax-only -fsycl-is-device -verify=sycl -Wno-unused %s
+
+// sycl-no-diagnostics
+void foo() {
+  // notsycl-error at +1{{expected '(' for function-style cast or type construction}}
+  __builtin_sycl_unique_stable_name(int);
+}

diff  --git a/clang/test/SemaSYCL/unique_stable_name.cpp b/clang/test/SemaSYCL/unique_stable_name.cpp
new file mode 100644
index 0000000000000..f86000fca4e23
--- /dev/null
+++ b/clang/test/SemaSYCL/unique_stable_name.cpp
@@ -0,0 +1,215 @@
+// RUN: %clang_cc1 %s -std=c++17 -triple x86_64-pc-windows-msvc -fsycl-is-device -verify -fsyntax-only -Wno-unused
+// RUN: %clang_cc1 %s -std=c++17 -triple x86_64-linux-gnu -fsycl-is-device -verify -fsyntax-only -Wno-unused
+
+template <typename KernelName, typename KernelType>
+[[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 <typename Func>
+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 at +1{{in instantiation of function template specialization}}
+  kernel_single_task<class kernel1>(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 
diff erent 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 <typename Func>
+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 at +1{{in instantiation of function template specialization}}
+  kernel_single_task<class kernel2>([]() {});
+}
+
+void callkernel2() {
+  kernel2func([]() {}); // #kernel2func_call
+}
+
+template <template <typename> typename Outer, typename Inner>
+struct S {
+  void operator()() const;
+};
+
+template <typename Ty>
+struct Tangerine {};
+
+template <typename Func>
+void kernel3_4func(const Func &F) {
+  // Test that passing the same lambda to two kernels does not cause an error
+  // because the kernel uses do not interfere with each other or invalidate
+  // the stable name in any way.
+  kernel_single_task<class kernel3>(F);
+  kernel_single_task<class kernel4>(F);
+  // Using the same functor twice should be fine
+}
+
+// kernel3 and kernel4 - expect no errors
+void callkernel3_4() {
+  kernel3_4func([]() {});
+}
+
+template <typename T>
+static constexpr const char *output1 = __builtin_sycl_unique_stable_name(T);
+
+#define MACRO()                      \
+  auto l14 = []() { return 1; };     \
+  constexpr const char *l14_output = \
+      __builtin_sycl_unique_stable_name(decltype(l14));
+
+int main() {
+
+  // kernel5 - expect no error
+  // Test that passing the lambda to the unique stable name builtin and then
+  // using the lambda in a way that does not  contribute to the kernel name
+  // does not cause an error because the  stable name is not invalidated in
+  // this situation.
+  auto l5 = []() {};
+  constexpr const char *l5_output =
+      __builtin_sycl_unique_stable_name(decltype(l5));
+  kernel_single_task<class kernel5>(
+      [=]() { l5(); }); // Used in the kernel, but not the kernel name itself
+
+  // kernel6 - expect error
+  // Test that passing the lambda to the unique stable name builtin and then
+  // using the same lambda in the naming of a kernel causes a diagnostic on the
+  // kernel use due to the change in results to the stable name.
+  auto l6 = []() { return 1; };
+  constexpr const char *l6_output =
+      __builtin_sycl_unique_stable_name(decltype(l6)); // #USN_l6
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
+  // expected-note@#USN_l6{{'__builtin_sycl_unique_stable_name' evaluated here}}
+  // expected-note at +1{{in instantiation of function template specialization}}
+  kernel_single_task<class kernel6>(l6); // Used in the kernel name after builtin
+
+  // kernel7 - expect error
+  // Same as kernel11 (below) except make the lambda part of naming the kernel.
+  // Test that passing a lambda to the unique stable name builtin and then
+  // passing a second lambda to the kernel throws an error because the first
+  // lambda is included in the signature of the second lambda, hence it changes
+  // the mangling of the kernel.
+  auto l7 = []() { return 1; };
+  auto l8 = [](decltype(l7) *derp = nullptr) { return 2; };
+  constexpr const char *l7_output =
+      __builtin_sycl_unique_stable_name(decltype(l7)); // #USN_l7
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
+  // expected-note@#USN_l7{{'__builtin_sycl_unique_stable_name' evaluated here}}
+  // expected-note at +1{{in instantiation of function template specialization}}
+  kernel_single_task<class kernel7>(l8);
+
+  // kernel8 and kernel9 - expect error
+  // Tests that passing a lambda to the unique stable name builtin and passing it
+  // to a kernel called with an if constexpr branch causes a diagnostic on the
+  // kernel9 use due to the change in the results to the stable name. This happens
+  // even though the use of kernel9 happens in the false branch of a constexpr if
+  // because both the true and the false branches cause the instantiation of
+  // kernel_single_task.
+  auto l9 = []() { return 1; };
+  auto l10 = []() { return 2; };
+  constexpr const char *l10_output =
+      __builtin_sycl_unique_stable_name(decltype(l10)); // #USN_l10
+  if constexpr (1) {
+    kernel_single_task<class kernel8>(l9);
+  } else {
+    // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
+    // expected-note@#USN_l10{{'__builtin_sycl_unique_stable_name' evaluated here}}
+    // expected-note at +1{{in instantiation of function template specialization}}
+    kernel_single_task<class kernel9>(l10);
+  }
+
+  // kernel11 - expect no error
+  // Test that passing a lambda to the unique stable name builtin and then
+  // passing a second lambda capturing the first one to the kernel does not
+  // throw an error because the first lambda is not involved in naming the
+  // kernel i.e., the mangling does not change.
+  auto l11 = []() { return 1; };
+  auto l12 = [l11]() { return 2; };
+  constexpr const char *l11_output =
+      __builtin_sycl_unique_stable_name(decltype(l11));
+  kernel_single_task<class kernel11>(l12);
+
+  // kernel12 - expect an error
+  // Test that passing a lambda to the unique stable name builtin and then
+  // passing it to the kernel as a template template parameter causes a
+  // diagnostic on the kernel use due to template template parameter being
+  // involved in the mangling of the kernel name.
+  auto l13 = []() { return 1; };
+  constexpr const char *l13_output =
+      __builtin_sycl_unique_stable_name(decltype(l13)); // #USN_l13
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
+  // expected-note@#USN_l13{{'__builtin_sycl_unique_stable_name' evaluated here}}
+  // expected-note at +1{{in instantiation of function template specialization}}
+  kernel_single_task<class kernel12>(S<Tangerine, decltype(l13)>{});
+
+  // kernel13 - expect an error
+  // Test that passing a lambda to the unique stable name builtin within a macro
+  // and then calling the macro within the kernel causes an error on the kernel
+  // and diagnoses in all the expected places despite the use of a macro.
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
+  // expected-note@#USN_MACRO{{'__builtin_sycl_unique_stable_name' evaluated here}}
+  // expected-note at +1{{in instantiation of function template specialization}}
+  kernel_single_task<class kernel13>(
+      []() {
+        MACRO(); // #USN_MACRO
+      });
+}
+
+namespace NS {}
+
+void f() {
+  // expected-error at +1{{unknown type name 'bad_var'}}
+  __builtin_sycl_unique_stable_name(bad_var);
+  // expected-error at +1{{use of undeclared identifier 'bad'}}
+  __builtin_sycl_unique_stable_name(bad::type);
+  // expected-error at +1{{no type named 'still_bad' in namespace 'NS'}}
+  __builtin_sycl_unique_stable_name(NS::still_bad);
+
+  // FIXME: warning about side-effects in an unevaluated context expected, but
+  // none currently emitted.
+  int i = 0;
+  __builtin_sycl_unique_stable_name(decltype(i++));
+
+  // Tests that use within a VLA does not diagnose as a side-effecting use in
+  // an unevaluated context because the use within a VLA extent forces
+  // evaluation.
+  int j = 55;
+  __builtin_sycl_unique_stable_name(int[++j]); // no warning expected
+}
+
+template <typename T>
+void f2() {
+  // expected-error at +1{{no type named 'bad_val' in 'St'}}
+  __builtin_sycl_unique_stable_name(typename T::bad_val);
+  // expected-error at +1{{no type named 'bad_type' in 'St'}}
+  __builtin_sycl_unique_stable_name(typename T::bad_type);
+}
+
+struct St {};
+
+void use() {
+  // expected-note at +1{{in instantiation of}}
+  f2<St>();
+}

diff  --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index b4822d3b1e934..7c6b7bcd05937 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -336,6 +336,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   case Stmt::ObjCBoxedExprClass:
   case Stmt::ObjCSubscriptRefExprClass:
   case Stmt::RecoveryExprClass:
+  case Stmt::SYCLUniqueStableNameExprClass:
     K = CXCursor_UnexposedExpr;
     break;
 


        


More information about the cfe-commits mailing list