erichkeane updated this revision to Diff 348060.
erichkeane added a comment.

Remove 'expression' form per suggestion.  Still need to do the 
device-mangling-number removal/rewrite business.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D103112/new/

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/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/Basic/IdentifierTable.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/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
  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::SYCLUniqueStableNameExprClass:
     K = CXCursor_UnexposedExpr;
     break;
 
Index: clang/test/SemaSYCL/unique_stable_name.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaSYCL/unique_stable_name.cpp
@@ -0,0 +1,215 @@
+// RUN: %clang_cc1 %s -std=c++17 -triple x86_64-pc-windows-msvc -fsycl-is-device -verify -fsyntax-only -Wno-unused
+// RUN: %clang_cc1 %s -std=c++17 -triple x86_64-linux-gnu -fsycl-is-device -verify -fsyntax-only -Wno-unused
+
+template <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@+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_sycl_unique_stable_name(Func); // #USN_F2
+  // expected-error@#kernelSingleTask{{kernel instantiation changes the result of an evaluated '__builtin_sycl_unique_stable_name'}}
+  // expected-note@#kernel2func_call{{in instantiation of function template specialization}}
+  // expected-note@#USN_F2{{'__builtin_sycl_unique_stable_name' evaluated here}}
+  // expected-note@+1{{in instantiation of function template specialization}}
+  kernel_single_task<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@+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@+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@+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@+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@+1{{in instantiation of function template specialization}}
+  kernel_single_task<class kernel13>(
+      []() {
+        MACRO(); // #USN_MACRO
+      });
+}
+
+namespace NS {}
+
+void f() {
+  // expected-error@+1{{unknown type name 'bad_var'}}
+  __builtin_sycl_unique_stable_name(bad_var);
+  // expected-error@+1{{use of undeclared identifier 'bad'}}
+  __builtin_sycl_unique_stable_name(bad::type);
+  // expected-error@+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@+1{{no type named 'bad_val' in 'St'}}
+  __builtin_sycl_unique_stable_name(typename T::bad_val);
+  // expected-error@+1{{no type named 'bad_type' in 'St'}}
+  __builtin_sycl_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_sycl_only.cpp
===================================================================
--- /dev/null
+++ clang/test/ParserSYCL/unique_stable_name_sycl_only.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=notsycl -Wno-unused %s
+// RUN: %clang_cc1 -fsyntax-only -fsycl-is-host -verify=sycl -Wno-unused %s
+// RUN: %clang_cc1 -fsyntax-only -fsycl-is-device -verify=sycl -Wno-unused %s
+
+// sycl-no-diagnostics
+void foo() {
+  // notsycl-error@+1{{expected '(' for function-style cast or type construction}}
+  __builtin_sycl_unique_stable_name(int);
+}
Index: clang/test/ParserSYCL/unique_stable_name.cpp
===================================================================
--- /dev/null
+++ clang/test/ParserSYCL/unique_stable_name.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify -Wno-unused %s
+
+namespace NS {
+  using good = double;
+}
+
+void f(int var) {
+  // expected-error@+1{{expected '(' after '__builtin_sycl_unique_stable_name'}}
+  __builtin_sycl_unique_stable_name int; // Correct usage is __builtin_sycl_unique_stable_name(int);
+
+  // expected-error@+1{{expected '(' after '__builtin_sycl_unique_stable_name'}}
+  __builtin_sycl_unique_stable_name{int}; // Correct usage is __builtin_sycl_unique_stable_name(int);
+
+  // expected-error@+2{{expected ')'}}
+  // expected-note@+1{{to match this '('}}
+  __builtin_sycl_unique_stable_name(int; // Missing paren before semicolon
+
+  // expected-error@+2{{expected ')'}}
+  // expected-note@+1{{to match this '('}}
+  __builtin_sycl_unique_stable_name(int, float); // Missing paren before comma
+
+  // expected-error@+1{{unknown type name 'var'}}
+  __builtin_sycl_unique_stable_name(var);
+  __builtin_sycl_unique_stable_name(NS::good);
+
+  // expected-error@+1{{expected a type}}
+  __builtin_sycl_unique_stable_name(for (int i = 0; i < 10; ++i) {})
+  __builtin_sycl_unique_stable_name({
+    (for (int i = 0; i < 10; ++i){})})
+}
+
+template <typename T>
+void f2() {
+  __builtin_sycl_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,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)*))
Index: clang/test/AST/ast-print-sycl-unique-stable-name.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/ast-print-sycl-unique-stable-name.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -ast-print -fsycl-is-device %s -o - -triple spir64-sycldevice | FileCheck %s
+
+template <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()));
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1419,6 +1419,7 @@
     case Stmt::OMPArraySectionExprClass:
     case Stmt::OMPArrayShapingExprClass:
     case Stmt::OMPIteratorExprClass:
