[clang] [SYCL] SYCL host kernel launch support for the sycl_kernel_entry_point attribute. (PR #152403)

Tom Honermann via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 20 22:05:39 PST 2025


https://github.com/tahonermann updated https://github.com/llvm/llvm-project/pull/152403

>From 919cd88a003bea179c89cf45a3f6c17e276bccbe Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Wed, 6 Aug 2025 19:26:54 -0700
Subject: [PATCH 01/17] [SYCL] SYCL host kernel launch support for the
 sycl_kernel_entry_point attribute.

The `sycl_kernel_entry_point` attribute facilitates the generation of an
offload kernel entry point function with parameters corresponding to the
(potentially decomposed) kernel arguments and a body that (potentially
reconstructs the arguments and) executes the kernel. This change adds
symmetric support for the SYCL host through an interface that provides
symbol names and (potentially decomposed) kernel arguments to the SYCL
library.

Consider the following function declared with the `sycl_kernel_entry_point`
attribute with a call to this function occurring in the implementation of
a SYCL kernel invocation function such as `sycl::handler::single_task()`.
  template<typename KernelNameType, typename KernelType>
  [[clang::sycl_kernel_entry_point(KernelNameType)]]
  void kernel_entry_point(KernelType kerne) {
    kernel();
  }

The body of the above function specifies the parameters and body of the
generated offload kernel entry point. Clearly, a call to the above function
by a SYCL kernel invocation function is not intended to execute the body
as written. Previously, code generation emitted an empty function body so
that calls to the function had no effect other than to trigger the generation
of the offload kernel entry point. The function body is therefore available
to hook for SYCL library support and is now substituted with a call to a
(SYCL library provided) function template named `sycl_enqueue_kernel_launch()`
with the kernel name type passed as the first template argument, the
symbol name of the offload kernel entry point passed as a string literal for
the first function argument, and the (possibly decomposed) parameters passed
as the remaining explicit function arguments. Given a call like this:
  kernel_entry_point<struct KN>([]{})
the body of the instantiated `kernel_entry_point()` specialization would be
substituted as follows with "kernel-symbol-name" substituted for the
generated symbol name and `kernel` forwarded (This assumes no kernel
argument decomposition; if decomposition was required, `kernel` would be
replaced with its corresponding decomposed arguments).
  sycl_enqueue_kernel_launch<KN>("kernel-symbol-name", kernel)

Name lookup and overload resolution for the `sycl_enqueue_kernel_launch()`
function is performed at the point of definition of the
`sycl_kernel_entry_point` attributed function (or the point of instantiation
for an instantiated function template specialization). If overload
resolution fails, the program is ill-formed.

Implementation of the `sycl_enqueue_kernel_launch()` function might require
additional information provided by the SYCL library. This is facilitated by
removing the previous prohibition against use of the `sycl_kernel_entry_point`
attribute with a non-static member function. If the `sycl_kernel_entry_point`
attributed function is a non-static member function, then overload resolution
for the `sycl_enqueue_kernel_launch()` function template may select a
non-static member function in which case, `this` will be implicitly passed
as the implicit object argument.

If a `sycl_kernel_entry_point` attributed function is a non-static member
function, use of `this` in a potentially evaluated expression is prohibited
in the definition (since `this` is not a kernel argument and will not be
available within the generated offload kernel entry point function).

Support for kernel argument decomposition and reconstruction is not yet
implemented.
---
 clang/include/clang/AST/ASTNodeTraverser.h    |   4 +-
 clang/include/clang/AST/RecursiveASTVisitor.h |   1 +
 clang/include/clang/AST/StmtSYCL.h            |  28 ++--
 clang/include/clang/Basic/AttrDocs.td         | 151 +++++++++++-------
 .../clang/Basic/DiagnosticSemaKinds.td        |   7 +-
 clang/lib/AST/ASTContext.cpp                  |   4 +
 clang/lib/AST/StmtPrinter.cpp                 |   2 +-
 clang/lib/CodeGen/CGStmt.cpp                  |  17 +-
 clang/lib/CodeGen/CodeGenFunction.h           |   2 +
 clang/lib/CodeGen/CodeGenSYCL.cpp             |  15 ++
 clang/lib/Sema/SemaDecl.cpp                   |   8 +-
 clang/lib/Sema/SemaExceptionSpec.cpp          |  11 +-
 clang/lib/Sema/SemaSYCL.cpp                   | 151 ++++++++++++++----
 clang/lib/Serialization/ASTReaderStmt.cpp     |   1 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |   1 +
 clang/test/AST/ast-print-sycl-kernel-call.cpp |  22 +++
 .../ast-dump-sycl-kernel-call-stmt.cpp        |  51 +++++-
 .../CodeGenSYCL/kernel-caller-entry-point.cpp |  13 +-
 ...-kernel-entry-point-attr-appertainment.cpp |  29 ++--
 .../sycl-kernel-entry-point-attr-grammar.cpp  |   2 +
 ...cl-kernel-entry-point-attr-kernel-name.cpp |   2 +
 .../sycl-kernel-entry-point-attr-sfinae.cpp   |   2 +
 22 files changed, 385 insertions(+), 139 deletions(-)
 create mode 100644 clang/test/AST/ast-print-sycl-kernel-call.cpp

diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index e74bb72571d64..0c64fb7d75df3 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -836,8 +836,10 @@ class ASTNodeTraverser
 
   void VisitSYCLKernelCallStmt(const SYCLKernelCallStmt *Node) {
     Visit(Node->getOriginalStmt());
-    if (Traversal != TK_IgnoreUnlessSpelledInSource)
+    if (Traversal != TK_IgnoreUnlessSpelledInSource) {
+      Visit(Node->getKernelLaunchStmt());
       Visit(Node->getOutlinedFunctionDecl());
+    }
   }
 
   void VisitOMPExecutableDirective(const OMPExecutableDirective *Node) {
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 8f427427d71ed..04a64539a4ee1 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3027,6 +3027,7 @@ DEF_TRAVERSE_STMT(CapturedStmt, { TRY_TO(TraverseDecl(S->getCapturedDecl())); })
 DEF_TRAVERSE_STMT(SYCLKernelCallStmt, {
   if (getDerived().shouldVisitImplicitCode()) {
     TRY_TO(TraverseStmt(S->getOriginalStmt()));
+    TRY_TO(TraverseStmt(S->getKernelLaunchStmt()));
     TRY_TO(TraverseDecl(S->getOutlinedFunctionDecl()));
     ShouldVisitChildren = false;
   }
diff --git a/clang/include/clang/AST/StmtSYCL.h b/clang/include/clang/AST/StmtSYCL.h
index 28ace12d7916b..70d8137992110 100644
--- a/clang/include/clang/AST/StmtSYCL.h
+++ b/clang/include/clang/AST/StmtSYCL.h
@@ -28,35 +28,45 @@ namespace clang {
 /// of such a function specifies the statements to be executed on a SYCL device
 /// to invoke a SYCL kernel with a particular set of kernel arguments. The
 /// SYCLKernelCallStmt associates an original statement (the compound statement
-/// that is the function body) with an OutlinedFunctionDecl that holds the
-/// kernel parameters and the transformed body. During code generation, the
-/// OutlinedFunctionDecl is used to emit an offload kernel entry point suitable
-/// for invocation from a SYCL library implementation. If executed, the
-/// SYCLKernelCallStmt behaves as a no-op; no code generation is performed for
-/// it.
+/// that is the function body) with a kernel launch statement to execute on a
+/// SYCL host and an OutlinedFunctionDecl that holds the kernel parameters and
+/// the transformed body to execute on a SYCL device. During code generation,
+/// the OutlinedFunctionDecl is used to emit an offload kernel entry point
+/// suitable for invocation from a SYCL library implementation.
 class SYCLKernelCallStmt : public Stmt {
   friend class ASTStmtReader;
   friend class ASTStmtWriter;
 
 private:
   Stmt *OriginalStmt = nullptr;
+  Stmt *KernelLaunchStmt = nullptr;
   OutlinedFunctionDecl *OFDecl = nullptr;
 
 public:
   /// Construct a SYCL kernel call statement.
-  SYCLKernelCallStmt(CompoundStmt *CS, OutlinedFunctionDecl *OFD)
-      : Stmt(SYCLKernelCallStmtClass), OriginalStmt(CS), OFDecl(OFD) {}
+  SYCLKernelCallStmt(CompoundStmt *CS, Stmt *S, OutlinedFunctionDecl *OFD)
+      : Stmt(SYCLKernelCallStmtClass), OriginalStmt(CS), KernelLaunchStmt(S),
+        OFDecl(OFD) {}
 
   /// Construct an empty SYCL kernel call statement.
   SYCLKernelCallStmt(EmptyShell Empty) : Stmt(SYCLKernelCallStmtClass, Empty) {}
 
-  /// Retrieve the model statement.
+  /// Retrieve the original statement.
   CompoundStmt *getOriginalStmt() { return cast<CompoundStmt>(OriginalStmt); }
   const CompoundStmt *getOriginalStmt() const {
     return cast<CompoundStmt>(OriginalStmt);
   }
+
+  /// Set the original statement.
   void setOriginalStmt(CompoundStmt *CS) { OriginalStmt = CS; }
 
+  /// Retrieve the kernel launch statement.
+  Stmt *getKernelLaunchStmt() { return KernelLaunchStmt; }
+  const Stmt *getKernelLaunchStmt() const { return KernelLaunchStmt; }
+
+  /// Set the kernel launch statement.
+  void setKernelLaunchStmt(Stmt *S) { KernelLaunchStmt = S; }
+
   /// Retrieve the outlined function declaration.
   OutlinedFunctionDecl *getOutlinedFunctionDecl() { return OFDecl; }
   const OutlinedFunctionDecl *getOutlinedFunctionDecl() const { return OFDecl; }
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index f1dbd8af6093a..4c4858b257d13 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -532,13 +532,13 @@ The following examples demonstrate the use of this attribute:
 def SYCLKernelEntryPointDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
-The ``sycl_kernel_entry_point`` attribute facilitates the generation of an
-offload kernel entry point, sometimes called a SYCL kernel caller function,
-suitable for invoking a SYCL kernel on an offload device. The attribute is
-intended for use in the implementation of SYCL kernel invocation functions
-like the ``single_task`` and ``parallel_for`` member functions of the
-``sycl::handler`` class specified in section 4.9.4, "Command group ``handler``
-class", of the SYCL 2020 specification.
+The ``sycl_kernel_entry_point`` attribute facilitates the launch of a SYCL
+kernel and the generation of an offload kernel entry point, sometimes called
+a SYCL kernel caller function, suitable for invoking a SYCL kernel on an
+offload device. The attribute is intended for use in the implementation of
+SYCL kernel invocation functions like the ``single_task`` and ``parallel_for``
+member functions of the ``sycl::handler`` class specified in section 4.9.4,
+"Command group ``handler`` class", of the SYCL 2020 specification.
 
 The attribute requires a single type argument that specifies a class type that
 meets the requirements for a SYCL kernel name as described in section 5.2,
@@ -550,7 +550,7 @@ The attribute only appertains to functions and only those that meet the
 following requirements.
 
 * Has a non-deduced ``void`` return type.
-* Is not a non-static member function, constructor, or destructor.
+* Is not a constructor or destructor.
 * Is not a C variadic function.
 * Is not a coroutine.
 * Is not defined as deleted or as defaulted.
@@ -565,39 +565,43 @@ follows.
 
   namespace sycl {
   class handler {
+    template<typename KernelNameType, typename... Ts>
+    void sycl_enqueue_kernel_launch(const char *KernelName, Ts...) {
+      // Call functions appropriate for the desired offload backend
+      // (OpenCL, CUDA, HIP, Level Zero, etc...) to enqueue kernel invocation.
+    }
+
     template<typename KernelNameType, typename KernelType>
     [[ clang::sycl_kernel_entry_point(KernelNameType) ]]
-    static void kernel_entry_point(KernelType kernel) {
-      kernel();
+    void kernel_entry_point(KernelType Kernel) {
+      Kernel();
     }
 
   public:
     template<typename KernelNameType, typename KernelType>
-    void single_task(KernelType kernel) {
-      // Call kernel_entry_point() to trigger generation of an offload
-      // kernel entry point.
-      kernel_entry_point<KernelNameType>(kernel);
-      // Call functions appropriate for the desired offload backend
-      // (OpenCL, CUDA, HIP, Level Zero, etc...).
+    void single_task(KernelType Kernel) {
+      // Call kernel_entry_point() to launch the kernel and to trigger
+      // generation of an offload kernel entry point.
+      kernel_entry_point<KernelNameType>(Kernel);
     }
   };
   } // namespace sycl
 
-A SYCL kernel is a callable object of class type that is constructed on a host,
-often via a lambda expression, and then passed to a SYCL kernel invocation
-function to be executed on an offload device. A SYCL kernel invocation function
-is responsible for copying the provided SYCL kernel object to an offload
-device and initiating a call to it. The SYCL kernel object and its data members
-constitute the parameters of an offload kernel.
-
-A SYCL kernel type is required to satisfy the device copyability requirements
-specified in section 3.13.1, "Device copyable", of the SYCL 2020 specification.
-Additionally, any data members of the kernel object type are required to satisfy
-section 4.12.4, "Rules for parameter passing to kernels". For most types, these
-rules require that the type is trivially copyable.  However, the SYCL
-specification mandates that certain special SYCL types, such as
-``sycl::accessor`` and ``sycl::stream`` be device copyable even if they are not
-trivially copyable. These types require special handling because they cannot
+A SYCL kernel object is a callable object of class type that is constructed on
+a host, often via a lambda expression, and then passed to a SYCL kernel
+invocation function to be executed on an offload device. A SYCL kernel
+invocation function is responsible for copying the provided SYCL kernel object
+to an offload device and initiating a call to it. The SYCL kernel object and
+its data members constitute the parameters of an offload kernel.
+
+A SYCL kernel object type is required to satisfy the device copyability
+requirements specified in section 3.13.1, "Device copyable", of the SYCL 2020
+specification. Additionally, any data members of the kernel object type are
+required to satisfy section 4.12.4, "Rules for parameter passing to kernels".
+For most types, these rules require that the type is trivially copyable.
+However, the SYCL specification mandates that certain special SYCL types, such
+as ``sycl::accessor`` and ``sycl::stream`` be device copyable even if they are
+not trivially copyable. These types require special handling because they cannot
 be copied to device memory as if by ``memcpy()``. Additionally, some offload
 backends, OpenCL for example, require objects of some of these types to be
 passed as individual arguments to the offload kernel.
@@ -612,7 +616,7 @@ like OpenCL):
 
 #. Identifying the offload kernel entry point to be used for the SYCL kernel.
 
-#. Deconstructing the SYCL kernel object, if necessary, to produce the set of
+#. Decomposing the SYCL kernel object, if necessary, to produce the set of
    offload kernel arguments required by the offload kernel entry point.
 
 #. Copying the offload kernel arguments to device memory.
@@ -621,17 +625,23 @@ like OpenCL):
 
 The offload kernel entry point for a SYCL kernel performs the following tasks:
 
-#. Reconstituting the SYCL kernel object, if necessary, using the offload
+#. Reconstructing the SYCL kernel object, if necessary, using the offload
    kernel parameters.
 
-#. Calling the ``operator()`` member function of the (reconstituted) SYCL kernel
+#. Calling the ``operator()`` member function of the (reconstructed) SYCL kernel
    object.
 
-The ``sycl_kernel_entry_point`` attribute automates generation of an offload
-kernel entry point that performs those latter tasks. The parameters and body of
-a function declared with the ``sycl_kernel_entry_point`` attribute specify a
-pattern from which the parameters and body of the entry point function are
-derived. Consider the following call to a SYCL kernel invocation function.
+The ``sycl_kernel_entry_point`` attribute facilitates or automates these tasks
+by generating the offload kernel entry point, generating a unique symbol name
+for it, synthesizing code for kernel argument decomposition and reconstruction,
+and synthesizing a call to a ``sycl_enqueue_kernel_launch`` function template
+with the kernel name type, kernel symbol name, and (decomposed) kernel arguments
+passed as template or function arguments.
+
+A function declared with the ``sycl_kernel_entry_point`` attribute specifies
+the parameters and body of the offload entry point function. Consider the
+following call to the ``single_task()`` SYCL kernel invocation function assuming
+an implementation similar to the one shown above.
 
 .. code-block:: c++
 
@@ -645,31 +655,33 @@ derived. Consider the following call to a SYCL kernel invocation function.
 The SYCL kernel object is the result of the lambda expression. It has two
 data members corresponding to the captures of ``sout`` and ``s``. Since one
 of these data members corresponds to a special SYCL type that must be passed
-individually as an offload kernel parameter, it is necessary to decompose the
-SYCL kernel object into its constituent parts; the offload kernel will have
-two kernel parameters. Given a SYCL implementation that uses a
-``sycl_kernel_entry_point`` attributed function like the one shown above, an
+individually as an offload kernel argument, it is necessary to decompose the
+SYCL kernel object into its constituent parts and pass them individually. An
 offload kernel entry point function will be generated that looks approximately
 as follows.
 
 .. code-block:: c++
 
   void sycl-kernel-caller-for-KN(sycl::stream sout, S s) {
-    kernel-type kernel = { sout, s );
-    kernel();
+    kernel-type Kernel = { sout, s );
+    Kernel();
   }
 
 There are a few items worthy of note:
 
 #. The name of the generated function incorporates the SYCL kernel name,
    ``KN``, that was passed as the ``KernelNameType`` template parameter to
-   ``kernel_entry_point()`` and provided as the argument to the
+   ``single_task()`` and eventually provided as the argument to the
    ``sycl_kernel_entry_point`` attribute. There is a one-to-one correspondence
    between SYCL kernel names and offload kernel entry points.
 
+#. The parameters and the call to ``Kernel()`` correspond to the definition of
+   ``kernel_entry_point()`` called by ``single_task()`` with the SYCL kernel
+   object argument decomposed and reconstructed.
+
 #. The SYCL kernel is a lambda closure type and therefore has no name;
    ``kernel-type`` is substituted above and corresponds to the ``KernelType``
-   template parameter deduced in the call to ``kernel_entry_point()``.
+   template parameter deduced in the call to ``single_task()``.
    Lambda types cannot be declared and initialized using the aggregate
    initialization syntax used above, but the intended behavior should be clear.
 
@@ -683,24 +695,55 @@ There are a few items worthy of note:
    or more parameters depending on how the SYCL library implementation defines
    these types.
 
-#. The call to ``kernel_entry_point()`` has no effect other than to trigger
-   emission of the entry point function. The statments that make up the body
-   of the function are not executed when the function is called; they are
-   only used in the generation of the entry point function.
+The call to ``kernel_entry_point()`` by ``single_task()`` is effectively
+replaced with synthesized code that looks approximately as follows.
+
+.. code-block:: c++
+
+  sycl::stream sout = Kernel.sout;
+  S s = Kernel.s;
+  sycl_enqueue_kernel_launch<KN>("kernel-symbol-name", sout, s);
+
+There are a few items worthy of note:
+
+#. The SYCL kernel object is a lambda closure type and its captures do not
+   have formal names and cannot be accessed using the member access syntax used
+   above, but the intended behavior should be clear.
+
+#. ``kernel-symbol-name`` is substituted for the actual symbol name that would
+   be generated; these names are implementation details subject to change.
+
+#. Lookup for the ``sycl_enqueue_kernel_launch()`` function template is
+   performed from the (possibly instantiated) location of the definition of
+   ``kernel_entry_point()``. If overload resolution fails, the program is
+   ill-formed. If the selected overload is a non-static member function, then
+   ``this`` is passed for the implicit object parameter.
+
+#. Function arguments passed to ``sycl_enqueue_kernel_launch()`` are passed
+   as if by ``std::forward<X>(x)``.
+
+#. The ``sycl_enqueue_kernel_launch()`` function is expected to be provided by
+   the SYCL library implementation. It is responsible for scheduling execution
+   of the generated offload kernel entry point identified by
+   ``kernel-symbol-name`` and copying the (decomposed) kernel arguments to
+   device memory, presumably via an offload backend such as OpenCL.
 
 It is not necessary for a function declared with the ``sycl_kernel_entry_point``
 attribute to be called for the offload kernel entry point to be emitted. For
 inline functions and function templates, any ODR-use will suffice. For other
 functions, an ODR-use is not required; the offload kernel entry point will be
-emitted if the function is defined.
+emitted if the function is defined. In any case, a call to the function is
+required for the synthesized call to ``sycl_enqueue_kernel_launch()`` to occur.
 
 Functions declared with the ``sycl_kernel_entry_point`` attribute are not
 limited to the simple example shown above. They may have additional template
 parameters, declare additional function parameters, and have complex control
-flow in the function body. Function parameter decomposition and reconstitution
+flow in the function body. Function parameter decomposition and reconstruction
 is performed for all function parameters. The function must abide by the
 language feature restrictions described in section 5.4, "Language restrictions
-for device functions" in the SYCL 2020 specification.
+for device functions" in the SYCL 2020 specification. If the function is a
+non-static member function, ``this`` shall not be used in a potentially
+evaluated expression.
   }];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3e864475f22a1..28d0aee6660e7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13062,9 +13062,10 @@ def warn_sycl_external_missing_on_first_decl : Warning<
 // SYCL kernel entry point diagnostics
 def err_sycl_entry_point_invalid : Error<
   "the %0 attribute cannot be applied to a"
-  " %select{non-static member function|variadic function|deleted function|"
-           "defaulted function|constexpr function|consteval function|"
-           "function declared with the 'noreturn' attribute|coroutine|"
+  " %select{variadic function|deleted function|defaulted function|"
+           "constructor|destructor|coroutine|"
+           "constexpr function|consteval function|"
+           "function declared with the 'noreturn' attribute|"
            "function defined with a function try block}1">;
 def err_sycl_entry_point_invalid_redeclaration : Error<
   "the %0 kernel name argument does not match prior"
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index fab907b9c1a40..948d13b67b45d 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -14975,6 +14975,10 @@ static SYCLKernelInfo BuildSYCLKernelInfo(ASTContext &Context,
   MC->mangleCanonicalTypeName(KernelNameType, Out);
   std::string KernelName = Out.str();
 
+  // FIXME: Diagnose kernel names that are not representable in the ordinary
+  // literal encoding. This is not necessarily the right place to add such
+  // a diagnostic.
+
   return {KernelNameType, FD, KernelName};
 }
 
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index ff8ca01ec5477..21a4001907439 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -595,7 +595,7 @@ void StmtPrinter::VisitCapturedStmt(CapturedStmt *Node) {
 }
 
 void StmtPrinter::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *Node) {
-  PrintStmt(Node->getOutlinedFunctionDecl()->getBody());
+  PrintStmt(Node->getOriginalStmt());
 }
 
 void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) {
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 36be3295950b8..29357a5442941 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -19,6 +19,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/Stmt.h"
+#include "clang/AST/StmtSYCL.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/DiagnosticSema.h"
@@ -543,21 +544,7 @@ bool CodeGenFunction::EmitSimpleStmt(const Stmt *S,
     EmitSEHLeaveStmt(cast<SEHLeaveStmt>(*S));
     break;
   case Stmt::SYCLKernelCallStmtClass:
-    // SYCL kernel call statements are generated as wrappers around the body
-    // of functions declared with the sycl_kernel_entry_point attribute. Such
-    // functions are used to specify how a SYCL kernel (a function object) is
-    // to be invoked; the SYCL kernel call statement contains a transformed
-    // variation of the function body and is used to generate a SYCL kernel
-    // caller function; a function that serves as the device side entry point
-    // used to execute the SYCL kernel. The sycl_kernel_entry_point attributed
-    // function is invoked by host code in order to trigger emission of the
-    // device side SYCL kernel caller function and to generate metadata needed
-    // by SYCL run-time library implementations; the function is otherwise
-    // intended to have no effect. As such, the function body is not evaluated
-    // as part of the invocation during host compilation (and the function
-    // should not be called or emitted during device compilation); the SYCL
-    // kernel call statement is thus handled as a null statement for the
-    // purpose of code generation.
+    EmitSYCLKernelCallStmt(cast<SYCLKernelCallStmt>(*S));
     break;
   }
   return true;
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 8c4c1c8c2dc95..b64d84c3f7fb6 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3643,6 +3643,8 @@ class CodeGenFunction : public CodeGenTypeCache {
   LValue EmitCoyieldLValue(const CoyieldExpr *E);
   RValue EmitCoroutineIntrinsic(const CallExpr *E, unsigned int IID);
 
+  void EmitSYCLKernelCallStmt(const SYCLKernelCallStmt &S);
+
   void EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock = false);
   void ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock = false);
 
diff --git a/clang/lib/CodeGen/CodeGenSYCL.cpp b/clang/lib/CodeGen/CodeGenSYCL.cpp
index 7d66d96ad0a1b..5ad5671250a92 100644
--- a/clang/lib/CodeGen/CodeGenSYCL.cpp
+++ b/clang/lib/CodeGen/CodeGenSYCL.cpp
@@ -17,6 +17,21 @@
 using namespace clang;
 using namespace CodeGen;
 
+void CodeGenFunction::EmitSYCLKernelCallStmt(const SYCLKernelCallStmt &S) {
+  if (getLangOpts().SYCLIsDevice) {
+    // A sycl_kernel_entry_point attributed function is unlikely to be emitted
+    // during device compilation, but might be if it is ODR-used from device
+    // code that is emitted. In these cases, the function is emitted with an
+    // empty body; the original body is emitted in the offload kernel entry
+    // point and the synthesized kernel launch code is only relevant for host
+    // compilation.
+    return;
+  }
+
+  assert(getLangOpts().SYCLIsHost);
+  EmitStmt(S.getKernelLaunchStmt());
+}
+
 static void SetSYCLKernelAttributes(llvm::Function *Fn, CodeGenFunction &CGF) {
   // SYCL 2020 device language restrictions require forward progress and
   // disallow recursion.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 086dd8ba1c670..b468f3b0fe214 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16412,19 +16412,19 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation,
         FD->getAttr<SYCLKernelEntryPointAttr>();
     if (FD->isDefaulted()) {
       Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-          << SKEPAttr << /*defaulted function*/ 3;
+          << SKEPAttr << /*defaulted function*/ 2;
       SKEPAttr->setInvalidAttr();
     } else if (FD->isDeleted()) {
       Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-          << SKEPAttr << /*deleted function*/ 2;
+          << SKEPAttr << /*deleted function*/ 1;
       SKEPAttr->setInvalidAttr();
     } else if (FSI->isCoroutine()) {
       Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-          << SKEPAttr << /*coroutine*/ 7;
+          << SKEPAttr << /*coroutine*/ 5;
       SKEPAttr->setInvalidAttr();
     } else if (Body && isa<CXXTryStmt>(Body)) {
       Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-          << SKEPAttr << /*function defined with a function try block*/ 8;
+          << SKEPAttr << /*function defined with a function try block*/ 9;
       SKEPAttr->setInvalidAttr();
     }
 
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index a0483c3027199..826f2ea9317c8 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -15,6 +15,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/StmtObjC.h"
+#include "clang/AST/StmtSYCL.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceManager.h"
@@ -1250,6 +1251,15 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     return CT;
   }
 
+  case Stmt::SYCLKernelCallStmtClass: {
+    auto *SKCS = cast<SYCLKernelCallStmt>(S);
+    if (getLangOpts().SYCLIsDevice)
+      return canSubStmtsThrow(*this,
+                              SKCS->getOutlinedFunctionDecl()->getBody());
+    assert(getLangOpts().SYCLIsHost);
+    return canSubStmtsThrow(*this, SKCS->getKernelLaunchStmt());
+  }
+
     // ObjC message sends are like function calls, but never have exception
     // specs.
   case Expr::ObjCMessageExprClass:
@@ -1430,7 +1440,6 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::AttributedStmtClass:
   case Stmt::BreakStmtClass:
   case Stmt::CapturedStmtClass:
-  case Stmt::SYCLKernelCallStmtClass:
   case Stmt::CaseStmtClass:
   case Stmt::CompoundStmtClass:
   case Stmt::ContinueStmtClass:
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 67f3856c10615..78761c54cb18d 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -315,43 +315,46 @@ void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {
     }
   }
 
-  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
-    if (!MD->isStatic()) {
-      Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-          << SKEPAttr << /*non-static member function*/ 0;
-      SKEPAttr->setInvalidAttr();
-    }
+  if (isa<CXXConstructorDecl>(FD)) {
+    Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
+        << SKEPAttr << /*constructor*/ 3;
+    SKEPAttr->setInvalidAttr();
+  }
+  if (isa<CXXDestructorDecl>(FD)) {
+    Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
+        << SKEPAttr << /*destructor*/ 4;
+    SKEPAttr->setInvalidAttr();
   }
 
   if (FD->isVariadic()) {
     Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-        << SKEPAttr << /*variadic function*/ 1;
+        << SKEPAttr << /*variadic function*/ 0;
     SKEPAttr->setInvalidAttr();
   }
 
   if (FD->isDefaulted()) {
     Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-        << SKEPAttr << /*defaulted function*/ 3;
+        << SKEPAttr << /*defaulted function*/ 2;
     SKEPAttr->setInvalidAttr();
   } else if (FD->isDeleted()) {
     Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-        << SKEPAttr << /*deleted function*/ 2;
+        << SKEPAttr << /*deleted function*/ 1;
     SKEPAttr->setInvalidAttr();
   }
 
   if (FD->isConsteval()) {
     Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-        << SKEPAttr << /*consteval function*/ 5;
+        << SKEPAttr << /*consteval function*/ 7;
     SKEPAttr->setInvalidAttr();
   } else if (FD->isConstexpr()) {
     Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-        << SKEPAttr << /*constexpr function*/ 4;
+        << SKEPAttr << /*constexpr function*/ 6;
     SKEPAttr->setInvalidAttr();
   }
 
   if (FD->isNoReturn()) {
     Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
-        << SKEPAttr << /*function declared with the 'noreturn' attribute*/ 6;
+        << SKEPAttr << /*function declared with the 'noreturn' attribute*/ 8;
     SKEPAttr->setInvalidAttr();
   }
 
@@ -389,6 +392,67 @@ void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {
 
 namespace {
 
+CompoundStmt *BuildSYCLKernelLaunchStmt(Sema &SemaRef, FunctionDecl *FD,
+                                        const std::string &KernelName) {
+  ASTContext &Ctx = SemaRef.getASTContext();
+  SmallVector<Stmt *> Stmts;
+
+  // Prepare a string literal that contains the kernel name in the ordinary
+  // literal encoding.
+  // FIXME: transcode the contents of KernelName from UTF-8 to the
+  // ordinary literal encoding.
+  QualType KernelNameCharTy = Ctx.CharTy.withConst();
+  llvm::APInt KernelNameSize(Ctx.getTypeSize(Ctx.getSizeType()),
+                             KernelName.size() + 1);
+  QualType KernelNameArrayTy = Ctx.getConstantArrayType(
+      KernelNameCharTy, KernelNameSize, nullptr, ArraySizeModifier::Normal, 0);
+  StringLiteral *KernelNameExpr = StringLiteral::Create(
+      Ctx, KernelName, StringLiteralKind::Ordinary,
+      /*Pascal*/ false, KernelNameArrayTy, SourceLocation());
+
+  // FIXME: An extern variable declaration with assignment to the kernel
+  // name expression is added to Stmts as a temporary measure to see results.
+  // reflected in tests. The kernel name expression will need to be passed as
+  // the first function argument in a call to sycl_enqueue_kernel_launch.
+  QualType ExternVarType = Ctx.getPointerType(Ctx.CharTy.withConst());
+  const IdentifierInfo *ExternVarName =
+      SemaRef.getPreprocessor().getIdentifierInfo("kernel_name");
+  VarDecl *ExternVarDecl = VarDecl::Create(
+      Ctx, FD, SourceLocation(), SourceLocation(), ExternVarName, ExternVarType,
+      /*TInfo*/ nullptr, SC_Extern);
+  DeclStmt *ExternVarDeclStmt = new (Ctx)
+      DeclStmt(DeclGroupRef(ExternVarDecl), SourceLocation(), SourceLocation());
+  Stmts.push_back(ExternVarDeclStmt);
+  DeclRefExpr *ExternVarDeclRef = new (Ctx) DeclRefExpr(
+      Ctx, ExternVarDecl, /*RefersToEnclosingVariableOrCapture*/ false,
+      ExternVarType, VK_LValue, SourceLocation());
+  ImplicitCastExpr *KernelNameArrayDecayExpr = new (Ctx) ImplicitCastExpr(
+      ImplicitCastExpr::OnStack, ExternVarType, CK_ArrayToPointerDecay,
+      KernelNameExpr, VK_PRValue, FPOptionsOverride());
+  BinaryOperator *AssignmentExpr = BinaryOperator::Create(
+      Ctx, ExternVarDeclRef, KernelNameArrayDecayExpr, BO_Assign, ExternVarType,
+      VK_LValue, OK_Ordinary, SourceLocation(), FPOptionsOverride());
+  Stmts.push_back(AssignmentExpr);
+
+  // Perform overload resolution for a call to an accessible (member) function
+  // template named 'sycl_enqueue_kernel_launch' from within the definition of
+  // FD where:
+  // - The kernel name type is passed as the first template argument.
+  // - Any remaining template parameters are deduced from the function arguments
+  //   or assigned by default template arguments.
+  // - 'this' is passed as the implicit function argument if 'FD' is a
+  //   non-static member function.
+  // - The name of the kernel, expressed as a string literal, is passed as the
+  //   first function argument.
+  // - The parameters of FD are forwarded as-if by 'std::forward()' as the
+  //   remaining explicit function arguments.
+  // - Any remaining function arguments are initialized by default arguments.
+  CompoundStmt *LaunchStmt = CompoundStmt::Create(
+      Ctx, Stmts, FPOptionsOverride(), SourceLocation(), SourceLocation());
+
+  return LaunchStmt;
+}
+
 // The body of a function declared with the [[sycl_kernel_entry_point]]
 // attribute is cloned and transformed to substitute references to the original
 // function parameters with references to replacement variables that stand in
@@ -432,6 +496,36 @@ class OutlinedFunctionDeclBodyInstantiator
   ParmDeclMap &MapRef;
 };
 
+OutlinedFunctionDecl *BuildSYCLKernelEntryPointOutline(Sema &SemaRef,
+                                                       FunctionDecl *FD,
+                                                       CompoundStmt *Body) {
+  using ParmDeclMap = OutlinedFunctionDeclBodyInstantiator::ParmDeclMap;
+  ParmDeclMap ParmMap;
+
+  OutlinedFunctionDecl *OFD = OutlinedFunctionDecl::Create(
+      SemaRef.getASTContext(), FD, FD->getNumParams());
+  unsigned i = 0;
+  for (ParmVarDecl *PVD : FD->parameters()) {
+    ImplicitParamDecl *IPD = ImplicitParamDecl::Create(
+        SemaRef.getASTContext(), OFD, SourceLocation(), PVD->getIdentifier(),
+        PVD->getType(), ImplicitParamKind::Other);
+    OFD->setParam(i, IPD);
+    ParmMap[PVD] = IPD;
+    ++i;
+  }
+
+  // FIXME: Diagnose (implicit or explicit) use of CXXThisExpr in potentially
+  // evaluated contexts in the function body. This is not necessarily the
+  // right place to add such a diagnostic.
+
+  OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap);
+  Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(Body).get();
+  OFD->setBody(OFDBody);
+  OFD->setNothrow();
+
+  return OFD;
+}
+
 } // unnamed namespace
 
 StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
@@ -440,6 +534,11 @@ StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
   assert(!FD->isTemplated());
   assert(FD->hasPrototype());
 
+  // The current context must be the function definition context to ensure
+  // that name lookup and parameter and local variable creation are performed
+  // within the correct scope.
+  assert(SemaRef.CurContext == FD);
+
   const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
   assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");
   assert(!SKEPAttr->isInvalidAttr() &&
@@ -451,29 +550,19 @@ StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
       getASTContext().getSYCLKernelInfo(SKEPAttr->getKernelName());
   assert(declaresSameEntity(SKI.getKernelEntryPointDecl(), FD) &&
          "SYCL kernel name conflict");
-  (void)SKI;
 
-  using ParmDeclMap = OutlinedFunctionDeclBodyInstantiator::ParmDeclMap;
-  ParmDeclMap ParmMap;
+  // Build the kernel launch statement.
+  Stmt *LaunchStmt =
+      BuildSYCLKernelLaunchStmt(SemaRef, FD, SKI.GetKernelName());
+  assert(LaunchStmt);
 
-  assert(SemaRef.CurContext == FD);
+  // Build the outline of the synthesized device entry point function.
   OutlinedFunctionDecl *OFD =
-      OutlinedFunctionDecl::Create(getASTContext(), FD, FD->getNumParams());
-  unsigned i = 0;
-  for (ParmVarDecl *PVD : FD->parameters()) {
-    ImplicitParamDecl *IPD = ImplicitParamDecl::Create(
-        getASTContext(), OFD, SourceLocation(), PVD->getIdentifier(),
-        PVD->getType(), ImplicitParamKind::Other);
-    OFD->setParam(i, IPD);
-    ParmMap[PVD] = IPD;
-    ++i;
-  }
+      BuildSYCLKernelEntryPointOutline(SemaRef, FD, Body);
+  assert(OFD);
 
-  OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap);
-  Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(Body).get();
-  OFD->setBody(OFDBody);
-  OFD->setNothrow();
-  Stmt *NewBody = new (getASTContext()) SYCLKernelCallStmt(Body, OFD);
+  Stmt *NewBody =
+      new (getASTContext()) SYCLKernelCallStmt(Body, LaunchStmt, OFD);
 
   return NewBody;
 }
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index eef97a8588f0b..6256b7352e425 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -532,6 +532,7 @@ void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) {
 void ASTStmtReader::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
   VisitStmt(S);
   S->setOriginalStmt(cast<CompoundStmt>(Record.readSubStmt()));
+  S->setKernelLaunchStmt(cast<Stmt>(Record.readSubStmt()));
   S->setOutlinedFunctionDecl(readDeclAs<OutlinedFunctionDecl>());
 }
 
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index acf345392aa1a..bbcb0b52d30db 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -625,6 +625,7 @@ void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
 void ASTStmtWriter::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
   VisitStmt(S);
   Record.AddStmt(S->getOriginalStmt());
+  Record.AddStmt(S->getKernelLaunchStmt());
   Record.AddDeclRef(S->getOutlinedFunctionDecl());
 
   Code = serialization::STMT_SYCLKERNELCALL;
