https://github.com/koparasy updated 
https://github.com/llvm/llvm-project/pull/224667

>From d12197e30ab9bfc84977085d5d57208867044b98 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Fri, 18 Sep 2026 07:04:11 -0700
Subject: [PATCH 1/2] [CIR][SYCL] Emit norecurse, mustprogress, and
 sycl-module-id on the kernel caller

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../clang/CIR/Dialect/IR/CIRDialect.td        |  3 ++
 clang/lib/CIR/CodeGen/CIRGenFunction.h        | 16 ++++++++
 clang/lib/CIR/CodeGen/CIRGenModule.h          |  2 +
 clang/lib/CIR/CodeGen/CIRGenSYCL.cpp          | 26 +++++++++++--
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 27 ++++++++++---
 .../CodeGenSYCL/kernel-caller-attributes.cpp  | 39 +++++++++++++++++++
 6 files changed, 104 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp

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 f690774a25921..91cf7afafc248 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 59654786d1eca..50adaad64a763 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2694,6 +2694,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() ||
          attr.getName() == func.getComdatAttrName() ||
          attr.getName() == func.getAlignmentAttrName();
@@ -2848,13 +2851,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"

>From afb747a12347bc9b59801fa2a4734f62f9aba7d2 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Fri, 18 Sep 2026 18:46:12 -0700
Subject: [PATCH 2/2] Address comments

---
 clang/lib/CIR/CodeGen/CIRGenSYCL.cpp                    | 6 +++++-
 clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp | 8 ++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp 
b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
index cfbd580fa3aff..6d2be6844da2e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenSYCL.cpp
@@ -87,7 +87,11 @@ static void setSYCLKernelAttributes(CIRGenFunction &cgf, 
cir::FuncOp fn) {
 
 void CIRGenModule::addSYCLModuleIdAttr(cir::FuncOp fn) {
   assert(getLangOpts().SYCLIsDevice);
-  StringRef moduleId = theModule.getSymName().value_or("");
+  // Classic CodeGen uses the LLVM module identifier, which is the main input
+  // file name. CIR stores that as the module's symbol name; fall back to the
+  // main file name so the attribute is never empty, matching classic CodeGen.
+  StringRef moduleId =
+      theModule.getSymName().value_or(codeGenOpts.MainFileName);
   fn->setAttr(cir::CIRDialect::getSYCLModuleIdAttrName(),
               mlir::StringAttr::get(&getMLIRContext(), moduleId));
 }
diff --git a/clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp 
b/clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp
index ba0e563a4acff..54b168b2929f7 100644
--- a/clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp
+++ b/clang/test/CIR/CodeGenSYCL/kernel-caller-attributes.cpp
@@ -4,6 +4,8 @@
 // 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
+// RUN: %clang_cc1 -std=c++20 -fsycl-is-device -triple spirv64-unknown-unknown 
-fclangir -emit-llvm -fno-finite-loops %s -o %t-nomp.ll
+// RUN: FileCheck --input-file=%t-nomp.ll %s -check-prefix=LLVM-NOMP
 
 // The SYCL kernel caller offload entry point receives the SYCL 2020 device
 // language attributes: it must not recurse (norecurse) and is guaranteed to
@@ -37,3 +39,9 @@ void test(int *p) {
 
 // OGCG: define spir_kernel void @_ZTS2KN({{.*}}) #[[KATTR:[0-9]+]]
 // OGCG: attributes #[[KATTR]] = 
{{[{].*}}mustprogress{{.*}}norecurse{{.*}}"sycl-module-id"="{{.*}}kernel-caller-attributes.cpp"
+
+// With -fno-finite-loops, checkIfFunctionMustProgress() is false, so the 
kernel
+// caller must not carry mustprogress; norecurse and sycl-module-id remain. The
+// exact attribute set (which omits mustprogress) is verified here.
+// LLVM-NOMP: define spir_kernel void @_ZTS2KN({{.*}}) #[[KATTR:[0-9]+]]
+// LLVM-NOMP: attributes #[[KATTR]] = { convergent noinline norecurse 
"sycl-module-id"="{{.*}}kernel-caller-attributes.cpp" }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to