+    case Stmt::SYCLUniqueStableNameExprClass:
     case Stmt::TypeTraitExprClass: {
       Bldr.takeNodes(Pred);
       ExplodedNodeSet preVisit;
Index: clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterStmt.cpp
+++ clang/lib/Serialization/ASTWriterStmt.cpp
@@ -580,6 +580,17 @@
   Code = serialization::EXPR_CONSTANT;
 }
 
+void ASTStmtWriter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
+  VisitExpr(E);
+
+  Record.AddSourceLocation(E->getLocation());
+  Record.AddSourceLocation(E->getLParenLocation());
+  Record.AddSourceLocation(E->getRParenLocation());
+  Record.AddTypeSourceInfo(E->getTypeSourceInfo());
+
+  Code = serialization::EXPR_SYCL_UNIQUE_STABLE_NAME;
+}
+
 void ASTStmtWriter::VisitPredefinedExpr(PredefinedExpr *E) {
   VisitExpr(E);
 
Index: clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderStmt.cpp
+++ clang/lib/Serialization/ASTReaderStmt.cpp
@@ -581,6 +581,16 @@
   E->setSubExpr(Record.readSubExpr());
 }
 
+void ASTStmtReader::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
+  VisitExpr(E);
+
+  E->setLocation(readSourceLocation());
+  E->setLParenLocation(readSourceLocation());
+  E->setRParenLocation(readSourceLocation());
+
+  E->setTypeSourceInfo(Record.readTypeSourceInfo());
+}
+
 void ASTStmtReader::VisitPredefinedExpr(PredefinedExpr *E) {
   VisitExpr(E);
   bool HasFunctionName = Record.readInt();
@@ -2802,6 +2812,10 @@
                        /*StorageKind=*/Record[ASTStmtReader::NumExprFields]));
       break;
 
+    case EXPR_SYCL_UNIQUE_STABLE_NAME:
+      S = SYCLUniqueStableNameExpr::CreateEmpty(Context);
+      break;
+
     case EXPR_PREDEFINED:
       S = PredefinedExpr::CreateEmpty(
           Context,
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -2399,6 +2399,19 @@
     return SEHFinallyStmt::Create(getSema().getASTContext(), Loc, Block);
   }
 
+  ExprResult RebuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                             SourceLocation LParen,
+                                             SourceLocation RParen, Expr *E) {
+    return getSema().BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, E);
+  }
+
+  ExprResult RebuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                             SourceLocation LParen,
+                                             SourceLocation RParen,
+                                             TypeSourceInfo *TSI) {
+    return getSema().BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
+  }
+
   /// Build a new predefined expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -10172,6 +10185,24 @@
   return TransformExpr(E->getSubExpr());
 }
 