diff --git a/clang/test/AST/ast-print-sycl-kernel-call.cpp b/clang/test/AST/ast-print-sycl-kernel-call.cpp
new file mode 100644
index 0000000000000..2243ee024be1a
--- /dev/null
+++ b/clang/test/AST/ast-print-sycl-kernel-call.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -fsycl-is-host -ast-print %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fsycl-is-device -ast-print %s -o - | FileCheck %s
+
+struct sycl_kernel_launcher {
+  template<typename KernelName, typename... Ts>
+  void sycl_enqueue_kernel_launch(const char *, Ts...) {}
+
+  template<typename KernelName, typename KernelType>
+  void kernel_entry_point(KernelType kernel) {
+    kernel();
+  }
+// CHECK:      template <typename KernelName, typename KernelType> void kernel_entry_point(KernelType kernel) {
+// CHECK-NEXT:     kernel();
+// CHECK-NEXT: }
+// CHECK:      template<> void kernel_entry_point<KN, (lambda at {{.*}})>((lambda at {{.*}}) kernel) {
+// CHECK-NEXT:     kernel();
+// CHECK-NEXT: }
+};
+
+void f(sycl_kernel_launcher skl) {
+  skl.kernel_entry_point<struct KN>([]{});
+}
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
index e3ff3dea19514..328899ed5ce7d 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
@@ -41,6 +41,13 @@ void skep1() {
 // CHECK:      |-FunctionDecl {{.*}} skep1 'void ()'
 // CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | | | |-DeclStmt {{.*}}
+// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
+// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
+// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
+// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi1EE"
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
 // CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<1>
@@ -77,6 +84,13 @@ void skep2<KN<2>>(K<2>);
 // CHECK-NEXT: |   | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
 // CHECK-NEXT: |   | |   `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
 // CHECK-NEXT: |   | |     `-DeclRefExpr {{.*}} 'K<2>' lvalue ParmVar {{.*}} 'k' 'K<2>'
+// CHECK-NEXT: |   | |-CompoundStmt {{.*}}
+// CHECK-NEXT: |   | | |-DeclStmt {{.*}}
+// CHECK-NEXT: |   | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
+// CHECK-NEXT: |   | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
+// CHECK-NEXT: |   | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
+// CHECK-NEXT: |   | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: |   | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi2EE"
 // CHECK-NEXT: |   | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: |   |   |-ImplicitParamDecl {{.*}} implicit used k 'K<2>'
 // CHECK-NEXT: |   |   `-CompoundStmt {{.*}}
@@ -123,6 +137,13 @@ void skep3<KN<3>>(K<3> k) {
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
 // CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'K<3>' lvalue ParmVar {{.*}} 'k' 'K<3>'
+// CHECK-NEXT: | | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | | | |-DeclStmt {{.*}}
+// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
+// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
+// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
+// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi3EE"
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<3>'
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
@@ -152,6 +173,13 @@ void skep4(K<4> k, int p1, int p2) {
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p1' 'int'
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
 // CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p2' 'int'
+// CHECK-NEXT: | | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | | | |-DeclStmt {{.*}}
+// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
+// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
+// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
+// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi4EE"
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<4>'
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used p1 'int'
@@ -182,7 +210,14 @@ void skep5(int unused1, K<5> k, int unused2, int p, int unused3) {
 // CHECK-NEXT: | |-ParmVarDecl {{.*}} unused3 'int'
 // CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
-// CHECK:      | | `-OutlinedFunctionDecl {{.*}}
+// CHECK:      | | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | | | |-DeclStmt {{.*}}
+// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
+// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
+// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
+// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi5EE"
+// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit unused1 'int'
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<5>'
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit unused2 'int'
@@ -227,6 +262,13 @@ void skep6(const S6 &k) {
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
 // CHECK-NEXT: | | |   `-DeclRefExpr {{.*}} 'const S6' lvalue ParmVar {{.*}} 'k' 'const S6 &'
+// CHECK-NEXT: | | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | | | |-DeclStmt {{.*}}
+// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
+// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
+// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
+// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi6EE"
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'const S6 &'
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
@@ -260,6 +302,13 @@ void skep7(S7 k) {
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const S7' lvalue <NoOp>
 // CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'S7' lvalue ParmVar {{.*}} 'k' 'S7'
+// CHECK-NEXT: | | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | | | |-DeclStmt {{.*}}
+// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
+// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
+// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
+// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi7EE"
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'S7'
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
diff --git a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
index cd1d4d801951d..729778aaee191 100644
--- a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
+++ b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
@@ -55,8 +55,8 @@ int main() {
 // Verify that SYCL kernel caller functions are not emitted during host
 // compilation.
 //
-// CHECK-HOST-NOT: _ZTS26single_purpose_kernel_name
-// CHECK-HOST-NOT: _ZTSZ4mainE18lambda_kernel_name
+// CHECK-HOST-NOT: define {{.*}} @_ZTS26single_purpose_kernel_name
+// CHECK-HOST-NOT: define {{.*}} @_ZTSZ4mainE18lambda_kernel_name
 
 // Verify that sycl_kernel_entry_point attributed functions are not emitted
 // during device compilation.
@@ -64,13 +64,13 @@ int main() {
 // CHECK-DEVICE-NOT: single_purpose_kernel_task
 // CHECK-DEVICE-NOT: kernel_single_task
 
-// Verify that no code is generated for the bodies of sycl_kernel_entry_point
-// attributed functions during host compilation. ODR-use of these functions may
-// require them to be emitted, but they have no effect if called.
+// Verify that kernel launch code is generated for sycl_kernel_entry_point
+// attributed functions during host compilation.
 //
 // CHECK-HOST-LINUX:      define dso_local void @_Z26single_purpose_kernel_task21single_purpose_kernel() #{{[0-9]+}} {
 // CHECK-HOST-LINUX-NEXT: entry:
 // CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %struct.single_purpose_kernel, align 1
+// CHECK-HOST-LINUX-NEXT:   store ptr @.str, ptr @kernel_name, align 8
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 //
@@ -79,6 +79,7 @@ int main() {
 // CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %class.anon, align 4
 // CHECK-HOST-LINUX-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon, ptr %kernelFunc, i32 0, i32 0
 // CHECK-HOST-LINUX-NEXT:   store i32 %kernelFunc.coerce, ptr %coerce.dive, align 4
+// CHECK-HOST-LINUX-NEXT:   store ptr @.str.1, ptr @kernel_name, align 8
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 //
@@ -87,6 +88,7 @@ int main() {
 // CHECK-HOST-WINDOWS-NEXT:   %kernelFunc = alloca %struct.single_purpose_kernel, align 1
 // CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %struct.single_purpose_kernel, ptr %kernelFunc, i32 0, i32 0
 // CHECK-HOST-WINDOWS-NEXT:   store i8 %kernelFunc.coerce, ptr %coerce.dive, align 1
+// CHECK-HOST-WINDOWS-NEXT:   store ptr @"??_C at _0CB@KFIJOMLB at _ZTS26single_purpose_kernel_name@", ptr @"?kernel_name@?0??single_purpose_kernel_task@@YAXUsingle_purpose_kernel@@@Z at 3PEBDEB", align 8
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
 //
@@ -95,6 +97,7 @@ int main() {
 // CHECK-HOST-WINDOWS-NEXT:   %kernelFunc = alloca %class.anon, align 4
 // CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon, ptr %kernelFunc, i32 0, i32 0
 // CHECK-HOST-WINDOWS-NEXT:   store i32 %kernelFunc.coerce, ptr %coerce.dive, align 4
+// CHECK-HOST-WINDOWS-NEXT:   store ptr @"??_C at _0BC@NHCDOLAA at _ZTSZ4mainEUlT_E_?$AA@", ptr @"?kernel_name@?0???$kernel_single_task at V<lambda_1>@?0??main@@9 at V1?0??2 at 9@@@YAXV<lambda_1>@?0??main@@9@@Z at 3PEBDEB", align 8
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
 
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
index 9aba284145fcb..3f07feb87c9a1 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
@@ -1,5 +1,8 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-device -verify %s
 
 // These tests validate appertainment for the sycl_kernel_entry_point attribute.
@@ -131,6 +134,16 @@ struct S15 {
   static T ok15();
 };
 
+struct S16 {
+  // Non-static member function declaration.
+  [[clang::sycl_kernel_entry_point(KN<16>)]]
+  void ok16();
+};
+
+#if __cplusplus >= 202302L
+auto ok17 = [] [[clang::sycl_kernel_entry_point(KN<17>)]] -> void {};
+#endif
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // Invalid declarations.
@@ -163,13 +176,6 @@ struct B2 {
   static int bad2;
 };
 
-struct B3 {
-  // Non-static member function declaration.
-  // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}}
-  [[clang::sycl_kernel_entry_point(BADKN<3>)]]
-  void bad3();
-};
-
 // expected-error at +1 {{'clang::sycl_kernel_entry_point' attribute only applies to functions}}
 namespace [[clang::sycl_kernel_entry_point(BADKN<4>)]] bad4 {}
 
@@ -244,13 +250,13 @@ void bad19() {
 #endif
 
 struct B20 {
-  // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}}
+  // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a constructor}}
   [[clang::sycl_kernel_entry_point(BADKN<20>)]]
   B20();
 };
 
 struct B21 {
-  // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}}
+  // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a destructor}}
   [[clang::sycl_kernel_entry_point(BADKN<21>)]]
   ~B21();
 };
@@ -337,11 +343,6 @@ struct B34 {
   [[noreturn]] friend void bad34() {}
 };
 
-#if __cplusplus >= 202302L
-// expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}}
-auto bad35 = [] [[clang::sycl_kernel_entry_point(BADKN<35>)]] -> void {};
-#endif
-
 #if __cplusplus >= 202302L
 // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute only applies to functions with a non-deduced 'void' return type}}
 auto bad36 = [] [[clang::sycl_kernel_entry_point(BADKN<36>)]] static {};
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
index 8f81fa218c171..fd1f00ae05d7a 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
@@ -1,4 +1,6 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s
 
 // These tests validate parsing of the sycl_kernel_entry_point argument list
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
index c7b83932fefe6..5a3b43be66daf 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
@@ -1,4 +1,6 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s
 
 // These tests validate that the kernel name type argument provided to the
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
index 4c61570419629..3689adaab9b5b 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
@@ -1,4 +1,6 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s
 
 // These tests are intended to validate that a sycl_kernel_entry_point attribute

>From 350a4764008ee40f7ec61cae7015c78fcacb9957 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Sat, 16 Aug 2025 10:54:45 -0700
Subject: [PATCH 02/17] Add diagnostics for use of 'this' in a potentially
 evaluated expression.

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/lib/Sema/SemaSYCL.cpp                   |  19 +-
 .../sycl-kernel-entry-point-attr-this.cpp     | 183 ++++++++++++++++++
 3 files changed, 202 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 28d0aee6660e7..89ff1ee16b2cf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13070,6 +13070,9 @@ def err_sycl_entry_point_invalid : Error<
 def err_sycl_entry_point_invalid_redeclaration : Error<
   "the %0 kernel name argument does not match prior"
   " declaration%diff{: $ vs $|}1,2">;
+def err_sycl_entry_point_invalid_this : Error<
+  "'this' cannot be%select{| implicitly}0 used in a potentially evaluated"
+  " expression in the body of a function declared with the %1 attribute">;
 def err_sycl_kernel_name_conflict : Error<
   "the %0 kernel name argument conflicts with a previous declaration">;
 def warn_sycl_kernel_name_not_a_class_type : Warning<
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 78761c54cb18d..312e689d105f4 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -463,9 +463,10 @@ class OutlinedFunctionDeclBodyInstantiator
 public:
   using ParmDeclMap = llvm::DenseMap<ParmVarDecl *, VarDecl *>;
 
-  OutlinedFunctionDeclBodyInstantiator(Sema &S, ParmDeclMap &M)
+  OutlinedFunctionDeclBodyInstantiator(Sema &S, ParmDeclMap &M,
+                                       FunctionDecl *FD)
       : TreeTransform<OutlinedFunctionDeclBodyInstantiator>(S), SemaRef(S),
-        MapRef(M) {}
+        MapRef(M), FD(FD) {}
 
   // A new set of AST nodes is always required.
   bool AlwaysRebuild() { return true; }
@@ -491,9 +492,20 @@ class OutlinedFunctionDeclBodyInstantiator
     return DRE;
   }
 
+  // Diagnose CXXThisExpr in a potentially evaluated expression.
+  ExprResult TransformCXXThisExpr(CXXThisExpr *CTE) {
+    if (SemaRef.currentEvaluationContext().isPotentiallyEvaluated()) {
+      SemaRef.Diag(CTE->getExprLoc(), diag::err_sycl_entry_point_invalid_this)
+          << (CTE->isImplicitCXXThis() ? /* implicit */ 1 : /* empty */ 0)
+          << FD->getAttr<SYCLKernelEntryPointAttr>();
+    }
+    return CTE;
+  }
+
 private:
   Sema &SemaRef;
   ParmDeclMap &MapRef;
+  FunctionDecl *FD;
 };
 
 OutlinedFunctionDecl *BuildSYCLKernelEntryPointOutline(Sema &SemaRef,
@@ -518,7 +530,8 @@ OutlinedFunctionDecl *BuildSYCLKernelEntryPointOutline(Sema &SemaRef,
   // evaluated contexts in the function body. This is not necessarily the
   // right place to add such a diagnostic.
 
-  OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap);
+  OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap,
+                                                           FD);
   Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(Body).get();
   OFD->setBody(OFDBody);
   OFD->setNothrow();
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
new file mode 100644
index 0000000000000..fc0640e1900cb
--- /dev/null
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
@@ -0,0 +1,183 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++17 -fsycl-is-host -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++17 -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++20 -fsycl-is-host -verify -DCXX20 %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++20 -fsycl-is-device -verify -DCXX20 %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++23 -fsycl-is-host -verify -DCXX23 %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++23 -fsycl-is-device -verify -DCXX23 %s
+
+// These tests validate diagnostics for invalid use of 'this' in the body of
+// a function declared with the sycl_kernel_entry_point attribute.
+
+
+template<typename T> struct remove_reference_t {
+  using type = T;
+};
+template<typename T> struct remove_reference_t<T&> {
+  using type = T;
+};
+
+namespace std {
+struct type_info {
+  virtual ~type_info();
+};
+} // namespace std
+
+////////////////////////////////////////////////////////////////////////////////
+// Valid declarations.
+////////////////////////////////////////////////////////////////////////////////
+template<int, int=0> struct KN;
+
+struct S1 {
+  [[clang::sycl_kernel_entry_point(KN<1>)]] void ok1() {
+    (void)sizeof(this);
+  }
+};
+
+struct S2 {
+  [[clang::sycl_kernel_entry_point(KN<2>)]] void ok2() {
+    (void)noexcept(this);
+  }
+};
+
+struct S3 {
+  [[clang::sycl_kernel_entry_point(KN<3>)]] void ok3() {
+    decltype(this) x = nullptr;
+  }
+};
+
+struct S4 {
+  static void smf();
+  [[clang::sycl_kernel_entry_point(KN<4>)]] void ok4() {
+    remove_reference_t<decltype(*this)>::type::smf();
+  }
+};
+
+struct S5 {
+  int dm;
+  void mf();
+  [[clang::sycl_kernel_entry_point(KN<5>)]] void ok5() {
+    (void)typeid(*this); // S5 is not abstract, so 'this' is not evaluated.
+    (void)typeid(dm);    // 'int' is not an abstract class type; implicit 'this' is not evaluated.
+    (void)typeid(mf());  // 'void' is not an abstract class type; implicit 'this' is not evaluated.
+  }
+};
+
+template<typename KN, bool B>
+struct S6 {
+  void mf() noexcept(B);
+  [[clang::sycl_kernel_entry_point(KN)]] void ok6() noexcept(noexcept(mf())) {}
+};
+template void S6<KN<6,0>, false>::ok6();
+template void S6<KN<6,1>, true>::ok6();
+
+template<typename KN, bool B>
+struct S7 {
+  void mf() noexcept(B);
+  [[clang::sycl_kernel_entry_point(KN)]] void ok7() noexcept(noexcept(this->mf())) {}
+};
+template void S7<KN<7,0>, false>::ok7();
+template void S7<KN<7,1>, true>::ok7();
+
+#if defined(CXX20)
+template<typename KN, typename T>
+struct S8 {
+  void mf(T);
+  [[clang::sycl_kernel_entry_point(KN)]] void ok8() requires(requires { mf(1); }) {}
+};
+template void S8<KN<8>, int>::ok8();
+
+template<typename KN, typename T>
+struct S9 {
+  void mf(T);
+  [[clang::sycl_kernel_entry_point(KN)]] void ok9() requires(requires { this->mf(1); }) {}
+};
+template void S9<KN<9>, int>::ok9();
+#endif
+
+#if defined(CXX23)
+struct S10 {
+  [[clang::sycl_kernel_entry_point(KN<10>)]] void ok10(this S10 self) {
+    (void)self;
+  }
+};
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Invalid declarations.
+////////////////////////////////////////////////////////////////////////////////
+
+template<int, int=0> struct BADKN;
+
+// expected-error at +3 {{'this' cannot be used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
+struct B1 {
+  [[clang::sycl_kernel_entry_point(BADKN<1>)]] void bad1() {
+    (void)this;
+  }
+};
+
+// expected-error at +4 {{'this' cannot be implicitly used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
+struct B2 {
+  int dm;
+  [[clang::sycl_kernel_entry_point(BADKN<2>)]] void bad2() {
+    (void)dm;
+  }
+};
+
+// expected-error at +4 {{'this' cannot be implicitly used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
+struct B3 {
+  void mf();
+  [[clang::sycl_kernel_entry_point(BADKN<3>)]] void bad3() {
+    (void)mf();
+  }
+};
+
+// expected-error at +4 {{'this' cannot be used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
+struct B4 {
+  virtual void vmf() = 0;
+  [[clang::sycl_kernel_entry_point(BADKN<4>)]] void bad4() {
+    (void)typeid(*this); // B4 is abstract, so 'this' is evaluated.
+  }
+};
+
+// A diagnostic is not currently issued for uninstantiated definitions. In this
+// case, a declaration is instantiated, but a definition isn't. A diagnostic
+// will be issued if a definition is instantiated (as the next test exercises).
+struct B5 {
+  template<typename KN>
+  [[clang::sycl_kernel_entry_point(KN)]] void bad5() {
+    (void)this;
+  }
+};
+extern template void B5::bad5<BADKN<5>>();
+
+// expected-error at +4 {{'this' cannot be used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
+struct B6 {
+  template<typename KN>
+  [[clang::sycl_kernel_entry_point(KN)]] void bad6() {
+    (void)this;
+  }
+};
+// expected-note at +1 {{in instantiation of function template specialization 'B6::bad6<BADKN<6>>' requested here}}
+template void B6::bad6<BADKN<6>>();
+
+// A diagnostic is not currently issued for uninstantiated definitions. In this
+// case, a declaration is instantiated, but a definition isn't. A diagnostic
+// will be issued if a definition is instantiated (as the next test exercises).
+template<typename KN>
+struct B7 {
+  [[clang::sycl_kernel_entry_point(KN)]] void bad7() {
+    (void)this;
+  }
+};
+extern template void B7<BADKN<7>>::bad7();
+
+// expected-error at +4 {{'this' cannot be used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
+template<typename KN>
+struct B8 {
+  [[clang::sycl_kernel_entry_point(KN)]] void bad8() {
+    (void)this;
+  }
+};
+// expected-note at +1 {{in instantiation of member function 'B8<BADKN<8>>::bad8' requested here}}
+template void B8<BADKN<8>>::bad8();

>From 1f1b8b76a4abb19ef081154fd7c7de3b00b29a1b Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Sat, 16 Aug 2025 11:01:00 -0700
Subject: [PATCH 03/17] Remove stale FIXME comment regarding diagnostics for
 'this'.

---
 clang/lib/Sema/SemaSYCL.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 312e689d105f4..7984914e424d3 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -526,10 +526,6 @@ OutlinedFunctionDecl *BuildSYCLKernelEntryPointOutline(Sema &SemaRef,
     ++i;
   }
 
-  // FIXME: Diagnose (implicit or explicit) use of CXXThisExpr in potentially
-  // evaluated contexts in the function body. This is not necessarily the
-  // right place to add such a diagnostic.
-
   OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap,
                                                            FD);
   Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(Body).get();

>From 2a1c23d09a77121fa80e928db1eb36e241b5cea6 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Sun, 17 Aug 2025 18:32:54 -0700
Subject: [PATCH 04/17] Add tests for kernel name character encoding concerns.

---
 clang/lib/AST/ASTContext.cpp                  |  4 --
 clang/lib/Sema/SemaSYCL.cpp                   |  5 +-
 .../ast-dump-sycl-kernel-call-stmt.cpp        | 21 ++++++
 .../CodeGenSYCL/kernel-caller-entry-point.cpp | 65 ++++++++++++++++++-
 4 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 948d13b67b45d..fab907b9c1a40 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -14975,10 +14975,6 @@ static SYCLKernelInfo BuildSYCLKernelInfo(ASTContext &Context,
   MC->mangleCanonicalTypeName(KernelNameType, Out);
   std::string KernelName = Out.str();
 
-  // FIXME: Diagnose kernel names that are not representable in the ordinary
-  // literal encoding. This is not necessarily the right place to add such
-  // a diagnostic.
-
   return {KernelNameType, FD, KernelName};
 }
 
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 7984914e424d3..be9ae1856855e 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -397,10 +397,7 @@ CompoundStmt *BuildSYCLKernelLaunchStmt(Sema &SemaRef, FunctionDecl *FD,
   ASTContext &Ctx = SemaRef.getASTContext();
   SmallVector<Stmt *> Stmts;
 
-  // Prepare a string literal that contains the kernel name in the ordinary
-  // literal encoding.
-  // FIXME: transcode the contents of KernelName from UTF-8 to the
-  // ordinary literal encoding.
+  // Prepare a string literal that contains the kernel name.
   QualType KernelNameCharTy = Ctx.CharTy.withConst();
   llvm::APInt KernelNameSize(Ctx.getTypeSize(Ctx.getSizeType()),
                              KernelName.size() + 1);
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
index 328899ed5ce7d..cc26d1d195dde 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
@@ -319,6 +319,27 @@ void skep7(S7 k) {
 // CHECK-NEXT: | |         `-DeclRefExpr {{.*}} 'S7' lvalue ImplicitParam {{.*}} 'k' 'S7'
 // CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<7>
 
+// Symbol names generated for the kernel entry point function should be
+// representable in the ordinary literal encoding even when the kernel name
+// type is named with esoteric characters.
+struct \u03b4\u03c4\u03c7; // Delta Tau Chi (δτχ)
+struct S8 {
+  void operator()() const;
+};
+[[clang::sycl_kernel_entry_point(\u03b4\u03c4\u03c7)]]
+void skep8(S8 k) {
+  k();
+}
+// CHECK:      |-FunctionDecl {{.*}} skep8 'void (S8)'
+// CHECK-NEXT: | |-ParmVarDecl {{.*}} used k 'S8'
+// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
+// CHECK-NEXT: | | |-CompoundStmt {{.*}}
+// CHECK:      | | |-CompoundStmt {{.*}}
+// CHECK:      | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[12]' lvalue "_ZTS6\316\264\317\204\317\207"
+// CHECK:      | | `-OutlinedFunctionDecl {{.*}}
+// CHECK:      | `-SYCLKernelEntryPointAttr {{.*}}
+
 
 void the_end() {}
 // CHECK:      `-FunctionDecl {{.*}} the_end 'void ()'
diff --git a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
index 729778aaee191..e88e4b7cf3149 100644
--- a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
+++ b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
@@ -44,19 +44,24 @@ void kernel_single_task(KernelType kernelFunc) {
   kernelFunc(42);
 }
 
+// Exercise code gen with kernel name types named with esoteric characters.
+struct \u03b4\u03c4\u03c7; // Delta Tau Chi (δτχ)
+
 int main() {
   single_purpose_kernel obj;
   single_purpose_kernel_task(obj);
   int capture;
   auto lambda = [=](auto) { (void) capture; };
   kernel_single_task<decltype(lambda)>(lambda);
+  kernel_single_task<\u03b4\u03c4\u03c7>([](int){});
 }
 
 // Verify that SYCL kernel caller functions are not emitted during host
 // compilation.
 //
 // CHECK-HOST-NOT: define {{.*}} @_ZTS26single_purpose_kernel_name
-// CHECK-HOST-NOT: define {{.*}} @_ZTSZ4mainE18lambda_kernel_name
+// CHECK-HOST-NOT: define {{.*}} @_ZTSZ4mainEUlT_E_
+// CHECK-HOST-NOT: define {{.*}} @"_ZTS6\CE\B4\CF\84\CF\87"
 
 // Verify that sycl_kernel_entry_point attributed functions are not emitted
 // during device compilation.
@@ -67,6 +72,10 @@ int main() {
 // Verify that kernel launch code is generated for sycl_kernel_entry_point
 // attributed functions during host compilation.
 //
+// CHECK-HOST-LINUX:      @.str = private unnamed_addr constant [33 x i8] c"_ZTS26single_purpose_kernel_name\00", align 1
+// CHECK-HOST-LINUX:      @.str.1 = private unnamed_addr constant [18 x i8] c"_ZTSZ4mainEUlT_E_\00", align 1
+// CHECK-HOST-LINUX:      @.str.2 = private unnamed_addr constant [12 x i8] c"_ZTS6\CE\B4\CF\84\CF\87\00", align 1
+//
 // CHECK-HOST-LINUX:      define dso_local void @_Z26single_purpose_kernel_task21single_purpose_kernel() #{{[0-9]+}} {
 // CHECK-HOST-LINUX-NEXT: entry:
 // CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %struct.single_purpose_kernel, align 1
@@ -83,6 +92,13 @@ int main() {
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 //
+// CHECK-HOST-LINUX:      define internal void @"_Z18kernel_single_taskI6\CE\B4\CF\84\CF\87Z4mainEUliE_EvT0_"() #{{[0-9]+}} {
+// CHECK-HOST-LINUX-NEXT: entry:
+// CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %class.anon.0, align 1
+// CHECK-HOST-LINUX-NEXT:   store ptr @.str.2, ptr @kernel_name, align 8
+// CHECK-HOST-LINUX-NEXT:   ret void
+// CHECK-HOST-LINUX-NEXT: }
+//
 // CHECK-HOST-WINDOWS:      define dso_local void @"?single_purpose_kernel_task@@YAXUsingle_purpose_kernel@@@Z"(i8 %kernelFunc.coerce) #{{[0-9]+}} {
 // CHECK-HOST-WINDOWS-NEXT: entry:
 // CHECK-HOST-WINDOWS-NEXT:   %kernelFunc = alloca %struct.single_purpose_kernel, align 1
@@ -100,6 +116,15 @@ int main() {
 // CHECK-HOST-WINDOWS-NEXT:   store ptr @"??_C at _0BC@NHCDOLAA at _ZTSZ4mainEUlT_E_?$AA@", ptr @"?kernel_name@?0???$kernel_single_task at V<lambda_1>@?0??main@@9 at V1?0??2 at 9@@@YAXV<lambda_1>@?0??main@@9@@Z at 3PEBDEB", align 8
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
+//
+// CHECK-HOST-WINDOWS:      define internal void @"??$kernel_single_task at U\CE\B4\CF\84\CF\87@@V<lambda_2>@?0??main@@9@@@YAXV<lambda_2>@?0??main@@9@@Z"(i8 %kernelFunc.coerce) #{{[0-9]+}} {
+// CHECK-HOST-WINDOWS-NEXT: entry:
+// CHECK-HOST-WINDOWS-NEXT:   %kernelFunc = alloca %class.anon.0, align 1
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon.0, ptr %kernelFunc, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   store i8 %kernelFunc.coerce, ptr %coerce.dive, align 1
+// CHECK-HOST-WINDOWS-NEXT:   store ptr @"??_C at _0M@BCGAEMBE at _ZTS6?N?$LE?O?$IE?O?$IH?$AA@", ptr @"?kernel_name@?0???$kernel_single_task at U\CE\B4\CF\84\CF\87@@V<lambda_2>@?0??main@@9@@@YAXV<lambda_2>@?0??main@@9@@Z at 3PEBDEB", align 8
+// CHECK-HOST-WINDOWS-NEXT:   ret void
+// CHECK-HOST-WINDOWS-NEXT: }
 
 // Verify that SYCL kernel caller functions are emitted for each device target.
 //
@@ -182,6 +207,44 @@ int main() {
 // CHECK-SPIR-NEXT:   }
 // CHECK-SPIR:        define internal spir_func void @_ZZ4mainENKUlT_E_clIiEEDaS_
 
+// IR for the SYCL kernel caller function generated for kernel_single_task with
+// the Delta Tau Chi type as the SYCL kernel name type.
+//
+// CHECK-AMDGCN:      Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
+// CHECK-AMDGCN-NEXT: define dso_local amdgpu_kernel void @"_ZTS6\CE\B4\CF\84\CF\87"
+// CHECK-AMDGCN-SAME:   (ptr addrspace(4) noundef byref(%class.anon.0) align 1 %0) #[[AMDGCN_ATTR0]] {
+// CHECK-AMDGCN-NEXT: entry:
+// CHECK-AMDGCN-NEXT:   %coerce = alloca %class.anon.0, align 1, addrspace(5)
+// CHECK-AMDGCN-NEXT:   %kernelFunc = addrspacecast ptr addrspace(5) %coerce to ptr
+// CHECK-AMDGCN-NEXT:   call void @llvm.memcpy.p0.p4.i64(ptr align 1 %kernelFunc, ptr addrspace(4) align 1 %0, i64 1, i1 false)
+// CHECK-AMDGCN-NEXT:   call void @_ZZ4mainENKUliE_clEi
+// CHECK-AMDGCN-SAME:     (ptr noundef nonnull align 1 dereferenceable(1) %kernelFunc, i32 noundef 42) #[[AMDGCN_ATTR1:[0-9]+]]
+// CHECK-AMDGCN-NEXT:   ret void
+// CHECK-AMDGCN-NEXT: }
+// CHECK-AMDGCN:      define internal void @_ZZ4mainENKUliE_clEi
+//
+// CHECK-NVPTX:       Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
+// CHECK-NVPTX-NEXT:  define dso_local ptx_kernel void @"_ZTS6\CE\B4\CF\84\CF\87"
+// CHECK-NVPTX-SAME:    (ptr noundef byval(%class.anon.0) align 1 %kernelFunc) #[[NVPTX_ATTR0:[0-9]+]] {
+// CHECK-NVPTX-NEXT:  entry:
+// CHECK-NVPTX-NEXT:    call void @_ZZ4mainENKUliE_clEi
+// CHECK-NVPTX-SAME:      (ptr noundef nonnull align 1 dereferenceable(1) %kernelFunc, i32 noundef 42) #[[NVPTX_ATTR1:[0-9]+]]
+// CHECK-NVPTX-NEXT:    ret void
+// CHECK-NVPTX-NEXT:  }
+// CHECK-NVPTX:       define internal void @_ZZ4mainENKUliE_clEi
+//
+// CHECK-SPIR:        Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
+// CHECK-SPIR-NEXT:   define {{[a-z_ ]*}}spir_kernel void @"_ZTS6\CE\B4\CF\84\CF\87"
+// CHECK-SPIR-SAME:     (ptr noundef byval(%class.anon.0) align 1 %kernelFunc) #[[SPIR_ATTR0:[0-9]+]] {
+// CHECK-SPIR-NEXT:   entry:
+// CHECK-SPIR-NEXT:     %kernelFunc.ascast = addrspacecast ptr %kernelFunc to ptr addrspace(4)
+// CHECK-SPIR-NEXT:     call spir_func void @_ZZ4mainENKUliE_clEi
+// CHECK-SPIR-SAME:       (ptr addrspace(4) noundef align 1 dereferenceable_or_null(1) %kernelFunc.ascast, i32 noundef 42) #[[SPIR_ATTR1:[0-9]+]]
+// CHECK-SPIR-NEXT:     ret void
+// CHECK-SPIR-NEXT:   }
+// CHECK-SPIR:        define internal spir_func void @_ZZ4mainENKUliE_clEi
+
+
 // CHECK-AMDGCN: #[[AMDGCN_ATTR0]] = { convergent mustprogress noinline norecurse nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // CHECK-AMDGCN: #[[AMDGCN_ATTR1]] = { convergent nounwind }
 //

>From c1ffcf8dc2c0bfb6a39a95087ae458b57be9f567 Mon Sep 17 00:00:00 2001
From: Mariya Podchishchaeva <mariya.podchishchaeva at intel.com>
Date: Mon, 29 Sep 2025 19:16:00 +0200
Subject: [PATCH 05/17] [SYCL-Upstreaming] Add support for host kernel launch
 stmt generation (#51)

* Add support for host kernel launch stmt generation

This adds generation of a call to sycl_enqueue_kernel_launch function
aka "launcher" function. The launcher function can be a memeber of a
class or a free function defined at namespace scope. The lookup is
performed from SKEP attributed function scope. Because unqualified
lookup requires Scope object present and it only exists during parsing
stage and already EOLed at the point where templates instantiated, I had
to move some parts of SYCLKernelCallStmt generation to earlier stages
and now TreeTransform knows how to process SYCLKernelCallStmt.
I also had to invent a new expression - UnresolvedSYCLKernelExpr which
represents a string containing kernel name of a kernel that doesn't
exist yet. This expression is supposed to be transformed to a
StringLiteral during template instantiation phase. It should never reach
AST consumers like CodeGen of constexpr evaluators. This still requires
more testing and FIXME cleanups, but since it evolved into a quite
complicated patch I'm pushing it for earlier feedback.

* Remove a fixme from SemaSYCL

* Do not crash if original body was invalid

* Add AST test for skep-attributed member

* Fix a warning

* Extend codegen test a bit

* Find and replace

UnresolvedSYCLKernelNameExpr -> UnresolvedSYCLKernelLaunchExpr

* Implement the thing

* One more find and replace

* I don't know how it looks like

* Find and replace again

* Switch to UnresolvedSYCLKernelEntryPointStmt

* Apply suggestions from code review

* Remove log.txt

* Implement visiting

* Add tests

* Apply suggestions from code review

Co-authored-by: Tom Honermann <tom at honermann.net>

* IdExpr -> KernelLaunchIdExpr

* Don't rely on compound

* UnresolvedSYCLKernelEntryPointStmt -> UnresolvedSYCLKernelCall

* Fix warnings

* Rename sycl_enqueue_kernel_launch -> sycl_kernel_launch

* Apply suggestions from code review

Co-authored-by: Tom Honermann <tom at honermann.net>

* Remove array decay

* Add windows run line to the sema test

---------

Co-authored-by: Tom Honermann <tom at honermann.net>
---
 clang/include/clang/AST/RecursiveASTVisitor.h |   7 +
 clang/include/clang/AST/StmtSYCL.h            |  53 +++++
 clang/include/clang/Basic/AttrDocs.td         |  14 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   9 +
 clang/include/clang/Basic/StmtNodes.td        |   1 +
 clang/include/clang/Sema/ScopeInfo.h          |   4 +
 clang/include/clang/Sema/SemaSYCL.h           |   6 +-
 .../include/clang/Serialization/ASTBitCodes.h |   3 +
 clang/lib/AST/ComputeDependence.cpp           |   1 +
 clang/lib/AST/StmtPrinter.cpp                 |   5 +
 clang/lib/AST/StmtProfile.cpp                 |   5 +
 clang/lib/CodeGen/CGStmt.cpp                  |   1 +
 clang/lib/Sema/SemaDecl.cpp                   |  33 ++-
 clang/lib/Sema/SemaExceptionSpec.cpp          |   3 +
 clang/lib/Sema/SemaSYCL.cpp                   | 173 ++++++++++-----
 clang/lib/Sema/TreeTransform.h                |  19 ++
 clang/lib/Serialization/ASTReaderStmt.cpp     |  12 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |  10 +
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   1 +
 clang/test/AST/ast-print-sycl-kernel-call.cpp |   2 +-
 .../ast-dump-sycl-kernel-call-stmt.cpp        | 209 ++++++++++++++----
 .../ast-dump-sycl-kernel-entry-point.cpp      |   3 +
 .../CodeGenSYCL/kernel-caller-entry-point.cpp |  91 +++++++-
 .../unique_stable_name_windows_diff.cpp       |   2 +
 .../test/SemaSYCL/sycl-host-kernel-launch.cpp | 199 +++++++++++++++++
 ...-kernel-entry-point-attr-appertainment.cpp |   4 +
 .../sycl-kernel-entry-point-attr-grammar.cpp  |   4 +
 ...el-entry-point-attr-kernel-name-module.cpp |   6 +-
 ...ernel-entry-point-attr-kernel-name-pch.cpp |   9 +-
 ...cl-kernel-entry-point-attr-kernel-name.cpp |   6 +
 .../sycl-kernel-entry-point-attr-sfinae.cpp   |   5 +
 .../sycl-kernel-entry-point-attr-this.cpp     |   5 +
 clang/tools/libclang/CXCursor.cpp             |   1 +
 33 files changed, 780 insertions(+), 126 deletions(-)
 create mode 100644 clang/test/SemaSYCL/sycl-host-kernel-launch.cpp

diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 04a64539a4ee1..974edc89073cc 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2990,6 +2990,13 @@ DEF_TRAVERSE_STMT(ParenListExpr, {})
 DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, {
   TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
 })
+DEF_TRAVERSE_STMT(UnresolvedSYCLKernelCallStmt, {
+  if (getDerived().shouldVisitImplicitCode()) {
+    TRY_TO(TraverseStmt(S->getOriginalStmt()));
+    TRY_TO(TraverseStmt(S->getKernelLaunchIdExpr()));
+    ShouldVisitChildren = false;
+  }
+})
 DEF_TRAVERSE_STMT(OpenACCAsteriskSizeExpr, {})
 DEF_TRAVERSE_STMT(PredefinedExpr, {})
 DEF_TRAVERSE_STMT(ShuffleVectorExpr, {})
diff --git a/clang/include/clang/AST/StmtSYCL.h b/clang/include/clang/AST/StmtSYCL.h
index 70d8137992110..c8da4987321a9 100644
--- a/clang/include/clang/AST/StmtSYCL.h
+++ b/clang/include/clang/AST/StmtSYCL.h
@@ -99,6 +99,59 @@ class SYCLKernelCallStmt : public Stmt {
   }
 };
 
+// UnresolvedSYCLKernelCallStmt represents an invocation of a SYCL kernel in
+// a dependent context for which lookup of the sycl_enqueue_kernel_launch
+// identifier cannot be performed. These statements are transformed to
+// SYCLKernelCallStmt during template instantiation.
+class UnresolvedSYCLKernelCallStmt : public Stmt {
+  friend class ASTStmtReader;
+  Stmt *OriginalStmt = nullptr;
+  // KernelLaunchIdExpr stores an UnresolvedLookupExpr or UnresolvedMemberExpr
+  // corresponding to the SYCL kernel launch function for which a call
+  // will be synthesized during template instantiation.
+  Expr *KernelLaunchIdExpr = nullptr;
+  UnresolvedSYCLKernelCallStmt(CompoundStmt *CS, Expr *IdExpr)
+      : Stmt(UnresolvedSYCLKernelCallStmtClass), OriginalStmt(CS),
+        KernelLaunchIdExpr(IdExpr) {}
+
+  void setKernelLaunchIdExpr(Expr *IdExpr) { KernelLaunchIdExpr = IdExpr; }
+  void setOriginalStmt(CompoundStmt *CS) { OriginalStmt = CS; }
+
+public:
+  static UnresolvedSYCLKernelCallStmt *
+  Create(const ASTContext &C, CompoundStmt *CS, Expr *IdExpr) {
+    return new (C) UnresolvedSYCLKernelCallStmt(CS, IdExpr);
+  }
+
+  static UnresolvedSYCLKernelCallStmt *CreateEmpty(const ASTContext &C) {
+    return new (C) UnresolvedSYCLKernelCallStmt(nullptr, nullptr);
+  }
+
+  Expr *getKernelLaunchIdExpr() const { return KernelLaunchIdExpr; }
+  CompoundStmt *getOriginalStmt() { return cast<CompoundStmt>(OriginalStmt); }
+  const CompoundStmt *getOriginalStmt() const {
+    return cast<CompoundStmt>(OriginalStmt);
+  }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY {
+    return getOriginalStmt()->getBeginLoc();
+  }
+
+  SourceLocation getEndLoc() const LLVM_READONLY {
+    return getOriginalStmt()->getEndLoc();
+  }
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == UnresolvedSYCLKernelCallStmtClass;
+  }
+  child_range children() {
+    return child_range(&OriginalStmt, &OriginalStmt + 1);
+  }
+
+  const_child_range children() const {
+    return const_child_range(&OriginalStmt, &OriginalStmt + 1);
+  }
+};
+
 } // end namespace clang
 
 #endif // LLVM_CLANG_AST_STMTSYCL_H
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 4c4858b257d13..01013d10e8ddc 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -566,7 +566,7 @@ follows.
   namespace sycl {
   class handler {
     template<typename KernelNameType, typename... Ts>
-    void sycl_enqueue_kernel_launch(const char *KernelName, Ts...) {
+    void sycl_kernel_launch(const char *KernelName, Ts...) {
       // Call functions appropriate for the desired offload backend
       // (OpenCL, CUDA, HIP, Level Zero, etc...) to enqueue kernel invocation.
     }
@@ -634,7 +634,7 @@ The offload kernel entry point for a SYCL kernel performs the following tasks:
 The ``sycl_kernel_entry_point`` attribute facilitates or automates these tasks
 by generating the offload kernel entry point, generating a unique symbol name
 for it, synthesizing code for kernel argument decomposition and reconstruction,
-and synthesizing a call to a ``sycl_enqueue_kernel_launch`` function template
+and synthesizing a call to a ``sycl_kernel_launch`` function template
 with the kernel name type, kernel symbol name, and (decomposed) kernel arguments
 passed as template or function arguments.
 
@@ -702,7 +702,7 @@ replaced with synthesized code that looks approximately as follows.
 
   sycl::stream sout = Kernel.sout;
   S s = Kernel.s;
-  sycl_enqueue_kernel_launch<KN>("kernel-symbol-name", sout, s);
+  sycl_kernel_launch<KN>("kernel-symbol-name", sout, s);
 
 There are a few items worthy of note:
 
@@ -713,16 +713,16 @@ There are a few items worthy of note:
 #. ``kernel-symbol-name`` is substituted for the actual symbol name that would
    be generated; these names are implementation details subject to change.
 
-#. Lookup for the ``sycl_enqueue_kernel_launch()`` function template is
+#. Lookup for the ``sycl_kernel_launch()`` function template is
    performed from the (possibly instantiated) location of the definition of
    ``kernel_entry_point()``. If overload resolution fails, the program is
    ill-formed. If the selected overload is a non-static member function, then
    ``this`` is passed for the implicit object parameter.
 
-#. Function arguments passed to ``sycl_enqueue_kernel_launch()`` are passed
+#. Function arguments passed to ``sycl_kernel_launch()`` are passed
    as if by ``std::forward<X>(x)``.
 
-#. The ``sycl_enqueue_kernel_launch()`` function is expected to be provided by
+#. The ``sycl_kernel_launch()`` function is expected to be provided by
    the SYCL library implementation. It is responsible for scheduling execution
    of the generated offload kernel entry point identified by
    ``kernel-symbol-name`` and copying the (decomposed) kernel arguments to
@@ -733,7 +733,7 @@ attribute to be called for the offload kernel entry point to be emitted. For
 inline functions and function templates, any ODR-use will suffice. For other
 functions, an ODR-use is not required; the offload kernel entry point will be
 emitted if the function is defined. In any case, a call to the function is
-required for the synthesized call to ``sycl_enqueue_kernel_launch()`` to occur.
+required for the synthesized call to ``sycl_kernel_launch()`` to occur.
 
 Functions declared with the ``sycl_kernel_entry_point`` attribute are not
 limited to the simple example shown above. They may have additional template
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 89ff1ee16b2cf..47360f29f27f5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13088,6 +13088,15 @@ def err_sycl_entry_point_return_type : Error<
 def err_sycl_entry_point_deduced_return_type : Error<
   "the %0 attribute only applies to functions with a non-deduced 'void' return"
   " type">;
+def err_sycl_host_no_launch_function : Error<
+  "unable to find suitable 'sycl_kernel_launch' function for host code "
+  "synthesis">;
+def warn_sycl_device_no_host_launch_function : Warning<
+  "unable to find suitable 'sycl_kernel_launch' function for host code "
+  "synthesis">,
+  InGroup<DiagGroup<"sycl-host-launcher">>;
+def note_sycl_host_launch_function : Note<
+  "define 'sycl_kernel_launch' function template to fix this problem">;
 
 def warn_cuda_maxclusterrank_sm_90 : Warning<
   "maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index bf3686bb372d5..b681d3e086cd2 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -23,6 +23,7 @@ def CaseStmt : StmtNode<SwitchCase>;
 def DefaultStmt : StmtNode<SwitchCase>;
 def CapturedStmt : StmtNode<Stmt>;
 def SYCLKernelCallStmt : StmtNode<Stmt>;
+def UnresolvedSYCLKernelCallStmt : StmtNode<Stmt>;
 
 // Break/continue.
 def LoopControlStmt : StmtNode<Stmt, 1>;
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index 4f4d38c961140..f334f58ebd0a7 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -245,6 +245,10 @@ class FunctionScopeInfo {
   /// The set of GNU address of label extension "&&label".
   llvm::SmallVector<AddrLabelExpr *, 4> AddrLabels;
 
+  /// An unresolved identifier lookup expression for an implicit call
+  /// to a SYCL kernel launch function in a dependent context.
+  Expr *SYCLKernelLaunchIdExpr = nullptr;
+
 public:
   /// Represents a simple identification of a weak object.
   ///
diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h
index 7ae556da2bec1..76046b765c0d6 100644
--- a/clang/include/clang/Sema/SemaSYCL.h
+++ b/clang/include/clang/Sema/SemaSYCL.h
@@ -66,7 +66,11 @@ class SemaSYCL : public SemaBase {
 
   void CheckSYCLExternalFunctionDecl(FunctionDecl *FD);
   void CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD);
-  StmtResult BuildSYCLKernelCallStmt(FunctionDecl *FD, CompoundStmt *Body);
+  StmtResult BuildSYCLKernelCallStmt(FunctionDecl *FD, CompoundStmt *Body,
+                                     Expr *LaunchIdExpr);
+  ExprResult BuildSYCLKernelLaunchIdExpr(FunctionDecl *FD, QualType KNT);
+  StmtResult BuildUnresolvedSYCLKernelCallStmt(CompoundStmt *CS,
+                                                     Expr *IdExpr);
 };
 
 } // namespace clang
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 5d09d5536e5ab..32b9803a2cc53 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1615,6 +1615,9 @@ enum StmtCode {
   /// A SYCLKernelCallStmt record.
   STMT_SYCLKERNELCALL,
 
+  /// A SYCLKernelCallStmt record.
+  STMT_UNRESOLVED_SYCL_KERNEL_CALL,
+
   /// A GCC-style AsmStmt record.
   STMT_GCCASM,
 
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 638080ea781a9..a20ea95a5c997 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/ExprOpenMP.h"
+#include "clang/AST/StmtSYCL.h"
 #include "clang/Basic/ExceptionSpecificationType.h"
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 21a4001907439..efc7b9d73bfb6 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1442,6 +1442,11 @@ void StmtPrinter::VisitSYCLUniqueStableNameExpr(
   OS << ")";
 }
 
+void StmtPrinter::VisitUnresolvedSYCLKernelCallStmt(
+    UnresolvedSYCLKernelCallStmt *Node) {
+  PrintStmt(Node->getOriginalStmt());
+}
+
 void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) {
   OS << PredefinedExpr::getIdentKindName(Node->getIdentKind());
 }
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 4a8c638c85331..de9628128ba94 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -1402,6 +1402,11 @@ void StmtProfiler::VisitSYCLUniqueStableNameExpr(
   VisitType(S->getTypeSourceInfo()->getType());
 }
 
+void StmtProfiler::VisitUnresolvedSYCLKernelCallStmt(
+    const UnresolvedSYCLKernelCallStmt *S) {
+  VisitStmt(S);
+}
+
 void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) {
   VisitExpr(S);
   ID.AddInteger(llvm::to_underlying(S->getIdentKind()));
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 29357a5442941..529218cac6315 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -117,6 +117,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
   case Stmt::CaseStmtClass:
   case Stmt::SEHLeaveStmtClass:
   case Stmt::SYCLKernelCallStmtClass:
+  case Stmt::UnresolvedSYCLKernelCallStmtClass:
     llvm_unreachable("should have emitted these statements as simple");
 
 #define STMT(Type, Base)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b468f3b0fe214..c984e3d55ff58 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15806,7 +15806,6 @@ Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D,
   if (!Bases.empty())
     OpenMP().ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(Dcl,
                                                                         Bases);
-
   return Dcl;
 }
 
@@ -16227,6 +16226,20 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
 
   maybeAddDeclWithEffects(FD);
 
+  if (FD && !FD->isInvalidDecl() &&
+      FD->hasAttr<SYCLKernelEntryPointAttr>() && FnBodyScope) {
+    // Building KernelLaunchIdExpr requires performing an unqualified lookup
+    // which can only be done correctly while the stack of parsing scopes is
+    // alive, so we do it here when we start parsing function body even if it is
+    // a templated function.
+    const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
+    if (!SKEPAttr->isInvalidAttr()) {
+      ExprResult LaunchIdExpr =
+          SYCL().BuildSYCLKernelLaunchIdExpr(FD, SKEPAttr->getKernelName());
+      getCurFunction()->SYCLKernelLaunchIdExpr = LaunchIdExpr.get();
+    }
+  }
+
   return D;
 }
 
@@ -16428,9 +16441,21 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation,
       SKEPAttr->setInvalidAttr();
     }
 
-    if (Body && !FD->isTemplated() && !SKEPAttr->isInvalidAttr()) {
-      StmtResult SR =
-          SYCL().BuildSYCLKernelCallStmt(FD, cast<CompoundStmt>(Body));
+    // We don't need to build SYCLKernelCallStmt for template instantiations
+    // since it was already created by template instantiator.
+    if (Body && !SKEPAttr->isInvalidAttr()) {
+      StmtResult SR;
+      if (FD->isTemplated()) {
+        SR = SYCL().BuildUnresolvedSYCLKernelCallStmt(
+            cast<CompoundStmt>(Body), getCurFunction()->SYCLKernelLaunchIdExpr);
+      } else if (FD->isTemplateInstantiation()) {
+        assert(isa<SYCLKernelCallStmt>(Body));
+        SR = Body;
+      } else {
+        SR = SYCL().BuildSYCLKernelCallStmt(
+            FD, cast<CompoundStmt>(Body),
+            getCurFunction()->SYCLKernelLaunchIdExpr);
+      }
       if (SR.isInvalid())
         return nullptr;
       Body = SR.get();
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 826f2ea9317c8..8477161c38b02 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1260,6 +1260,9 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     return canSubStmtsThrow(*this, SKCS->getKernelLaunchStmt());
   }
 
+  case Stmt::UnresolvedSYCLKernelCallStmtClass:
+    return CT_Dependent;
+
     // ObjC message sends are like function calls, but never have exception
     // specs.
   case Expr::ObjCMessageExprClass:
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index be9ae1856855e..f6a5fab2fe86e 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -390,53 +390,23 @@ void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {
   }
 }
 
-namespace {
+ExprResult SemaSYCL::BuildSYCLKernelLaunchIdExpr(FunctionDecl *FD,
+                                                 QualType KNT) {
 
-CompoundStmt *BuildSYCLKernelLaunchStmt(Sema &SemaRef, FunctionDecl *FD,
-                                        const std::string &KernelName) {
   ASTContext &Ctx = SemaRef.getASTContext();
-  SmallVector<Stmt *> Stmts;
+  // Some routines need a valid source location to work correctly.
+  SourceLocation BodyLoc =
+      FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation();
 
-  // Prepare a string literal that contains the kernel name.
-  QualType KernelNameCharTy = Ctx.CharTy.withConst();
-  llvm::APInt KernelNameSize(Ctx.getTypeSize(Ctx.getSizeType()),
-                             KernelName.size() + 1);
-  QualType KernelNameArrayTy = Ctx.getConstantArrayType(
-      KernelNameCharTy, KernelNameSize, nullptr, ArraySizeModifier::Normal, 0);
-  StringLiteral *KernelNameExpr = StringLiteral::Create(
-      Ctx, KernelName, StringLiteralKind::Ordinary,
-      /*Pascal*/ false, KernelNameArrayTy, SourceLocation());
-
-  // FIXME: An extern variable declaration with assignment to the kernel
-  // name expression is added to Stmts as a temporary measure to see results.
-  // reflected in tests. The kernel name expression will need to be passed as
-  // the first function argument in a call to sycl_enqueue_kernel_launch.
-  QualType ExternVarType = Ctx.getPointerType(Ctx.CharTy.withConst());
-  const IdentifierInfo *ExternVarName =
-      SemaRef.getPreprocessor().getIdentifierInfo("kernel_name");
-  VarDecl *ExternVarDecl = VarDecl::Create(
-      Ctx, FD, SourceLocation(), SourceLocation(), ExternVarName, ExternVarType,
-      /*TInfo*/ nullptr, SC_Extern);
-  DeclStmt *ExternVarDeclStmt = new (Ctx)
-      DeclStmt(DeclGroupRef(ExternVarDecl), SourceLocation(), SourceLocation());
-  Stmts.push_back(ExternVarDeclStmt);
-  DeclRefExpr *ExternVarDeclRef = new (Ctx) DeclRefExpr(
-      Ctx, ExternVarDecl, /*RefersToEnclosingVariableOrCapture*/ false,
-      ExternVarType, VK_LValue, SourceLocation());
-  ImplicitCastExpr *KernelNameArrayDecayExpr = new (Ctx) ImplicitCastExpr(
-      ImplicitCastExpr::OnStack, ExternVarType, CK_ArrayToPointerDecay,
-      KernelNameExpr, VK_PRValue, FPOptionsOverride());
-  BinaryOperator *AssignmentExpr = BinaryOperator::Create(
-      Ctx, ExternVarDeclRef, KernelNameArrayDecayExpr, BO_Assign, ExternVarType,
-      VK_LValue, OK_Ordinary, SourceLocation(), FPOptionsOverride());
-  Stmts.push_back(AssignmentExpr);
+  IdentifierInfo &LaunchFooName =
+      Ctx.Idents.get("sycl_kernel_launch", tok::TokenKind::identifier);
 
   // Perform overload resolution for a call to an accessible (member) function
-  // template named 'sycl_enqueue_kernel_launch' from within the definition of
-  // FD where:
+  // template named 'sycl_kernel_launch' "from within the definition of
+  // FD where":
   // - The kernel name type is passed as the first template argument.
-  // - Any remaining template parameters are deduced from the function arguments
-  //   or assigned by default template arguments.
+  // - Any remaining template parameters are deduced from the function
+  //   arguments or assigned by default template arguments.
   // - 'this' is passed as the implicit function argument if 'FD' is a
   //   non-static member function.
   // - The name of the kernel, expressed as a string literal, is passed as the
@@ -444,10 +414,108 @@ CompoundStmt *BuildSYCLKernelLaunchStmt(Sema &SemaRef, FunctionDecl *FD,
   // - The parameters of FD are forwarded as-if by 'std::forward()' as the
   //   remaining explicit function arguments.
   // - Any remaining function arguments are initialized by default arguments.
-  CompoundStmt *LaunchStmt = CompoundStmt::Create(
-      Ctx, Stmts, FPOptionsOverride(), SourceLocation(), SourceLocation());
+  LookupResult Result(SemaRef, &LaunchFooName, BodyLoc,
+                      Sema::LookupOrdinaryName);
+  CXXScopeSpec SS;
+  SemaRef.LookupTemplateName(Result, SemaRef.getCurScope(), SS,
+                             /*ObjectType=*/QualType(),
+                             /*EnteringContext=*/false, BodyLoc);
+
+  if (Result.empty() || Result.isAmbiguous()) {
+    SemaRef.Diag(BodyLoc, SemaRef.getLangOpts().SYCLIsHost
+                              ? diag::err_sycl_host_no_launch_function
+                              : diag::warn_sycl_device_no_host_launch_function);
+    SemaRef.Diag(BodyLoc, diag::note_sycl_host_launch_function);
+
+    return ExprError();
+  }
+
+  TemplateArgumentListInfo TALI{BodyLoc, BodyLoc};
+  TemplateArgument KNTA = TemplateArgument(KNT);
+  TemplateArgumentLoc TAL =
+      SemaRef.getTrivialTemplateArgumentLoc(KNTA, QualType(), BodyLoc);
+  TALI.addArgument(TAL);
+  ExprResult IdExpr;
+  if (SemaRef.isPotentialImplicitMemberAccess(SS, Result,
+                                              /*IsAddressOfOperand=*/false))
+    // BuildPossibleImplicitMemberExpr creates UnresolvedMemberExpr. Using it
+    // allows to pass implicit/explicit this argument automatically.
+    IdExpr = SemaRef.BuildPossibleImplicitMemberExpr(SS, BodyLoc, Result, &TALI,
+                                                     SemaRef.getCurScope());
+  else
+    IdExpr = SemaRef.BuildTemplateIdExpr(SS, BodyLoc, Result,
+                                         /*RequiresADL=*/true, &TALI);
+
+  // Can happen if SKEP attributed function is a static member, but the launcher
+  // is a regular member. Perhaps emit a note saying that we're in host code
+  // synthesis.
+  if (IdExpr.isInvalid())
+    return ExprError();
+
+  return IdExpr;
+}
 
-  return LaunchStmt;
+StmtResult SemaSYCL::BuildUnresolvedSYCLKernelCallStmt(CompoundStmt *CS,
+                                                             Expr *IdExpr) {
+  return UnresolvedSYCLKernelCallStmt::Create(SemaRef.getASTContext(), CS,
+                                                    IdExpr);
+}
+
+namespace {
+
+void PrepareKernelArgumentsForKernelLaunch(SmallVectorImpl<Expr *> &Args,
+                                                  const SYCLKernelInfo *SKI,
+                                                  Sema &SemaRef,
+                                                  SourceLocation Loc) {
+  assert(SKI && "Need a kernel!");
+  ASTContext &Ctx = SemaRef.getASTContext();
+
+  // Prepare a string literal that contains the kernel name.
+  const std::string KernelName = SKI->GetKernelName();
+  QualType KernelNameCharTy = Ctx.CharTy.withConst();
+  llvm::APInt KernelNameSize(Ctx.getTypeSize(Ctx.getSizeType()),
+                             KernelName.size() + 1);
+  QualType KernelNameArrayTy = Ctx.getConstantArrayType(
+      KernelNameCharTy, KernelNameSize, nullptr, ArraySizeModifier::Normal, 0);
+  Expr *KernelNameExpr =
+      StringLiteral::Create(Ctx, KernelName, StringLiteralKind::Ordinary,
+                            /*Pascal*/ false, KernelNameArrayTy, Loc);
+  Args.push_back(KernelNameExpr);
+
+  // Right now we simply forward the arguments of the skep-attributed function.
+  // With decomposition present there can be another logic.
+  // Make sure to use CurContext to avoid diagnostics that we're using a
+  // variable coming from another context. The function should be the same as in
+  // the kernel info though.
+  auto *FD = cast<FunctionDecl>(SemaRef.CurContext);
+  assert(declaresSameEntity(FD, SKI->getKernelEntryPointDecl()));
+  for (ParmVarDecl *PVD : FD->parameters()) {
+    QualType ParamType = PVD->getOriginalType().getNonReferenceType();
+    Expr *DRE = SemaRef.BuildDeclRefExpr(PVD, ParamType, VK_LValue, Loc);
+    assert(DRE);
+    Args.push_back(DRE);
+  }
+}
+
+StmtResult BuildSYCLKernelLaunchStmt(Sema &SemaRef,
+                                            const SYCLKernelInfo *SKI,
+                                            Expr *IdExpr, SourceLocation Loc) {
+  SmallVector<Stmt *> Stmts;
+  assert(SKI && "Need a Kernel!");
+
+  if (IdExpr) {
+    llvm::SmallVector<Expr *, 12> Args;
+    PrepareKernelArgumentsForKernelLaunch(Args, SKI, SemaRef, Loc);
+    ExprResult LaunchResult =
+        SemaRef.BuildCallExpr(SemaRef.getCurScope(), IdExpr, Loc, Args, Loc);
+    if (LaunchResult.isInvalid())
+      return StmtError();
+
+    Stmts.push_back(LaunchResult.get());
+  }
+
+  return CompoundStmt::Create(SemaRef.getASTContext(), Stmts,
+                              FPOptionsOverride(), Loc, Loc);
 }
 
 // The body of a function declared with the [[sycl_kernel_entry_point]]
@@ -535,11 +603,11 @@ OutlinedFunctionDecl *BuildSYCLKernelEntryPointOutline(Sema &SemaRef,
 } // unnamed namespace
 
 StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
-                                             CompoundStmt *Body) {
+                                             CompoundStmt *Body,
+                                             Expr *LaunchIdExpr) {
   assert(!FD->isInvalidDecl());
   assert(!FD->isTemplated());
   assert(FD->hasPrototype());
-
   // The current context must be the function definition context to ensure
   // that name lookup and parameter and local variable creation are performed
   // within the correct scope.
@@ -557,18 +625,19 @@ StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
   assert(declaresSameEntity(SKI.getKernelEntryPointDecl(), FD) &&
          "SYCL kernel name conflict");
 
-  // Build the kernel launch statement.
-  Stmt *LaunchStmt =
-      BuildSYCLKernelLaunchStmt(SemaRef, FD, SKI.GetKernelName());
-  assert(LaunchStmt);
-
   // Build the outline of the synthesized device entry point function.
   OutlinedFunctionDecl *OFD =
       BuildSYCLKernelEntryPointOutline(SemaRef, FD, Body);
   assert(OFD);
 
+  // Build host kernel launch stmt.
+  SourceLocation BodyLoc =
+      FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation();
+  StmtResult LaunchRes =
+      BuildSYCLKernelLaunchStmt(SemaRef, &SKI, LaunchIdExpr, BodyLoc);
+
   Stmt *NewBody =
-      new (getASTContext()) SYCLKernelCallStmt(Body, LaunchStmt, OFD);
+      new (getASTContext()) SYCLKernelCallStmt(Body, LaunchRes.get(), OFD);
 
   return NewBody;
 }
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c2491489f40d2..6e57cbe5239ce 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12966,6 +12966,25 @@ ExprResult TreeTransform<Derived>::TransformSYCLUniqueStableNameExpr(
       E->getLocation(), E->getLParenLocation(), E->getRParenLocation(), NewT);
 }
 
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformUnresolvedSYCLKernelCallStmt(
+    UnresolvedSYCLKernelCallStmt *S) {
+  ExprResult IdExpr = getDerived().TransformExpr(S->getKernelLaunchIdExpr());
+
+  if (IdExpr.isInvalid())
+     return StmtError();
+
+  StmtResult Body = getDerived().TransformStmt(S->getOriginalStmt());
+  if (Body.isInvalid())
+     return StmtError();
+
+  StmtResult SR = SemaRef.SYCL().BuildSYCLKernelCallStmt(
+      cast<FunctionDecl>(SemaRef.CurContext), cast<CompoundStmt>(Body.get()),
+      IdExpr.get());
+
+  return SR;
+}
+
 template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformPredefinedExpr(PredefinedExpr *E) {
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 6256b7352e425..234bf0f30d85a 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -598,6 +598,14 @@ void ASTStmtReader::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
   E->setTypeSourceInfo(Record.readTypeSourceInfo());
 }
 
+void ASTStmtReader::VisitUnresolvedSYCLKernelCallStmt(
+    UnresolvedSYCLKernelCallStmt *S) {
+  VisitStmt(S);
+
+  S->setOriginalStmt(cast<CompoundStmt>(Record.readSubStmt()));
+  S->setKernelLaunchIdExpr(Record.readExpr());
+}
+
 void ASTStmtReader::VisitPredefinedExpr(PredefinedExpr *E) {
   VisitExpr(E);
   bool HasFunctionName = Record.readInt();
@@ -3183,6 +3191,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       S = SYCLUniqueStableNameExpr::CreateEmpty(Context);
       break;
 
+    case STMT_UNRESOLVED_SYCL_KERNEL_CALL:
+      S = UnresolvedSYCLKernelCallStmt::CreateEmpty(Context);
+      break;
+
     case EXPR_OPENACC_ASTERISK_SIZE:
       S = OpenACCAsteriskSizeExpr::CreateEmpty(Context);
       break;
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index bbcb0b52d30db..477a0f90a6a7e 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -684,6 +684,16 @@ void ASTStmtWriter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
   Code = serialization::EXPR_SYCL_UNIQUE_STABLE_NAME;
 }
 
+void ASTStmtWriter::VisitUnresolvedSYCLKernelCallStmt(
+    UnresolvedSYCLKernelCallStmt *S) {
+  VisitStmt(S);
+
+  Record.AddStmt(S->getOriginalStmt());
+  Record.AddStmt(S->getKernelLaunchIdExpr());
+
+  Code = serialization::STMT_UNRESOLVED_SYCL_KERNEL_CALL;
+}
+
 void ASTStmtWriter::VisitPredefinedExpr(PredefinedExpr *E) {
   VisitExpr(E);
 
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 4e472b7fc38b0..da164cc1bf160 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1825,6 +1825,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
     case Stmt::CapturedStmtClass:
     case Stmt::SYCLKernelCallStmtClass:
+    case Stmt::UnresolvedSYCLKernelCallStmtClass:
     case Stmt::OpenACCComputeConstructClass:
     case Stmt::OpenACCLoopConstructClass:
     case Stmt::OpenACCCombinedConstructClass:
diff --git a/clang/test/AST/ast-print-sycl-kernel-call.cpp b/clang/test/AST/ast-print-sycl-kernel-call.cpp
index 2243ee024be1a..64c6624b768c9 100644
--- a/clang/test/AST/ast-print-sycl-kernel-call.cpp
+++ b/clang/test/AST/ast-print-sycl-kernel-call.cpp
@@ -3,7 +3,7 @@
 
 struct sycl_kernel_launcher {
   template<typename KernelName, typename... Ts>
-  void sycl_enqueue_kernel_launch(const char *, Ts...) {}
+  void sycl_kernel_launch(const char *, Ts...) {}
 
   template<typename KernelName, typename KernelType>
   void kernel_entry_point(KernelType kernel) {
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
index cc26d1d195dde..d64db19c8304e 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
@@ -34,6 +34,8 @@ template<int> struct K {
   void operator()(Ts...) const {}
 };
 
+template <typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void skep1() {
@@ -42,12 +44,11 @@ void skep1() {
 // CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
-// CHECK-NEXT: | | | |-DeclStmt {{.*}}
-// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
-// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
-// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
+// CHECK-NEXT: | | | `-CallExpr {{.*}}
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)(const char *)' <FunctionToPointerDecay>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *)' lvalue Function {{.*}} 'sycl_kernel_launch' {{.*}}
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
-// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi1EE"
+// CHECK-NEXT: | | |       `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi1EE"
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
 // CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<1>
@@ -64,9 +65,10 @@ void skep2<KN<2>>(K<2>);
 // CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KT
 // CHECK-NEXT: | |-FunctionDecl {{.*}} skep2 'void (KT)'
 // CHECK-NEXT: | | |-ParmVarDecl {{.*}} k 'KT'
-// CHECK-NEXT: | | |-CompoundStmt {{.*}}
-// CHECK-NEXT: | | | `-CallExpr {{.*}} '<dependent type>'
-// CHECK-NEXT: | | |   `-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
+// CHECK-NEXT: | | |-UnresolvedSYCLKernelCallStmt {{.*}}
+// CHECK-NEXT: | | | `-CompoundStmt {{.*}}
+// CHECK-NEXT: | | |   `-CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
 // CHECK-NEXT: | | `-SYCLKernelEntryPointAttr {{.*}} KNT
 
 // CHECK-NEXT: | `-FunctionDecl {{.*}} skep2 'void (K<2>)' explicit_instantiation_definition instantiated_from 0x{{.+}}
@@ -85,12 +87,14 @@ void skep2<KN<2>>(K<2>);
 // CHECK-NEXT: |   | |   `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
 // CHECK-NEXT: |   | |     `-DeclRefExpr {{.*}} 'K<2>' lvalue ParmVar {{.*}} 'k' 'K<2>'
 // CHECK-NEXT: |   | |-CompoundStmt {{.*}}
-// CHECK-NEXT: |   | | |-DeclStmt {{.*}}
-// CHECK-NEXT: |   | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
-// CHECK-NEXT: |   | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
-// CHECK-NEXT: |   | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
-// CHECK-NEXT: |   | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
-// CHECK-NEXT: |   | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi2EE"
+// CHECK-NEXT: |   | | `-CallExpr {{.*}} 'void'
+// CHECK-NEXT: |   | |   |-ImplicitCastExpr {{.*}} <FunctionToPointerDecay>
+// CHECK-NEXT: |   | |   | `-DeclRefExpr {{.*}} 'void (const char *, K<2>)' lvalue Function {{.*}} 'sycl_kernel_launch' {{.*}}
+// CHECK-NEXT: |   | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: |   | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi2EE"
+// CHECK-NEXT: |   | |   `-CXXConstructExpr {{.*}} 'K<2>' 'void (const K<2> &) noexcept'
+// CHECK-NEXT: |   | |     `-ImplicitCastExpr {{.*}} <NoOp>
+// CHECK-NEXT: |   | |       `-DeclRefExpr {{.*}} 'K<2>' lvalue ParmVar {{.*}} 'k' 'K<2>'
 // CHECK-NEXT: |   | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: |   |   |-ImplicitParamDecl {{.*}} implicit used k 'K<2>'
 // CHECK-NEXT: |   |   `-CompoundStmt {{.*}}
@@ -116,9 +120,10 @@ void skep3<KN<3>>(K<3> k) {
 // CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KT
 // CHECK-NEXT: | |-FunctionDecl {{.*}} skep3 'void (KT)'
 // CHECK-NEXT: | | |-ParmVarDecl {{.*}} k 'KT'
-// CHECK-NEXT: | | |-CompoundStmt {{.*}}
-// CHECK-NEXT: | | | `-CallExpr {{.*}} '<dependent type>'
-// CHECK-NEXT: | | |   `-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
+// CHECK-NEXT: | | |-UnresolvedSYCLKernelCallStmt {{.*}}
+// CHECK-NEXT: | | | `-CompoundStmt {{.*}}
+// CHECK-NEXT: | | |   `-CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
 // CHECK-NEXT: | | `-SYCLKernelEntryPointAttr {{.*}} KNT
 
 // CHECK-NEXT: | `-Function {{.*}} 'skep3' 'void (K<3>)'
@@ -138,12 +143,14 @@ void skep3<KN<3>>(K<3> k) {
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
 // CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'K<3>' lvalue ParmVar {{.*}} 'k' 'K<3>'
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
-// CHECK-NEXT: | | | |-DeclStmt {{.*}}
-// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
-// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
-// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
-// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
-// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi3EE"
+// CHECK-NEXT: | | | `-CallExpr {{.*}}
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)(const char *, K<3>)' <FunctionToPointerDecay>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, K<3>)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, K<3>)' {{.*}}
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi3EE"
+// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'K<3>' 'void (const K<3> &) noexcept'
+// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
+// CHECK-NEXT: | | |       `-DeclRefExpr {{.*}} 'K<3>' lvalue ParmVar {{.*}} 'k' 'K<3>'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<3>'
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
@@ -174,12 +181,18 @@ void skep4(K<4> k, int p1, int p2) {
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
 // CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p2' 'int'
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
-// CHECK-NEXT: | | | |-DeclStmt {{.*}}
-// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
-// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
-// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
-// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
-// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi4EE"
+// CHECK-NEXT: | | | `-CallExpr {{.*}} 'void'
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)(const char *, K<4>, int, int)' <FunctionToPointerDecay>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, K<4>, int, int)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, K<4>, int, int)' {{.*}}
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi4EE"
+// CHECK-NEXT: | | |   |-CXXConstructExpr {{.*}} 'K<4>' 'void (const K<4> &) noexcept'
+// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
+// CHECK-NEXT: | | |   |   `-DeclRefExpr {{.*}} 'K<4>' lvalue ParmVar {{.*}} 'k' 'K<4>'
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p1' 'int'
+// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p2' 'int'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<4>'
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used p1 'int'
@@ -211,12 +224,22 @@ void skep5(int unused1, K<5> k, int unused2, int p, int unused3) {
 // CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
 // CHECK:      | | |-CompoundStmt {{.*}}
-// CHECK-NEXT: | | | |-DeclStmt {{.*}}
-// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
-// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
-// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
-// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
-// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi5EE"
+// CHECK-NEXT: | | | `-CallExpr {{.*}} 'void'
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)(const char *, int, K<5>, int, int, int)' <FunctionToPointerDecay>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, int, K<5>, int, int, int)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, int, K<5>, int, int, int)' {{.*}}
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi5EE"
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused1' 'int'
+// CHECK-NEXT: | | |   |-CXXConstructExpr {{.*}} 'K<5>' 'void (const K<5> &) noexcept'
+// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'const K<5>' lvalue <NoOp>
+// CHECK-NEXT: | | |   |   `-DeclRefExpr {{.*}} 'K<5>' lvalue ParmVar {{.*}} 'k' 'K<5>'
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused2' 'int'
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p' 'int'
+// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused3' 'int'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit unused1 'int'
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<5>'
@@ -263,12 +286,13 @@ void skep6(const S6 &k) {
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
 // CHECK-NEXT: | | |   `-DeclRefExpr {{.*}} 'const S6' lvalue ParmVar {{.*}} 'k' 'const S6 &'
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
-// CHECK-NEXT: | | | |-DeclStmt {{.*}}
-// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
-// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
-// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
-// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
-// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi6EE"
+// CHECK-NEXT: | | | `-CallExpr {{.*}} 'void'
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)(const char *, S6)' <FunctionToPointerDecay>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, S6)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, S6)' {{.*}}
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi6EE"
+// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'S6' 'void (const S6 &) noexcept'
+// CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'const S6' lvalue ParmVar {{.*}} 'k' 'const S6 &'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'const S6 &'
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
@@ -303,12 +327,14 @@ void skep7(S7 k) {
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const S7' lvalue <NoOp>
 // CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'S7' lvalue ParmVar {{.*}} 'k' 'S7'
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
-// CHECK-NEXT: | | | |-DeclStmt {{.*}}
-// CHECK-NEXT: | | | | `-VarDecl {{.*}} kernel_name 'const char *' extern
-// CHECK-NEXT: | | | `-BinaryOperator {{.*}} 'const char *' lvalue '='
-// CHECK-NEXT: | | |   |-DeclRefExpr {{.*}} 'const char *' lvalue Var {{.*}} 'kernel_name' 'const char *'
-// CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
-// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi7EE"
+// CHECK-NEXT: | | | `-CallExpr {{.*}} 'void'
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)(const char *, S7)' <FunctionToPointerDecay>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, S7)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, S7)' {{.*}}
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi7EE"
+// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'S7' 'void (const S7 &) noexcept'
+// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'const S7' lvalue <NoOp>
+// CHECK-NEXT: | | |       `-DeclRefExpr {{.*}} 'S7' lvalue ParmVar {{.*}} 'k' 'S7'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'S7'
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
@@ -335,11 +361,96 @@ void skep8(S8 k) {
 // CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
 // CHECK:      | | |-CompoundStmt {{.*}}
-// CHECK:      | | |   `-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
-// CHECK-NEXT: | | |     `-StringLiteral {{.*}} 'const char[12]' lvalue "_ZTS6\316\264\317\204\317\207"
+// CHECK-NEXT: | | | `-CallExpr {{.*}} 'void'
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)(const char *, S8)' <FunctionToPointerDecay>
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, S8)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, S8)' {{.*}}
+// CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[12]' lvalue "_ZTS6\316\264\317\204\317\207"
+// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'S8' 'void (const S8 &) noexcept'
+// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'const S8' lvalue <NoOp>
+// CHECK-NEXT: | | |       `-DeclRefExpr {{.*}} 'S8' lvalue ParmVar {{.*}} 'k' 'S8'
 // CHECK:      | | `-OutlinedFunctionDecl {{.*}}
 // CHECK:      | `-SYCLKernelEntryPointAttr {{.*}}
 
+class Handler {
+template <typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
+public:
+template<typename KNT, typename KT>
+[[clang::sycl_kernel_entry_point(KNT)]]
+void skep9(KT k, int a, int b) {
+  k(a, b);
+}
+};
+void foo() {
+  Handler H;
+  H.skep9<KN<9>>([=](int a, int b){return a+b;}, 1, 2);
+}
+
+// CHECK: | |-FunctionTemplateDecl {{.*}} skep9
+// CHECK-NEXT: | | |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 KNT
+// CHECK-NEXT: | | |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 1 KT
+// CHECK-NEXT: | | |-CXXMethodDecl {{.*}} skep9 'void (KT, int, int)' implicit-inline
+// CHECK-NEXT: | | | |-ParmVarDecl {{.*}} referenced k 'KT'
+// CHECK-NEXT: | | | |-ParmVarDecl {{.*}} referenced a 'int'
+// CHECK-NEXT: | | | |-ParmVarDecl {{.*}} referenced b 'int'
+// CHECK-NEXT: | | | |-UnresolvedSYCLKernelCallStmt {{.*}}
+// CHECK-NEXT: | | | | `-CompoundStmt {{.*}}
+// CHECK-NEXT: | | | |   `-CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: | | | |     |-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
+// CHECK-NEXT: | | | |     |-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'a' 'int'
+// CHECK-NEXT: | | | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'b' 'int'
+// CHECK-NEXT: | | | `-SYCLKernelEntryPointAttr {{.*}} KNT
+// CHECK-NEXT: | | `-CXXMethodDecl {{.*}} used skep9 {{.*}} implicit_instantiation implicit-inline
+// CHECK-NEXT: | |   |-TemplateArgument type 'KN<9>'
+// CHECK-NEXT: | |   | `-RecordType {{.*}} 'KN<9>' canonical
+// CHECK-NEXT: | |   |   `-ClassTemplateSpecialization {{.*}}'KN'
+// CHECK-NEXT: | |   |-TemplateArgument type {{.*}}
+// CHECK-NEXT: | |   | `-RecordType {{.*}}
+// CHECK-NEXT: | |   |   `-CXXRecord {{.*}}
+// CHECK-NEXT: | |   |-ParmVarDecl {{.*}} used k {{.*}}
+// CHECK-NEXT: | |   |-ParmVarDecl {{.*}} used a 'int'
+// CHECK-NEXT: | |   |-ParmVarDecl {{.*}} used b 'int'
+// CHECK-NEXT: | |   |-SYCLKernelCallStmt {{.*}}
+// CHECK-NEXT: | |   | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | |   | | `-CXXOperatorCallExpr {{.*}} 'int' '()'
+// CHECK-NEXT: | |   | |   |-ImplicitCastExpr {{.*}} 'int (*)(int, int) const' <FunctionToPointerDecay>
+// CHECK-NEXT: | |   | |   | `-DeclRefExpr {{.*}} 'int (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'int (int, int) const'
+// CHECK-NEXT: | |   | |   |-ImplicitCastExpr {{.*}} lvalue <NoOp>
+// CHECK-NEXT: | |   | |   | `-DeclRefExpr {{.*}} lvalue ParmVar {{.*}} 'k' {{.*}}
+// CHECK-NEXT: | |   | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | |   | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'a' 'int'
+// CHECK-NEXT: | |   | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | |   | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'b' 'int'
+// CHECK-NEXT: | |   | |-CompoundStmt {{.*}}
+// CHECK-NEXT: | |   | | `-CXXMemberCallExpr {{.*}} 'void'
+// CHECK-NEXT: | |   | |   |-MemberExpr {{.*}} '<bound member function type>' ->sycl_kernel_launch {{.*}}
+// CHECK-NEXT: | |   | |   | `-CXXThisExpr {{.*}} 'Handler *' implicit this
+// CHECK-NEXT: | |   | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
+// CHECK-NEXT: | |   | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi9EE"
+// CHECK-NEXT: | |   | |   |-CXXConstructExpr {{.*}}
+// CHECK-NEXT: | |   | |   | `-ImplicitCastExpr {{.*}} lvalue <NoOp>
+// CHECK-NEXT: | |   | |   |   `-DeclRefExpr {{.*}} lvalue ParmVar {{.*}} 'k' {{.*}}
+// CHECK-NEXT: | |   | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | |   | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'a' 'int'
+// CHECK-NEXT: | |   | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | |   | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'b' 'int'
+// CHECK-NEXT: | |   | `-OutlinedFunctionDecl {{.*}}
+// CHECK-NEXT: | |   |   |-ImplicitParamDecl {{.*}} implicit used k {{.*}}
+// CHECK-NEXT: | |   |   |-ImplicitParamDecl {{.*}} implicit used a 'int'
+// CHECK-NEXT: | |   |   |-ImplicitParamDecl {{.*}} implicit used b 'int'
+// CHECK-NEXT: | |   |   `-CompoundStmt {{.*}}
+// CHECK-NEXT: | |   |     `-CXXOperatorCallExpr {{.*}} 'int' '()'
+// CHECK-NEXT: | |   |       |-ImplicitCastExpr {{.*}} 'int (*)(int, int) const' <FunctionToPointerDecay>
+// CHECK-NEXT: | |   |       | `-DeclRefExpr {{.*}} 'int (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'int (int, int) const'
+// CHECK-NEXT: | |   |       |-ImplicitCastExpr {{.*}} lvalue <NoOp>
+// CHECK-NEXT: | |   |       | `-DeclRefExpr {{.*}} lvalue ImplicitParam {{.*}} 'k' {{.*}}
+// CHECK-NEXT: | |   |       |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | |   |       | `-DeclRefExpr {{.*}} 'int' lvalue ImplicitParam {{.*}} 'a' 'int'
+// CHECK-NEXT: | |   |       `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | |   |         `-DeclRefExpr {{.*}} 'int' lvalue ImplicitParam {{.*}} 'b' 'int'
+// CHECK-NEXT: | |   `-SYCLKernelEntryPointAttr {{.*}} struct KN<9>
+
 
 void the_end() {}
 // CHECK:      `-FunctionDecl {{.*}} the_end 'void ()'
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp
index 0171f72df0b37..7645dd4216271 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp
@@ -28,6 +28,9 @@
 // A unique kernel name type is required for each declared kernel entry point.
 template<int, int=0> struct KN;
 
+template <typename KernelName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys &&...Args) {}
+
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void skep1() {
 }
diff --git a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
index e88e4b7cf3149..48c13240b1ffc 100644
--- a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
+++ b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
@@ -25,8 +25,13 @@
 
 // Test the generation of SYCL kernel caller functions. These functions are
 // generated from functions declared with the sycl_kernel_entry_point attribute
-// and emited during device compilation. They are not emitted during device
-// compilation.
+// and emited during device compilation.
+// Test the generation of SYCL kernel launch statements during host compilation.
+// These statements are calls to sycl_enqueus_kernel_launch functions or class
+// members in case skep-attributed functions are also members of the same class.
+
+template <typename KernelName, typename KernelObj>
+void sycl_kernel_launch(const char *, KernelObj) {}
 
 struct single_purpose_kernel_name;
 struct single_purpose_kernel {
@@ -47,6 +52,17 @@ void kernel_single_task(KernelType kernelFunc) {
 // Exercise code gen with kernel name types named with esoteric characters.
 struct \u03b4\u03c4\u03c7; // Delta Tau Chi (δτχ)
 
+class Handler {
+template <typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
+public:
+template<typename KNT, typename KT>
+[[clang::sycl_kernel_entry_point(KNT)]]
+void skep(KT k, int a, int b) {
+  k(a, b);
+}
+};
+
 int main() {
   single_purpose_kernel obj;
   single_purpose_kernel_task(obj);
@@ -54,6 +70,8 @@ int main() {
   auto lambda = [=](auto) { (void) capture; };
   kernel_single_task<decltype(lambda)>(lambda);
   kernel_single_task<\u03b4\u03c4\u03c7>([](int){});
+  Handler H;
+  H.skep<class notaverygoodkernelname>([=](int a, int b){return a+b;}, 1, 2);
 }
 
 // Verify that SYCL kernel caller functions are not emitted during host
@@ -79,50 +97,105 @@ int main() {
 // CHECK-HOST-LINUX:      define dso_local void @_Z26single_purpose_kernel_task21single_purpose_kernel() #{{[0-9]+}} {
 // CHECK-HOST-LINUX-NEXT: entry:
 // CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %struct.single_purpose_kernel, align 1
-// CHECK-HOST-LINUX-NEXT:   store ptr @.str, ptr @kernel_name, align 8
+// CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %struct.single_purpose_kernel, align 1
+// CHECK-HOST-LINUX-NEXT:   call void @_Z18sycl_kernel_launchI26single_purpose_kernel_name21single_purpose_kernelEvPKcT0_(ptr noundef @.str)
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 //
 // CHECK-HOST-LINUX:      define internal void @_Z18kernel_single_taskIZ4mainEUlT_E_S1_EvT0_(i32 %kernelFunc.coerce) #{{[0-9]+}} {
 // CHECK-HOST-LINUX-NEXT: entry:
 // CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %class.anon, align 4
+// CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %class.anon, align 4
 // CHECK-HOST-LINUX-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon, ptr %kernelFunc, i32 0, i32 0
 // CHECK-HOST-LINUX-NEXT:   store i32 %kernelFunc.coerce, ptr %coerce.dive, align 4
-// CHECK-HOST-LINUX-NEXT:   store ptr @.str.1, ptr @kernel_name, align 8
+// CHECK-HOST-LINUX-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %kernelFunc, i64 4, i1 false)
+// CHECK-HOST-LINUX-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %class.anon, ptr %agg.tmp, i32 0, i32 0
+// CHECK-HOST-LINUX-NEXT:   %0 = load i32, ptr %coerce.dive1, align 4
+// CHECK-HOST-LINUX-NEXT:   call void @_Z18sycl_kernel_launchIZ4mainEUlT_E_S1_EvPKcT0_(ptr noundef @.str.1, i32 %0)
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 //
 // CHECK-HOST-LINUX:      define internal void @"_Z18kernel_single_taskI6\CE\B4\CF\84\CF\87Z4mainEUliE_EvT0_"() #{{[0-9]+}} {
 // CHECK-HOST-LINUX-NEXT: entry:
 // CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %class.anon.0, align 1
-// CHECK-HOST-LINUX-NEXT:   store ptr @.str.2, ptr @kernel_name, align 8
+// CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %class.anon.0, align 1
+// CHECK-HOST-LINUX-NEXT:   call void @"_Z18sycl_kernel_launchI6\CE\B4\CF\84\CF\87Z4mainEUliE_EvPKcT0_"(ptr noundef @.str.2)
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
-//
+
+// CHECK-HOST-LINUX: define internal void @_ZN7Handler4skepIZ4mainE22notaverygoodkernelnameZ4mainEUliiE_EEvT0_ii(ptr noundef nonnull align 1 dereferenceable(1) %this, i32 noundef %a, i32 noundef %b) #0 align 2 {
+// CHECK-HOST-LINUX-NEXT: entry:
+// CHECK-HOST-LINUX-NEXT:   %k = alloca %class.anon.1, align 1
+// CHECK-HOST-LINUX-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-HOST-LINUX-NEXT:   %a.addr = alloca i32, align 4
+// CHECK-HOST-LINUX-NEXT:   %b.addr = alloca i32, align 4
+// CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %class.anon.1, align 1
+// CHECK-HOST-LINUX-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-HOST-LINUX-NEXT:   store i32 %a, ptr %a.addr, align 4
+// CHECK-HOST-LINUX-NEXT:   store i32 %b, ptr %b.addr, align 4
+// CHECK-HOST-LINUX-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-HOST-LINUX-NEXT:   %0 = load i32, ptr %a.addr, align 4
+// CHECK-HOST-LINUX-NEXT:   %1 = load i32, ptr %b.addr, align 4
+// CHECK-HOST-LINUX-NEXT:   call void @_ZN7Handler18sycl_kernel_launchIZ4mainE22notaverygoodkernelnameJZ4mainEUliiE_iiEEEvPKcDpT0_(ptr noundef nonnull align 1 dereferenceable(1) %this1, ptr noundef @.str.3, i32 noundef %0, i32 noundef %1)
+// CHECK-HOST-LINUX-NEXT:   ret void
+// CHECK-HOST-LINUX-NEXT: }
+
 // CHECK-HOST-WINDOWS:      define dso_local void @"?single_purpose_kernel_task@@YAXUsingle_purpose_kernel@@@Z"(i8 %kernelFunc.coerce) #{{[0-9]+}} {
 // CHECK-HOST-WINDOWS-NEXT: entry:
 // CHECK-HOST-WINDOWS-NEXT:   %kernelFunc = alloca %struct.single_purpose_kernel, align 1
+// CHECK-HOST-WINDOWS-NEXT:   %agg.tmp = alloca %struct.single_purpose_kernel, align 1
 // CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %struct.single_purpose_kernel, ptr %kernelFunc, i32 0, i32 0
 // CHECK-HOST-WINDOWS-NEXT:   store i8 %kernelFunc.coerce, ptr %coerce.dive, align 1
-// CHECK-HOST-WINDOWS-NEXT:   store ptr @"??_C at _0CB@KFIJOMLB at _ZTS26single_purpose_kernel_name@", ptr @"?kernel_name@?0??single_purpose_kernel_task@@YAXUsingle_purpose_kernel@@@Z at 3PEBDEB", align 8
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %struct.single_purpose_kernel, ptr %agg.tmp, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %0 = load i8, ptr %coerce.dive1, align 1
+// CHECK-HOST-WINDOWS-NEXT:   call void @"??$sycl_kernel_launch at Usingle_purpose_kernel_name@@Usingle_purpose_kernel@@@@YAXPEBDUsingle_purpose_kernel@@@Z"(ptr noundef @"??_C at _0CB@KFIJOMLB at _ZTS26single_purpose_kernel_name@", i8 %0)
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
 //
 // CHECK-HOST-WINDOWS:      define internal void @"??$kernel_single_task at V<lambda_1>@?0??main@@9 at V1?0??2 at 9@@@YAXV<lambda_1>@?0??main@@9@@Z"(i32 %kernelFunc.coerce) #{{[0-9]+}} {
 // CHECK-HOST-WINDOWS-NEXT: entry:
 // CHECK-HOST-WINDOWS-NEXT:   %kernelFunc = alloca %class.anon, align 4
+// CHECK-HOST-WINDOWS-NEXT:   %agg.tmp = alloca %class.anon, align 4
 // CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon, ptr %kernelFunc, i32 0, i32 0
 // CHECK-HOST-WINDOWS-NEXT:   store i32 %kernelFunc.coerce, ptr %coerce.dive, align 4
-// CHECK-HOST-WINDOWS-NEXT:   store ptr @"??_C at _0BC@NHCDOLAA at _ZTSZ4mainEUlT_E_?$AA@", ptr @"?kernel_name@?0???$kernel_single_task at V<lambda_1>@?0??main@@9 at V1?0??2 at 9@@@YAXV<lambda_1>@?0??main@@9@@Z at 3PEBDEB", align 8
+// CHECK-HOST-WINDOWS-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %kernelFunc, i64 4, i1 false)
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %class.anon, ptr %agg.tmp, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %0 = load i32, ptr %coerce.dive1, align 4
+// CHECK-HOST-WINDOWS-NEXT:   call void @"??$sycl_kernel_launch at V<lambda_1>@?0??main@@9 at V1?0??2 at 9@@@YAXPEBDV<lambda_1>@?0??main@@9@@Z"(ptr noundef @"??_C at _0BC@NHCDOLAA at _ZTSZ4mainEUlT_E_?$AA@", i32 %0)
+//
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
 //
 // CHECK-HOST-WINDOWS:      define internal void @"??$kernel_single_task at U\CE\B4\CF\84\CF\87@@V<lambda_2>@?0??main@@9@@@YAXV<lambda_2>@?0??main@@9@@Z"(i8 %kernelFunc.coerce) #{{[0-9]+}} {
 // CHECK-HOST-WINDOWS-NEXT: entry:
 // CHECK-HOST-WINDOWS-NEXT:   %kernelFunc = alloca %class.anon.0, align 1
+// CHECK-HOST-WINDOWS-NEXT:   %agg.tmp = alloca %class.anon.0, align 1
 // CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon.0, ptr %kernelFunc, i32 0, i32 0
 // CHECK-HOST-WINDOWS-NEXT:   store i8 %kernelFunc.coerce, ptr %coerce.dive, align 1
-// CHECK-HOST-WINDOWS-NEXT:   store ptr @"??_C at _0M@BCGAEMBE at _ZTS6?N?$LE?O?$IE?O?$IH?$AA@", ptr @"?kernel_name@?0???$kernel_single_task at U\CE\B4\CF\84\CF\87@@V<lambda_2>@?0??main@@9@@@YAXV<lambda_2>@?0??main@@9@@Z at 3PEBDEB", align 8
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %class.anon.0, ptr %agg.tmp, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %0 = load i8, ptr %coerce.dive1, align 1
+// CHECK-HOST-WINDOWS-NEXT:   call void @"??$sycl_kernel_launch at U\CE\B4\CF\84\CF\87@@V<lambda_2>@?0??main@@9@@@YAXPEBDV<lambda_2>@?0??main@@9@@Z"(ptr noundef @"??_C at _0M@BCGAEMBE at _ZTS6?N?$LE?O?$IE?O?$IH?$AA@", i8 %0)
+// CHECK-HOST-WINDOWS-NEXT:   ret void
+// CHECK-HOST-WINDOWS-NEXT: }
+
+// CHECK-HOST-WINDOWS: define internal void @"??$skep at Vnotaverygoodkernelname@?1??main@@9 at V<lambda_3>@?0??2 at 9@@Handler@@QEAAXV<lambda_3>@?0??main@@9 at HH@Z"(ptr noundef nonnull align 1 dereferenceable(1) %this, i8 %k.coerce, i32 noundef %a, i32 noundef %b) #0 align 2 {
+// CHECK-HOST-WINDOWS-NEXT: entry:
+// CHECK-HOST-WINDOWS-NEXT:   %k = alloca %class.anon.1, align 1
+// CHECK-HOST-WINDOWS-NEXT:   %b.addr = alloca i32, align 4
+// CHECK-HOST-WINDOWS-NEXT:   %a.addr = alloca i32, align 4
+// CHECK-HOST-WINDOWS-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-HOST-WINDOWS-NEXT:   %agg.tmp = alloca %class.anon.1, align 1
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon.1, ptr %k, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   store i8 %k.coerce, ptr %coerce.dive, align 1
+// CHECK-HOST-WINDOWS-NEXT:   store i32 %b, ptr %b.addr, align 4
+// CHECK-HOST-WINDOWS-NEXT:   store i32 %a, ptr %a.addr, align 4
+// CHECK-HOST-WINDOWS-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-HOST-WINDOWS-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-HOST-WINDOWS-NEXT:   %0 = load i32, ptr %b.addr, align 4
+// CHECK-HOST-WINDOWS-NEXT:   %1 = load i32, ptr %a.addr, align 4
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive2 = getelementptr inbounds nuw %class.anon.1, ptr %agg.tmp, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %2 = load i8, ptr %coerce.dive2, align 1
+// CHECK-HOST-WINDOWS-NEXT:   call void @"??$sycl_kernel_launch at Vnotaverygoodkernelname@?1??main@@9 at V<lambda_3>@?0??2 at 9@HH at Handler@@AEAAXPEBDV<lambda_3>@?0??main@@9 at HH@Z"(ptr noundef nonnull align 1 dereferenceable(1) %this1, ptr noundef @"??_C at _0CE@NJIGCEIA at _ZTSZ4mainE22notaverygoodkerneln@", i8 %2, i32 noundef %1, i32 noundef %0)
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
 
diff --git a/clang/test/CodeGenSYCL/unique_stable_name_windows_diff.cpp b/clang/test/CodeGenSYCL/unique_stable_name_windows_diff.cpp
index 14366a092a1fe..63db83c02bbef 100644
--- a/clang/test/CodeGenSYCL/unique_stable_name_windows_diff.cpp
+++ b/clang/test/CodeGenSYCL/unique_stable_name_windows_diff.cpp
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -triple spir64-unknown-unknown -aux-triple x86_64-pc-windows-msvc -fsycl-is-device -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s '-D$ADDRSPACE=addrspace(1) '
 // RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsycl-is-host -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s '-D$ADDRSPACE='
 
+template <typename KernelName, typename KernelObj>
+void sycl_kernel_launch(const char *, KernelObj) {}
 
 template<typename KN, typename Func>
 [[clang::sycl_kernel_entry_point(KN)]] void kernel(Func F){
diff --git a/clang/test/SemaSYCL/sycl-host-kernel-launch.cpp b/clang/test/SemaSYCL/sycl-host-kernel-launch.cpp
new file mode 100644
index 0000000000000..eda0c4da489a9
--- /dev/null
+++ b/clang/test/SemaSYCL/sycl-host-kernel-launch.cpp
@@ -0,0 +1,199 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=c++17 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -fcxx-exceptions -verify=device,expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
+
+// A unique kernel name type is required for each declared kernel entry point.
+template<int, int = 0> struct KN;
+
+[[clang::sycl_kernel_entry_point(KN<1>)]]
+void nolauncher() {} 
+// host-error at -1 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+// device-warning at -2 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+// expected-note at -3 {{define 'sycl_kernel_launch' function template to fix}}
+
+void sycl_kernel_launch(const char *, int arg);
+// expected-note at -1 {{declared as a non-template here}}
+
+[[clang::sycl_kernel_entry_point(KN<2>)]]
+void nontemplatel() {}
+// host-error at -1 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+// device-warning at -2 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+// expected-note at -3 {{define 'sycl_kernel_launch' function template to fix}}
+// expected-error at -4 {{'sycl_kernel_launch' following the 'template' keyword does not refer to a template}}
+
+template <typename KernName>
+void sycl_kernel_launch(const char *, int arg);
+// expected-note at -1 {{candidate function template not viable: requires 2 arguments, but 1 was provided}}
+// expected-note at -2 2{{candidate function template not viable: no known conversion from 'Kern' to 'int' for 2nd argument}}
+
+[[clang::sycl_kernel_entry_point(KN<3>)]]
+void notenoughargs() {}
+// expected-error at -1 {{no matching function for call to 'sycl_kernel_launch'}}
+// FIXME: Should this also say "no suitable function for host code synthesis"?
+
+
+template <typename KernName>
+void sycl_kernel_launch(const char *, bool arg = 1);
+// expected-note at -1 2{{candidate function template not viable: no known conversion from 'Kern' to 'bool' for 2nd argument}}
+
+[[clang::sycl_kernel_entry_point(KN<4>)]]
+void enoughargs() {}
+
+namespace boop {
+template <typename KernName, typename KernelObj>
+void sycl_kernel_launch(const char *, KernelObj);
+
+template <typename KernName, typename KernelObj>
+[[clang::sycl_kernel_entry_point(KernName)]]
+void iboop(KernelObj Kernel) {
+  Kernel();
+}
+}
+
+template <typename KernName, typename KernelObj>
+[[clang::sycl_kernel_entry_point(KernName)]]
+void idontboop(KernelObj Kernel) {
+  Kernel();
+}
+// expected-error at -3 {{no matching function for call to 'sycl_kernel_launch'}}
+
+struct Kern {
+  int a;
+  int *b;
+  Kern(int _a, int* _b) : a(_a), b(_b) {}
+  void operator()(){ *b = a;}
+};
+
+void foo() {
+  int *a;
+  Kern b(1, a);
+  idontboop<KN<6>>(b);
+  // expected-note at -1 {{in instantiation of function template specialization 'idontboop<KN<6>, Kern>' requested here}}
+  boop::iboop<KN<7>>(b);
+}
+
+class MaybeHandler {
+
+template <typename KernName>
+void sycl_kernel_launch(const char *);
+
+template <typename KernName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys ...Args);
+
+public:
+
+template <typename KernName, typename KernelObj>
+[[clang::sycl_kernel_entry_point(KernName)]]
+void entry(KernelObj Kernel) {
+  Kernel();
+}
+};
+
+class MaybeHandler2 {
+
+template <typename KernName, typename... Tys>
+static void sycl_kernel_launch(const char *, Tys ...Args);
+
+public:
+
+template <typename KernName, typename KernelObj>
+[[clang::sycl_kernel_entry_point(KernName)]]
+void entry(KernelObj Kernel) {
+  Kernel();
+}
+};
+
+class MaybeHandler3 {
+
+template <typename KernName, typename... Tys>
+static void sycl_kernel_launch(const char *, Tys ...Args);
+
+public:
+
+template <typename KernName, typename KernelObj>
+[[clang::sycl_kernel_entry_point(KernName)]]
+static void entry(KernelObj Kernel) {
+  Kernel();
+}
+};
+
+class MaybeHandler4 {
+
+template <typename KernName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys ...Args);
+
+public:
+
+template <typename KernName, typename KernelObj>
+[[clang::sycl_kernel_entry_point(KernName)]]
+static void entry(KernelObj Kernel) { 
+  // expected-error at -1 {{call to non-static member function without an object argument}}
+  // FIXME: Should that be clearer?
+  Kernel();
+}
+};
+
+template<typename>
+struct base_handler {
+  template<typename KNT, typename... Ts>
+  void sycl_kernel_launch(const char*, Ts...) {}
+};
+struct derived_handler : base_handler<derived_handler> {
+  template<typename KNT, typename KT>
+  [[clang::sycl_kernel_entry_point(KNT)]]
+  void entry(KT k) { k(); }
+};
+
+template<int N>
+struct derived_handler_t : base_handler<derived_handler_t<N>> {
+  template<typename KNT, typename KT>
+// FIXME this fails because accessing members of dependent bases requires
+// explicit qualification.
+  [[clang::sycl_kernel_entry_point(KNT)]]
+  void entry(KT k) { k(); }
+  // expected-error at -1 {{no matching function for call to 'sycl_kernel_launch'}}
+};
+
+template<typename KNT>
+struct kernel_launcher {
+  template<typename... Ts>
+  void operator()(const char*, Ts...) const {}
+};
+
+namespace var {
+template<typename KNT>
+kernel_launcher<KNT> sycl_kernel_launch;
+
+struct handler {
+  template<typename KNT, typename KT>
+  [[clang::sycl_kernel_entry_point(KNT)]]
+  void entry(KT k) { k(); }
+};
+}
+
+
+void bar() {
+  int *a;
+  Kern b(1, a);
+  MaybeHandler H;
+  MaybeHandler2 H1;
+  MaybeHandler3 H2;
+  MaybeHandler4 H3;
+  H.entry<KN<8>>(b);
+  H1.entry<KN<9>>(b);
+  H2.entry<KN<10>>(b);
+  H3.entry<KN<11>>(b);
+
+  derived_handler H5;
+  H5.entry<KN<12>>(b);
+
+  derived_handler_t<13> H6;
+  H6.entry<KN<13>>(b); //expected-note {{in instantiation of function template specialization}}
+
+  var::handler h;
+  h.entry<KN<14>>(b);
+}
+
+
+
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
index 3f07feb87c9a1..c181f76321a26 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
@@ -40,6 +40,10 @@ struct coroutine_traits {
 // A unique kernel name type is required for each declared kernel entry point.
 template<int, int = 0> struct KN;
 
+// A launcher function definition required for host code synthesis to silence
+// complains.
+template <typename KernelName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys &&...Args) {}
 
 ////////////////////////////////////////////////////////////////////////////////
 // Valid declarations.
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
index fd1f00ae05d7a..1cdd48f1e5840 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
@@ -10,6 +10,10 @@
 template<int> struct ST; // #ST-decl
 template<int N> using TTA = ST<N>; // #TTA-decl
 
+// A launcher function definition required for host code synthesis to silence
+// complains.
+template <typename KernelName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys &&...Args) {}
 
 ////////////////////////////////////////////////////////////////////////////////
 // Valid declarations.
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp
index 8788e147a2ae4..44a3ce6f3640a 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp
@@ -17,6 +17,11 @@ module M2 { header "m2.h" }
 #--- common.h
 template<int> struct KN;
 
+// A launcher function definition required for host code synthesis to silence
+// complains.
+template <typename KernelName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys &&...Args) {}
+
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void common_test1() {}
 
@@ -25,7 +30,6 @@ template<typename T>
 void common_test2() {}
 template void common_test2<KN<2>>();
 
-
 #--- m1.h
 #include "common.h"
 
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp
index 0575a7a5a67eb..0e6d1a6c57e39 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp
@@ -15,6 +15,11 @@
 #--- pch.h
 template<int> struct KN;
 
+// A launcher function definition required for host code synthesis to silence
+// complains.
+template <typename KernelName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys &&...Args) {}
+
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void pch_test1() {} // << expected previous declaration note here.
 
@@ -26,11 +31,11 @@ template void pch_test2<KN<2>>();
 
 #--- test.cpp
 // expected-error at +3 {{the 'clang::sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}}
-// expected-note at pch.h:4 {{previous declaration is here}}
+// expected-note at pch.h:9 {{previous declaration is here}}
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void test1() {}
 
 // expected-error at +3 {{the 'clang::sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}}
-// expected-note at pch.h:8 {{previous declaration is here}}
+// expected-note at pch.h:13 {{previous declaration is here}}
 [[clang::sycl_kernel_entry_point(KN<2>)]]
 void test2() {}
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
index 5a3b43be66daf..7b525abadd2c3 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
@@ -9,6 +9,12 @@
 // specification.
 
 struct S1;
+
+// A launcher function definition required for host code synthesis to silence
+// complains.
+template <typename KernelName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys &&...Args) {}
+
 // expected-warning at +3 {{redundant 'clang::sycl_kernel_entry_point' attribute}}
 // expected-note at +1  {{previous attribute is here}}
 [[clang::sycl_kernel_entry_point(S1),
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
index 3689adaab9b5b..9674dac456f9f 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
@@ -10,6 +10,11 @@
 // attribute during instantiation of a specialization unless that specialization
 // is selected by overload resolution.
 
+// A launcher function definition required for host code synthesis to silence
+// complains.
+template <typename KernelName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys &&...Args) {}
+
 // FIXME: C++23 [temp.expl.spec]p12 states:
 // FIXME:   ... Similarly, attributes appearing in the declaration of a template
 // FIXME:   have no effect on an explicit specialization of that template.
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
index fc0640e1900cb..7b8fc6c9a7630 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
@@ -22,6 +22,11 @@ struct type_info {
 };
 } // namespace std
 
+// A launcher function definition required for host code synthesis to silence
+// complains.
+template <typename KernelName, typename... Tys>
+void sycl_kernel_launch(const char *, Tys &&...Args) {}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Valid declarations.
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index 0a43d73063c1f..9dd0582d0c97e 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -376,6 +376,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
     break;
 
   case Stmt::SYCLKernelCallStmtClass:
+  case Stmt::UnresolvedSYCLKernelCallStmtClass:
     K = CXCursor_UnexposedStmt;
     break;
 

>From 59baed4207184ad196541cf316dd7f3db5c53808 Mon Sep 17 00:00:00 2001
From: Mariya Podchishchaeva <mariya.podchishchaeva at intel.com>
Date: Wed, 1 Oct 2025 19:52:56 +0200
Subject: [PATCH 06/17] [SYCL-Upstreaming] Fix a crash (#52)

In case a function with skep attribute is instantiated two times with
the same kernel name the attribute is invalid due to the conflicting name.
Make sure to exit from instantiation of UnresolvedSYCLKernelCallStmt in
this case.
---
 clang/lib/Sema/TreeTransform.h                |  5 +++++
 ...cl-kernel-entry-point-attr-kernel-name.cpp | 22 +++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 6e57cbe5239ce..4f16c9bd6f87d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12969,6 +12969,11 @@ ExprResult TreeTransform<Derived>::TransformSYCLUniqueStableNameExpr(
 template <typename Derived>
 StmtResult TreeTransform<Derived>::TransformUnresolvedSYCLKernelCallStmt(
     UnresolvedSYCLKernelCallStmt *S) {
+  auto *FD = cast<FunctionDecl>(SemaRef.CurContext);
+  const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
+  if (!SKEPAttr || SKEPAttr->isInvalidAttr())
+    return StmtError();
+
   ExprResult IdExpr = getDerived().TransformExpr(S->getKernelLaunchIdExpr());
 
   if (IdExpr.isInvalid())
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
index 7b525abadd2c3..e2e6bf3314614 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
@@ -124,3 +124,25 @@ struct B19 {
 };
 // expected-note at +1 {{in instantiation of template class 'B19<int>' requested here}}
 B19<int> b19;
+
+struct auto_name;
+
+// expected-error at +4 {{the 'clang::sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}}
+// expected-note at +3 {{previous declaration is here}}
+template <typename KernelName, typename KernelType>
+[[clang::sycl_kernel_entry_point(KernelName)]]
+void __kernel_single_task(const KernelType KernelFunc) {
+  KernelFunc();
+}
+
+template <typename KernelType, typename KernelName = auto_name>
+void pf(KernelType K) {
+  // expected-note at +1 {{requested here}}
+  __kernel_single_task<KernelName>(K);
+}
+
+void foo() {
+  pf([](){});
+  // expected-note at +1 {{requested here}}
+  pf([](){});
+}

>From 4abaf068067c7870005c29a09f2be991a156ed3e Mon Sep 17 00:00:00 2001
From: Mariya Podchishchaeva <mariya.podchishchaeva at intel.com>
Date: Wed, 1 Oct 2025 20:00:08 +0200
Subject: [PATCH 07/17] [SYCL-Upstreaming] Fix a crash an argument of skep
 function is not trivially-copyable (#53)

device-copyable doesn't mean trivially-copyable, so we may encounter
arguments that need cleanup. Adds test that verifies presence of the
dtor call in the synthesized code.
---
 clang/lib/Sema/SemaSYCL.cpp                   |  2 +-
 .../CodeGenSYCL/kernel-caller-entry-point.cpp | 47 +++++++++++++++++++
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index f6a5fab2fe86e..899f37711b0d2 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -511,7 +511,7 @@ StmtResult BuildSYCLKernelLaunchStmt(Sema &SemaRef,
     if (LaunchResult.isInvalid())
       return StmtError();
 
-    Stmts.push_back(LaunchResult.get());
+    Stmts.push_back(SemaRef.MaybeCreateExprWithCleanups(LaunchResult).get());
   }
 
   return CompoundStmt::Create(SemaRef.getASTContext(), Stmts,
diff --git a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
index 48c13240b1ffc..3f5617c552024 100644
--- a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
+++ b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
@@ -63,6 +63,23 @@ void skep(KT k, int a, int b) {
 }
 };
 
+struct auto_name;
+
+template <typename KernelName, typename KernelType>
+[[clang::sycl_kernel_entry_point(KernelName)]]
+void __kernel_single_task(const KernelType KernelFunc) {
+  KernelFunc();
+}
+
+template <typename KernelType, typename KernelName = auto_name>
+void pf(KernelType K) {
+  __kernel_single_task<KernelName>(K);
+}
+struct DCopyable {
+  int i;
+  ~DCopyable();
+};
+
 int main() {
   single_purpose_kernel obj;
   single_purpose_kernel_task(obj);
@@ -72,6 +89,9 @@ int main() {
   kernel_single_task<\u03b4\u03c4\u03c7>([](int){});
   Handler H;
   H.skep<class notaverygoodkernelname>([=](int a, int b){return a+b;}, 1, 2);
+
+  DCopyable b;
+  pf([b](){});
 }
 
 // Verify that SYCL kernel caller functions are not emitted during host
@@ -140,6 +160,17 @@ int main() {
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 
+// CHECK-HOST-LINUX: define internal void @_Z20__kernel_single_taskI9auto_nameZ4mainEUlvE_EvT0_(ptr noundef %KernelFunc)
+// CHECK-HOST-LINUX-NEXT: entry:
+// CHECK-HOST-LINUX-NEXT:   %KernelFunc.indirect_addr = alloca ptr, align 8
+// CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %class.anon.3, align 4
+// CHECK-HOST-LINUX-NEXT:   store ptr %KernelFunc, ptr %KernelFunc.indirect_addr, align 8
+// CHECK-HOST-LINUX-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %KernelFunc, i64 4, i1 false)
+// CHECK-HOST-LINUX-NEXT:   call void @_Z18sycl_kernel_launchI9auto_nameZ4mainEUlvE_EvPKcT0_(ptr noundef @.str.4, ptr noundef %agg.tmp)
+// CHECK-HOST-LINUX-NEXT:   call void @_ZZ4mainENUlvE_D1Ev(ptr noundef nonnull align 4 dereferenceable(4) %agg.tmp) #4
+// CHECK-HOST-LINUX-NEXT:   ret void
+// CHECK-HOST-LINUX-NEXT: }
+
 // CHECK-HOST-WINDOWS:      define dso_local void @"?single_purpose_kernel_task@@YAXUsingle_purpose_kernel@@@Z"(i8 %kernelFunc.coerce) #{{[0-9]+}} {
 // CHECK-HOST-WINDOWS-NEXT: entry:
 // CHECK-HOST-WINDOWS-NEXT:   %kernelFunc = alloca %struct.single_purpose_kernel, align 1
@@ -199,6 +230,22 @@ int main() {
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
 
+// CHECK-HOST-WINDOWS: define internal void @"??$__kernel_single_task at Uauto_name@@V<lambda_4>@?0??main@@9@@@YAXV<lambda_4>@?0??main@@9@@Z"(i32 %KernelFunc.coerce)
+// CHECK-HOST-WINDOWS-NEXT: entry:
+// CHECK-HOST-WINDOWS-NEXT:   %KernelFunc = alloca %class.anon.3, align 4
+// CHECK-HOST-WINDOWS-NEXT:   %agg.tmp = alloca %class.anon.3, align 4
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon.3, ptr %KernelFunc, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %struct.DCopyable, ptr %coerce.dive, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   store i32 %KernelFunc.coerce, ptr %coerce.dive1, align 4
+// CHECK-HOST-WINDOWS-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %KernelFunc, i64 4, i1 false)
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive2 = getelementptr inbounds nuw %class.anon.3, ptr %agg.tmp, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive3 = getelementptr inbounds nuw %struct.DCopyable, ptr %coerce.dive2, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %0 = load i32, ptr %coerce.dive3, align 4
+// CHECK-HOST-WINDOWS-NEXT:   call void @"??$sycl_kernel_launch at Uauto_name@@V<lambda_4>@?0??main@@9@@@YAXPEBDV<lambda_4>@?0??main@@9@@Z"(ptr noundef @"??_C at _0P@HMAAEHI at _ZTS9auto_name?$AA@", i32 %0)
+// CHECK-HOST-WINDOWS-NEXT:   call void @"??1<lambda_4>@?0??main@@9 at QEAA@XZ"(ptr noundef nonnull align 4 dereferenceable(4) %KernelFunc)
+// CHECK-HOST-WINDOWS-NEXT:   ret void
+// CHECK-HOST-WINDOWS-NEXT: }
+
 // Verify that SYCL kernel caller functions are emitted for each device target.
 //
 // main() shouldn't be emitted in device code.

>From 97795255e2df1cc83b22c0d87adcddd6bd1087a7 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Mon, 10 Nov 2025 15:11:51 -0800
Subject: [PATCH 08/17] Correct expected AST output for a test following a
 rebase.

---
 clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
index d64db19c8304e..bf9e2e22d67d4 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
@@ -401,7 +401,7 @@ void foo() {
 // CHECK-NEXT: | | | |     |-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'a' 'int'
 // CHECK-NEXT: | | | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'b' 'int'
 // CHECK-NEXT: | | | `-SYCLKernelEntryPointAttr {{.*}} KNT
-// CHECK-NEXT: | | `-CXXMethodDecl {{.*}} used skep9 {{.*}} implicit_instantiation implicit-inline
+// CHECK-NEXT: | | `-CXXMethodDecl {{.*}} used skep9 {{.*}} implicit_instantiation implicit-inline instantiated_from 0x{{.*}}
 // CHECK-NEXT: | |   |-TemplateArgument type 'KN<9>'
 // CHECK-NEXT: | |   | `-RecordType {{.*}} 'KN<9>' canonical
 // CHECK-NEXT: | |   |   `-ClassTemplateSpecialization {{.*}}'KN'

>From 07c9c1c0528c0ad431e684052d91994f37f1dad9 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Fri, 14 Nov 2025 14:02:15 -0800
Subject: [PATCH 09/17] Misc cleanup.

- Added/edited lots of comments.
- Expanded testing for sycl_kernel_launch lookup and reorganized the tests
  to make it easier to identify and/or plug testing gaps.
- Added a missing sycl_kernel_entry_point attribute in a test.
- Reordered various declarations for better grouping and consistency.
- Renamed some variables and functions.
- Removed an unnecessary include directive.
- Adjusted the location used for the implicit sycl_kernel_launch call. The
  opening brace of the original function body is used where available and
  the general function location is used otherwise.
- Corrected lookup to unconditionally look for a template name; previously
  a spurious error about a 'template' keyword could be issued.
- Added missing code gen checks for use of a member function as the SYCL
  kernel entry point function.
- Other misc style edits for consistency.
---
 clang/include/clang/AST/StmtSYCL.h            |  21 +-
 clang/include/clang/Sema/SemaSYCL.h           |  27 +-
 clang/lib/AST/ComputeDependence.cpp           |   1 -
 clang/lib/CodeGen/CGStmt.cpp                  |   2 +-
 clang/lib/Sema/SemaDecl.cpp                   |  20 +-
 clang/lib/Sema/SemaSYCL.cpp                   | 136 +++---
 clang/lib/Sema/TreeTransform.h                |   1 -
 .../ast-dump-sycl-kernel-call-stmt.cpp        |  18 +-
 .../ast-dump-sycl-kernel-entry-point.cpp      |   4 +-
 .../ast-print-sycl-kernel-call.cpp            |  13 +-
 .../CodeGenSYCL/kernel-caller-entry-point.cpp | 225 +++++----
 .../unique_stable_name_windows_diff.cpp       |   4 +-
 .../test/SemaSYCL/sycl-host-kernel-launch.cpp | 199 --------
 ...-kernel-entry-point-attr-appertainment.cpp |   7 +-
 .../sycl-kernel-entry-point-attr-grammar.cpp  |   7 +-
 ...el-entry-point-attr-kernel-name-module.cpp |   8 +-
 ...ernel-entry-point-attr-kernel-name-pch.cpp |  11 +-
 ...cl-kernel-entry-point-attr-kernel-name.cpp |  29 +-
 .../sycl-kernel-entry-point-attr-sfinae.cpp   |   7 +-
 .../sycl-kernel-entry-point-attr-this.cpp     |   7 +-
 clang/test/SemaSYCL/sycl-kernel-launch.cpp    | 448 ++++++++++++++++++
 21 files changed, 750 insertions(+), 445 deletions(-)
 rename clang/test/{AST => ASTSYCL}/ast-print-sycl-kernel-call.cpp (61%)
 delete mode 100644 clang/test/SemaSYCL/sycl-host-kernel-launch.cpp
 create mode 100644 clang/test/SemaSYCL/sycl-kernel-launch.cpp

diff --git a/clang/include/clang/AST/StmtSYCL.h b/clang/include/clang/AST/StmtSYCL.h
index c8da4987321a9..cce62466eec10 100644
--- a/clang/include/clang/AST/StmtSYCL.h
+++ b/clang/include/clang/AST/StmtSYCL.h
@@ -100,23 +100,30 @@ class SYCLKernelCallStmt : public Stmt {
 };
 
 // UnresolvedSYCLKernelCallStmt represents an invocation of a SYCL kernel in
-// a dependent context for which lookup of the sycl_enqueue_kernel_launch
-// identifier cannot be performed. These statements are transformed to
-// SYCLKernelCallStmt during template instantiation.
+// a dependent context for which lookup of the sycl_kernel_launch identifier
+// cannot be performed. These statements are transformed to SYCLKernelCallStmt
+// during template instantiation.
 class UnresolvedSYCLKernelCallStmt : public Stmt {
   friend class ASTStmtReader;
+  friend class ASTStmtWriter;
+
+private:
   Stmt *OriginalStmt = nullptr;
   // KernelLaunchIdExpr stores an UnresolvedLookupExpr or UnresolvedMemberExpr
   // corresponding to the SYCL kernel launch function for which a call
   // will be synthesized during template instantiation.
   Expr *KernelLaunchIdExpr = nullptr;
+
   UnresolvedSYCLKernelCallStmt(CompoundStmt *CS, Expr *IdExpr)
       : Stmt(UnresolvedSYCLKernelCallStmtClass), OriginalStmt(CS),
         KernelLaunchIdExpr(IdExpr) {}
 
-  void setKernelLaunchIdExpr(Expr *IdExpr) { KernelLaunchIdExpr = IdExpr; }
+  /// Set the original statement.
   void setOriginalStmt(CompoundStmt *CS) { OriginalStmt = CS; }
 
+  /// Set the kernel launch ID expression.
+  void setKernelLaunchIdExpr(Expr *IdExpr) { KernelLaunchIdExpr = IdExpr; }
+
 public:
   static UnresolvedSYCLKernelCallStmt *
   Create(const ASTContext &C, CompoundStmt *CS, Expr *IdExpr) {
@@ -127,12 +134,16 @@ class UnresolvedSYCLKernelCallStmt : public Stmt {
     return new (C) UnresolvedSYCLKernelCallStmt(nullptr, nullptr);
   }
 
-  Expr *getKernelLaunchIdExpr() const { return KernelLaunchIdExpr; }
+  /// Retrieve the original statement.
   CompoundStmt *getOriginalStmt() { return cast<CompoundStmt>(OriginalStmt); }
   const CompoundStmt *getOriginalStmt() const {
     return cast<CompoundStmt>(OriginalStmt);
   }
 
+  /// Retrieve the kernel launch ID expression.
+  Expr *getKernelLaunchIdExpr() { return KernelLaunchIdExpr; }
+  const Expr *getKernelLaunchIdExpr() const { return KernelLaunchIdExpr; }
+
   SourceLocation getBeginLoc() const LLVM_READONLY {
     return getOriginalStmt()->getBeginLoc();
   }
diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h
index 76046b765c0d6..5018c1093b5c6 100644
--- a/clang/include/clang/Sema/SemaSYCL.h
+++ b/clang/include/clang/Sema/SemaSYCL.h
@@ -66,11 +66,32 @@ class SemaSYCL : public SemaBase {
 
   void CheckSYCLExternalFunctionDecl(FunctionDecl *FD);
   void CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD);
+
+  /// Builds an expression for the lookup of a 'sycl_kernel_launch' template
+  /// with 'KernelName' as an explicit template argument. Lookup is performed
+  /// as if from the first statement of the body of 'FD' and thus requires
+  /// searching the scopes that exist at parse time. This function therefore
+  /// requires the current semantic context to be the definition of 'FD'. In a
+  /// dependent context, the returned expression will be an UnresolvedLookupExpr
+  /// or an UnresolvedMemberExpr. In a non-dependent context, the returned
+  /// expression will be a DeclRefExpr or MemberExpr. If lookup fails, a null
+  /// error result is returned. The resulting expression is intended to be
+  /// passed as the 'LaunchIdExpr' argument in a call to either
+  /// BuildSYCLKernelCallStmt() or BuildUnresolvedSYCLKernelCallStmt() after
+  /// the function body has been parsed.
+  ExprResult BuildSYCLKernelLaunchIdExpr(FunctionDecl *FD, QualType KernelName);
+
+  /// Builds a SYCLKernelCallStmt to wrap 'Body' and to be used as the body of
+  /// 'FD'. 'LaunchIdExpr' specifies the lookup result returned by a previous
+  /// call to BuildSYCLKernelLaunchIdExpr().
   StmtResult BuildSYCLKernelCallStmt(FunctionDecl *FD, CompoundStmt *Body,
                                      Expr *LaunchIdExpr);
-  ExprResult BuildSYCLKernelLaunchIdExpr(FunctionDecl *FD, QualType KNT);
-  StmtResult BuildUnresolvedSYCLKernelCallStmt(CompoundStmt *CS,
-                                                     Expr *IdExpr);
+
+  /// Builds an UnresolvedSYCLKernelCallStmt to wrap 'Body'. 'LaunchIdExpr'
+  /// specifies the lookup result returned by a previous call to
+  /// BuildSYCLKernelLaunchIdExpr().
+  StmtResult BuildUnresolvedSYCLKernelCallStmt(CompoundStmt *Body,
+                                               Expr *LaunchIdExpr);
 };
 
 } // namespace clang
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index a20ea95a5c997..638080ea781a9 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -16,7 +16,6 @@
 #include "clang/AST/ExprConcepts.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/ExprOpenMP.h"
-#include "clang/AST/StmtSYCL.h"
 #include "clang/Basic/ExceptionSpecificationType.h"
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 529218cac6315..eb62bff00f68c 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -104,6 +104,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
   case Stmt::SEHExceptStmtClass:
   case Stmt::SEHFinallyStmtClass:
   case Stmt::MSDependentExistsStmtClass:
+  case Stmt::UnresolvedSYCLKernelCallStmtClass:
     llvm_unreachable("invalid statement class to emit generically");
   case Stmt::NullStmtClass:
   case Stmt::CompoundStmtClass:
@@ -117,7 +118,6 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
   case Stmt::CaseStmtClass:
   case Stmt::SEHLeaveStmtClass:
   case Stmt::SYCLKernelCallStmtClass:
-  case Stmt::UnresolvedSYCLKernelCallStmtClass:
     llvm_unreachable("should have emitted these statements as simple");
 
 #define STMT(Type, Base)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c984e3d55ff58..877eef41a847f 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16228,10 +16228,15 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
 
   if (FD && !FD->isInvalidDecl() &&
       FD->hasAttr<SYCLKernelEntryPointAttr>() && FnBodyScope) {
-    // Building KernelLaunchIdExpr requires performing an unqualified lookup
-    // which can only be done correctly while the stack of parsing scopes is
-    // alive, so we do it here when we start parsing function body even if it is
-    // a templated function.
+    // An implicit call expression is synthesized for functions declared with
+    // the sycl_kernel_entry_point attribute. The call may resolve to a
+    // function template, a member function template, or a call operator
+    // of a variable template depending on the results of unqualified lookup
+    // for 'sycl_kernel_launch' from the beginning of the function body.
+    // Performing that lookup requires the stack of parsing scopes active
+    // when the definition is parsed and is thus done here; the result is
+    // cached in FunctionScopeInfo and used to synthesize the (possibly
+    // unresolved) call expression after the function body has been parsed.
     const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
     if (!SKEPAttr->isInvalidAttr()) {
       ExprResult LaunchIdExpr =
@@ -16441,8 +16446,11 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation,
       SKEPAttr->setInvalidAttr();
     }
 
-    // We don't need to build SYCLKernelCallStmt for template instantiations
-    // since it was already created by template instantiator.
+    // Build an unresolved SYCL kernel call statement for a function template,
+    // validate that a SYCL kernel call statement was instantiated for an
+    // (implicit or explicit) instantiation of a function template, or otherwise
+    // build a (resolved) SYCL kernel call statement for a non-templated
+    // function or an explicit specialization.
     if (Body && !SKEPAttr->isInvalidAttr()) {
       StmtResult SR;
       if (FD->isTemplated()) {
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 899f37711b0d2..cd4bb3f88f6c6 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -392,85 +392,82 @@ void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {
 
 ExprResult SemaSYCL::BuildSYCLKernelLaunchIdExpr(FunctionDecl *FD,
                                                  QualType KNT) {
+  // The current context must be the function definition context to ensure
+  // that name lookup is performed within the correct scope.
+  assert(SemaRef.CurContext == FD);
 
-  ASTContext &Ctx = SemaRef.getASTContext();
-  // Some routines need a valid source location to work correctly.
-  SourceLocation BodyLoc =
-      FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation();
+  // An appropriate source location is required to emit diagnostics if
+  // lookup fails to produce an overload set. The desired location is the
+  // start of the function body, but that is not yet available since the
+  // body of the function has not yet been set when this function is called.
+  // The general location of the function is used instead.
+  SourceLocation Loc = FD->getLocation();
 
-  IdentifierInfo &LaunchFooName =
+  ASTContext &Ctx = SemaRef.getASTContext();
+  IdentifierInfo &SYCLKernelLaunchID =
       Ctx.Idents.get("sycl_kernel_launch", tok::TokenKind::identifier);
 
-  // Perform overload resolution for a call to an accessible (member) function
-  // template named 'sycl_kernel_launch' "from within the definition of
-  // FD where":
-  // - The kernel name type is passed as the first template argument.
-  // - Any remaining template parameters are deduced from the function
-  //   arguments or assigned by default template arguments.
-  // - 'this' is passed as the implicit function argument if 'FD' is a
-  //   non-static member function.
-  // - The name of the kernel, expressed as a string literal, is passed as the
-  //   first function argument.
-  // - The parameters of FD are forwarded as-if by 'std::forward()' as the
-  //   remaining explicit function arguments.
-  // - Any remaining function arguments are initialized by default arguments.
-  LookupResult Result(SemaRef, &LaunchFooName, BodyLoc,
+  // Perform ordinary name lookup for a function or variable template that
+  // accepts a single type template argument.
+  LookupResult Result(SemaRef, &SYCLKernelLaunchID, Loc,
                       Sema::LookupOrdinaryName);
-  CXXScopeSpec SS;
-  SemaRef.LookupTemplateName(Result, SemaRef.getCurScope(), SS,
-                             /*ObjectType=*/QualType(),
-                             /*EnteringContext=*/false, BodyLoc);
+  CXXScopeSpec EmptySS;
+  SemaRef.LookupTemplateName(Result, SemaRef.getCurScope(), EmptySS,
+                             /*ObjectType*/ QualType(),
+                             /*EnteringContext*/ false,
+                             Sema::TemplateNameIsRequired);
 
   if (Result.empty() || Result.isAmbiguous()) {
-    SemaRef.Diag(BodyLoc, SemaRef.getLangOpts().SYCLIsHost
-                              ? diag::err_sycl_host_no_launch_function
-                              : diag::warn_sycl_device_no_host_launch_function);
-    SemaRef.Diag(BodyLoc, diag::note_sycl_host_launch_function);
+    SemaRef.Diag(Loc, SemaRef.getLangOpts().SYCLIsHost
+                          ? diag::err_sycl_host_no_launch_function
+                          : diag::warn_sycl_device_no_host_launch_function);
+    SemaRef.Diag(Loc, diag::note_sycl_host_launch_function);
 
     return ExprError();
   }
 
-  TemplateArgumentListInfo TALI{BodyLoc, BodyLoc};
+  TemplateArgumentListInfo TALI{Loc, Loc};
   TemplateArgument KNTA = TemplateArgument(KNT);
   TemplateArgumentLoc TAL =
-      SemaRef.getTrivialTemplateArgumentLoc(KNTA, QualType(), BodyLoc);
+      SemaRef.getTrivialTemplateArgumentLoc(KNTA, QualType(), Loc);
   TALI.addArgument(TAL);
   ExprResult IdExpr;
-  if (SemaRef.isPotentialImplicitMemberAccess(SS, Result,
-                                              /*IsAddressOfOperand=*/false))
-    // BuildPossibleImplicitMemberExpr creates UnresolvedMemberExpr. Using it
-    // allows to pass implicit/explicit this argument automatically.
-    IdExpr = SemaRef.BuildPossibleImplicitMemberExpr(SS, BodyLoc, Result, &TALI,
+  if (SemaRef.isPotentialImplicitMemberAccess(EmptySS, Result,
+                                              /*IsAddressOfOperand*/ false))
+    // The lookup result allows for a possible implicit member access that
+    // would require an implicit or explicit 'this' argument.
+    IdExpr = SemaRef.BuildPossibleImplicitMemberExpr(EmptySS, SourceLocation(),
+                                                     Result, &TALI,
                                                      SemaRef.getCurScope());
   else
-    IdExpr = SemaRef.BuildTemplateIdExpr(SS, BodyLoc, Result,
-                                         /*RequiresADL=*/true, &TALI);
+    IdExpr = SemaRef.BuildTemplateIdExpr(EmptySS, SourceLocation(), Result,
+                                         /*RequiresADL*/ true, &TALI);
 
-  // Can happen if SKEP attributed function is a static member, but the launcher
-  // is a regular member. Perhaps emit a note saying that we're in host code
-  // synthesis.
+  // The resulting expression may be invalid if, for example, 'FD' is a
+  // non-static member function and sycl_kernel_launch lookup selects a
+  // member function (which would require a 'this' argument which is
+  // not available).
   if (IdExpr.isInvalid())
     return ExprError();
 
   return IdExpr;
 }
 
-StmtResult SemaSYCL::BuildUnresolvedSYCLKernelCallStmt(CompoundStmt *CS,
-                                                             Expr *IdExpr) {
-  return UnresolvedSYCLKernelCallStmt::Create(SemaRef.getASTContext(), CS,
-                                                    IdExpr);
-}
-
 namespace {
 
-void PrepareKernelArgumentsForKernelLaunch(SmallVectorImpl<Expr *> &Args,
-                                                  const SYCLKernelInfo *SKI,
-                                                  Sema &SemaRef,
-                                                  SourceLocation Loc) {
-  assert(SKI && "Need a kernel!");
-  ASTContext &Ctx = SemaRef.getASTContext();
+// Constructs the arguments to be passed for the SYCL kernel launch call.
+// The first argument is a string literal that contains the SYCL kernel
+// name. The remaining arguments are the parameters of 'FD'.
+void BuildSYCLKernelLaunchCallArgs(Sema &SemaRef, FunctionDecl *FD,
+                                   const SYCLKernelInfo *SKI,
+                                   SmallVectorImpl<Expr *> &Args,
+                                   SourceLocation Loc) {
+  // The current context must be the function definition context to ensure
+  // that parameter references occur within the correct scope.
+  assert(SemaRef.CurContext == FD);
 
   // Prepare a string literal that contains the kernel name.
+  ASTContext &Ctx = SemaRef.getASTContext();
   const std::string KernelName = SKI->GetKernelName();
   QualType KernelNameCharTy = Ctx.CharTy.withConst();
   llvm::APInt KernelNameSize(Ctx.getTypeSize(Ctx.getSizeType()),
@@ -482,30 +479,23 @@ void PrepareKernelArgumentsForKernelLaunch(SmallVectorImpl<Expr *> &Args,
                             /*Pascal*/ false, KernelNameArrayTy, Loc);
   Args.push_back(KernelNameExpr);
 
-  // Right now we simply forward the arguments of the skep-attributed function.
-  // With decomposition present there can be another logic.
-  // Make sure to use CurContext to avoid diagnostics that we're using a
-  // variable coming from another context. The function should be the same as in
-  // the kernel info though.
-  auto *FD = cast<FunctionDecl>(SemaRef.CurContext);
-  assert(declaresSameEntity(FD, SKI->getKernelEntryPointDecl()));
   for (ParmVarDecl *PVD : FD->parameters()) {
     QualType ParamType = PVD->getOriginalType().getNonReferenceType();
     Expr *DRE = SemaRef.BuildDeclRefExpr(PVD, ParamType, VK_LValue, Loc);
-    assert(DRE);
     Args.push_back(DRE);
   }
 }
 
-StmtResult BuildSYCLKernelLaunchStmt(Sema &SemaRef,
-                                            const SYCLKernelInfo *SKI,
-                                            Expr *IdExpr, SourceLocation Loc) {
+// Constructs the SYCL kernel launch call.
+StmtResult BuildSYCLKernelLaunchCallStmt(Sema &SemaRef, FunctionDecl *FD,
+                                         const SYCLKernelInfo *SKI,
+                                         Expr *IdExpr, SourceLocation Loc) {
   SmallVector<Stmt *> Stmts;
-  assert(SKI && "Need a Kernel!");
 
+  // IdExpr may be null if name lookup failed.
   if (IdExpr) {
     llvm::SmallVector<Expr *, 12> Args;
-    PrepareKernelArgumentsForKernelLaunch(Args, SKI, SemaRef, Loc);
+    BuildSYCLKernelLaunchCallArgs(SemaRef, FD, SKI, Args, Loc);
     ExprResult LaunchResult =
         SemaRef.BuildCallExpr(SemaRef.getCurScope(), IdExpr, Loc, Args, Loc);
     if (LaunchResult.isInvalid())
@@ -630,14 +620,20 @@ StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
       BuildSYCLKernelEntryPointOutline(SemaRef, FD, Body);
   assert(OFD);
 
-  // Build host kernel launch stmt.
-  SourceLocation BodyLoc =
-      FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation();
-  StmtResult LaunchRes =
-      BuildSYCLKernelLaunchStmt(SemaRef, &SKI, LaunchIdExpr, BodyLoc);
+  // Build the host kernel launch statement. An appropriate source location
+  // is required to emit diagnostics.
+  SourceLocation Loc = Body->getLBracLoc();
+  StmtResult LaunchResult =
+      BuildSYCLKernelLaunchCallStmt(SemaRef, FD, &SKI, LaunchIdExpr, Loc);
 
   Stmt *NewBody =
-      new (getASTContext()) SYCLKernelCallStmt(Body, LaunchRes.get(), OFD);
+      new (getASTContext()) SYCLKernelCallStmt(Body, LaunchResult.get(), OFD);
 
   return NewBody;
 }
+
+StmtResult SemaSYCL::BuildUnresolvedSYCLKernelCallStmt(CompoundStmt *Body,
+                                                       Expr *LaunchIdExpr) {
+  return UnresolvedSYCLKernelCallStmt::Create(SemaRef.getASTContext(), Body,
+                                              LaunchIdExpr);
+}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4f16c9bd6f87d..e11bce9758b30 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12975,7 +12975,6 @@ StmtResult TreeTransform<Derived>::TransformUnresolvedSYCLKernelCallStmt(
     return StmtError();
 
   ExprResult IdExpr = getDerived().TransformExpr(S->getKernelLaunchIdExpr());
-
   if (IdExpr.isInvalid())
      return StmtError();
 
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
index bf9e2e22d67d4..5eef5db069faf 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
@@ -34,7 +34,7 @@ template<int> struct K {
   void operator()(Ts...) const {}
 };
 
-template <typename KernelName, typename... Ts>
+template<typename KernelName, typename... Ts>
 void sycl_kernel_launch(const char *, Ts...) {}
 
 [[clang::sycl_kernel_entry_point(KN<1>)]]
@@ -373,18 +373,18 @@ void skep8(S8 k) {
 // CHECK:      | `-SYCLKernelEntryPointAttr {{.*}}
 
 class Handler {
-template <typename KernelName, typename... Ts>
-void sycl_kernel_launch(const char *, Ts...) {}
+  template <typename KNT, typename... Ts>
+  void sycl_kernel_launch(const char *, Ts...) {}
 public:
-template<typename KNT, typename KT>
-[[clang::sycl_kernel_entry_point(KNT)]]
-void skep9(KT k, int a, int b) {
-  k(a, b);
-}
+  template<typename KNT, typename KT>
+  [[clang::sycl_kernel_entry_point(KNT)]]
+  void skep9(KT k, int a, int b) {
+    k(a, b);
+  }
 };
 void foo() {
   Handler H;
-  H.skep9<KN<9>>([=](int a, int b){return a+b;}, 1, 2);
+  H.skep9<KN<9>>([=] (int a, int b) { return a+b; }, 1, 2);
 }
 
 // CHECK: | |-FunctionTemplateDecl {{.*}} skep9
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp
index 7645dd4216271..011f48e91c292 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp
@@ -28,8 +28,8 @@
 // A unique kernel name type is required for each declared kernel entry point.
 template<int, int=0> struct KN;
 
-template <typename KernelName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys &&...Args) {}
+template<typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts... Args) {}
 
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void skep1() {
diff --git a/clang/test/AST/ast-print-sycl-kernel-call.cpp b/clang/test/ASTSYCL/ast-print-sycl-kernel-call.cpp
similarity index 61%
rename from clang/test/AST/ast-print-sycl-kernel-call.cpp
rename to clang/test/ASTSYCL/ast-print-sycl-kernel-call.cpp
index 64c6624b768c9..5adaa367ed9c1 100644
--- a/clang/test/AST/ast-print-sycl-kernel-call.cpp
+++ b/clang/test/ASTSYCL/ast-print-sycl-kernel-call.cpp
@@ -6,17 +6,20 @@ struct sycl_kernel_launcher {
   void sycl_kernel_launch(const char *, Ts...) {}
 
   template<typename KernelName, typename KernelType>
-  void kernel_entry_point(KernelType kernel) {
+  [[clang::sycl_kernel_entry_point(KernelName)]]
+  void sycl_kernel_entry_point(KernelType kernel) {
     kernel();
   }
-// CHECK:      template <typename KernelName, typename KernelType> void kernel_entry_point(KernelType kernel) {
+};
+// CHECK:      template <typename KernelName, typename KernelType> void sycl_kernel_entry_point(KernelType kernel)
+// CHECK-NEXT: {
 // CHECK-NEXT:     kernel();
 // CHECK-NEXT: }
-// CHECK:      template<> void kernel_entry_point<KN, (lambda at {{.*}})>((lambda at {{.*}}) kernel) {
+// CHECK:      template<> void sycl_kernel_entry_point<KN, (lambda at {{.*}})>((lambda at {{.*}}) kernel)
+// CHECK-NEXT: {
 // CHECK-NEXT:     kernel();
 // CHECK-NEXT: }
-};
 
 void f(sycl_kernel_launcher skl) {
-  skl.kernel_entry_point<struct KN>([]{});
+  skl.sycl_kernel_entry_point<struct KN>([]{});
 }
diff --git a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
index 3f5617c552024..c508cc526db72 100644
--- a/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
+++ b/clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
@@ -2,36 +2,36 @@
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple amdgcn-amd-amdhsa -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-AMDGCN %s
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple nvptx-nvidia-cuda -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-NVPTX %s
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple nvptx64-nvidia-cuda -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-NVPTX %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple spir-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple spir64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple spirv32-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple spirv64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple spir-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRNV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple spir64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRNV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple spirv32-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-unknown-linux-gnu -triple spirv64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRV %s
 // RUN: %clang_cc1 -fsycl-is-host -emit-llvm -triple x86_64-pc-windows-msvc -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-HOST,CHECK-HOST-WINDOWS %s
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple amdgcn-amd-amdhsa -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-AMDGCN %s
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple nvptx-nvidia-cuda -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-NVPTX %s
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple nvptx64-nvidia-cuda -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-NVPTX %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple spir-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple spir64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple spirv32-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple spirv64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple spir-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRNV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple spir64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRNV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple spirv32-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-pc-windows-msvc -triple spirv64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRV %s
 // RUN: %clang_cc1 -fsycl-is-host -emit-llvm -triple x86_64-uefi -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-HOST,CHECK-HOST-WINDOWS %s
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple amdgcn-amd-amdhsa -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-AMDGCN %s
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple nvptx-nvidia-cuda -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-NVPTX %s
 // RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple nvptx64-nvidia-cuda -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-NVPTX %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple spir-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple spir64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple spirv32-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
-// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple spirv64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple spir-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRNV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple spir64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRNV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple spirv32-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRV %s
+// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -aux-triple x86_64-uefi -triple spirv64-unknown-unknown -std=c++17 %s -o - | FileCheck --check-prefixes=CHECK-DEVICE,CHECK-SPIR,CHECK-SPIRV %s
 
-// Test the generation of SYCL kernel caller functions. These functions are
-// generated from functions declared with the sycl_kernel_entry_point attribute
-// and emited during device compilation.
-// Test the generation of SYCL kernel launch statements during host compilation.
-// These statements are calls to sycl_enqueus_kernel_launch functions or class
-// members in case skep-attributed functions are also members of the same class.
+// Test code generation for functions declared with the sycl_kernel_entry_point
+// attribute. During host compilation, the bodies of such functions are replaced
+// with calls to a function template or variable template (with suitable call
+// operator) named sycl_kernel_launch. During device compilation, the bodies of
+// these functions are used to generate offload kernel entry points (SYCL kernel
+// caller functions).
 
-template <typename KernelName, typename KernelObj>
-void sycl_kernel_launch(const char *, KernelObj) {}
+template <typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 struct single_purpose_kernel_name;
 struct single_purpose_kernel {
@@ -52,32 +52,20 @@ void kernel_single_task(KernelType kernelFunc) {
 // Exercise code gen with kernel name types named with esoteric characters.
 struct \u03b4\u03c4\u03c7; // Delta Tau Chi (δτχ)
 
-class Handler {
-template <typename KernelName, typename... Ts>
-void sycl_kernel_launch(const char *, Ts...) {}
+class handler {
+  template <typename KernelName, typename... Ts>
+  void sycl_kernel_launch(const char *, Ts...) {}
 public:
-template<typename KNT, typename KT>
-[[clang::sycl_kernel_entry_point(KNT)]]
-void skep(KT k, int a, int b) {
-  k(a, b);
-}
+  template <typename KernelName, typename KernelType>
+  [[clang::sycl_kernel_entry_point(KernelName)]]
+  void kernel_entry_point(KernelType k, int a, int b) {
+    k(a, b);
+  }
 };
 
-struct auto_name;
-
-template <typename KernelName, typename KernelType>
-[[clang::sycl_kernel_entry_point(KernelName)]]
-void __kernel_single_task(const KernelType KernelFunc) {
-  KernelFunc();
-}
-
-template <typename KernelType, typename KernelName = auto_name>
-void pf(KernelType K) {
-  __kernel_single_task<KernelName>(K);
-}
-struct DCopyable {
+struct copyable {
   int i;
-  ~DCopyable();
+  ~copyable();
 };
 
 int main() {
@@ -87,11 +75,9 @@ int main() {
   auto lambda = [=](auto) { (void) capture; };
   kernel_single_task<decltype(lambda)>(lambda);
   kernel_single_task<\u03b4\u03c4\u03c7>([](int){});
-  Handler H;
-  H.skep<class notaverygoodkernelname>([=](int a, int b){return a+b;}, 1, 2);
-
-  DCopyable b;
-  pf([b](){});
+  handler h;
+  copyable c{42};
+  h.kernel_entry_point<struct KN>([=] (int a, int b) { return c.i + a + b; }, 1, 2);
 }
 
 // Verify that SYCL kernel caller functions are not emitted during host
@@ -100,12 +86,14 @@ int main() {
 // CHECK-HOST-NOT: define {{.*}} @_ZTS26single_purpose_kernel_name
 // CHECK-HOST-NOT: define {{.*}} @_ZTSZ4mainEUlT_E_
 // CHECK-HOST-NOT: define {{.*}} @"_ZTS6\CE\B4\CF\84\CF\87"
+// CHECK-HOST-NOT: define {{.*}} @_ZTSZ4mainE2KN
 
 // Verify that sycl_kernel_entry_point attributed functions are not emitted
 // during device compilation.
 //
 // CHECK-DEVICE-NOT: single_purpose_kernel_task
 // CHECK-DEVICE-NOT: kernel_single_task
+// CHECK-DEVICE-NOT: kernel_entry_point
 
 // Verify that kernel launch code is generated for sycl_kernel_entry_point
 // attributed functions during host compilation.
@@ -118,7 +106,7 @@ int main() {
 // CHECK-HOST-LINUX-NEXT: entry:
 // CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %struct.single_purpose_kernel, align 1
 // CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %struct.single_purpose_kernel, align 1
-// CHECK-HOST-LINUX-NEXT:   call void @_Z18sycl_kernel_launchI26single_purpose_kernel_name21single_purpose_kernelEvPKcT0_(ptr noundef @.str)
+// CHECK-HOST-LINUX-NEXT:   call void @_Z18sycl_kernel_launchI26single_purpose_kernel_nameJ21single_purpose_kernelEEvPKcDpT0_(ptr noundef @.str)
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 //
@@ -131,7 +119,7 @@ int main() {
 // CHECK-HOST-LINUX-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %kernelFunc, i64 4, i1 false)
 // CHECK-HOST-LINUX-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %class.anon, ptr %agg.tmp, i32 0, i32 0
 // CHECK-HOST-LINUX-NEXT:   %0 = load i32, ptr %coerce.dive1, align 4
-// CHECK-HOST-LINUX-NEXT:   call void @_Z18sycl_kernel_launchIZ4mainEUlT_E_S1_EvPKcT0_(ptr noundef @.str.1, i32 %0)
+// CHECK-HOST-LINUX-NEXT:   call void @_Z18sycl_kernel_launchIZ4mainEUlT_E_JS1_EEvPKcDpT0_(ptr noundef @.str.1, i32 %0)
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 //
@@ -139,35 +127,28 @@ int main() {
 // CHECK-HOST-LINUX-NEXT: entry:
 // CHECK-HOST-LINUX-NEXT:   %kernelFunc = alloca %class.anon.0, align 1
 // CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %class.anon.0, align 1
-// CHECK-HOST-LINUX-NEXT:   call void @"_Z18sycl_kernel_launchI6\CE\B4\CF\84\CF\87Z4mainEUliE_EvPKcT0_"(ptr noundef @.str.2)
+// CHECK-HOST-LINUX-NEXT:   call void @"_Z18sycl_kernel_launchI6\CE\B4\CF\84\CF\87JZ4mainEUliE_EEvPKcDpT0_"(ptr noundef @.str.2)
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 
-// CHECK-HOST-LINUX: define internal void @_ZN7Handler4skepIZ4mainE22notaverygoodkernelnameZ4mainEUliiE_EEvT0_ii(ptr noundef nonnull align 1 dereferenceable(1) %this, i32 noundef %a, i32 noundef %b) #0 align 2 {
+
+// CHECK-HOST-LINUX:      define internal void @_ZN7handler18kernel_entry_pointIZ4mainE2KNZ4mainEUliiE_EEvT0_ii(ptr noundef nonnull align 1 dereferenceable(1) %this, ptr noundef %k, i32 noundef %a, i32 noundef %b) #{{[0-9]+}} align 2 {
 // CHECK-HOST-LINUX-NEXT: entry:
-// CHECK-HOST-LINUX-NEXT:   %k = alloca %class.anon.1, align 1
 // CHECK-HOST-LINUX-NEXT:   %this.addr = alloca ptr, align 8
+// CHECK-HOST-LINUX-NEXT:   %k.indirect_addr = alloca ptr, align 8
 // CHECK-HOST-LINUX-NEXT:   %a.addr = alloca i32, align 4
 // CHECK-HOST-LINUX-NEXT:   %b.addr = alloca i32, align 4
-// CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %class.anon.1, align 1
+// CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %class.anon.1, align 4
 // CHECK-HOST-LINUX-NEXT:   store ptr %this, ptr %this.addr, align 8
+// CHECK-HOST-LINUX-NEXT:   store ptr %k, ptr %k.indirect_addr, align 8
 // CHECK-HOST-LINUX-NEXT:   store i32 %a, ptr %a.addr, align 4
 // CHECK-HOST-LINUX-NEXT:   store i32 %b, ptr %b.addr, align 4
 // CHECK-HOST-LINUX-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-HOST-LINUX-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %k, i64 4, i1 false)
 // CHECK-HOST-LINUX-NEXT:   %0 = load i32, ptr %a.addr, align 4
 // CHECK-HOST-LINUX-NEXT:   %1 = load i32, ptr %b.addr, align 4
-// CHECK-HOST-LINUX-NEXT:   call void @_ZN7Handler18sycl_kernel_launchIZ4mainE22notaverygoodkernelnameJZ4mainEUliiE_iiEEEvPKcDpT0_(ptr noundef nonnull align 1 dereferenceable(1) %this1, ptr noundef @.str.3, i32 noundef %0, i32 noundef %1)
-// CHECK-HOST-LINUX-NEXT:   ret void
-// CHECK-HOST-LINUX-NEXT: }
-
-// CHECK-HOST-LINUX: define internal void @_Z20__kernel_single_taskI9auto_nameZ4mainEUlvE_EvT0_(ptr noundef %KernelFunc)
-// CHECK-HOST-LINUX-NEXT: entry:
-// CHECK-HOST-LINUX-NEXT:   %KernelFunc.indirect_addr = alloca ptr, align 8
-// CHECK-HOST-LINUX-NEXT:   %agg.tmp = alloca %class.anon.3, align 4
-// CHECK-HOST-LINUX-NEXT:   store ptr %KernelFunc, ptr %KernelFunc.indirect_addr, align 8
-// CHECK-HOST-LINUX-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %KernelFunc, i64 4, i1 false)
-// CHECK-HOST-LINUX-NEXT:   call void @_Z18sycl_kernel_launchI9auto_nameZ4mainEUlvE_EvPKcT0_(ptr noundef @.str.4, ptr noundef %agg.tmp)
-// CHECK-HOST-LINUX-NEXT:   call void @_ZZ4mainENUlvE_D1Ev(ptr noundef nonnull align 4 dereferenceable(4) %agg.tmp) #4
+// CHECK-HOST-LINUX-NEXT:   call void @_ZN7handler18sycl_kernel_launchIZ4mainE2KNJZ4mainEUliiE_iiEEEvPKcDpT0_(ptr noundef nonnull align 1 dereferenceable(1) %this1, ptr noundef @.str.3, ptr noundef %agg.tmp, i32 noundef %0, i32 noundef %1)
+// CHECK-HOST-LINUX-NEXT:   call void @_ZZ4mainENUliiE_D1Ev(ptr noundef nonnull align 4 dereferenceable(4) %agg.tmp) #{{[0-9]+}}
 // CHECK-HOST-LINUX-NEXT:   ret void
 // CHECK-HOST-LINUX-NEXT: }
 
@@ -209,40 +190,28 @@ int main() {
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
 
-// CHECK-HOST-WINDOWS: define internal void @"??$skep at Vnotaverygoodkernelname@?1??main@@9 at V<lambda_3>@?0??2 at 9@@Handler@@QEAAXV<lambda_3>@?0??main@@9 at HH@Z"(ptr noundef nonnull align 1 dereferenceable(1) %this, i8 %k.coerce, i32 noundef %a, i32 noundef %b) #0 align 2 {
+// CHECK-HOST-WINDOWS:      define internal void @"??$kernel_entry_point at UKN@?1??main@@9 at V<lambda_3>@?0??2 at 9@@handler@@QEAAXV<lambda_3>@?0??main@@9 at HH@Z"(ptr noundef nonnull align 1 dereferenceable(1) %this, i32 %k.coerce, i32 noundef %a, i32 noundef %b) #{{[0-9]+}} align 2
 // CHECK-HOST-WINDOWS-NEXT: entry:
-// CHECK-HOST-WINDOWS-NEXT:   %k = alloca %class.anon.1, align 1
+// CHECK-HOST-WINDOWS-NEXT:   %k = alloca %class.anon.1, align 4
 // CHECK-HOST-WINDOWS-NEXT:   %b.addr = alloca i32, align 4
 // CHECK-HOST-WINDOWS-NEXT:   %a.addr = alloca i32, align 4
 // CHECK-HOST-WINDOWS-NEXT:   %this.addr = alloca ptr, align 8
-// CHECK-HOST-WINDOWS-NEXT:   %agg.tmp = alloca %class.anon.1, align 1
+// CHECK-HOST-WINDOWS-NEXT:   %agg.tmp = alloca %class.anon.1, align 4
 // CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon.1, ptr %k, i32 0, i32 0
-// CHECK-HOST-WINDOWS-NEXT:   store i8 %k.coerce, ptr %coerce.dive, align 1
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %struct.copyable, ptr %coerce.dive, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   store i32 %k.coerce, ptr %coerce.dive1, align 4
 // CHECK-HOST-WINDOWS-NEXT:   store i32 %b, ptr %b.addr, align 4
 // CHECK-HOST-WINDOWS-NEXT:   store i32 %a, ptr %a.addr, align 4
 // CHECK-HOST-WINDOWS-NEXT:   store ptr %this, ptr %this.addr, align 8
-// CHECK-HOST-WINDOWS-NEXT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-HOST-WINDOWS-NEXT:   %this2 = load ptr, ptr %this.addr, align 8
 // CHECK-HOST-WINDOWS-NEXT:   %0 = load i32, ptr %b.addr, align 4
 // CHECK-HOST-WINDOWS-NEXT:   %1 = load i32, ptr %a.addr, align 4
-// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive2 = getelementptr inbounds nuw %class.anon.1, ptr %agg.tmp, i32 0, i32 0
-// CHECK-HOST-WINDOWS-NEXT:   %2 = load i8, ptr %coerce.dive2, align 1
-// CHECK-HOST-WINDOWS-NEXT:   call void @"??$sycl_kernel_launch at Vnotaverygoodkernelname@?1??main@@9 at V<lambda_3>@?0??2 at 9@HH at Handler@@AEAAXPEBDV<lambda_3>@?0??main@@9 at HH@Z"(ptr noundef nonnull align 1 dereferenceable(1) %this1, ptr noundef @"??_C at _0CE@NJIGCEIA at _ZTSZ4mainE22notaverygoodkerneln@", i8 %2, i32 noundef %1, i32 noundef %0)
-// CHECK-HOST-WINDOWS-NEXT:   ret void
-// CHECK-HOST-WINDOWS-NEXT: }
-
-// CHECK-HOST-WINDOWS: define internal void @"??$__kernel_single_task at Uauto_name@@V<lambda_4>@?0??main@@9@@@YAXV<lambda_4>@?0??main@@9@@Z"(i32 %KernelFunc.coerce)
-// CHECK-HOST-WINDOWS-NEXT: entry:
-// CHECK-HOST-WINDOWS-NEXT:   %KernelFunc = alloca %class.anon.3, align 4
-// CHECK-HOST-WINDOWS-NEXT:   %agg.tmp = alloca %class.anon.3, align 4
-// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon.3, ptr %KernelFunc, i32 0, i32 0
-// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %struct.DCopyable, ptr %coerce.dive, i32 0, i32 0
-// CHECK-HOST-WINDOWS-NEXT:   store i32 %KernelFunc.coerce, ptr %coerce.dive1, align 4
-// CHECK-HOST-WINDOWS-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %KernelFunc, i64 4, i1 false)
-// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive2 = getelementptr inbounds nuw %class.anon.3, ptr %agg.tmp, i32 0, i32 0
-// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive3 = getelementptr inbounds nuw %struct.DCopyable, ptr %coerce.dive2, i32 0, i32 0
-// CHECK-HOST-WINDOWS-NEXT:   %0 = load i32, ptr %coerce.dive3, align 4
-// CHECK-HOST-WINDOWS-NEXT:   call void @"??$sycl_kernel_launch at Uauto_name@@V<lambda_4>@?0??main@@9@@@YAXPEBDV<lambda_4>@?0??main@@9@@Z"(ptr noundef @"??_C at _0P@HMAAEHI at _ZTS9auto_name?$AA@", i32 %0)
-// CHECK-HOST-WINDOWS-NEXT:   call void @"??1<lambda_4>@?0??main@@9 at QEAA@XZ"(ptr noundef nonnull align 4 dereferenceable(4) %KernelFunc)
+// CHECK-HOST-WINDOWS-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %k, i64 4, i1 false)
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive3 = getelementptr inbounds nuw %class.anon.1, ptr %agg.tmp, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %coerce.dive4 = getelementptr inbounds nuw %struct.copyable, ptr %coerce.dive3, i32 0, i32 0
+// CHECK-HOST-WINDOWS-NEXT:   %2 = load i32, ptr %coerce.dive4, align 4
+// CHECK-HOST-WINDOWS-NEXT:   call void @"??$sycl_kernel_launch at UKN@?1??main@@9 at V<lambda_3>@?0??2 at 9@HH at handler@@AEAAXPEBDV<lambda_3>@?0??main@@9 at HH@Z"(ptr noundef nonnull align 1 dereferenceable(1) %this2, ptr noundef @"??_C at _0P@DLGHPODL at _ZTSZ4mainE2KN?$AA@", i32 %2, i32 noundef %1, i32 noundef %0)
+// CHECK-HOST-WINDOWS-NEXT:   call void @"??1<lambda_3>@?0??main@@9 at QEAA@XZ"(ptr noundef nonnull align 4 dereferenceable(4) %k) #{{[0-9]+}}
 // CHECK-HOST-WINDOWS-NEXT:   ret void
 // CHECK-HOST-WINDOWS-NEXT: }
 
@@ -364,6 +333,84 @@ int main() {
 // CHECK-SPIR-NEXT:   }
 // CHECK-SPIR:        define internal spir_func void @_ZZ4mainENKUliE_clEi
 
+// IR for the SYCL kernel caller function generated for
+// handler::kernel_entry_point with main::KN as the SYCL kernel name type.
+//
+// CHECK-AMDGCN:      Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
+// CHECK-AMDGCN-NEXT: define dso_local amdgpu_kernel void @_ZTSZ4mainE2KN
+// CHECK-AMDGCN-SAME:   (i32 %k.coerce, i32 noundef %a, i32 noundef %b) #[[AMDGCN_ATTR0]] {
+// CHECK-AMDGCN-NEXT: entry:
+// CHECK-AMDGCN-NEXT:   %k = alloca %class.anon.1, align 4, addrspace(5)
+// CHECK-AMDGCN-NEXT:   %a.addr = alloca i32, align 4, addrspace(5)
+// CHECK-AMDGCN-NEXT:   %b.addr = alloca i32, align 4, addrspace(5)
+// CHECK-AMDGCN-NEXT:   %k2 = addrspacecast ptr addrspace(5) %k to ptr
+// CHECK-AMDGCN-NEXT:   %a.addr.ascast = addrspacecast ptr addrspace(5) %a.addr to ptr
+// CHECK-AMDGCN-NEXT:   %b.addr.ascast = addrspacecast ptr addrspace(5) %b.addr to ptr
+// CHECK-AMDGCN-NEXT:   %coerce.dive = getelementptr inbounds nuw %class.anon.1, ptr %k2, i32 0, i32 0
+// CHECK-AMDGCN-NEXT:   %coerce.dive1 = getelementptr inbounds nuw %struct.copyable, ptr %coerce.dive, i32 0, i32 0
+// CHECK-AMDGCN-NEXT:   store i32 %k.coerce, ptr %coerce.dive1, align 4
+// CHECK-AMDGCN-NEXT:   store i32 %a, ptr %a.addr.ascast, align 4
+// CHECK-AMDGCN-NEXT:   store i32 %b, ptr %b.addr.ascast, align 4
+// CHECK-AMDGCN-NEXT:   %0 = load i32, ptr %a.addr.ascast, align 4
+// CHECK-AMDGCN-NEXT:   %1 = load i32, ptr %b.addr.ascast, align 4
+// CHECK-AMDGCN-NEXT:   %call = call noundef i32 @_ZZ4mainENKUliiE_clEii
+// CHECK-AMDGCN-SAME:     (ptr noundef nonnull align 4 dereferenceable(4) %k2, i32 noundef %0, i32 noundef %1) #[[AMDGCN_ATTR1:[0-9]+]]
+// CHECK-AMDGCN-NEXT:   ret void
+// CHECK-AMDGCN-NEXT: }
+//
+// CHECK-NVPTX:       Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
+// CHECK-NVPTX-NEXT:  define dso_local ptx_kernel void @_ZTSZ4mainE2KN
+// CHECK-NVPTX-SAME:    (ptr noundef byval(%class.anon.1) align 4 %k, i32 noundef %a, i32 noundef %b) #[[NVPTX_ATTR0:[0-9]+]] {
+// CHECK-NVPTX-NEXT:  entry:
+// CHECK-NVPTX-NEXT:    %a.addr = alloca i32, align 4
+// CHECK-NVPTX-NEXT:    %b.addr = alloca i32, align 4
+// CHECK-NVPTX-NEXT:    store i32 %a, ptr %a.addr, align 4
+// CHECK-NVPTX-NEXT:    store i32 %b, ptr %b.addr, align 4
+// CHECK-NVPTX-NEXT:    %0 = load i32, ptr %a.addr, align 4
+// CHECK-NVPTX-NEXT:    %1 = load i32, ptr %b.addr, align 4
+// CHECK-NVPTX-NEXT:    %call = call noundef i32 @_ZZ4mainENKUliiE_clEii
+// CHECK-NVPTX-SAME:      (ptr noundef nonnull align 4 dereferenceable(4) %k, i32 noundef %0, i32 noundef %1) #[[NVPTX_ATTR1:[0-9]+]]
+// CHECK-NVPTX-NEXT:    ret void
+// CHECK-NVPTX-NEXT:  }
+//
+// CHECK-SPIRNV:      Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
+// CHECK-SPIRNV-NEXT: define dso_local spir_kernel void @_ZTSZ4mainE2KN
+// CHECK-SPIRNV-SAME:   (ptr noundef %k, i32 noundef %a, i32 noundef %b) #[[SPIR_ATTR0:[0-9]+]] {
+// CHECK-SPIRNV-NEXT: entry:
+// CHECK-SPIRNV-NEXT:   %k.indirect_addr = alloca ptr addrspace(4), align {{[48]}}
+// CHECK-SPIRNV-NEXT:   %a.addr = alloca i32, align 4
+// CHECK-SPIRNV-NEXT:   %b.addr = alloca i32, align 4
+// CHECK-SPIRNV-NEXT:   %k.indirect_addr.ascast = addrspacecast ptr %k.indirect_addr to ptr addrspace(4)
+// CHECK-SPIRNV-NEXT:   %a.addr.ascast = addrspacecast ptr %a.addr to ptr addrspace(4)
+// CHECK-SPIRNV-NEXT:   %b.addr.ascast = addrspacecast ptr %b.addr to ptr addrspace(4)
+// CHECK-SPIRNV-NEXT:   store ptr %k, ptr addrspace(4) %k.indirect_addr.ascast, align {{[48]}}
+// CHECK-SPIRNV-NEXT:   %k.ascast = addrspacecast ptr %k to ptr addrspace(4)
+// CHECK-SPIRNV-NEXT:   store i32 %a, ptr addrspace(4) %a.addr.ascast, align 4
+// CHECK-SPIRNV-NEXT:   store i32 %b, ptr addrspace(4) %b.addr.ascast, align 4
+// CHECK-SPIRNV-NEXT:   %0 = load i32, ptr addrspace(4) %a.addr.ascast, align 4
+// CHECK-SPIRNV-NEXT:   %1 = load i32, ptr addrspace(4) %b.addr.ascast, align 4
+// CHECK-SPIRNV-NEXT:   %call = call spir_func noundef i32 @_ZZ4mainENKUliiE_clEii
+// CHECK-SPIRNV-SAME:     (ptr addrspace(4) noundef align 4 dereferenceable_or_null(4) %k.ascast, i32 noundef %0, i32 noundef %1) #[[SPIR_ATTR1:[0-9]+]]
+// CHECK-SPIRNV-NEXT:   ret void
+// CHECK-SPIRNV-NEXT: }
+//
+// CHECK-SPIRV:       Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
+// CHECK-SPIRV-NEXT:  define spir_kernel void @_ZTSZ4mainE2KN
+// CHECK-SPIRV-SAME:    (ptr noundef byval(%class.anon.1) align 4 %k, i32 noundef %a, i32 noundef %b) #[[SPIR_ATTR0:[0-9]+]] {
+// CHECK-SPIRV-NEXT:  entry:
+// CHECK-SPIRV-NEXT:    %a.addr = alloca i32, align 4
+// CHECK-SPIRV-NEXT:    %b.addr = alloca i32, align 4
+// CHECK-SPIRV-NEXT:    %a.addr.ascast = addrspacecast ptr %a.addr to ptr addrspace(4)
+// CHECK-SPIRV-NEXT:    %b.addr.ascast = addrspacecast ptr %b.addr to ptr addrspace(4)
+// CHECK-SPIRV-NEXT:    %k.ascast = addrspacecast ptr %k to ptr addrspace(4)
+// CHECK-SPIRV-NEXT:    store i32 %a, ptr addrspace(4) %a.addr.ascast, align 4
+// CHECK-SPIRV-NEXT:    store i32 %b, ptr addrspace(4) %b.addr.ascast, align 4
+// CHECK-SPIRV-NEXT:    %0 = load i32, ptr addrspace(4) %a.addr.ascast, align 4
+// CHECK-SPIRV-NEXT:    %1 = load i32, ptr addrspace(4) %b.addr.ascast, align 4
+// CHECK-SPIRV-NEXT:    %call = call spir_func noundef i32 @_ZZ4mainENKUliiE_clEii
+// CHECK-SPIRV-SAME:      (ptr addrspace(4) noundef align 4 dereferenceable_or_null(4) %k.ascast, i32 noundef %0, i32 noundef %1) #[[SPIR_ATTR1:[0-9]+]]
+// CHECK-SPIRV-NEXT:    ret void
+// CHECK-SPIRV-NEXT:  }
 
 // CHECK-AMDGCN: #[[AMDGCN_ATTR0]] = { convergent mustprogress noinline norecurse nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // CHECK-AMDGCN: #[[AMDGCN_ATTR1]] = { convergent nounwind }
diff --git a/clang/test/CodeGenSYCL/unique_stable_name_windows_diff.cpp b/clang/test/CodeGenSYCL/unique_stable_name_windows_diff.cpp
index 63db83c02bbef..c298593e2f1ab 100644
--- a/clang/test/CodeGenSYCL/unique_stable_name_windows_diff.cpp
+++ b/clang/test/CodeGenSYCL/unique_stable_name_windows_diff.cpp
@@ -1,8 +1,8 @@
 // RUN: %clang_cc1 -triple spir64-unknown-unknown -aux-triple x86_64-pc-windows-msvc -fsycl-is-device -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s '-D$ADDRSPACE=addrspace(1) '
 // RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsycl-is-host -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s '-D$ADDRSPACE='
 
-template <typename KernelName, typename KernelObj>
-void sycl_kernel_launch(const char *, KernelObj) {}
+template<typename KN, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 template<typename KN, typename Func>
 [[clang::sycl_kernel_entry_point(KN)]] void kernel(Func F){
diff --git a/clang/test/SemaSYCL/sycl-host-kernel-launch.cpp b/clang/test/SemaSYCL/sycl-host-kernel-launch.cpp
deleted file mode 100644
index eda0c4da489a9..0000000000000
--- a/clang/test/SemaSYCL/sycl-host-kernel-launch.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
-// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=c++17 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -fcxx-exceptions -verify=device,expected %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
-
-// A unique kernel name type is required for each declared kernel entry point.
-template<int, int = 0> struct KN;
-
-[[clang::sycl_kernel_entry_point(KN<1>)]]
-void nolauncher() {} 
-// host-error at -1 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-// device-warning at -2 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-// expected-note at -3 {{define 'sycl_kernel_launch' function template to fix}}
-
-void sycl_kernel_launch(const char *, int arg);
-// expected-note at -1 {{declared as a non-template here}}
-
-[[clang::sycl_kernel_entry_point(KN<2>)]]
-void nontemplatel() {}
-// host-error at -1 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-// device-warning at -2 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-// expected-note at -3 {{define 'sycl_kernel_launch' function template to fix}}
-// expected-error at -4 {{'sycl_kernel_launch' following the 'template' keyword does not refer to a template}}
-
-template <typename KernName>
-void sycl_kernel_launch(const char *, int arg);
-// expected-note at -1 {{candidate function template not viable: requires 2 arguments, but 1 was provided}}
-// expected-note at -2 2{{candidate function template not viable: no known conversion from 'Kern' to 'int' for 2nd argument}}
-
-[[clang::sycl_kernel_entry_point(KN<3>)]]
-void notenoughargs() {}
-// expected-error at -1 {{no matching function for call to 'sycl_kernel_launch'}}
-// FIXME: Should this also say "no suitable function for host code synthesis"?
-
-
-template <typename KernName>
-void sycl_kernel_launch(const char *, bool arg = 1);
-// expected-note at -1 2{{candidate function template not viable: no known conversion from 'Kern' to 'bool' for 2nd argument}}
-
-[[clang::sycl_kernel_entry_point(KN<4>)]]
-void enoughargs() {}
-
-namespace boop {
-template <typename KernName, typename KernelObj>
-void sycl_kernel_launch(const char *, KernelObj);
-
-template <typename KernName, typename KernelObj>
-[[clang::sycl_kernel_entry_point(KernName)]]
-void iboop(KernelObj Kernel) {
-  Kernel();
-}
-}
-
-template <typename KernName, typename KernelObj>
-[[clang::sycl_kernel_entry_point(KernName)]]
-void idontboop(KernelObj Kernel) {
-  Kernel();
-}
-// expected-error at -3 {{no matching function for call to 'sycl_kernel_launch'}}
-
-struct Kern {
-  int a;
-  int *b;
-  Kern(int _a, int* _b) : a(_a), b(_b) {}
-  void operator()(){ *b = a;}
-};
-
-void foo() {
-  int *a;
-  Kern b(1, a);
-  idontboop<KN<6>>(b);
-  // expected-note at -1 {{in instantiation of function template specialization 'idontboop<KN<6>, Kern>' requested here}}
-  boop::iboop<KN<7>>(b);
-}
-
-class MaybeHandler {
-
-template <typename KernName>
-void sycl_kernel_launch(const char *);
-
-template <typename KernName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys ...Args);
-
-public:
-
-template <typename KernName, typename KernelObj>
-[[clang::sycl_kernel_entry_point(KernName)]]
-void entry(KernelObj Kernel) {
-  Kernel();
-}
-};
-
-class MaybeHandler2 {
-
-template <typename KernName, typename... Tys>
-static void sycl_kernel_launch(const char *, Tys ...Args);
-
-public:
-
-template <typename KernName, typename KernelObj>
-[[clang::sycl_kernel_entry_point(KernName)]]
-void entry(KernelObj Kernel) {
-  Kernel();
-}
-};
-
-class MaybeHandler3 {
-
-template <typename KernName, typename... Tys>
-static void sycl_kernel_launch(const char *, Tys ...Args);
-
-public:
-
-template <typename KernName, typename KernelObj>
-[[clang::sycl_kernel_entry_point(KernName)]]
-static void entry(KernelObj Kernel) {
-  Kernel();
-}
-};
-
-class MaybeHandler4 {
-
-template <typename KernName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys ...Args);
-
-public:
-
-template <typename KernName, typename KernelObj>
-[[clang::sycl_kernel_entry_point(KernName)]]
-static void entry(KernelObj Kernel) { 
-  // expected-error at -1 {{call to non-static member function without an object argument}}
-  // FIXME: Should that be clearer?
-  Kernel();
-}
-};
-
-template<typename>
-struct base_handler {
-  template<typename KNT, typename... Ts>
-  void sycl_kernel_launch(const char*, Ts...) {}
-};
-struct derived_handler : base_handler<derived_handler> {
-  template<typename KNT, typename KT>
-  [[clang::sycl_kernel_entry_point(KNT)]]
-  void entry(KT k) { k(); }
-};
-
-template<int N>
-struct derived_handler_t : base_handler<derived_handler_t<N>> {
-  template<typename KNT, typename KT>
-// FIXME this fails because accessing members of dependent bases requires
-// explicit qualification.
-  [[clang::sycl_kernel_entry_point(KNT)]]
-  void entry(KT k) { k(); }
-  // expected-error at -1 {{no matching function for call to 'sycl_kernel_launch'}}
-};
-
-template<typename KNT>
-struct kernel_launcher {
-  template<typename... Ts>
-  void operator()(const char*, Ts...) const {}
-};
-
-namespace var {
-template<typename KNT>
-kernel_launcher<KNT> sycl_kernel_launch;
-
-struct handler {
-  template<typename KNT, typename KT>
-  [[clang::sycl_kernel_entry_point(KNT)]]
-  void entry(KT k) { k(); }
-};
-}
-
-
-void bar() {
-  int *a;
-  Kern b(1, a);
-  MaybeHandler H;
-  MaybeHandler2 H1;
-  MaybeHandler3 H2;
-  MaybeHandler4 H3;
-  H.entry<KN<8>>(b);
-  H1.entry<KN<9>>(b);
-  H2.entry<KN<10>>(b);
-  H3.entry<KN<11>>(b);
-
-  derived_handler H5;
-  H5.entry<KN<12>>(b);
-
-  derived_handler_t<13> H6;
-  H6.entry<KN<13>>(b); //expected-note {{in instantiation of function template specialization}}
-
-  var::handler h;
-  h.entry<KN<14>>(b);
-}
-
-
-
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
index c181f76321a26..358ed18b6672b 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
@@ -40,10 +40,9 @@ struct coroutine_traits {
 // A unique kernel name type is required for each declared kernel entry point.
 template<int, int = 0> struct KN;
 
-// A launcher function definition required for host code synthesis to silence
-// complains.
-template <typename KernelName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys &&...Args) {}
+// A generic kernel launch function.
+template<typename KNT, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 ////////////////////////////////////////////////////////////////////////////////
 // Valid declarations.
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
index 1cdd48f1e5840..b1c9e270a02b8 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
@@ -10,10 +10,9 @@
 template<int> struct ST; // #ST-decl
 template<int N> using TTA = ST<N>; // #TTA-decl
 
-// A launcher function definition required for host code synthesis to silence
-// complains.
-template <typename KernelName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys &&...Args) {}
+// A generic kernel launch function.
+template<typename KN, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 ////////////////////////////////////////////////////////////////////////////////
 // Valid declarations.
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp
index 44a3ce6f3640a..05a660e91e82c 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp
@@ -17,10 +17,9 @@ module M2 { header "m2.h" }
 #--- common.h
 template<int> struct KN;
 
-// A launcher function definition required for host code synthesis to silence
-// complains.
-template <typename KernelName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys &&...Args) {}
+// A generic kernel launch function.
+template<typename KN, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void common_test1() {}
@@ -30,6 +29,7 @@ template<typename T>
 void common_test2() {}
 template void common_test2<KN<2>>();
 
+
 #--- m1.h
 #include "common.h"
 
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp
index 0e6d1a6c57e39..dcea60e016d12 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp
@@ -15,10 +15,9 @@
 #--- pch.h
 template<int> struct KN;
 
-// A launcher function definition required for host code synthesis to silence
-// complains.
-template <typename KernelName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys &&...Args) {}
+// A generic kernel launch function.
+template<typename KN, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void pch_test1() {} // << expected previous declaration note here.
@@ -31,11 +30,11 @@ template void pch_test2<KN<2>>();
 
 #--- test.cpp
 // expected-error at +3 {{the 'clang::sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}}
-// expected-note at pch.h:9 {{previous declaration is here}}
+// expected-note at pch.h:8 {{previous declaration is here}}
 [[clang::sycl_kernel_entry_point(KN<1>)]]
 void test1() {}
 
 // expected-error at +3 {{the 'clang::sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}}
-// expected-note at pch.h:13 {{previous declaration is here}}
+// expected-note at pch.h:12 {{previous declaration is here}}
 [[clang::sycl_kernel_entry_point(KN<2>)]]
 void test2() {}
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
index e2e6bf3314614..2abb24cde6663 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
@@ -10,10 +10,9 @@
 
 struct S1;
 
-// A launcher function definition required for host code synthesis to silence
-// complains.
-template <typename KernelName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys &&...Args) {}
+// A generic kernel launch function.
+template<typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 // expected-warning at +3 {{redundant 'clang::sycl_kernel_entry_point' attribute}}
 // expected-note at +1  {{previous attribute is here}}
@@ -124,25 +123,3 @@ struct B19 {
 };
 // expected-note at +1 {{in instantiation of template class 'B19<int>' requested here}}
 B19<int> b19;
-
-struct auto_name;
-
-// expected-error at +4 {{the 'clang::sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}}
-// expected-note at +3 {{previous declaration is here}}
-template <typename KernelName, typename KernelType>
-[[clang::sycl_kernel_entry_point(KernelName)]]
-void __kernel_single_task(const KernelType KernelFunc) {
-  KernelFunc();
-}
-
-template <typename KernelType, typename KernelName = auto_name>
-void pf(KernelType K) {
-  // expected-note at +1 {{requested here}}
-  __kernel_single_task<KernelName>(K);
-}
-
-void foo() {
-  pf([](){});
-  // expected-note at +1 {{requested here}}
-  pf([](){});
-}
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
index 9674dac456f9f..b39a77bd35878 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
@@ -10,10 +10,9 @@
 // attribute during instantiation of a specialization unless that specialization
 // is selected by overload resolution.
 
-// A launcher function definition required for host code synthesis to silence
-// complains.
-template <typename KernelName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys &&...Args) {}
+// A generic kernel launch function.
+template<typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 // FIXME: C++23 [temp.expl.spec]p12 states:
 // FIXME:   ... Similarly, attributes appearing in the declaration of a template
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
index 7b8fc6c9a7630..e4508710ee10b 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
@@ -22,10 +22,9 @@ struct type_info {
 };
 } // namespace std
 
-// A launcher function definition required for host code synthesis to silence
-// complains.
-template <typename KernelName, typename... Tys>
-void sycl_kernel_launch(const char *, Tys &&...Args) {}
+// A generic kernell launch function.
+template<typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
 
 ////////////////////////////////////////////////////////////////////////////////
 // Valid declarations.
diff --git a/clang/test/SemaSYCL/sycl-kernel-launch.cpp b/clang/test/SemaSYCL/sycl-kernel-launch.cpp
new file mode 100644
index 0000000000000..74a82aa498aab
--- /dev/null
+++ b/clang/test/SemaSYCL/sycl-kernel-launch.cpp
@@ -0,0 +1,448 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify=device,expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify=device,expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-device -verify=device,expected %s
+
+// Test overload resolution for implicit calls to sycl_kernel_launch<KN>(...)
+// synthesized for functions declared with the sycl_kernel_entry_point
+// attribute.
+
+////////////////////////////////////////////////////////////////////////////////
+// Valid declarations.
+////////////////////////////////////////////////////////////////////////////////
+
+// A unique kernel name type is required for each declared kernel entry point.
+template<int, int = 0> struct KN;
+
+// A generic kernel object type.
+template<int, int = 0>
+struct KT {
+  void operator()() const;
+};
+
+
+// sycl_kernel_launch as function template at namespace scope.
+namespace ok1 {
+  template<typename KN, typename... Ts>
+  void sycl_kernel_launch(const char *, Ts...);
+  [[clang::sycl_kernel_entry_point(KN<1>)]]
+  void skep(KT<1> k) {
+    k();
+  }
+}
+
+// sycl_kernel_launch as function template at namespace scope with default
+// template arguments and default function arguments..
+namespace ok2 {
+  template<typename KN, typename T = int>
+  void sycl_kernel_launch(const char *, KT<2>, T = 2);
+  [[clang::sycl_kernel_entry_point(KN<2>)]]
+  void skep(KT<2> k) {
+    k();
+  }
+}
+
+// sycl_kernel_launch as overload set.
+namespace ok3 {
+  template<typename KN>
+  void sycl_kernel_launch(const char *);
+  template<typename KN, typename... Ts>
+  void sycl_kernel_launch(const char *, Ts...);
+  [[clang::sycl_kernel_entry_point(KN<3>)]]
+  void skep(KT<3> k) {
+    k();
+  }
+}
+
+// sycl_kernel_launch as static member function template.
+namespace ok4 {
+  struct handler {
+  private:
+    template<typename KN, typename... Ts>
+    static void sycl_kernel_launch(const char *, Ts...);
+  public:
+    [[clang::sycl_kernel_entry_point(KN<4,0>)]]
+    static void skep(KT<4,0> k) {
+      k();
+    }
+    [[clang::sycl_kernel_entry_point(KN<4,1>)]]
+    void skep(KT<4,1> k) {
+      k();
+    }
+  };
+}
+
+// sycl_kernel_launch as non-static member function template.
+namespace ok5 {
+  struct handler {
+  private:
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(const char *, Ts...);
+  public:
+    [[clang::sycl_kernel_entry_point(KN<5>)]]
+    void skep(KT<5> k) {
+      k();
+    }
+  };
+}
+
+#if __cplusplus >= 202302L
+// sycl_kernel_launch as non-static member function template with explicit
+// object parameter.
+namespace ok6 {
+  struct handler {
+  private:
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(this handler self, const char *, Ts...);
+  public:
+    [[clang::sycl_kernel_entry_point(KN<6>)]]
+    void skep(KT<6> k) {
+      k();
+    }
+  };
+}
+#endif
+
+// sycl_kernel_launch as variable template.
+namespace ok7 {
+  template<typename KN>
+  struct launcher {
+    template<typename... Ts>
+    void operator()(const char *, Ts...);
+  };
+  template<typename KN>
+  launcher<KN> sycl_kernel_launch;
+  [[clang::sycl_kernel_entry_point(KN<7>)]]
+  void skep(KT<7> k) {
+    k();
+  }
+}
+
+#if __cplusplus >= 202302L
+// sycl_kernel_launch as variable template with static call operator template.
+namespace ok8 {
+  template<typename KN>
+  struct launcher {
+    template<typename... Ts>
+    static void operator()(const char *, Ts...);
+  };
+  template<typename KN>
+  launcher<KN> sycl_kernel_launch;
+  [[clang::sycl_kernel_entry_point(KN<8>)]]
+  void skep(KT<8> k) {
+    k();
+  }
+}
+#endif
+
+#if __cplusplus >= 202302L
+// sycl_kernel_launch as variable template with call operator template with
+// explicit object parameter.
+namespace ok9 {
+  template<typename KN>
+  struct launcher {
+    template<typename... Ts>
+    void operator()(this launcher self, const char *, Ts...);
+  };
+  template<typename KN>
+  launcher<KN> sycl_kernel_launch;
+  [[clang::sycl_kernel_entry_point(KN<9>)]]
+  void skep(KT<9> k) {
+    k();
+  }
+}
+#endif
+
+// sycl_kernel_launch as base class non-static member function template.
+namespace ok10 {
+  template<typename Derived>
+  struct base_handler {
+  protected:
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(const char *, Ts...);
+  };
+  struct handler : protected base_handler<handler> {
+  public:
+    [[clang::sycl_kernel_entry_point(KN<10>)]]
+    void skep(KT<10> k) {
+      k();
+    }
+  };
+}
+
+// sycl_kernel_launch with forward reference parameters.
+namespace ok11 {
+  template<typename KN, typename... Ts>
+  void sycl_kernel_launch(const char *, Ts &&...);
+  struct non_copyable {
+    non_copyable(const non_copyable&) = delete;
+  };
+  struct non_moveable {
+    non_moveable(non_moveable&&) = delete;
+  };
+  [[clang::sycl_kernel_entry_point(KN<11>)]]
+  void skep(KT<11> k, non_copyable, non_moveable) {
+    k();
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Invalid declarations.
+////////////////////////////////////////////////////////////////////////////////
+
+// A unique kernel name type is required for each declared kernel entry point.
+template<int, int = 0> struct BADKN;
+
+// A generic kernel object type.
+template<int, int = 0>
+struct BADKT {
+  void operator()() const;
+};
+
+// Undeclared sycl_kernel_launch identifier.
+namespace bad1 {
+  // host-error at +5 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+  // device-warning at +4 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+  // expected-note at +3 {{define 'sycl_kernel_launch' function template to fix this problem}}
+  template<typename KN, typename KT>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep(KT k) {
+    k();
+  }
+  template void skep<BADKN<1>>(BADKT<1>);
+}
+
+// No matching function for call to sycl_kernel_launch; not a template.
+namespace bad2 {
+  // expected-note at +1 {{declared as a non-template here}}
+  void sycl_kernel_launch(const char *, BADKT<2>);
+  // expected-error at +5 {{'sycl_kernel_launch' does not refer to a template}}
+  // host-error at +4 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+  // device-warning at +3 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+  // expected-note at +2 {{define 'sycl_kernel_launch' function template to fix this problem}}
+  [[clang::sycl_kernel_entry_point(BADKN<2>)]]
+  void skep(BADKT<2> k) {
+    k();
+  }
+}
+
+// No matching function for call to sycl_kernel_launch; not enough arguments.
+namespace bad3 {
+  // expected-note at +2 {{candidate function template not viable: requires 2 arguments, but 1 was provided}}
+  template<typename KN, typename KT>
+  void sycl_kernel_launch(const char *, KT);
+  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  template<typename KN>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep() {}
+  // expected-note at +1 {{in instantiation of function template specialization 'bad3::skep<BADKN<3>>' requested here}}
+  template void skep<BADKN<3>>();
+}
+
+// No matching function for call to sycl_kernel_launch; too many arguments.
+namespace bad4 {
+  // expected-note at +2 {{candidate function template not viable: requires 2 arguments, but 3 were provided}}
+  template<typename KN, typename KT>
+  void sycl_kernel_launch(const char *, KT);
+  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  template<typename KN, typename KT>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep(KT k, int i) {
+    k();
+  }
+  // expected-note at +1 {{in instantiation of function template specialization 'bad4::skep<BADKN<4>, BADKT<4>>' requested here}}
+  template void skep<BADKN<4>>(BADKT<4>, int);
+}
+
+// No matching function for call to sycl_kernel_launch; mismatched function parameter type.
+namespace bad5 {
+  // expected-note at +2 {{candidate function template not viable: no known conversion from 'const char[21]' to 'int' for 1st argument}}
+  template<typename KN, typename... Ts>
+  void sycl_kernel_launch(int, Ts...);
+  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  template<typename KN, typename KT>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep(KT k) {
+    k();
+  }
+  // expected-note at +1 {{in instantiation of function template specialization 'bad5::skep<BADKN<5>, BADKT<5>>' requested here}}
+  template void skep<BADKN<5>>(BADKT<5>);
+}
+
+// No matching function for call to sycl_kernel_launch; mismatched template parameter kind.
+namespace bad6 {
+  // expected-note at +2 {{candidate template ignored: invalid explicitly-specified argument for 1st template parameter}}
+  template<int, typename... Ts>
+  void sycl_kernel_launch(const char *, Ts...);
+  // expected-error at +2 {{no matching function for call to 'sycl_kernel_launch'}}
+  [[clang::sycl_kernel_entry_point(BADKN<6>)]]
+  void skep(BADKT<6> k) {
+    k();
+  }
+}
+
+// No matching function for call to sycl_kernel_launch object; mismatched function parameter type.
+namespace bad7 {
+  template<typename KN>
+  struct launcher {
+    // expected-note at +2 {{candidate function template not viable: no known conversion from 'const char[21]' to 'int' for 1st argument}}
+    template<typename... Ts>
+    void operator()(int, Ts...);
+  };
+  template<typename KN>
+  launcher<KN> sycl_kernel_launch;
+  // expected-error at +3 {{no matching function for call to object of type 'launcher<BADKN<7, 0>>'}}
+  template<typename KN, typename KT>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep(KT k) {
+    k();
+  }
+  // expected-note at +1 {{in instantiation of function template specialization 'bad7::skep<BADKN<7>, BADKT<7>>' requested here}}
+  template void skep<BADKN<7>>(BADKT<7>);
+}
+
+// No matching function for call to sycl_kernel_launch object; mismatched template parameter kind.
+namespace bad8 {
+  template<int KN>
+  struct launcher {
+    template<typename... Ts>
+    void operator()(int, Ts...);
+  };
+  // expected-note at +1 {{template parameter is declared here}}
+  template<int KN>
+  launcher<KN> sycl_kernel_launch;
+  // expected-error at +3 {{template argument for non-type template parameter must be an expression}}
+  template<typename KN, typename KT>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep(KT k) {
+    k();
+  }
+  template void skep<BADKN<8>>(BADKT<8>);
+}
+
+// sycl_kernel_launch as variable template with private call operator template.
+namespace bad9 {
+  template<typename KN>
+  struct launcher {
+  private:
+    // expected-note at +2 {{declared private here}}
+    template<typename... Ts>
+    void operator()(const char *, Ts...);
+  };
+  template<typename KN>
+  launcher<KN> sycl_kernel_launch;
+  // expected-error at +2 {{'operator()' is a private member of 'bad9::launcher<BADKN<9>>'}}
+  [[clang::sycl_kernel_entry_point(BADKN<9>)]]
+  void skep(BADKT<9> k) {
+    k();
+  }
+}
+
+// Ambiguous reference to sycl_kernel_launch.
+namespace bad10 {
+  inline namespace in1 {
+    // expected-note at +2 {{candidate found by name lookup is 'bad10::in1::sycl_kernel_launch'}}
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(const char *, Ts...);
+  }
+  inline namespace in2 {
+    template<typename KN>
+    struct launcher {
+      template<typename KT, typename... Ts>
+      void operator()(const char *, Ts...);
+    };
+    // expected-note at +2 {{candidate found by name lookup is 'bad10::in2::sycl_kernel_launch'}}
+    template<typename KN>
+    launcher<KN> sycl_kernel_launch;
+  }
+  // expected-error at +6 {{reference to 'sycl_kernel_launch' is ambiguous}}
+  // host-error at +5 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+  // device-warning at +4 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+  // expected-note at +3 {{define 'sycl_kernel_launch' function template to fix this problem}}
+  template<typename KN, typename KT>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep(KT k) {
+    k();
+  }
+  template void skep<BADKN<10>>(BADKT<10>);
+}
+
+// Ambiguous call to sycl_kernel_launch.
+namespace bad11 {
+  // expected-note at +2 {{candidate function [with KN = BADKN<11>, KT = BADKT<11>]}}
+  template<typename KN, typename KT>
+  void sycl_kernel_launch(const char *, KT, signed char);
+  // expected-note at +2 {{candidate function [with KN = BADKN<11>, KT = BADKT<11>]}}
+  template<typename KN, typename KT>
+  void sycl_kernel_launch(const char *, KT, unsigned char);
+  // expected-error at +2 {{call to 'sycl_kernel_launch' is ambiguous}}
+  [[clang::sycl_kernel_entry_point(BADKN<11>)]]
+  void skep(BADKT<11> k, int i) {
+    k();
+  }
+}
+
+// Call to member sycl_kernel_launch from non-static member.
+namespace bad12 {
+  struct S {
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(const char *, Ts...);
+    // expected-error at +2 {{call to non-static member function without an object argument}}
+    [[clang::sycl_kernel_entry_point(BADKN<12>)]]
+    static void skep(BADKT<12> k) {
+      k();
+    }
+  };
+}
+
+// sycl_kernel_launch as dependent base class non-static member function
+// template.
+namespace bad13 {
+  template<typename Derived>
+  struct base_handler {
+  protected:
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(const char *, Ts...);
+  };
+  template<int N>
+  struct handler : protected base_handler<handler<N>> {
+    // Lookup for sycl_kernel_launch fails because lookup in dependent base
+    // classes requires explicit qualification.
+    // host-error at +4 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+    // device-warning at +3 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
+    // expected-note at +2 {{define 'sycl_kernel_launch' function template to fix this problem}}
+    [[clang::sycl_kernel_entry_point(BADKN<13>)]]
+    void skep(BADKT<13> k) {
+      k();
+    }
+  };
+  template void handler<13>::skep(BADKT<13>);
+}
+
+// sycl_kernel_launch with non-reference parameters and non-moveable arguments.
+namespace bad14 {
+  // expected-note at +2 2 {{passing argument to parameter here}}
+  template<typename KN, typename... Ts>
+  void sycl_kernel_launch(const char *, Ts...);
+  struct non_copyable {
+    // expected-note at +1 {{'non_copyable' has been explicitly marked deleted here}}
+    non_copyable(const non_copyable&) = delete;
+  };
+  // expected-error at +2 {{call to deleted constructor of 'bad14::non_copyable'}}
+  [[clang::sycl_kernel_entry_point(BADKN<14,0>)]]
+  void skep(BADKT<14,0> k, non_copyable) {
+    k();
+  }
+  struct non_moveable {
+    // expected-note at +1 {{copy constructor is implicitly deleted because 'non_moveable' has a user-declared move constructor}}
+    non_moveable(non_moveable&&) = delete;
+  };
+  // expected-error at +2 {{call to implicitly-deleted copy constructor of 'bad14::non_moveable'}}
+  [[clang::sycl_kernel_entry_point(BADKN<14,1>)]]
+  void skep(BADKT<14,1> k, non_moveable) {
+    k();
+  }
+}

>From 5b18f99f9c05e48e2785b583573b5a502e4efd76 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Fri, 14 Nov 2025 14:29:58 -0800
Subject: [PATCH 10/17] Address clang-format complaints.

---
 clang/include/clang/AST/StmtSYCL.h | 4 ++--
 clang/lib/Sema/SemaDecl.cpp        | 4 ++--
 clang/lib/Sema/SemaSYCL.cpp        | 5 ++---
 clang/lib/Sema/TreeTransform.h     | 4 ++--
 4 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/AST/StmtSYCL.h b/clang/include/clang/AST/StmtSYCL.h
index cce62466eec10..f3f4f040fb63c 100644
--- a/clang/include/clang/AST/StmtSYCL.h
+++ b/clang/include/clang/AST/StmtSYCL.h
@@ -125,8 +125,8 @@ class UnresolvedSYCLKernelCallStmt : public Stmt {
   void setKernelLaunchIdExpr(Expr *IdExpr) { KernelLaunchIdExpr = IdExpr; }
 
 public:
-  static UnresolvedSYCLKernelCallStmt *
-  Create(const ASTContext &C, CompoundStmt *CS, Expr *IdExpr) {
+  static UnresolvedSYCLKernelCallStmt *Create(const ASTContext &C,
+                                              CompoundStmt *CS, Expr *IdExpr) {
     return new (C) UnresolvedSYCLKernelCallStmt(CS, IdExpr);
   }
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 877eef41a847f..d9516114df37a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16226,8 +16226,8 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
 
   maybeAddDeclWithEffects(FD);
 
-  if (FD && !FD->isInvalidDecl() &&
-      FD->hasAttr<SYCLKernelEntryPointAttr>() && FnBodyScope) {
+  if (FD && !FD->isInvalidDecl() && FD->hasAttr<SYCLKernelEntryPointAttr>() &&
+      FnBodyScope) {
     // An implicit call expression is synthesized for functions declared with
     // the sycl_kernel_entry_point attribute. The call may resolve to a
     // function template, a member function template, or a call operator
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index cd4bb3f88f6c6..e754f934cab34 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -436,9 +436,8 @@ ExprResult SemaSYCL::BuildSYCLKernelLaunchIdExpr(FunctionDecl *FD,
                                               /*IsAddressOfOperand*/ false))
     // The lookup result allows for a possible implicit member access that
     // would require an implicit or explicit 'this' argument.
-    IdExpr = SemaRef.BuildPossibleImplicitMemberExpr(EmptySS, SourceLocation(),
-                                                     Result, &TALI,
-                                                     SemaRef.getCurScope());
+    IdExpr = SemaRef.BuildPossibleImplicitMemberExpr(
+        EmptySS, SourceLocation(), Result, &TALI, SemaRef.getCurScope());
   else
     IdExpr = SemaRef.BuildTemplateIdExpr(EmptySS, SourceLocation(), Result,
                                          /*RequiresADL*/ true, &TALI);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e11bce9758b30..07cfd4e734e26 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12976,11 +12976,11 @@ StmtResult TreeTransform<Derived>::TransformUnresolvedSYCLKernelCallStmt(
 
   ExprResult IdExpr = getDerived().TransformExpr(S->getKernelLaunchIdExpr());
   if (IdExpr.isInvalid())
-     return StmtError();
+    return StmtError();
 
   StmtResult Body = getDerived().TransformStmt(S->getOriginalStmt());
   if (Body.isInvalid())
-     return StmtError();
+    return StmtError();
 
   StmtResult SR = SemaRef.SYCL().BuildSYCLKernelCallStmt(
       cast<FunctionDecl>(SemaRef.CurContext), cast<CompoundStmt>(Body.get()),

>From 8a4fc3fc79d325f8f9735c4b475c969302918d46 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Fri, 14 Nov 2025 18:11:34 -0800
Subject: [PATCH 11/17] Added support for passing arguments to
 sycl_kernel_launch as xvalues.

---
 clang/lib/Sema/SemaSYCL.cpp                   | 24 +++++++--
 .../ast-dump-sycl-kernel-call-stmt.cpp        | 50 +++++++++++--------
 clang/test/SemaSYCL/sycl-kernel-launch.cpp    | 26 ++++++++--
 3 files changed, 69 insertions(+), 31 deletions(-)

diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index e754f934cab34..4da7d29b53882 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -456,8 +456,9 @@ namespace {
 
 // Constructs the arguments to be passed for the SYCL kernel launch call.
 // The first argument is a string literal that contains the SYCL kernel
-// name. The remaining arguments are the parameters of 'FD'.
-void BuildSYCLKernelLaunchCallArgs(Sema &SemaRef, FunctionDecl *FD,
+// name. The remaining arguments are the parameters of 'FD' passed as
+// move-elligible xvalues. Returns true on error and false otherwise.
+bool BuildSYCLKernelLaunchCallArgs(Sema &SemaRef, FunctionDecl *FD,
                                    const SYCLKernelInfo *SKI,
                                    SmallVectorImpl<Expr *> &Args,
                                    SourceLocation Loc) {
@@ -478,11 +479,23 @@ void BuildSYCLKernelLaunchCallArgs(Sema &SemaRef, FunctionDecl *FD,
                             /*Pascal*/ false, KernelNameArrayTy, Loc);
   Args.push_back(KernelNameExpr);
 
+  // Forward all parameters of 'FD' to the SYCL kernel launch function as if
+  // by std::move().
   for (ParmVarDecl *PVD : FD->parameters()) {
     QualType ParamType = PVD->getOriginalType().getNonReferenceType();
-    Expr *DRE = SemaRef.BuildDeclRefExpr(PVD, ParamType, VK_LValue, Loc);
-    Args.push_back(DRE);
+    ExprResult E = SemaRef.BuildDeclRefExpr(PVD, ParamType, VK_LValue, Loc);
+    if (E.isInvalid())
+      return true;
+    if (!PVD->getType()->isLValueReferenceType())
+      E = ImplicitCastExpr::Create(SemaRef.Context, E.get()->getType(), CK_NoOp,
+                                   E.get(), nullptr, VK_XValue,
+                                   FPOptionsOverride());
+    if (E.isInvalid())
+      return true;
+    Args.push_back(E.get());
   }
+
+  return false;
 }
 
 // Constructs the SYCL kernel launch call.
@@ -494,7 +507,8 @@ StmtResult BuildSYCLKernelLaunchCallStmt(Sema &SemaRef, FunctionDecl *FD,
   // IdExpr may be null if name lookup failed.
   if (IdExpr) {
     llvm::SmallVector<Expr *, 12> Args;
-    BuildSYCLKernelLaunchCallArgs(SemaRef, FD, SKI, Args, Loc);
+    if (BuildSYCLKernelLaunchCallArgs(SemaRef, FD, SKI, Args, Loc))
+      return StmtError();
     ExprResult LaunchResult =
         SemaRef.BuildCallExpr(SemaRef.getCurScope(), IdExpr, Loc, Args, Loc);
     if (LaunchResult.isInvalid())
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
index 5eef5db069faf..c5518d9038448 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
@@ -92,8 +92,8 @@ void skep2<KN<2>>(K<2>);
 // CHECK-NEXT: |   | |   | `-DeclRefExpr {{.*}} 'void (const char *, K<2>)' lvalue Function {{.*}} 'sycl_kernel_launch' {{.*}}
 // CHECK-NEXT: |   | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
 // CHECK-NEXT: |   | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi2EE"
-// CHECK-NEXT: |   | |   `-CXXConstructExpr {{.*}} 'K<2>' 'void (const K<2> &) noexcept'
-// CHECK-NEXT: |   | |     `-ImplicitCastExpr {{.*}} <NoOp>
+// CHECK-NEXT: |   | |   `-CXXConstructExpr {{.*}} 'K<2>' 'void (K<2> &&) noexcept'
+// CHECK-NEXT: |   | |     `-ImplicitCastExpr {{.*}} 'K<2>' xvalue <NoOp>
 // CHECK-NEXT: |   | |       `-DeclRefExpr {{.*}} 'K<2>' lvalue ParmVar {{.*}} 'k' 'K<2>'
 // CHECK-NEXT: |   | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: |   |   |-ImplicitParamDecl {{.*}} implicit used k 'K<2>'
@@ -148,8 +148,8 @@ void skep3<KN<3>>(K<3> k) {
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, K<3>)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, K<3>)' {{.*}}
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
 // CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi3EE"
-// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'K<3>' 'void (const K<3> &) noexcept'
-// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
+// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'K<3>' 'void (K<3> &&) noexcept'
+// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'K<3>' xvalue <NoOp>
 // CHECK-NEXT: | | |       `-DeclRefExpr {{.*}} 'K<3>' lvalue ParmVar {{.*}} 'k' 'K<3>'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<3>'
@@ -186,13 +186,15 @@ void skep4(K<4> k, int p1, int p2) {
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, K<4>, int, int)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, K<4>, int, int)' {{.*}}
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
 // CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi4EE"
-// CHECK-NEXT: | | |   |-CXXConstructExpr {{.*}} 'K<4>' 'void (const K<4> &) noexcept'
-// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
+// CHECK-NEXT: | | |   |-CXXConstructExpr {{.*}} 'K<4>' 'void (K<4> &&) noexcept'
+// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'K<4>' xvalue <NoOp>
 // CHECK-NEXT: | | |   |   `-DeclRefExpr {{.*}} 'K<4>' lvalue ParmVar {{.*}} 'k' 'K<4>'
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
-// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p1' 'int'
+// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'int' xvalue <NoOp>
+// CHECK-NEXT: | | |   |   `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p1' 'int'
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
-// CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p2' 'int'
+// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'int' xvalue <NoOp>
+// CHECK-NEXT: | | |       `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p2' 'int'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<4>'
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used p1 'int'
@@ -230,16 +232,20 @@ void skep5(int unused1, K<5> k, int unused2, int p, int unused3) {
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
 // CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi5EE"
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
-// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused1' 'int'
-// CHECK-NEXT: | | |   |-CXXConstructExpr {{.*}} 'K<5>' 'void (const K<5> &) noexcept'
-// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'const K<5>' lvalue <NoOp>
+// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'int' xvalue <NoOp>
+// CHECK-NEXT: | | |   |   `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused1' 'int'
+// CHECK-NEXT: | | |   |-CXXConstructExpr {{.*}} 'K<5>' 'void (K<5> &&) noexcept'
+// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'K<5>' xvalue <NoOp>
 // CHECK-NEXT: | | |   |   `-DeclRefExpr {{.*}} 'K<5>' lvalue ParmVar {{.*}} 'k' 'K<5>'
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
-// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused2' 'int'
+// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'int' xvalue <NoOp>
+// CHECK-NEXT: | | |   |   `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused2' 'int'
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
-// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p' 'int'
+// CHECK-NEXT: | | |   | `-ImplicitCastExpr {{.*}} 'int' xvalue <NoOp>
+// CHECK-NEXT: | | |   |   `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p' 'int'
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
-// CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused3' 'int'
+// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'int' xvalue <NoOp>
+// CHECK-NEXT: | | |       `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'unused3' 'int'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit unused1 'int'
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'K<5>'
@@ -332,8 +338,8 @@ void skep7(S7 k) {
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, S7)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, S7)' {{.*}}
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
 // CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi7EE"
-// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'S7' 'void (const S7 &) noexcept'
-// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'const S7' lvalue <NoOp>
+// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'S7' 'void (S7 &&) noexcept'
+// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'S7' xvalue <NoOp>
 // CHECK-NEXT: | | |       `-DeclRefExpr {{.*}} 'S7' lvalue ParmVar {{.*}} 'k' 'S7'
 // CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |-ImplicitParamDecl {{.*}} implicit used k 'S7'
@@ -366,8 +372,8 @@ void skep8(S8 k) {
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (const char *, S8)' lvalue Function {{.*}} 'sycl_kernel_launch' 'void (const char *, S8)' {{.*}}
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
 // CHECK-NEXT: | | |   | `-StringLiteral {{.*}} 'const char[12]' lvalue "_ZTS6\316\264\317\204\317\207"
-// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'S8' 'void (const S8 &) noexcept'
-// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'const S8' lvalue <NoOp>
+// CHECK-NEXT: | | |   `-CXXConstructExpr {{.*}} 'S8' 'void (S8 &&) noexcept'
+// CHECK-NEXT: | | |     `-ImplicitCastExpr {{.*}} 'S8' xvalue <NoOp>
 // CHECK-NEXT: | | |       `-DeclRefExpr {{.*}} 'S8' lvalue ParmVar {{.*}} 'k' 'S8'
 // CHECK:      | | `-OutlinedFunctionDecl {{.*}}
 // CHECK:      | `-SYCLKernelEntryPointAttr {{.*}}
@@ -429,12 +435,14 @@ void foo() {
 // CHECK-NEXT: | |   | |   |-ImplicitCastExpr {{.*}} 'const char *' <ArrayToPointerDecay>
 // CHECK-NEXT: | |   | |   | `-StringLiteral {{.*}} 'const char[14]' lvalue "_ZTS2KNILi9EE"
 // CHECK-NEXT: | |   | |   |-CXXConstructExpr {{.*}}
-// CHECK-NEXT: | |   | |   | `-ImplicitCastExpr {{.*}} lvalue <NoOp>
+// CHECK-NEXT: | |   | |   | `-ImplicitCastExpr {{.*}} xvalue <NoOp>
 // CHECK-NEXT: | |   | |   |   `-DeclRefExpr {{.*}} lvalue ParmVar {{.*}} 'k' {{.*}}
 // CHECK-NEXT: | |   | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
-// CHECK-NEXT: | |   | |   | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'a' 'int'
+// CHECK-NEXT: | |   | |   | `-ImplicitCastExpr {{.*}} 'int' xvalue <NoOp>
+// CHECK-NEXT: | |   | |   |   `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'a' 'int'
 // CHECK-NEXT: | |   | |   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
-// CHECK-NEXT: | |   | |     `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'b' 'int'
+// CHECK-NEXT: | |   | |     `-ImplicitCastExpr {{.*}} 'int' xvalue <NoOp>
+// CHECK-NEXT: | |   | |       `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'b' 'int'
 // CHECK-NEXT: | |   | `-OutlinedFunctionDecl {{.*}}
 // CHECK-NEXT: | |   |   |-ImplicitParamDecl {{.*}} implicit used k {{.*}}
 // CHECK-NEXT: | |   |   |-ImplicitParamDecl {{.*}} implicit used a 'int'
diff --git a/clang/test/SemaSYCL/sycl-kernel-launch.cpp b/clang/test/SemaSYCL/sycl-kernel-launch.cpp
index 74a82aa498aab..f69de9ab245bf 100644
--- a/clang/test/SemaSYCL/sycl-kernel-launch.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-launch.cpp
@@ -172,8 +172,21 @@ namespace ok10 {
   };
 }
 
-// sycl_kernel_launch with forward reference parameters.
+// sycl_kernel_launch with non-reference parameters.
 namespace ok11 {
+  template<typename KN, typename... Ts>
+  void sycl_kernel_launch(const char *, Ts...);
+  struct move_only {
+    move_only(move_only&&) = default;
+  };
+  [[clang::sycl_kernel_entry_point(KN<11>)]]
+  void skep(KT<11> k, move_only) {
+    k();
+  }
+}
+
+// sycl_kernel_launch with forward reference parameters.
+namespace ok12 {
   template<typename KN, typename... Ts>
   void sycl_kernel_launch(const char *, Ts &&...);
   struct non_copyable {
@@ -182,8 +195,11 @@ namespace ok11 {
   struct non_moveable {
     non_moveable(non_moveable&&) = delete;
   };
-  [[clang::sycl_kernel_entry_point(KN<11>)]]
-  void skep(KT<11> k, non_copyable, non_moveable) {
+  struct move_only {
+    move_only(move_only&&) = default;
+  };
+  [[clang::sycl_kernel_entry_point(KN<12>)]]
+  void skep(KT<12> k, non_copyable, non_moveable, move_only) {
     k();
   }
 }
@@ -437,10 +453,10 @@ namespace bad14 {
     k();
   }
   struct non_moveable {
-    // expected-note at +1 {{copy constructor is implicitly deleted because 'non_moveable' has a user-declared move constructor}}
+    // expected-note at +1 {{'non_moveable' has been explicitly marked deleted here}}
     non_moveable(non_moveable&&) = delete;
   };
-  // expected-error at +2 {{call to implicitly-deleted copy constructor of 'bad14::non_moveable'}}
+  // expected-error at +2 {{call to deleted constructor of 'bad14::non_moveable'}}
   [[clang::sycl_kernel_entry_point(BADKN<14,1>)]]
   void skep(BADKT<14,1> k, non_moveable) {
     k();

>From 69740b2c0d43ede25a0fd160dc78b5d1eec0a56c Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Fri, 14 Nov 2025 19:36:43 -0800
Subject: [PATCH 12/17] Prohibited use of the sycl_kernel_entry_point attribute
 with a non-static member function with an explicit object parameter.

---
 clang/include/clang/Basic/AttrDocs.td         |  1 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 ++-
 clang/lib/Sema/SemaSYCL.cpp                   |  7 +++++
 ...-kernel-entry-point-attr-appertainment.cpp | 26 ++++++++++++++++++
 .../sycl-kernel-entry-point-attr-this.cpp     | 27 ++++++++++---------
 5 files changed, 50 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 01013d10e8ddc..b95426bf76d32 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -551,6 +551,7 @@ following requirements.
 
 * Has a non-deduced ``void`` return type.
 * Is not a constructor or destructor.
+* Is not a non-static member function with an explicit object parameter.
 * Is not a C variadic function.
 * Is not a coroutine.
 * Is not defined as deleted or as defaulted.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 47360f29f27f5..8936bed2c5187 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13066,7 +13066,8 @@ def err_sycl_entry_point_invalid : Error<
            "constructor|destructor|coroutine|"
            "constexpr function|consteval function|"
            "function declared with the 'noreturn' attribute|"
-           "function defined with a function try block}1">;
+           "function defined with a function try block|"
+           "function with an explicit object parameter}1">;
 def err_sycl_entry_point_invalid_redeclaration : Error<
   "the %0 kernel name argument does not match prior"
   " declaration%diff{: $ vs $|}1,2">;
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 4da7d29b53882..c3b4c0ae3f336 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -325,6 +325,13 @@ void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {
         << SKEPAttr << /*destructor*/ 4;
     SKEPAttr->setInvalidAttr();
   }
+  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+    if (MD->isExplicitObjectMemberFunction()) {
+      Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
+          << SKEPAttr << /*function with an explicit object parameter*/ 10;
+      SKEPAttr->setInvalidAttr();
+    }
+  }
 
   if (FD->isVariadic()) {
     Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
index 358ed18b6672b..a17e4c1d1be0a 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
@@ -377,3 +377,29 @@ struct B42 {
   // expected-warning at +1 {{declaration does not declare anything}}
   [[clang::sycl_kernel_entry_point(BADKN<42>)]];
 };
+
+#if __cplusplus >= 202302L
+struct B43 {
+  // expected-error at +2 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a function with an explicit object parameter}}
+  template<typename KNT>
+  [[clang::sycl_kernel_entry_point(KNT)]]
+  void bad43(this B43) {}
+};
+#endif
+
+#if __cplusplus >= 202302L
+struct B44 {
+  // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a function with an explicit object parameter}}
+  [[clang::sycl_kernel_entry_point(BADKN<44>)]]
+  void bad44(this B44);
+};
+#endif
+
+#if __cplusplus >= 202302L
+template<typename KNT>
+struct B45 {
+  // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a function with an explicit object parameter}}
+  [[clang::sycl_kernel_entry_point(KNT)]]
+  void bad45(this B45);
+};
+#endif
diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
index e4508710ee10b..c9ab242754899 100644
--- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-this.cpp
@@ -1,9 +1,9 @@
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++17 -fsycl-is-host -verify %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++17 -fsycl-is-device -verify %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++20 -fsycl-is-host -verify -DCXX20 %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++20 -fsycl-is-device -verify -DCXX20 %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++23 -fsycl-is-host -verify -DCXX23 %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++23 -fsycl-is-device -verify -DCXX23 %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++20 -fsycl-is-host -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++20 -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++23 -fsycl-is-host -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++23 -fsycl-is-device -verify %s
 
 // These tests validate diagnostics for invalid use of 'this' in the body of
 // a function declared with the sycl_kernel_entry_point attribute.
@@ -82,7 +82,7 @@ struct S7 {
 template void S7<KN<7,0>, false>::ok7();
 template void S7<KN<7,1>, true>::ok7();
 
-#if defined(CXX20)
+#if __cplusplus >= 202002L
 template<typename KN, typename T>
 struct S8 {
   void mf(T);
@@ -98,14 +98,6 @@ struct S9 {
 template void S9<KN<9>, int>::ok9();
 #endif
 
-#if defined(CXX23)
-struct S10 {
-  [[clang::sycl_kernel_entry_point(KN<10>)]] void ok10(this S10 self) {
-    (void)self;
-  }
-};
-#endif
-
 
 ////////////////////////////////////////////////////////////////////////////////
 // Invalid declarations.
@@ -185,3 +177,12 @@ struct B8 {
 };
 // expected-note at +1 {{in instantiation of member function 'B8<BADKN<8>>::bad8' requested here}}
 template void B8<BADKN<8>>::bad8();
+
+#if __cplusplus >= 202302L
+struct B9 {
+  // expected-error at +1 {{the 'clang::sycl_kernel_entry_point' attribute cannot be applied to a function with an explicit object parameter}}
+  [[clang::sycl_kernel_entry_point(BADKN<9>)]] void bad9(this B9 self) {
+    (void)self;
+  }
+};
+#endif

>From 1aee751e4a540a9efdafac2abcb0b9449187e273 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Mon, 17 Nov 2025 20:39:42 -0800
Subject: [PATCH 13/17] Diagnostic improvements, additional tests.

Reworked diagnostics to include a synthesized code context and generation
of notes detailing implicit calls to sycl_kernel_launch().
---
 .../clang/Basic/DiagnosticSemaKinds.td        |  15 +-
 clang/include/clang/Sema/Sema.h               |  24 ++
 clang/lib/Frontend/FrontendActions.cpp        |   4 +
 clang/lib/Sema/SemaDecl.cpp                   |  33 ++-
 clang/lib/Sema/SemaSYCL.cpp                   |  53 +++-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  47 +++-
 clang/lib/Sema/TreeTransform.h                |   2 +
 clang/test/CodeGenSYCL/function-attrs.cpp     |   3 +
 .../SemaSYCL/sycl-kernel-launch-ms-compat.cpp |  43 ++++
 clang/test/SemaSYCL/sycl-kernel-launch.cpp    | 235 ++++++++++++------
 10 files changed, 349 insertions(+), 110 deletions(-)
 create mode 100644 clang/test/SemaSYCL/sycl-kernel-launch-ms-compat.cpp

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8936bed2c5187..0dec5dac3f1eb 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13089,15 +13089,12 @@ def err_sycl_entry_point_return_type : Error<
 def err_sycl_entry_point_deduced_return_type : Error<
   "the %0 attribute only applies to functions with a non-deduced 'void' return"
   " type">;
-def err_sycl_host_no_launch_function : Error<
-  "unable to find suitable 'sycl_kernel_launch' function for host code "
-  "synthesis">;
-def warn_sycl_device_no_host_launch_function : Warning<
-  "unable to find suitable 'sycl_kernel_launch' function for host code "
-  "synthesis">,
-  InGroup<DiagGroup<"sycl-host-launcher">>;
-def note_sycl_host_launch_function : Note<
-  "define 'sycl_kernel_launch' function template to fix this problem">;
+def note_sycl_kernel_launch_lookup_here : Note<
+  "in implicit call to 'sycl_kernel_launch' with template argument %0 required"
+  " here">;
+def note_sycl_kernel_launch_overload_resolution_here : Note<
+  "in implicit call to 'sycl_kernel_launch' with template argument %0 and"
+ " function arguments %1 required here">;
 
 def warn_cuda_maxclusterrank_sm_90 : Warning<
   "maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 0470645a9e7ad..b340627425c67 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13159,6 +13159,14 @@ class Sema final : public SemaBase {
 
       /// We are performing partial ordering for template template parameters.
       PartialOrderingTTP,
+
+      /// We are performing name lookup for a function template or variable
+      /// template named 'sycl_kernel_launch'.
+      SYCLKernelLaunchLookup,
+
+      /// We are performing overload resolution for a call to a function
+      /// template or variable template named 'sycl_kernel_launch'.
+      SYCLKernelLaunchOverloadResolution,
     } Kind;
 
     /// Whether we're substituting into constraints.
@@ -13510,6 +13518,22 @@ class Sema final : public SemaBase {
     }
   };
 
+  /// RAII object to ensure that a code synthesis context is popped on scope
+  /// exit.
+  class ScopedCodeSynthesisContext {
+    Sema &S;
+
+  public:
+    ScopedCodeSynthesisContext(Sema &S, const CodeSynthesisContext &Ctx)
+        : S(S) {
+      S.pushCodeSynthesisContext(Ctx);
+    }
+
+    ~ScopedCodeSynthesisContext() {
+      S.popCodeSynthesisContext();
+    }
+  };
+
   /// List of active code synthesis contexts.
   ///
   /// This vector is treated as a stack. As synthesis of one entity requires
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index 3595bbc6c9b9e..fde47443de26f 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -476,6 +476,10 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
       return "TypeAliasTemplateInstantiation";
     case CodeSynthesisContext::PartialOrderingTTP:
       return "PartialOrderingTTP";
+    case CodeSynthesisContext::SYCLKernelLaunchLookup:
+      return "SYCLKernelLaunchLookup";
+    case CodeSynthesisContext::SYCLKernelLaunchOverloadResolution:
+      return "SYCLKernelLaunchOverloadResolution";
     }
     return "";
   }
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d9516114df37a..ce511fa34cb4e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15806,6 +15806,7 @@ Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D,
   if (!Bases.empty())
     OpenMP().ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(Dcl,
                                                                         Bases);
+
   return Dcl;
 }
 
@@ -16241,6 +16242,13 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
     if (!SKEPAttr->isInvalidAttr()) {
       ExprResult LaunchIdExpr =
           SYCL().BuildSYCLKernelLaunchIdExpr(FD, SKEPAttr->getKernelName());
+      if (LaunchIdExpr.isInvalid()) {
+        // Do not mark 'FD' as invalid. Name lookup failure for
+        // 'sycl_kernel_launch' is treated as an error in the definition of
+        // 'FD'; treating it as an error of the declaration would affect
+        // overload resolution.
+      }
+
       getCurFunction()->SYCLKernelLaunchIdExpr = LaunchIdExpr.get();
     }
   }
@@ -16453,20 +16461,31 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation,
     // function or an explicit specialization.
     if (Body && !SKEPAttr->isInvalidAttr()) {
       StmtResult SR;
-      if (FD->isTemplated()) {
+      if (FD->isTemplateInstantiation()) {
+        // The function body should already be a SYCLKernelCallStmt in this
+        // case, but might not be if errors previous occurred.
+        SR = Body;
+      } else if (!getCurFunction()->SYCLKernelLaunchIdExpr) {
+        // If name lookup for a template named sycl_kernel_launch failed
+        // earlier, don't try to build a SYCL kernel call statement as that
+        // would cause additional errors to be issued; just proceed with the
+        // original function body.
+        SR = Body;
+      } else if (FD->isTemplated()) {
         SR = SYCL().BuildUnresolvedSYCLKernelCallStmt(
             cast<CompoundStmt>(Body), getCurFunction()->SYCLKernelLaunchIdExpr);
-      } else if (FD->isTemplateInstantiation()) {
-        assert(isa<SYCLKernelCallStmt>(Body));
-        SR = Body;
       } else {
         SR = SYCL().BuildSYCLKernelCallStmt(
             FD, cast<CompoundStmt>(Body),
             getCurFunction()->SYCLKernelLaunchIdExpr);
       }
-      if (SR.isInvalid())
-        return nullptr;
-      Body = SR.get();
+      // If construction of the replacement body fails, just continue with the
+      // original function body. An early error return here is not valid; the
+      // current declaration context and function scopes must be popped before
+      // returning.
+      if (SR.isUsable()) {
+        Body = SR.get();
+      }
     }
   }
 
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index c3b4c0ae3f336..5430694ce1c0e 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -414,30 +414,34 @@ ExprResult SemaSYCL::BuildSYCLKernelLaunchIdExpr(FunctionDecl *FD,
   IdentifierInfo &SYCLKernelLaunchID =
       Ctx.Idents.get("sycl_kernel_launch", tok::TokenKind::identifier);
 
+  // Establish a code synthesis context for the implicit name lookup of
+  // a template named 'sycl_kernel_launch'. In the event of an error, this
+  // ensures an appropriate diagnostic note is issued to explain why the
+  // lookup was performed.
+  Sema::CodeSynthesisContext CSC;
+  CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchLookup;
+  CSC.Entity = FD;
+  Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
+
   // Perform ordinary name lookup for a function or variable template that
   // accepts a single type template argument.
   LookupResult Result(SemaRef, &SYCLKernelLaunchID, Loc,
                       Sema::LookupOrdinaryName);
   CXXScopeSpec EmptySS;
-  SemaRef.LookupTemplateName(Result, SemaRef.getCurScope(), EmptySS,
-                             /*ObjectType*/ QualType(),
-                             /*EnteringContext*/ false,
-                             Sema::TemplateNameIsRequired);
-
-  if (Result.empty() || Result.isAmbiguous()) {
-    SemaRef.Diag(Loc, SemaRef.getLangOpts().SYCLIsHost
-                          ? diag::err_sycl_host_no_launch_function
-                          : diag::warn_sycl_device_no_host_launch_function);
-    SemaRef.Diag(Loc, diag::note_sycl_host_launch_function);
-
+  if (SemaRef.LookupTemplateName(Result, SemaRef.getCurScope(), EmptySS,
+                                 /*ObjectType*/ QualType(),
+                                 /*EnteringContext*/ false,
+                                 Sema::TemplateNameIsRequired))
+    return ExprError();
+  if (Result.isAmbiguous())
     return ExprError();
-  }
 
   TemplateArgumentListInfo TALI{Loc, Loc};
   TemplateArgument KNTA = TemplateArgument(KNT);
   TemplateArgumentLoc TAL =
       SemaRef.getTrivialTemplateArgumentLoc(KNTA, QualType(), Loc);
   TALI.addArgument(TAL);
+
   ExprResult IdExpr;
   if (SemaRef.isPotentialImplicitMemberAccess(EmptySS, Result,
                                               /*IsAddressOfOperand*/ false))
@@ -510,18 +514,39 @@ StmtResult BuildSYCLKernelLaunchCallStmt(Sema &SemaRef, FunctionDecl *FD,
                                          const SYCLKernelInfo *SKI,
                                          Expr *IdExpr, SourceLocation Loc) {
   SmallVector<Stmt *> Stmts;
-
   // IdExpr may be null if name lookup failed.
   if (IdExpr) {
     llvm::SmallVector<Expr *, 12> Args;
+
+    // Establish a code synthesis context for construction of the arguments
+    // for the implicit call to 'sycl_kernel_launch'.
+    {
+    Sema::CodeSynthesisContext CSC;
+    CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchLookup;
+    CSC.Entity = FD;
+    Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
+
     if (BuildSYCLKernelLaunchCallArgs(SemaRef, FD, SKI, Args, Loc))
       return StmtError();
+    }
+
+    // Establish a code synthesis context for the implicit call to
+    // 'sycl_kernel_launch'.
+    {
+    Sema::CodeSynthesisContext CSC;
+    CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchOverloadResolution;
+    CSC.Entity = FD;
+    CSC.CallArgs = Args.data();
+    CSC.NumCallArgs = Args.size();
+    Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
+
     ExprResult LaunchResult =
         SemaRef.BuildCallExpr(SemaRef.getCurScope(), IdExpr, Loc, Args, Loc);
     if (LaunchResult.isInvalid())
       return StmtError();
 
     Stmts.push_back(SemaRef.MaybeCreateExprWithCleanups(LaunchResult).get());
+    }
   }
 
   return CompoundStmt::Create(SemaRef.getASTContext(), Stmts,
@@ -645,6 +670,8 @@ StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,
   SourceLocation Loc = Body->getLBracLoc();
   StmtResult LaunchResult =
       BuildSYCLKernelLaunchCallStmt(SemaRef, FD, &SKI, LaunchIdExpr, Loc);
+  if (LaunchResult.isInvalid())
+    return StmtError();
 
   Stmt *NewBody =
       new (getASTContext()) SYCLKernelCallStmt(Body, LaunchResult.get(), OFD);
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 35205f40cbcef..e00a5b9bd4aec 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -10,7 +10,6 @@
 //===----------------------------------------------------------------------===/
 
 #include "TreeTransform.h"
-#include "clang/AST/ASTConcept.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTLambda.h"
@@ -593,6 +592,8 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
   case BuildingDeductionGuides:
   case TypeAliasTemplateInstantiation:
   case PartialOrderingTTP:
+  case SYCLKernelLaunchLookup:
+  case SYCLKernelLaunchOverloadResolution:
     return false;
 
   // This function should never be called when Kind's value is Memoization.
@@ -898,6 +899,25 @@ static std::string convertCallArgsToString(Sema &S,
   return Result;
 }
 
+static std::string
+printCallArgsValueCategoryAndType(Sema &S, llvm::ArrayRef<const Expr *> Args) {
+  std::string Result;
+  llvm::raw_string_ostream OS(Result);
+  llvm::ListSeparator Comma;
+  OS << "(";
+  for (const Expr *Arg : Args) {
+    ExprValueKind EVK = Arg->getValueKind();
+    const char *ValueCategory =
+        (EVK == VK_LValue ? "lvalue"
+                          : (EVK == VK_XValue ? "xvalue" : "prvalue"));
+    OS << Comma << ValueCategory << " of type '";
+    Arg->getType().print(OS, S.getPrintingPolicy());
+    OS << "'";
+  }
+  OS << ")";
+  return Result;
+}
+
 void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) {
   // Determine which template instantiations to skip, if any.
   unsigned SkipStart = CodeSynthesisContexts.size(), SkipEnd = SkipStart;
@@ -1260,6 +1280,31 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) {
                                << /*isTemplateTemplateParam=*/true
                                << Active->InstantiationRange);
       break;
+    case CodeSynthesisContext::SYCLKernelLaunchLookup: {
+      const auto *SKEPAttr =
+          Active->Entity->getAttr<SYCLKernelEntryPointAttr>();
+      assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");
+      assert(!SKEPAttr->isInvalidAttr() &&
+             "sycl_kernel_entry_point attribute is invalid");
+      DiagFunc(SKEPAttr->getLocation(),
+               PDiag(diag::note_sycl_kernel_launch_lookup_here)
+                   << SKEPAttr->getKernelName());
+      break;
+    }
+    case CodeSynthesisContext::SYCLKernelLaunchOverloadResolution: {
+      const auto *SKEPAttr =
+          Active->Entity->getAttr<SYCLKernelEntryPointAttr>();
+      assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");
+      assert(!SKEPAttr->isInvalidAttr() &&
+             "sycl_kernel_entry_point attribute is invalid");
+      DiagFunc(SKEPAttr->getLocation(),
+               PDiag(diag::note_sycl_kernel_launch_overload_resolution_here)
+                   << SKEPAttr->getKernelName()
+                   << printCallArgsValueCategoryAndType(
+                          *this, llvm::ArrayRef(Active->CallArgs,
+                                                Active->NumCallArgs)));
+      break;
+    }
     }
   }
 }
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 07cfd4e734e26..982cfe0b03c54 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12985,6 +12985,8 @@ StmtResult TreeTransform<Derived>::TransformUnresolvedSYCLKernelCallStmt(
   StmtResult SR = SemaRef.SYCL().BuildSYCLKernelCallStmt(
       cast<FunctionDecl>(SemaRef.CurContext), cast<CompoundStmt>(Body.get()),
       IdExpr.get());
+  if (SR.isInvalid())
+    return StmtError();
 
   return SR;
 }
diff --git a/clang/test/CodeGenSYCL/function-attrs.cpp b/clang/test/CodeGenSYCL/function-attrs.cpp
index 81f893644bc7c..14195fa75072c 100644
--- a/clang/test/CodeGenSYCL/function-attrs.cpp
+++ b/clang/test/CodeGenSYCL/function-attrs.cpp
@@ -28,6 +28,9 @@ int foo() {
   return 1;
 }
 
+template <typename Name, typename... Ts>
+void sycl_kernel_launch(Ts...) {}
+
 template <typename Name, typename Func>
 [[clang::sycl_kernel_entry_point(Name)]] void kernel_single_task(const Func &kernelFunc) {
   kernelFunc();
diff --git a/clang/test/SemaSYCL/sycl-kernel-launch-ms-compat.cpp b/clang/test/SemaSYCL/sycl-kernel-launch-ms-compat.cpp
new file mode 100644
index 0000000000000..8125ec53fffed
--- /dev/null
+++ b/clang/test/SemaSYCL/sycl-kernel-launch-ms-compat.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -std=c++20 -fsyntax-only -fsycl-is-host -fms-compatibility -fcxx-exceptions -verify=host,expected %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -std=c++20 -fsyntax-only -fsycl-is-device -fms-compatibility -verify=device,expected %s
+
+// Test Microsoft extensions for lookup of a sycl_kernel_launch member template
+// in a dependent base class.
+
+////////////////////////////////////////////////////////////////////////////////
+// Valid declarations.
+////////////////////////////////////////////////////////////////////////////////
+
+// A unique kernel name type is required for each declared kernel entry point.
+template<int> struct KN;
+
+// A generic kernel object type.
+template<int>
+struct KT {
+  void operator()() const;
+};
+
+
+namespace ok1 {
+  template<typename Derived>
+  struct base_handler {
+  protected:
+    // expected-note at +2 {{must qualify identifier to find this declaration in dependent base class}}
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(const char *, Ts...);
+  };
+  template<int N>
+  struct handler : protected base_handler<handler<N>> {
+    // A warning is issued because, in standard C++, unqualified lookup for
+    // sycl_kernel_launch would not consider dependent base classes. Such
+    // lookups are allowed as a Microsoft compatible extension.
+    // expected-warning at +3 {{use of member 'sycl_kernel_launch' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+    // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'KN<1>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'KT<1>') required here}}
+    [[clang::sycl_kernel_entry_point(KN<1>)]]
+    void skep(KT<1> k) {
+      k();
+    }
+  };
+  // expected-note at +1 {{in instantiation of member function 'ok1::handler<1>::skep' requested here}}
+  template void handler<1>::skep(KT<1>);
+}
diff --git a/clang/test/SemaSYCL/sycl-kernel-launch.cpp b/clang/test/SemaSYCL/sycl-kernel-launch.cpp
index f69de9ab245bf..bf1d786e8be26 100644
--- a/clang/test/SemaSYCL/sycl-kernel-launch.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-launch.cpp
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify=device,expected %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify=device,expected %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify=host,expected %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-device -verify=device,expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-host -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-device -verify %s
 
 // Test overload resolution for implicit calls to sycl_kernel_launch<KN>(...)
 // synthesized for functions declared with the sycl_kernel_entry_point
@@ -204,6 +204,21 @@ namespace ok12 {
   }
 }
 
+// ADL for sycl_kernel_launch.
+namespace ok13 {
+  template<typename KN, typename KT, typename T>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep(KT k, T t) {
+    k();
+  }
+  namespace nested {
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(const char *, Ts...);
+    struct S13 {};
+  }
+  template void skep<KN<13>>(KT<13>, nested::S13);
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // Invalid declarations.
@@ -218,110 +233,149 @@ struct BADKT {
   void operator()() const;
 };
 
-// Undeclared sycl_kernel_launch identifier.
+// Undeclared sycl_kernel_launch identifier from non-template function.
 namespace bad1 {
-  // host-error at +5 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-  // device-warning at +4 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-  // expected-note at +3 {{define 'sycl_kernel_launch' function template to fix this problem}}
+  // expected-error at +3 {{use of undeclared identifier 'sycl_kernel_launch'}}
+  // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<1>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<1>') required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<1>)]]
+  void skep(BADKT<1> k) {
+    k();
+  }
+}
+
+// Undeclared sycl_kernel_launch identifier from function template.
+namespace bad2 {
+  // expected-error at +4 {{use of undeclared identifier 'sycl_kernel_launch'}}
+  // expected-note-re at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<2>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<2>') required here}}
   template<typename KN, typename KT>
   [[clang::sycl_kernel_entry_point(KN)]]
   void skep(KT k) {
     k();
   }
-  template void skep<BADKN<1>>(BADKT<1>);
+  // expected-note at +1 {{in instantiation of function template specialization 'bad2::skep<BADKN<2>, BADKT<2>>' requested here}}
+  template void skep<BADKN<2>>(BADKT<2>);
 }
 
 // No matching function for call to sycl_kernel_launch; not a template.
-namespace bad2 {
+namespace bad3 {
   // expected-note at +1 {{declared as a non-template here}}
-  void sycl_kernel_launch(const char *, BADKT<2>);
-  // expected-error at +5 {{'sycl_kernel_launch' does not refer to a template}}
-  // host-error at +4 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-  // device-warning at +3 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-  // expected-note at +2 {{define 'sycl_kernel_launch' function template to fix this problem}}
-  [[clang::sycl_kernel_entry_point(BADKN<2>)]]
-  void skep(BADKT<2> k) {
+  void sycl_kernel_launch(const char *, BADKT<3>);
+  // expected-error at +3 {{'sycl_kernel_launch' does not refer to a template}}
+  // expected-note at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<3>' required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<3>)]]
+  void skep(BADKT<3> k) {
     k();
   }
 }
 
 // No matching function for call to sycl_kernel_launch; not enough arguments.
-namespace bad3 {
+namespace bad4 {
   // expected-note at +2 {{candidate function template not viable: requires 2 arguments, but 1 was provided}}
   template<typename KN, typename KT>
   void sycl_kernel_launch(const char *, KT);
-  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-error at +4 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-note-re at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<4>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]') required here}}
   template<typename KN>
   [[clang::sycl_kernel_entry_point(KN)]]
   void skep() {}
-  // expected-note at +1 {{in instantiation of function template specialization 'bad3::skep<BADKN<3>>' requested here}}
-  template void skep<BADKN<3>>();
+  // expected-note at +1 {{in instantiation of function template specialization 'bad4::skep<BADKN<4>>' requested here}}
+  template void skep<BADKN<4>>();
 }
 
 // No matching function for call to sycl_kernel_launch; too many arguments.
-namespace bad4 {
+namespace bad5 {
   // expected-note at +2 {{candidate function template not viable: requires 2 arguments, but 3 were provided}}
   template<typename KN, typename KT>
   void sycl_kernel_launch(const char *, KT);
-  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-error at +4 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-note-re at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<5>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<5>', xvalue of type 'int') required here}}
   template<typename KN, typename KT>
   [[clang::sycl_kernel_entry_point(KN)]]
   void skep(KT k, int i) {
     k();
   }
-  // expected-note at +1 {{in instantiation of function template specialization 'bad4::skep<BADKN<4>, BADKT<4>>' requested here}}
-  template void skep<BADKN<4>>(BADKT<4>, int);
+  // expected-note at +1 {{in instantiation of function template specialization 'bad5::skep<BADKN<5>, BADKT<5>>' requested here}}
+  template void skep<BADKN<5>>(BADKT<5>, int);
 }
 
 // No matching function for call to sycl_kernel_launch; mismatched function parameter type.
-namespace bad5 {
-  // expected-note at +2 {{candidate function template not viable: no known conversion from 'const char[21]' to 'int' for 1st argument}}
+namespace bad6 {
+  // expected-note-re at +2 {{candidate function template not viable: no known conversion from 'const char[{{[0-9]*}}]' to 'int' for 1st argument}}
   template<typename KN, typename... Ts>
   void sycl_kernel_launch(int, Ts...);
-  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-error at +4 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-note-re at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<6>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<6>') required here}}
   template<typename KN, typename KT>
   [[clang::sycl_kernel_entry_point(KN)]]
   void skep(KT k) {
     k();
   }
-  // expected-note at +1 {{in instantiation of function template specialization 'bad5::skep<BADKN<5>, BADKT<5>>' requested here}}
-  template void skep<BADKN<5>>(BADKT<5>);
+  // expected-note at +1 {{in instantiation of function template specialization 'bad6::skep<BADKN<6>, BADKT<6>>' requested here}}
+  template void skep<BADKN<6>>(BADKT<6>);
 }
 
 // No matching function for call to sycl_kernel_launch; mismatched template parameter kind.
-namespace bad6 {
+namespace bad7 {
   // expected-note at +2 {{candidate template ignored: invalid explicitly-specified argument for 1st template parameter}}
   template<int, typename... Ts>
   void sycl_kernel_launch(const char *, Ts...);
-  // expected-error at +2 {{no matching function for call to 'sycl_kernel_launch'}}
-  [[clang::sycl_kernel_entry_point(BADKN<6>)]]
-  void skep(BADKT<6> k) {
+  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<7>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<7>') required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<7>)]]
+  void skep(BADKT<7> k) {
+    k();
+  }
+}
+
+// No matching function for call to sycl_kernel_launch; substitution failure.
+namespace bad8 {
+  // expected-note at +2 {{candidate template ignored: substitution failure [with KN = BADKN<8>, KT = BADKT<8>]: no type named 'no_such_type' in 'BADKT<8>'}}
+  template<typename KN, typename KT, typename T = typename KT::no_such_type>
+  void sycl_kernel_launch(const char *, KT);
+  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<8>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<8>') required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<8>)]]
+  void skep(BADKT<8> k) {
+    k();
+  }
+}
+
+// No matching function for call to sycl_kernel_launch; deduction failure.
+namespace bad9 {
+  // expected-note at +2 {{candidate template ignored: couldn't infer template argument 'T'}}
+  template<typename KN, typename KT, typename T>
+  void sycl_kernel_launch(const char *, KT);
+  // expected-error at +3 {{no matching function for call to 'sycl_kernel_launch'}}
+  // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<9>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<9>') required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<9>)]]
+  void skep(BADKT<9> k) {
     k();
   }
 }
 
 // No matching function for call to sycl_kernel_launch object; mismatched function parameter type.
-namespace bad7 {
+namespace bad10 {
   template<typename KN>
   struct launcher {
-    // expected-note at +2 {{candidate function template not viable: no known conversion from 'const char[21]' to 'int' for 1st argument}}
+    // expected-note-re at +2 {{candidate function template not viable: no known conversion from 'const char[{{[0-9]*}}]' to 'int' for 1st argument}}
     template<typename... Ts>
     void operator()(int, Ts...);
   };
   template<typename KN>
   launcher<KN> sycl_kernel_launch;
-  // expected-error at +3 {{no matching function for call to object of type 'launcher<BADKN<7, 0>>'}}
+  // expected-error at +4 {{no matching function for call to object of type 'launcher<BADKN<10, 0>>'}}
+  // expected-note-re at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<10>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<10>') required here}}
   template<typename KN, typename KT>
   [[clang::sycl_kernel_entry_point(KN)]]
   void skep(KT k) {
     k();
   }
-  // expected-note at +1 {{in instantiation of function template specialization 'bad7::skep<BADKN<7>, BADKT<7>>' requested here}}
-  template void skep<BADKN<7>>(BADKT<7>);
+  // expected-note at +1 {{in instantiation of function template specialization 'bad10::skep<BADKN<10>, BADKT<10>>' requested here}}
+  template void skep<BADKN<10>>(BADKT<10>);
 }
 
 // No matching function for call to sycl_kernel_launch object; mismatched template parameter kind.
-namespace bad8 {
+namespace bad11 {
   template<int KN>
   struct launcher {
     template<typename... Ts>
@@ -330,17 +384,18 @@ namespace bad8 {
   // expected-note at +1 {{template parameter is declared here}}
   template<int KN>
   launcher<KN> sycl_kernel_launch;
-  // expected-error at +3 {{template argument for non-type template parameter must be an expression}}
+  // expected-error at +4 {{template argument for non-type template parameter must be an expression}}
+  // expected-note at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'KN' required here}}
   template<typename KN, typename KT>
   [[clang::sycl_kernel_entry_point(KN)]]
   void skep(KT k) {
     k();
   }
-  template void skep<BADKN<8>>(BADKT<8>);
+  template void skep<BADKN<11>>(BADKT<11>);
 }
 
 // sycl_kernel_launch as variable template with private call operator template.
-namespace bad9 {
+namespace bad12 {
   template<typename KN>
   struct launcher {
   private:
@@ -350,17 +405,18 @@ namespace bad9 {
   };
   template<typename KN>
   launcher<KN> sycl_kernel_launch;
-  // expected-error at +2 {{'operator()' is a private member of 'bad9::launcher<BADKN<9>>'}}
-  [[clang::sycl_kernel_entry_point(BADKN<9>)]]
-  void skep(BADKT<9> k) {
+  // expected-error at +3 {{'operator()' is a private member of 'bad12::launcher<BADKN<12>>'}}
+  // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<12>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<12>') required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<12>)]]
+  void skep(BADKT<12> k) {
     k();
   }
 }
 
 // Ambiguous reference to sycl_kernel_launch.
-namespace bad10 {
+namespace bad13 {
   inline namespace in1 {
-    // expected-note at +2 {{candidate found by name lookup is 'bad10::in1::sycl_kernel_launch'}}
+    // expected-note at +2 {{candidate found by name lookup is 'bad13::in1::sycl_kernel_launch'}}
     template<typename KN, typename... Ts>
     void sycl_kernel_launch(const char *, Ts...);
   }
@@ -370,45 +426,45 @@ namespace bad10 {
       template<typename KT, typename... Ts>
       void operator()(const char *, Ts...);
     };
-    // expected-note at +2 {{candidate found by name lookup is 'bad10::in2::sycl_kernel_launch'}}
+    // expected-note at +2 {{candidate found by name lookup is 'bad13::in2::sycl_kernel_launch'}}
     template<typename KN>
     launcher<KN> sycl_kernel_launch;
   }
-  // expected-error at +6 {{reference to 'sycl_kernel_launch' is ambiguous}}
-  // host-error at +5 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-  // device-warning at +4 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-  // expected-note at +3 {{define 'sycl_kernel_launch' function template to fix this problem}}
+  // expected-error at +4 {{reference to 'sycl_kernel_launch' is ambiguous}}
+  // expected-note at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'KN' required here}}
   template<typename KN, typename KT>
   [[clang::sycl_kernel_entry_point(KN)]]
   void skep(KT k) {
     k();
   }
-  template void skep<BADKN<10>>(BADKT<10>);
+  template void skep<BADKN<13>>(BADKT<13>);
 }
 
 // Ambiguous call to sycl_kernel_launch.
-namespace bad11 {
-  // expected-note at +2 {{candidate function [with KN = BADKN<11>, KT = BADKT<11>]}}
+namespace bad14 {
+  // expected-note at +2 {{candidate function [with KN = BADKN<14>, KT = BADKT<14>]}}
   template<typename KN, typename KT>
   void sycl_kernel_launch(const char *, KT, signed char);
-  // expected-note at +2 {{candidate function [with KN = BADKN<11>, KT = BADKT<11>]}}
+  // expected-note at +2 {{candidate function [with KN = BADKN<14>, KT = BADKT<14>]}}
   template<typename KN, typename KT>
   void sycl_kernel_launch(const char *, KT, unsigned char);
-  // expected-error at +2 {{call to 'sycl_kernel_launch' is ambiguous}}
-  [[clang::sycl_kernel_entry_point(BADKN<11>)]]
-  void skep(BADKT<11> k, int i) {
+  // expected-error at +3 {{call to 'sycl_kernel_launch' is ambiguous}}
+  // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<14>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<14>', xvalue of type 'int') required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<14>)]]
+  void skep(BADKT<14> k, int i) {
     k();
   }
 }
 
 // Call to member sycl_kernel_launch from non-static member.
-namespace bad12 {
+namespace bad15 {
   struct S {
     template<typename KN, typename... Ts>
     void sycl_kernel_launch(const char *, Ts...);
-    // expected-error at +2 {{call to non-static member function without an object argument}}
-    [[clang::sycl_kernel_entry_point(BADKN<12>)]]
-    static void skep(BADKT<12> k) {
+    // expected-error at +3 {{call to non-static member function without an object argument}}
+    // expected-note at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<15>' required here}}
+    [[clang::sycl_kernel_entry_point(BADKN<15>)]]
+    static void skep(BADKT<15> k) {
       k();
     }
   };
@@ -416,10 +472,11 @@ namespace bad12 {
 
 // sycl_kernel_launch as dependent base class non-static member function
 // template.
-namespace bad13 {
+namespace bad16 {
   template<typename Derived>
   struct base_handler {
   protected:
+    // expected-note at +2 {{member is declared here}}
     template<typename KN, typename... Ts>
     void sycl_kernel_launch(const char *, Ts...);
   };
@@ -427,19 +484,19 @@ namespace bad13 {
   struct handler : protected base_handler<handler<N>> {
     // Lookup for sycl_kernel_launch fails because lookup in dependent base
     // classes requires explicit qualification.
-    // host-error at +4 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-    // device-warning at +3 {{unable to find suitable 'sycl_kernel_launch' function for host code synthesis}}
-    // expected-note at +2 {{define 'sycl_kernel_launch' function template to fix this problem}}
-    [[clang::sycl_kernel_entry_point(BADKN<13>)]]
-    void skep(BADKT<13> k) {
+    // expected-error at +3 {{explicit qualification required to use member 'sycl_kernel_launch' from dependent base class}}
+    // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<16>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<16>') required here}}
+    [[clang::sycl_kernel_entry_point(BADKN<16>)]]
+    void skep(BADKT<16> k) {
       k();
     }
   };
-  template void handler<13>::skep(BADKT<13>);
+  // expected-note at +1 {{in instantiation of member function 'bad16::handler<16>::skep' requested here}}
+  template void handler<16>::skep(BADKT<16>);
 }
 
 // sycl_kernel_launch with non-reference parameters and non-moveable arguments.
-namespace bad14 {
+namespace bad17 {
   // expected-note at +2 2 {{passing argument to parameter here}}
   template<typename KN, typename... Ts>
   void sycl_kernel_launch(const char *, Ts...);
@@ -447,18 +504,36 @@ namespace bad14 {
     // expected-note at +1 {{'non_copyable' has been explicitly marked deleted here}}
     non_copyable(const non_copyable&) = delete;
   };
-  // expected-error at +2 {{call to deleted constructor of 'bad14::non_copyable'}}
-  [[clang::sycl_kernel_entry_point(BADKN<14,0>)]]
-  void skep(BADKT<14,0> k, non_copyable) {
+  // expected-error at +3 {{call to deleted constructor of 'bad17::non_copyable'}}
+  // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<17, 0>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<17, 0>', xvalue of type 'non_copyable') required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<17,0>)]]
+  void skep(BADKT<17,0> k, non_copyable) {
     k();
   }
   struct non_moveable {
     // expected-note at +1 {{'non_moveable' has been explicitly marked deleted here}}
     non_moveable(non_moveable&&) = delete;
   };
-  // expected-error at +2 {{call to deleted constructor of 'bad14::non_moveable'}}
-  [[clang::sycl_kernel_entry_point(BADKN<14,1>)]]
-  void skep(BADKT<14,1> k, non_moveable) {
+  // expected-error at +3 {{call to deleted constructor of 'bad17::non_moveable'}}
+  // expected-note-re at +1 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<17, 1>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<17, 1>', xvalue of type 'non_moveable') required here}}
+  [[clang::sycl_kernel_entry_point(BADKN<17,1>)]]
+  void skep(BADKT<17,1> k, non_moveable) {
     k();
   }
 }
+
+// sycl_kernel_launch declared after use and not found by ADL.
+namespace bad18 {
+  // expected-error at +4 {{call to function 'sycl_kernel_launch' that is neither visible in the template definition nor found by argument-dependent lookup}}
+  // expected-note-re at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<18>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<18>') required here}}
+  template<typename KN, typename KT>
+  [[clang::sycl_kernel_entry_point(KN)]]
+  void skep(KT k) {
+    k();
+  }
+  // expected-note at +2 {{'sycl_kernel_launch' should be declared prior to the call site or in the global namespace}}
+  template<typename KN, typename... Ts>
+  void sycl_kernel_launch(Ts...) {}
+  // expected-note at +1 {{in instantiation of function template specialization 'bad18::skep<BADKN<18>, BADKT<18>>' requested here}}
+  template void skep<BADKN<18>>(BADKT<18>);
+}

>From fa42860acabf0823805e76000ed53096d2871276 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Mon, 17 Nov 2025 20:57:43 -0800
Subject: [PATCH 14/17] Make clang-format happy again.

---
 clang/include/clang/Sema/Sema.h |  4 +---
 clang/lib/Sema/SemaSYCL.cpp     | 38 ++++++++++++++++-----------------
 2 files changed, 20 insertions(+), 22 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b340627425c67..5b2ea0b39b55f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13529,9 +13529,7 @@ class Sema final : public SemaBase {
       S.pushCodeSynthesisContext(Ctx);
     }
 
-    ~ScopedCodeSynthesisContext() {
-      S.popCodeSynthesisContext();
-    }
+    ~ScopedCodeSynthesisContext() { S.popCodeSynthesisContext(); }
   };
 
   /// List of active code synthesis contexts.
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 5430694ce1c0e..95a0bff66b31b 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -521,31 +521,31 @@ StmtResult BuildSYCLKernelLaunchCallStmt(Sema &SemaRef, FunctionDecl *FD,
     // Establish a code synthesis context for construction of the arguments
     // for the implicit call to 'sycl_kernel_launch'.
     {
-    Sema::CodeSynthesisContext CSC;
-    CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchLookup;
-    CSC.Entity = FD;
-    Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
+      Sema::CodeSynthesisContext CSC;
+      CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchLookup;
+      CSC.Entity = FD;
+      Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
 
-    if (BuildSYCLKernelLaunchCallArgs(SemaRef, FD, SKI, Args, Loc))
-      return StmtError();
+      if (BuildSYCLKernelLaunchCallArgs(SemaRef, FD, SKI, Args, Loc))
+        return StmtError();
     }
 
     // Establish a code synthesis context for the implicit call to
     // 'sycl_kernel_launch'.
     {
-    Sema::CodeSynthesisContext CSC;
-    CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchOverloadResolution;
-    CSC.Entity = FD;
-    CSC.CallArgs = Args.data();
-    CSC.NumCallArgs = Args.size();
-    Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
-
-    ExprResult LaunchResult =
-        SemaRef.BuildCallExpr(SemaRef.getCurScope(), IdExpr, Loc, Args, Loc);
-    if (LaunchResult.isInvalid())
-      return StmtError();
-
-    Stmts.push_back(SemaRef.MaybeCreateExprWithCleanups(LaunchResult).get());
+      Sema::CodeSynthesisContext CSC;
+      CSC.Kind = Sema::CodeSynthesisContext::SYCLKernelLaunchOverloadResolution;
+      CSC.Entity = FD;
+      CSC.CallArgs = Args.data();
+      CSC.NumCallArgs = Args.size();
+      Sema::ScopedCodeSynthesisContext ScopedCSC(SemaRef, CSC);
+
+      ExprResult LaunchResult =
+          SemaRef.BuildCallExpr(SemaRef.getCurScope(), IdExpr, Loc, Args, Loc);
+      if (LaunchResult.isInvalid())
+        return StmtError();
+
+      Stmts.push_back(SemaRef.MaybeCreateExprWithCleanups(LaunchResult).get());
     }
   }
 

>From f89a6af173b9d6e4cd64f7e34ac2f3fbd60cb5ca Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Thu, 20 Nov 2025 21:51:43 -0800
Subject: [PATCH 15/17] Reworded much of the sycl_kernel_entry_point attribute
 documentation.

Prompted by code review, this rewording is intended to clarify the
prose and better tie it to the example code. Mention of kernel
argument decomposition and reconstruction is removed since such support
is not yet implemented. Additional changes include use of identifiers
that match the SYCL specification for consistency, avoidance of
duplication, a change of std::forward<>() to std::move(), and oher
misc edits.
---
 clang/include/clang/Basic/AttrDocs.td | 190 ++++++++++++--------------
 1 file changed, 87 insertions(+), 103 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b95426bf76d32..9664387b97e10 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -540,11 +540,11 @@ SYCL kernel invocation functions like the ``single_task`` and ``parallel_for``
 member functions of the ``sycl::handler`` class specified in section 4.9.4,
 "Command group ``handler`` class", of the SYCL 2020 specification.
 
-The attribute requires a single type argument that specifies a class type that
-meets the requirements for a SYCL kernel name as described in section 5.2,
-"Naming of kernels", of the SYCL 2020 specification. A unique kernel name type
-is required for each function declared with the attribute. The attribute may
-not first appear on a declaration that follows a definition of the function.
+The attribute requires a single type argument that meets the requirements for
+a SYCL kernel name as described in section 5.2, "Naming of kernels", of the
+SYCL 2020 specification. A unique kernel name type is required for each
+function declared with the attribute. The attribute may not first appear on a
+declaration that follows a definition of the function.
 
 The attribute only appertains to functions and only those that meet the
 following requirements.
@@ -566,34 +566,42 @@ follows.
 
   namespace sycl {
   class handler {
-    template<typename KernelNameType, typename... Ts>
-    void sycl_kernel_launch(const char *KernelName, Ts...) {
-      // Call functions appropriate for the desired offload backend
-      // (OpenCL, CUDA, HIP, Level Zero, etc...) to enqueue kernel invocation.
+    template<typename KernelName, typename... Ts>
+    void sycl_kernel_launch(const char* kernelSymbol, Ts&&... kernelArgs) {
+      // This code will run on the host and is responsible for calling functions
+      // appropriate for the desired offload backend (OpenCL, CUDA, HIP,
+      // Level Zero, etc...) to copy the kernel arguments denoted by kernelArgs
+      // to a device and to schedule an invocation of the offload kernel entry
+      // point denoted by kernelSymbol with the copied arguments.
     }
 
-    template<typename KernelNameType, typename KernelType>
-    [[ clang::sycl_kernel_entry_point(KernelNameType) ]]
-    void kernel_entry_point(KernelType Kernel) {
-      Kernel();
+    template<typename KernelName, typename KernelType>
+    [[ clang::sycl_kernel_entry_point(KernelName) ]]
+    void kernel_entry_point(KernelType kernelFunc) {
+      // This code will run on the device. The call to kernelFunc() invokes
+      // the SYCL kernel.
+      kernelFunc();
     }
 
   public:
-    template<typename KernelNameType, typename KernelType>
-    void single_task(KernelType Kernel) {
-      // Call kernel_entry_point() to launch the kernel and to trigger
-      // generation of an offload kernel entry point.
-      kernel_entry_point<KernelNameType>(Kernel);
+    template<typename KernelName, typename KernelType>
+    void single_task(const KernelType& kernelFunc) {
+      // This code will run on the host. kernel_entry_point() is called to
+      // trigger generation of an offload kernel entry point and to schedule
+      // an invocation of it on a device with kernelFunc (a SYCL kernel object)
+      // passed as a kernel argument. This call will result in an implicit call
+      // to sycl_kernel_launch() with the symbol name for the generated offload
+      // kernel entry point passed as the first function argument followed by
+      // kernelFunc.
+      kernel_entry_point<KernelName>(kernelFunc);
     }
   };
   } // namespace sycl
 
 A SYCL kernel object is a callable object of class type that is constructed on
 a host, often via a lambda expression, and then passed to a SYCL kernel
-invocation function to be executed on an offload device. A SYCL kernel
-invocation function is responsible for copying the provided SYCL kernel object
-to an offload device and initiating a call to it. The SYCL kernel object and
-its data members constitute the parameters of an offload kernel.
+invocation function to be executed on an offload device. The ``kernelFunc``
+parameters in the example code above correspond to SYCL kernel objects.
 
 A SYCL kernel object type is required to satisfy the device copyability
 requirements specified in section 3.13.1, "Device copyable", of the SYCL 2020
@@ -601,46 +609,39 @@ specification. Additionally, any data members of the kernel object type are
 required to satisfy section 4.12.4, "Rules for parameter passing to kernels".
 For most types, these rules require that the type is trivially copyable.
 However, the SYCL specification mandates that certain special SYCL types, such
-as ``sycl::accessor`` and ``sycl::stream`` be device copyable even if they are
+as ``sycl::accessor`` and ``sycl::stream``, be device copyable even if they are
 not trivially copyable. These types require special handling because they cannot
-be copied to device memory as if by ``memcpy()``. Additionally, some offload
-backends, OpenCL for example, require objects of some of these types to be
-passed as individual arguments to the offload kernel.
+necessarily be copied to device memory as if by ``memcpy()``.
 
-An offload kernel consists of an entry point function that declares the
-parameters of the offload kernel and the set of all functions and variables that
-are directly or indirectly used by the entry point function.
+The SYCL kernel object and its data members constitute the parameters of an
+offload kernel. An offload kernel consists of an offload entry point function
+and the set of all functions and variables that are directly or indirectly used
+by the entry point function.
 
-A SYCL kernel invocation function invokes a SYCL kernel on a device by
-performing the following tasks (likely with the help of an offload backend
-like OpenCL):
+A SYCL kernel invocation function is responsible for performing the following
+tasks (likely with the help of an offload backend like OpenCL):
 
 #. Identifying the offload kernel entry point to be used for the SYCL kernel.
 
-#. Decomposing the SYCL kernel object, if necessary, to produce the set of
-   offload kernel arguments required by the offload kernel entry point.
+#. Validating that the SYCL kernel object type and its data members meet the
+   SYCL device copyability and kernel parameter requirements noted above.
 
-#. Copying the offload kernel arguments to device memory.
+#. Copying the SYCL kernel object and any other kernel arguments to device
+   memory including any special handling required for SYCL special types.
 
 #. Initiating execution of the offload kernel entry point.
 
 The offload kernel entry point for a SYCL kernel performs the following tasks:
 
-#. Reconstructing the SYCL kernel object, if necessary, using the offload
-   kernel parameters.
-
-#. Calling the ``operator()`` member function of the (reconstructed) SYCL kernel
-   object.
+#. Calling the ``operator()`` member function of the SYCL kernel object.
 
 The ``sycl_kernel_entry_point`` attribute facilitates or automates these tasks
-by generating the offload kernel entry point, generating a unique symbol name
-for it, synthesizing code for kernel argument decomposition and reconstruction,
-and synthesizing a call to a ``sycl_kernel_launch`` function template
-with the kernel name type, kernel symbol name, and (decomposed) kernel arguments
-passed as template or function arguments.
+by providing generation of an offload kernel entry point with a unique symbol
+name, type checking of kernel argument requirements, and initiation of kernel
+execution via synthesized calls to a ``sycl_kernel_launch`` template.
 
 A function declared with the ``sycl_kernel_entry_point`` attribute specifies
-the parameters and body of the offload entry point function. Consider the
+the parameters and body of an offload entry point function. Consider the
 following call to the ``single_task()`` SYCL kernel invocation function assuming
 an implementation similar to the one shown above.
 
@@ -653,81 +654,65 @@ an implementation similar to the one shown above.
     });
   }
 
-The SYCL kernel object is the result of the lambda expression. It has two
-data members corresponding to the captures of ``sout`` and ``s``. Since one
-of these data members corresponds to a special SYCL type that must be passed
-individually as an offload kernel argument, it is necessary to decompose the
-SYCL kernel object into its constituent parts and pass them individually. An
-offload kernel entry point function will be generated that looks approximately
+The SYCL kernel object is the result of the lambda expression. The call to
+``kernel_entry_point()`` via the call to ``single_task()`` triggers the
+generation of an offload kernel entry point function that looks approximately
 as follows.
 
 .. code-block:: c++
 
-  void sycl-kernel-caller-for-KN(sycl::stream sout, S s) {
-    kernel-type Kernel = { sout, s );
-    Kernel();
+  void sycl-kernel-caller-for-KN(kernel-type kernelFunc) {
+    kernelFunc();
   }
 
 There are a few items worthy of note:
 
-#. The name of the generated function incorporates the SYCL kernel name,
-   ``KN``, that was passed as the ``KernelNameType`` template parameter to
-   ``single_task()`` and eventually provided as the argument to the
-   ``sycl_kernel_entry_point`` attribute. There is a one-to-one correspondence
-   between SYCL kernel names and offload kernel entry points.
-
-#. The parameters and the call to ``Kernel()`` correspond to the definition of
-   ``kernel_entry_point()`` called by ``single_task()`` with the SYCL kernel
-   object argument decomposed and reconstructed.
+#.  ``sycl-kernel-caller-for-KN`` is an exposition only name; the actual name
+    generated for an entry point is an implementation detail and subject to
+    change. However, the name will incorporate the SYCL kernel name, ``KN``,
+    that was passed as the ``KernelName`` template parameter to
+    ``single_task()`` and eventually provided as the argument to the
+    ``sycl_kernel_entry_point`` attribute in order to ensure that a unique
+    name is generated for each entry point. There is a one-to-one correspondence
+    between SYCL kernel names and offload kernel entry points.
 
 #. The SYCL kernel is a lambda closure type and therefore has no name;
    ``kernel-type`` is substituted above and corresponds to the ``KernelType``
    template parameter deduced in the call to ``single_task()``.
-   Lambda types cannot be declared and initialized using the aggregate
-   initialization syntax used above, but the intended behavior should be clear.
 
-#. ``S`` is a device copyable type that does not directly or indirectly contain
-   a data member of a SYCL special type. It therefore does not need to be
-   decomposed into its constituent members to be passed as a kernel argument.
+#. The parameter and the call to ``kernelFunc()`` in the function body
+   correspond to the definition of ``kernel_entry_point()`` as called by
+   ``single_task()``.
 
-#. The depiction of the ``sycl::stream`` parameter as a single self contained
-   kernel parameter is an oversimplification. SYCL special types may require
-   additional decomposition such that the generated function might have three
-   or more parameters depending on how the SYCL library implementation defines
-   these types.
+#. The parameter is type checked for conformance with the SYCL device
+   copyability and kernel parameter requirements.
 
-The call to ``kernel_entry_point()`` by ``single_task()`` is effectively
-replaced with synthesized code that looks approximately as follows.
+Within ``single_task()``, the call to ``kernel_entry_point()`` is effectively
+replaced with a synthesized call to a ''sycl_kernel_launch`` template that
+looks approximately as follows.
 
 .. code-block:: c++
 
-  sycl::stream sout = Kernel.sout;
-  S s = Kernel.s;
-  sycl_kernel_launch<KN>("kernel-symbol-name", sout, s);
+  sycl_kernel_launch<KN>("sycl-kernel-caller-for-KN", kernelFunc);
 
 There are a few items worthy of note:
 
-#. The SYCL kernel object is a lambda closure type and its captures do not
-   have formal names and cannot be accessed using the member access syntax used
-   above, but the intended behavior should be clear.
-
-#. ``kernel-symbol-name`` is substituted for the actual symbol name that would
-   be generated; these names are implementation details subject to change.
-
-#. Lookup for the ``sycl_kernel_launch()`` function template is
-   performed from the (possibly instantiated) location of the definition of
-   ``kernel_entry_point()``. If overload resolution fails, the program is
-   ill-formed. If the selected overload is a non-static member function, then
-   ``this`` is passed for the implicit object parameter.
+#. Lookup for the ``sycl_kernel_launch`` template is performed as if from the
+   body of the (possibly instantiated) definition of ``kernel_entry_point()``.
+   If name lookup or overload resolution fails, the program is ill-formed.
+   If the selected overload is a non-static member function, then ``this`` is
+   passed as the implicit object parameter.
 
 #. Function arguments passed to ``sycl_kernel_launch()`` are passed
-   as if by ``std::forward<X>(x)``.
+   as if by ``std::move(x)``.
 
-#. The ``sycl_kernel_launch()`` function is expected to be provided by
-   the SYCL library implementation. It is responsible for scheduling execution
-   of the generated offload kernel entry point identified by
-   ``kernel-symbol-name`` and copying the (decomposed) kernel arguments to
-   device memory, presumably via an offload backend such as OpenCL.
+#. The ``sycl_kernel_launch`` template is expected to be provided by the SYCL
+   library implementation. It is responsible for copying the kernel arguments
+   to device memory  and for scheduling execution of the generated offload
+   kernel entry point identified by the symbol name passed as the first
+   function argument. ``sycl-kernel-caller-for-KN`` is substituted above for
+   the actual symbol name that would be generated for the offload kernel entry
+   point.
 
 It is not necessary for a function declared with the ``sycl_kernel_entry_point``
 attribute to be called for the offload kernel entry point to be emitted. For
@@ -739,12 +724,11 @@ required for the synthesized call to ``sycl_kernel_launch()`` to occur.
 Functions declared with the ``sycl_kernel_entry_point`` attribute are not
 limited to the simple example shown above. They may have additional template
 parameters, declare additional function parameters, and have complex control
-flow in the function body. Function parameter decomposition and reconstruction
-is performed for all function parameters. The function must abide by the
-language feature restrictions described in section 5.4, "Language restrictions
-for device functions" in the SYCL 2020 specification. If the function is a
-non-static member function, ``this`` shall not be used in a potentially
-evaluated expression.
+flow in the function body. The function must abide by the language feature
+restrictions described in section 5.4, "Language restrictions for device
+functions" in the SYCL 2020 specification. If the function is a non-static
+member function, ``this`` shall not be used in a potentially evaluated
+expression.
   }];
 }
 

>From ee8ba91d82571753f980ad46e255b6599ee88859 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Thu, 20 Nov 2025 21:58:36 -0800
Subject: [PATCH 16/17] Added an additional test case to
 sycl-kernel-launch-ms-compat.cpp.

---
 .../SemaSYCL/sycl-kernel-launch-ms-compat.cpp | 43 +++++++++++++++++++
 clang/test/SemaSYCL/sycl-kernel-launch.cpp    |  2 +
 2 files changed, 45 insertions(+)

diff --git a/clang/test/SemaSYCL/sycl-kernel-launch-ms-compat.cpp b/clang/test/SemaSYCL/sycl-kernel-launch-ms-compat.cpp
index 8125ec53fffed..fdcfee3860c76 100644
--- a/clang/test/SemaSYCL/sycl-kernel-launch-ms-compat.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-launch-ms-compat.cpp
@@ -4,6 +4,7 @@
 // Test Microsoft extensions for lookup of a sycl_kernel_launch member template
 // in a dependent base class.
 
+
 ////////////////////////////////////////////////////////////////////////////////
 // Valid declarations.
 ////////////////////////////////////////////////////////////////////////////////
@@ -41,3 +42,45 @@ namespace ok1 {
   // expected-note at +1 {{in instantiation of member function 'ok1::handler<1>::skep' requested here}}
   template void handler<1>::skep(KT<1>);
 }
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Invalid declarations.
+////////////////////////////////////////////////////////////////////////////////
+
+// A unique kernel name type is required for each declared kernel entry point.
+template<int> struct BADKN;
+
+// A generic kernel object type.
+template<int>
+struct BADKT {
+  void operator()() const;
+};
+
+
+namespace bad1 {
+  template<typename Derived>
+  struct base_handler {
+  private:
+    // expected-note at +3 {{must qualify identifier to find this declaration in dependent base class}}
+    // expected-note at +2 {{declared private here}}
+    template<typename KN, typename... Ts>
+    void sycl_kernel_launch(const char *, Ts...);
+  };
+  template<int N>
+  struct handler : protected base_handler<handler<N>> {
+    // In standard C++, unqualified lookup for sycl_kernel_launch would not
+    // consider dependent base classes. Such lookups are allowed as a Microsoft
+    // compatible extension, but access checks are still performed which makes
+    // this case an error.
+    // expected-warning at +4 {{use of member 'sycl_kernel_launch' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+    // expected-note-re at +2 {{in implicit call to 'sycl_kernel_launch' with template argument 'BADKN<1>' and function arguments (lvalue of type 'const char[{{[0-9]*}}]', xvalue of type 'BADKT<1>') required here}}
+    // expected-error at +2 {{'sycl_kernel_launch' is a private member of 'bad1::base_handler<bad1::handler<1>>'}}
+    [[clang::sycl_kernel_entry_point(BADKN<1>)]]
+    void skep(BADKT<1> k) {
+      k();
+    }
+  };
+  // expected-note at +1 {{in instantiation of member function 'bad1::handler<1>::skep' requested here}}
+  template void handler<1>::skep(BADKT<1>);
+}
diff --git a/clang/test/SemaSYCL/sycl-kernel-launch.cpp b/clang/test/SemaSYCL/sycl-kernel-launch.cpp
index bf1d786e8be26..00f61b8c30ccd 100644
--- a/clang/test/SemaSYCL/sycl-kernel-launch.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-launch.cpp
@@ -9,6 +9,7 @@
 // synthesized for functions declared with the sycl_kernel_entry_point
 // attribute.
 
+
 ////////////////////////////////////////////////////////////////////////////////
 // Valid declarations.
 ////////////////////////////////////////////////////////////////////////////////
@@ -233,6 +234,7 @@ struct BADKT {
   void operator()() const;
 };
 
+
 // Undeclared sycl_kernel_launch identifier from non-template function.
 namespace bad1 {
   // expected-error at +3 {{use of undeclared identifier 'sycl_kernel_launch'}}

>From ac929134e07fb4031b9bb403d7dcc14c2527c189 Mon Sep 17 00:00:00 2001
From: Tom Honermann <tom.honermann at intel.com>
Date: Thu, 20 Nov 2025 22:05:00 -0800
Subject: [PATCH 17/17] Fix a style nit noted in code review.

---
 clang/lib/Sema/SemaDecl.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index ce511fa34cb4e..a1c63d495cfc1 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16483,9 +16483,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation,
       // original function body. An early error return here is not valid; the
       // current declaration context and function scopes must be popped before
       // returning.
-      if (SR.isUsable()) {
+      if (SR.isUsable())
         Body = SR.get();
-      }
     }
   }
 



More information about the cfe-commits mailing list