[clang] [CIR][SYCL] Support SYCL kernel call statement in host codegen (PR #213728)
Konstantinos Parasyris via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 3 10:55:24 PDT 2026
https://github.com/koparasy created https://github.com/llvm/llvm-project/pull/213728
Add CIRGen support for lowering SYCLKernelCallStmt during host compilation,
emitting the kernel launch statement in place of the `sycl_kernel_entry_point`
function body (mirroring classic CodeGen).
Device compilation, where the
offload kernel caller entry point is emitted instead, is future work and thus marked as NIY.
>From a81e0c8a8531d8a8e97cf80f0fe11ff4fce8d486 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Mon, 3 Aug 2026 10:37:17 -0700
Subject: [PATCH 1/2] [CIR] Support SYCL kernel call statement in host codegen
---
clang/lib/CIR/CodeGen/CIRGenFunction.h | 6 +++
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 20 +++++++++
clang/lib/CIR/CodeGen/CIRGenSYCL.cpp | 35 +++++++++++++++
clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 4 +-
clang/lib/CIR/CodeGen/CMakeLists.txt | 1 +
.../kernel-call-stmt-device-nyi.cpp | 23 ++++++++++
.../test/CIR/CodeGenSYCL/kernel-call-stmt.cpp | 45 +++++++++++++++++++
7 files changed, 133 insertions(+), 1 deletion(-)
create mode 100644 clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
create mode 100644 clang/test/CIR/CodeGenSYCL/kernel-call-stmt-device-nyi.cpp
create mode 100644 clang/test/CIR/CodeGenSYCL/kernel-call-stmt.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 4ba3ee59f49b0..5c0fac6f0330c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -48,6 +48,10 @@ class LoopOp;
} // namespace acc
} // namespace mlir
+namespace clang {
+class SYCLKernelCallStmt;
+} // namespace clang
+
namespace clang::CIRGen {
struct CGCoroData;
@@ -2267,6 +2271,8 @@ class CIRGenFunction : public CIRGenTypeCache {
bool buildingTopLevelCase);
mlir::LogicalResult emitSwitchStmt(const clang::SwitchStmt &s);
+ mlir::LogicalResult emitSYCLKernelCallStmt(const SYCLKernelCallStmt &s);
+
std::optional<mlir::Value>
emitTargetBuiltinExpr(unsigned builtinID, const clang::CallExpr *e,
ReturnValueSlot &returnValue);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index a7143408c9bee..b475b17cd4816 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -432,6 +432,26 @@ void CIRGenModule::emitDeferred() {
curDeclsToEmit.swap(deferredDeclsToEmit);
for (const GlobalDecl &d : curDeclsToEmit) {
+ // Functions declared with the sycl_kernel_entry_point attribute are
+ // emitted normally during host compilation. During device compilation, a
+ // SYCL kernel caller offload entry point function is generated and emitted
+ // in place of each of these functions.
+ if (const auto *fd = d.getDecl()->getAsFunction()) {
+ if (langOpts.SYCLIsDevice && fd->hasAttr<SYCLKernelEntryPointAttr>() &&
+ fd->isDefined()) {
+ // Functions with an invalid sycl_kernel_entry_point attribute are
+ // ignored during device compilation.
+ if (!fd->getAttr<SYCLKernelEntryPointAttr>()->isInvalidAttr()) {
+ // Generating the SYCL kernel caller offload entry point is not yet
+ // implemented in CIR.
+ errorNYI(fd->getSourceRange(),
+ "SYCL kernel caller offload entry point");
+ }
+ // Do not emit the sycl_kernel_entry_point attributed function.
+ continue;
+ }
+ }
+
emitGlobalDecl(d);
// If we found out that we need to emit more decls, do that recursively.
diff --git a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
new file mode 100644
index 0000000000000..9308b1fe4189f
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
@@ -0,0 +1,35 @@
+//===--------- CIRGenSYCL.cpp - Emit CIR for SYCL kernels -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code required for the generation of SYCL kernel code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+
+#include "clang/AST/StmtSYCL.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+mlir::LogicalResult
+CIRGenFunction::emitSYCLKernelCallStmt(const SYCLKernelCallStmt &s) {
+ // SYCLKernelCallStmt nodes are only present in the bodies of functions
+ // declared with the sycl_kernel_entry_point attribute. ODR-use of such a
+ // function in code emitted during device compilation should be diagnosed.
+ // During device compilation, the offload kernel entry point is emitted in
+ // place of such a function (see CIRGenModule::emitDeferred), so this
+ // function is only reached during host compilation.
+ assert(!getLangOpts().SYCLIsDevice &&
+ "Attempt to emit a SYCL kernel call statement during device "
+ "compilation");
+
+ // During host compilation, the kernel launch statement is emitted in place
+ // of the original function body.
+ return emitStmt(s.getKernelLaunchStmt(), /*useCurrentScope=*/true);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index d34769200dbfd..8e4c5f008c5a0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -20,6 +20,7 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
+#include "clang/AST/StmtSYCL.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/Support/SaveAndRestore.h"
@@ -210,6 +211,8 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
return emitIndirectGotoStmt(cast<IndirectGotoStmt>(*s));
case Stmt::CoreturnStmtClass:
return emitCoreturnStmt(cast<CoreturnStmt>(*s));
+ case Stmt::SYCLKernelCallStmtClass:
+ return emitSYCLKernelCallStmt(cast<SYCLKernelCallStmt>(*s));
case Stmt::OpenACCComputeConstructClass:
return emitOpenACCComputeConstruct(cast<OpenACCComputeConstruct>(*s));
case Stmt::OpenACCLoopConstructClass:
@@ -431,7 +434,6 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
case Stmt::DefaultStmtClass:
case Stmt::CaseStmtClass:
case Stmt::SEHLeaveStmtClass:
- case Stmt::SYCLKernelCallStmtClass:
case Stmt::ObjCAtTryStmtClass:
case Stmt::ObjCAtThrowStmtClass:
case Stmt::ObjCAtSynchronizedStmtClass:
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index 78569b11651e0..1fe9b25dd786c 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -52,6 +52,7 @@ add_clang_library(clangCIR
CIRGenStmtOpenACC.cpp
CIRGenStmtOpenACCLoop.cpp
CIRGenStmtOpenMP.cpp
+ CIRGenSYCL.cpp
CIRGenTypes.cpp
CIRGenVTables.cpp
TargetInfo.cpp
diff --git a/clang/test/CIR/CodeGenSYCL/kernel-call-stmt-device-nyi.cpp b/clang/test/CIR/CodeGenSYCL/kernel-call-stmt-device-nyi.cpp
new file mode 100644
index 0000000000000..c6522064f2fdc
--- /dev/null
+++ b/clang/test/CIR/CodeGenSYCL/kernel-call-stmt-device-nyi.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spir64-unknown-unknown \
+// RUN: -fclangir -emit-cir -verify %s
+
+// During device compilation, a SYCL kernel caller offload entry point is
+// emitted in place of each sycl_kernel_entry_point attributed function. That
+// lowering is not yet implemented in CIR, so it must be reported as a clean
+// "Not Yet Implemented" diagnostic rather than crashing.
+
+// Required by sycl_kernel_entry_point semantics.
+template <typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
+
+template <typename KernelName, typename KernelType>
+[[clang::sycl_kernel_entry_point(KernelName)]]
+// expected-error at +1 {{ClangIR code gen Not Yet Implemented: SYCL kernel caller offload entry point}}
+void kernel_single_task(KernelType kf) { kf(); }
+
+struct KN;
+struct K {
+ void operator()() const {}
+};
+
+void test() { kernel_single_task<KN>(K{}); }
diff --git a/clang/test/CIR/CodeGenSYCL/kernel-call-stmt.cpp b/clang/test/CIR/CodeGenSYCL/kernel-call-stmt.cpp
new file mode 100644
index 0000000000000..3f42bad95dc14
--- /dev/null
+++ b/clang/test/CIR/CodeGenSYCL/kernel-call-stmt.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-host -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-host -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-host -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+// Verify that, during host compilation, the body of a function declared with
+// the sycl_kernel_entry_point attribute is lowered to its kernel launch
+// statement rather than reporting a not-yet-implemented error. The kernel
+// entry point body must be replaced by the launch call; the original kernel
+// functor invocation must not be emitted on the host.
+
+// Required by sycl_kernel_entry_point semantics.
+template <typename KernelName, typename... Ts>
+void sycl_kernel_launch(const char *, Ts...) {}
+
+template <typename KernelName, typename KernelType>
+[[clang::sycl_kernel_entry_point(KernelName)]]
+void kernel_single_task(KernelType kf) { kf(); }
+
+struct KN;
+struct K {
+ void operator()() const {}
+};
+
+void test() { kernel_single_task<KN>(K{}); }
+
+// The kernel entry point body is replaced by a call to the sycl_kernel_launch
+// specialization, and does not invoke the kernel functor's operator() on the
+// host.
+// CIR-LABEL: cir.func {{.*}}@_Z18kernel_single_taskI2KN1KEvT0_
+// CIR-NOT: cir.call @_ZNK1KclEv
+// CIR: cir.call @_Z18sycl_kernel_launchI2KNJ1KEEvPKcDpT0_
+// CIR: cir.return
+
+// LLVM-LABEL: define {{.*}}void @_Z18kernel_single_taskI2KN1KEvT0_
+// LLVM-NOT: call {{.*}}@_ZNK1KclEv
+// LLVM: call void @_Z18sycl_kernel_launchI2KNJ1KEEvPKcDpT0_
+// LLVM: ret void
+
+// OGCG-LABEL: define {{.*}}void @_Z18kernel_single_taskI2KN1KEvT0_
+// OGCG-NOT: call {{.*}}@_ZNK1KclEv
+// OGCG: call void @_Z18sycl_kernel_launchI2KNJ1KEEvPKcDpT0_
+// OGCG: ret void
>From 891b22888d7cec2cb1b4e63c015986b5ab73f08d Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Mon, 3 Aug 2026 10:48:08 -0700
Subject: [PATCH 2/2] Rename file to align with OG SYCL
---
...call-stmt-device-nyi.cpp => kernel-caller-entry-point-nyi.cpp} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename clang/test/CIR/CodeGenSYCL/{kernel-call-stmt-device-nyi.cpp => kernel-caller-entry-point-nyi.cpp} (100%)
diff --git a/clang/test/CIR/CodeGenSYCL/kernel-call-stmt-device-nyi.cpp b/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point-nyi.cpp
similarity index 100%
rename from clang/test/CIR/CodeGenSYCL/kernel-call-stmt-device-nyi.cpp
rename to clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point-nyi.cpp
More information about the cfe-commits
mailing list