+template <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) {
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_sycl_unique_stable_name values.
+  for (auto &Itr : S.Context.SYCLUniqueStableNameEvaluatedValues) {
+    const std::string &CurName = Itr.first->ComputeName(S.Context);
+    if (Itr.second != CurName) {
+      S.Diag(New->getLocation(),
+             diag::err_kernel_invalidates_sycl_unique_stable_name);
+      S.Diag(Itr.first->getLocation(),
+             diag::note_sycl_unique_stable_name_evaluated_here);
+      // Update this so future diagnostics work correctly.
+      Itr.second = CurName;
+    }
+  }
+
+  New->addAttr(Attr.clone(S.getASTContext()));
+}
+
 /// Determine whether the attribute A might be relevent to the declaration D.
 /// If not, we can skip instantiating it. The attribute may or may not have
 /// been instantiated yet.
@@ -723,6 +752,11 @@
       continue;
     }
 
+    if (auto *A = dyn_cast<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_sycl_unique_stable_name implementation, which depends on lambda
+    // mangling.
     MCtx = getMangleNumberingContext(Class, ManglingContextDecl);
     assert(MCtx && "Retrieving mangle numbering context failed!");
     HasKnownInternalLinkage = true;
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -3506,6 +3506,28 @@
   return PredefinedExpr::Create(Context, Loc, ResTy, IK, SL);
 }
 
+ExprResult Sema::BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                               SourceLocation LParen,
+                                               SourceLocation RParen,
+                                               TypeSourceInfo *TSI) {
+  return SYCLUniqueStableNameExpr::Create(Context, OpLoc, LParen, RParen, TSI);
+}
+
+ExprResult Sema::ActOnSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                               SourceLocation LParen,
+                                               SourceLocation RParen,
+                                               ParsedType ParsedTy) {
+  TypeSourceInfo *TSI = nullptr;
+  QualType Ty = GetTypeFromParser(ParsedTy, &TSI);
+
+  if (Ty.isNull())
+    return ExprError();
+  if (!TSI)
+    TSI = Context.getTrivialTypeSourceInfo(Ty, LParen);
+
+  return BuildSYCLUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
+}
+
 ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) {
   PredefinedExpr::IdentKind IK;
 
Index: clang/lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- clang/lib/Sema/SemaExceptionSpec.cpp
+++ clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1575,6 +1575,8 @@
     return mergeCanThrow(CT, canThrow(TS->getTryBody()));
   }
 
+  case Stmt::SYCLUniqueStableNameExprClass:
+    return CT_Cannot;
   case Stmt::NoStmtClass:
     llvm_unreachable("Invalid class for statement");
   }
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ clang/lib/Parse/ParseExpr.cpp
@@ -1469,6 +1469,9 @@
   case tok::kw_this:
     Res = ParseCXXThis();
     break;
+  case tok::kw___builtin_sycl_unique_stable_name:
+    Res = ParseSYCLUniqueStableNameExpression();
+    break;
 
   case tok::annot_typename:
     if (isStartOfObjCClassMessageMissingOpenBracket()) {
@@ -2324,6 +2327,33 @@
   return Operand;
 }
 
+/// Parse a __builtin_sycl_unique_stable_name expression.  Accepts a type-id or
+/// an arbitrary expression as a parameter.
+ExprResult Parser::ParseSYCLUniqueStableNameExpression() {
+  assert(Tok.is(tok::kw___builtin_sycl_unique_stable_name) &&
+         "Not __bulitin_sycl_unique_stable_name");
+
+  SourceLocation OpLoc = ConsumeToken();
+  BalancedDelimiterTracker T(*this, tok::l_paren);
+
+  // __builtin_sycl_unique_stable_name expressions are always parenthesized.
+  if (T.expectAndConsume(diag::err_expected_lparen_after,
+                         "__builtin_sycl_unique_stable_name"))
+    return ExprError();
+
+  TypeResult Ty = ParseTypeName();
+
+  if (Ty.isInvalid()) {
+    T.skipToEnd();
+    return ExprError();
+  }
+
+  if (T.consumeClose())
+    return ExprError();
+
+  return Actions.ActOnSYCLUniqueStableNameExpr(
+      OpLoc, T.getOpenLocation(), T.getCloseLocation(), Ty.get());
+}
 
 /// Parse a sizeof or alignof expression.
 ///
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -486,6 +486,8 @@
     return CGF.EmitPseudoObjectRValue(E).getScalarVal();
   }
 
