erichkeane created this revision.
erichkeane added reviewers: aaron.ballman, rjmccall.
Herald added subscribers: Naghasan, dexonsmith, martong, Anastasia, mgrang.
erichkeane requested review of this revision.

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).


https://reviews.llvm.org/D103112

Files:
  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/Attr.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  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/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/test/CodeGenSYCL/unique_stable_name.cpp
  clang/test/ParserSYCL/unique_stable_name.cpp
  clang/test/SemaSYCL/unique_stable_name.cpp
  clang/tools/libclang/CXCursor.cpp

Index: clang/tools/libclang/CXCursor.cpp
===================================================================
--- clang/tools/libclang/CXCursor.cpp
+++ clang/tools/libclang/CXCursor.cpp
@@ -336,6 +336,7 @@
   case Stmt::ObjCBoxedExprClass:
   case Stmt::ObjCSubscriptRefExprClass:
   case Stmt::RecoveryExprClass:
+  case Stmt::UniqueStableNameExprClass:
     K = CXCursor_UnexposedExpr;
     break;
 
Index: clang/test/SemaSYCL/unique_stable_name.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaSYCL/unique_stable_name.cpp
@@ -0,0 +1,208 @@
+// 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_unique_stable_name(F1); // #USN_F1
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_unique_stable_name'}}
+  // expected-note@#kernel1func_call{{in instantiation of function template specialization}}
+  // expected-note@#USN_F1{{'__builtin_unique_stable_name' evaluated here}}
+  // expected-note@+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 different lambda to the kernel still throws an error because
+// the calling context is part of naming the kernel. Even though the given
+// function (F2) is not passed to the kernel, its mangling changes due to
+// kernel call with the unrelated lambda.
+template <typename Func>
+void kernel2func(const Func &F2) {
+  constexpr const char *F2_output = __builtin_unique_stable_name(F2); // #USN_F2
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_unique_stable_name'}}
+  // expected-note@#kernel2func_call{{in instantiation of function template specialization}}
+  // expected-note@#USN_F2{{'__builtin_unique_stable_name' evaluated here}}
+  // expected-note@+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_unique_stable_name(T);
+
+#define MACRO()                  \
+  auto l14 = []() { return 1; }; \
+  constexpr const char *l14_output = __builtin_unique_stable_name(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_unique_stable_name(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_unique_stable_name(l6); // #USN_l6
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_unique_stable_name'}}
+  // expected-note@#USN_l6{{'__builtin_unique_stable_name' evaluated here}}
+  // expected-note@+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_unique_stable_name(l7); // #USN_l7
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_unique_stable_name'}}
+  // expected-note@#USN_l7{{'__builtin_unique_stable_name' evaluated here}}
+  // expected-note@+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_unique_stable_name(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_unique_stable_name'}}
+    // expected-note@#USN_l10{{'__builtin_unique_stable_name' evaluated here}}
+    // expected-note@+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_unique_stable_name(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_unique_stable_name(l13); // #USN_l13
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_unique_stable_name'}}
+  // expected-note@#USN_l13{{'__builtin_unique_stable_name' evaluated here}}
+  // expected-note@+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_unique_stable_name'}}
+  // expected-note@#USN_MACRO{{'__builtin_unique_stable_name' evaluated here}}
+  // expected-note@+1{{in instantiation of function template specialization}}
+  kernel_single_task<class kernel13>(
+      []() {
+        MACRO(); // #USN_MACRO
+      });
+}
+
+namespace NS {}
+
+void f() {
+  // 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);
+
+  // FIXME: warning about side-effects in an unevaluated context expected, but
+  // none currently emitted.
+  int i = 0;
+  __builtin_unique_stable_name(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_unique_stable_name(int[++j]); // no warning expected
+}
+
+template <typename T>
+void f2() {
+  // expected-error@+1{{no member named 'bad_val' in 'St'}}
+  __builtin_unique_stable_name(T::bad_val);
+  // expected-error@+1{{no type named 'bad_type' in 'St'}}
+  __builtin_unique_stable_name(typename T::bad_type);
+}
+
+struct St {};
+
+void use() {
+  // expected-note@+1{{in instantiation of}}
+  f2<St>();
+}
Index: clang/test/ParserSYCL/unique_stable_name.cpp
===================================================================
--- /dev/null
+++ clang/test/ParserSYCL/unique_stable_name.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused %s
+
+namespace NS {
+int good = 55;
+}
+
+void f(int var) {
+  // expected-error@+1{{expected '(' after '__builtin_unique_stable_name'}}
+  __builtin_unique_stable_name int; // Correct usage is __builtin_unique_stable_name(int);
+
+  // expected-error@+1{{expected '(' after '__builtin_unique_stable_name'}}
+  __builtin_unique_stable_name {int}; // Correct usage is __builtin_unique_stable_name(int);
+
+  // expected-error@+2{{expected ')'}}
+  // expected-note@+1{{to match this '('}}
+  __builtin_unique_stable_name(int; // Missing paren before semicolon
+
+  // expected-error@+2{{expected ')'}}
+  // expected-note@+1{{to match this '('}}
+  __builtin_unique_stable_name(int, float); // Missing paren before comma
+
+  __builtin_unique_stable_name(var);
+  __builtin_unique_stable_name(NS::good);
+
+  // expected-error@+1{{expected expression}}
+  __builtin_unique_stable_name(for (int i = 0; i < 10; ++i) {})
+  __builtin_unique_stable_name({
+    (for (int i = 0; i < 10; ++i){})})
+}
+
+template <typename T>
+void f2() {
+  __builtin_unique_stable_name(typename T::good_type);
+}
+
+struct S {
+  class good_type {};
+};
+
+void use() {
+  f2<S>();
+}
Index: clang/test/CodeGenSYCL/unique_stable_name.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenSYCL/unique_stable_name.cpp
@@ -0,0 +1,162 @@
+// 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: @[[LAMBDA_Y:[^\w]+]] = private unnamed_addr constant [[LAMBDA_Y_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_unique_stable_name(T));
+}
+
+template <typename T>
+void lambda_in_dependent_function() {
+  auto y = [] {};
+  puts(__builtin_unique_stable_name(y));
+}
+
+template <typename Tw, typename Tz>
+void lambda_two_dep() {
+  auto z = [] {};
+  puts(__builtin_unique_stable_name(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_unique_stable_name(p));
+}
+
+#define DEF_IN_MACRO()                         \
+  auto MACRO_X = []() {};                      \
+  auto MACRO_Y = []() {};                      \
+  puts(__builtin_unique_stable_name(MACRO_X)); \
+  puts(__builtin_unique_stable_name(MACRO_Y));
+
+#define MACRO_CALLS_MACRO() \
+  { DEF_IN_MACRO(); }       \
+  { DEF_IN_MACRO(); }
+
+template <typename Ty>
+auto func() -> decltype(__builtin_unique_stable_name(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)* ()* @_Z4funcI4DerpEDTu20__unique_stable_nameXsrT_3strEEEv)
+
+  auto l1 = []() { return 1; };
+  auto l2 = [](decltype(l1) *l = nullptr) { return 2; };
+  kernel_single_task<class kernel3>(l2);
+  puts(__builtin_unique_stable_name(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_unique_stable_name(str), "_ZTSA7_Kc\0") == 0, "unexpected mangling");
+
+  int i = 0;
+  puts(__builtin_unique_stable_name(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_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)* @_Z4funcI4DerpEDTu20__unique_stable_nameXsrT_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_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_unique_stable_name(x));
+        puts(__builtin_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)*))
+        // CHECK: call spir_func void @puts(i8 addrspace(4)* addrspacecast (i8* getelementptr inbounds ([[LAMBDA_Y_SIZE]], [[LAMBDA_Y_SIZE]]* @[[LAMBDA_Y]], 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)*))
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1419,6 +1419,7 @@
     case Stmt::OMPArraySectionExprClass:
     case Stmt::OMPArrayShapingExprClass:
     case Stmt::OMPIteratorExprClass:
+    case Stmt::UniqueStableNameExprClass:
     case Stmt::TypeTraitExprClass: {
       Bldr.takeNodes(Pred);
       ExplodedNodeSet preVisit;
Index: clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterStmt.cpp
+++ clang/lib/Serialization/ASTWriterStmt.cpp
@@ -580,6 +580,22 @@
   Code = serialization::EXPR_CONSTANT;
 }
 
+void ASTStmtWriter::VisitUniqueStableNameExpr(UniqueStableNameExpr *E) {
+  VisitExpr(E);
+
+  Record.writeBool(E->isExpr());
+  Record.AddSourceLocation(E->getLocation());
+  Record.AddSourceLocation(E->getLParenLocation());
+  Record.AddSourceLocation(E->getRParenLocation());
+
+  if (E->isExpr())
+    Record.AddStmt(E->getExpr());
+  else
+    Record.AddTypeSourceInfo(E->getTypeSourceInfo());
+
+  Code = serialization::EXPR_UNIQUESTABLENAME;
+}
+
 void ASTStmtWriter::VisitPredefinedExpr(PredefinedExpr *E) {
   VisitExpr(E);
 
Index: clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderStmt.cpp
+++ clang/lib/Serialization/ASTReaderStmt.cpp
@@ -581,6 +581,21 @@
   E->setSubExpr(Record.readSubExpr());
 }
 
+void ASTStmtReader::VisitUniqueStableNameExpr(UniqueStableNameExpr *E) {
+  VisitExpr(E);
+
+  bool IsExpr = Record.readBool();
+  assert(E->isExpr() == IsExpr && "Wrong unique-stable-name trailing type");
+  E->setLocation(readSourceLocation());
+  E->setLParenLocation(readSourceLocation());
+  E->setRParenLocation(readSourceLocation());
+
+  if (IsExpr)
+    E->setExpr(Record.readExpr());
+  else
+    E->setTypeSourceInfo(Record.readTypeSourceInfo());
+}
+
 void ASTStmtReader::VisitPredefinedExpr(PredefinedExpr *E) {
   VisitExpr(E);
   bool HasFunctionName = Record.readInt();
@@ -2802,6 +2817,11 @@
                        /*StorageKind=*/Record[ASTStmtReader::NumExprFields]));
       break;
 
+    case EXPR_UNIQUESTABLENAME:
+      S = UniqueStableNameExpr::CreateEmpty(
+          Context, /*IsExpr*/ Record[ASTStmtReader::NumExprFields]);
+      break;
+
     case EXPR_PREDEFINED:
       S = PredefinedExpr::CreateEmpty(
           Context,
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -2399,6 +2399,19 @@
     return SEHFinallyStmt::Create(getSema().getASTContext(), Loc, Block);
   }
 
+  ExprResult RebuildUniqueStableNameExpr(SourceLocation OpLoc,
+                                         SourceLocation LParen,
+                                         SourceLocation RParen, Expr *E) {
+    return getSema().BuildUniqueStableNameExpr(OpLoc, LParen, RParen, E);
+  }
+
+  ExprResult RebuildUniqueStableNameExpr(SourceLocation OpLoc,
+                                         SourceLocation LParen,
+                                         SourceLocation RParen,
+                                         TypeSourceInfo *TSI) {
+    return getSema().BuildUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
+  }
+
   /// Build a new predefined expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -10172,6 +10185,43 @@
   return TransformExpr(E->getSubExpr());
 }
 
