Skip to content
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