Skip to content

[SYCL] Basic code generation for SYCL kernel caller offload entry point functions. #133030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 17, 2025
Merged
8 changes: 6 additions & 2 deletions clang/include/clang/AST/SYCLKernelInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,23 @@ namespace clang {
class SYCLKernelInfo {
public:
SYCLKernelInfo(CanQualType KernelNameType,
const FunctionDecl *KernelEntryPointDecl)
const FunctionDecl *KernelEntryPointDecl,
const std::string &KernelName)
: KernelNameType(KernelNameType),
KernelEntryPointDecl(KernelEntryPointDecl) {}
KernelEntryPointDecl(KernelEntryPointDecl), KernelName(KernelName) {}

CanQualType getKernelNameType() const { return KernelNameType; }

const FunctionDecl *getKernelEntryPointDecl() const {
return KernelEntryPointDecl;
}

const std::string &GetKernelName() const { return KernelName; }

private:
CanQualType KernelNameType;
const FunctionDecl *KernelEntryPointDecl;
std::string KernelName;
};

} // namespace clang
Expand Down
44 changes: 40 additions & 4 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12825,6 +12825,15 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
if (!FD->doesThisDeclarationHaveABody())
return FD->doesDeclarationForceExternallyVisibleDefinition();

// Function definitions with the sycl_kernel_entry_point attribute are
// required during device compilation so that SYCL kernel caller offload
// entry points are emitted.
if (LangOpts.SYCLIsDevice && FD->hasAttr<SYCLKernelEntryPointAttr>())
return true;

// FIXME: Functions declared with SYCL_EXTERNAL are required during
// device compilation.

// Constructors and destructors are required.
if (FD->hasAttr<ConstructorAttr>() || FD->hasAttr<DestructorAttr>())
return true;
Expand Down Expand Up @@ -14832,9 +14841,36 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
}
}

static SYCLKernelInfo BuildSYCLKernelInfo(CanQualType KernelNameType,
static SYCLKernelInfo BuildSYCLKernelInfo(ASTContext &Context,
CanQualType KernelNameType,
const FunctionDecl *FD) {
return {KernelNameType, FD};
// Host and device compilation may use different ABIs and different ABIs
// may allocate name mangling discriminators differently. A discriminator
// override is used to ensure consistent discriminator allocation across
// host and device compilation.
auto DeviceDiscriminatorOverrider =
[](ASTContext &Ctx, const NamedDecl *ND) -> UnsignedOrNone {
if (const auto *RD = dyn_cast<CXXRecordDecl>(ND))
if (RD->isLambda())
return RD->getDeviceLambdaManglingNumber();
return std::nullopt;
};
std::unique_ptr<MangleContext> MC{ItaniumMangleContext::create(
Context, Context.getDiagnostics(), DeviceDiscriminatorOverrider)};

// Construct a mangled name for the SYCL kernel caller offload entry point.
// FIXME: The Itanium typeinfo mangling (_ZTS<type>) is currently used to
// name the SYCL kernel caller offload entry point function. This mangling
// does not suffice to clearly identify symbols that correspond to SYCL
// kernel caller functions, nor is this mangling natural for targets that
// use a non-Itanium ABI.
std::string Buffer;
Buffer.reserve(128);
llvm::raw_string_ostream Out(Buffer);
MC->mangleCanonicalTypeName(KernelNameType, Out);
std::string KernelName = Out.str();

return {KernelNameType, FD, KernelName};
}

void ASTContext::registerSYCLEntryPointFunction(FunctionDecl *FD) {
Expand All @@ -14855,8 +14891,8 @@ void ASTContext::registerSYCLEntryPointFunction(FunctionDecl *FD) {
declaresSameEntity(FD, IT->second.getKernelEntryPointDecl())) &&
"SYCL kernel name conflict");
(void)IT;
SYCLKernels.insert(
std::make_pair(KernelNameType, BuildSYCLKernelInfo(KernelNameType, FD)));
SYCLKernels.insert(std::make_pair(
KernelNameType, BuildSYCLKernelInfo(*this, KernelNameType, FD)));
}

const SYCLKernelInfo &ASTContext::getSYCLKernelInfo(QualType T) const {
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,17 @@ CodeGenTypes::arrangeBuiltinFunctionDeclaration(CanQualType resultType,
RequiredArgs::All);
}

const CGFunctionInfo &
CodeGenTypes::arrangeSYCLKernelCallerDeclaration(QualType resultType,
const FunctionArgList &args) {
CanQualTypeList argTypes = getArgTypesForDeclaration(Context, args);

return arrangeLLVMFunctionInfo(GetReturnType(resultType), FnInfoOpts::None,
argTypes,
FunctionType::ExtInfo(CC_OpenCLKernel),
/*paramInfos=*/{}, RequiredArgs::All);
}