+template <typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformUniqueStableNameExpr(UniqueStableNameExpr *E) {
+  if (!E->isTypeDependent())
+    return E;
+
+  if (E->isExpr()) {
+    ExprResult ER = getDerived().TransformExpr(E->getExpr());
+
+    if (ER.isInvalid())
+      return ExprError();
+
+    ER = getSema().CheckPlaceholderExpr(ER.get());
+
+    if (ER.isInvalid())
+      return ExprError();
+
+    if (!getDerived().AlwaysRebuild() && E->getExpr() == ER.get())
+      return E;
+
+    return getDerived().RebuildUniqueStableNameExpr(
+        E->getLocation(), E->getLParenLocation(), E->getRParenLocation(),
+        ER.get());
+  }
+
+  TypeSourceInfo *NewT = getDerived().TransformType(E->getTypeSourceInfo());
+
+  if (!NewT)
+    return ExprError();
+
+  if (!getDerived().AlwaysRebuild() && E->getTypeSourceInfo() == NewT)
+    return E;
+
+  return getDerived().RebuildUniqueStableNameExpr(
+      E->getLocation(), E->getLParenLocation(), E->getRParenLocation(), NewT);
+}
+
 template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformPredefinedExpr(PredefinedExpr *E) {
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -548,6 +548,35 @@
   S.addAMDGPUWavesPerEUAttr(New, Attr, MinExpr, MaxExpr);
 }
 
