[clang] [CIR][SYCL] Emit attributes on the SYCL kernel caller (PR #224667)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 18 08:03:33 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Konstantinos Parasyris (koparasy)
<details>
<summary>Changes</summary>
Emit norecurse, mustprogress, and sycl-module-id.
This matches the classic behavior that is specific to the SYCL kernel caller.
---
Full diff: https://github.com/llvm/llvm-project/pull/224667.diff
6 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIRDialect.td (+3)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+16)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+2)
- (modified) clang/lib/CIR/CodeGen/CIRGenSYCL.cpp (+23-3)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+21-6)
- (added) clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp (+39)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index e323eff0b9aa6..f07cd7c0dbeb3 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -53,6 +53,9 @@ def CIR_Dialect : Dialect {
static llvm::StringRef getStrictFPAttrName() { return "strictfp"; }
static llvm::StringRef getNoDuplicatesAttrName() { return "noduplicate"; }
static llvm::StringRef getConvergentAttrName() { return "convergent"; }
+ static llvm::StringRef getNoRecurseAttrName() { return "norecurse"; }
+ static llvm::StringRef getMustProgressAttrName() { return "mustprogress"; }
+ static llvm::StringRef getSYCLModuleIdAttrName() { return "sycl-module-id"; }
static llvm::StringRef getNoUnwindAttrName() { return "nounwind"; }
static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; }
static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 86a8736980773..1ef61e3276bb6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -571,6 +571,22 @@ class CIRGenFunction : public CIRGenTypeCache {
const clang::LangOptions &getLangOpts() const { return cgm.getLangOpts(); }
+ bool checkIfFunctionMustProgress() {
+ if (cgm.getCodeGenOpts().getFiniteLoops() ==
+ clang::CodeGenOptions::FiniteLoopsKind::Never)
+ return false;
+
+ // C++11 and later guarantees that a thread eventually will do one of the
+ // following (C++11 [intro.multithread]p24 and C++17 [intro.progress]p1):
+ // - terminate,
+ // - make a call to a library I/O function,
+ // - perform an access through a volatile glvalue, or
+ // - perform a synchronization operation or an atomic operation.
+ //
+ // Hence each function is 'mustprogress' in C++11 or later.
+ return getLangOpts().CPlusPlus11;
+ }
+
/// True if an insertion point is defined. If not, this indicates that the
/// current code being emitted is unreachable.
/// FIXME(cir): we need to inspect this and perhaps use a cleaner mechanism
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 51b9c420c94be..4bdd302764b44 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -668,6 +668,8 @@ class CIRGenModule : public CIRGenTypeCache {
/// function declared with the sycl_kernel_entry_point attribute.
void emitSYCLKernelCaller(const clang::FunctionDecl *kernelEntryPointFn,
clang::ASTContext &ctx);
+
+ void addSYCLModuleIdAttr(cir::FuncOp fn);
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 edba083c7c406..cfbd580fa3aff 100644
--- a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
@@ -74,6 +74,24 @@ void CIRGenFunction::emitSYCLKernelCaller(
eraseEmptyAndUnusedBlocks(funcOp);
}
+static void setSYCLKernelAttributes(CIRGenFunction &cgf, cir::FuncOp fn) {
+ mlir::MLIRContext *ctx = &cgf.getMLIRContext();
+ // SYCL 2020 device language restrictions require forward progress and
+ // disallow recursion.
+ fn->setAttr(cir::CIRDialect::getNoRecurseAttrName(),
+ mlir::UnitAttr::get(ctx));
+ if (cgf.checkIfFunctionMustProgress())
+ fn->setAttr(cir::CIRDialect::getMustProgressAttrName(),
+ mlir::UnitAttr::get(ctx));
+}
+
+void CIRGenModule::addSYCLModuleIdAttr(cir::FuncOp fn) {
+ assert(getLangOpts().SYCLIsDevice);
+ StringRef moduleId = theModule.getSymName().value_or("");
+ fn->setAttr(cir::CIRDialect::getSYCLModuleIdAttrName(),
+ mlir::StringAttr::get(&getMLIRContext(), moduleId));
+}
+
void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
ASTContext &ctx) {
assert(ctx.getLangOpts().SYCLIsDevice &&
@@ -119,18 +137,20 @@ void CIRGenModule::emitSYCLKernelCaller(const FunctionDecl *kernelEntryPointFn,
// opFuncCallingConv onto the FuncOp, so set it from the target hook.
funcOp.setCallingConv(getTargetCIRGenInfo().getDeviceKernelCallingConv());
+ CIRGenFunction cgf(*this, builder);
+
// 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.
+ setSYCLKernelAttributes(cgf, funcOp);
+ addSYCLModuleIdAttr(funcOp);
+
assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
// Emit the SYCL kernel caller function.
- CIRGenFunction cgf(*this, builder);
llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
{
mlir::OpBuilder::InsertionGuard guard(builder);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 186d91599e806..e1e309947786e 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2672,6 +2672,9 @@ static bool shouldDropFuncAttribute(cir::FuncOp func, mlir::NamedAttribute attr,
attr.getName() == func.getSideEffectAttrName() ||
attr.getName() == CIRDialect::getNoReturnAttrName() ||
attr.getName() == CIRDialect::getStrictFPAttrName() ||
+ attr.getName() == CIRDialect::getNoRecurseAttrName() ||
+ attr.getName() == CIRDialect::getMustProgressAttrName() ||
+ attr.getName() == CIRDialect::getSYCLModuleIdAttrName() ||
attr.getName() == func.getAnnotationsAttrName();
}
@@ -2811,13 +2814,25 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
if (op->hasAttr(CIRDialect::getNoReturnAttrName()))
fn.setNoreturn(true);
- // The LLVM dialect's LLVMFuncOp has no dedicated field for the `strictfp`
- // function attribute, so route it through the `passthrough` array. The MLIR
- // LLVM IR translator forwards `passthrough` entries to LLVM IR as function
+ // Function attributes with no dedicated field on the LLVM dialect's
+ // LLVMFuncOp are routed through the `passthrough` array. The MLIR LLVM IR
+ // translator forwards `passthrough` entries to LLVM IR as function
// attributes.
- if (op->hasAttr(CIRDialect::getStrictFPAttrName()))
- fn.setPassthroughAttr(rewriter.getArrayAttr(
- {rewriter.getStringAttr(CIRDialect::getStrictFPAttrName())}));
+ SmallVector<mlir::Attribute> passthrough;
+ for (llvm::StringRef flagAttr :
+ {CIRDialect::getStrictFPAttrName(), CIRDialect::getNoRecurseAttrName(),
+ CIRDialect::getMustProgressAttrName()})
+ if (op->hasAttr(flagAttr))
+ passthrough.push_back(rewriter.getStringAttr(flagAttr));
+
+ if (auto moduleId = op->getAttrOfType<mlir::StringAttr>(
+ CIRDialect::getSYCLModuleIdAttrName()))
+ passthrough.push_back(rewriter.getArrayAttr(
+ {rewriter.getStringAttr(CIRDialect::getSYCLModuleIdAttrName()),
+ moduleId}));
+
+ if (!passthrough.empty())
+ fn.setPassthroughAttr(rewriter.getArrayAttr(passthrough));
if (std::optional<cir::InlineKind> inlineKind = op.getInlineKind()) {
fn.setNoInline(*inlineKind == cir::InlineKind::NoInline);
diff --git a/clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp b/clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp
new file mode 100644
index 0000000000000..ba0e563a4acff
--- /dev/null
+++ b/clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp
@@ -0,0 +1,39 @@
+// 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
+
+// The SYCL kernel caller offload entry point receives the SYCL 2020 device
+// language attributes: it must not recurse (norecurse) and is guaranteed to
+// make forward progress in C++11 and later (mustprogress). It also carries the
+// "sycl-module-id" attribute, marking it as an entry point for
+// per-translation-unit device-code splitting. This matches classic CodeGen's
+// SetSYCLKernelAttributes and addSYCLModuleIdAttr.
+
+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;
+
+void test(int *p) {
+ kernel_single_task<KN>([p]() { *p = 42; });
+}
+
+// CIR-LABEL: cir.func
+// CIR-SAME: @_ZTS2KN
+// CIR-SAME: cc(spir_kernel)
+// CIR-SAME: mustprogress
+// CIR-SAME: norecurse
+// CIR-SAME: "sycl-module-id" = "{{.*}}kernel-caller-attributes.cpp"
+
+// LLVM: define spir_kernel void @_ZTS2KN({{.*}}) #[[KATTR:[0-9]+]]
+// LLVM: attributes #[[KATTR]] = {{[{].*}}mustprogress{{.*}}norecurse{{.*}}"sycl-module-id"="{{.*}}kernel-caller-attributes.cpp"
+
+// OGCG: define spir_kernel void @_ZTS2KN({{.*}}) #[[KATTR:[0-9]+]]
+// OGCG: attributes #[[KATTR]] = {{[{].*}}mustprogress{{.*}}norecurse{{.*}}"sycl-module-id"="{{.*}}kernel-caller-attributes.cpp"
``````````
</details>
https://github.com/llvm/llvm-project/pull/224667
More information about the cfe-commits
mailing list