/// Arrange a call to a C++ method, passing the given arguments.
///
/// numPrefixArgs is the number of ABI-specific prefix arguments we have. It
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ add_clang_library(clangCodeGen
CodeGenFunction.cpp
CodeGenModule.cpp
CodeGenPGO.cpp
CodeGenSYCL.cpp
CodeGenTBAA.cpp
CodeGenTypes.cpp
ConstantInitBuilder.cpp
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3309,6 +3309,27 @@ void CodeGenModule::EmitDeferred() {
CurDeclsToEmit.swap(DeferredDeclsToEmit);

for (GlobalDecl &D : CurDeclsToEmit) {
// Functions declared with the sycl_kernel_entry_point attribute are
// emitted normally during host compilation. During device compilation,
// a SYCL kernel caller offload entry point function is generated and
// emitted in place of each of these functions.
if (const auto *FD = D.getDecl()->getAsFunction()) {
if (LangOpts.SYCLIsDevice && FD->hasAttr<SYCLKernelEntryPointAttr>() &&
FD->isDefined()) {
// Functions with an invalid sycl_kernel_entry_point attribute are
// ignored during device compilation.
if (!FD->getAttr<SYCLKernelEntryPointAttr>()->isInvalidAttr()) {
// Generate and emit the SYCL kernel caller function.
EmitSYCLKernelCaller(FD, getContext());
// 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;
}
}

// We should call GetAddrOfGlobal with IsForDefinition set to true in order
// to get GlobalValue with exactly the type we need, not something that
// might had been created for another decl with the same mangled name but
Expand Down Expand Up @@ -3644,6 +3665,10 @@ bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
// Defer until all versions have been semantically checked.
if (FD->hasAttr<TargetVersionAttr>() && !FD->isMultiVersion())
return false;
// Defer emission of SYCL kernel entry point functions during device
// compilation.
if (LangOpts.SYCLIsDevice && FD->hasAttr<SYCLKernelEntryPointAttr>())
return false;
}
if (const auto *VD = dyn_cast<VarDecl>(Global)) {
if (Context.getInlineVariableDefinitionKind(VD) ==
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,11 @@ class CodeGenModule : public CodeGenTypeCache {
/// .gcda files in a way that persists in .bc files.
void EmitCoverageFile();

/// Given a sycl_kernel_entry_point attributed function, emit the
/// corresponding SYCL kernel caller offload entry point function.
void EmitSYCLKernelCaller(const FunctionDecl *KernelEntryPointFn,
ASTContext &Ctx);

/// Determine whether the definition must be emitted; if this returns \c
/// false, the definition can be emitted lazily if it's used.
bool MustBeEmitted(const ValueDecl *D);
Expand Down
72 changes: 72 additions & 0 deletions clang/lib/CodeGen/CodeGenSYCL.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//===--------- CodeGenSYCL.cpp - Code for SYCL kernel generation ----------===//
//
// 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 generation of SYCL kernel caller offload
// entry point functions.
//
//===----------------------------------------------------------------------===//

#include "CodeGenFunction.h"
#include "CodeGenModule.h"

using namespace clang;
using namespace CodeGen;

static void SetSYCLKernelAttributes(llvm::Function *Fn, CodeGenFunction &CGF) {
// SYCL 2020 device language restrictions require forward progress and
// disallow recursion.
Fn->setDoesNotRecurse();
if (CGF.checkIfFunctionMustProgress())
Fn->addFnAttr(llvm::Attribute::MustProgress);
}

void CodeGenModule::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 LLVM function type.
const CGFunctionInfo &FnInfo =
getTypes().arrangeSYCLKernelCallerDeclaration(Ctx.VoidTy, Args);
llvm::FunctionType *FnTy = 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);
auto *Fn = llvm::Function::Create(FnTy, llvm::Function::ExternalLinkage,
KernelInfo.GetKernelName(), &getModule());

// Emit the SYCL kernel caller function.
CodeGenFunction CGF(*this);
SetLLVMFunctionAttributes(GlobalDecl(), FnInfo, Fn, false);
SetSYCLKernelAttributes(Fn, CGF);
CGF.StartFunction(GlobalDecl(), Ctx.VoidTy, Fn, FnInfo, Args,
SourceLocation(), SourceLocation());
CGF.EmitFunctionBody(OutlinedFnDecl->getBody());
setDSOLocal(Fn);
SetLLVMFunctionAttributesForDefinition(cast<Decl>(OutlinedFnDecl), Fn);
CGF.FinishFunction();
}
7 changes: 7 additions & 0 deletions clang/lib/CodeGen/CodeGenTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@ class CodeGenTypes {
const CGFunctionInfo &arrangeBuiltinFunctionCall(QualType resultType,
const CallArgList &args);

/// A SYCL kernel caller function is an offload device entry point function
/// with a target device dependent calling convention such as amdgpu_kernel,
/// ptx_kernel, or spir_kernel.
const CGFunctionInfo &
arrangeSYCLKernelCallerDeclaration(QualType resultType,
const FunctionArgList &args);

/// Objective-C methods are C functions with some implicit parameters.
const CGFunctionInfo &arrangeObjCMethodDeclaration(const ObjCMethodDecl *MD);
const CGFunctionInfo &arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD,
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/Targets/NVPTX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ class NVPTXTargetCodeGenInfo : public TargetCodeGenInfo {
return true;
}

unsigned getOpenCLKernelCallingConv() const override {
return llvm::CallingConv::PTX_Kernel;
}

// Adds a NamedMDNode with GV, Name, and Operand as operands, and adds the
// resulting MDNode to the nvvm.annotations MDNode.
static void addNVVMMetadata(llvm::GlobalValue *GV, StringRef Name,
Expand Down
Loading
Loading