+// This doesn't take any template parameters, but we have a custom action that
+// needs to happen when the kernel itself is instantiated. We need to run the
+// ItaniumMangler to mark the names required to name this kernel.
+static void instantiateDependentSYCLKernelAttr(
+    Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
+    const SYCLKernelAttr &Attr, Decl *New) {
+  // Functions cannot be partially specialized, so if we are being instantiated,
+  // we are obviously a complete specialization. Since this attribute is only
+  // valid on function template declarations, we know that this is a full
+  // instantiation of a kernel.
+  S.AddSYCLKernelLambda(cast<FunctionDecl>(New));
+
+  // Evaluate whether this would change any of the already evaluated
+  // __builtin_unique_stable_name values.
+  for (auto &Itr : S.Context.UniqueStableNameEvaluatedValues) {
+    const std::string &CurName = Itr.first->ComputeName(S.Context);
+    if (Itr.second != CurName) {
+      S.Diag(New->getLocation(),
+             diag::err_kernel_invalidates_unique_stable_name);
+      S.Diag(Itr.first->getLocation(),
+             diag::note_unique_stable_name_evaluated_here);
+      // Update this so future diagnostics work correctly.
+      Itr.second = CurName;
+    }
+  }
+
+  New->addAttr(Attr.clone(S.getASTContext()));
+}
+
 /// Determine whether the attribute A might be relevent to the declaration D.
 /// If not, we can skip instantiating it. The attribute may or may not have
 /// been instantiated yet.
@@ -723,6 +752,11 @@
       continue;
     }
 
+    if (auto *A = dyn_cast<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
Index: clang/lib/Sema/SemaSYCL.cpp
===================================================================
--- clang/lib/Sema/SemaSYCL.cpp
+++ clang/lib/Sema/SemaSYCL.cpp
@@ -8,6 +8,7 @@
 // This implements Semantic Analysis for SYCL constructs.
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/Mangle.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaDiagnostic.h"
 
@@ -47,3 +48,33 @@
   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 ShouldMangleCallback = [](ASTContext &Ctx, const CXXRecordDecl *RD) {
+    // We ALWAYS want to descend into the lambda mangling for these.
+    return true;
+  };
+  auto MangleCallback = [](ASTContext &Ctx, const CXXRecordDecl *RD,
+                           raw_ostream &) { Ctx.AddSYCLKernelNamingDecl(RD); };
+
+  QualType Ty = GetSYCLKernelObjectType(FD);
+  std::unique_ptr<MangleContext> Ctx{ItaniumMangleContext::create(
+      Context, Context.getDiagnostics(), ShouldMangleCallback, MangleCallback)};
+  llvm::raw_null_ostream Out;
+  Ctx->mangleTypeName(Ty, Out);
+}
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -461,11 +461,15 @@
   std::tie(MCtx, ManglingContextDecl) =
       getCurrentMangleNumberContext(Class->getDeclContext());
   bool HasKnownInternalLinkage = false;
-  if (!MCtx && getLangOpts().CUDA) {
+  if (!MCtx && (getLangOpts().CUDA || getLangOpts().SYCLIsDevice ||
+                getLangOpts().SYCLIsHost)) {
     // Force lambda numbering in CUDA/HIP as we need to name lambdas following
     // ODR. Both device- and host-compilation need to have a consistent naming
     // on kernel functions. As lambdas are potential part of these `__global__`
     // function names, they needs numbering following ODR.
+    // Also force for SYCL, since we need this for the
+    // __builtin_unique_stable_name implementation, which depends on lambda
+    // mangling.
     MCtx = getMangleNumberingContext(Class, ManglingContextDecl);
     assert(MCtx && "Retrieving mangle numbering context failed!");
     HasKnownInternalLinkage = true;
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -3506,6 +3506,46 @@
   return PredefinedExpr::Create(Context, Loc, ResTy, IK, SL);
 }
 
+ExprResult Sema::BuildUniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen,
+                                           TypeSourceInfo *TSI) {
+  return UniqueStableNameExpr::Create(Context, OpLoc, LParen, RParen, TSI);
+}
+ExprResult Sema::BuildUniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen, Expr *E) {
+  // If this isn't dependent anymore, we know the type, so convert to a Type
+  // based version of the UniqueStableNameExpr.
+  if (E->isInstantiationDependent())
+    return UniqueStableNameExpr::Create(Context, OpLoc, LParen, RParen, E);
+
+  return BuildUniqueStableNameExpr(
+      OpLoc, LParen, RParen, Context.getTrivialTypeSourceInfo(E->getType()));
+}
+
+ExprResult Sema::ActOnUniqueStableNameExpr(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 BuildUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
+}
+
+ExprResult Sema::ActOnUniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen,
+                                           Expr *Operand) {
+  return BuildUniqueStableNameExpr(OpLoc, LParen, RParen, Operand);
+}
+
 ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) {
   PredefinedExpr::IdentKind IK;
 
Index: clang/lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- clang/lib/Sema/SemaExceptionSpec.cpp
+++ clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1575,6 +1575,8 @@
     return mergeCanThrow(CT, canThrow(TS->getTryBody()));
   }
 
+  case Stmt::UniqueStableNameExprClass:
+    return CT_Cannot;
   case Stmt::NoStmtClass:
     llvm_unreachable("Invalid class for statement");
   }
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ clang/lib/Parse/ParseExpr.cpp
@@ -1469,6 +1469,9 @@
   case tok::kw_this:
     Res = ParseCXXThis();
     break;
+  case tok::kw___builtin_unique_stable_name:
+    Res = ParseUniqueStableNameExpression();
+    break;
 
   case tok::annot_typename:
     if (isStartOfObjCClassMessageMissingOpenBracket()) {
@@ -2324,6 +2327,44 @@
   return Operand;
 }
 