+  Value *VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E);
+
   Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) {
     if (E->isGLValue())
       return EmitLoadOfLValue(CGF.getOrCreateOpaqueLValueMapping(E),
@@ -1581,6 +1583,25 @@
   return llvm::UndefValue::get(CGF.ConvertType(E->getType()));
 }
 
+Value *
+ScalarExprEmitter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
+  ASTContext &Context = CGF.getContext();
+  llvm::Optional<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/Basic/IdentifierTable.cpp
===================================================================
--- clang/lib/Basic/IdentifierTable.cpp
+++ clang/lib/Basic/IdentifierTable.cpp
@@ -107,8 +107,9 @@
     KEYCXX20      = 0x200000,
     KEYOPENCLCXX  = 0x400000,
     KEYMSCOMPAT   = 0x800000,
+    KEYSYCL       = 0x1000000,
     KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
-    KEYALL = (0xffffff & ~KEYNOMS18 &
+    KEYALL = (0xfffffff & ~KEYNOMS18 &
               ~KEYNOOPENCL) // KEYNOMS18 and KEYNOOPENCL are used to exclude.
   };
 
@@ -155,6 +156,8 @@
   if (LangOpts.CPlusPlus && (Flags & KEYALLCXX)) return KS_Future;
   if (LangOpts.CPlusPlus && !LangOpts.CPlusPlus20 && (Flags & CHAR8SUPPORT))
     return KS_Future;
+  if ((LangOpts.SYCLIsDevice || LangOpts.SYCLIsHost) && Flags & KEYSYCL)
+    return KS_Enabled;
   return KS_Disabled;
 }
 
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -1018,6 +1018,11 @@
     OS << " isFreeIvar";
 }
 
+void TextNodeDumper::VisitSYCLUniqueStableNameExpr(
+    const SYCLUniqueStableNameExpr *Node) {
+  dumpType(Node->getTypeSourceInfo()->getType());
+}
+
 void TextNodeDumper::VisitPredefinedExpr(const PredefinedExpr *Node) {
   OS << " " << PredefinedExpr::getIdentKindName(Node->getIdentKind());
 }
Index: clang/lib/AST/StmtProfile.cpp
===================================================================
--- clang/lib/AST/StmtProfile.cpp
+++ clang/lib/AST/StmtProfile.cpp
@@ -1190,6 +1190,12 @@
   }
 }
 
+void StmtProfiler::VisitSYCLUniqueStableNameExpr(
+    const SYCLUniqueStableNameExpr *S) {
+  VisitExpr(S);
+  VisitType(S->getTypeSourceInfo()->getType());
+}
+
 void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) {
   VisitExpr(S);
   ID.AddInteger(S->getIdentKind());
Index: clang/lib/AST/StmtPrinter.cpp
===================================================================
--- clang/lib/AST/StmtPrinter.cpp
+++ clang/lib/AST/StmtPrinter.cpp
@@ -1081,6 +1081,13 @@
   OS << "]";
 }
 
+void StmtPrinter::VisitSYCLUniqueStableNameExpr(
+    SYCLUniqueStableNameExpr *Node) {
+  OS << "__builtin_sycl_unique_stable_name(";
+  Node->getTypeSourceInfo()->getType().print(OS, Policy);
+  OS << ")";
+}
+
 void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) {
   OS << PredefinedExpr::getIdentKindName(Node->getIdentKind());
 }
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -1164,6 +1164,12 @@
   }
 }
 
+void JSONNodeDumper::VisitSYCLUniqueStableNameExpr(
+    const SYCLUniqueStableNameExpr *E) {
+  JOS.attribute("typeSourceInfo",
+                createQualType(E->getTypeSourceInfo()->getType()));
+}
+
 void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) {
   JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind()));
 }
