[OpenCL] Make global ctor init function a kernel

We need to be able to enqueue internal function that initializes
global constructors on the host side. Therefore it has to be
converted to a kernel.

This change factors out common logic for adding kernel metadata
and moves it from CodeGenFunction to CodeGenModule in order to
make it accessible for the extra use case.

Differential revision: https://reviews.llvm.org/D61488

llvm-svn: 360342
This commit is contained in:
Anastasia Stulova 2019-05-09 13:55:44 +00:00
parent 9db0e72570
commit e6cf6c78f8
5 changed files with 244 additions and 200 deletions

View File

@ -580,6 +580,19 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits);
AddGlobalCtor(Fn);
// In OpenCL global init functions must be converted to kernels in order to
// be able to launch them from the host.
// FIXME: Some more work might be needed to handle destructors correctly.
// Current initialization function makes use of function pointers callbacks.
// We can't support function pointers especially between host and device.
// However it seems global destruction has little meaning without any
// dynamic resource allocation on the device and program scope variables are
// destroyed by the runtime when program is released.
if (getLangOpts().OpenCL) {
GenOpenCLArgMetadata(Fn);
Fn->setCallingConv(llvm::CallingConv::SPIR_KERNEL);
}
CXXGlobalInits.clear();
}

View File

@ -536,205 +536,6 @@ CodeGenFunction::DecodeAddrUsedInPrologue(llvm::Value *F,
"decoded_addr");
}
static void removeImageAccessQualifier(std::string& TyName) {
std::string ReadOnlyQual("__read_only");
std::string::size_type ReadOnlyPos = TyName.find(ReadOnlyQual);
if (ReadOnlyPos != std::string::npos)
// "+ 1" for the space after access qualifier.
TyName.erase(ReadOnlyPos, ReadOnlyQual.size() + 1);
else {
std::string WriteOnlyQual("__write_only");
std::string::size_type WriteOnlyPos = TyName.find(WriteOnlyQual);
if (WriteOnlyPos != std::string::npos)
TyName.erase(WriteOnlyPos, WriteOnlyQual.size() + 1);
else {
std::string ReadWriteQual("__read_write");
std::string::size_type ReadWritePos = TyName.find(ReadWriteQual);
if (ReadWritePos != std::string::npos)
TyName.erase(ReadWritePos, ReadWriteQual.size() + 1);
}
}
}
// Returns the address space id that should be produced to the
// kernel_arg_addr_space metadata. This is always fixed to the ids
// as specified in the SPIR 2.0 specification in order to differentiate
// for example in clGetKernelArgInfo() implementation between the address
// spaces with targets without unique mapping to the OpenCL address spaces
// (basically all single AS CPUs).
static unsigned ArgInfoAddressSpace(LangAS AS) {
switch (AS) {
case LangAS::opencl_global: return 1;
case LangAS::opencl_constant: return 2;
case LangAS::opencl_local: return 3;
case LangAS::opencl_generic: return 4; // Not in SPIR 2.0 specs.
default:
return 0; // Assume private.
}
}
// OpenCL v1.2 s5.6.4.6 allows the compiler to store kernel argument
// information in the program executable. The argument information stored
// includes the argument name, its type, the address and access qualifiers used.
static void GenOpenCLArgMetadata(const FunctionDecl *FD, llvm::Function *Fn,
CodeGenModule &CGM, llvm::LLVMContext &Context,
CGBuilderTy &Builder, ASTContext &ASTCtx) {
// Create MDNodes that represent the kernel arg metadata.
// Each MDNode is a list in the form of "key", N number of values which is
// the same number of values as their are kernel arguments.
const PrintingPolicy &Policy = ASTCtx.getPrintingPolicy();
// MDNode for the kernel argument address space qualifiers.
SmallVector<llvm::Metadata *, 8> addressQuals;
// MDNode for the kernel argument access qualifiers (images only).
SmallVector<llvm::Metadata *, 8> accessQuals;
// MDNode for the kernel argument type names.
SmallVector<llvm::Metadata *, 8> argTypeNames;
// MDNode for the kernel argument base type names.
SmallVector<llvm::Metadata *, 8> argBaseTypeNames;
// MDNode for the kernel argument type qualifiers.
SmallVector<llvm::Metadata *, 8> argTypeQuals;
// MDNode for the kernel argument names.
SmallVector<llvm::Metadata *, 8> argNames;
for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) {
const ParmVarDecl *parm = FD->getParamDecl(i);
QualType ty = parm->getType();
std::string typeQuals;
if (ty->isPointerType()) {
QualType pointeeTy = ty->getPointeeType();
// Get address qualifier.
addressQuals.push_back(llvm::ConstantAsMetadata::get(Builder.getInt32(
ArgInfoAddressSpace(pointeeTy.getAddressSpace()))));
// Get argument type name.
std::string typeName =
pointeeTy.getUnqualifiedType().getAsString(Policy) + "*";
// Turn "unsigned type" to "utype"
std::string::size_type pos = typeName.find("unsigned");
if (pointeeTy.isCanonical() && pos != std::string::npos)
typeName.erase(pos+1, 8);
argTypeNames.push_back(llvm::MDString::get(Context, typeName));
std::string baseTypeName =
pointeeTy.getUnqualifiedType().getCanonicalType().getAsString(
Policy) +
"*";
// Turn "unsigned type" to "utype"
pos = baseTypeName.find("unsigned");
if (pos != std::string::npos)
baseTypeName.erase(pos+1, 8);
argBaseTypeNames.push_back(llvm::MDString::get(Context, baseTypeName));
// Get argument type qualifiers:
if (ty.isRestrictQualified())
typeQuals = "restrict";
if (pointeeTy.isConstQualified() ||
(pointeeTy.getAddressSpace() == LangAS::opencl_constant))
typeQuals += typeQuals.empty() ? "const" : " const";
if (pointeeTy.isVolatileQualified())
typeQuals += typeQuals.empty() ? "volatile" : " volatile";
} else {
uint32_t AddrSpc = 0;
bool isPipe = ty->isPipeType();
if (ty->isImageType() || isPipe)
AddrSpc = ArgInfoAddressSpace(LangAS::opencl_global);
addressQuals.push_back(
llvm::ConstantAsMetadata::get(Builder.getInt32(AddrSpc)));
// Get argument type name.
std::string typeName;
if (isPipe)
typeName = ty.getCanonicalType()->getAs<PipeType>()->getElementType()
.getAsString(Policy);
else
typeName = ty.getUnqualifiedType().getAsString(Policy);
// Turn "unsigned type" to "utype"
std::string::size_type pos = typeName.find("unsigned");
if (ty.isCanonical() && pos != std::string::npos)
typeName.erase(pos+1, 8);
std::string baseTypeName;
if (isPipe)
baseTypeName = ty.getCanonicalType()->getAs<PipeType>()
->getElementType().getCanonicalType()
.getAsString(Policy);
else
baseTypeName =
ty.getUnqualifiedType().getCanonicalType().getAsString(Policy);
// Remove access qualifiers on images
// (as they are inseparable from type in clang implementation,
// but OpenCL spec provides a special query to get access qualifier
// via clGetKernelArgInfo with CL_KERNEL_ARG_ACCESS_QUALIFIER):
if (ty->isImageType()) {
removeImageAccessQualifier(typeName);
removeImageAccessQualifier(baseTypeName);
}
argTypeNames.push_back(llvm::MDString::get(Context, typeName));
// Turn "unsigned type" to "utype"
pos = baseTypeName.find("unsigned");
if (pos != std::string::npos)
baseTypeName.erase(pos+1, 8);
argBaseTypeNames.push_back(llvm::MDString::get(Context, baseTypeName));
if (isPipe)
typeQuals = "pipe";
}
argTypeQuals.push_back(llvm::MDString::get(Context, typeQuals));
// Get image and pipe access qualifier:
if (ty->isImageType()|| ty->isPipeType()) {
const Decl *PDecl = parm;
if (auto *TD = dyn_cast<TypedefType>(ty))
PDecl = TD->getDecl();
const OpenCLAccessAttr *A = PDecl->getAttr<OpenCLAccessAttr>();
if (A && A->isWriteOnly())
accessQuals.push_back(llvm::MDString::get(Context, "write_only"));
else if (A && A->isReadWrite())
accessQuals.push_back(llvm::MDString::get(Context, "read_write"));
else
accessQuals.push_back(llvm::MDString::get(Context, "read_only"));
} else
accessQuals.push_back(llvm::MDString::get(Context, "none"));
// Get argument name.
argNames.push_back(llvm::MDString::get(Context, parm->getName()));
}
Fn->setMetadata("kernel_arg_addr_space",
llvm::MDNode::get(Context, addressQuals));
Fn->setMetadata("kernel_arg_access_qual",
llvm::MDNode::get(Context, accessQuals));
Fn->setMetadata("kernel_arg_type",
llvm::MDNode::get(Context, argTypeNames));
Fn->setMetadata("kernel_arg_base_type",
llvm::MDNode::get(Context, argBaseTypeNames));
Fn->setMetadata("kernel_arg_type_qual",
llvm::MDNode::get(Context, argTypeQuals));
if (CGM.getCodeGenOpts().EmitOpenCLArgMetadata)
Fn->setMetadata("kernel_arg_name",
llvm::MDNode::get(Context, argNames));
}
void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD,
llvm::Function *Fn)
{
@ -743,7 +544,7 @@ void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD,
llvm::LLVMContext &Context = getLLVMContext();
GenOpenCLArgMetadata(FD, Fn, CGM, Context, Builder, getContext());
CGM.GenOpenCLArgMetadata(Fn, FD, this);
if (const VecTypeHintAttr *A = FD->getAttr<VecTypeHintAttr>()) {
QualType HintQTy = A->getTypeHint();

View File

@ -1193,6 +1193,212 @@ void CodeGenModule::SetLLVMFunctionAttributes(GlobalDecl GD,
F->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
}
static void removeImageAccessQualifier(std::string& TyName) {
std::string ReadOnlyQual("__read_only");
std::string::size_type ReadOnlyPos = TyName.find(ReadOnlyQual);
if (ReadOnlyPos != std::string::npos)
// "+ 1" for the space after access qualifier.
TyName.erase(ReadOnlyPos, ReadOnlyQual.size() + 1);
else {
std::string WriteOnlyQual("__write_only");
std::string::size_type WriteOnlyPos = TyName.find(WriteOnlyQual);
if (WriteOnlyPos != std::string::npos)
TyName.erase(WriteOnlyPos, WriteOnlyQual.size() + 1);
else {
std::string ReadWriteQual("__read_write");
std::string::size_type ReadWritePos = TyName.find(ReadWriteQual);
if (ReadWritePos != std::string::npos)
TyName.erase(ReadWritePos, ReadWriteQual.size() + 1);
}
}
}
// Returns the address space id that should be produced to the
// kernel_arg_addr_space metadata. This is always fixed to the ids
// as specified in the SPIR 2.0 specification in order to differentiate
// for example in clGetKernelArgInfo() implementation between the address
// spaces with targets without unique mapping to the OpenCL address spaces
// (basically all single AS CPUs).
static unsigned ArgInfoAddressSpace(LangAS AS) {
switch (AS) {
case LangAS::opencl_global: return 1;
case LangAS::opencl_constant: return 2;
case LangAS::opencl_local: return 3;
case LangAS::opencl_generic: return 4; // Not in SPIR 2.0 specs.
default:
return 0; // Assume private.
}
}
void CodeGenModule::GenOpenCLArgMetadata(llvm::Function *Fn,
const FunctionDecl *FD,
CodeGenFunction *CGF) {
assert(((FD && CGF) || (!FD && !CGF)) &&
"Incorrect use - FD and CGF should either be both null or not!");
// Create MDNodes that represent the kernel arg metadata.
// Each MDNode is a list in the form of "key", N number of values which is
// the same number of values as their are kernel arguments.
const PrintingPolicy &Policy = Context.getPrintingPolicy();
// MDNode for the kernel argument address space qualifiers.
SmallVector<llvm::Metadata *, 8> addressQuals;
// MDNode for the kernel argument access qualifiers (images only).
SmallVector<llvm::Metadata *, 8> accessQuals;
// MDNode for the kernel argument type names.
SmallVector<llvm::Metadata *, 8> argTypeNames;
// MDNode for the kernel argument base type names.
SmallVector<llvm::Metadata *, 8> argBaseTypeNames;
// MDNode for the kernel argument type qualifiers.
SmallVector<llvm::Metadata *, 8> argTypeQuals;
// MDNode for the kernel argument names.
SmallVector<llvm::Metadata *, 8> argNames;
if (FD && CGF)
for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) {
const ParmVarDecl *parm = FD->getParamDecl(i);
QualType ty = parm->getType();
std::string typeQuals;
if (ty->isPointerType()) {
QualType pointeeTy = ty->getPointeeType();
// Get address qualifier.
addressQuals.push_back(
llvm::ConstantAsMetadata::get(CGF->Builder.getInt32(
ArgInfoAddressSpace(pointeeTy.getAddressSpace()))));
// Get argument type name.
std::string typeName =
pointeeTy.getUnqualifiedType().getAsString(Policy) + "*";
// Turn "unsigned type" to "utype"
std::string::size_type pos = typeName.find("unsigned");
if (pointeeTy.isCanonical() && pos != std::string::npos)
typeName.erase(pos + 1, 8);
argTypeNames.push_back(llvm::MDString::get(VMContext, typeName));
std::string baseTypeName =
pointeeTy.getUnqualifiedType().getCanonicalType().getAsString(
Policy) +
"*";
// Turn "unsigned type" to "utype"
pos = baseTypeName.find("unsigned");
if (pos != std::string::npos)
baseTypeName.erase(pos + 1, 8);
argBaseTypeNames.push_back(
llvm::MDString::get(VMContext, baseTypeName));
// Get argument type qualifiers:
if (ty.isRestrictQualified())
typeQuals = "restrict";
if (pointeeTy.isConstQualified() ||
(pointeeTy.getAddressSpace() == LangAS::opencl_constant))
typeQuals += typeQuals.empty() ? "const" : " const";
if (pointeeTy.isVolatileQualified())
typeQuals += typeQuals.empty() ? "volatile" : " volatile";
} else {
uint32_t AddrSpc = 0;
bool isPipe = ty->isPipeType();
if (ty->isImageType() || isPipe)
AddrSpc = ArgInfoAddressSpace(LangAS::opencl_global);
addressQuals.push_back(
llvm::ConstantAsMetadata::get(CGF->Builder.getInt32(AddrSpc)));
// Get argument type name.
std::string typeName;
if (isPipe)
typeName = ty.getCanonicalType()
->getAs<PipeType>()
->getElementType()
.getAsString(Policy);
else
typeName = ty.getUnqualifiedType().getAsString(Policy);
// Turn "unsigned type" to "utype"
std::string::size_type pos = typeName.find("unsigned");
if (ty.isCanonical() && pos != std::string::npos)
typeName.erase(pos + 1, 8);
std::string baseTypeName;
if (isPipe)
baseTypeName = ty.getCanonicalType()
->getAs<PipeType>()
->getElementType()
.getCanonicalType()
.getAsString(Policy);
else
baseTypeName =
ty.getUnqualifiedType().getCanonicalType().getAsString(Policy);
// Remove access qualifiers on images
// (as they are inseparable from type in clang implementation,
// but OpenCL spec provides a special query to get access qualifier
// via clGetKernelArgInfo with CL_KERNEL_ARG_ACCESS_QUALIFIER):
if (ty->isImageType()) {
removeImageAccessQualifier(typeName);
removeImageAccessQualifier(baseTypeName);
}
argTypeNames.push_back(llvm::MDString::get(VMContext, typeName));
// Turn "unsigned type" to "utype"
pos = baseTypeName.find("unsigned");
if (pos != std::string::npos)
baseTypeName.erase(pos + 1, 8);
argBaseTypeNames.push_back(
llvm::MDString::get(VMContext, baseTypeName));
if (isPipe)
typeQuals = "pipe";
}
argTypeQuals.push_back(llvm::MDString::get(VMContext, typeQuals));
// Get image and pipe access qualifier:
if (ty->isImageType() || ty->isPipeType()) {
const Decl *PDecl = parm;
if (auto *TD = dyn_cast<TypedefType>(ty))
PDecl = TD->getDecl();
const OpenCLAccessAttr *A = PDecl->getAttr<OpenCLAccessAttr>();
if (A && A->isWriteOnly())
accessQuals.push_back(llvm::MDString::get(VMContext, "write_only"));
else if (A && A->isReadWrite())
accessQuals.push_back(llvm::MDString::get(VMContext, "read_write"));
else
accessQuals.push_back(llvm::MDString::get(VMContext, "read_only"));
} else
accessQuals.push_back(llvm::MDString::get(VMContext, "none"));
// Get argument name.
argNames.push_back(llvm::MDString::get(VMContext, parm->getName()));
}
Fn->setMetadata("kernel_arg_addr_space",
llvm::MDNode::get(VMContext, addressQuals));
Fn->setMetadata("kernel_arg_access_qual",
llvm::MDNode::get(VMContext, accessQuals));
Fn->setMetadata("kernel_arg_type",
llvm::MDNode::get(VMContext, argTypeNames));
Fn->setMetadata("kernel_arg_base_type",
llvm::MDNode::get(VMContext, argBaseTypeNames));
Fn->setMetadata("kernel_arg_type_qual",
llvm::MDNode::get(VMContext, argTypeQuals));
if (getCodeGenOpts().EmitOpenCLArgMetadata)
Fn->setMetadata("kernel_arg_name",
llvm::MDNode::get(VMContext, argNames));
}
/// Determines whether the language options require us to model
/// unwind exceptions. We treat -fexceptions as mandating this
/// except under the fragile ObjC ABI with only ObjC exceptions

View File

@ -1315,6 +1315,19 @@ public:
llvm::Value *
createOpenCLIntToSamplerConversion(const Expr *E, CodeGenFunction &CGF);
/// OpenCL v1.2 s5.6.4.6 allows the compiler to store kernel argument
/// information in the program executable. The argument information stored
/// includes the argument name, its type, the address and access qualifiers
/// used. This helper can be used to generate metadata for source code kernel
/// function as well as generated implicitly kernels. If a kernel is generated
/// implicitly null value has to be passed to the last two parameters,
/// otherwise all parameters must have valid non-null values.
/// \param FN is a pointer to IR function being generated.
/// \param FD is a pointer to function declaration if any.
/// \param CGF is a pointer to CodeGenFunction that generates this function.
void GenOpenCLArgMetadata(llvm::Function *Fn, const FunctionDecl *FD=nullptr,
CodeGenFunction *CGF=nullptr);
/// Get target specific null pointer.
/// \param T is the LLVM type of the null pointer.
/// \param QT is the clang QualType of the null pointer.

View File

@ -0,0 +1,11 @@
// RUN: %clang_cc1 %s -triple spir -cl-std=c++ -emit-llvm -O0 -o - | FileCheck %s
struct S {
S() {}
};
S s;
//CHECK: define internal spir_kernel void @_GLOBAL__sub_I_{{.*}}!kernel_arg_addr_space [[ARGMD:![0-9]+]] !kernel_arg_access_qual [[ARGMD]] !kernel_arg_type [[ARGMD]] !kernel_arg_base_type [[ARGMD]] !kernel_arg_type_qual [[ARGMD]]
// Check that parameters are empty.
//CHECK: [[ARGMD]] = !{}