+/// Parse a __builtin_unique_stable_name expression.  Accepts a type-id or an
+/// arbitrary expression as a parameter.
+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);
+
+  // __builtin_unique_stable_name 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.
 ///
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -486,6 +486,8 @@
     return CGF.EmitPseudoObjectRValue(E).getScalarVal();
   }
 
+  Value *VisitUniqueStableNameExpr(UniqueStableNameExpr *E);
+
   Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) {
     if (E->isGLValue())
       return EmitLoadOfLValue(CGF.getOrCreateOpaqueLValueMapping(E),
@@ -1581,6 +1583,24 @@
   return llvm::UndefValue::get(CGF.ConvertType(E->getType()));
 }
 
+Value *ScalarExprEmitter::VisitUniqueStableNameExpr(UniqueStableNameExpr *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) {
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -1018,6 +1018,12 @@
     OS << " isFreeIvar";
 }
 
+void TextNodeDumper::VisitUniqueStableNameExpr(
+    const UniqueStableNameExpr *Node) {
+  if (Node->isTypeSourceInfo())
+    dumpType(Node->getTypeSourceInfo()->getType());
+}
+
 void TextNodeDumper::VisitPredefinedExpr(const PredefinedExpr *Node) {
   OS << " " << PredefinedExpr::getIdentKindName(Node->getIdentKind());
 }
Index: clang/lib/AST/StmtProfile.cpp
===================================================================
--- clang/lib/AST/StmtProfile.cpp
+++ clang/lib/AST/StmtProfile.cpp
@@ -1190,6 +1190,15 @@
   }
 }
 
+void StmtProfiler::VisitUniqueStableNameExpr(const UniqueStableNameExpr *S) {
+  VisitExpr(S);
+  ID.AddBoolean(S->isExpr());
+  if (S->isExpr())
+    VisitStmt(S->getExpr());
+  else
+    VisitType(S->getTypeSourceInfo()->getType());
+}
+
 void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) {
   VisitExpr(S);
   ID.AddInteger(S->getIdentKind());
Index: clang/lib/AST/StmtPrinter.cpp
===================================================================
--- clang/lib/AST/StmtPrinter.cpp
+++ clang/lib/AST/StmtPrinter.cpp
@@ -1081,6 +1081,16 @@
   OS << "]";
 }
 
+void StmtPrinter::VisitUniqueStableNameExpr(UniqueStableNameExpr *Node) {
+  // FIXME: Is this the correct thing?
+  OS << "__builtin_unique_stable_name(";
+  if (Node->isExpr())
+    PrintExpr(Node->getExpr());
+  else
+    Node->getTypeSourceInfo()->getType().print(OS, Policy);
+  OS << ")";
+}
+
 void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) {
   OS << PredefinedExpr::getIdentKindName(Node->getIdentKind());
 }
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -1164,6 +1164,12 @@
   }
 }
 
+void JSONNodeDumper::VisitUniqueStableNameExpr(const UniqueStableNameExpr *E) {
+  if (E->isTypeSourceInfo())
+    JOS.attribute("typeSourceInfo",
+                  createQualType(E->getTypeSourceInfo()->getType()));
+}
+
 void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) {
   JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind()));
 }
Index: clang/lib/AST/ItaniumMangle.cpp
===================================================================
--- clang/lib/AST/ItaniumMangle.cpp
+++ clang/lib/AST/ItaniumMangle.cpp
@@ -125,14 +125,20 @@
   typedef std::pair<const DeclContext*, IdentifierInfo*> DiscriminatorKeyTy;
   llvm::DenseMap<DiscriminatorKeyTy, unsigned> Discriminator;
   llvm::DenseMap<const NamedDecl*, unsigned> Uniquifier;
+  const ShouldCallKernelCallbackTy ShouldCallKernelCallback = nullptr;
+  const KernelMangleCallbackTy KernelMangleCallback = nullptr;
 
   bool IsDevCtx = false;
   bool NeedsUniqueInternalLinkageNames = false;
 
 public:
-  explicit ItaniumMangleContextImpl(ASTContext &Context,
-                                    DiagnosticsEngine &Diags)
-      : ItaniumMangleContext(Context, Diags) {}
+  explicit ItaniumMangleContextImpl(
+      ASTContext &Context, DiagnosticsEngine &Diags,
+      ShouldCallKernelCallbackTy ShouldCallKernelCB,
+      KernelMangleCallbackTy KernelCB)
+      : ItaniumMangleContext(Context, Diags),
+        ShouldCallKernelCallback(ShouldCallKernelCB),
+        KernelMangleCallback(KernelCB) {}
 
   /// @name Mangler Entry Points
   /// @{
@@ -246,6 +252,13 @@
     return Name;
   }
 
+  ShouldCallKernelCallbackTy getShouldCallKernelCallback() const override {
+    return ShouldCallKernelCallback;
+  }
+  KernelMangleCallbackTy getKernelMangleCallback() const override {
+    return KernelMangleCallback;
+  }
+
   /// @}
 };
 
@@ -1515,7 +1528,9 @@
     // <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.getShouldCallKernelCallback()(
+                                     Context.getASTContext(), Record))) {
         assert(!AdditionalAbiTags &&
                "Lambda type cannot have additional abi tags");
         mangleLambda(Record);
@@ -1953,9 +1968,17 @@
   // if the host-side CXX ABI has different numbering for lambda. In such case,
   // if the mangle context is that device-side one, use the device-side lambda
   // mangling number for this lambda.
+
   unsigned Number = Context.isDeviceMangleContext()
                         ? Lambda->getDeviceLambdaManglingNumber()
                         : Lambda->getLambdaManglingNumber();
+
+  if (Context.getShouldCallKernelCallback()(Context.getASTContext(), Lambda)) {
+    Context.getKernelMangleCallback()(Context.getASTContext(), Lambda, Out);
+    Out << '_';
+    return;
+  }
+
   assert(Number > 0 && "Lambda should be mangled as an unnamed class");
   if (Number > 1)
     mangleNumber(Number - 2);
@@ -5026,6 +5049,20 @@
     Out << "v18co_yield";
     mangleExpression(cast<CoawaitExpr>(E)->getOperand());
     break;
+  case Expr::UniqueStableNameExprClass: {
+    const auto *USN = cast<UniqueStableNameExpr>(E);
+    NotPrimaryExpr();
+
+    TemplateArgument TA;
+    Out << "u20__unique_stable_name";
+    if (USN->isExpr()) {
+      mangleTemplateArgExpr(USN->getExpr());
+    } else {
+      mangleType(USN->getTypeSourceInfo()->getType());
+    }
+    Out << "E";
+    break;
+  }
   }
 
   if (AsTemplateArg && !IsPrimaryExpr)
