blob: 6d2be6844da2e5fe081659fa9c6f57141991b51c [file] [edit]
//===--------- 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 "CIRGenModule.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/SYCLKernelInfo.h"
#include "clang/AST/StmtSYCL.h"
#include "llvm/Support/SaveAndRestore.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);
}
// 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};
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);
}
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);
// 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));
}
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());
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);
setSYCLKernelAttributes(cgf, funcOp);
addSYCLModuleIdAttr(funcOp);
assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
// Emit the SYCL kernel caller function.
llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
{
mlir::OpBuilder::InsertionGuard guard(builder);
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.
assert(!cir::MissingFeatures::opFuncExtraAttrs());
setCIRFunctionAttributesForDefinition(/*fd=*/nullptr, funcOp);
}