Index: clang/lib/AST/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,16 @@
     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 +6411,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 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 @@
 
       Result = APValue(APValue::UninitArray(), 0,
                        CAT->getSize().getZExtValue());
-      if (!Result.hasArrayFiller()) return true;
+      if (!Result.hasArrayFiller())
+        return true;
 
       // Zero-initialize all elements.
       LValue Subobject = This;
@@ -15154,6 +15175,7 @@
   case Expr::CoawaitExprClass:
   case Expr::DependentCoawaitExprClass:
   case Expr::CoyieldExprClass:
+  case Expr::SYCLUniqueStableNameExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
 
   case Expr::InitListExprClass: {
Index: clang/lib/AST/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::SYCLUniqueStableNameExprClass:
+    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,74 @@
   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) {
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<TypeSourceInfo *>(1),
+                           alignof(SYCLUniqueStableNameExpr));
+  QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  return new (Mem)
+      SYCLUniqueStableNameExpr(OpLoc, LParen, RParen, ResultTy, TSI);
+}
+
+SYCLUniqueStableNameExpr *
+SYCLUniqueStableNameExpr::CreateEmpty(const ASTContext &Ctx) {
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<TypeSourceInfo *>(1),
+                           alignof(SYCLUniqueStableNameExpr));
+  QualType ResultTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  return new (Mem) 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 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 +3449,7 @@
   case SourceLocExprClass:
   case ConceptSpecializationExprClass:
   case RequiresExprClass:
+  case SYCLUniqueStableNameExprClass:
     // 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,10 @@
   return D;
 }
 
+ExprDependence clang::computeDependence(SYCLUniqueStableNameExpr *E) {
+  return toExprDependence(E->getTypeSourceInfo()->getType()->getDependence());
+}
+
 ExprDependence clang::computeDependence(PredefinedExpr *E) {
   return toExprDependence(E->getType()->getDependence()) &
          ~ExprDependence::UnexpandedPack;
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -11648,3 +11648,89 @@
   CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true);
   return CUIDHash;
 }
+
+// Get the closest named parent, so we can order the sycl naming decls somewhere
+// that mangling is meaningful.
+static const DeclContext *GetNamedParent(const CXXRecordDecl *RD) {
+  const DeclContext *DC = RD->getDeclContext();
+
+  while (!isa<NamedDecl, TranslationUnitDecl>(DC))
+    DC = DC->getParent();
+  return DC;
+}
+
+void ASTContext::AddSYCLKernelNamingDecl(const CXXRecordDecl *RD) {
+  assert((getLangOpts().SYCLIsDevice || getLangOpts().SYCLIsHost) &&
+         "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 CXXRecordDecl *RD) const {
+  assert((getLangOpts().SYCLIsDevice || getLangOpts().SYCLIsHost) &&
+         "Only valid for SYCL programs");
+  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((getLangOpts().SYCLIsDevice || getLangOpts().SYCLIsHost) &&
+         "Only valid for SYCL programs");
+  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,
+
+  // SYCLUniqueStableNameExpr
+  EXPR_SYCL_UNIQUE_STABLE_NAME,
 };
 
 /// 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,15 @@
   ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind);
   ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val);
 
+  ExprResult BuildSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen,
+                                           TypeSourceInfo *TSI);
+  ExprResult ActOnSYCLUniqueStableNameExpr(SourceLocation OpLoc,
+                                           SourceLocation LParen,
+                                           SourceLocation RParen,
+                                           ParsedType ParsedTy);
+
   bool CheckLoopHintExpr(Expr *E, SourceLocation Loc);
 
   ExprResult ActOnNumericConstant(const Token &Tok, Scope *UDLScope = nullptr);
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -1800,6 +1800,7 @@
   ExprResult ParsePostfixExpressionSuffix(ExprResult LHS);
   ExprResult ParseUnaryExprOrTypeTraitExpression();
   ExprResult ParseBuiltinPrimaryExpression();