@@ -6378,7 +6415,17 @@
   Mangler.mangleLambdaSig(Lambda);
 }
 
-ItaniumMangleContext *
-ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) {
-  return new ItaniumMangleContextImpl(Context, Diags);
+ItaniumMangleContext *ItaniumMangleContext::create(ASTContext &Context,
+                                                   DiagnosticsEngine &Diags) {
+  return new ItaniumMangleContextImpl(
+      Context, Diags, [](ASTContext &, const CXXRecordDecl *) { return false; },
+      [](ASTContext &, const CXXRecordDecl *, raw_ostream &) {});
+}
+
+ItaniumMangleContext *ItaniumMangleContext::create(
+    ASTContext &Context, DiagnosticsEngine &Diags,
+    ShouldCallKernelCallbackTy ShouldCallKernelCallback,
+    KernelMangleCallbackTy MangleCallback) {
+  return new ItaniumMangleContextImpl(Context, Diags, ShouldCallKernelCallback,
+                                      MangleCallback);
 }
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -8669,6 +8669,26 @@
     return true;
   }
 
+  bool VisitUniqueStableNameExpr(const UniqueStableNameExpr *E) {
+    std::string ResultStr = E->ComputeName(Info.Ctx);
+
+    Info.Ctx.UniqueStableNameEvaluatedValues[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 @@
 
       Result = APValue(APValue::UninitArray(), 0,
                        CAT->getSize().getZExtValue());
-      if (!Result.hasArrayFiller()) return true;
+      if (!Result.hasArrayFiller())
+        return true;
 
       // Zero-initialize all elements.
       LValue Subobject = This;
@@ -15154,6 +15175,7 @@
   case Expr::CoawaitExprClass:
   case Expr::DependentCoawaitExprClass:
   case Expr::CoyieldExprClass:
+  case Expr::UniqueStableNameExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
 
   case Expr::InitListExprClass: {
Index: clang/lib/AST/ExprClassification.cpp
===================================================================
--- clang/lib/AST/ExprClassification.cpp
+++ clang/lib/AST/ExprClassification.cpp
@@ -433,6 +433,9 @@
   case Expr::CoawaitExprClass:
   case Expr::CoyieldExprClass:
     return ClassifyInternal(Ctx, cast<CoroutineSuspendExpr>(E)->getResumeExpr());
+  case Expr::UniqueStableNameExprClass:
+    return Cl::CL_PRValue;
+    break;
   }
 
   llvm_unreachable("unhandled expression kind in classification");
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -504,6 +504,100 @@
   return getNameInfo().getEndLoc();
 }
 
+UniqueStableNameExpr::UniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen,
+                                           QualType ResultTy,
+                                           TypeSourceInfo *TSI)
+    : Expr(UniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary),
+      OpLoc(OpLoc), LParen(LParen), RParen(RParen), Kind(ParamKind::Type) {
+  setTypeSourceInfo(TSI);
+  setDependence(computeDependence(this));
+}
+
+UniqueStableNameExpr::UniqueStableNameExpr(EmptyShell Empty, QualType ResultTy,
+                                           bool IsExpr)
+    : Expr(UniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary) {
+  Kind = IsExpr ? ParamKind::Expr : ParamKind::Type;
+}
+
+UniqueStableNameExpr::UniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen,
+                                           QualType ResultTy, Expr *E)
+    : Expr(UniqueStableNameExprClass, ResultTy, VK_RValue, OK_Ordinary),
+      OpLoc(OpLoc), LParen(LParen), RParen(RParen), Kind(ParamKind::Expr) {
+  setExpr(E);
+  setDependence(computeDependence(this));
+}
+
+UniqueStableNameExpr *UniqueStableNameExpr::Create(const ASTContext &Ctx,
+                                                   SourceLocation OpLoc,
+                                                   SourceLocation LParen,
+                                                   SourceLocation RParen,
+                                                   Expr *E) {
+  assert(E->isInstantiationDependent() &&
+         "Expr type only valid if the expr is dependent");
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *, TypeSourceInfo *>(1, 0),
+                           alignof(UniqueStableNameExpr));
+
+  QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  return new (Mem) UniqueStableNameExpr(OpLoc, LParen, RParen, ResultTy, E);
+}
+
+UniqueStableNameExpr *UniqueStableNameExpr::Create(const ASTContext &Ctx,
+                                                   SourceLocation OpLoc,
+                                                   SourceLocation LParen,
+                                                   SourceLocation RParen,
+                                                   TypeSourceInfo *TSI) {
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *, TypeSourceInfo *>(1, 0),
+                           alignof(UniqueStableNameExpr));
+  QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  return new (Mem) UniqueStableNameExpr(OpLoc, LParen, RParen, ResultTy, TSI);
+}
+
+UniqueStableNameExpr *UniqueStableNameExpr::CreateEmpty(const ASTContext &Ctx,
+                                                        bool IsExpr) {
+  void *Mem =
+      Ctx.Allocate(totalSizeToAlloc<Stmt *, TypeSourceInfo *>(IsExpr, !IsExpr),
+                   alignof(UniqueStableNameExpr));
+  QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  return new (Mem) UniqueStableNameExpr(EmptyShell(), ResultTy, IsExpr);
+}
+
+std::string UniqueStableNameExpr::ComputeName(ASTContext &Context) const {
+  return UniqueStableNameExpr::ComputeName(Context,
+                                           getTypeSourceInfo()->getType());
+}
+
+std::string UniqueStableNameExpr::ComputeName(ASTContext &Context,
+                                              QualType Ty) {
+  auto ShouldMangleCallback = [](ASTContext &Ctx, const CXXRecordDecl *RD) {
+    return Ctx.IsSYCLKernelNamingDecl(RD);
+  };
+  auto MangleCallback = [](ASTContext &Ctx, const CXXRecordDecl *RD,
+                           raw_ostream &OS) {
+    assert(Ctx.IsSYCLKernelNamingDecl(RD) && "Not a sycl kernel?");
+    // This replaces the 'lambda number' in the mangling with a unique number
+    // based on its order in the declaration.  To provide some level of visual
+    // notability (actual uniqueness from normal lambdas isn't necessary, as
+    // these are used differently), we add 10,000 to the number.
+    // For example:
+    // _ZTSZ3foovEUlvE10005_
+    // Demangles to: typeinfo name for foo()::'lambda10005'()
+    OS << (10'000 + Ctx.GetSYCLKernelNamingIndex(RD));
+  };
+  std::unique_ptr<MangleContext> Ctx{ItaniumMangleContext::create(
+      Context, Context.getDiagnostics(), ShouldMangleCallback, 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 +3475,7 @@
   case SourceLocExprClass:
   case ConceptSpecializationExprClass:
   case RequiresExprClass:
+  case UniqueStableNameExprClass:
     // These never have a side-effect.
     return false;
 
Index: clang/lib/AST/ComputeDependence.cpp
===================================================================
--- clang/lib/AST/ComputeDependence.cpp
+++ clang/lib/AST/ComputeDependence.cpp
@@ -556,6 +556,12 @@
   return D;
 }
 
+ExprDependence clang::computeDependence(UniqueStableNameExpr *E) {
+  if (E->isExpr())
+    return E->getExpr()->getDependence();
+  return toExprDependence(E->getTypeSourceInfo()->getType()->getDependence());
+}
+
 ExprDependence clang::computeDependence(PredefinedExpr *E) {
   return toExprDependence(E->getType()->getDependence()) &
          ~ExprDependence::UnexpandedPack;
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -2458,7 +2458,7 @@
   // The preferred alignment of member pointers is that of a pointer.
   if (T->isMemberPointerType())
     return getPreferredTypeAlign(getPointerDiffType().getTypePtr());
- 
+
   if (!Target->allowsLargerPreferedTypeAlignment())
     return ABIAlign;
 
@@ -11648,3 +11648,83 @@
   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) {
+  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 CXXRecordDecl *RD) const {
+  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 CXXRecordDecl *RD) const {
+  assert(IsSYCLKernelNamingDecl(RD) &&
+         "Lambda not involved in mangling asked for a naming index?");
+
+  RD = RD->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();
+}
Index: clang/include/clang/Serialization/ASTBitCodes.h
===================================================================
--- clang/include/clang/Serialization/ASTBitCodes.h
+++ clang/include/clang/Serialization/ASTBitCodes.h
@@ -1960,6 +1960,9 @@
 
   // FixedPointLiteral
   EXPR_FIXEDPOINT_LITERAL,
+
+  // UniqueStableNameExpr
+  EXPR_UNIQUESTABLENAME,
 };
 
 /// The kinds of designators that can occur in a
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -911,6 +911,10 @@
     OpaqueParser = P;
   }
 
+  // Does the work necessary to deal with a SYCL kernel lambda. At the moment,
+  // this just marks the list of lambdas required to name the kernel.
+  void AddSYCLKernelLambda(const FunctionDecl *FD);
+
   class DelayedDiagnostics;
 
   class DelayedDiagnosticsState {
@@ -5190,6 +5194,21 @@
   ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind);
   ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val);
 
