Skip to content

Commit

Permalink
[OpenCL] Make global ctor init function a kernel
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Anastasia Stulova committed May 9, 2019
1 parent 9db0e72 commit e6cf6c7
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 200 deletions.
13 changes: 13 additions & 0 deletions clang/lib/CodeGen/CGDeclCXX.cpp
Expand Up @@ -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();
}

Expand Down
201 changes: 1 addition & 200 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Expand Up @@ -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)
{
Expand All @@ -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();
Expand Down

0 comments on commit e6cf6c7

Please sign in to comment.