+  ExprResult ParseSYCLUniqueStableNameExpression();
 
   ExprResult ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
                                                      bool &isCastExpr,
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ clang/include/clang/Basic/TokenKinds.def
@@ -694,11 +694,12 @@
 ALIAS("_pascal"      , __pascal   , KEYBORLAND)
 
 // Clang Extensions.
-KEYWORD(__builtin_convertvector     , KEYALL)
-ALIAS("__char16_t"   , char16_t     , KEYCXX)
-ALIAS("__char32_t"   , char32_t     , KEYCXX)
-KEYWORD(__builtin_bit_cast          , KEYALL)
-KEYWORD(__builtin_available         , KEYALL)
+KEYWORD(__builtin_convertvector          , KEYALL)
+ALIAS("__char16_t"   , char16_t          , KEYCXX)
+ALIAS("__char32_t"   , char32_t          , KEYCXX)
+KEYWORD(__builtin_bit_cast               , KEYALL)
+KEYWORD(__builtin_available              , KEYALL)
+KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL)
 
 // Clang-specific keywords enabled only in testing.
 TESTING_KEYWORD(__unknown_anytype , KEYALL)
Index: clang/include/clang/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 SYCLUniqueStableNameExpr : 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_sycl_unique_stable_name
+    : Error<"kernel instantiation changes the result of an evaluated "
+            "'__builtin_sycl_unique_stable_name'">;
+def note_sycl_unique_stable_name_evaluated_here
+    : Note<"'__builtin_sycl_unique_stable_name' evaluated here">;
 
 def warn_floatingpoint_eq : Warning<
   "comparing floating point with == or != is unsafe">,
Index: clang/include/clang/AST/TextNodeDumper.h
===================================================================
--- clang/include/clang/AST/TextNodeDumper.h
+++ clang/include/clang/AST/TextNodeDumper.h
@@ -249,6 +249,7 @@
   void VisitCastExpr(const CastExpr *Node);
   void VisitImplicitCastExpr(const ImplicitCastExpr *Node);
   void VisitDeclRefExpr(const DeclRefExpr *Node);
+  void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *Node);
   void VisitPredefinedExpr(const PredefinedExpr *Node);
   void VisitCharacterLiteral(const CharacterLiteral *Node);
   void VisitIntegerLiteral(const IntegerLiteral *Node);
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2651,6 +2651,9 @@
 DEF_TRAVERSE_STMT(ObjCAvailabilityCheckExpr, {})
 DEF_TRAVERSE_STMT(ParenExpr, {})
 DEF_TRAVERSE_STMT(ParenListExpr, {})
+DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, {
+  TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
+})
 DEF_TRAVERSE_STMT(PredefinedExpr, {})
 DEF_TRAVERSE_STMT(ShuffleVectorExpr, {})
 DEF_TRAVERSE_STMT(ConvertVectorExpr, {})
Index: clang/include/clang/AST/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 VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *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,76 @@
   }
 };
 