+  ExprResult BuildUniqueStableNameExpr(SourceLocation OpLoc,
+                                       SourceLocation LParen,
+                                       SourceLocation RParen,
+                                       TypeSourceInfo *TSI);
+  ExprResult BuildUniqueStableNameExpr(SourceLocation OpLoc,
+                                       SourceLocation LParen,
+                                       SourceLocation RParen, Expr *E);
+  ExprResult ActOnUniqueStableNameExpr(SourceLocation OpLoc,
+                                       SourceLocation LParen,
+                                       SourceLocation RParen,
+                                       ParsedType ParsedTy);
+  ExprResult ActOnUniqueStableNameExpr(SourceLocation OpLoc,
+                                       SourceLocation LParen,
+                                       SourceLocation RParen, Expr *Operand);
+
   bool CheckLoopHintExpr(Expr *E, SourceLocation Loc);
 
   ExprResult ActOnNumericConstant(const Token &Tok, Scope *UDLScope = nullptr);
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -1800,6 +1800,7 @@
   ExprResult ParsePostfixExpressionSuffix(ExprResult LHS);
   ExprResult ParseUnaryExprOrTypeTraitExpression();
   ExprResult ParseBuiltinPrimaryExpression();
+  ExprResult ParseUniqueStableNameExpression();
 
   ExprResult ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
                                                      bool &isCastExpr,
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ clang/include/clang/Basic/TokenKinds.def
@@ -699,6 +699,7 @@
 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)
Index: clang/include/clang/Basic/StmtNodes.td
===================================================================
--- clang/include/clang/Basic/StmtNodes.td
+++ clang/include/clang/Basic/StmtNodes.td
@@ -57,6 +57,7 @@
 // Expressions
 def Expr : StmtNode<ValueStmt, 1>;
 def PredefinedExpr : StmtNode<Expr>;
+def UniqueStableNameExpr : StmtNode<Expr>;
 def DeclRefExpr : StmtNode<Expr>;
 def IntegerLiteral : StmtNode<Expr>;
 def FixedPointLiteral : StmtNode<Expr>;
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6352,6 +6352,11 @@
 def warn_gnu_null_ptr_arith : Warning<
   "arithmetic on a null pointer treated as a cast from integer to pointer is a GNU extension">,
   InGroup<NullPointerArithmetic>, DefaultIgnore;
+def err_kernel_invalidates_unique_stable_name
+    : Error<"kernel instantiation changes the result of an evaluated "
+            "'__builtin_unique_stable_name'">;
+def note_unique_stable_name_evaluated_here
+    : Note<"'__builtin_unique_stable_name' evaluated here">;
 
 def warn_floatingpoint_eq : Warning<
   "comparing floating point with == or != is unsafe">,
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -325,7 +325,8 @@
 def Borland : LangOpt<"Borland">;
 def CUDA : LangOpt<"CUDA">;
 def HIP : LangOpt<"HIP">;
