[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:
parent
9db0e72570
commit
e6cf6c78f8
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]] = !{}
|
Loading…
Reference in New Issue