[clang] [CIR][SYCL] Device kernel caller (PR #213771)

Akimasa Watanuki via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 11 04:00:17 PDT 2026


================
@@ -33,3 +40,102 @@ 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. 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) {
+  const Stmt *body = outlinedFnDecl->getBody();
+  SourceLocation loc = outlinedFnDecl->getLocation();
+  SourceRange bodyRange = body->getSourceRange();
+
+  // Synthesized entry point: no FunctionDecl, emitted with an empty GlobalDecl.
+  curGD = GlobalDecl();
+
+  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());
+  }
+
+  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().arrangeDeviceKernelCallerDeclaration(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);
+
+  // 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));
----------------
Men-cotton wrote:

Could we move `setDSOLocal()` after body emission?

The `funcOp` is still a declaration here, so `shouldAssumeDSOLocal()` does not attach `dso_local`. Consequently, CIR-generated kernel entries lack `dso_local` on ELF targets such as spir64, NVPTX, and AMDGPU, unlike in classic CodeGen.

Moving this after body emission would match classic CodeGen’s ordering. Please also add CIR and lowered LLVM IR checks for an ELF target, such as spir64.

https://github.com/llvm/llvm-project/pull/213771


More information about the cfe-commits mailing list