-def SYCL : LangOpt<"SYCLIsDevice">;
+def SYCLDevice : LangOpt<"SYCLIsDevice">;
+def SYCLHost : LangOpt<"SYCLIsDevice">;
 def COnly : LangOpt<"", "!LangOpts.CPlusPlus">;
 def CPlusPlus : LangOpt<"CPlusPlus">;
 def OpenCL : LangOpt<"OpenCL">;
@@ -1180,7 +1181,7 @@
 def SYCLKernel : InheritableAttr {
   let Spellings = [Clang<"sycl_kernel">];
   let Subjects = SubjectList<[FunctionTmpl]>;
-  let LangOpts = [SYCL];
+  let LangOpts = [SYCLDevice, SYCLHost];
   let Documentation = [SYCLKernelDocs];
 }
 
Index: clang/include/clang/AST/TextNodeDumper.h
===================================================================
--- clang/include/clang/AST/TextNodeDumper.h
+++ clang/include/clang/AST/TextNodeDumper.h
@@ -249,6 +249,7 @@
   void VisitCastExpr(const CastExpr *Node);
   void VisitImplicitCastExpr(const ImplicitCastExpr *Node);
   void VisitDeclRefExpr(const DeclRefExpr *Node);
+  void VisitUniqueStableNameExpr(const UniqueStableNameExpr *Node);
   void VisitPredefinedExpr(const PredefinedExpr *Node);
   void VisitCharacterLiteral(const CharacterLiteral *Node);
   void VisitIntegerLiteral(const IntegerLiteral *Node);
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2651,6 +2651,10 @@
 DEF_TRAVERSE_STMT(ObjCAvailabilityCheckExpr, {})
 DEF_TRAVERSE_STMT(ParenExpr, {})
 DEF_TRAVERSE_STMT(ParenListExpr, {})
+DEF_TRAVERSE_STMT(UniqueStableNameExpr, {
+  if (S->isTypeSourceInfo())
+    TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
 DEF_TRAVERSE_STMT(PredefinedExpr, {})
 DEF_TRAVERSE_STMT(ShuffleVectorExpr, {})
 DEF_TRAVERSE_STMT(ConvertVectorExpr, {})
Index: clang/include/clang/AST/Mangle.h
===================================================================
--- clang/include/clang/AST/Mangle.h
+++ clang/include/clang/AST/Mangle.h
@@ -173,6 +173,10 @@
 
 class ItaniumMangleContext : public MangleContext {
 public:
+  using ShouldCallKernelCallbackTy = bool (*)(ASTContext &,
+                                              const CXXRecordDecl *);
+  using KernelMangleCallbackTy = void (*)(ASTContext &, const CXXRecordDecl *,
+                                          raw_ostream &);
   explicit ItaniumMangleContext(ASTContext &C, DiagnosticsEngine &D)
       : MangleContext(C, D, MK_Itanium) {}
 
@@ -195,12 +199,21 @@
 
   virtual void mangleDynamicStermFinalizer(const VarDecl *D, raw_ostream &) = 0;
 
+  // These have to live here, otherwise the CXXNameMangler won't have access to
+  // them.
+  virtual ShouldCallKernelCallbackTy getShouldCallKernelCallback() const = 0;
+  virtual KernelMangleCallbackTy getKernelMangleCallback() 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,
+         ShouldCallKernelCallbackTy ShouldKernelMangleCB,
+         KernelMangleCallbackTy KernelMangleCB);
 };
 
 class MicrosoftMangleContext : public MangleContext {
Index: clang/include/clang/AST/JSONNodeDumper.h
===================================================================
--- clang/include/clang/AST/JSONNodeDumper.h
+++ clang/include/clang/AST/JSONNodeDumper.h
@@ -263,6 +263,7 @@
   void VisitBlockDecl(const BlockDecl *D);
 
   void VisitDeclRefExpr(const DeclRefExpr *DRE);
+  void VisitUniqueStableNameExpr(const UniqueStableNameExpr *E);
   void VisitPredefinedExpr(const PredefinedExpr *PE);
   void VisitUnaryOperator(const UnaryOperator *UO);
   void VisitBinaryOperator(const BinaryOperator *BO);
Index: clang/include/clang/AST/Expr.h
===================================================================
--- clang/include/clang/AST/Expr.h
+++ clang/include/clang/AST/Expr.h
@@ -2039,6 +2039,119 @@
   }
 };
 