+// This represents a use of the __builtin_sycl_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 SYCLUniqueStableNameExpr final
+    : public Expr,
+      private llvm::TrailingObjects<SYCLUniqueStableNameExpr, TypeSourceInfo *> {
+  friend class ASTStmtReader;
+  friend TrailingObjects;
+  SourceLocation OpLoc, LParen, RParen;
+
+  SYCLUniqueStableNameExpr(EmptyShell Empty, QualType ResultTy);
+  SYCLUniqueStableNameExpr(SourceLocation OpLoc, SourceLocation LParen,
+                           SourceLocation RParen, QualType ResultTy,
+                           TypeSourceInfo *TSI);
+
+  size_t numTrailingObjects(OverloadToken<TypeSourceInfo *>) const {
+    return 1;
+  }
+
+  void setTypeSourceInfo(TypeSourceInfo *Ty) {
+    *getTrailingObjects<TypeSourceInfo *>() = Ty;
+  }
+
+  void setLocation(SourceLocation L) { OpLoc = L; }
+  void setLParenLocation(SourceLocation L) { LParen = L; }
+  void setRParenLocation(SourceLocation L) { RParen = L; }
+
+public:
+  TypeSourceInfo *getTypeSourceInfo() {
+    return *getTrailingObjects<TypeSourceInfo *>();
+  }
+
+  const TypeSourceInfo *getTypeSourceInfo() const {
+    return *getTrailingObjects<TypeSourceInfo *>();
+  }
+
+  static SYCLUniqueStableNameExpr *
+  Create(const ASTContext &Ctx, SourceLocation OpLoc, SourceLocation LParen,
+         SourceLocation RParen, TypeSourceInfo *TSI);
+
+  static SYCLUniqueStableNameExpr *CreateEmpty(const ASTContext &Ctx);
+
+  SourceLocation getBeginLoc() const { return getLocation(); }
+  SourceLocation getEndLoc() const { return RParen; }
+  SourceLocation getLocation() const { return OpLoc; }
+  SourceLocation getLParenLocation() const { return LParen; }
+  SourceLocation getRParenLocation() const { return RParen; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == SYCLUniqueStableNameExprClass;
+  }
+
+  // Iterators
+  child_range children() {
+    return child_range(child_iterator(), child_iterator());
+  }
+
+  const_child_range children() const {
+    return const_child_range(const_child_iterator(), const_child_iterator());
+  }
+
+  // Convenience function to generate the name of the currently stored type.
+  std::string ComputeName(ASTContext &Context) const;
+
+  // Get the generated name of the type.  Note that this only works after all
+  // kernels have been instantiated.
+  static std::string ComputeName(ASTContext &Context, QualType Ty);
+};
+
 /// ParenExpr - This represents a parethesized expression, e.g. "(1)".  This
 /// AST node is only formed if full location information is requested.
 class ParenExpr : public Expr {
Index: clang/include/clang/AST/ComputeDependence.h
===================================================================
--- clang/include/clang/AST/ComputeDependence.h
+++ clang/include/clang/AST/ComputeDependence.h
@@ -78,6 +78,7 @@
 class CXXFoldExpr;
 class TypeTraitExpr;
 class ConceptSpecializationExpr;
+class SYCLUniqueStableNameExpr;
 class PredefinedExpr;
 class CallExpr;
 class OffsetOfExpr;
@@ -165,6 +166,7 @@
 ExprDependence computeDependence(ConceptSpecializationExpr *E,
                                  bool ValueDependent);
 
+ExprDependence computeDependence(SYCLUniqueStableNameExpr *E);
 ExprDependence computeDependence(PredefinedExpr *E);
 ExprDependence computeDependence(CallExpr *E, llvm::ArrayRef<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 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.
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ clang/docs/LanguageExtensions.rst
@@ -1794,7 +1794,7 @@
   metaprogramming algorithms to be able to specify/detect types generically.
 
 - the generated kernel binary does not contain indirect calls because they
-  are eliminated using compiler optimizations e.g. devirtualization. 
+  are eliminated using compiler optimizations e.g. devirtualization.
 
 - the selected target supports the function pointer like functionality e.g.
   most CPU targets.
@@ -2404,6 +2404,44 @@
   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 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_sycl_unique_stable_name( type-id );
+
+  // Computes a unique stable name for the type of the given expression.
+  constexpr const char * __builtin_sycl_unique_stable_name( expression );
+
 Multiprecision Arithmetic Builtins
 ----------------------------------
 
@@ -2598,7 +2636,7 @@
 ``__builtin_memcpy_inline`` has been designed as a building block for efficient
 ``memcpy`` implementations. It is identical to ``__builtin_memcpy`` but also
 guarantees not to call any external functions. See LLVM IR `llvm.memcpy.inline
-<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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to