[clang] [CIR][SYCL] Device kernel caller (PR #213771)
Konstantinos Parasyris via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 15 08:32:43 PDT 2026
https://github.com/koparasy updated https://github.com/llvm/llvm-project/pull/213771
>From cd92614a8023553deed153e9bcd43c5256ac0bf6 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Mon, 3 Aug 2026 14:33:04 -0700
Subject: [PATCH 1/5] [CIR][SYCL] Emit SYCL kernel caller offload entry point
in device codegen
---
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 9 +-
clang/lib/CIR/CodeGen/CIRGenFunction.h | 8 ++
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 10 +-
clang/lib/CIR/CodeGen/CIRGenModule.h | 5 +
clang/lib/CIR/CodeGen/CIRGenSYCL.cpp | 130 ++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 13 ++
clang/lib/CIR/CodeGen/CIRGenTypes.h | 7 +
clang/lib/CIR/CodeGen/TargetInfo.h | 6 +
clang/lib/CIR/CodeGen/Targets/SPIRV.cpp | 4 +
.../CodeGenSYCL/kernel-caller-entry-point.cpp | 60 ++++++--
10 files changed, 238 insertions(+), 14 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 66e7b6d5061df..5c476ebe668da 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -369,14 +369,17 @@ void CIRGenFunction::LexicalScope::emitImplicitReturn() {
CIRGenBuilderTy &builder = cgf.getBuilder();
LexicalScope *localScope = cgf.curLexScope;
- const auto *fd = cast<clang::FunctionDecl>(cgf.curGD.getDecl());
+ // Synthesized functions (e.g. SYCL kernel caller offload entry points) are
+ // emitted without an associated FunctionDecl. They return void, so the
+ // non-void C++ flow-off-the-end handling below does not apply to them.
+ const auto *fd = dyn_cast_or_null<clang::FunctionDecl>(cgf.curGD.getDecl());
// In C++, flowing off the end of a non-void function is always undefined
// behavior. In C, flowing off the end of a non-void function is undefined
// behavior only if the non-existent return value is used by the caller.
// That influences whether the terminating op is trap, unreachable, or
// return.
- if (cgf.getLangOpts().CPlusPlus && !fd->hasImplicitReturnZero() &&
+ if (fd && cgf.getLangOpts().CPlusPlus && !fd->hasImplicitReturnZero() &&
!cgf.sawAsmBlock && !fd->getReturnType()->isVoidType() &&
builder.getInsertionBlock() &&
!previousOpIsNonYieldingCleanup(builder.getInsertionBlock())) {
@@ -651,7 +654,7 @@ mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
return emitStmt(body, /*useCurrentScope=*/true);
}
-static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) {
+void CIRGenFunction::eraseEmptyAndUnusedBlocks(cir::FuncOp func) {
// Remove any leftover blocks that are unreachable and empty, since they do
// not represent unreachable code useful for warnings nor anything deemed
// useful in general.
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 9f8454309f13a..071b28fc33282 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -49,6 +49,7 @@ class LoopOp;
} // namespace mlir
namespace clang {
+class OutlinedFunctionDecl;
class SYCLKernelCallStmt;
} // namespace clang
@@ -2307,6 +2308,13 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitSYCLKernelCallStmt(const SYCLKernelCallStmt &s);
+ void emitSYCLKernelCaller(const clang::OutlinedFunctionDecl *outlinedFnDecl,
+ cir::FuncOp funcOp, cir::FuncType funcType,
+ FunctionArgList &args);
+
+ /// Remove leftover empty and unreachable blocks from an emitted function.
+ static void eraseEmptyAndUnusedBlocks(cir::FuncOp func);
+
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 ae9cad0b7c30f..4c47b9eb8be3d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -441,9 +441,13 @@ void CIRGenModule::emitDeferred() {
fd->isDefined()) {
// Functions with an invalid sycl_kernel_entry_point attribute are
// ignored during device compilation.
- if (!fd->getAttr<SYCLKernelEntryPointAttr>()->isInvalidAttr())
- errorNYI(fd->getSourceRange(),
- "SYCL kernel caller offload entry point");
+ if (!fd->getAttr<SYCLKernelEntryPointAttr>()->isInvalidAttr()) {
+ // Generate and emit the SYCL kernel caller function.
+ emitSYCLKernelCaller(fd, getASTContext());
+ // Recurse to emit any symbols directly or indirectly referenced
+ // by the SYCL kernel caller function.
+ emitDeferred();
+ }
// Do not emit the sycl_kernel_entry_point attributed function.
continue;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index ba7b20b91cc03..24648605d8719 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -654,6 +654,11 @@ class CIRGenModule : public CIRGenTypeCache {
void emitGlobalDefinition(clang::GlobalDecl gd,
mlir::Operation *op = nullptr);
void emitGlobalFunctionDefinition(clang::GlobalDecl gd, mlir::Operation *op);
+
+ /// Emit the SYCL kernel caller offload entry point function generated for a
+ /// function declared with the sycl_kernel_entry_point attribute.
+ void emitSYCLKernelCaller(const clang::FunctionDecl *kernelEntryPointFn,
+ clang::ASTContext &ctx);
void emitGlobalVarDefinition(const clang::VarDecl *vd,
bool isTentative = false);
diff --git a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
index 9308b1fe4189f..ba694e086ff41 100644
--- a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
@@ -11,8 +11,13 @@
//===----------------------------------------------------------------------===//
#include "CIRGenFunction.h"
+#include "CIRGenModule.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/StmtSYCL.h"
+#include "clang/AST/SYCLKernelInfo.h"
using namespace clang;
using namespace clang::CIRGen;
@@ -33,3 +38,128 @@ CIRGenFunction::emitSYCLKernelCallStmt(const SYCLKernelCallStmt &s) {
// of the original function body.
return emitStmt(s.getKernelLaunchStmt(), /*useCurrentScope=*/true);
}
+
+// Emit the body of a SYCL kernel caller offload entry point function. The body
+// is the transformed body held by the OutlinedFunctionDecl associated with the
+// sycl_kernel_entry_point attributed function. This mirrors the tail of
+// CIRGenFunction::generateCode, but is driven by an OutlinedFunctionDecl and an
+// explicit argument list rather than a FunctionDecl.
+void CIRGenFunction::emitSYCLKernelCaller(
+ const OutlinedFunctionDecl *outlinedFnDecl, cir::FuncOp funcOp,
+ cir::FuncType funcType, FunctionArgList &args) {
+ const Stmt *body = outlinedFnDecl->getBody();
+ SourceLocation loc = outlinedFnDecl->getLocation();
+ SourceRange bodyRange = body->getSourceRange();
+
+ // The offload entry point is synthesized and has no FunctionDecl of its own.
+ // As in classic CodeGen's EmitSYCLKernelCaller, it is emitted with an empty
+ // GlobalDecl: it is a free function (never an implicit-object member) and
+ // must not run a C++ instance-function prologue. LexicalScope's implicit
+ // return handles a null curGD (the entry point returns void).
+ curGD = GlobalDecl();
+
+ // Establish a source location for the function so that the prologue can
+ // inherit one (see CIRGenFunction::getLoc / currSrcLoc).
+ SourceLocRAIIObject fnLoc{*this, loc.isValid() ? getLoc(loc)
+ : builder.getUnknownLoc()};
+
+ mlir::Location fusedLoc = getLoc(bodyRange);
+ mlir::Block *entryBB = funcOp.addEntryBlock();
+
+ SymTableScopeTy varScope(symbolTable);
+ {
+ LexicalScope lexScope(*this, fusedLoc, entryBB);
+ startFunction(GlobalDecl(), getContext().VoidTy, funcOp, funcType, args,
+ loc, bodyRange.getBegin());
+ if (mlir::failed(emitFunctionBody(body)))
+ return;
+ if (mlir::failed(funcOp.verifyBody()))
+ return;
+ finishFunction(body->getEndLoc());
+ }
+
+ // Mirror the tail of generateCode: drop leftover empty/unreachable blocks the
+ // lexical-scope machinery may have created.
+ eraseEmptyAndUnusedBlocks(funcOp);
+}
+
+void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
+ ASTContext &ctx) {
+ assert(ctx.getLangOpts().SYCLIsDevice &&
+ "SYCL kernel caller offload entry point functions can only be emitted"
+ " during device compilation");
+
+ const auto *kernelEntryPointAttr =
+ kernelEntryPointFn->getAttr<SYCLKernelEntryPointAttr>();
+ assert(kernelEntryPointAttr && "Missing sycl_kernel_entry_point attribute");
+ assert(!kernelEntryPointAttr->isInvalidAttr() &&
+ "sycl_kernel_entry_point attribute is invalid");
+
+ // Find the SYCLKernelCallStmt.
+ SYCLKernelCallStmt *kernelCallStmt =
+ cast<SYCLKernelCallStmt>(kernelEntryPointFn->getBody());
+
+ // Retrieve the SYCL kernel caller parameters from the OutlinedFunctionDecl.
+ FunctionArgList args;
+ const OutlinedFunctionDecl *outlinedFnDecl =
+ kernelCallStmt->getOutlinedFunctionDecl();
+ args.append(outlinedFnDecl->param_begin(), outlinedFnDecl->param_end());
+
+ // Compute the function info and CIR function type.
+ const CIRGenFunctionInfo &fnInfo =
+ getTypes().arrangeSYCLKernelCallerDeclaration(ctx.VoidTy, args);
+ cir::FuncType funcType = getTypes().getFunctionType(fnInfo);
+
+ // Retrieve the generated name for the SYCL kernel caller function.
+ CanQualType kernelNameType =
+ ctx.getCanonicalType(kernelEntryPointAttr->getKernelName());
+ const SYCLKernelInfo &kernelInfo = ctx.getSYCLKernelInfo(kernelNameType);
+
+ // Create the SYCL kernel caller function. Unlike an ordinary function, this
+ // offload entry point is synthesized from the OutlinedFunctionDecl held by
+ // the SYCLKernelCallStmt rather than emitted from a FunctionDecl, so it is
+ // constructed with an empty GlobalDecl.
+ cir::FuncOp funcOp = getOrCreateCIRFunction(
+ kernelInfo.GetKernelName(), funcType, GlobalDecl(), /*forVTable=*/false,
+ /*dontDefer=*/true, /*isThunk=*/false, ForDefinition);
+
+ // The kernel caller offload entry point has external linkage. Classic
+ // CodeGen creates it with ExternalLinkage explicitly (EmitSYCLKernelCaller);
+ // createCIRFunction already applies ExternalLinkage by default, so set it
+ // explicitly here to make the contract clear rather than rely on the default.
+ funcOp.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
+
+ // Set the device kernel calling convention so the entry point is emitted as
+ // a kernel (e.g. spir_kernel) rather than an ordinary device function.
+ // Classic CodeGen derives this from the CC_DeviceKernel function info via
+ // SetLLVMFunctionAttributes; CIR does not yet route the function-info calling
+ // convention onto the FuncOp (opFuncCallingConv), so set it directly from the
+ // target hook, matching how CIRGen sets kernel calling conventions elsewhere.
+ funcOp.setCallingConv(getTargetCIRGenInfo().getDeviceKernelCallingConv());
+
+ // TODO: The following attributes applied by classic CodeGen's
+ // EmitSYCLKernelCaller are not yet applied in CIR:
+ // - SetSYCLKernelAttributes: norecurse and mustprogress.
+ // - addSYCLModuleIdAttr: the "sycl-module-id" attribute.
+ // - setDSOLocal.
+ assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
+ assert(!cir::MissingFeatures::setDSOLocal());
+
+ // Emit the SYCL kernel caller function.
+ CIRGenFunction cgf(*this, builder);
+ curCGF = &cgf;
+ {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ cgf.emitSYCLKernelCaller(outlinedFnDecl, funcOp, funcType, args);
+ }
+ curCGF = nullptr;
+
+ setNonAliasAttributes(GlobalDecl(), funcOp);
+ // The SYCL kernel caller is synthesized from an OutlinedFunctionDecl rather
+ // than a FunctionDecl. Classic CodeGen passes the OutlinedFunctionDecl to
+ // SetLLVMFunctionAttributesForDefinition, but CIR's setter takes a
+ // FunctionDecl; passing nullptr here skips OutlinedFunctionDecl-derived
+ // attributes (e.g. inline hints), which are not yet handled.
+ assert(!cir::MissingFeatures::opFuncExtraAttrs());
+ setCIRFunctionAttributesForDefinition(/*fd=*/nullptr, funcOp);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index e5af4eec7720f..34f5a2993101d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -1,6 +1,7 @@
#include "CIRGenTypes.h"
#include "CIRGenCXXABI.h"
+#include "CIRGenCall.h"
#include "CIRGenFunctionInfo.h"
#include "CIRGenModule.h"
#include "mlir/IR/BuiltinTypes.h"
@@ -783,6 +784,18 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo(
return *fi;
}
+const CIRGenFunctionInfo &CIRGenTypes::arrangeSYCLKernelCallerDeclaration(
+ QualType resultType, const FunctionArgList &args) {
+ SmallVector<CanQualType, 16> argTypes;
+ for (const VarDecl *arg : args)
+ argTypes.push_back(astContext.getCanonicalParamType(arg->getType()));
+
+ assert(!cir::MissingFeatures::opCallFnInfoOpts());
+ return arrangeCIRFunctionInfo(
+ resultType->getCanonicalTypeUnqualified(), /*isInstanceMethod=*/false,
+ argTypes, FunctionType::ExtInfo(CC_DeviceKernel), RequiredArgs::All);
+}
+
const CIRGenFunctionInfo &CIRGenTypes::arrangeGlobalDeclaration(GlobalDecl gd) {
assert(!dyn_cast<ObjCMethodDecl>(gd.getDecl()) &&
"This is reported as a FIXME in LLVM codegen");
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index a7827f76bd5f2..bc2a1d599cd8a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -44,6 +44,7 @@ class CallArgList;
class CIRGenBuilderTy;
class CIRGenCXXABI;
class CIRGenModule;
+class FunctionArgList;
/// This class organizes the cross-module state that is used while lowering
/// AST types to CIR types.
@@ -186,6 +187,12 @@ class CIRGenTypes {
const CIRGenFunctionInfo &
arrangeFunctionDeclaration(const clang::FunctionDecl *fd);
+ /// Arrange the function info for the offload kernel entry point function
+ /// generated for a SYCL kernel caller function.
+ const CIRGenFunctionInfo &
+ arrangeSYCLKernelCallerDeclaration(clang::QualType resultType,
+ const FunctionArgList &args);
+
/// A builtin function is a freestanding function using the default
/// C conventions.
const CIRGenFunctionInfo &arrangeBuiltinFunctionCall(QualType resultType,
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h
index e720a4ad2ec5c..39fd3daf4b3db 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -141,6 +141,12 @@ class TargetCIRGenInfo {
mlir::Operation *global,
CIRGenModule &module) const {}
+ /// Get the CIR calling convention to use for a device kernel entry point
+ /// (e.g. an OpenCL/SYCL or CUDA/HIP kernel) on this target.
+ virtual cir::CallingConv getDeviceKernelCallingConv() const {
+ return cir::CallingConv::C;
+ }
+
virtual bool isScalarizableAsmOperand(CIRGenFunction &cgf,
mlir::Type ty) const {
return false;
diff --git a/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp b/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
index 643c635128d09..f2d9810b36061 100644
--- a/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
+++ b/clang/lib/CIR/CodeGen/Targets/SPIRV.cpp
@@ -48,6 +48,10 @@ class SPIRVTargetCIRGenInfo : public TargetCIRGenInfo {
func.setCallingConv(cir::CallingConv::SpirKernel);
}
}
+
+ cir::CallingConv getDeviceKernelCallingConv() const override {
+ return cir::CallingConv::SpirKernel;
+ }
};
} // namespace
diff --git a/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp b/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
index c6522064f2fdc..d6d8006d09cc8 100644
--- a/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
+++ b/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
@@ -1,10 +1,15 @@
-// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spir64-unknown-unknown \
-// RUN: -fclangir -emit-cir -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spirv64-unknown-unknown -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-device -triple spirv64-unknown-unknown -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-device -triple spirv64-unknown-unknown -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
-// 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.
+// During device compilation, an offload kernel caller entry point is emitted
+// in place of each sycl_kernel_entry_point attributed function. The entry
+// point is named after the kernel name type and its body is the transformed
+// body held by the OutlinedFunctionDecl (which invokes the kernel functor).
+// The sycl_kernel_entry_point attributed function itself is not emitted.
// Required by sycl_kernel_entry_point semantics.
template <typename KernelName, typename... Ts>
@@ -12,12 +17,51 @@ 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 MemberKN;
struct K {
void operator()() const {}
};
-void test() { kernel_single_task<KN>(K{}); }
+// A sycl_kernel_entry_point function may also be a non-static member function
+// (Sema only rejects explicit-object members, ctors and dtors). The offload
+// entry point is still a free function and must not run an instance-function
+// prologue.
+struct Invoker {
+ template <typename KernelName, typename KernelType>
+ [[clang::sycl_kernel_entry_point(KernelName)]]
+ void kernel_single_task(KernelType kf) { kf(); }
+};
+
+void test() {
+ kernel_single_task<KN>(K{});
+ Invoker{}.kernel_single_task<MemberKN>(K{});
+}
+
+// The kernel caller entry point is named after the kernel name type (KN), is
+// emitted with the spir_kernel calling convention, and its body calls the
+// kernel functor's operator(). The sycl_kernel_entry_point function and its
+// launch call are not emitted during device compilation.
+// CIR-LABEL: cir.func {{.*}}@_ZTS2KN{{.*}}cc(spir_kernel)
+// CIR: cir.call @_ZNK1KclEv
+// CIR: cir.return
+// CIR-NOT: cir.func {{.*}}@_Z18kernel_single_task
+// CIR-NOT: cir.call {{.*}}@_Z17sycl_kernel_launch
+
+// The member-function entry point is emitted the same way, as a free function
+// (no implicit `this` parameter).
+// CIR-LABEL: cir.func {{.*}}@_ZTS8MemberKN{{.*}}cc(spir_kernel)
+// CIR: cir.call @_ZNK1KclEv
+// CIR: cir.return
+
+// LLVM-LABEL: define {{.*}}spir_kernel void @_ZTS2KN
+// LLVM: call {{.*}}void @_ZNK1KclEv
+// LLVM: ret void
+// LLVM-NOT: define {{.*}}@_Z18kernel_single_task
+
+// OGCG-LABEL: define {{.*}}spir_kernel void @_ZTS2KN
+// OGCG: call {{.*}}spir_func void @_ZNK1KclEv
+// OGCG: ret void
+// OGCG-NOT: define {{.*}}@_Z18kernel_single_task
>From e9daf2b5bce98b06f5437833881eb273b729ac3d Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Wed, 5 Aug 2026 18:32:29 -0700
Subject: [PATCH 2/5] Align with classic codegen, concise comments
---
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 5 +--
clang/lib/CIR/CodeGen/CIRGenSYCL.cpp | 55 +++++++-----------------
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 2 +-
clang/lib/CIR/CodeGen/CIRGenTypes.h | 8 ++--
4 files changed, 22 insertions(+), 48 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 5c476ebe668da..0578931fe9419 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -369,9 +369,8 @@ void CIRGenFunction::LexicalScope::emitImplicitReturn() {
CIRGenBuilderTy &builder = cgf.getBuilder();
LexicalScope *localScope = cgf.curLexScope;
- // Synthesized functions (e.g. SYCL kernel caller offload entry points) are
- // emitted without an associated FunctionDecl. They return void, so the
- // non-void C++ flow-off-the-end handling below does not apply to them.
+ // Synthesized functions (e.g. SYCL kernel caller entry points) have no
+ // FunctionDecl; the non-void flow-off-the-end handling below is guarded on fd.
const auto *fd = dyn_cast_or_null<clang::FunctionDecl>(cgf.curGD.getDecl());
// In C++, flowing off the end of a non-void function is always undefined
diff --git a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
index ba694e086ff41..e474f1c42b3a2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
@@ -39,11 +39,9 @@ CIRGenFunction::emitSYCLKernelCallStmt(const SYCLKernelCallStmt &s) {
return emitStmt(s.getKernelLaunchStmt(), /*useCurrentScope=*/true);
}
-// Emit the body of a SYCL kernel caller offload entry point function. The body
-// is the transformed body held by the OutlinedFunctionDecl associated with the
-// sycl_kernel_entry_point attributed function. This mirrors the tail of
-// CIRGenFunction::generateCode, but is driven by an OutlinedFunctionDecl and an
-// explicit argument list rather than a FunctionDecl.
+// Emit the body of a SYCL kernel caller offload entry point. Mirrors the tail
+// of generateCode, but is driven by an OutlinedFunctionDecl and an explicit
+// argument list rather than a FunctionDecl.
void CIRGenFunction::emitSYCLKernelCaller(
const OutlinedFunctionDecl *outlinedFnDecl, cir::FuncOp funcOp,
cir::FuncType funcType, FunctionArgList &args) {
@@ -51,15 +49,9 @@ void CIRGenFunction::emitSYCLKernelCaller(
SourceLocation loc = outlinedFnDecl->getLocation();
SourceRange bodyRange = body->getSourceRange();
- // The offload entry point is synthesized and has no FunctionDecl of its own.
- // As in classic CodeGen's EmitSYCLKernelCaller, it is emitted with an empty
- // GlobalDecl: it is a free function (never an implicit-object member) and
- // must not run a C++ instance-function prologue. LexicalScope's implicit
- // return handles a null curGD (the entry point returns void).
+ // Synthesized entry point: no FunctionDecl, emitted with an empty GlobalDecl.
curGD = GlobalDecl();
- // Establish a source location for the function so that the prologue can
- // inherit one (see CIRGenFunction::getLoc / currSrcLoc).
SourceLocRAIIObject fnLoc{*this, loc.isValid() ? getLoc(loc)
: builder.getUnknownLoc()};
@@ -78,8 +70,6 @@ void CIRGenFunction::emitSYCLKernelCaller(
finishFunction(body->getEndLoc());
}
- // Mirror the tail of generateCode: drop leftover empty/unreachable blocks the
- // lexical-scope machinery may have created.
eraseEmptyAndUnusedBlocks(funcOp);
}
@@ -107,7 +97,7 @@ void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
// Compute the function info and CIR function type.
const CIRGenFunctionInfo &fnInfo =
- getTypes().arrangeSYCLKernelCallerDeclaration(ctx.VoidTy, args);
+ getTypes().arrangeDeviceKernelCallerDeclaration(ctx.VoidTy, args);
cir::FuncType funcType = getTypes().getFunctionType(fnInfo);
// Retrieve the generated name for the SYCL kernel caller function.
@@ -115,33 +105,21 @@ void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
ctx.getCanonicalType(kernelEntryPointAttr->getKernelName());
const SYCLKernelInfo &kernelInfo = ctx.getSYCLKernelInfo(kernelNameType);
- // Create the SYCL kernel caller function. Unlike an ordinary function, this
- // offload entry point is synthesized from the OutlinedFunctionDecl held by
- // the SYCLKernelCallStmt rather than emitted from a FunctionDecl, so it is
- // constructed with an empty GlobalDecl.
+ // Synthesized from the OutlinedFunctionDecl, not a FunctionDecl, so create it
+ // with an empty GlobalDecl.
cir::FuncOp funcOp = getOrCreateCIRFunction(
kernelInfo.GetKernelName(), funcType, GlobalDecl(), /*forVTable=*/false,
/*dontDefer=*/true, /*isThunk=*/false, ForDefinition);
-
- // The kernel caller offload entry point has external linkage. Classic
- // CodeGen creates it with ExternalLinkage explicitly (EmitSYCLKernelCaller);
- // createCIRFunction already applies ExternalLinkage by default, so set it
- // explicitly here to make the contract clear rather than rely on the default.
funcOp.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
- // Set the device kernel calling convention so the entry point is emitted as
- // a kernel (e.g. spir_kernel) rather than an ordinary device function.
- // Classic CodeGen derives this from the CC_DeviceKernel function info via
- // SetLLVMFunctionAttributes; CIR does not yet route the function-info calling
- // convention onto the FuncOp (opFuncCallingConv), so set it directly from the
- // target hook, matching how CIRGen sets kernel calling conventions elsewhere.
+ // Emit as a device kernel (e.g. spir_kernel). Classic CodeGen derives this
+ // from CC_DeviceKernel via SetLLVMFunctionAttributes; CIR does not yet route
+ // opFuncCallingConv onto the FuncOp, so set it from the target hook.
funcOp.setCallingConv(getTargetCIRGenInfo().getDeviceKernelCallingConv());
- // TODO: The following attributes applied by classic CodeGen's
- // EmitSYCLKernelCaller are not yet applied in CIR:
- // - SetSYCLKernelAttributes: norecurse and mustprogress.
- // - addSYCLModuleIdAttr: the "sycl-module-id" attribute.
- // - setDSOLocal.
+ // TODO: attributes applied by classic CodeGen not yet handled in CIR:
+ // SetSYCLKernelAttributes (norecurse, mustprogress), addSYCLModuleIdAttr,
+ // setDSOLocal.
assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
assert(!cir::MissingFeatures::setDSOLocal());
@@ -155,11 +133,8 @@ void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
curCGF = nullptr;
setNonAliasAttributes(GlobalDecl(), funcOp);
- // The SYCL kernel caller is synthesized from an OutlinedFunctionDecl rather
- // than a FunctionDecl. Classic CodeGen passes the OutlinedFunctionDecl to
- // SetLLVMFunctionAttributesForDefinition, but CIR's setter takes a
- // FunctionDecl; passing nullptr here skips OutlinedFunctionDecl-derived
- // attributes (e.g. inline hints), which are not yet handled.
+ // CIR's setter takes a FunctionDecl; nullptr skips OutlinedFunctionDecl-
+ // derived attributes (e.g. inline hints), not yet handled.
assert(!cir::MissingFeatures::opFuncExtraAttrs());
setCIRFunctionAttributesForDefinition(/*fd=*/nullptr, funcOp);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 34f5a2993101d..3707455a79b21 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -784,7 +784,7 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo(
return *fi;
}
-const CIRGenFunctionInfo &CIRGenTypes::arrangeSYCLKernelCallerDeclaration(
+const CIRGenFunctionInfo &CIRGenTypes::arrangeDeviceKernelCallerDeclaration(
QualType resultType, const FunctionArgList &args) {
SmallVector<CanQualType, 16> argTypes;
for (const VarDecl *arg : args)
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index bc2a1d599cd8a..c5f8b521ed888 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -187,11 +187,11 @@ class CIRGenTypes {
const CIRGenFunctionInfo &
arrangeFunctionDeclaration(const clang::FunctionDecl *fd);
- /// Arrange the function info for the offload kernel entry point function
- /// generated for a SYCL kernel caller function.
+ /// Arrange the function info for a device kernel caller entry point (e.g. a
+ /// SYCL kernel caller).
const CIRGenFunctionInfo &
- arrangeSYCLKernelCallerDeclaration(clang::QualType resultType,
- const FunctionArgList &args);
+ arrangeDeviceKernelCallerDeclaration(clang::QualType resultType,
+ const FunctionArgList &args);
/// A builtin function is a freestanding function using the default
/// C conventions.
>From 0fdafb9f874be50079d31e453078deccf773e80d Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Mon, 10 Aug 2026 13:37:15 -0700
Subject: [PATCH 3/5] Address comments
---
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 3 ++-
clang/lib/CIR/CodeGen/CIRGenSYCL.cpp | 23 ++++++++++++-----------
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 5 +++--
3 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 0578931fe9419..39cf3ee20d52b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -370,7 +370,8 @@ void CIRGenFunction::LexicalScope::emitImplicitReturn() {
LexicalScope *localScope = cgf.curLexScope;
// Synthesized functions (e.g. SYCL kernel caller entry points) have no
- // FunctionDecl; the non-void flow-off-the-end handling below is guarded on fd.
+ // FunctionDecl; the non-void flow-off-the-end handling below is guarded on
+ // fd.
const auto *fd = dyn_cast_or_null<clang::FunctionDecl>(cgf.curGD.getDecl());
// In C++, flowing off the end of a non-void function is always undefined
diff --git a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
index e474f1c42b3a2..2f4d908d5c3e1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
@@ -16,8 +16,10 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
-#include "clang/AST/StmtSYCL.h"
#include "clang/AST/SYCLKernelInfo.h"
+#include "clang/AST/StmtSYCL.h"
+
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
using namespace clang::CIRGen;
@@ -105,32 +107,31 @@ void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
ctx.getCanonicalType(kernelEntryPointAttr->getKernelName());
const SYCLKernelInfo &kernelInfo = ctx.getSYCLKernelInfo(kernelNameType);
- // Synthesized from the OutlinedFunctionDecl, not a FunctionDecl, so create it
- // with an empty GlobalDecl.
- cir::FuncOp funcOp = getOrCreateCIRFunction(
- kernelInfo.GetKernelName(), funcType, GlobalDecl(), /*forVTable=*/false,
- /*dontDefer=*/true, /*isThunk=*/false, ForDefinition);
+ // Synthesized from the OutlinedFunctionDecl, not a FunctionDecl, so create
+ // the function directly with a null FunctionDecl (mirrors classic CodeGen's
+ // llvm::Function::Create).
+ cir::FuncOp funcOp = createCIRFunction(
+ getLoc(kernelEntryPointFn->getSourceRange()), kernelInfo.GetKernelName(),
+ funcType, /*funcDecl=*/nullptr);
funcOp.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
// Emit as a device kernel (e.g. spir_kernel). Classic CodeGen derives this
// from CC_DeviceKernel via SetLLVMFunctionAttributes; CIR does not yet route
// opFuncCallingConv onto the FuncOp, so set it from the target hook.
funcOp.setCallingConv(getTargetCIRGenInfo().getDeviceKernelCallingConv());
+ setDSOLocal(static_cast<mlir::Operation *>(funcOp));
// TODO: attributes applied by classic CodeGen not yet handled in CIR:
- // SetSYCLKernelAttributes (norecurse, mustprogress), addSYCLModuleIdAttr,
- // setDSOLocal.
+ // SetSYCLKernelAttributes (norecurse, mustprogress), addSYCLModuleIdAttr.
assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
- assert(!cir::MissingFeatures::setDSOLocal());
// Emit the SYCL kernel caller function.
CIRGenFunction cgf(*this, builder);
- curCGF = &cgf;
+ llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
{
mlir::OpBuilder::InsertionGuard guard(builder);
cgf.emitSYCLKernelCaller(outlinedFnDecl, funcOp, funcType, args);
}
- curCGF = nullptr;
setNonAliasAttributes(GlobalDecl(), funcOp);
// CIR's setter takes a FunctionDecl; nullptr skips OutlinedFunctionDecl-
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 3707455a79b21..6273c463e85f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -784,8 +784,9 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo(
return *fi;
}
-const CIRGenFunctionInfo &CIRGenTypes::arrangeDeviceKernelCallerDeclaration(
- QualType resultType, const FunctionArgList &args) {
+const CIRGenFunctionInfo &
+CIRGenTypes::arrangeDeviceKernelCallerDeclaration(QualType resultType,
+ const FunctionArgList &args) {
SmallVector<CanQualType, 16> argTypes;
for (const VarDecl *arg : args)
argTypes.push_back(astContext.getCanonicalParamType(arg->getType()));
>From 6217d4fefe2f3a8bde051b450bc22283c60e74e7 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Tue, 11 Aug 2026 15:05:22 -0700
Subject: [PATCH 4/5] Fix comments
---
clang/lib/CIR/CodeGen/CIRGenSYCL.cpp | 9 ++++++++-
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 3 ++-
.../CodeGenSYCL/kernel-caller-entry-point.cpp | 16 ++++++++++++++++
3 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
index 2f4d908d5c3e1..b873c2d6f24b7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
@@ -119,7 +119,12 @@ void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
// from CC_DeviceKernel via SetLLVMFunctionAttributes; CIR does not yet route
// opFuncCallingConv onto the FuncOp, so set it from the target hook.
funcOp.setCallingConv(getTargetCIRGenInfo().getDeviceKernelCallingConv());
- setDSOLocal(static_cast<mlir::Operation *>(funcOp));
+
+ // Route through the shared attribute path so generic function attributes
+ // (e.g. convergent) are applied, matching classic CodeGen's
+ // SetLLVMFunctionAttributes. There is no FunctionDecl, so pass an empty
+ // GlobalDecl.
+ setCIRFunctionAttributes(GlobalDecl(), fnInfo, funcOp, /*isThunk=*/false);
// TODO: attributes applied by classic CodeGen not yet handled in CIR:
// SetSYCLKernelAttributes (norecurse, mustprogress), addSYCLModuleIdAttr.
@@ -133,6 +138,8 @@ void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
cgf.emitSYCLKernelCaller(outlinedFnDecl, funcOp, funcType, args);
}
+ setDSOLocal(static_cast<mlir::Operation *>(funcOp));
+
setNonAliasAttributes(GlobalDecl(), funcOp);
// CIR's setter takes a FunctionDecl; nullptr skips OutlinedFunctionDecl-
// derived attributes (e.g. inline hints), not yet handled.
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 6273c463e85f1..d89b31134da2f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -791,7 +791,8 @@ CIRGenTypes::arrangeDeviceKernelCallerDeclaration(QualType resultType,
for (const VarDecl *arg : args)
argTypes.push_back(astContext.getCanonicalParamType(arg->getType()));
- assert(!cir::MissingFeatures::opCallFnInfoOpts());
+ // Classic CodeGen passes FnInfoOpts::None here; that is the no-op case, so
+ // nothing is needed even once CIR models FnInfoOpts.
return arrangeCIRFunctionInfo(
resultType->getCanonicalTypeUnqualified(), /*isInstanceMethod=*/false,
argTypes, FunctionType::ExtInfo(CC_DeviceKernel), RequiredArgs::All);
diff --git a/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp b/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
index d6d8006d09cc8..25981f7df76e1 100644
--- a/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
+++ b/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
@@ -5,6 +5,16 @@
// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spirv64-unknown-unknown -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// On an ELF target such as spir64, the kernel caller entry point definition is
+// dso_local. dso_local is only attached to a definition, so this also verifies
+// that setDSOLocal() runs after body emission.
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spir64-unknown-unknown -fclangir -emit-cir %s -o %t-elf.cir
+// RUN: FileCheck --input-file=%t-elf.cir %s -check-prefix=CIR-ELF
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spir64-unknown-unknown -fclangir -emit-llvm %s -o %t-elf-cir.ll
+// RUN: FileCheck --input-file=%t-elf-cir.ll %s -check-prefix=LLVM-ELF
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spir64-unknown-unknown -emit-llvm %s -o %t-elf.ll
+// RUN: FileCheck --input-file=%t-elf.ll %s -check-prefix=OGCG-ELF
+
// During device compilation, an offload kernel caller entry point is emitted
// in place of each sycl_kernel_entry_point attributed function. The entry
// point is named after the kernel name type and its body is the transformed
@@ -65,3 +75,9 @@ void test() {
// OGCG: call {{.*}}spir_func void @_ZNK1KclEv
// OGCG: ret void
// OGCG-NOT: define {{.*}}@_Z18kernel_single_task
+
+// On ELF, the kernel caller entry point definition is dso_local in CIR,
+// CIR-lowered LLVM IR, and classic CodeGen alike.
+// CIR-ELF: cir.func {{.*}}dso_local {{.*}}@_ZTS2KN
+// LLVM-ELF: define {{.*}}dso_local {{.*}}void @_ZTS2KN
+// OGCG-ELF: define {{.*}}dso_local {{.*}}void @_ZTS2KN
>From 4e92e287ac8521cbeae6e20f3a69d55600b3d3e4 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Sat, 15 Aug 2026 08:32:02 -0700
Subject: [PATCH 5/5] Merge identical LLVM/OGCG FileCheck prefixes in kernel
caller test
---
.../CodeGenSYCL/kernel-caller-entry-point.cpp | 27 ++++++++-----------
1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp b/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
index 25981f7df76e1..4d7ffe01fdebe 100644
--- a/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
+++ b/clang/test/CIR/CodeGenSYCL/kernel-caller-entry-point.cpp
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spirv64-unknown-unknown -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-device -triple spirv64-unknown-unknown -fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefixes=LLVM,LLVM-OGCG
// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spirv64-unknown-unknown -emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=OGCG,LLVM-OGCG
// On an ELF target such as spir64, the kernel caller entry point definition is
// dso_local. dso_local is only attached to a definition, so this also verifies
@@ -11,9 +11,9 @@
// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spir64-unknown-unknown -fclangir -emit-cir %s -o %t-elf.cir
// RUN: FileCheck --input-file=%t-elf.cir %s -check-prefix=CIR-ELF
// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spir64-unknown-unknown -fclangir -emit-llvm %s -o %t-elf-cir.ll
-// RUN: FileCheck --input-file=%t-elf-cir.ll %s -check-prefix=LLVM-ELF
+// RUN: FileCheck --input-file=%t-elf-cir.ll %s -check-prefix=LLVM-OGCG-ELF
// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spir64-unknown-unknown -emit-llvm %s -o %t-elf.ll
-// RUN: FileCheck --input-file=%t-elf.ll %s -check-prefix=OGCG-ELF
+// RUN: FileCheck --input-file=%t-elf.ll %s -check-prefix=LLVM-OGCG-ELF
// During device compilation, an offload kernel caller entry point is emitted
// in place of each sycl_kernel_entry_point attributed function. The entry
@@ -66,18 +66,13 @@ void test() {
// CIR: cir.call @_ZNK1KclEv
// CIR: cir.return
-// LLVM-LABEL: define {{.*}}spir_kernel void @_ZTS2KN
-// LLVM: call {{.*}}void @_ZNK1KclEv
-// LLVM: ret void
-// LLVM-NOT: define {{.*}}@_Z18kernel_single_task
-
-// OGCG-LABEL: define {{.*}}spir_kernel void @_ZTS2KN
-// OGCG: call {{.*}}spir_func void @_ZNK1KclEv
-// OGCG: ret void
-// OGCG-NOT: define {{.*}}@_Z18kernel_single_task
+// LLVM-OGCG-LABEL: define {{.*}}spir_kernel void @_ZTS2KN
+// LLVM: call {{.*}}void @_ZNK1KclEv
+// OGCG: call {{.*}}spir_func void @_ZNK1KclEv
+// LLVM-OGCG: ret void
+// LLVM-OGCG-NOT: define {{.*}}@_Z18kernel_single_task
// On ELF, the kernel caller entry point definition is dso_local in CIR,
// CIR-lowered LLVM IR, and classic CodeGen alike.
-// CIR-ELF: cir.func {{.*}}dso_local {{.*}}@_ZTS2KN
-// LLVM-ELF: define {{.*}}dso_local {{.*}}void @_ZTS2KN
-// OGCG-ELF: define {{.*}}dso_local {{.*}}void @_ZTS2KN
+// CIR-ELF: cir.func {{.*}}dso_local {{.*}}@_ZTS2KN
+// LLVM-OGCG-ELF: define {{.*}}dso_local {{.*}}void @_ZTS2KN
More information about the cfe-commits
mailing list