+// This represents a use of the __builtin_unique_stable_name, which takes either
+// a type-id or an expression, and at CodeGen time emits a unique string
+// representation of the type (or type of the expression) in a way that permits
+// us to properly encode information about the SYCL kernels.
+class UniqueStableNameExpr final
+    : public Expr,
+      private llvm::TrailingObjects<UniqueStableNameExpr, Stmt *,
+                                    TypeSourceInfo *> {
+  friend class ASTStmtReader;
+  friend TrailingObjects;
+  SourceLocation OpLoc, LParen, RParen;
+  // Note: We store a Stmt* instead of the Expr* so that we can implement
+  // 'children'.
+  enum class ParamKind { Type, Expr };
+  ParamKind Kind;
+
+  UniqueStableNameExpr(EmptyShell Empty, QualType ResultTy, bool IsExpr);
+  UniqueStableNameExpr(SourceLocation OpLoc, SourceLocation LParen,
+                       SourceLocation RParen, QualType ResultTy,
+                       TypeSourceInfo *TSI);
+  UniqueStableNameExpr(SourceLocation OpLoc, SourceLocation LParen,
+                       SourceLocation RParen, QualType ResultTy, Expr *E);
+
+  size_t numTrailingObjects(OverloadToken<TypeSourceInfo *>) const {
+    return Kind == ParamKind::Type ? 1 : 0;
+  }
+  size_t numTrailingObjects(OverloadToken<Stmt *>) const {
+    return Kind == ParamKind::Expr ? 1 : 0;
+  }
+  void setTypeSourceInfo(TypeSourceInfo *Ty) {
+    assert(Kind == ParamKind::Type &&
+           "TypeSourceInfo only valid for UniqueStableName of a Type");
+    *getTrailingObjects<TypeSourceInfo *>() = Ty;
+  }
+  void setExpr(Expr *E) {
+    assert(Kind == ParamKind::Expr &&
+           "Expr only valid for UniqueStableName of an Expr");
+    assert(E->isInstantiationDependent() &&
+           "Expr type only valid if the expr is dependent");
+    *getTrailingObjects<Stmt *>() = E;
+  }
+
+  void setLocation(SourceLocation L) { OpLoc = L; }
+  void setLParenLocation(SourceLocation L) { LParen = L; }
+  void setRParenLocation(SourceLocation L) { RParen = L; }
+
+public:
+  TypeSourceInfo *getTypeSourceInfo() {
+    assert(Kind == ParamKind::Type &&
+           "TypeSourceInfo only valid for UniqueStableName of a Type");
+    return *getTrailingObjects<TypeSourceInfo *>();
+  }
+
+  const TypeSourceInfo *getTypeSourceInfo() const {
+    assert(Kind == ParamKind::Type &&
+           "TypeSourceInfo only valid for UniqueStableName of a Type");
+    return *getTrailingObjects<TypeSourceInfo *>();
+  }
+
+  Expr *getExpr() {
+    assert(Kind == ParamKind::Expr &&
+           "Expr only valid for UniqueStableName of an Expr");
+    return cast<Expr>(*getTrailingObjects<Stmt *>());
+  }
+
+  const Expr *getExpr() const {
+    assert(Kind == ParamKind::Expr &&
+           "Expr only valid for UniqueStableName of an Expr");
+    return cast<Expr>(*getTrailingObjects<Stmt *>());
+  }
+
+  bool isExpr() const { return Kind == ParamKind::Expr; }
+
+  bool isTypeSourceInfo() const { return Kind == ParamKind::Type; }
+
+  static UniqueStableNameExpr *
+  Create(const ASTContext &Ctx, SourceLocation OpLoc, SourceLocation LParen,
+         SourceLocation RParen, TypeSourceInfo *TSI);
+
+  static UniqueStableNameExpr *Create(const ASTContext &Ctx,
+                                      SourceLocation OpLoc,
+                                      SourceLocation LParen,
+                                      SourceLocation RParen, Expr *E);
+  static UniqueStableNameExpr *CreateEmpty(const ASTContext &Ctx, bool IsExpr);
+
+  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() == UniqueStableNameExprClass;
+  }
+
+  // Iterators
+  child_range children() {
+    return child_range(getTrailingObjects<Stmt *>(),
+                       getTrailingObjects<Stmt *>() + isExpr());
+  }
+  const_child_range children() const {
+    return const_child_range(getTrailingObjects<Stmt *>(),
+                             getTrailingObjects<Stmt *>() + isExpr());
+  }
+
+  // Convenience function to generate the name of the currently stored type.
+  std::string ComputeName(ASTContext &Context) const;
+
+  // Get the generated name of the type.  Note that this only works after all
+  // kernels have been instantiated.
+  static std::string ComputeName(ASTContext &Context, QualType Ty);
+};
+
 /// ParenExpr - This represents a parethesized expression, e.g. "(1)".  This
 /// AST node is only formed if full location information is requested.
 class ParenExpr : public Expr {
Index: clang/include/clang/AST/ComputeDependence.h
===================================================================
--- clang/include/clang/AST/ComputeDependence.h
+++ clang/include/clang/AST/ComputeDependence.h
@@ -78,6 +78,7 @@
 class CXXFoldExpr;
 class TypeTraitExpr;
 class ConceptSpecializationExpr;
+class UniqueStableNameExpr;
 class PredefinedExpr;
 class CallExpr;
 class OffsetOfExpr;
@@ -165,6 +166,7 @@
 ExprDependence computeDependence(ConceptSpecializationExpr *E,
                                  bool ValueDependent);
 
+ExprDependence computeDependence(UniqueStableNameExpr *E);
 ExprDependence computeDependence(PredefinedExpr *E);
 ExprDependence computeDependence(CallExpr *E, llvm::ArrayRef<Expr *> PreArgs);
 ExprDependence computeDependence(OffsetOfExpr *E);
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ clang/include/clang/AST/ASTContext.h
@@ -3158,10 +3158,26 @@
 
   StringRef getCUIDHash() const;
 
+  void AddSYCLKernelNamingDecl(const CXXRecordDecl *RD);
+  bool IsSYCLKernelNamingDecl(const CXXRecordDecl *RD) const;
+  unsigned GetSYCLKernelNamingIndex(const CXXRecordDecl *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 UniqueStableNameExpr *, std::string>
+      UniqueStableNameEvaluatedValues;
+
 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_unique_stable_name.
+  llvm::DenseMap<const DeclContext *,
+                 llvm::SmallPtrSet<const CXXRecordDecl *, 4>>
+      SYCLKernelNamingTypes;
 };
 
 /// Insertion operator for diagnostics.
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ clang/docs/LanguageExtensions.rst
@@ -2404,6 +2404,44 @@
   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 unevaluated
+expression and produces a string literal containing a unique name for the type
+(or type of the expression) that is stable across split compilations, mainly to
+support SYCL/Data Parallel C++ language.
+
+In cases where the split compilation needs to share a unique token for a type
+across the boundary (such as in an offloading situation), this name can be used
+for lookup purposes, such as in the SYCL Integration Header.
+
+The value of this builtin is computed entirely at compile time, so it can be
+used in constant expressions. This value encodes lambda functions based on a
+stable numbering order in which they appear in their local declaration contexts.
+Once this builtin is evaluated in a constexpr context, it is erroneous to use
+it in an instantiation which changes its value.
+
+In order to produce the unique name, the current implementation of the bultin
+uses Itanium mangling even if the host compilation uses a different name
+mangling scheme at runtime. The mangler marks all the lambdas required to name
+the SYCL kernel and emits a stable local ordering of the respective lambdas,
+starting from ``10000``. The initial value of ``10000`` serves as an obvious
+differentiator from ordinary lambda mangling numbers but does not serve any
+other purpose and may change in the future. The resulting pattern is
+demanglable. When non-lambda types are passed to the builtin, the mangler emits
+their usual pattern without any special treatment.
+
+**Syntax**:
+
+.. code-block:: c
+
+  // Computes a unique stable name for the given type.
+  constexpr const char * __builtin_unique_stable_name( type-id );
+
+  // Computes a unique stable name for the type of the given expression.
+  constexpr const char * __builtin_unique_stable_name( expression );
+
 Multiprecision Arithmetic Builtins
 ----------------------------------
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to