From 7974c35e0e838ed98b85ccaf7abceb67cd01c860 Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Mon, 8 Jun 2020 17:02:36 -0700 Subject: [PATCH 01/42] Support for arrays as kernel parameters. Signed-off-by: rdeodhar --- clang/lib/Sema/SemaSYCL.cpp | 201 ++++++-- .../CodeGenSYCL/kernel-param-acc-array-ih.cpp | 55 +++ .../CodeGenSYCL/kernel-param-acc-array.cpp | 74 +++ .../kernel-param-member-acc-array-ih.cpp | 58 +++ .../kernel-param-member-acc-array.cpp | 84 ++++ .../CodeGenSYCL/kernel-param-pod-array-ih.cpp | 51 ++ .../CodeGenSYCL/kernel-param-pod-array.cpp | 38 ++ .../test/SemaSYCL/array-kernel-param-neg.cpp | 56 +++ clang/test/SemaSYCL/array-kernel-param.cpp | 94 ++++ sycl/doc/Array_Kernel_Parameters.md | 435 ++++++++++++++++++ .../array_param/array-kernel-param-run.cpp | 250 ++++++++++ 11 files changed, 1350 insertions(+), 46 deletions(-) create mode 100755 clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp create mode 100644 clang/test/CodeGenSYCL/kernel-param-acc-array.cpp create mode 100644 clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp create mode 100644 clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp create mode 100755 clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp create mode 100755 clang/test/CodeGenSYCL/kernel-param-pod-array.cpp create mode 100755 clang/test/SemaSYCL/array-kernel-param-neg.cpp create mode 100755 clang/test/SemaSYCL/array-kernel-param.cpp create mode 100755 sycl/doc/Array_Kernel_Parameters.md create mode 100755 sycl/test/array_param/array-kernel-param-run.cpp diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index fb420be750a29..5e5ef9a1ffabc 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -878,7 +878,6 @@ template class SyclKernelFieldHandler { virtual void leaveField(const CXXRecordDecl *, const CXXBaseSpecifier &) {} virtual void enterField(const CXXRecordDecl *, FieldDecl *) {} virtual void leaveField(const CXXRecordDecl *, FieldDecl *) {} - virtual void enterArray(const CXXBaseSpecifier &) {} virtual void enterArray() {} virtual void nextElement(QualType) {} virtual void leaveArray(QualType, int64_t) {} @@ -897,10 +896,10 @@ class SyclKernelFieldChecker if (const auto *CAT = dyn_cast(FieldTy)) { QualType ET = CAT->getElementType(); return checkNotCopyableToKernel(FD, ET); - } else - return Diag.Report(FD->getLocation(), - diag::err_sycl_non_constant_array_type) - << FieldTy; + } + return Diag.Report(FD->getLocation(), + diag::err_sycl_non_constant_array_type) + << FieldTy; } if (SemaRef.getASTContext().getLangOpts().SYCLStdLayoutKernelParams) @@ -1125,6 +1124,30 @@ class SyclKernelDeclCreator return true; } + bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { + // if (!Util::isArrayOfSpecialSyclType(FieldTy)) { + if (!cast(FieldTy) + ->getElementType() + ->isStructureOrClassType()) { + // Wrap the array in a struct. + RecordDecl *NewClass = + SemaRef.getASTContext().buildImplicitRecord("wrapped_array"); + NewClass->startDefinition(); + FieldDecl *Field = FieldDecl::Create( + SemaRef.getASTContext(), NewClass, SourceLocation(), SourceLocation(), + /*Id=*/nullptr, FieldTy, + SemaRef.getASTContext().getTrivialTypeSourceInfo(FieldTy, + SourceLocation()), + /*BW=*/nullptr, /*Mutable=*/false, /*InitStyle=*/ICIS_NoInit); + Field->setAccess(AS_public); + NewClass->addDecl(Field); + NewClass->completeDefinition(); + QualType ST = SemaRef.getASTContext().getRecordType(NewClass); + addParam(FD, ST); + } + return true; + } + bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy); return true; @@ -1260,6 +1283,41 @@ class SyclKernelBodyCreator InitExprs.push_back(MemberInit.get()); } + void createExprForArray(FieldDecl *FD) { + ParmVarDecl *KernelParameter = + DeclCreator.getParamVarDeclsForCurrentField()[0]; + QualType ParamType = KernelParameter->getOriginalType(); + CXXRecordDecl *WrapperStruct = ParamType->getAsCXXRecordDecl(); + // The first and only field of the wrapper struct is the array + FieldDecl *Array = *(WrapperStruct->field_begin()); + auto DRE = DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), + SourceLocation(), KernelParameter, false, + DeclarationNameInfo(), ParamType, VK_LValue); + DeclAccessPair ArrayDAP = DeclAccessPair::make(Array, AS_none); + Expr *InitExpr = MemberExpr::Create( + SemaRef.Context, DRE, false, SourceLocation(), NestedNameSpecifierLoc(), + SourceLocation(), Array, ArrayDAP, + DeclarationNameInfo(Array->getDeclName(), SourceLocation()), nullptr, + Array->getType(), VK_LValue, OK_Ordinary, NOUR_None); + InitializationKind InitKind = InitializationKind::CreateDirect( + SourceLocation(), SourceLocation(), SourceLocation()); + InitializedEntity Entity = InitializedEntity::InitializeLambdaCapture( + nullptr, Array->getType(), SourceLocation()); + InitializationSequence InitSeq(SemaRef, Entity, InitKind, InitExpr); + ExprResult MemberInit = + InitSeq.Perform(SemaRef, Entity, InitKind, InitExpr); + InitExprs.push_back(MemberInit.get()); + } + + void createExprForArrayElement(size_t ArrayIndex) { + Expr *ArrayBase = MemberExprBases.back(); + ExprResult IndexExpr = + SemaRef.ActOnIntegerConstant(SourceLocation(), ArrayIndex); + ExprResult ElementBase = SemaRef.CreateBuiltinArraySubscriptExpr( + ArrayBase, SourceLocation(), IndexExpr.get(), SourceLocation()); + MemberExprBases.push_back(ElementBase.get()); + } + void createSpecialMethodCall(const CXXRecordDecl *SpecialClass, Expr *Base, const std::string &MethodName, FieldDecl *Field) { @@ -1276,9 +1334,7 @@ class SyclKernelBodyCreator ParamDREs[I] = SemaRef.BuildDeclRefExpr(KernelParameters[I], ParamType, VK_LValue, SourceLocation()); } - - MemberExpr *SpecialObjME = BuildMemberExpr(Base, Field); - MemberExpr *MethodME = BuildMemberExpr(SpecialObjME, Method); + MemberExpr *MethodME = BuildMemberExpr(Base, Method); QualType ResultTy = Method->getReturnType(); ExprValueKind VK = Expr::getValueKindForType(ResultTy); @@ -1313,7 +1369,7 @@ class SyclKernelBodyCreator bool handleSpecialType(FieldDecl *FD, QualType Ty) { const auto *RecordDecl = Ty->getAsCXXRecordDecl(); // Perform initialization only if it is field of kernel object - if (MemberExprBases.size() == 1) { + if (MemberExprBases.size() == 2) { InitializedEntity Entity = InitializedEntity::InitializeMember(FD, &VarEntity); // Initialize with the default constructor. @@ -1371,9 +1427,10 @@ class SyclKernelBodyCreator bool handleSyclStreamType(FieldDecl *FD, QualType Ty) final { const auto *StreamDecl = Ty->getAsCXXRecordDecl(); createExprForStructOrScalar(FD); - createSpecialMethodCall(StreamDecl, MemberExprBases.back(), InitMethodName, - FD); - createSpecialMethodCall(StreamDecl, MemberExprBases.back(), + size_t NumBases = MemberExprBases.size(); + createSpecialMethodCall(StreamDecl, MemberExprBases[NumBases - 2], + InitMethodName, FD); + createSpecialMethodCall(StreamDecl, MemberExprBases[NumBases - 2], FinalizeMethodName, FD); return true; } @@ -1399,16 +1456,53 @@ class SyclKernelBodyCreator return true; } - void enterStruct(const CXXRecordDecl *, FieldDecl *FD) final { - MemberExprBases.push_back(BuildMemberExpr(MemberExprBases.back(), FD)); + bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { + if (!cast(FieldTy) + ->getElementType() + ->isStructureOrClassType()) { + createExprForArray(FD); + } + return true; } - void leaveStruct(const CXXRecordDecl *, FieldDecl *FD) final { - MemberExprBases.pop_back(); + void enterField(const CXXRecordDecl *RD, FieldDecl *FD) final { + if (!FD->getType()->isReferenceType()) + MemberExprBases.push_back(BuildMemberExpr(MemberExprBases.back(), FD)); } - using SyclKernelFieldHandler::enterStruct; - using SyclKernelFieldHandler::leaveStruct; + void leaveField(const CXXRecordDecl *, FieldDecl *FD) final { + if (!FD->getType()->isReferenceType()) + MemberExprBases.pop_back(); + } + + void enterArray() final { + Expr *ArrayBase = MemberExprBases.back(); + ExprResult IndexExpr = SemaRef.ActOnIntegerConstant(SourceLocation(), 0); + ExprResult ElementBase = SemaRef.CreateBuiltinArraySubscriptExpr( + ArrayBase, SourceLocation(), IndexExpr.get(), SourceLocation()); + MemberExprBases.push_back(ElementBase.get()); + } + + void nextElement(QualType) final { + ArraySubscriptExpr *LastArrayRef = + dyn_cast(MemberExprBases.back()); + MemberExprBases.pop_back(); + Expr *LastIdx = LastArrayRef->getIdx(); + llvm::APSInt Result; + SemaRef.VerifyIntegerConstantExpression(LastIdx, &Result); + Expr *ArrayBase = MemberExprBases.back(); + ExprResult IndexExpr = SemaRef.ActOnIntegerConstant( + SourceLocation(), Result.getExtValue() + 1); + ExprResult ElementBase = SemaRef.CreateBuiltinArraySubscriptExpr( + ArrayBase, SourceLocation(), IndexExpr.get(), SourceLocation()); + MemberExprBases.push_back(ElementBase.get()); + } + + void leaveArray(QualType, int64_t) final { MemberExprBases.pop_back(); } + + using SyclKernelFieldHandler::enterArray; + using SyclKernelFieldHandler::enterField; + using SyclKernelFieldHandler::leaveField; }; class SyclKernelIntHeaderCreator @@ -1419,23 +1513,20 @@ class SyclKernelIntHeaderCreator const CXXRecordDecl *CurStruct = nullptr; int64_t CurOffset = 0; - uint64_t getOffset(const CXXRecordDecl *RD) const { - assert(CurOffset && - "Cannot have a base class without setting the active struct"); - const ASTRecordLayout &Layout = - SemaRef.getASTContext().getASTRecordLayout(CurStruct); - return CurOffset + Layout.getBaseClassOffset(RD).getQuantity(); - } - uint64_t getOffset(const FieldDecl *FD) const { - return CurOffset + SemaRef.getASTContext().getFieldOffset(FD) / 8; - } - - void addParam(const FieldDecl *FD, QualType FieldTy, + void addParam(const FieldDecl *FD, QualType ArgTy, SYCLIntegrationHeader::kernel_param_kind_t Kind) { - uint64_t Size = - SemaRef.getASTContext().getTypeSizeInChars(FieldTy).getQuantity(); + uint64_t Size; + const ConstantArrayType *CAT = + SemaRef.getASTContext().getAsConstantArrayType(ArgTy); + if (CAT) { + QualType ET = CAT->getElementType(); + Size = static_cast(CAT->getSize().getZExtValue()) * + SemaRef.getASTContext().getTypeSizeInChars(ET).getQuantity(); + } else { + Size = SemaRef.getASTContext().getTypeSizeInChars(ArgTy).getQuantity(); + } Header.addParamDesc(Kind, static_cast(Size), - static_cast(getOffset(FD))); + static_cast(CurOffset)); } public: @@ -1456,8 +1547,7 @@ class SyclKernelIntHeaderCreator int Dims = static_cast( AccTy->getTemplateArgs()[1].getAsIntegral().getExtValue()); int Info = getAccessTarget(AccTy) | (Dims << 11); - Header.addParamDesc(SYCLIntegrationHeader::kind_accessor, Info, - getOffset(BC.getType()->getAsCXXRecordDecl())); + Header.addParamDesc(SYCLIntegrationHeader::kind_accessor, Info, CurOffset); return true; } @@ -1469,8 +1559,7 @@ class SyclKernelIntHeaderCreator int Dims = static_cast( AccTy->getTemplateArgs()[1].getAsIntegral().getExtValue()); int Info = getAccessTarget(AccTy) | (Dims << 11); - Header.addParamDesc(SYCLIntegrationHeader::kind_accessor, Info, - getOffset(FD)); + Header.addParamDesc(SYCLIntegrationHeader::kind_accessor, Info, CurOffset); return true; } @@ -1511,10 +1600,21 @@ class SyclKernelIntHeaderCreator addParam(FD, FieldTy, SYCLIntegrationHeader::kind_pointer); return true; } + + bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { + // if (!Util::isArrayOfSpecialSyclType(FieldTy)) + if (!cast(FieldTy) + ->getElementType() + ->isStructureOrClassType()) + addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); + return true; + } + bool handleStructType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); return true; } + bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); return true; @@ -1524,6 +1624,7 @@ class SyclKernelIntHeaderCreator addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); return true; } + bool handleSyclStreamType(const CXXBaseSpecifier &BC, QualType FieldTy) final { // FIXME SYCL stream should be usable as a base type @@ -1531,32 +1632,40 @@ class SyclKernelIntHeaderCreator return true; } - // Keep track of the current struct offset. - void enterStruct(const CXXRecordDecl *RD, FieldDecl *FD) final { - CurStruct = FD->getType()->getAsCXXRecordDecl(); + void enterField(const CXXRecordDecl *RD, FieldDecl *FD) final { CurOffset += SemaRef.getASTContext().getFieldOffset(FD) / 8; } - void leaveStruct(const CXXRecordDecl *RD, FieldDecl *FD) final { - CurStruct = RD; + void leaveField(const CXXRecordDecl *, FieldDecl *FD) final { CurOffset -= SemaRef.getASTContext().getFieldOffset(FD) / 8; } - void enterStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { - CurStruct = BS.getType()->getAsCXXRecordDecl(); + void enterField(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { const ASTRecordLayout &Layout = SemaRef.getASTContext().getASTRecordLayout(RD); CurOffset += Layout.getBaseClassOffset(BS.getType()->getAsCXXRecordDecl()) .getQuantity(); } - void leaveStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { - CurStruct = RD; + void leaveField(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { const ASTRecordLayout &Layout = SemaRef.getASTContext().getASTRecordLayout(RD); CurOffset -= Layout.getBaseClassOffset(BS.getType()->getAsCXXRecordDecl()) .getQuantity(); } + + void nextElement(QualType ET) final { + CurOffset += SemaRef.getASTContext().getTypeSizeInChars(ET).getQuantity(); + } + + void leaveArray(QualType ET, int64_t Count) final { + int64_t ArraySize = + SemaRef.getASTContext().getTypeSizeInChars(ET).getQuantity(); + if (!ET->isArrayType()) { + ArraySize *= Count; + } + CurOffset -= ArraySize; + } }; } // namespace diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp new file mode 100755 index 0000000000000..fbaffee9f9b92 --- /dev/null +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp @@ -0,0 +1,55 @@ +// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv +// RUN: FileCheck -input-file=%t.h %s + +// This test checks the integration header generated when +// the kernel argument is an Accessor array. + +// CHECK: #include + +// CHECK: class kernel_A; + +// CHECK: __SYCL_INLINE_NAMESPACE(cl) { +// CHECK-NEXT: namespace sycl { +// CHECK-NEXT: namespace detail { + +// CHECK: static constexpr +// CHECK-NEXT: const char* const kernel_names[] = { +// CHECK-NEXT: "_ZTSZ4mainE8kernel_A" +// CHECK-NEXT: }; + +// CHECK: static constexpr +// CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { +// CHECK-NEXT: //--- _ZTSZ4mainE8kernel_A +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 0 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 12 }, +// CHECK-EMPTY: +// CHECK-NEXT: }; + +// CHECK: static constexpr +// CHECK-NEXT: const unsigned kernel_signature_start[] = { +// CHECK-NEXT: 0 // _ZTSZ4mainE8kernel_A +// CHECK-NEXT: }; + +// CHECK: template <> struct KernelInfo { + +#include + +using namespace cl::sycl; + +template +__attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { + kernelFunc(); +} + +int main() { + + using Accessor = + accessor; + + Accessor acc[2]; + + a_kernel( + [=]() { + acc[1].use(); + }); +} diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp new file mode 100644 index 0000000000000..09d1f48f86907 --- /dev/null +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s + +// This test checks a kernel argument that is an Accessor array + +#include + +using namespace cl::sycl; + +template +__attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { + kernelFunc(); +} + +int main() { + + using Accessor = + accessor; + Accessor acc[2]; + + a_kernel( + [=]() { + acc[1].use(); + }); +} + +// Check kernel_A parameters +// CHECK: define spir_kernel void @{{.*}}kernel_A +// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG1:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE1:%[a-zA-Z0-9_]+_1]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE1:%[a-zA-Z0-9_]+_2]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET1:%[a-zA-Z0-9_]+_3]], +// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG2:%[a-zA-Z0-9_]+_4]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE2:%[a-zA-Z0-9_]+_6]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE2:%[a-zA-Z0-9_]+_7]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET2:%[a-zA-Z0-9_]+_8]]) + +// Check alloca for pointer arguments +// CHECK: [[MEM_ARG1]].addr{{[0-9]*}} = alloca i32 addrspace(1)*, align 8 +// CHECK: [[MEM_ARG1]].addr{{[0-9]*}} = alloca i32 addrspace(1)*, align 8 + +// Check lambda object alloca +// CHECK: [[LOCAL_OBJECT:%0]] = alloca %"class.{{.*}}.anon", align 4 + +// Check allocas for ranges +// CHECK: [[ACC_RANGE1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" +// CHECK: [[MEM_RANGE1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" +// CHECK: [[OFFSET1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" +// CHECK: [[ACC_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" +// CHECK: [[MEM_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" +// CHECK: [[OFFSET2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" + +// Check accessor array GEP for acc[0] +// CHECK: [[ACCESSOR_ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY1]], i64 0, i64 0 + +// Check load from kernel pointer argument alloca +// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}} + +// Check acc[0] __init method call +// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z0]] to %"class{{.*}}accessor" addrspace(4)* + +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) + +// Check accessor array GEP for acc[1] +// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY2]], i64 0, i64 1 + +// Check load from kernel pointer argument alloca +// CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}} + +// Check acc[1] __init method call +// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)* + +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) diff --git a/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp new file mode 100644 index 0000000000000..649d6fd88f0b8 --- /dev/null +++ b/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp @@ -0,0 +1,58 @@ +// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv +// RUN: FileCheck -input-file=%t.h %s + +// This test checks the integration header when kernel argument +// is a struct containing an Accessor array. + +// CHECK: #include + +// CHECK: class kernel_C; + +// CHECK: __SYCL_INLINE_NAMESPACE(cl) { +// CHECK-NEXT: namespace sycl { +// CHECK-NEXT: namespace detail { + +// CHECK: static constexpr +// CHECK-NEXT: const char* const kernel_names[] = { +// CHECK-NEXT: "_ZTSZ4mainE8kernel_C" +// CHECK-NEXT: }; + +// CHECK: static constexpr +// CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { +// CHECK-NEXT: //--- _ZTSZ4mainE8kernel_C +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 24, 0 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 0 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 12 }, +// CHECK-EMPTY: +// CHECK-NEXT: }; + +// CHECK: static constexpr +// CHECK-NEXT: const unsigned kernel_signature_start[] = { +// CHECK-NEXT: 0 // _ZTSZ4mainE8kernel_C +// CHECK-NEXT: }; + +// CHECK: template <> struct KernelInfo { + +#include + +using namespace cl::sycl; + +template +__attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { + kernelFunc(); +} + +int main() { + + using Accessor = + accessor; + + struct struct_acc_t { + Accessor member_acc[2]; + } struct_acc; + + a_kernel( + [=]() { + struct_acc.member_acc[1].use(); + }); +} diff --git a/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp b/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp new file mode 100644 index 0000000000000..d72691eb0e279 --- /dev/null +++ b/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -fsycl-int-header=%t.h -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s + +// This test checks a kernel with struct parameter that contains an Accessor array. + +#include + +using namespace cl::sycl; + +template +__attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { + kernelFunc(); +} + +int main() { + + using Accessor = + accessor; + + struct struct_acc_t { + Accessor member_acc[2]; + } struct_acc; + + a_kernel( + [=]() { + struct_acc.member_acc[1].use(); + }); +} + +// CHECK kernel_C parameters +// CHECK: define spir_kernel void @{{.*}}kernel_C +// CHECK-SAME: %struct.{{.*}}.struct_acc_t* byval(%struct.{{.*}}.struct_acc_t) align 4 [[STRUCT:%[a-zA-Z0-9_]+]], +// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG1:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1:%[a-zA-Z0-9_]+1]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1:%[a-zA-Z0-9_]+2]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1:%[a-zA-Z0-9_]+3]], +// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG2:%[a-zA-Z0-9_]+4]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2:%[a-zA-Z0-9_]+6]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2:%[a-zA-Z0-9_]+7]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2:%[a-zA-Z0-9_]+8]]) + +// Check alloca for pointer arguments +// CHECK: [[MEM_ARG1]].addr{{[0-9]*}} = alloca i32 addrspace(1)*, align 8 +// CHECK: [[MEM_ARG1]].addr{{[0-9]*}} = alloca i32 addrspace(1)*, align 8 + +// Check lambda object alloca +// CHECK: [[LOCAL_OBJECT:%0]] = alloca %"class.{{.*}}.anon", align 4 + +// Check allocas for ranges +// CHECK: [[ACC_RANGE1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" +// CHECK: [[MEM_RANGE1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" +// CHECK: [[OFFSET1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" +// CHECK: [[ACC_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" +// CHECK: [[MEM_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" +// CHECK: [[OFFSET2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" + +// Check init of local struct +// CHECK: [[L_STRUCT_ADDR:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[MEMCPY_DST:%[0-9a-zA-Z_]+]] = bitcast %struct.{{.*}}struct_acc_t* [[L_STRUCT_ADDR]] to i8* +// CHECK: [[MEMCPY_SRC:%[0-9a-zA-Z_]+]] = bitcast %struct.{{.*}}struct_acc_t* %{{[0-9a-zA-Z_]+}} to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST]], i8* align 4 [[MEMCPY_SRC]], i64 24, i1 false) + +// Check accessor array GEP for member_acc[0] +// CHECK: [[ACCESSOR_ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[MEMBER1:%[a-zA-Z_]+]] = getelementptr inbounds %struct.{{.*}}.struct_acc_t, %struct.{{.*}}.struct_acc_t* [[ACCESSOR_ARRAY1]], i32 0, i32 0 +// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds [2 x %"class.{{.*}}.cl::sycl::accessor"], [2 x %"class.{{.*}}.cl::sycl::accessor"]* [[MEMBER1]], i64 0, i64 0 + +// Check load from kernel pointer argument alloca +// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}} + +// Check acc[0] __init method call +// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z0]] to %"class{{.*}}accessor" addrspace(4)* +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) + +// Check accessor array GEP for member_acc[1] +// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[MEMBER2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct.{{.*}}.struct_acc_t, %struct.{{.*}}.struct_acc_t* [[ACCESSOR_ARRAY2]], i32 0, i32 0 +// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds [2 x %"class.{{.*}}.cl::sycl::accessor"], [2 x %"class.{{.*}}.cl::sycl::accessor"]* [[MEMBER2]], i64 0, i64 1 + +// Check load from kernel pointer argument alloca +// CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}} + +// Check acc[1] __init method call +// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)* +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) diff --git a/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp new file mode 100755 index 0000000000000..799a0fb9183f1 --- /dev/null +++ b/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp @@ -0,0 +1,51 @@ +// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv +// RUN: FileCheck -input-file=%t.h %s + +// This test checks the integration header generated for a kernel +// with an argument that is a POD array. + +// CHECK: #include + +// CHECK: class kernel_B; + +// CHECK: __SYCL_INLINE_NAMESPACE(cl) { +// CHECK-NEXT: namespace sycl { +// CHECK-NEXT: namespace detail { + +// CHECK: static constexpr +// CHECK-NEXT: const char* const kernel_names[] = { +// CHECK-NEXT: "_ZTSZ4mainE8kernel_B" +// CHECK-NEXT: }; + +// CHECK: static constexpr +// CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { +// CHECK-NEXT: //--- _ZTSZ4mainE8kernel_B +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 400, 0 }, +// CHECK-EMPTY: +// CHECK-NEXT: }; + +// CHECK: static constexpr +// CHECK-NEXT: const unsigned kernel_signature_start[] = { +// CHECK-NEXT: 0 // _ZTSZ4mainE8kernel_B +// CHECK-NEXT: }; + +// CHECK: template <> struct KernelInfo { + +#include + +using namespace cl::sycl; + +template +__attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { + kernelFunc(); +} + +int main() { + + int a[100]; + + a_kernel( + [=]() { + int local = a[3]; + }); +} diff --git a/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp b/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp new file mode 100755 index 0000000000000..7549afa16c91d --- /dev/null +++ b/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s + +// This test checks a kernel with an argument that is a POD array. + +#include + +using namespace cl::sycl; + +template +__attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { + kernelFunc(); +} + +int main() { + + int a[100]; + + a_kernel( + [=]() { + int local = a[3]; + }); +} + +// Check kernel_B parameters +// CHECK: define spir_kernel void @{{.*}}kernel_B +// CHECK-SAME: %struct.{{.*}}.wrapped_array* byval{{.*}}align 4 [[ARG_STRUCT:%[a-zA-Z0-9_]+]] + +// Check local lambda object alloca +// CHECK: [[LOCAL_OBJECT:%0]] = alloca %"class.{{.*}}.anon", align 4 + +// Check init of local array +// CHECK: [[ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 + +// CHECK: [[ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct.{{.*}}.wrapped_array, %struct.{{.*}}.wrapped_array* [[ARG_STRUCT]], i32 0, i32 0 + +// CHECK: %{{[a-zA-Z0-9._]+}} = getelementptr inbounds [100 x i32], [100 x i32]* [[ARRAY1]], i64 0, i64 0 + +// CHECK: %{{[a-zA-Z0-9_]+}} = getelementptr inbounds [100 x i32], [100 x i32]* [[ARRAY2]], i64 0, i64 diff --git a/clang/test/SemaSYCL/array-kernel-param-neg.cpp b/clang/test/SemaSYCL/array-kernel-param-neg.cpp new file mode 100755 index 0000000000000..b7f669ecd6671 --- /dev/null +++ b/clang/test/SemaSYCL/array-kernel-param-neg.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -fcxx-exceptions -verify -fsyntax-only %s + +// This test checks if compiler reports compilation error on an attempt to pass +// an array of non-trivially copyable structs as SYCL kernel parameter or +// a non-constant size array. + +struct A { + int i; +}; + +struct B { + int i; + B(int _i) : i(_i) {} + B(const B &x) : i(x.i) {} +}; + +struct C : A { + const A C2; + C() : A{0}, C2{2} {} +}; + +struct D { + int i; + ~D(); +}; + +class E { + // expected-error@+1 {{kernel parameter is not a constant size array}} + int i[]; + +public: + int operator()() { return i[0]; } +}; + +template +__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) { + kernelFunc(); +} + +void test() { + A cs1[10]; + B nsl1[4] = {1, 2, 3, 4}; + C cs2[6]; + D nsl2[5]; + E es; + kernel_single_task([=] { + int a = cs1[6].i; + // expected-error@+1 {{kernel parameter has non-trivially copy constructible class/struct type}} + int b = nsl1[2].i; + int c = cs2[0].i; + // expected-error@+1 {{kernel parameter has non-trivially destructible class/struct type}} + int d = nsl2[4].i; + }); + + kernel_single_task(es); +} diff --git a/clang/test/SemaSYCL/array-kernel-param.cpp b/clang/test/SemaSYCL/array-kernel-param.cpp new file mode 100755 index 0000000000000..a7157f479b997 --- /dev/null +++ b/clang/test/SemaSYCL/array-kernel-param.cpp @@ -0,0 +1,94 @@ +// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -ast-dump %s | FileCheck %s + +// This test checks that compiler generates correct kernel arguments for +// arrays, Accessor arrays, and structs containing Accessors. + +#include + +using namespace cl::sycl; + +template +__attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { + kernelFunc(); +} + +int main() { + + using Accessor = + accessor; + + Accessor acc[2]; + int a[100]; + struct struct_acc_t { + Accessor member_acc[4]; + } struct_acc; + + a_kernel( + [=]() { + acc[1].use(); + }); + + a_kernel( + [=]() { + int local = a[3]; + }); + + a_kernel( + [=]() { + struct_acc.member_acc[2].use(); + }); +} + +// Check kernel_A parameters +// CHECK: FunctionDecl {{.*}}kernel_A{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ '__global int *' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::id<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ '__global int *' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::id<1>' +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}}__init +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}}__init + +// Check kernel_B parameters +// CHECK: FunctionDecl {{.*}}kernel_B{{.*}} 'void (wrapped_array)' +// CHECK-NEXT: ParmVarDecl {{.*}} 'wrapped_array' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: VarDecl +// CHECK-NEXT: InitListExpr +// CHECK-NEXT: ArrayInitLoopExpr {{.*}} 'int [100]' + +// Check kernel_C parameters +// CHECK: FunctionDecl {{.*}}kernel_C{{.*}} 'void (struct {{.*}}, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' +// CHECK-NEXT: ParmVarDecl {{.*}} 'struct {{.*}}' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' + +// Check that four accessor init functions are called +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}}__init +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}}__init +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}}__init +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}}__init diff --git a/sycl/doc/Array_Kernel_Parameters.md b/sycl/doc/Array_Kernel_Parameters.md new file mode 100755 index 0000000000000..22ec0b32c513b --- /dev/null +++ b/sycl/doc/Array_Kernel_Parameters.md @@ -0,0 +1,435 @@ +

Array Parameters of SYCL Kernels

+ +

Introduction

+ +This document describes the changes to support passing arrays to SYCL kernels +and special treatment of Accessor arrays. +The following cases are handled: + +1. arrays of standard-layout type as top-level arguments +2. arrays of Accessors as top-level arguments +3. arrays of accessors within structs that are top-level arguments + +The motivation for this correction to kernel parameters processing is to +bring consistency to the treatment of arrays. +On the CPU, a lambda function is allowed to access an element of an array +defined outside the lambda. The implementation captures the entire array +by value. A user would naturally expect this to work in SYCL as well. +However, the current implementation flags references to arrays defined +outside a SYCL kernel as errors. + +The first few sections describe the current design. +The last three sections describe the design to support 1. to 3. above. +The implementation of this design is confined to three functions in the +file `SemaSYCL.cpp`. + +

A SYCL Kernel

+ +The SYCL constructs `single_task`, `parallel_for`, and +`parallel_for_work_group` each take a function object or a lambda function + as one of their arguments. The code within the function object or +lambda function is executed on the device. +To enable execution of the kernel on OpenCL devices, the lambda/function object +is converted into the format of an OpenCL kernel. + +

SYCL Kernel Code Generation

+ +Consider a source code example that captures an int, a struct and an accessor +by value: + +```C++ +constexpr size_t c_num_items = 10; +range<1> num_items{c_num_items}; // range<1>(num_items) + +int main() +{ + int output[c_num_items]; + queue myQueue; + + int i = 55; + struct S { + int m; + } s = { 66 }; + auto outBuf = buffer(&output[0], num_items); + + myQueue.submit([&](handler &cgh) { + auto outAcc = outBuf.get_access(cgh); + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + outAcc[index] = i + s.m; + }); + }); + + return 0; +} +``` + +The input to the code generation routines is a function object that represents +the kernel. In pseudo-code: + +```C++ +struct Capture { + Accessor outAcc; + int i; + struct S s; + () { + outAcc[index] = i + s.m; + } +} +``` + +On the CPU a call to such a lambda function would look like this: +```C++ +()(struct Capture* this); +``` + +When offloading the kernel to a device, the lambda/function object's +function operator cannot be directly called with a capture object address. +Instead, the code generated for the device is in the form of a +“kernel caller” and a “kernel callee”. +The callee is a clone of the SYCL kernel object. +The caller is generated in the form of an OpenCL kernel function. +It receives the lambda capture object in pieces, assembles the pieces +into the original lambda capture object and then calls the callee: + +```C++ +spir_kernel void caller( + __global int* AccData, // arg1 of Accessor init function + range<1> AccR1, // arg2 of Accessor init function + range<1> AccR2, // arg3 of Accessor init function + id<1> I, // arg4 of Accessor init function + int i, + struct S s +) +{ + // Local capture object + struct Capture local; + + // Reassemble capture object from parts + local.i = i; + local.s = s; + // Call accessor’s init function + Accessor::init(&local.outAcc, AccData, AccR1, AccR2, I); + + // Call the kernel body + callee(&local, id<1> wi); +} + +spir_func void callee(struct Capture* this, id<1> wi) +{ +} +``` + +As may be observed from the example above, standard-layout lambda capture +components are passed by value to the device as separate parameters. +This includes scalars, pointers, and standard-layout structs. +Certain SYCL struct types that are not standard-layout, +such as Accessors and Samplers, are treated specially. +The arguments to their init functions are passed as separate parameters +and used within the kernel caller function to initialize Accessors/Samplers +on the device by calling their init functions using the received arguments. + +There is one other aspect of code generation. An “integration header” +is generated for use during host compilation. +This header file contains entries for each kernel. +Among the items it defines is a table of sizes and offsets of the +kernel parameters. +For the source example above the integration header contains the +following snippet: + +```C++ +// array representing signatures of all kernels defined in the +// corresponding source +static constexpr +const kernel_param_desc_t kernel_signatures[] = { + //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE19->18clES2_E6Worker + { kernel_param_kind_t::kind_accessor, 4062, 0 }, + { kernel_param_kind_t::kind_std_layout, 4, 32 }, + { kernel_param_kind_t::kind_std_layout, 4, 36 }, +}; +``` + +Each entry in the kernel_signatures table contains three values: +1) an encoding of the type of capture object member +2) a field that encodes additional properties, and +3) an offset within a block of memory where the value of that +4) kernel argument is placed. + +The previous sections described how kernel arguments are handled today. +The next three sections describe support for arrays. + +

Fix 1: Kernel Arguments that are Standard-Layout Arrays

+ +As described earlier, each variable captured by a lambda that comprises a +SYCL kernel becomes a parameter of the kernel caller function. +For arrays, simply allowing them through would result in a +function parameter of array type. This is not supported in C++. +Therefore, the array needing capture is wrapped in a struct for +the purposes of passing to the device. Once received on the device +within its wrapper, the array is copied into the local capture object. +All references to the array within the kernel body are directed to +the non-wrapped array which is a member of the local capture object. + +

Source code fragment:

+ +```C++ + int array[100]; + auto outBuf = buffer(&output[0], num_items); + + myQueue.submit([&](handler &cgh) { + auto outAcc = outBuf.get_access(cgh); + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + outAcc[index] = array[index.get(0)]; + }); + }); +``` + +

Integration header produced:

+ +```C++ +static constexpr +const kernel_param_desc_t kernel_signatures[] = { + //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE16->18clES2_E6Worker + { kernel_param_kind_t::kind_accessor, 4062, 0 }, + { kernel_param_kind_t::kind_std_layout, 400, 32 }, +}; +``` + +

The changes to device code made to support this extension, in pseudo-code:

+ +```C++ +struct Capture { + Accessor outAcc; + int array[100]; + () { + // Body + } +} + +struct wrapper { + int array[100]; +}; +spir_kernel void caller( + __global int* AccData, // arg1 of Accessor init function + range<1> AccR1, // arg2 of Accessor init function + range<1> AccR2, // arg3 of Accessor init function + id<1> I, // arg4 of Accessor init function + struct wrapper w_s // Pass the array wrapped in a struct +) +{ + // Local capture object + struct Capture local; + + // Reassemble capture object from parts + // Initialize array using existing clang Initialization mechanisms + local.array = w_s; + // Call accessor’s init function + Accessor::init(&local.outAcc, AccData, AccR1, AccR2, I); + + callee(&local, id<1> wi); +} +``` + +The sharp-eyed reviewer of `SemaSYCL.cpp` will notice that the array +is actually double-wrapped in structs. This was done simply to preserve +the interface to an existing function (`CreateAndAddPrmDsc`) which +processes each kernel caller parameter as a capture object member. +The object being added to a list in `CreateAndAddPrmDsc` is `Fld`, +which is expected to be a field of some struct. So a wrapped struct +cannot be passed to this function. A double-wrapped struct is needed +as shown below. This does not affect the generated code. + +```C++ +struct { + struct { + int array[100]; + } +} +``` + +This could be changed but it would mean changes to the `CreateAndAddPrmDsc` +implementation, to all its callers and to the place where the list created +by it is processed. +By wrapping the array twice, the inner, single-wrapped array appears as a +member of a struct and meets the requirements of the existing code. + +

Fix 2: Kernel Arguments that are Arrays of Accessors

+ +Arrays of accessors are supported in a manner similar to that of a plain +Accessor. For each accessor array element, the four values required to +call its init function are passed as separate arguments to the kernel. +Reassembly within the kernel caller is serialized by accessor array element. + +

Source code fragment:

+ +```C++ + myQueue.submit([&](handler &cgh) { + using Accessor = + accessor; + Accessor inAcc[2] = {in_buffer1.get_access(cgh), + in_buffer2.get_access(cgh)}; + auto outAcc = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + outAcc[index] = inAcc[0][index] + inAcc[1][index]; + }); + }); +``` + +

Integration header:

+ +```C++ +static constexpr +const kernel_param_desc_t kernel_signatures[] = { + //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE20->18clES2_E6Worker + { kernel_param_kind_t::kind_accessor, 4062, 0 }, + { kernel_param_kind_t::kind_accessor, 4062, 32 }, + { kernel_param_kind_t::kind_accessor, 4062, 64 }, +}; +``` + +

Device code generated in pseudo-code form:

+ +```C++ +struct Capture { + Accessor outAcc; + Accessor inAcc[2]; + () { + // Body + } +} + +spir_kernel void caller( + __global int* outAccData, // args of OutAcc + range<1> outAccR1, + range<1> outAccR2, + id<1> outI, + __global int* inAccData_0, // args of inAcc[0] + range<1> inAccR1_0, + range<1> inAccR2_0, + id<1> inI_0, + __global int* inAccData_1, // args of inAcc[1] + range<1> inAccR1_1, + range<1> inAccR2_1, + id<1> inI_1, +) +{ + // Local capture object + struct Capture local; + + // Reassemble capture object from parts + // Call outAcc accessor’s init function + Accessor::init(&local.outAcc, outAccData, outAccR1, outAccR2, outI); + + // Call inAcc[0] accessor’s init function + Accessor::init(&local.inAcc[0], inAccData_0, inAccR1_0, inAccR2_0, inI_0); + + // Call inAcc[1] accessor’s init function + Accessor::init(&local.inAcc[1], inAccData_1, inAccR1_1, inAccR2_1, inI_1); + + callee(&local, id<1> wi); +} +``` + +

Fix 3: Accessor Arrays within Structs

+ +*Individual* Accessors within structs were already supported. +Struct parameters of kernels that are structs are traversed member +by member, recursively, to enumerate member structs that are one of +the SYCL special types: Accessors and Samplers. For each special +struct encountered in the scan, arguments of their init functions +are added as separate arguments to the kernel. +However, *arrays* of accessors within structs were not supported. +Building on the support for single Accessors within structs, +the extension to arrays of Accessors/Samplers within structs +is straightforward. Each element of such arrays is treated as +an individual object, and the arguments of its init function +are added to the kernel arguments in sequence. +Within the kernel caller function, the lambda object is reassembled +in a manner similar to other instances of Accessor arrays. + + +

Source code fragment:

+ +```C++ + myQueue.submit([&](handler &cgh) { + using Accessor = + accessor; + struct S { + int m; + Accessor inAcc[2]; + } s = { 55, + {in_buffer1.get_access(cgh), + in_buffer2.get_access(cgh)} + }; + auto outAcc = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + outAcc[index] = s.m + s.inAcc[0][index] + s.inAcc[1][index]; + }); +}); +``` + +

Integration header:

+ +```C++ +static constexpr +const kernel_param_desc_t kernel_signatures[] = { + //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE20->18clES2_E6Worker + { kernel_param_kind_t::kind_accessor, 4062, 0 }, + { kernel_param_kind_t::kind_std_layout, 72, 32 }, + { kernel_param_kind_t::kind_accessor, 4062, 40 }, + { kernel_param_kind_t::kind_accessor, 4062, 72 }, + +}; +``` + +

Device code generated in pseudo-code form:

+ +```C++ +struct Capture { + Accessor outAcc; + struct S s; + () { + // Body + } +} + +spir_kernel void caller( + __global int* outAccData, // args of OutAcc + range<1> outAccR1, + range<1> outAccR2, + id<1> outI, + struct S s, // the struct S + __global int* inAccData_0, // args of s.inAcc[0] + range<1> inAccR1_0, + range<1> inAccR2_0, + id<1> inI_0, + __global int* inAccData_1, // args of s.inAcc[1] + range<1> inAccR1_1, + range<1> inAccR2_1, + id<1> inI_1, +) +{ + // Local capture object + struct Capture local; + + // Reassemble capture object from parts + // Copy struct argument contents to local copy + // Accessor array will be initialized by calling init functions + local.s = s; + + // Call outAcc accessor’s init function + Accessor::init( + &local.outAcc, outAccData, outAccR1, outAccR2, outI); + + // Call s.inAcc[0] accessor’s init function + Accessor::init( + &local.s.inAcc[0], inAccData_0, inAccR1_0, inAccR2_0, inI_0); + + // Call s.inAcc[1] accessor’s init function + Accessor::init( + &local.s.inAcc[1], inAccData_1, inAccR1_1, inAccR2_1, inI_1); + + callee(&local, id<1> wi); +} +``` diff --git a/sycl/test/array_param/array-kernel-param-run.cpp b/sycl/test/array_param/array-kernel-param-run.cpp new file mode 100755 index 0000000000000..179901a072985 --- /dev/null +++ b/sycl/test/array_param/array-kernel-param-run.cpp @@ -0,0 +1,250 @@ +// This test checks kernel execution with array kernel parameters. + +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: env SYCL_DEVICE_TYPE=HOST %t.out +// RUN: %CPU_RUN_PLACEHOLDER %t.out +// RUN: %GPU_RUN_PLACEHOLDER %t.out +// RUN: %ACC_RUN_PLACEHOLDER %t.out + +#include +#include + +using namespace cl::sycl; + +constexpr size_t c_num_items = 100; +range<1> num_items{c_num_items}; // range<1>(num_items) + +// Change if tests are added/removed +static int testCount = 5; +static int passCount; + +template +static bool verify_1D(const char *name, int X, T A, T A_ref) { + int ErrCnt = 0; + + for (int i = 0; i < X; i++) { + if (A_ref[i] != A[i]) { + if (++ErrCnt < 10) { + std::cout << name << " mismatch at " << i << ". Expected " << A_ref[i] + << " result is " << A[i] << "\n"; + } + } + } + + if (ErrCnt == 0) { + return true; + } + std::cout << " Failed. Failure rate: " << ErrCnt << "/" << X << "(" + << ErrCnt / (float)X * 100.f << "%)\n"; + return false; +} + +template +void init(T &A, int value, int increment) { + for (int i = 0; i < c_num_items; i++) { + A[i] = value; + value += increment; + } +} + +bool test_one_array(queue &myQueue) { + int input1[c_num_items]; + int output[c_num_items]; + int ref[c_num_items]; + init(input1, 1, 1); + init(output, 51, 1); + init(ref, 2, 1); + + auto out_buffer = buffer(&output[0], num_items); + + myQueue.submit([&](handler &cgh) { + auto output_accessor = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + output_accessor[index] = input1[index] + 1; + }); + }); + const auto HostAccessor = out_buffer.get_access(); + + return verify_1D("One array", c_num_items, output, ref); +} + +bool test_two_arrays(queue &myQueue) { + int input1[c_num_items]; + int input2[c_num_items]; + int output[c_num_items]; + int ref[c_num_items]; + init(input1, 1, 1); + init(input2, 22, 1); + init(ref, 23, 2); + + auto out_buffer = buffer(&output[0], num_items); + + myQueue.submit([&](handler &cgh) { + auto output_accessor = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + output_accessor[index] = input1[index] + input2[index]; + }); + }); + const auto HostAccessor = out_buffer.get_access(); + + return verify_1D("Two arrays", c_num_items, output, ref); +} + +bool test_accessor_arrays_1(queue &myQueue) { + std::array input1; + std::array input2; + std::array ref; + init(input1, 1, 1); + init(input2, 22, 1); + init(ref, 24, 1); + + auto in_buffer1 = buffer(input1.data(), num_items); + auto in_buffer2 = buffer(input2.data(), num_items); + + myQueue.submit([&](handler &cgh) { + using Accessor = + accessor; + Accessor a[2] = { + in_buffer1.get_access(cgh), + in_buffer2.get_access(cgh), + }; + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + a[0][index] = a[1][index] + 2; + }); + }); + const auto HostAccessor = in_buffer1.get_access(); + + return verify_1D>("Accessor arrays 1", c_num_items, input1, ref); +} + +bool test_accessor_arrays_2(queue &myQueue) { + std::array input1; + std::array input2; + std::array output; + std::array ref; + init(input1, 1, 1); + init(input2, 22, 1); + init(ref, 23, 2); + + auto in_buffer1 = buffer(input1.data(), num_items); + auto in_buffer2 = buffer(input2.data(), num_items); + auto out_buffer = buffer(output.data(), num_items); + + myQueue.submit([&](handler &cgh) { + using Accessor = + accessor; + Accessor a[4] = {in_buffer1.get_access(cgh), + in_buffer2.get_access(cgh), + in_buffer1.get_access(cgh), + in_buffer2.get_access(cgh)}; + auto output_accessor = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + output_accessor[index] = a[0][index] + a[3][index]; + }); + }); + const auto HostAccessor = out_buffer.get_access(); + + return verify_1D>("Accessor arrays 2", c_num_items, output, ref); +} + +bool test_accessor_array_in_struct(queue &myQueue) { + std::array input1; + std::array input2; + std::array output; + std::array ref; + init(input1, 1, 1); + init(input2, 22, 1); + init(ref, 35, 2); + + auto in_buffer1 = buffer(input1.data(), num_items); + auto in_buffer2 = buffer(input2.data(), num_items); + auto out_buffer = buffer(output.data(), num_items); + + myQueue.submit([&](handler &cgh) { + using Accessor = + accessor; + struct S { + int w; + int x; + Accessor a[2]; + int y; + int z; + } S = { + 3, 3, {in_buffer1.get_access(cgh), in_buffer2.get_access(cgh)}, 7, 7}; + auto output_accessor = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + S.a[0][index]++; + S.a[1][index]++; + output_accessor[index] = S.a[0][index] + S.a[1][index] + S.x + S.y; + }); + }); + const auto HostAccessor = out_buffer.get_access(); + + return verify_1D("Accessor array in struct", c_num_items, output, ref); +} + +bool run_tests() { + queue Q([](exception_list L) { + for (auto ep : L) { + try { + std::rethrow_exception(ep); + } catch (std::exception &E) { + std::cout << "*** std exception caught:\n"; + std::cout << E.what(); + } catch (cl::sycl::exception const &E1) { + std::cout << "*** SYCL exception caught:\n"; + std::cout << E1.what(); + } + } + }); + + passCount = 0; + if (test_one_array(Q)) { + ++passCount; + } + if (test_two_arrays(Q)) { + ++passCount; + } + if (test_accessor_arrays_1(Q)) { + ++passCount; + } + if (test_accessor_arrays_2(Q)) { + ++passCount; + } + if (test_accessor_array_in_struct(Q)) { + ++passCount; + } + + auto D = Q.get_device(); + const char *devType = D.is_host() ? "Host" : D.is_cpu() ? "CPU" : "GPU"; + std::cout << passCount << " of " << testCount << " tests passed on " + << devType << "\n"; + + return (testCount == passCount); +} + +int main(int argc, char *argv[]) { + bool passed = true; + default_selector selector{}; + auto D = selector.select_device(); + const char *devType = D.is_host() ? "Host" : D.is_cpu() ? "CPU" : "GPU"; + std::cout << "Running on device " << devType << " (" + << D.get_info() << ")\n"; + try { + passed &= run_tests(); + } catch (exception e) { + std::cout << e.what(); + } + + if (!passed) { + std::cout << "FAILED\n"; + return 1; + } + std::cout << "PASSED\n"; + return 0; +} From 49071944ac7bf5220c1b50481d6f53cc76baf7df Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Tue, 9 Jun 2020 12:37:39 -0700 Subject: [PATCH 02/42] Reusing some memberexpr building code. --- clang/lib/Sema/SemaSYCL.cpp | 45 +++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 5e5ef9a1ffabc..3495fd2b13a3e 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1035,6 +1035,23 @@ class SyclKernelDeclCreator return true; } + // Create a new class around a field - used to wrap arrays. + RecordDecl *wrapAnArray(const QualType ArgTy, FieldDecl *Field) { + RecordDecl *NewClass = + SemaRef.getASTContext().buildImplicitRecord("wrapped_array"); + NewClass->startDefinition(); + Field = FieldDecl::Create( + SemaRef.getASTContext(), NewClass, SourceLocation(), SourceLocation(), + /*Id=*/nullptr, ArgTy, + SemaRef.getASTContext().getTrivialTypeSourceInfo(ArgTy, + SourceLocation()), + /*BW=*/nullptr, /*Mutable=*/false, /*InitStyle=*/ICIS_NoInit); + Field->setAccess(AS_public); + NewClass->addDecl(Field); + NewClass->completeDefinition(); + return NewClass; + }; + static void setKernelImplicitAttrs(ASTContext &Context, FunctionDecl *FD, StringRef Name) { // Set implicit attributes. @@ -1125,23 +1142,10 @@ class SyclKernelDeclCreator } bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { - // if (!Util::isArrayOfSpecialSyclType(FieldTy)) { if (!cast(FieldTy) ->getElementType() ->isStructureOrClassType()) { - // Wrap the array in a struct. - RecordDecl *NewClass = - SemaRef.getASTContext().buildImplicitRecord("wrapped_array"); - NewClass->startDefinition(); - FieldDecl *Field = FieldDecl::Create( - SemaRef.getASTContext(), NewClass, SourceLocation(), SourceLocation(), - /*Id=*/nullptr, FieldTy, - SemaRef.getASTContext().getTrivialTypeSourceInfo(FieldTy, - SourceLocation()), - /*BW=*/nullptr, /*Mutable=*/false, /*InitStyle=*/ICIS_NoInit); - Field->setAccess(AS_public); - NewClass->addDecl(Field); - NewClass->completeDefinition(); + RecordDecl *NewClass = wrapAnArray(FieldTy, FD); QualType ST = SemaRef.getASTContext().getRecordType(NewClass); addParam(FD, ST); } @@ -1290,15 +1294,9 @@ class SyclKernelBodyCreator CXXRecordDecl *WrapperStruct = ParamType->getAsCXXRecordDecl(); // The first and only field of the wrapper struct is the array FieldDecl *Array = *(WrapperStruct->field_begin()); - auto DRE = DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), - SourceLocation(), KernelParameter, false, - DeclarationNameInfo(), ParamType, VK_LValue); - DeclAccessPair ArrayDAP = DeclAccessPair::make(Array, AS_none); - Expr *InitExpr = MemberExpr::Create( - SemaRef.Context, DRE, false, SourceLocation(), NestedNameSpecifierLoc(), - SourceLocation(), Array, ArrayDAP, - DeclarationNameInfo(Array->getDeclName(), SourceLocation()), nullptr, - Array->getType(), VK_LValue, OK_Ordinary, NOUR_None); + Expr *DRE = SemaRef.BuildDeclRefExpr(KernelParameter, ParamType, VK_LValue, + SourceLocation()); + Expr *InitExpr = BuildMemberExpr(DRE, Array); InitializationKind InitKind = InitializationKind::CreateDirect( SourceLocation(), SourceLocation(), SourceLocation()); InitializedEntity Entity = InitializedEntity::InitializeLambdaCapture( @@ -1602,7 +1600,6 @@ class SyclKernelIntHeaderCreator } bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { - // if (!Util::isArrayOfSpecialSyclType(FieldTy)) if (!cast(FieldTy) ->getElementType() ->isStructureOrClassType()) From 44d866389576431f986ebe9a7b2ce7acef745f3a Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Wed, 10 Jun 2020 09:05:24 +0300 Subject: [PATCH 03/42] Rebased changes from Elizabeth Signed-off-by: Mariya Podchishchaeva --- clang/lib/Sema/SemaSYCL.cpp | 134 +++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 42 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index fb420be750a29..ddb78741f18cc 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -683,9 +683,6 @@ constructKernelName(Sema &S, FunctionDecl *KernelCallerFunc, // anonymous namespace so these don't get linkage. namespace { -QualType getItemType(const FieldDecl *FD) { return FD->getType(); } -QualType getItemType(const CXXBaseSpecifier &BS) { return BS.getType(); } - // These enable handler execution only when previous handlers succeed. template static bool handleField(FieldDecl *FD, QualType FDTy, Tn &&... tn) { @@ -729,11 +726,6 @@ template using bind_param_t = typename bind_param::type; // })...) // Implements the 'for-each-visitor' pattern. -template -static void VisitAccessorWrapper(CXXRecordDecl *Owner, ParentTy &Parent, - CXXRecordDecl *Wrapper, - Handlers &... handlers); - template static void VisitField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, Handlers &... handlers) { @@ -742,7 +734,7 @@ static void VisitField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, if (Util::isSyclStreamType(ItemTy)) KF_FOR_EACH(handleSyclStreamType, Item, ItemTy); if (ItemTy->isStructureOrClassType()) - VisitAccessorWrapper(Owner, Item, ItemTy->getAsCXXRecordDecl(), + VisitRecord(Owner, Item, ItemTy->getAsCXXRecordDecl(), handlers...); if (ItemTy->isArrayType()) VisitArrayElements(Item, ItemTy, handlers...); @@ -762,38 +754,68 @@ static void VisitArrayElements(RangeTy Item, QualType FieldTy, (void)std::initializer_list{(handlers.leaveArray(ET, ElemCount), 0)...}; } -template -static void VisitAccessorWrapperHelper(CXXRecordDecl *Owner, RangeTy Range, - Handlers &... handlers) { - for (const auto &Item : Range) { - QualType ItemTy = getItemType(Item); - (void)std::initializer_list{(handlers.enterField(Owner, Item), 0)...}; - VisitField(Owner, Item, ItemTy, handlers...); - (void)std::initializer_list{(handlers.leaveField(Owner, Item), 0)...}; +template +static void VisitRecord(CXXRecordDecl *Owner, ParentTy &Parent, + CXXRecordDecl *Wrapper, Handlers &... handlers); + +template +static void VisitRecordHelper(CXXRecordDecl *Owner, + clang::CXXRecordDecl::base_class_range Range, + Handlers &... handlers) { + for (const auto &Base : Range) { + QualType BaseTy = Base.getType(); + if (Util::isSyclAccessorType(BaseTy)) + (void)std::initializer_list{ + (handlers.handleSyclAccessorType(Base, BaseTy), 0)...}; + else if (Util::isSyclStreamType(BaseTy)) + (void)std::initializer_list{ + (handlers.handleSyclStreamType(Base, BaseTy), 0)...}; + else + VisitRecord(Owner, Base, BaseTy->getAsCXXRecordDecl(), handlers...); } } +template +static void VisitRecordHelper(CXXRecordDecl *Owner, + clang::RecordDecl::field_range Range, + Handlers &... handlers) { + VisitRecordFields(Owner, handlers...); +} + // Parent contains the FieldDecl or CXXBaseSpecifier that was used to enter // the Wrapper structure that we're currently visiting. Owner is the parent // type (which doesn't exist in cases where it is a FieldDecl in the // 'root'), and Wrapper is the current struct being unwrapped. template -static void VisitAccessorWrapper(CXXRecordDecl *Owner, ParentTy &Parent, - CXXRecordDecl *Wrapper, - Handlers &... handlers) { +static void VisitRecord(CXXRecordDecl *Owner, ParentTy &Parent, + CXXRecordDecl *Wrapper, Handlers &... handlers) { (void)std::initializer_list{(handlers.enterStruct(Owner, Parent), 0)...}; - VisitAccessorWrapperHelper(Wrapper, Wrapper->bases(), handlers...); - VisitAccessorWrapperHelper(Wrapper, Wrapper->fields(), handlers...); + VisitRecordHelper(Wrapper, Wrapper->bases(), handlers...); + VisitRecordHelper(Wrapper, Wrapper->fields(), handlers...); (void)std::initializer_list{(handlers.leaveStruct(Owner, Parent), 0)...}; } +int getFieldNumber(const CXXRecordDecl *BaseDecl) { + int Members = 0; + for (const auto *Field : BaseDecl->fields()) + ++Members; + + return Members; +} + +template +static void VisitFunctorBases(CXXRecordDecl *KernelFunctor, + Handlers &... handlers) { + VisitRecordHelper(KernelFunctor, KernelFunctor->bases(), handlers...); +} + + // A visitor function that dispatches to functions as defined in // SyclKernelFieldHandler for the purposes of kernel generation. template -static void VisitRecordFields(RecordDecl::field_range Fields, - Handlers &... handlers) { +static void VisitRecordFields(CXXRecordDecl *Owner, Handlers &... handlers) { - for (const auto Field : Fields) { + for (const auto Field : Owner->fields()) { (void)std::initializer_list{ (handlers.enterField(nullptr, Field), 0)...}; QualType FieldTy = Field->getType(); @@ -807,12 +829,12 @@ static void VisitRecordFields(RecordDecl::field_range Fields, else if (Util::isSyclStreamType(FieldTy)) { // Stream actually wraps accessors, so do recursion CXXRecordDecl *RD = FieldTy->getAsCXXRecordDecl(); - VisitAccessorWrapper(nullptr, Field, RD, handlers...); + VisitRecord(nullptr, Field, RD, handlers...); KF_FOR_EACH(handleSyclStreamType, Field, FieldTy); } else if (FieldTy->isStructureOrClassType()) { if (KF_FOR_EACH(handleStructType, Field, FieldTy)) { CXXRecordDecl *RD = FieldTy->getAsCXXRecordDecl(); - VisitAccessorWrapper(nullptr, Field, RD, handlers...); + VisitRecord(nullptr, Field, RD, handlers...); } } else if (FieldTy->isReferenceType()) KF_FOR_EACH(handleReferenceType, Field, FieldTy); @@ -1131,7 +1153,7 @@ class SyclKernelDeclCreator } bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - addParam(FD, FieldTy); + // addParam(FD, FieldTy); return true; } @@ -1313,16 +1335,14 @@ class SyclKernelBodyCreator bool handleSpecialType(FieldDecl *FD, QualType Ty) { const auto *RecordDecl = Ty->getAsCXXRecordDecl(); // Perform initialization only if it is field of kernel object - if (MemberExprBases.size() == 1) { - InitializedEntity Entity = - InitializedEntity::InitializeMember(FD, &VarEntity); - // Initialize with the default constructor. - InitializationKind InitKind = - InitializationKind::CreateDefault(SourceLocation()); - InitializationSequence InitSeq(SemaRef, Entity, InitKind, None); - ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, None); - InitExprs.push_back(MemberInit.get()); - } + InitializedEntity Entity = + InitializedEntity::InitializeMember(FD, &VarEntity); + // Initialize with the default constructor. + InitializationKind InitKind = + InitializationKind::CreateDefault(SourceLocation()); + InitializationSequence InitSeq(SemaRef, Entity, InitKind, None); + ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, None); + InitExprs.push_back(MemberInit.get()); createSpecialMethodCall(RecordDecl, MemberExprBases.back(), InitMethodName, FD); return true; @@ -1390,11 +1410,12 @@ class SyclKernelBodyCreator } bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - createExprForStructOrScalar(FD); + // createExprForStructOrScalar(FD); return true; } bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { + FieldTy->dump(); createExprForStructOrScalar(FD); return true; } @@ -1403,8 +1424,35 @@ class SyclKernelBodyCreator MemberExprBases.push_back(BuildMemberExpr(MemberExprBases.back(), FD)); } + void addStructInit(const CXXRecordDecl *RD){ + if (!RD) + return; + + int NumberOfFields = getFieldNumber(RD); + int popOut = NumberOfFields + RD->getNumBases(); + llvm::SmallVector BaseInitExprs; + for (int I = 0; I < popOut; I++) { + BaseInitExprs.push_back(InitExprs.back()); + InitExprs.pop_back(); + } + std::reverse(BaseInitExprs.begin(), BaseInitExprs.end()); + + Expr *ILE = new (SemaRef.getASTContext()) + InitListExpr(SemaRef.getASTContext(), SourceLocation(), BaseInitExprs, + SourceLocation()); + ILE->setType(QualType(RD->getTypeForDecl(), 0)); + InitExprs.push_back(ILE); + + } + void leaveStruct(const CXXRecordDecl *, FieldDecl *FD) final { - MemberExprBases.pop_back(); + const CXXRecordDecl *RD = FD->getType()->getAsCXXRecordDecl(); + addStructInit(RD); + } + + void leaveStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { + const CXXRecordDecl *BaseClass = BS.getType()->getAsCXXRecordDecl(); + addStructInit(BaseClass); } using SyclKernelFieldHandler::enterStruct; @@ -1512,7 +1560,7 @@ class SyclKernelIntHeaderCreator return true; } bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); + // addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); return true; } bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { @@ -1606,7 +1654,9 @@ void Sema::ConstructOpenCLKernel(FunctionDecl *KernelCallerFunc, StableName); ConstructingOpenCLKernel = true; - VisitRecordFields(KernelLambda->fields(), checker, kernel_decl, kernel_body, + VisitFunctorBases(KernelLambda, checker, kernel_decl, kernel_body, + int_header); + VisitRecordFields(KernelLambda, checker, kernel_decl, kernel_body, int_header); ConstructingOpenCLKernel = false; } From 801a0ea08f0d6087b81e17da36182b4a4b8084c1 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Wed, 10 Jun 2020 09:07:17 +0300 Subject: [PATCH 04/42] Implement special bases handling Signed-off-by: Mariya Podchishchaeva --- clang/lib/Sema/SemaSYCL.cpp | 47 +++++++++++++++---- clang/test/CodeGenSYCL/integration_header.cpp | 38 ++++++++------- 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index ddb78741f18cc..beba1f504085e 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1299,7 +1299,10 @@ class SyclKernelBodyCreator VK_LValue, SourceLocation()); } - MemberExpr *SpecialObjME = BuildMemberExpr(Base, Field); + Expr *SpecialObjME = Base; + if (Field) + SpecialObjME = BuildMemberExpr(Base, Field); + MemberExpr *MethodME = BuildMemberExpr(SpecialObjME, Method); QualType ResultTy = Method->getReturnType(); @@ -1334,7 +1337,8 @@ class SyclKernelBodyCreator bool handleSpecialType(FieldDecl *FD, QualType Ty) { const auto *RecordDecl = Ty->getAsCXXRecordDecl(); - // Perform initialization only if it is field of kernel object + // TODO: VarEntity is initialized entity for KernelObjClone, I guess we need + // to create new one when enter new struct. InitializedEntity Entity = InitializedEntity::InitializeMember(FD, &VarEntity); // Initialize with the default constructor. @@ -1348,6 +1352,24 @@ class SyclKernelBodyCreator return true; } + bool handleSpecialType(const CXXBaseSpecifier &BS, QualType Ty) { + const auto *RecordDecl = Ty->getAsCXXRecordDecl(); + // TODO: VarEntity is initialized entity for KernelObjClone, I guess we need + // to create new one when enter new struct. + InitializedEntity Entity = InitializedEntity::InitializeBase( + SemaRef.Context, &BS, /*IsInheritedVirtualBase*/ false, &VarEntity); + // Initialize with the default constructor. + InitializationKind InitKind = + InitializationKind::CreateDefault(SourceLocation()); + InitializationSequence InitSeq(SemaRef, Entity, InitKind, None); + ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, None); + InitExprs.push_back(MemberInit.get()); + + createSpecialMethodCall(RecordDecl, MemberExprBases.back(), InitMethodName, + nullptr); + return true; + } + public: SyclKernelBodyCreator(Sema &S, SyclKernelDeclCreator &DC, CXXRecordDecl *KernelObj, @@ -1379,9 +1401,7 @@ class SyclKernelBodyCreator } bool handleSyclAccessorType(const CXXBaseSpecifier &BS, QualType Ty) final { - // FIXME SYCL accessor should be usable as a base type - // See https://github.com/intel/llvm/issues/28. - return true; + return handleSpecialType(BS, Ty); } bool handleSyclSamplerType(FieldDecl *FD, QualType Ty) final { @@ -1415,7 +1435,6 @@ class SyclKernelBodyCreator } bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { - FieldTy->dump(); createExprForStructOrScalar(FD); return true; } @@ -1424,6 +1443,19 @@ class SyclKernelBodyCreator MemberExprBases.push_back(BuildMemberExpr(MemberExprBases.back(), FD)); } + void enterStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { + CXXCastPath BasePath; + QualType DerivedTy(RD->getTypeForDecl(), 0); + QualType BaseTy = BS.getType(); + SemaRef.CheckDerivedToBaseConversion(DerivedTy, BaseTy, SourceLocation(), + SourceRange(), &BasePath, + /*IgnoreBaseAccess*/ true); + auto Cast = ImplicitCastExpr::Create( + SemaRef.Context, BaseTy, CK_DerivedToBase, MemberExprBases.back(), + /* CXXCastPath=*/&BasePath, VK_LValue); + MemberExprBases.push_back(Cast); + } + void addStructInit(const CXXRecordDecl *RD){ if (!RD) return; @@ -1443,6 +1475,7 @@ class SyclKernelBodyCreator ILE->setType(QualType(RD->getTypeForDecl(), 0)); InitExprs.push_back(ILE); + MemberExprBases.pop_back(); } void leaveStruct(const CXXRecordDecl *, FieldDecl *FD) final { @@ -1455,8 +1488,6 @@ class SyclKernelBodyCreator addStructInit(BaseClass); } - using SyclKernelFieldHandler::enterStruct; - using SyclKernelFieldHandler::leaveStruct; }; class SyclKernelIntHeaderCreator diff --git a/clang/test/CodeGenSYCL/integration_header.cpp b/clang/test/CodeGenSYCL/integration_header.cpp index 58d0c3addcd8c..1c766b2dccd3f 100644 --- a/clang/test/CodeGenSYCL/integration_header.cpp +++ b/clang/test/CodeGenSYCL/integration_header.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -triple spir64-unknown-unknown-sycldevice -fsycl-int-header=%t.h %s -fsyntax-only +// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -triple spir64-unknown-unknown-sycldevice -fsycl-int-header=%t.h %s -emit-llvm // RUN: FileCheck -input-file=%t.h %s // // CHECK: #include @@ -28,9 +28,11 @@ // CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { // CHECK-NEXT: //--- _ZTSZ4mainE12first_kernel // CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 0 }, -// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 4 }, -// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 6112, 16 }, -// CHECK-NEXT: { kernel_param_kind_t::kind_sampler, 8, 32 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 1, 4 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 8 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 12 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 6112, 24 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_sampler, 8, 40 }, // CHECK-EMPTY: // CHECK-NEXT: //--- _ZTSN16second_namespace13second_kernelIcEE // CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 0 }, @@ -46,12 +48,15 @@ // CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 0 }, // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 6112, 4 }, // CHECK-EMPTY: -// CHECK-NEXT: //--- _ZTSZ4mainE16accessor_in_base -// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 64, 0 }, -// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 8 }, -// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 24 }, -// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 40 }, -// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 52 }, +// CHECK-NEXT: //--- _ZTSZ4mainE16accessor_in_base +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 0 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 4 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 8 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 20 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 24 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 36 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 40 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 52 }, // CHECK-EMPTY: // CHECK-NEXT: }; // @@ -116,15 +121,13 @@ int main() { acc2; int i = 13; cl::sycl::sampler smplr; - // TODO: Uncomemnt when structures in kernel arguments are correctly processed - // by SYCL compiler - /* struct { + struct { char c; int i; } test_s; - test_s.c = 14;*/ + test_s.c = 14; kernel_single_task([=]() { - if (i == 13 /*&& test_s.c == 14*/) { + if (i == 13 && test_s.c == 14) { acc1.use(); acc2.use(); @@ -151,10 +154,9 @@ int main() { } }); - // FIXME: We cannot use the member-capture because all the handlers except the - // integration header handler in SemaSYCL don't handle base types right. accessor_in_base::captured c; - kernel_single_task([c]() { + kernel_single_task([=]() { + c.use(); }); return 0; From 52f2e5aaa434f72caf3457abecf00d972d9dbc05 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Thu, 11 Jun 2020 08:43:54 +0300 Subject: [PATCH 05/42] Handle vector types like scalar types Vector types started appearing as top level arguments since they are wrapped with cl::sycl::vec class. Signed-off-by: Mariya Podchishchaeva --- clang/lib/Sema/SemaSYCL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index a6809af953cea..9c20125286c1b 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -843,7 +843,7 @@ static void VisitRecordFields(CXXRecordDecl *Owner, Handlers &... handlers) { else if (FieldTy->isArrayType()) { if (KF_FOR_EACH(handleArrayType, Field, FieldTy)) VisitArrayElements(Field, FieldTy, handlers...); - } else if (FieldTy->isScalarType()) + } else if (FieldTy->isScalarType() || FieldTy->isVectorType()) KF_FOR_EACH(handleScalarType, Field, FieldTy); else KF_FOR_EACH(handleOtherType, Field, FieldTy); From 2a36a93cc0b268b00ab827a0fb54ae1dd1a16849 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Thu, 11 Jun 2020 16:25:16 -0700 Subject: [PATCH 06/42] Fixed failing lit tests. Structs/Classes are no longer passed whole. They are decomposed and individual fields are passed Signed-off-by: Elizabeth Andrews --- clang/test/CodeGenSYCL/wrapped-accessor.cpp | 1 - clang/test/SemaSYCL/built-in-type-kernel-arg.cpp | 12 ++++++------ clang/test/SemaSYCL/fake-accessors.cpp | 6 +++--- clang/test/SemaSYCL/wrapped-accessor.cpp | 10 ++++------ 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/clang/test/CodeGenSYCL/wrapped-accessor.cpp b/clang/test/CodeGenSYCL/wrapped-accessor.cpp index 0cd651efc58f5..1380aebba234e 100644 --- a/clang/test/CodeGenSYCL/wrapped-accessor.cpp +++ b/clang/test/CodeGenSYCL/wrapped-accessor.cpp @@ -17,7 +17,6 @@ // CHECK: static constexpr // CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { // CHECK-NEXT: //--- _ZTSZ4mainE14wrapped_access -// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 12, 0 }, // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 0 }, // CHECK-EMPTY: // CHECK-NEXT: }; diff --git a/clang/test/SemaSYCL/built-in-type-kernel-arg.cpp b/clang/test/SemaSYCL/built-in-type-kernel-arg.cpp index 82cd21bf01552..2d04e0f453579 100644 --- a/clang/test/SemaSYCL/built-in-type-kernel-arg.cpp +++ b/clang/test/SemaSYCL/built-in-type-kernel-arg.cpp @@ -65,15 +65,15 @@ int main() { // CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} '_arg_' 'int' // Check kernel parameters -// CHECK: {{.*}}kernel_struct{{.*}} 'void (test_struct)' -// CHECK: ParmVarDecl {{.*}} used _arg_ 'test_struct' +// CHECK: {{.*}}kernel_struct{{.*}} 'void (int)' +// CHECK: ParmVarDecl {{.*}} used _arg_data 'int' // Check that lambda field of struct type is initialized // CHECK: VarDecl {{.*}}'(lambda at {{.*}}built-in-type-kernel-arg.cpp{{.*}})' -// CHECK-NEXT: InitListExpr -// CHECK-NEXT: CXXConstructExpr {{.*}}'test_struct'{{.*}}void (const test_struct &) -// CHECK-NEXT: ImplicitCastExpr {{.*}}'const test_struct' lvalue -// CHECK-NEXT: DeclRefExpr {{.*}} 'test_struct' lvalue ParmVar {{.*}} '_arg_' 'test_struct' +// CHECK-NEXT: InitListExpr {{.*}}'(lambda at {{.*}}built-in-type-kernel-arg.cpp{{.*}})' +// CHECK-NEXT: InitListExpr {{.*}}'test_struct'{{.*}} +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} '_arg_data' 'int' // Check kernel parameters // CHECK: {{.*}}kernel_pointer{{.*}} 'void (__global int *, __global int *)' diff --git a/clang/test/SemaSYCL/fake-accessors.cpp b/clang/test/SemaSYCL/fake-accessors.cpp index 24d36a6ba54b6..a1357083dd2ce 100644 --- a/clang/test/SemaSYCL/fake-accessors.cpp +++ b/clang/test/SemaSYCL/fake-accessors.cpp @@ -51,6 +51,6 @@ int main() { }); return 0; } -// CHECK: fake_accessors{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, foo::cl::sycl::accessor, accessor) -// CHECK: accessor_typedef{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, foo::cl::sycl::accessor, accessor) -// CHECK: accessor_alias{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, foo::cl::sycl::accessor, accessor) +// CHECK: fake_accessors{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, int, int) +// CHECK: accessor_typedef{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, int, int) +// CHECK: accessor_alias{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, int, int) diff --git a/clang/test/SemaSYCL/wrapped-accessor.cpp b/clang/test/SemaSYCL/wrapped-accessor.cpp index 83bb3ff2448fb..1052b4ac24e0f 100644 --- a/clang/test/SemaSYCL/wrapped-accessor.cpp +++ b/clang/test/SemaSYCL/wrapped-accessor.cpp @@ -23,10 +23,9 @@ int main() { } // Check declaration of the kernel -// CHECK: wrapped_access{{.*}} 'void (AccWrapper>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' +// CHECK: wrapped_access{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' // Check parameters of the kernel -// CHECK: ParmVarDecl {{.*}} used _arg_ 'AccWrapper>':'AccWrapper>' // CHECK: ParmVarDecl {{.*}} used _arg_accessor '__global int *' // CHECK: ParmVarDecl {{.*}} used [[_arg_AccessRange:[0-9a-zA-Z_]+]] 'cl::sycl::range<1>' // CHECK: ParmVarDecl {{.*}} used [[_arg_MemRange:[0-9a-zA-Z_]+]] 'cl::sycl::range<1>' @@ -35,10 +34,9 @@ int main() { // Check that wrapper object itself is initialized with corresponding kernel // argument // CHECK: VarDecl {{.*}}'(lambda at {{.*}}wrapped-accessor.cpp{{.*}})' -// CHECK-NEXT: InitListExpr -// CHECK-NEXT: CXXConstructExpr {{.*}}AccWrapper>' lvalue -// CHECK-NEXT: DeclRefExpr {{.*}} 'AccWrapper>':'AccWrapper>' lvalue ParmVar {{.*}} '_arg_' 'AccWrapper>':'AccWrapper>' +// CHECK-NEXT: InitListExpr {{.*}}'(lambda at {{.*}}wrapped-accessor.cpp{{.*}})' +// CHECK-NEXT: InitListExpr {{.*}}'AccWrapper>' +// CHECK-NEXT: CXXConstructExpr {{.*}}'cl::sycl::accessor':'cl::sycl::accessor' 'void () noexcept' // Check that accessor field of the wrapper object is initialized using __init method // CHECK-NEXT: CXXMemberCallExpr {{.*}} 'void' From ab74fcfd378a5e792870e9e6c785dbcda19b669f Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Thu, 11 Jun 2020 17:13:45 -0700 Subject: [PATCH 07/42] Owner should be record being visited. Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 9c20125286c1b..ec8c4a6fa03cc 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -834,7 +834,7 @@ static void VisitRecordFields(CXXRecordDecl *Owner, Handlers &... handlers) { } else if (FieldTy->isStructureOrClassType()) { if (KF_FOR_EACH(handleStructType, Field, FieldTy)) { CXXRecordDecl *RD = FieldTy->getAsCXXRecordDecl(); - VisitRecord(nullptr, Field, RD, handlers...); + VisitRecord(Owner, Field, RD, handlers...); } } else if (FieldTy->isReferenceType()) KF_FOR_EACH(handleReferenceType, Field, FieldTy); From 4370d76553778ea4ee8558f4f02237fb742392b1 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Thu, 11 Jun 2020 21:12:18 -0700 Subject: [PATCH 08/42] Avoid decomposing stream class. If field type is stream, we iterate through it's fields and initialize accessors using respective handlers. Other fields of stream class are not handled. --- clang/lib/Sema/SemaSYCL.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index ec8c4a6fa03cc..adc93bab1a82d 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -795,6 +795,21 @@ static void VisitRecord(CXXRecordDecl *Owner, ParentTy &Parent, (void)std::initializer_list{(handlers.leaveStruct(Owner, Parent), 0)...}; } +// FIXME: Can this be refactored/handled some other way? +template +static void VisitStreamRecord(CXXRecordDecl *Owner, ParentTy &Parent, + CXXRecordDecl *Wrapper, Handlers &... handlers) { + (void)std::initializer_list{(handlers.enterStruct(Owner, Parent), 0)...}; + for (const auto &Field : Wrapper->fields()) { + QualType FieldTy = Field->getType(); + // Required to initialize accessors inside streams. + if (Util::isSyclAccessorType(FieldTy)) + (void)std::initializer_list{ + (handlers.handleSyclAccessorType(Field, FieldTy), 0)...}; + } + (void)std::initializer_list{(handlers.leaveStruct(Owner, Parent), 0)...}; +} + int getFieldNumber(const CXXRecordDecl *BaseDecl) { int Members = 0; for (const auto *Field : BaseDecl->fields()) @@ -827,9 +842,9 @@ static void VisitRecordFields(CXXRecordDecl *Owner, Handlers &... handlers) { else if (Util::isSyclSpecConstantType(FieldTy)) KF_FOR_EACH(handleSyclSpecConstantType, Field, FieldTy); else if (Util::isSyclStreamType(FieldTy)) { - // Stream actually wraps accessors, so do recursion CXXRecordDecl *RD = FieldTy->getAsCXXRecordDecl(); - VisitRecord(nullptr, Field, RD, handlers...); + // Handle accessors in stream class. + VisitStreamRecord(Owner, Field, RD, handlers...); KF_FOR_EACH(handleSyclStreamType, Field, FieldTy); } else if (FieldTy->isStructureOrClassType()) { if (KF_FOR_EACH(handleStructType, Field, FieldTy)) { @@ -1480,7 +1495,18 @@ class SyclKernelBodyCreator void leaveStruct(const CXXRecordDecl *, FieldDecl *FD) final { const CXXRecordDecl *RD = FD->getType()->getAsCXXRecordDecl(); - addStructInit(RD); + + // Initializers for accessors inside stream not added. + if (!Util::isSyclStreamType(FD->getType())) + addStructInit(RD); + // Pop out unused initializers created in handleSyclAccesorType + else { + for (const auto &Field : RD->fields()) { + QualType FieldTy = Field->getType(); + if (Util::isSyclAccessorType(FieldTy)) + InitExprs.pop_back(); + } + } } void leaveStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { From 52ce3f2dddfeaed583f29669bd968f3453e3a06f Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Fri, 12 Jun 2020 10:41:54 -0700 Subject: [PATCH 09/42] Updated support for arrays. --- clang/lib/Sema/SemaSYCL.cpp | 66 +-- .../CodeGenSYCL/kernel-param-acc-array-ih.cpp | 5 +- .../CodeGenSYCL/kernel-param-acc-array.cpp | 67 +-- clang/test/SemaSYCL/array-kernel-param.cpp | 9 +- sycl/doc/CompilerAndRuntimeDesign.md | 3 +- sycl/doc/KernelParameterPassing.md | 424 ++++++++++++++++++ 6 files changed, 493 insertions(+), 81 deletions(-) create mode 100755 sycl/doc/KernelParameterPassing.md diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index dafa4f2357c1e..78497f1599e05 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -644,6 +644,22 @@ static ParamDesc makeParamDesc(ASTContext &Ctx, const CXXBaseSpecifier &Src, Ctx.getTrivialTypeSourceInfo(Ty)); } +// Create a new class around a field - used to wrap arrays. +static RecordDecl *wrapAnArray(ASTContext &Ctx, const QualType ArgTy, + FieldDecl *&Field) { + RecordDecl *NewClass = Ctx.buildImplicitRecord("wrapped_array"); + NewClass->startDefinition(); + Field = FieldDecl::Create( + Ctx, NewClass, SourceLocation(), SourceLocation(), + /*Id=*/nullptr, ArgTy, + Ctx.getTrivialTypeSourceInfo(ArgTy, SourceLocation()), + /*BW=*/nullptr, /*Mutable=*/false, /*InitStyle=*/ICIS_NoInit); + Field->setAccess(AS_public); + NewClass->addDecl(Field); + NewClass->completeDefinition(); + return NewClass; +} + /// \return the target of given SYCL accessor type static target getAccessTarget(const ClassTemplateSpecializationDecl *AccTy) { return static_cast( @@ -1035,23 +1051,6 @@ class SyclKernelDeclCreator return true; } - // Create a new class around a field - used to wrap arrays. - RecordDecl *wrapAnArray(const QualType ArgTy, FieldDecl *Field) { - RecordDecl *NewClass = - SemaRef.getASTContext().buildImplicitRecord("wrapped_array"); - NewClass->startDefinition(); - Field = FieldDecl::Create( - SemaRef.getASTContext(), NewClass, SourceLocation(), SourceLocation(), - /*Id=*/nullptr, ArgTy, - SemaRef.getASTContext().getTrivialTypeSourceInfo(ArgTy, - SourceLocation()), - /*BW=*/nullptr, /*Mutable=*/false, /*InitStyle=*/ICIS_NoInit); - Field->setAccess(AS_public); - NewClass->addDecl(Field); - NewClass->completeDefinition(); - return NewClass; - }; - static void setKernelImplicitAttrs(ASTContext &Context, FunctionDecl *FD, StringRef Name) { // Set implicit attributes. @@ -1142,13 +1141,9 @@ class SyclKernelDeclCreator } bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { - if (!cast(FieldTy) - ->getElementType() - ->isStructureOrClassType()) { - RecordDecl *NewClass = wrapAnArray(FieldTy, FD); - QualType ST = SemaRef.getASTContext().getRecordType(NewClass); - addParam(FD, ST); - } + RecordDecl *NewClass = wrapAnArray(SemaRef.getASTContext(), FieldTy, FD); + QualType ST = SemaRef.getASTContext().getRecordType(NewClass); + addParam(FD, ST); return true; } @@ -1295,7 +1290,7 @@ class SyclKernelBodyCreator // The first and only field of the wrapper struct is the array FieldDecl *Array = *(WrapperStruct->field_begin()); Expr *DRE = SemaRef.BuildDeclRefExpr(KernelParameter, ParamType, VK_LValue, - SourceLocation()); + SourceLocation()); Expr *InitExpr = BuildMemberExpr(DRE, Array); InitializationKind InitKind = InitializationKind::CreateDirect( SourceLocation(), SourceLocation(), SourceLocation()); @@ -1307,15 +1302,6 @@ class SyclKernelBodyCreator InitExprs.push_back(MemberInit.get()); } - void createExprForArrayElement(size_t ArrayIndex) { - Expr *ArrayBase = MemberExprBases.back(); - ExprResult IndexExpr = - SemaRef.ActOnIntegerConstant(SourceLocation(), ArrayIndex); - ExprResult ElementBase = SemaRef.CreateBuiltinArraySubscriptExpr( - ArrayBase, SourceLocation(), IndexExpr.get(), SourceLocation()); - MemberExprBases.push_back(ElementBase.get()); - } - void createSpecialMethodCall(const CXXRecordDecl *SpecialClass, Expr *Base, const std::string &MethodName, FieldDecl *Field) { @@ -1455,11 +1441,7 @@ class SyclKernelBodyCreator } bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { - if (!cast(FieldTy) - ->getElementType() - ->isStructureOrClassType()) { - createExprForArray(FD); - } + createExprForArray(FD); return true; } @@ -1600,10 +1582,8 @@ class SyclKernelIntHeaderCreator } bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { - if (!cast(FieldTy) - ->getElementType() - ->isStructureOrClassType()) - addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); + wrapAnArray(SemaRef.getASTContext(), FieldTy, FD); + addParam(FD, FD->getType(), SYCLIntegrationHeader::kind_std_layout); return true; } diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp index fbaffee9f9b92..0f2b0b32d315c 100755 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv +// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=/iusers/rdeodhar/work/dpcc/jira/7004/t.h %s -c -o %T/kernel.spv // RUN: FileCheck -input-file=%t.h %s // This test checks the integration header generated when @@ -19,7 +19,8 @@ // CHECK: static constexpr // CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { -// CHECK-NEXT: //--- _ZTSZ4mainE8kernel_A +// CHECK-NEXT: //--- _ZTSZ4mainE8kernel_A +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 24, 0 }, // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 0 }, // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 12 }, // CHECK-EMPTY: diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp index 09d1f48f86907..2306599393b7a 100644 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp @@ -25,23 +25,24 @@ int main() { // Check kernel_A parameters // CHECK: define spir_kernel void @{{.*}}kernel_A -// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG1:%[a-zA-Z0-9_]+]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE1:%[a-zA-Z0-9_]+_1]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE1:%[a-zA-Z0-9_]+_2]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET1:%[a-zA-Z0-9_]+_3]], -// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG2:%[a-zA-Z0-9_]+_4]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE2:%[a-zA-Z0-9_]+_6]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE2:%[a-zA-Z0-9_]+_7]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET2:%[a-zA-Z0-9_]+_8]]) - -// Check alloca for pointer arguments -// CHECK: [[MEM_ARG1]].addr{{[0-9]*}} = alloca i32 addrspace(1)*, align 8 -// CHECK: [[MEM_ARG1]].addr{{[0-9]*}} = alloca i32 addrspace(1)*, align 8 - -// Check lambda object alloca +// CHECK-SAME: %struct.{{.*}}.wrapped_array* byval{{.*}}align 4 %_arg_, +// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG1:%[a-zA-Z0-9_]+_1]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE1:%[a-zA-Z0-9_]+_2]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE1:%[a-zA-Z0-9_]+_3]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET1:%[a-zA-Z0-9_]+_4]], +// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG2:%[a-zA-Z0-9_]+_5]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE2:%[a-zA-Z0-9_]+_7]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE2:%[a-zA-Z0-9_]+_8]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET2:%[a-zA-Z0-9_]+_9]]) + +// CHECK alloca for pointer arguments +// CHECK: [[MEM_ARG1:%[a-zA-Z0-9_.]+]] = alloca i32 addrspace(1)*, align 8 +// CHECK: [[MEM_ARG2:%[a-zA-Z0-9_.]+]] = alloca i32 addrspace(1)*, align 8 + +// CHECK lambda object alloca // CHECK: [[LOCAL_OBJECT:%0]] = alloca %"class.{{.*}}.anon", align 4 -// Check allocas for ranges +// CHECK allocas for ranges // CHECK: [[ACC_RANGE1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" // CHECK: [[MEM_RANGE1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" // CHECK: [[OFFSET1:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" @@ -49,26 +50,30 @@ int main() { // CHECK: [[MEM_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" // CHECK: [[OFFSET2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" -// Check accessor array GEP for acc[0] -// CHECK: [[ACCESSOR_ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY1]], i64 0, i64 0 +// Check array initialization +// CHECK: arrayinit.body: +// CHECK: arrayinit.end: -// Check load from kernel pointer argument alloca -// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}} +// CHECK accessor array GEP for acc[0] +// CHECK: [[ACCESSOR_ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY1]], i64 0, i64 0 -// Check acc[0] __init method call -// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z0]] to %"class{{.*}}accessor" addrspace(4)* +// CHECK load from kernel pointer argument alloca +// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]] -// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) +// CHECK acc[0] __init method call +// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z0]] to %"class{{.*}}accessor" addrspace(4)* -// Check accessor array GEP for acc[1] -// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY2]], i64 0, i64 1 +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) -// Check load from kernel pointer argument alloca -// CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}} +// CHECK accessor array GEP for acc[1] +// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY2]], i64 0, i64 1 -// Check acc[1] __init method call -// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)* +// CHECK load from kernel pointer argument alloca +// CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG2]] -// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) +// CHECK acc[1] __init method call +// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)* + +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) diff --git a/clang/test/SemaSYCL/array-kernel-param.cpp b/clang/test/SemaSYCL/array-kernel-param.cpp index a7157f479b997..d94c76600bb13 100755 --- a/clang/test/SemaSYCL/array-kernel-param.cpp +++ b/clang/test/SemaSYCL/array-kernel-param.cpp @@ -40,7 +40,8 @@ int main() { } // Check kernel_A parameters -// CHECK: FunctionDecl {{.*}}kernel_A{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' +// CHECK: FunctionDecl {{.*}}kernel_A{{.*}} 'void (wrapped_array, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'wrapped_array' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ '__global int *' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::range<1>' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::range<1>' @@ -54,7 +55,7 @@ int main() { // CHECK: CXXMemberCallExpr {{.*}} 'void' // CHECK-NEXT: MemberExpr {{.*}}__init -// Check kernel_B parameters +// CHECK kernel_B parameters // CHECK: FunctionDecl {{.*}}kernel_B{{.*}} 'void (wrapped_array)' // CHECK-NEXT: ParmVarDecl {{.*}} 'wrapped_array' // CHECK-NEXT: CompoundStmt @@ -63,7 +64,7 @@ int main() { // CHECK-NEXT: InitListExpr // CHECK-NEXT: ArrayInitLoopExpr {{.*}} 'int [100]' -// Check kernel_C parameters +// CHECK kernel_C parameters // CHECK: FunctionDecl {{.*}}kernel_C{{.*}} 'void (struct {{.*}}, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' // CHECK-NEXT: ParmVarDecl {{.*}} 'struct {{.*}}' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' @@ -83,7 +84,7 @@ int main() { // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' -// Check that four accessor init functions are called +// CHECK that four accessor init functions are called // CHECK: CXXMemberCallExpr {{.*}} 'void' // CHECK-NEXT: MemberExpr {{.*}}__init // CHECK: CXXMemberCallExpr {{.*}} 'void' diff --git a/sycl/doc/CompilerAndRuntimeDesign.md b/sycl/doc/CompilerAndRuntimeDesign.md index dbc73ca657094..d503687fcd153 100644 --- a/sycl/doc/CompilerAndRuntimeDesign.md +++ b/sycl/doc/CompilerAndRuntimeDesign.md @@ -168,7 +168,8 @@ __kernel KernelName(global int* a) { ``` OpenCL kernel function is generated by the compiler inside the Sema using AST -nodes. +nodes. Additional details of kernel parameter passing may be found in the document +[SYCL Kernel Parameter Handling and Array Support](KernelParameterPassing.md) . ### SYCL support in the driver diff --git a/sycl/doc/KernelParameterPassing.md b/sycl/doc/KernelParameterPassing.md new file mode 100755 index 0000000000000..a6f31ec114482 --- /dev/null +++ b/sycl/doc/KernelParameterPassing.md @@ -0,0 +1,424 @@ +

SYCL Kernel Parameter Handling and Array Support

+ +

Introduction

+ +This document describes how parameters of SYCL kernels are passed +from host to device. Support for arrays as kernel parameters was added +later and aspects of that design are covered in more detail. +The special treatment of arrays of `sycl::accessor` objects is also discussed. +Array support covers these cases: + +1. arrays of standard-layout type +2. arrays of accessors +3. arrays of structs that contain accessor arrays or accessor fields + +The motivation for allowing arrays as kernel parameters is to +bring consistency to the treatment of arrays. +In C++ a lambda function is allowed to access an element of an array +defined outside the lambda. The compiler captures the entire array +by value. Note that this behavior is limited to implicit +capture of the array by value. If the array name were in +the capture list then the base address of the array would be captured +and not the entire array. + +A user would expect the same mode of array capture in a SYCL kernel +lambda object as in any other lambda object. + +The first few sections describe the overall design. +The last three sections provide additional details of array support. +The implementation of this design is confined to four classes in the +file `SemaSYCL.cpp`. +

A SYCL Kernel

+ +The SYCL constructs `single_task`, `parallel_for`, and +`parallel_for_work_group` each take a function object or a lambda function + as one of their arguments. The code within the function object or +lambda function is executed on the device. +To enable execution of the kernel on OpenCL devices, the lambda/function object +is converted into the format of an OpenCL kernel. + +

SYCL Kernel Code Generation

+ +Consider a source code example that captures an int, a struct and an accessor +by value: + +```C++ +constexpr size_t c_num_items = 10; +range<1> num_items{c_num_items}; // range<1>(num_items) + +int main() +{ + int output[c_num_items]; + queue myQueue; + + int i = 55; + struct S { + int m; + } s = { 66 }; + auto outBuf = buffer(&output[0], num_items); + + myQueue.submit([&](handler &cgh) { + auto outAcc = outBuf.get_access(cgh); + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + outAcc[index] = i + s.m; + }); + }); + + return 0; +} +``` + +The input to the code generation routines is a function object that represents +the kernel. In pseudo-code: + +```C++ +struct Capture { + sycl::accessor outAcc; + int i; + struct S s; + () { + outAcc[index] = i + s.m; + } +} +``` + +The compiler-generated code for a call to such a lambda function would look like this: +```C++ +()(struct Capture* this); +``` + +When offloading the kernel to a device, the lambda/function object's +function operator cannot be directly called with a capture object address. +Instead, the code generated for the device is in the form of a +“kernel caller” and a “kernel callee”. +The callee is a clone of the SYCL kernel object. +The caller is generated in the form of an OpenCL kernel function. +It receives the lambda capture object in pieces, assembles the pieces +into the original lambda capture object and then calls the callee: + +```C++ +spir_kernel void caller( + __global int* AccData, // arg1 of accessor init function + range<1> AccR1, // arg2 of accessor init function + range<1> AccR2, // arg3 of accessor init function + id<1> I, // arg4 of accessor init function + int i, + struct S s +) +{ + // Local capture object + struct Capture local; + + // Reassemble capture object from parts + local.i = i; + local.s = s; + // Call accessor’s init function + sycl::accessor::init(&local.outAcc, AccData, AccR1, AccR2, I); + + // Call the kernel body + callee(&local, id<1> wi); +} + +spir_func void callee(struct Capture* this, id<1> wi) +{ +} +``` + +As may be observed from the example above, standard-layout lambda capture +components are passed by value to the device as separate parameters. +This includes scalars, pointers, and standard-layout structs. +Certain object types defined by the SYCL standard, such as +`sycl::accessor` and `sycl::sampler` although standard-layout, cannot be +simply copied from host to device. Their layout on the device may be different +from that on the host. Some host fields may be absent on the device, +other host fields replaced with device-specific fields and +the host data pointer field must be translated to an OpenCL +or L0 memory object before it can be passed as a kernel parameter. +To enable all of this, the parameters of the `sycl::accessor` +and `sycl::sampler` init functions are transfered from +host to device separately. The values received on the device +are passed to the `init` functions executed on the device, +which results in the reassembly of the SYCL object in a form usable on the device. + +There is one other aspect of code generation. An “integration header” +is generated for use during host compilation. +This header file contains entries for each kernel. +Among the items it defines is a table of sizes and offsets of the +kernel parameters. +For the source example above the integration header contains the +following snippet: + +```C++ +// array representing signatures of all kernels defined in the +// corresponding source +static constexpr +const kernel_param_desc_t kernel_signatures[] = { + //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE19->18clES2_E6Worker + { kernel_param_kind_t::kind_accessor, 4062, 0 }, + { kernel_param_kind_t::kind_std_layout, 4, 32 }, + { kernel_param_kind_t::kind_std_layout, 4, 36 }, +}; +``` + +Each entry in the kernel_signatures table is a `kernel_param_desc_t` +object which contains three values: +1) an encoding of the type of capture object member +2) a field that encodes additional properties, and +3) an offset within the lambda object where the value of that kernel argument is placed + +The previous sections described how kernel arguments are handled today. +The next three sections describe support for arrays. + +

Fix 1: Kernel Arguments that are Standard-Layout Arrays

+ +As described earlier, each variable captured by a lambda that comprises a +SYCL kernel becomes a parameter of the kernel caller function. +For arrays, simply allowing them through would result in a +function parameter of array type. This is not supported in C++. +Therefore, the array needing capture is wrapped in a struct for +the purposes of passing to the device. Once received on the device +within its wrapper, the array is copied into the local capture object. +All references to the array within the kernel body are directed to +the non-wrapped array which is a member of the local capture object. + +

Source code fragment:

+ +```C++ + int array[100]; + auto outBuf = buffer(&output[0], num_items); + + myQueue.submit([&](handler &cgh) { + auto outAcc = outBuf.get_access(cgh); + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + outAcc[index] = array[index.get(0)]; + }); + }); +``` + +

Integration header produced:

+ +```C++ +static constexpr +const kernel_param_desc_t kernel_signatures[] = { + //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE16->18clES2_E6Worker + { kernel_param_kind_t::kind_accessor, 4062, 0 }, + { kernel_param_kind_t::kind_std_layout, 400, 32 }, +}; +``` + +

The changes to device code made to support this extension, in pseudo-code:

+ +```C++ +struct Capture { + sycl::accessor outAcc; + int array[100]; + () { + // Body + } +} + +struct wrapper { + int array[100]; +}; +spir_kernel void caller( + __global int* AccData, // arg1 of accessor init function + range<1> AccR1, // arg2 of accessor init function + range<1> AccR2, // arg3 of accessor init function + id<1> I, // arg4 of accessor init function + struct wrapper w_s // Pass the array wrapped in a struct +) +{ + // Local capture object + struct Capture local; + + // Reassemble capture object from parts + // Initialize array using existing clang Initialization mechanisms + local.array = w_s; + // Call accessor’s init function + sycl::accessor::init(&local.outAcc, AccData, AccR1, AccR2, I); + + callee(&local, id<1> wi); +} +``` + +

Fix 2: Kernel Arguments that are Arrays of Accessors

+ +Arrays of accessors are supported in a manner similar to that of a plain +accessor. For each accessor array element, the four values required to +call its init function are passed as separate arguments to the kernel. +Reassembly within the kernel caller is done by calling the `init` functions +of each accessor array element in ascending index value. + +

Source code fragment:

+ +```C++ + myQueue.submit([&](handler &cgh) { + using Accessor = + accessor; + Accessor inAcc[2] = {in_buffer1.get_access(cgh), + in_buffer2.get_access(cgh)}; + auto outAcc = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + outAcc[index] = inAcc[0][index] + inAcc[1][index]; + }); + }); +``` + +

Integration header:

+ +```C++ +static constexpr +const kernel_param_desc_t kernel_signatures[] = { + //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE20->18clES2_E6Worker + { kernel_param_kind_t::kind_accessor, 4062, 0 }, + { kernel_param_kind_t::kind_accessor, 4062, 32 }, + { kernel_param_kind_t::kind_accessor, 4062, 64 }, +}; +``` + +

Device code generated in pseudo-code form:

+ +```C++ +struct Capture { + sycl::accessor outAcc; + sycl::accessor inAcc[2]; + () { + // Body + } +} + +spir_kernel void caller( + __global int* outAccData, // args of OutAcc + range<1> outAccR1, + range<1> outAccR2, + id<1> outI, + __global int* inAccData_0, // args of inAcc[0] + range<1> inAccR1_0, + range<1> inAccR2_0, + id<1> inI_0, + __global int* inAccData_1, // args of inAcc[1] + range<1> inAccR1_1, + range<1> inAccR2_1, + id<1> inI_1, +) +{ + // Local capture object + struct Capture local; + + // Reassemble capture object from parts + // Call outAcc accessor’s init function + sycl::accessor::init(&local.outAcc, outAccData, outAccR1, outAccR2, outI); + + // Call inAcc[0] accessor’s init function + sycl::accessor::init(&local.inAcc[0], inAccData_0, inAccR1_0, inAccR2_0, inI_0); + + // Call inAcc[1] accessor’s init function + sycl::accessor::init(&local.inAcc[1], inAccData_1, inAccR1_1, inAccR2_1, inI_1); + + callee(&local, id<1> wi); +} +``` + +

Fix 3: Accessor Arrays within Structs

+ +Kernel parameters that are structs are traversed member +by member, recursively, to enumerate member structs that are one of +the SYCL special types: `sycl::accessor` and `sycl::sampler`. +The arguments of the `init` functions of each special struct encountered +in the traversal are added as separate arguments to the kernel. +Support for arrays containing SYCL special types +builds upon the support for single accessors within structs. +Each element of such arrays is treated as +an individual object, and the arguments of its init function +are added to the kernel arguments in sequence. +Within the kernel caller function, the lambda object is reassembled +in a manner similar to other instances of accessor arrays. + + +

Source code fragment:

+ +```C++ + myQueue.submit([&](handler &cgh) { + using Accessor = + accessor; + struct S { + int m; + sycl::accessor inAcc[2]; + } s = { 55, + {in_buffer1.get_access(cgh), + in_buffer2.get_access(cgh)} + }; + auto outAcc = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + outAcc[index] = s.m + s.inAcc[0][index] + s.inAcc[1][index]; + }); +}); +``` + +

Integration header:

+ +```C++ +static constexpr +const kernel_param_desc_t kernel_signatures[] = { + //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE20->18clES2_E6Worker + { kernel_param_kind_t::kind_accessor, 4062, 0 }, + { kernel_param_kind_t::kind_std_layout, 72, 32 }, + { kernel_param_kind_t::kind_accessor, 4062, 40 }, + { kernel_param_kind_t::kind_accessor, 4062, 72 }, + +}; +``` + +

Device code generated in pseudo-code form:

+ +```C++ +struct Capture { + sycl::accessor outAcc; + struct S s; + () { + // Body + } +} + +spir_kernel void caller( + __global int* outAccData, // args of OutAcc + range<1> outAccR1, + range<1> outAccR2, + id<1> outI, + struct S s, // the struct S + __global int* inAccData_0, // args of s.inAcc[0] + range<1> inAccR1_0, + range<1> inAccR2_0, + id<1> inI_0, + __global int* inAccData_1, // args of s.inAcc[1] + range<1> inAccR1_1, + range<1> inAccR2_1, + id<1> inI_1, +) +{ + // Local capture object + struct Capture local; + + // Reassemble capture object from parts + + // 1. Copy struct argument contents to local copy + local.s = s; + + // 2. Initialize accessors by calling init functions + // 2a. Call outAcc accessor’s init function + sycl::accessor::init( + &local.outAcc, outAccData, outAccR1, outAccR2, outI); + + // 2b. Call s.inAcc[0] accessor’s init function + sycl::accessor::init( + &local.s.inAcc[0], inAccData_0, inAccR1_0, inAccR2_0, inI_0); + + // 2c. Call s.inAcc[1] accessor’s init function + sycl::accessor::init( + &local.s.inAcc[1], inAccData_1, inAccR1_1, inAccR2_1, inI_1); + + callee(&local, id<1> wi); +} +``` From 1bf090337d01834a045dd7d42ace24b6e6f58541 Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Fri, 12 Jun 2020 11:30:54 -0700 Subject: [PATCH 10/42] Formatting changes. --- clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp index 0f2b0b32d315c..b7384ae0af9f9 100755 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp @@ -1,5 +1,6 @@ -// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=/iusers/rdeodhar/work/dpcc/jira/7004/t.h %s -c -o %T/kernel.spv -// RUN: FileCheck -input-file=%t.h %s +// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang +// -fsycl-int-header=/iusers/rdeodhar/work/dpcc/jira/7004/t.h %s -c -o +// %T/kernel.spv RUN: FileCheck -input-file=%t.h %s // This test checks the integration header generated when // the kernel argument is an Accessor array. @@ -49,8 +50,5 @@ int main() { Accessor acc[2]; - a_kernel( - [=]() { - acc[1].use(); - }); + a_kernel([=]() { acc[1].use(); }); } From 5d5121b42794ee1f45bbd2fa34313df147437c9a Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Fri, 12 Jun 2020 16:06:09 -0700 Subject: [PATCH 11/42] Formatting changes. --- clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp index b7384ae0af9f9..f977534d260fd 100755 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp @@ -1,6 +1,5 @@ -// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -// -fsycl-int-header=/iusers/rdeodhar/work/dpcc/jira/7004/t.h %s -c -o -// %T/kernel.spv RUN: FileCheck -input-file=%t.h %s +// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=/iusers/rdeodhar/work/dpcc/jira/7004/t.h %s -c -o %T/kernel.spv +// RUN: FileCheck -input-file=%t.h %s // This test checks the integration header generated when // the kernel argument is an Accessor array. From 96ca8f4034d548237550dedb5f0285dd107b3c90 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Sun, 14 Jun 2020 19:57:12 -0700 Subject: [PATCH 12/42] Fix crash for stream type. Also changed handler call for consistency Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index adc93bab1a82d..a7c9156fc23e3 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -804,8 +804,7 @@ static void VisitStreamRecord(CXXRecordDecl *Owner, ParentTy &Parent, QualType FieldTy = Field->getType(); // Required to initialize accessors inside streams. if (Util::isSyclAccessorType(FieldTy)) - (void)std::initializer_list{ - (handlers.handleSyclAccessorType(Field, FieldTy), 0)...}; + KF_FOR_EACH(handleSyclAccessorType, Field, FieldTy); } (void)std::initializer_list{(handlers.leaveStruct(Owner, Parent), 0)...}; } @@ -1490,7 +1489,6 @@ class SyclKernelBodyCreator ILE->setType(QualType(RD->getTypeForDecl(), 0)); InitExprs.push_back(ILE); - MemberExprBases.pop_back(); } void leaveStruct(const CXXRecordDecl *, FieldDecl *FD) final { @@ -1507,11 +1505,13 @@ class SyclKernelBodyCreator InitExprs.pop_back(); } } + MemberExprBases.pop_back(); } void leaveStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { const CXXRecordDecl *BaseClass = BS.getType()->getAsCXXRecordDecl(); addStructInit(BaseClass); + MemberExprBases.pop_back(); } }; From f03edd9f30db18912ea8ca8df5ebe766fb0b5f56 Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Mon, 15 Jun 2020 09:00:11 -0700 Subject: [PATCH 13/42] Correction to a test. --- clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp index f977534d260fd..1020fbb4c12e2 100755 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=/iusers/rdeodhar/work/dpcc/jira/7004/t.h %s -c -o %T/kernel.spv +// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv // RUN: FileCheck -input-file=%t.h %s // This test checks the integration header generated when From 4868d455c149c08f3366eb07ad47938d1e4612a3 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Tue, 16 Jun 2020 19:18:25 -0700 Subject: [PATCH 14/42] Fixed some crashes after merge. Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index e6c6a97ac9d79..2076de02f5557 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -824,13 +824,21 @@ static void VisitRecordHelper(CXXRecordDecl *Owner, Handlers &... handlers) { for (const auto &Base : Range) { QualType BaseTy = Base.getType(); - if (Util::isSyclAccessorType(BaseTy)) + if (Util::isSyclAccessorType(BaseTy)) { + (void)std::initializer_list{ + (handlers.enterField(Owner, Base), 0)...}; (void)std::initializer_list{ (handlers.handleSyclAccessorType(Base, BaseTy), 0)...}; - else if (Util::isSyclStreamType(BaseTy)) + (void)std::initializer_list{ + (handlers.leaveField(Owner, Base), 0)...}; + } else if (Util::isSyclStreamType(BaseTy)) { + (void)std::initializer_list{ + (handlers.enterField(Owner, Base), 0)...}; (void)std::initializer_list{ (handlers.handleSyclStreamType(Base, BaseTy), 0)...}; - else + (void)std::initializer_list{ + (handlers.leaveField(Owner, Base), 0)...}; + } else VisitRecord(Owner, Base, BaseTy->getAsCXXRecordDecl(), handlers...); } } @@ -862,9 +870,13 @@ static void VisitStreamRecord(CXXRecordDecl *Owner, ParentTy &Parent, (void)std::initializer_list{(handlers.enterStruct(Owner, Parent), 0)...}; for (const auto &Field : Wrapper->fields()) { QualType FieldTy = Field->getType(); + (void)std::initializer_list{ + (handlers.enterField(Wrapper, Field), 0)...}; // Required to initialize accessors inside streams. if (Util::isSyclAccessorType(FieldTy)) KF_FOR_EACH(handleSyclAccessorType, Field, FieldTy); + (void)std::initializer_list{ + (handlers.leaveField(Wrapper, Field), 0)...}; } (void)std::initializer_list{(handlers.leaveStruct(Owner, Parent), 0)...}; } @@ -890,8 +902,7 @@ template static void VisitRecordFields(CXXRecordDecl *Owner, Handlers &... handlers) { for (const auto Field : Owner->fields()) { - (void)std::initializer_list{ - (handlers.enterField(nullptr, Field), 0)...}; + (void)std::initializer_list{(handlers.enterField(Owner, Field), 0)...}; QualType FieldTy = Field->getType(); if (Util::isSyclAccessorType(FieldTy)) @@ -921,8 +932,7 @@ static void VisitRecordFields(CXXRecordDecl *Owner, Handlers &... handlers) { KF_FOR_EACH(handleScalarType, Field, FieldTy); else KF_FOR_EACH(handleOtherType, Field, FieldTy); - (void)std::initializer_list{ - (handlers.leaveField(nullptr, Field), 0)...}; + (void)std::initializer_list{(handlers.leaveField(Owner, Field), 0)...}; } #undef KF_FOR_EACH } // namespace @@ -1404,11 +1414,12 @@ class SyclKernelBodyCreator VK_LValue, SourceLocation()); } - Expr *SpecialObjME = Base; + /*Expr *SpecialObjME = Base; if (Field) SpecialObjME = BuildMemberExpr(Base, Field); - MemberExpr *MethodME = BuildMemberExpr(SpecialObjME, Method); + MemberExpr *MethodME = BuildMemberExpr(SpecialObjME, Method);*/ + MemberExpr *MethodME = BuildMemberExpr(Base, Method); QualType ResultTy = Method->getReturnType(); ExprValueKind VK = Expr::getValueKindForType(ResultTy); ResultTy = ResultTy.getNonLValueExprType(SemaRef.Context); @@ -1550,7 +1561,7 @@ class SyclKernelBodyCreator } void enterStruct(const CXXRecordDecl *, FieldDecl *FD) final { - MemberExprBases.push_back(BuildMemberExpr(MemberExprBases.back(), FD)); + // MemberExprBases.push_back(BuildMemberExpr(MemberExprBases.back(), FD)); } void enterStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { @@ -1601,7 +1612,7 @@ class SyclKernelBodyCreator InitExprs.pop_back(); } } - MemberExprBases.pop_back(); + // MemberExprBases.pop_back(); } void leaveStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { From 35383c55c31242c586ea6259e3e6bd14172e5ef4 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Tue, 16 Jun 2020 20:30:54 -0700 Subject: [PATCH 15/42] Minor refactor Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 2076de02f5557..dfe2be59041b3 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -881,13 +881,6 @@ static void VisitStreamRecord(CXXRecordDecl *Owner, ParentTy &Parent, (void)std::initializer_list{(handlers.leaveStruct(Owner, Parent), 0)...}; } -int getFieldNumber(const CXXRecordDecl *BaseDecl) { - int Members = 0; - for (const auto *Field : BaseDecl->fields()) - ++Members; - - return Members; -} template static void VisitFunctorBases(CXXRecordDecl *KernelFunctor, @@ -1581,8 +1574,11 @@ class SyclKernelBodyCreator if (!RD) return; - int NumberOfFields = getFieldNumber(RD); + const ASTRecordLayout &Info = + SemaRef.getASTContext().getASTRecordLayout(RD); + int NumberOfFields = Info.getFieldCount(); int popOut = NumberOfFields + RD->getNumBases(); + llvm::SmallVector BaseInitExprs; for (int I = 0; I < popOut; I++) { BaseInitExprs.push_back(InitExprs.back()); From de9e2aa61a1f6b4fec18b44915fd3dad30a3d321 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Tue, 16 Jun 2020 20:31:16 -0700 Subject: [PATCH 16/42] Added lit test for inheritance AST check --- clang/test/SemaSYCL/inheritance.cpp | 70 +++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 clang/test/SemaSYCL/inheritance.cpp diff --git a/clang/test/SemaSYCL/inheritance.cpp b/clang/test/SemaSYCL/inheritance.cpp new file mode 100644 index 0000000000000..ff0b263449a35 --- /dev/null +++ b/clang/test/SemaSYCL/inheritance.cpp @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -ast-dump %s | FileCheck %s + +#include + +class second_base { +public: + int e; +}; + +class InnerFieldBase { +public: + int d; +}; +class InnerField : public InnerFieldBase { + int c; +}; + +struct base { +public: + int b; + InnerField obj; +}; + +struct derived : base, second_base { + int a; + + void operator()() { + } +}; + +int main() { + cl::sycl::queue q; + + q.submit([&](cl::sycl::handler &cgh) { + derived f{}; + cgh.single_task(f); + }); + + return 0; +} + +// Check declaration of the kernel +// CHECK: derived{{.*}} 'void (int, int, int, int, int)' + +// Check parameters of the kernel +// CHECK: ParmVarDecl {{.*}} used _arg_b 'int' +// CHECK: ParmVarDecl {{.*}} used _arg_d 'int' +// CHECK: ParmVarDecl {{.*}} used _arg_c 'int' +// CHECK: ParmVarDecl {{.*}} used _arg_e 'int' +// CHECK: ParmVarDecl {{.*}} used _arg_a 'int' + +// Check initializers for derived and base classes. +// Each class has it's own initializer list +// Base classes should be initialized first. +// CHECK: VarDecl {{.*}} derived 'derived' cinit +// CHECK-NEXT: InitListExpr {{.*}} 'derived' +// CHECK-NEXT: InitListExpr {{.*}} 'base' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue ParmVar {{.*}} '_arg_b' 'int' +// CHECK-NEXT: InitListExpr {{.*}} 'InnerField' +// CHECK-NEXT: InitListExpr {{.*}} 'InnerFieldBase' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue ParmVar {{.*}} '_arg_d' 'int' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue ParmVar {{.*}} '_arg_c' 'int' +// CHECK-NEXT: InitListExpr {{.*}} 'second_base' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue ParmVar {{.*}} '_arg_e' 'int' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue ParmVar {{.*}} '_arg_a' 'int' From 63cc36225c3e225cd027ae5f7320f362b79352de Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Mon, 22 Jun 2020 18:56:25 -0700 Subject: [PATCH 17/42] Do not visit accessor fields Patch by: Mariya Podchishchaeva Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index dfe2be59041b3..30c1e1a6948e7 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -791,12 +791,12 @@ static void VisitField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, Handlers &... handlers) { if (Util::isSyclAccessorType(ItemTy)) KF_FOR_EACH(handleSyclAccessorType, Item, ItemTy); - if (Util::isSyclStreamType(ItemTy)) + else if (Util::isSyclStreamType(ItemTy)) KF_FOR_EACH(handleSyclStreamType, Item, ItemTy); - if (ItemTy->isStructureOrClassType()) + else if (ItemTy->isStructureOrClassType()) VisitRecord(Owner, Item, ItemTy->getAsCXXRecordDecl(), handlers...); - if (ItemTy->isArrayType()) + else if (ItemTy->isArrayType()) VisitArrayElements(Item, ItemTy, handlers...); } From 51b598e009ce135be3a69309c82e51c5d5c56831 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Mon, 22 Jun 2020 19:26:48 -0700 Subject: [PATCH 18/42] Do not generate default initializers for accessors in array Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 30c1e1a6948e7..aba98a4b727a0 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1277,6 +1277,7 @@ class SyclKernelBodyCreator CXXRecordDecl *KernelObj; llvm::SmallVector MemberExprBases; FunctionDecl *KernelCallerFunc; + bool ArrayState; // Using the statements/init expressions that we've created, this generates // the kernel body compound stmt. CompoundStmt needs to know its number of @@ -1447,14 +1448,16 @@ class SyclKernelBodyCreator const auto *RecordDecl = Ty->getAsCXXRecordDecl(); // TODO: VarEntity is initialized entity for KernelObjClone, I guess we need // to create new one when enter new struct. - InitializedEntity Entity = - InitializedEntity::InitializeMember(FD, &VarEntity); - // Initialize with the default constructor. - InitializationKind InitKind = - InitializationKind::CreateDefault(SourceLocation()); - InitializationSequence InitSeq(SemaRef, Entity, InitKind, None); - ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, None); - InitExprs.push_back(MemberInit.get()); + if (!ArrayState) { + InitializedEntity Entity = + InitializedEntity::InitializeMember(FD, &VarEntity); + // Initialize with the default constructor. + InitializationKind InitKind = + InitializationKind::CreateDefault(SourceLocation()); + InitializationSequence InitSeq(SemaRef, Entity, InitKind, None); + ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, None); + InitExprs.push_back(MemberInit.get()); + } createSpecialMethodCall(RecordDecl, MemberExprBases.back(), InitMethodName, FD); return true; @@ -1486,7 +1489,8 @@ class SyclKernelBodyCreator KernelObjClone(createKernelObjClone(S.getASTContext(), DC.getKernelDecl(), KernelObj)), VarEntity(InitializedEntity::InitializeVariable(KernelObjClone)), - KernelObj(KernelObj), KernelCallerFunc(KernelCallerFunc) { + KernelObj(KernelObj), KernelCallerFunc(KernelCallerFunc), + ArrayState(false) { markParallelWorkItemCalls(); Stmt *DS = new (S.Context) DeclStmt(DeclGroupRef(KernelObjClone), @@ -1628,6 +1632,7 @@ class SyclKernelBodyCreator } void enterArray() final { + ArrayState = true; Expr *ArrayBase = MemberExprBases.back(); ExprResult IndexExpr = SemaRef.ActOnIntegerConstant(SourceLocation(), 0); ExprResult ElementBase = SemaRef.CreateBuiltinArraySubscriptExpr( @@ -1650,7 +1655,10 @@ class SyclKernelBodyCreator MemberExprBases.push_back(ElementBase.get()); } - void leaveArray(QualType, int64_t) final { MemberExprBases.pop_back(); } + void leaveArray(QualType, int64_t) final { + ArrayState = false; + MemberExprBases.pop_back(); + } using SyclKernelFieldHandler::enterArray; using SyclKernelFieldHandler::enterField; From 2a1e9ba15a0ef5deb67cccc5661e3af966645848 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Wed, 24 Jun 2020 19:17:28 -0700 Subject: [PATCH 19/42] Added CodeGen lit test Signed-off-by: Elizabeth Andrews --- clang/test/CodeGenSYCL/inheritance.cpp | 84 ++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 clang/test/CodeGenSYCL/inheritance.cpp diff --git a/clang/test/CodeGenSYCL/inheritance.cpp b/clang/test/CodeGenSYCL/inheritance.cpp new file mode 100644 index 0000000000000..4ac785336fb39 --- /dev/null +++ b/clang/test/CodeGenSYCL/inheritance.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s + +#include + +class second_base { +public: + int e; +}; + +class InnerFieldBase { +public: + int d; +}; +class InnerField : public InnerFieldBase { + int c; +}; + +struct base { +public: + int b; + InnerField obj; +}; + +struct derived : base, second_base { + int a; + + void operator()() { + } +}; + +int main() { + cl::sycl::queue q; + + q.submit([&](cl::sycl::handler &cgh) { + derived f{}; + cgh.single_task(f); + }); + + return 0; +} + +// Check kernel paramters +// CHECK: define spir_kernel void @{{.*}}derived(i32 %_arg_b, i32 %_arg_d, i32 %_arg_c, i32 %_arg_e, i32 %_arg_a) + +// Check alloca for kernel paramters +// CHECK: %[[ARG_B:[a-zA-Z0-9_.]+]] = alloca i32, align 4 +// CHECK: %[[ARG_D:[a-zA-Z0-9_.]+]] = alloca i32, align 4 +// CHECK: %[[ARG_C:[a-zA-Z0-9_.]+]] = alloca i32, align 4 +// CHECK: %[[ARG_E:[a-zA-Z0-9_.]+]] = alloca i32, align 4 +// CHECK: %[[ARG_A:[a-zA-Z0-9_.]+]] = alloca i32, align 4 + +// Check alloca for local functor object +// CHECK: %[[LOCAL_OBJECT:[a-zA-Z0-9_.]+]] = alloca %struct.{{.*}}.derived, align 4 + +// Initialize field 'b' +// CHECK: %[[BITCAST1:[0-9]+]] = bitcast %struct.{{.*}}.derived* %[[LOCAL_OBJECT]] to %struct.{{.*}}.base* +// CHECK: %[[GEP_B:[a-zA-Z0-9]+]] = getelementptr inbounds %struct.{{.*}}.base, %struct.{{.*}}.base* %[[BITCAST1]], i32 0, i32 0 +// CHECK: %[[LOAD_B:[0-9]+]] = load i32, i32* %[[ARG_B]], align 4 +// CHECK: store i32 %[[LOAD_B]], i32* %[[GEP_B]], align 4 + +// Initialize field 'd' +// CHECK: %[[GEP_OBJ:[a-zA-Z0-9]+]] = getelementptr inbounds %struct.{{.*}}.base, %struct.{{.*}}.base* %[[BITCAST1]], i32 0, i32 1 +// CHECK: %[[BITCAST2:[0-9]+]] = bitcast %class.{{.*}}.InnerField* %[[GEP_OBJ]] to %class.{{.*}}.InnerFieldBase* +// CHECK: %[[GEP_D:[a-zA-Z0-9]+]] = getelementptr inbounds %class.{{.*}}.InnerFieldBase, %class.{{.*}}.InnerFieldBase* %[[BITCAST2]], i32 0, i32 0 +// CHECK: %[[LOAD_D:[0-9]+]] = load i32, i32* %[[ARG_D]], align 4 +// CHECK: store i32 %[[LOAD_D]], i32* %[[GEP_D]], align 4 + +// Initialize field 'c' +// CHECK: %[[GEP_C:[a-zA-Z0-9]+]] = getelementptr inbounds %class.{{.*}}.InnerField, %class.{{.*}}.InnerField* %[[GEP_OBJ]], i32 0, i32 1 +// CHECK: %[[LOAD_C:[0-9]+]] = load i32, i32* %[[ARG_C]], align 4 +// CHECK: store i32 %[[LOAD_C]], i32* %[[GEP_C]], align 4 + +// Initialize field 'e' +// CHECK: %[[BITCAST3:[0-9]+]] = bitcast %struct.{{.*}}.derived* %[[LOCAL_OBJECT]] to i8* +// CHECK: %[[GEP_DERIVED:[a-zA-Z0-9]+]] = getelementptr inbounds i8, i8* %[[BITCAST3]], i64 12 +// CHECK: %[[BITCAST4:[0-9]+]] = bitcast i8* %[[GEP_DERIVED]] to %class.{{.*}}.second_base* +// CHECK: %[[GEP_E:[a-zA-Z0-9]+]] = getelementptr inbounds %class.{{.*}}.second_base, %class.{{.*}}.second_base* %[[BITCAST4]], i32 0, i32 0 +// CHECK: %[[LOAD_E:[0-9]+]] = load i32, i32* %[[ARG_E]], align 4 +// CHECK: store i32 %[[LOAD_E]], i32* %[[GEP_E]], align 4 + +// Initialize field 'a' +// CHECK: %[[GEP_A:[a-zA-Z0-9]+]] = getelementptr inbounds %struct.{{.*}}.derived, %struct.{{.*}}.derived* %[[LOCAL_OBJECT]], i32 0, i32 2 +// CHECK: %[[LOAD_A:[0-9]+]] = load i32, i32* %[[ARG_A]], align 4 +// CHECK: store i32 %[[LOAD_A]], i32* %[[GEP_A]], align 4 From 0412db38150147199a6090a307678be53bb399ed Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Wed, 24 Jun 2020 21:10:27 -0700 Subject: [PATCH 20/42] Array elements are now passed as individual parameters. --- clang/lib/Sema/SemaSYCL.cpp | 147 +++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index d0a016a39b23f..6cc14e3df4835 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -688,22 +688,6 @@ static ParamDesc makeParamDesc(ASTContext &Ctx, const CXXBaseSpecifier &Src, Ctx.getTrivialTypeSourceInfo(Ty)); } -// Create a new class around a field - used to wrap arrays. -static RecordDecl *wrapAnArray(ASTContext &Ctx, const QualType ArgTy, - FieldDecl *&Field) { - RecordDecl *NewClass = Ctx.buildImplicitRecord("wrapped_array"); - NewClass->startDefinition(); - Field = FieldDecl::Create( - Ctx, NewClass, SourceLocation(), SourceLocation(), - /*Id=*/nullptr, ArgTy, - Ctx.getTrivialTypeSourceInfo(ArgTy, SourceLocation()), - /*BW=*/nullptr, /*Mutable=*/false, /*InitStyle=*/ICIS_NoInit); - Field->setAccess(AS_public); - NewClass->addDecl(Field); - NewClass->completeDefinition(); - return NewClass; -} - /// \return the target of given SYCL accessor type static target getAccessTarget(const ClassTemplateSpecializationDecl *AccTy) { return static_cast( @@ -799,15 +783,21 @@ static void VisitField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, Handlers &... handlers) { if (Util::isSyclAccessorType(ItemTy)) KF_FOR_EACH(handleSyclAccessorType, Item, ItemTy); - if (Util::isSyclStreamType(ItemTy)) + else if (Util::isSyclStreamType(ItemTy)) KF_FOR_EACH(handleSyclStreamType, Item, ItemTy); - if (ItemTy->isStructureOrClassType()) + else if (ItemTy->isStructureOrClassType()) VisitAccessorWrapper(Owner, Item, ItemTy->getAsCXXRecordDecl(), handlers...); - if (ItemTy->isArrayType()) + else if (ItemTy->isArrayType()) VisitArrayElements(Item, ItemTy, handlers...); } +template +static void VisitScalarField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, + Handlers &... handlers) { + KF_FOR_EACH(handleScalarType, Item, ItemTy); +} + template static void VisitArrayElements(RangeTy Item, QualType FieldTy, Handlers &... handlers) { @@ -816,7 +806,10 @@ static void VisitArrayElements(RangeTy Item, QualType FieldTy, int64_t ElemCount = CAT->getSize().getSExtValue(); std::initializer_list{(handlers.enterArray(), 0)...}; for (int64_t Count = 0; Count < ElemCount; Count++) { - VisitField(nullptr, Item, ET, handlers...); + if (ET->isScalarType()) + VisitScalarField(nullptr, Item, ET, handlers...); + else + VisitField(nullptr, Item, ET, handlers...); (void)std::initializer_list{(handlers.nextElement(ET), 0)...}; } (void)std::initializer_list{(handlers.leaveArray(ET, ElemCount), 0)...}; @@ -919,6 +912,9 @@ template class SyclKernelFieldHandler { virtual bool handleReferenceType(FieldDecl *, QualType) { return true; } virtual bool handlePointerType(FieldDecl *, QualType) { return true; } virtual bool handleArrayType(FieldDecl *, QualType) { return true; } + virtual bool handleScalarType(const CXXBaseSpecifier &, QualType) { + return true; + } virtual bool handleScalarType(FieldDecl *, QualType) { return true; } // Most handlers shouldn't be handling this, just the field checker. virtual bool handleOtherType(FieldDecl *, QualType) { return true; } @@ -1003,7 +999,8 @@ class SyclKernelFieldChecker public: SyclKernelFieldChecker(Sema &S) - : SyclKernelFieldHandler(S), Diag(S.getASTContext().getDiagnostics()) {} + : SyclKernelFieldHandler(S), Diag(S.getASTContext().getDiagnostics()) { + } bool isValid() { return !IsInvalid; } bool handleReferenceType(FieldDecl *FD, QualType FieldTy) final { @@ -1052,6 +1049,10 @@ class SyclKernelDeclCreator size_t LastParamIndex = 0; void addParam(const FieldDecl *FD, QualType FieldTy) { + const ConstantArrayType *CAT = + SemaRef.getASTContext().getAsConstantArrayType(FieldTy); + if (CAT) + FieldTy = CAT->getElementType(); ParamDesc newParamDesc = makeParamDesc(FD, FieldTy); addParam(newParamDesc, FieldTy); } @@ -1068,7 +1069,6 @@ class SyclKernelDeclCreator SemaRef.getASTContext(), KernelDecl, SourceLocation(), SourceLocation(), std::get<1>(newParamDesc), std::get<0>(newParamDesc), std::get<2>(newParamDesc), SC_None, /*DefArg*/ nullptr); - NewParam->setScopeInfo(0, Params.size()); NewParam->setIsUsed(); @@ -1131,7 +1131,8 @@ class SyclKernelDeclCreator : SyclKernelFieldHandler(S), KernelDecl(createKernelDecl(S.getASTContext(), Name, Loc, IsInline, IsSIMDKernel)), - ArgChecker(ArgChecker), FuncContext(SemaRef, KernelDecl) {} + ArgChecker(ArgChecker), FuncContext(SemaRef, KernelDecl) { + } ~SyclKernelDeclCreator() { ASTContext &Ctx = SemaRef.getASTContext(); @@ -1189,18 +1190,12 @@ class SyclKernelDeclCreator return true; } - bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { - RecordDecl *NewClass = wrapAnArray(SemaRef.getASTContext(), FieldTy, FD); - QualType ST = SemaRef.getASTContext().getRecordType(NewClass); - addParam(FD, ST); - return true; - } - bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy); return true; } + //FIXME Remove this function when structs are replaced by their fields bool handleStructType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy); return true; @@ -1225,6 +1220,8 @@ class SyclKernelDeclCreator return ArrayRef(std::begin(Params) + LastParamIndex, std::end(Params)); } + + using SyclKernelFieldHandler::handleScalarType; }; class SyclKernelBodyCreator @@ -1309,11 +1306,9 @@ class SyclKernelBodyCreator return Result; } - void createExprForStructOrScalar(FieldDecl *FD) { + Expr *createInitExpr(FieldDecl *FD) { ParmVarDecl *KernelParameter = DeclCreator.getParamVarDeclsForCurrentField()[0]; - InitializedEntity Entity = - InitializedEntity::InitializeMember(FD, &VarEntity); QualType ParamType = KernelParameter->getOriginalType(); Expr *DRE = SemaRef.BuildDeclRefExpr(KernelParameter, ParamType, VK_LValue, SourceLocation()); @@ -1323,32 +1318,49 @@ class SyclKernelBodyCreator DRE = ImplicitCastExpr::Create(SemaRef.Context, FD->getType(), CK_AddressSpaceConversion, DRE, nullptr, VK_RValue); + return DRE; + } + + void createExprForStructOrScalar(FieldDecl *FD) { + InitializedEntity Entity = + InitializedEntity::InitializeMember(FD, &VarEntity); InitializationKind InitKind = InitializationKind::CreateCopy(SourceLocation(), SourceLocation()); + Expr *DRE = createInitExpr(FD); InitializationSequence InitSeq(SemaRef, Entity, InitKind, DRE); - ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, DRE); InitExprs.push_back(MemberInit.get()); } - void createExprForArray(FieldDecl *FD) { - ParmVarDecl *KernelParameter = - DeclCreator.getParamVarDeclsForCurrentField()[0]; - QualType ParamType = KernelParameter->getOriginalType(); - CXXRecordDecl *WrapperStruct = ParamType->getAsCXXRecordDecl(); - // The first and only field of the wrapper struct is the array - FieldDecl *Array = *(WrapperStruct->field_begin()); - Expr *DRE = SemaRef.BuildDeclRefExpr(KernelParameter, ParamType, VK_LValue, - SourceLocation()); - Expr *InitExpr = BuildMemberExpr(DRE, Array); - InitializationKind InitKind = InitializationKind::CreateDirect( - SourceLocation(), SourceLocation(), SourceLocation()); - InitializedEntity Entity = InitializedEntity::InitializeLambdaCapture( - nullptr, Array->getType(), SourceLocation()); - InitializationSequence InitSeq(SemaRef, Entity, InitKind, InitExpr); - ExprResult MemberInit = - InitSeq.Perform(SemaRef, Entity, InitKind, InitExpr); - InitExprs.push_back(MemberInit.get()); + void createExprForScalarElement(FieldDecl *FD, QualType FieldTy) { + InitializedEntity ArrayEntity = + InitializedEntity::InitializeMember(FD, &VarEntity); + InitializationKind InitKind = + InitializationKind::CreateCopy(SourceLocation(), SourceLocation()); + Expr *DRE = createInitExpr(FD); + Expr *Idx = dyn_cast(MemberExprBases.back())->getIdx(); + llvm::APSInt Result; + SemaRef.VerifyIntegerConstantExpression(Idx, &Result); + uint64_t IntIdx = Result.getZExtValue(); + InitializedEntity Entity = InitializedEntity::InitializeElement( + SemaRef.getASTContext(), IntIdx, ArrayEntity); + InitializationSequence InitSeq(SemaRef, Entity, InitKind, DRE); + ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, DRE); + llvm::SmallVector ArrayInitExprs; + if (IntIdx > 0) { + // Continue with the current InitList + InitListExpr *ILE = cast(InitExprs.back()); + InitExprs.pop_back(); + llvm::ArrayRef L = ILE->inits(); + for (size_t I = 0; I < L.size(); I++) + ArrayInitExprs.push_back(L[I]); + } + ArrayInitExprs.push_back(MemberInit.get()); + Expr *ILE = new (SemaRef.getASTContext()) + InitListExpr(SemaRef.getASTContext(), SourceLocation(), ArrayInitExprs, + SourceLocation()); + ILE->setType(FD->getType()); + InitExprs.push_back(ILE); } void createSpecialMethodCall(const CXXRecordDecl *SpecialClass, Expr *Base, @@ -1479,18 +1491,17 @@ class SyclKernelBodyCreator return true; } + //FIXME Remove this function when structs are replaced by their fields bool handleStructType(FieldDecl *FD, QualType FieldTy) final { createExprForStructOrScalar(FD); return true; } bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { - createExprForStructOrScalar(FD); - return true; - } - - bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { - createExprForArray(FD); + if (dyn_cast(MemberExprBases.back())) + createExprForScalarElement(FD, FieldTy); + else + createExprForStructOrScalar(FD); return true; } @@ -1531,6 +1542,7 @@ class SyclKernelBodyCreator using SyclKernelFieldHandler::enterArray; using SyclKernelFieldHandler::enterField; + using SyclKernelFieldHandler::handleScalarType; using SyclKernelFieldHandler::leaveField; }; @@ -1547,13 +1559,9 @@ class SyclKernelIntHeaderCreator uint64_t Size; const ConstantArrayType *CAT = SemaRef.getASTContext().getAsConstantArrayType(ArgTy); - if (CAT) { - QualType ET = CAT->getElementType(); - Size = static_cast(CAT->getSize().getZExtValue()) * - SemaRef.getASTContext().getTypeSizeInChars(ET).getQuantity(); - } else { - Size = SemaRef.getASTContext().getTypeSizeInChars(ArgTy).getQuantity(); - } + if (CAT) + ArgTy = CAT->getElementType(); + Size = SemaRef.getASTContext().getTypeSizeInChars(ArgTy).getQuantity(); Header.addParamDesc(Kind, static_cast(Size), static_cast(CurOffset)); } @@ -1630,12 +1638,7 @@ class SyclKernelIntHeaderCreator return true; } - bool handleArrayType(FieldDecl *FD, QualType FieldTy) final { - wrapAnArray(SemaRef.getASTContext(), FieldTy, FD); - addParam(FD, FD->getType(), SYCLIntegrationHeader::kind_std_layout); - return true; - } - + //FIXME Remove this function when structs are replaced by their fields bool handleStructType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); return true; @@ -1692,6 +1695,8 @@ class SyclKernelIntHeaderCreator } CurOffset -= ArraySize; } + + using SyclKernelFieldHandler::handleScalarType; }; } // namespace From d620e4ee06de22dd4907001ecd5607a6bc920224 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Thu, 25 Jun 2020 08:21:37 -0700 Subject: [PATCH 21/42] Clang-Format Changes Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 2f2e5f59d7cb9..f21b190d28d12 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -797,8 +797,7 @@ static void VisitField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, else if (Util::isSyclSamplerType(ItemTy)) KF_FOR_EACH(handleSyclSamplerType, Item, ItemTy); else if (ItemTy->isStructureOrClassType()) - VisitRecord(Owner, Item, ItemTy->getAsCXXRecordDecl(), - handlers...); + VisitRecord(Owner, Item, ItemTy->getAsCXXRecordDecl(), handlers...); else if (ItemTy->isArrayType()) VisitArrayElements(Item, ItemTy, handlers...); } @@ -1669,8 +1668,8 @@ class SyclKernelBodyCreator using SyclKernelFieldHandler::enterArray; using SyclKernelFieldHandler::enterField; - using SyclKernelFieldHandler::leaveField; using SyclKernelFieldHandler::handleSyclSamplerType; + using SyclKernelFieldHandler::leaveField; }; class SyclKernelIntHeaderCreator From af0b0c948654023f79cc2a6e43cd71dc5aa9ed18 Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Thu, 25 Jun 2020 12:38:22 -0700 Subject: [PATCH 22/42] Corrections to temporarily disable tests expected to fail. --- clang/lib/Sema/SemaSYCL.cpp | 5 +++++ clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp | 1 + clang/test/CodeGenSYCL/kernel-param-acc-array.cpp | 1 + clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp | 1 + clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp | 1 + clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp | 1 + clang/test/CodeGenSYCL/kernel-param-pod-array.cpp | 1 + clang/test/SemaSYCL/array-kernel-param.cpp | 1 + sycl/test/array_param/array-kernel-param-run.cpp | 1 + 9 files changed, 13 insertions(+) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 21d5b2e9f42eb..50c5a0804241a 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -786,11 +786,16 @@ static void VisitField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, KF_FOR_EACH(handleSyclAccessorType, Item, ItemTy); else if (Util::isSyclStreamType(ItemTy)) KF_FOR_EACH(handleSyclStreamType, Item, ItemTy); + else if (Util::isSyclSamplerType(ItemTy)) + KF_FOR_EACH(handleSyclSamplerType, Item, ItemTy); else if (ItemTy->isStructureOrClassType()) VisitAccessorWrapper(Owner, Item, ItemTy->getAsCXXRecordDecl(), handlers...); +#if 0 + // FIXME Enable this when structs are replaced by their fields else if (ItemTy->isArrayType()) VisitArrayElements(Item, ItemTy, handlers...); +#endif } template diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp index 1020fbb4c12e2..5ac40c1d18d18 100755 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp @@ -1,5 +1,6 @@ // RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv // RUN: FileCheck -input-file=%t.h %s +// XFAIL: * // This test checks the integration header generated when // the kernel argument is an Accessor array. diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp index 2306599393b7a..48ca6def5f74a 100644 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s +// XFAIL: * // This test checks a kernel argument that is an Accessor array diff --git a/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp index 649d6fd88f0b8..21726109a1be9 100644 --- a/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp @@ -1,5 +1,6 @@ // RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv // RUN: FileCheck -input-file=%t.h %s +// XFAIL: * // This test checks the integration header when kernel argument // is a struct containing an Accessor array. diff --git a/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp b/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp index d72691eb0e279..ae476edf08c2e 100644 --- a/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -fsycl-int-header=%t.h -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s +// XFAIL: * // This test checks a kernel with struct parameter that contains an Accessor array. diff --git a/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp index 799a0fb9183f1..0c57501c7a497 100755 --- a/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp @@ -1,5 +1,6 @@ // RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv // RUN: FileCheck -input-file=%t.h %s +// XFAIL: * // This test checks the integration header generated for a kernel // with an argument that is a POD array. diff --git a/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp b/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp index 7549afa16c91d..191b740aca1aa 100755 --- a/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s +// XFAIL: * // This test checks a kernel with an argument that is a POD array. diff --git a/clang/test/SemaSYCL/array-kernel-param.cpp b/clang/test/SemaSYCL/array-kernel-param.cpp index d94c76600bb13..69fa04f048884 100755 --- a/clang/test/SemaSYCL/array-kernel-param.cpp +++ b/clang/test/SemaSYCL/array-kernel-param.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -ast-dump %s | FileCheck %s +// XFAIL: * // This test checks that compiler generates correct kernel arguments for // arrays, Accessor arrays, and structs containing Accessors. diff --git a/sycl/test/array_param/array-kernel-param-run.cpp b/sycl/test/array_param/array-kernel-param-run.cpp index 179901a072985..379066340714d 100755 --- a/sycl/test/array_param/array-kernel-param-run.cpp +++ b/sycl/test/array_param/array-kernel-param-run.cpp @@ -5,6 +5,7 @@ // RUN: %CPU_RUN_PLACEHOLDER %t.out // RUN: %GPU_RUN_PLACEHOLDER %t.out // RUN: %ACC_RUN_PLACEHOLDER %t.out +// XFAIL: * #include #include From d5fb2d95b74355199b78c1823edfaba1b5243650 Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Thu, 25 Jun 2020 17:13:59 -0700 Subject: [PATCH 23/42] Changed tests to work with current array support. --- .../CodeGenSYCL/kernel-param-acc-array-ih.cpp | 2 - .../CodeGenSYCL/kernel-param-acc-array.cpp | 54 ++++++------- .../CodeGenSYCL/kernel-param-pod-array-ih.cpp | 11 +-- .../CodeGenSYCL/kernel-param-pod-array.cpp | 28 ++++--- clang/test/SemaSYCL/array-kernel-param.cpp | 77 +++++++++---------- 5 files changed, 82 insertions(+), 90 deletions(-) diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp index 5ac40c1d18d18..f9dfd144079e1 100755 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp @@ -1,6 +1,5 @@ // RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv // RUN: FileCheck -input-file=%t.h %s -// XFAIL: * // This test checks the integration header generated when // the kernel argument is an Accessor array. @@ -21,7 +20,6 @@ // CHECK: static constexpr // CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { // CHECK-NEXT: //--- _ZTSZ4mainE8kernel_A -// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 24, 0 }, // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 0 }, // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 12 }, // CHECK-EMPTY: diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp index 48ca6def5f74a..c70e25b2feb72 100644 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s -// XFAIL: * // This test checks a kernel argument that is an Accessor array @@ -26,15 +25,14 @@ int main() { // Check kernel_A parameters // CHECK: define spir_kernel void @{{.*}}kernel_A -// CHECK-SAME: %struct.{{.*}}.wrapped_array* byval{{.*}}align 4 %_arg_, -// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG1:%[a-zA-Z0-9_]+_1]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE1:%[a-zA-Z0-9_]+_2]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE1:%[a-zA-Z0-9_]+_3]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET1:%[a-zA-Z0-9_]+_4]], -// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG2:%[a-zA-Z0-9_]+_5]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE2:%[a-zA-Z0-9_]+_7]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE2:%[a-zA-Z0-9_]+_8]], -// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET2:%[a-zA-Z0-9_]+_9]]) +// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG1:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE1:%[a-zA-Z0-9_]+_1]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE1:%[a-zA-Z0-9_]+_2]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET1:%[a-zA-Z0-9_]+_3]], +// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG2:%[a-zA-Z0-9_]+_4]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE2:%[a-zA-Z0-9_]+_6]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE2:%[a-zA-Z0-9_]+_7]], +// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET2:%[a-zA-Z0-9_]+_8]]) // CHECK alloca for pointer arguments // CHECK: [[MEM_ARG1:%[a-zA-Z0-9_.]+]] = alloca i32 addrspace(1)*, align 8 @@ -51,30 +49,26 @@ int main() { // CHECK: [[MEM_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" // CHECK: [[OFFSET2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" -// Check array initialization -// CHECK: arrayinit.body: -// CHECK: arrayinit.end: +// CHECK accessor array GEP for acc[0] +// CHECK: [[ACCESSOR_ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY1]], i64 0, i64 0 -// CHECK accessor array GEP for acc[0] -// CHECK: [[ACCESSOR_ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY1]], i64 0, i64 0 +// CHECK load from kernel pointer argument alloca +// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]] -// CHECK load from kernel pointer argument alloca -// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]] +// CHECK acc[0] __init method call +// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z0]] to %"class{{.*}}accessor" addrspace(4)* -// CHECK acc[0] __init method call -// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z0]] to %"class{{.*}}accessor" addrspace(4)* +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) -// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) +// CHECK accessor array GEP for acc[1] +// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY2]], i64 0, i64 1 -// CHECK accessor array GEP for acc[1] -// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY2]], i64 0, i64 1 +// CHECK load from kernel pointer argument alloca +// CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG2]] -// CHECK load from kernel pointer argument alloca -// CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG2]] +// CHECK acc[1] __init method call +// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)* -// CHECK acc[1] __init method call -// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)* - -// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) diff --git a/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp index 0c57501c7a497..349d540f22ebc 100755 --- a/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp @@ -1,7 +1,4 @@ // RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv -// RUN: FileCheck -input-file=%t.h %s -// XFAIL: * - // This test checks the integration header generated for a kernel // with an argument that is a POD array. @@ -21,7 +18,11 @@ // CHECK: static constexpr // CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { // CHECK-NEXT: //--- _ZTSZ4mainE8kernel_B -// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 400, 0 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 0 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 4 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 8 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 12 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 16 }, // CHECK-EMPTY: // CHECK-NEXT: }; @@ -43,7 +44,7 @@ __attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { int main() { - int a[100]; + int a[5]; a_kernel( [=]() { diff --git a/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp b/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp index 191b740aca1aa..9a35239ad1fb8 100755 --- a/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-pod-array.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s -// XFAIL: * // This test checks a kernel with an argument that is a POD array. @@ -14,26 +13,31 @@ __attribute__((sycl_kernel)) void a_kernel(Func kernelFunc) { int main() { - int a[100]; + int a[2]; a_kernel( [=]() { - int local = a[3]; + int local = a[1]; }); } // Check kernel_B parameters // CHECK: define spir_kernel void @{{.*}}kernel_B -// CHECK-SAME: %struct.{{.*}}.wrapped_array* byval{{.*}}align 4 [[ARG_STRUCT:%[a-zA-Z0-9_]+]] +// CHECK-SAME: i32 [[ELEM_ARG0:%[a-zA-Z0-9_]+]], +// CHECK-SAME: i32 [[ELEM_ARG1:%[a-zA-Z_]+_[0-9]+]]) // Check local lambda object alloca -// CHECK: [[LOCAL_OBJECT:%0]] = alloca %"class.{{.*}}.anon", align 4 +// CHECK: [[LOCAL_OBJECT:%[0-9]+]] = alloca %"class.{{.*}}.anon", align 4 -// Check init of local array -// CHECK: [[ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 - -// CHECK: [[ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct.{{.*}}.wrapped_array, %struct.{{.*}}.wrapped_array* [[ARG_STRUCT]], i32 0, i32 0 +// Check local variables created for parameters +// CHECK: store i32 [[ELEM_ARG0]], i32* [[ELEM_L0:%[a-zA-Z_]+.addr]], align 4 +// CHECK: store i32 [[ELEM_ARG1]], i32* [[ELEM_L1:%[a-zA-Z_]+.addr[0-9]*]], align 4 -// CHECK: %{{[a-zA-Z0-9._]+}} = getelementptr inbounds [100 x i32], [100 x i32]* [[ARRAY1]], i64 0, i64 0 - -// CHECK: %{{[a-zA-Z0-9_]+}} = getelementptr inbounds [100 x i32], [100 x i32]* [[ARRAY2]], i64 0, i64 +// Check init of local array +// CHECK: [[ARRAY:%[0-9]*]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[ARRAY_BEGIN:%[a-zA-Z_.]+]] = getelementptr inbounds [2 x i32], [2 x i32]* [[ARRAY]], i64 0, i64 0 +// CHECK: [[ARRAY0:%[0-9]*]] = load i32, i32* [[ELEM_L0]], align 4 +// CHECK: store i32 [[ARRAY0]], i32* [[ARRAY_BEGIN]], align 4 +// CHECK: [[ARRAY_ELEMENT:%[a-zA-Z_.]+]] = getelementptr inbounds i32, i32* %arrayinit.begin, i64 1 +// CHECK: [[ARRAY1:%[0-9]*]] = load i32, i32* [[ELEM_L1]], align 4 +// CHECK: store i32 [[ARRAY1]], i32* [[ARRAY_ELEMENT]], align 4 \ No newline at end of file diff --git a/clang/test/SemaSYCL/array-kernel-param.cpp b/clang/test/SemaSYCL/array-kernel-param.cpp index 69fa04f048884..19d8562da1436 100755 --- a/clang/test/SemaSYCL/array-kernel-param.cpp +++ b/clang/test/SemaSYCL/array-kernel-param.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -ast-dump %s | FileCheck %s -// XFAIL: * // This test checks that compiler generates correct kernel arguments for // arrays, Accessor arrays, and structs containing Accessors. @@ -19,7 +18,7 @@ int main() { accessor; Accessor acc[2]; - int a[100]; + int a[2]; struct struct_acc_t { Accessor member_acc[4]; } struct_acc; @@ -31,7 +30,7 @@ int main() { a_kernel( [=]() { - int local = a[3]; + int local = a[1]; }); a_kernel( @@ -41,8 +40,7 @@ int main() { } // Check kernel_A parameters -// CHECK: FunctionDecl {{.*}}kernel_A{{.*}} 'void (wrapped_array, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'wrapped_array' +// CHECK: FunctionDecl {{.*}}kernel_A{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ '__global int *' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::range<1>' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::range<1>' @@ -56,41 +54,38 @@ int main() { // CHECK: CXXMemberCallExpr {{.*}} 'void' // CHECK-NEXT: MemberExpr {{.*}}__init -// CHECK kernel_B parameters -// CHECK: FunctionDecl {{.*}}kernel_B{{.*}} 'void (wrapped_array)' -// CHECK-NEXT: ParmVarDecl {{.*}} 'wrapped_array' -// CHECK-NEXT: CompoundStmt -// CHECK-NEXT: DeclStmt -// CHECK-NEXT: VarDecl -// CHECK-NEXT: InitListExpr -// CHECK-NEXT: ArrayInitLoopExpr {{.*}} 'int [100]' +// Check kernel_B parameters +// CHECK: FunctionDecl {{.*}}kernel_B{{.*}} 'void (int, int)' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'int' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'int' -// CHECK kernel_C parameters -// CHECK: FunctionDecl {{.*}}kernel_C{{.*}} 'void (struct {{.*}}, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' -// CHECK-NEXT: ParmVarDecl {{.*}} 'struct {{.*}}' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// Correct and enable after struct mebers are extracted into separate parameters +// C HECK kernel_C parameters +// C HECK: FunctionDecl {{.*}}kernel_C{{.*}} 'void (struct {{.*}}, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' +// C HECK-NEXT: ParmVarDecl {{.*}} 'struct {{.*}}' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' -// CHECK that four accessor init functions are called -// CHECK: CXXMemberCallExpr {{.*}} 'void' -// CHECK-NEXT: MemberExpr {{.*}}__init -// CHECK: CXXMemberCallExpr {{.*}} 'void' -// CHECK-NEXT: MemberExpr {{.*}}__init -// CHECK: CXXMemberCallExpr {{.*}} 'void' -// CHECK-NEXT: MemberExpr {{.*}}__init -// CHECK: CXXMemberCallExpr {{.*}} 'void' -// CHECK-NEXT: MemberExpr {{.*}}__init +// C HECK that four accessor init functions are called +// C HECK: CXXMemberCallExpr {{.*}} 'void' +// C HECK-NEXT: MemberExpr {{.*}}__init +// C HECK: CXXMemberCallExpr {{.*}} 'void' +// C HECK-NEXT: MemberExpr {{.*}}__init +// C HECK: CXXMemberCallExpr {{.*}} 'void' +// C HECK-NEXT: MemberExpr {{.*}}__init +// C HECK: CXXMemberCallExpr {{.*}} 'void' +// C HECK-NEXT: MemberExpr {{.*}}__init From 70a2076373c55d47850781646a0c252b0d5144f3 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Thu, 25 Jun 2020 16:04:13 +0300 Subject: [PATCH 24/42] Fix multiple inheritance Base class offset was missing in integration header. Fixes CodeGenSYCL::integration_header.cpp test. Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index f21b190d28d12..56f25f5ef3d85 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -825,23 +825,17 @@ static void VisitRecordHelper(CXXRecordDecl *Owner, clang::CXXRecordDecl::base_class_range Range, Handlers &... handlers) { for (const auto &Base : Range) { + (void)std::initializer_list{(handlers.enterField(Owner, Base), 0)...}; QualType BaseTy = Base.getType(); if (Util::isSyclAccessorType(BaseTy)) { - (void)std::initializer_list{ - (handlers.enterField(Owner, Base), 0)...}; (void)std::initializer_list{ (handlers.handleSyclAccessorType(Base, BaseTy), 0)...}; - (void)std::initializer_list{ - (handlers.leaveField(Owner, Base), 0)...}; } else if (Util::isSyclStreamType(BaseTy)) { - (void)std::initializer_list{ - (handlers.enterField(Owner, Base), 0)...}; (void)std::initializer_list{ (handlers.handleSyclStreamType(Base, BaseTy), 0)...}; - (void)std::initializer_list{ - (handlers.leaveField(Owner, Base), 0)...}; } else VisitRecord(Owner, Base, BaseTy->getAsCXXRecordDecl(), handlers...); + (void)std::initializer_list{(handlers.leaveField(Owner, Base), 0)...}; } } From f07c8d737342c72963fdf933dc73e850c806c540 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Thu, 25 Jun 2020 16:08:19 +0300 Subject: [PATCH 25/42] Add runtime test for functor inheritance Signed-off-by: Elizabeth Andrews --- sycl/test/functor/functor_inheritance.cpp | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 sycl/test/functor/functor_inheritance.cpp diff --git a/sycl/test/functor/functor_inheritance.cpp b/sycl/test/functor/functor_inheritance.cpp new file mode 100644 index 0000000000000..8bedd4126695b --- /dev/null +++ b/sycl/test/functor/functor_inheritance.cpp @@ -0,0 +1,62 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple -o %t.out %s +// RUN: env SYCL_DEVICE_TYPE=HOST %t.out +// RUN: %CPU_RUN_PLACEHOLDER %t.out +// RUN: %GPU_RUN_PLACEHOLDER %t.out +// RUN: %ACC_RUN_PLACEHOLDER %t.out + +#include +#include + +constexpr auto sycl_read_write = cl::sycl::access::mode::read_write; +constexpr auto sycl_global_buffer = cl::sycl::access::target::global_buffer; + +struct SecondBase { + SecondBase(int _E) : E(_E) {} + int E; +}; + +struct InnerFieldBase { + InnerFieldBase(int _D) : D(_D) {} + int D; +}; + +struct InnerField : public InnerFieldBase { + InnerField(int _C, int _D) : C(_C), InnerFieldBase(_D) {} + int C; +}; + +struct Base { + Base (int _B, int _C, int _D) : B(_B), InnerObj(_C, _D) {} + int B; + InnerField InnerObj; +}; + +struct Derived : public Base, public SecondBase { + Derived( + int _A, int _B, int _C, int _D, int _E, + cl::sycl::accessor &_Acc) + : A(_A), Acc(_Acc), /*Out(_Out),*/ Base(_B, _C, _D), SecondBase(_E) {} + void operator()() { + Acc[0] = this->A + this->B + this->InnerObj.C + this->InnerObj.D + this->E; + } + + int A; + cl::sycl::accessor Acc; +}; + +int main() { + int A[] = { 10 }; + { + cl::sycl::queue Q; + cl::sycl::buffer Buf(A, 1); + + Q.submit([&](cl::sycl::handler &cgh) { + auto Acc = Buf.get_access(cgh); + Derived F = {1, 2, 3, 4, 5, Acc/*, Out*/}; + cgh.single_task(F); + }); + } + assert(A[0] == 15); + return 0; +} + From b1365c2d34e24afaf2cf241771162097735567de Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Thu, 25 Jun 2020 16:11:51 +0300 Subject: [PATCH 26/42] Add runtime test for accessor base Signed-off-by: Elizabeth Andrews --- sycl/test/basic_tests/accessor/accessor.cpp | 49 +++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/sycl/test/basic_tests/accessor/accessor.cpp b/sycl/test/basic_tests/accessor/accessor.cpp index 84286c6117eff..2a6d0bf1e97e7 100644 --- a/sycl/test/basic_tests/accessor/accessor.cpp +++ b/sycl/test/basic_tests/accessor/accessor.cpp @@ -30,6 +30,16 @@ struct IdxID3 { operator sycl::id<3>() { return sycl::id<3>(x, y, z); } }; +template +using AccAlias = + cl::sycl::accessor; +template +struct InheritedAccessor : public AccAlias { + + using AccAlias::AccAlias; +}; + template struct AccWrapper { Acc accessor; }; template struct AccsWrapper { @@ -493,4 +503,43 @@ int main() { return 1; } } + { + try { + int data = -1; + int cnst = 399; + + { + sycl::buffer A(&cnst, sycl::range<1>(1)); + sycl::buffer B(&cnst, sycl::range<1>(1)); + sycl::buffer C(&data, sycl::range<1>(1)); + + + sycl::queue queue; + queue.submit([&](sycl::handler &cgh) { + + sycl::accessor + AccA(A, cgh); + sycl::accessor + AccB(B, cgh); + InheritedAccessor AccC(C, cgh); + cgh.single_task([=]() { + AccC[0] = AccA[0] + AccB[0]; + }); + }); + +#ifndef simplification_test + auto host_acc = C.get_access(); +#else + sycl::host_accessor host_acc(C, sycl::read_only); +#endif + assert(host_acc[0] == 798); + } + + } catch (sycl::exception e) { + std::cout << "SYCL exception caught: " << e.what(); + return 1; + } + } } From 4ea6f472775ddd13ace85d8752bf81718684c55c Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Thu, 25 Jun 2020 10:15:23 +0300 Subject: [PATCH 27/42] Do not decompose cl::sycl::half type Once we start structs decomposing, half type that is wrapped by cl::sycl::half type starts appearing in kernel arguments. OpenCL doesn't allow half kernel argument, so it causes runtime errors. To fix it we don't decompose cl::sycl::half type. Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 41 +++++++++++++++++++++++++ clang/test/SemaSYCL/Inputs/sycl.hpp | 14 +++++++++ clang/test/SemaSYCL/half-kernel-arg.cpp | 23 ++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 clang/test/SemaSYCL/half-kernel-arg.cpp diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 56f25f5ef3d85..bf23580a29a64 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -76,6 +76,10 @@ class Util { /// stream class. static bool isSyclStreamType(const QualType &Ty); + /// Checks whether given clang type is a full specialization of the SYCL + /// half class. + static bool isSyclHalfType(const QualType &Ty); + /// Checks whether given clang type is a standard SYCL API class with given /// name. /// \param Ty the clang type being checked @@ -796,6 +800,8 @@ static void VisitField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, KF_FOR_EACH(handleSyclStreamType, Item, ItemTy); else if (Util::isSyclSamplerType(ItemTy)) KF_FOR_EACH(handleSyclSamplerType, Item, ItemTy); + else if (Util::isSyclHalfType(ItemTy)) + KF_FOR_EACH(handleSyclHalfType, Item, ItemTy); else if (ItemTy->isStructureOrClassType()) VisitRecord(Owner, Item, ItemTy->getAsCXXRecordDecl(), handlers...); else if (ItemTy->isArrayType()) @@ -898,6 +904,8 @@ static void VisitRecordFields(CXXRecordDecl *Owner, Handlers &... handlers) { KF_FOR_EACH(handleSyclAccessorType, Field, FieldTy); else if (Util::isSyclSamplerType(FieldTy)) KF_FOR_EACH(handleSyclSamplerType, Field, FieldTy); + else if (Util::isSyclHalfType(FieldTy)) + KF_FOR_EACH(handleSyclHalfType, Field, FieldTy); else if (Util::isSyclSpecConstantType(FieldTy)) KF_FOR_EACH(handleSyclSpecConstantType, Field, FieldTy); else if (Util::isSyclStreamType(FieldTy)) { @@ -953,6 +961,10 @@ template class SyclKernelFieldHandler { return true; } virtual bool handleSyclStreamType(FieldDecl *, QualType) { return true; } + virtual bool handleSyclHalfType(const CXXBaseSpecifier &, QualType) { + return true; + } + virtual bool handleSyclHalfType(FieldDecl *, QualType) { return true; } virtual bool handleStructType(FieldDecl *, QualType) { return true; } virtual bool handleReferenceType(FieldDecl *, QualType) { return true; } virtual bool handlePointerType(FieldDecl *, QualType) { return true; } @@ -1244,6 +1256,11 @@ class SyclKernelDeclCreator return true; } + bool handleSyclHalfType(FieldDecl *FD, QualType FieldTy) final { + addParam(FD, FieldTy); + return true; + } + bool handleSyclStreamType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy); return true; @@ -1264,6 +1281,7 @@ class SyclKernelDeclCreator std::end(Params)); } using SyclKernelFieldHandler::handleSyclSamplerType; + using SyclKernelFieldHandler::handleSyclHalfType; }; class SyclKernelBodyCreator @@ -1537,6 +1555,11 @@ class SyclKernelBodyCreator return true; } + bool handleSyclHalfType(FieldDecl *FD, QualType Ty) final { + createExprForStructOrScalar(FD); + return true; + } + bool handlePointerType(FieldDecl *FD, QualType FieldTy) final { createExprForStructOrScalar(FD); return true; @@ -1663,6 +1686,7 @@ class SyclKernelBodyCreator using SyclKernelFieldHandler::enterArray; using SyclKernelFieldHandler::enterField; using SyclKernelFieldHandler::handleSyclSamplerType; + using SyclKernelFieldHandler::handleSyclHalfType; using SyclKernelFieldHandler::leaveField; }; @@ -1790,6 +1814,11 @@ class SyclKernelIntHeaderCreator return true; } + bool handleSyclHalfType(FieldDecl *FD, QualType FieldTy) final { + addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); + return true; + } + void enterField(const CXXRecordDecl *RD, FieldDecl *FD) final { CurOffset += SemaRef.getASTContext().getFieldOffset(FD) / 8; } @@ -1825,6 +1854,7 @@ class SyclKernelIntHeaderCreator CurOffset -= ArraySize; } using SyclKernelFieldHandler::handleSyclSamplerType; + using SyclKernelFieldHandler::handleSyclHalfType; }; } // namespace @@ -2659,6 +2689,17 @@ bool Util::isSyclStreamType(const QualType &Ty) { return isSyclType(Ty, "stream"); } +bool Util::isSyclHalfType(const QualType &Ty) { + const StringRef &Name = "half"; + std::array Scopes = { + Util::DeclContextDesc{clang::Decl::Kind::Namespace, "cl"}, + Util::DeclContextDesc{clang::Decl::Kind::Namespace, "sycl"}, + Util::DeclContextDesc{clang::Decl::Kind::Namespace, "detail"}, + Util::DeclContextDesc{clang::Decl::Kind::Namespace, "half_impl"}, + Util::DeclContextDesc{Decl::Kind::CXXRecord, Name}}; + return matchQualifiedTypeName(Ty, Scopes); +} + bool Util::isSyclSpecConstantType(const QualType &Ty) { const StringRef &Name = "spec_constant"; std::array Scopes = { diff --git a/clang/test/SemaSYCL/Inputs/sycl.hpp b/clang/test/SemaSYCL/Inputs/sycl.hpp index 5bd37447ce814..9e3efc6321096 100644 --- a/clang/test/SemaSYCL/Inputs/sycl.hpp +++ b/clang/test/SemaSYCL/Inputs/sycl.hpp @@ -37,6 +37,20 @@ enum class address_space : int { }; } // namespace access +namespace detail { +namespace half_impl { +struct half { +#ifdef __SYCL_DEVICE_ONLY + _Float16 data; +#else + char data[2]; +#endif +}; +} // namespace half_impl +} // namespace detail + +using half = detail::half_impl::half; + template struct range { }; diff --git a/clang/test/SemaSYCL/half-kernel-arg.cpp b/clang/test/SemaSYCL/half-kernel-arg.cpp new file mode 100644 index 0000000000000..13f89f38a3d96 --- /dev/null +++ b/clang/test/SemaSYCL/half-kernel-arg.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -ast-dump %s | FileCheck %s + +// This test checks that compiler generates correct initialization for arguments +// that have cl::sycl::half type inside the OpenCL kernel + +#include + +int main() { + cl::sycl::half HostHalf; + cl::sycl::kernel_single_task( + [=]() { + cl::sycl::half KernelHalf = HostHalf; + }); +} + +// CHECK: {{.*}}kernel_half{{.*}} 'void (cl::sycl::half)' +// CHECK: ParmVarDecl {{.*}} used _arg_ 'cl::sycl::half':'cl::sycl::detail::half_impl::half' +// // Check that lambda field of half type is initialized +// CHECK: VarDecl {{.*}}'(lambda at {{.*}}' +// CHECK-NEXT: InitListExpr {{.*}}'(lambda at {{.*}}' +// CHECK-NEXT: CXXConstructExpr {{.*}}'cl::sycl::detail::half_impl::half'{{.*}} +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'const cl::sycl::detail::half_impl::half' +// CHECK-NEXT: DeclRefExpr {{.*}} 'cl::sycl::half':'cl::sycl::detail::half_impl::half' lvalue ParmVar {{.*}} '_arg_' 'cl::sycl::half':'cl::sycl::detail::half_impl::half' From 1c9e17b9aa7da2fa851c733423f511d784c13e82 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Thu, 25 Jun 2020 19:22:18 -0700 Subject: [PATCH 28/42] Fix sampler lit test. Struct is decomposed. Signed-off-by: Elizabeth Andrews --- clang/test/CodeGenSYCL/sampler.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/clang/test/CodeGenSYCL/sampler.cpp b/clang/test/CodeGenSYCL/sampler.cpp index 749b5a0bfdaa9..ab22e8a12717c 100644 --- a/clang/test/CodeGenSYCL/sampler.cpp +++ b/clang/test/CodeGenSYCL/sampler.cpp @@ -12,11 +12,28 @@ // CHECK-NEXT: call spir_func void @{{[a-zA-Z0-9_]+}}(%"class.{{.*}}.cl::sycl::sampler" addrspace(4)* [[GEPCAST]], %opencl.sampler_t addrspace(2)* [[LOAD_SAMPLER_ARG]]) // -// CHECK: define spir_kernel void @{{[a-zA-Z0-9_]+}}(%struct{{.*}}sampler_wrapper{{.*}} %opencl.sampler_t addrspace(2)* [[SAMPLER_ARG_WRAPPED:%[a-zA-Z0-9_]+]]) +// CHECK: define spir_kernel void @{{[a-zA-Z0-9_]+}}(%opencl.sampler_t addrspace(2)* [[SAMPLER_ARG_WRAPPED:%[a-zA-Z0-9_]+]], i32 [[ARG_A:%[a-zA-Z0-9_]+]]) + +// Check alloca // CHECK: [[SAMPLER_ARG_WRAPPED]].addr = alloca %opencl.sampler_t addrspace(2)*, align 8 +// CHECK: [[ARG_A]].addr = alloca i32, align 4 +// CHECK: [[LAMBDA:%[0-9]+]] = alloca %"class.{{.*}}.anon.0", align 8 + +// Check argument store // CHECK: store %opencl.sampler_t addrspace(2)* [[SAMPLER_ARG_WRAPPED]], %opencl.sampler_t addrspace(2)** [[SAMPLER_ARG_WRAPPED]].addr, align 8 -// CHECK: [[LOAD_SAMPLER_ARG_WRAPPED:%[0-9]+]] = load %opencl.sampler_t addrspace(2)*, %opencl.sampler_t addrspace(2)** [[SAMPLER_ARG_WRAPPED]].addr, align 8 -// CHECK: call spir_func void @{{[a-zA-Z0-9_]+}}(%"class.{{.*}}.cl::sycl::sampler" addrspace(4)* {{.*}}, %opencl.sampler_t addrspace(2)* [[LOAD_SAMPLER_ARG_WRAPPED]]) +// CHECK: store i32 [[ARG_A]], i32* [[ARG_A]].addr, align 4 + +// Initialize 'a' +// CHECK: [[GEP_LAMBDA:%[0-9]+]] = getelementptr inbounds %"class.{{.*}}.anon.0", %"class.{{.*}}.anon.0"* [[LAMBDA]], i32 0, i32 0 +// CHECK: [[GEP_A:%[a-zA-Z0-9]+]] = getelementptr inbounds %struct.{{.*}}.sampler_wrapper, %struct.{{.*}}.sampler_wrapper* [[GEP_LAMBDA]], i32 0, i32 1 +// CHECK: [[LOAD_A:%[0-9]+]] = load i32, i32* [[ARG_A]].addr, align 4 +// CHECK: store i32 [[LOAD_A]], i32* [[GEP_A]], align 8 + +// Initialize wrapped sampler 'smpl' +// CHECK: [[GEP_LAMBDA_0:%[0-9]+]] = getelementptr inbounds %"class.{{.*}}.anon.0", %"class.{{.*}}.anon.0"* %0, i32 0, i32 0 +// CHECK: [[GEP_SMPL:%[a-zA-Z0-9]+]] = getelementptr inbounds %struct.{{.*}}.sampler_wrapper, %struct.{{.*}}.sampler_wrapper* [[GEP_LAMBDA_0]], i32 0, i32 0 +// CHECK: [[LOAD_SMPL:%[0-9]+]] = load %opencl.sampler_t addrspace(2)*, %opencl.sampler_t addrspace(2)** [[SAMPLER_ARG_WRAPPED]].addr, align 8 +// CHECK: call spir_func void @{{[a-zA-Z0-9_]+}}(%"class.{{.*}}.cl::sycl::sampler" addrspace(4)* {{.*}}, %opencl.sampler_t addrspace(2)* [[LOAD_SMPL]]) // #include "sycl.hpp" From 92e71bda38125080e25c5f369b9b6cf1edee708d Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Fri, 26 Jun 2020 08:17:50 -0700 Subject: [PATCH 29/42] Cleaned up code a bit: ClangFormat Changes Renamed VisitFunctorBases to VisitRecordBases Removed commented code Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index bf23580a29a64..a96573c16f107 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -883,14 +883,12 @@ static void VisitStreamRecord(CXXRecordDecl *Owner, ParentTy &Parent, (void)std::initializer_list{(handlers.leaveStruct(Owner, Parent), 0)...}; } - template -static void VisitFunctorBases(CXXRecordDecl *KernelFunctor, - Handlers &... handlers) { +static void VisitRecordBases(CXXRecordDecl *KernelFunctor, + Handlers &... handlers) { VisitRecordHelper(KernelFunctor, KernelFunctor->bases(), handlers...); } - // A visitor function that dispatches to functions as defined in // SyclKernelFieldHandler for the purposes of kernel generation. template @@ -1252,7 +1250,6 @@ class SyclKernelDeclCreator } bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - // addParam(FD, FieldTy); return true; } @@ -1280,8 +1277,8 @@ class SyclKernelDeclCreator return ArrayRef(std::begin(Params) + LastParamIndex, std::end(Params)); } - using SyclKernelFieldHandler::handleSyclSamplerType; using SyclKernelFieldHandler::handleSyclHalfType; + using SyclKernelFieldHandler::handleSyclSamplerType; }; class SyclKernelBodyCreator @@ -1426,11 +1423,6 @@ class SyclKernelBodyCreator VK_LValue, SourceLocation()); } - /*Expr *SpecialObjME = Base; - if (Field) - SpecialObjME = BuildMemberExpr(Base, Field); - - MemberExpr *MethodME = BuildMemberExpr(SpecialObjME, Method);*/ MemberExpr *MethodME = BuildMemberExpr(Base, Method); QualType ResultTy = Method->getReturnType(); ExprValueKind VK = Expr::getValueKindForType(ResultTy); @@ -1566,7 +1558,6 @@ class SyclKernelBodyCreator } bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - // createExprForStructOrScalar(FD); return true; } @@ -1581,7 +1572,6 @@ class SyclKernelBodyCreator } void enterStruct(const CXXRecordDecl *, FieldDecl *FD) final { - // MemberExprBases.push_back(BuildMemberExpr(MemberExprBases.back(), FD)); } void enterStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { @@ -1635,7 +1625,6 @@ class SyclKernelBodyCreator InitExprs.pop_back(); } } - // MemberExprBases.pop_back(); } void leaveStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { @@ -1793,7 +1782,6 @@ class SyclKernelIntHeaderCreator } bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - // addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); return true; } @@ -1904,8 +1892,7 @@ void Sema::ConstructOpenCLKernel(FunctionDecl *KernelCallerFunc, StableName); ConstructingOpenCLKernel = true; - VisitFunctorBases(KernelLambda, checker, kernel_decl, kernel_body, - int_header); + VisitRecordBases(KernelLambda, checker, kernel_decl, kernel_body, int_header); VisitRecordFields(KernelLambda, checker, kernel_decl, kernel_body, int_header); ConstructingOpenCLKernel = false; From 7bb1db5c250a62fcf95092b19ce1d5fe57ac872a Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Fri, 26 Jun 2020 08:29:45 -0700 Subject: [PATCH 30/42] ClangFormat Changes Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 1dde2074daa4f..9c8798ec200ae 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1273,8 +1273,8 @@ class SyclKernelDeclCreator return ArrayRef(std::begin(Params) + LastParamIndex, std::end(Params)); } - using SyclKernelFieldHandler::handleSyclHalfType; using SyclKernelFieldHandler::handleScalarType; + using SyclKernelFieldHandler::handleSyclHalfType; using SyclKernelFieldHandler::handleSyclSamplerType; }; @@ -1685,9 +1685,9 @@ class SyclKernelBodyCreator using SyclKernelFieldHandler::enterArray; using SyclKernelFieldHandler::enterField; - using SyclKernelFieldHandler::handleSyclSamplerType; - using SyclKernelFieldHandler::handleSyclHalfType; using SyclKernelFieldHandler::handleScalarType; + using SyclKernelFieldHandler::handleSyclHalfType; + using SyclKernelFieldHandler::handleSyclSamplerType; using SyclKernelFieldHandler::leaveField; }; @@ -1844,8 +1844,8 @@ class SyclKernelIntHeaderCreator } CurOffset -= ArraySize; } - using SyclKernelFieldHandler::handleSyclHalfType; using SyclKernelFieldHandler::handleScalarType; + using SyclKernelFieldHandler::handleSyclHalfType; using SyclKernelFieldHandler::handleSyclSamplerType; }; } // namespace From db492bd762db2c27826166b1e7b5ea90175b3374 Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Sat, 27 Jun 2020 12:20:31 -0700 Subject: [PATCH 31/42] Decomposed array elements, and changed manner of array element initialization. --- clang/lib/Sema/SemaSYCL.cpp | 124 +++-- .../CodeGenSYCL/kernel-param-acc-array.cpp | 24 +- .../test/SemaSYCL/array-kernel-param-neg.cpp | 13 - clang/test/SemaSYCL/array-kernel-param.cpp | 12 +- sycl/doc/Array_Kernel_Parameters.md | 435 ------------------ sycl/doc/KernelParameterPassing.md | 32 +- .../array-kernel-param-nested-run.cpp | 135 ++++++ .../array_param/array-kernel-param-run.cpp | 89 ++-- 8 files changed, 290 insertions(+), 574 deletions(-) delete mode 100755 sycl/doc/Array_Kernel_Parameters.md create mode 100755 sycl/test/array_param/array-kernel-param-nested-run.cpp diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 50c5a0804241a..c29b191181621 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -791,11 +791,15 @@ static void VisitField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, else if (ItemTy->isStructureOrClassType()) VisitAccessorWrapper(Owner, Item, ItemTy->getAsCXXRecordDecl(), handlers...); -#if 0 - // FIXME Enable this when structs are replaced by their fields + // FIXME Enable this when structs are replaced by their fields +#define STRUCTS_DECOMPOSED 0 +#if STRUCTS_DECOMPOSED else if (ItemTy->isArrayType()) VisitArrayElements(Item, ItemTy, handlers...); -#endif + else if (ItemTy->isScalarType()) + KF_FOR_EACH(handleScalarType, Item, ItemTy); +} +#else } template @@ -803,6 +807,7 @@ static void VisitScalarField(CXXRecordDecl *Owner, RangeTy &&Item, QualType ItemTy, Handlers &... handlers) { KF_FOR_EACH(handleScalarType, Item, ItemTy); } +#endif template static void VisitArrayElements(RangeTy Item, QualType FieldTy, @@ -812,13 +817,18 @@ static void VisitArrayElements(RangeTy Item, QualType FieldTy, int64_t ElemCount = CAT->getSize().getSExtValue(); std::initializer_list{(handlers.enterArray(), 0)...}; for (int64_t Count = 0; Count < ElemCount; Count++) { +#if STRUCTS_DECOMPOSED + VisitField(nullptr, Item, ET, handlers...); +#else if (ET->isScalarType()) VisitScalarField(nullptr, Item, ET, handlers...); else VisitField(nullptr, Item, ET, handlers...); +#endif (void)std::initializer_list{(handlers.nextElement(ET), 0)...}; } - (void)std::initializer_list{(handlers.leaveArray(ET, ElemCount), 0)...}; + (void)std::initializer_list{ + (handlers.leaveArray(Item, ET, ElemCount), 0)...}; } template @@ -932,20 +942,31 @@ template class SyclKernelFieldHandler { // class/field graph. Int Headers use this to calculate offset, most others // don't have a need for these. - virtual void enterStruct(const CXXRecordDecl *, FieldDecl *) {} - virtual void leaveStruct(const CXXRecordDecl *, FieldDecl *) {} - virtual void enterStruct(const CXXRecordDecl *, const CXXBaseSpecifier &) {} - virtual void leaveStruct(const CXXRecordDecl *, const CXXBaseSpecifier &) {} + virtual bool enterStruct(const CXXRecordDecl *, FieldDecl *) { return true; } + virtual bool leaveStruct(const CXXRecordDecl *, FieldDecl *) { return true; } + virtual bool enterStruct(const CXXRecordDecl *, const CXXBaseSpecifier &) { + return true; + } + virtual bool leaveStruct(const CXXRecordDecl *, const CXXBaseSpecifier &) { + return true; + } // The following are used for stepping through array elements. - virtual void enterField(const CXXRecordDecl *, const CXXBaseSpecifier &) {} - virtual void leaveField(const CXXRecordDecl *, const CXXBaseSpecifier &) {} - virtual void enterField(const CXXRecordDecl *, FieldDecl *) {} - virtual void leaveField(const CXXRecordDecl *, FieldDecl *) {} - virtual void enterArray() {} - virtual void nextElement(QualType) {} - virtual void leaveArray(QualType, int64_t) {} + virtual bool enterField(const CXXRecordDecl *, const CXXBaseSpecifier &) { + return true; + } + virtual bool leaveField(const CXXRecordDecl *, const CXXBaseSpecifier &) { + return true; + } + virtual bool enterField(const CXXRecordDecl *, FieldDecl *) { return true; } + virtual bool leaveField(const CXXRecordDecl *, FieldDecl *) { return true; } + virtual bool enterArray() { return true; } + virtual bool nextElement(QualType) { return true; } + virtual bool leaveArray(const CXXBaseSpecifier &, QualType, int64_t) { + return true; + } + virtual bool leaveArray(FieldDecl *, QualType, int64_t) { return true; } }; // A type to check the validity of all of the argument types. @@ -1242,6 +1263,7 @@ class SyclKernelBodyCreator InitializedEntity VarEntity; CXXRecordDecl *KernelObj; llvm::SmallVector MemberExprBases; + uint64_t ArrayIndex; FunctionDecl *KernelCallerFunc; // Using the statements/init expressions that we've created, this generates @@ -1340,30 +1362,27 @@ class SyclKernelBodyCreator InitExprs.push_back(MemberInit.get()); } - void createExprForScalarElement(FieldDecl *FD, QualType FieldTy) { + void createExprForScalarElement(FieldDecl *FD) { InitializedEntity ArrayEntity = InitializedEntity::InitializeMember(FD, &VarEntity); InitializationKind InitKind = InitializationKind::CreateCopy(SourceLocation(), SourceLocation()); Expr *DRE = createInitExpr(FD); - Expr *Idx = dyn_cast(MemberExprBases.back())->getIdx(); - llvm::APSInt Result; - SemaRef.VerifyIntegerConstantExpression(Idx, &Result); - uint64_t IntIdx = Result.getZExtValue(); InitializedEntity Entity = InitializedEntity::InitializeElement( - SemaRef.getASTContext(), IntIdx, ArrayEntity); + SemaRef.getASTContext(), ArrayIndex, ArrayEntity); + ArrayIndex++; InitializationSequence InitSeq(SemaRef, Entity, InitKind, DRE); ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, DRE); + InitExprs.push_back(MemberInit.get()); + } + + void addArrayInit(FieldDecl *FD, int64_t Count) { llvm::SmallVector ArrayInitExprs; - if (IntIdx > 0) { - // Continue with the current InitList - InitListExpr *ILE = cast(InitExprs.back()); + for (int64_t I = 0; I < Count; I++) { + ArrayInitExprs.push_back(InitExprs.back()); InitExprs.pop_back(); - llvm::ArrayRef L = ILE->inits(); - for (size_t I = 0; I < L.size(); I++) - ArrayInitExprs.push_back(L[I]); } - ArrayInitExprs.push_back(MemberInit.get()); + std::reverse(ArrayInitExprs.begin(), ArrayInitExprs.end()); Expr *ILE = new (SemaRef.getASTContext()) InitListExpr(SemaRef.getASTContext(), SourceLocation(), ArrayInitExprs, SourceLocation()); @@ -1421,8 +1440,10 @@ class SyclKernelBodyCreator bool handleSpecialType(FieldDecl *FD, QualType Ty) { const auto *RecordDecl = Ty->getAsCXXRecordDecl(); - // Perform initialization only if it is field of kernel object - if (MemberExprBases.size() == 2) { + ArraySubscriptExpr *ArrayRef = + dyn_cast(MemberExprBases.back()); + // Perform initialization only if decomposed from array + if (ArrayRef || MemberExprBases.size() == 2) { InitializedEntity Entity = InitializedEntity::InitializeMember(FD, &VarEntity); // Initialize with the default constructor. @@ -1507,31 +1528,37 @@ class SyclKernelBodyCreator bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { if (dyn_cast(MemberExprBases.back())) - createExprForScalarElement(FD, FieldTy); + createExprForScalarElement(FD); else createExprForStructOrScalar(FD); return true; } - void enterField(const CXXRecordDecl *RD, FieldDecl *FD) final { + bool enterField(const CXXRecordDecl *RD, FieldDecl *FD) final { if (!FD->getType()->isReferenceType()) MemberExprBases.push_back(BuildMemberExpr(MemberExprBases.back(), FD)); + return true; } - void leaveField(const CXXRecordDecl *, FieldDecl *FD) final { + bool leaveField(const CXXRecordDecl *, FieldDecl *FD) final { if (!FD->getType()->isReferenceType()) MemberExprBases.pop_back(); + return true; } - void enterArray() final { + bool enterArray() final { Expr *ArrayBase = MemberExprBases.back(); ExprResult IndexExpr = SemaRef.ActOnIntegerConstant(SourceLocation(), 0); ExprResult ElementBase = SemaRef.CreateBuiltinArraySubscriptExpr( ArrayBase, SourceLocation(), IndexExpr.get(), SourceLocation()); MemberExprBases.push_back(ElementBase.get()); + ArrayIndex = 0; + return true; } - void nextElement(QualType) final { + bool nextElement(QualType ET) final { + if (ET->isScalarType()) + return true; ArraySubscriptExpr *LastArrayRef = dyn_cast(MemberExprBases.back()); MemberExprBases.pop_back(); @@ -1544,14 +1571,20 @@ class SyclKernelBodyCreator ExprResult ElementBase = SemaRef.CreateBuiltinArraySubscriptExpr( ArrayBase, SourceLocation(), IndexExpr.get(), SourceLocation()); MemberExprBases.push_back(ElementBase.get()); + return true; } - void leaveArray(QualType, int64_t) final { MemberExprBases.pop_back(); } + bool leaveArray(FieldDecl *FD, QualType, int64_t Count) final { + addArrayInit(FD, Count); + MemberExprBases.pop_back(); + return true; + } using SyclKernelFieldHandler::enterArray; using SyclKernelFieldHandler::enterField; using SyclKernelFieldHandler::handleScalarType; using SyclKernelFieldHandler::handleSyclSamplerType; + using SyclKernelFieldHandler::leaveArray; using SyclKernelFieldHandler::leaveField; }; @@ -1670,43 +1703,50 @@ class SyclKernelIntHeaderCreator return true; } - void enterField(const CXXRecordDecl *RD, FieldDecl *FD) final { + bool enterField(const CXXRecordDecl *RD, FieldDecl *FD) final { CurOffset += SemaRef.getASTContext().getFieldOffset(FD) / 8; + return true; } - void leaveField(const CXXRecordDecl *, FieldDecl *FD) final { + bool leaveField(const CXXRecordDecl *, FieldDecl *FD) final { CurOffset -= SemaRef.getASTContext().getFieldOffset(FD) / 8; + return true; } - void enterField(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { + bool enterField(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { const ASTRecordLayout &Layout = SemaRef.getASTContext().getASTRecordLayout(RD); CurOffset += Layout.getBaseClassOffset(BS.getType()->getAsCXXRecordDecl()) .getQuantity(); + return true; } - void leaveField(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { + bool leaveField(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { const ASTRecordLayout &Layout = SemaRef.getASTContext().getASTRecordLayout(RD); CurOffset -= Layout.getBaseClassOffset(BS.getType()->getAsCXXRecordDecl()) .getQuantity(); + return true; } - void nextElement(QualType ET) final { + bool nextElement(QualType ET) final { CurOffset += SemaRef.getASTContext().getTypeSizeInChars(ET).getQuantity(); + return true; } - void leaveArray(QualType ET, int64_t Count) final { + bool leaveArray(FieldDecl *, QualType ET, int64_t Count) final { int64_t ArraySize = SemaRef.getASTContext().getTypeSizeInChars(ET).getQuantity(); if (!ET->isArrayType()) { ArraySize *= Count; } CurOffset -= ArraySize; + return true; } using SyclKernelFieldHandler::handleScalarType; using SyclKernelFieldHandler::handleSyclSamplerType; + using SyclKernelFieldHandler::leaveArray; }; } // namespace diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp index c70e25b2feb72..ec8ac8bc01f5f 100644 --- a/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array.cpp @@ -49,26 +49,30 @@ int main() { // CHECK: [[MEM_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" // CHECK: [[OFFSET2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" -// CHECK accessor array GEP for acc[0] +// CHECK accessor array default inits // CHECK: [[ACCESSOR_ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY1]], i64 0, i64 0 +// CHECK: [[BEGIN:%[a-zA-Z0-9._]*]] = getelementptr inbounds [2 x [[ACCESSOR:.*]]], [2 x [[ACCESSOR]]]* [[ACCESSOR_ARRAY1]], i64 0, i64 0 +// CHECK: [[END:%[a-zA-Z0-9._]*]] = getelementptr inbounds [[ACCESSOR:.*]], [[ACCESSOR]]* [[BEGIN]], i64 2 +// CHECK: [[NEXT0:%[a-zA-Z0-9._]*]] = getelementptr inbounds [[ACCESSOR]], [[ACCESSOR]]* {{.*}}, i64 1 +// CHECK: [[ELEMENT:%[a-zA-Z0-9._]*]] = getelementptr inbounds [[ACCESSOR]], [[ACCESSOR]]* {{.*}}, i64 1 +// CHECK: [[ELEMENT:%[a-zA-Z0-9._]*]] = getelementptr inbounds [[ACCESSOR]], [[ACCESSOR]]* {{.*}}, i64 2 +// CHECK: [[NEXT1:%[a-zA-Z0-9._]*]] = getelementptr inbounds [[ACCESSOR]], [[ACCESSOR]]* {{.*}}, i64 1 + +// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[INDEX:%[a-zA-Z0-9._]*]] = getelementptr inbounds [2 x [[ACCESSOR]]], [2 x [[ACCESSOR]]]* [[ACCESSOR_ARRAY2]], i64 0, i64 0 // CHECK load from kernel pointer argument alloca // CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]] -// CHECK acc[0] __init method call -// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z0]] to %"class{{.*}}accessor" addrspace(4)* +// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast [[ACCESSOR]]* {{.*}} to [[ACCESSOR]] addrspace(4)* +// CHECK acc[0] __init method call // CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) -// CHECK accessor array GEP for acc[1] -// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds{{.*}}[[ACCESSOR_ARRAY2]], i64 0, i64 1 - // CHECK load from kernel pointer argument alloca // CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG2]] -// CHECK acc[1] __init method call -// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)* +// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast [[ACCESSOR]]* {{.*}} to [[ACCESSOR]] addrspace(4)* +// CHECK acc[1] __init method call // CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) diff --git a/clang/test/SemaSYCL/array-kernel-param-neg.cpp b/clang/test/SemaSYCL/array-kernel-param-neg.cpp index b7f669ecd6671..0618014c9fb10 100755 --- a/clang/test/SemaSYCL/array-kernel-param-neg.cpp +++ b/clang/test/SemaSYCL/array-kernel-param-neg.cpp @@ -4,21 +4,12 @@ // an array of non-trivially copyable structs as SYCL kernel parameter or // a non-constant size array. -struct A { - int i; -}; - struct B { int i; B(int _i) : i(_i) {} B(const B &x) : i(x.i) {} }; -struct C : A { - const A C2; - C() : A{0}, C2{2} {} -}; - struct D { int i; ~D(); @@ -38,16 +29,12 @@ __attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) { } void test() { - A cs1[10]; B nsl1[4] = {1, 2, 3, 4}; - C cs2[6]; D nsl2[5]; E es; kernel_single_task([=] { - int a = cs1[6].i; // expected-error@+1 {{kernel parameter has non-trivially copy constructible class/struct type}} int b = nsl1[2].i; - int c = cs2[0].i; // expected-error@+1 {{kernel parameter has non-trivially destructible class/struct type}} int d = nsl2[4].i; }); diff --git a/clang/test/SemaSYCL/array-kernel-param.cpp b/clang/test/SemaSYCL/array-kernel-param.cpp index 19d8562da1436..c8bdb390467a1 100755 --- a/clang/test/SemaSYCL/array-kernel-param.cpp +++ b/clang/test/SemaSYCL/array-kernel-param.cpp @@ -58,8 +58,18 @@ int main() { // CHECK: FunctionDecl {{.*}}kernel_B{{.*}} 'void (int, int)' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'int' // CHECK-NEXT: ParmVarDecl {{.*}} used _arg_ 'int' +// Check kernel_B inits +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: VarDecl {{.*}} cinit +// CHECK-NEXT: InitListExpr +// CHECK-NEXT: InitListExpr {{.*}} 'int [2]' +// CHECK: ImplicitCastExpr +// CHECK: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} '_arg_' 'int' +// CHECK: ImplicitCastExpr +// CHECK: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} '_arg_' 'int' -// Correct and enable after struct mebers are extracted into separate parameters +// Correct and enable after struct members are extracted into separate parameters // C HECK kernel_C parameters // C HECK: FunctionDecl {{.*}}kernel_C{{.*}} 'void (struct {{.*}}, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' // C HECK-NEXT: ParmVarDecl {{.*}} 'struct {{.*}}' diff --git a/sycl/doc/Array_Kernel_Parameters.md b/sycl/doc/Array_Kernel_Parameters.md deleted file mode 100755 index 22ec0b32c513b..0000000000000 --- a/sycl/doc/Array_Kernel_Parameters.md +++ /dev/null @@ -1,435 +0,0 @@ -

Array Parameters of SYCL Kernels

- -

Introduction

- -This document describes the changes to support passing arrays to SYCL kernels -and special treatment of Accessor arrays. -The following cases are handled: - -1. arrays of standard-layout type as top-level arguments -2. arrays of Accessors as top-level arguments -3. arrays of accessors within structs that are top-level arguments - -The motivation for this correction to kernel parameters processing is to -bring consistency to the treatment of arrays. -On the CPU, a lambda function is allowed to access an element of an array -defined outside the lambda. The implementation captures the entire array -by value. A user would naturally expect this to work in SYCL as well. -However, the current implementation flags references to arrays defined -outside a SYCL kernel as errors. - -The first few sections describe the current design. -The last three sections describe the design to support 1. to 3. above. -The implementation of this design is confined to three functions in the -file `SemaSYCL.cpp`. - -

A SYCL Kernel

- -The SYCL constructs `single_task`, `parallel_for`, and -`parallel_for_work_group` each take a function object or a lambda function - as one of their arguments. The code within the function object or -lambda function is executed on the device. -To enable execution of the kernel on OpenCL devices, the lambda/function object -is converted into the format of an OpenCL kernel. - -

SYCL Kernel Code Generation

- -Consider a source code example that captures an int, a struct and an accessor -by value: - -```C++ -constexpr size_t c_num_items = 10; -range<1> num_items{c_num_items}; // range<1>(num_items) - -int main() -{ - int output[c_num_items]; - queue myQueue; - - int i = 55; - struct S { - int m; - } s = { 66 }; - auto outBuf = buffer(&output[0], num_items); - - myQueue.submit([&](handler &cgh) { - auto outAcc = outBuf.get_access(cgh); - cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { - outAcc[index] = i + s.m; - }); - }); - - return 0; -} -``` - -The input to the code generation routines is a function object that represents -the kernel. In pseudo-code: - -```C++ -struct Capture { - Accessor outAcc; - int i; - struct S s; - () { - outAcc[index] = i + s.m; - } -} -``` - -On the CPU a call to such a lambda function would look like this: -```C++ -()(struct Capture* this); -``` - -When offloading the kernel to a device, the lambda/function object's -function operator cannot be directly called with a capture object address. -Instead, the code generated for the device is in the form of a -“kernel caller” and a “kernel callee”. -The callee is a clone of the SYCL kernel object. -The caller is generated in the form of an OpenCL kernel function. -It receives the lambda capture object in pieces, assembles the pieces -into the original lambda capture object and then calls the callee: - -```C++ -spir_kernel void caller( - __global int* AccData, // arg1 of Accessor init function - range<1> AccR1, // arg2 of Accessor init function - range<1> AccR2, // arg3 of Accessor init function - id<1> I, // arg4 of Accessor init function - int i, - struct S s -) -{ - // Local capture object - struct Capture local; - - // Reassemble capture object from parts - local.i = i; - local.s = s; - // Call accessor’s init function - Accessor::init(&local.outAcc, AccData, AccR1, AccR2, I); - - // Call the kernel body - callee(&local, id<1> wi); -} - -spir_func void callee(struct Capture* this, id<1> wi) -{ -} -``` - -As may be observed from the example above, standard-layout lambda capture -components are passed by value to the device as separate parameters. -This includes scalars, pointers, and standard-layout structs. -Certain SYCL struct types that are not standard-layout, -such as Accessors and Samplers, are treated specially. -The arguments to their init functions are passed as separate parameters -and used within the kernel caller function to initialize Accessors/Samplers -on the device by calling their init functions using the received arguments. - -There is one other aspect of code generation. An “integration header” -is generated for use during host compilation. -This header file contains entries for each kernel. -Among the items it defines is a table of sizes and offsets of the -kernel parameters. -For the source example above the integration header contains the -following snippet: - -```C++ -// array representing signatures of all kernels defined in the -// corresponding source -static constexpr -const kernel_param_desc_t kernel_signatures[] = { - //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE19->18clES2_E6Worker - { kernel_param_kind_t::kind_accessor, 4062, 0 }, - { kernel_param_kind_t::kind_std_layout, 4, 32 }, - { kernel_param_kind_t::kind_std_layout, 4, 36 }, -}; -``` - -Each entry in the kernel_signatures table contains three values: -1) an encoding of the type of capture object member -2) a field that encodes additional properties, and -3) an offset within a block of memory where the value of that -4) kernel argument is placed. - -The previous sections described how kernel arguments are handled today. -The next three sections describe support for arrays. - -

Fix 1: Kernel Arguments that are Standard-Layout Arrays

- -As described earlier, each variable captured by a lambda that comprises a -SYCL kernel becomes a parameter of the kernel caller function. -For arrays, simply allowing them through would result in a -function parameter of array type. This is not supported in C++. -Therefore, the array needing capture is wrapped in a struct for -the purposes of passing to the device. Once received on the device -within its wrapper, the array is copied into the local capture object. -All references to the array within the kernel body are directed to -the non-wrapped array which is a member of the local capture object. - -

Source code fragment:

- -```C++ - int array[100]; - auto outBuf = buffer(&output[0], num_items); - - myQueue.submit([&](handler &cgh) { - auto outAcc = outBuf.get_access(cgh); - cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { - outAcc[index] = array[index.get(0)]; - }); - }); -``` - -

Integration header produced:

- -```C++ -static constexpr -const kernel_param_desc_t kernel_signatures[] = { - //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE16->18clES2_E6Worker - { kernel_param_kind_t::kind_accessor, 4062, 0 }, - { kernel_param_kind_t::kind_std_layout, 400, 32 }, -}; -``` - -

The changes to device code made to support this extension, in pseudo-code:

- -```C++ -struct Capture { - Accessor outAcc; - int array[100]; - () { - // Body - } -} - -struct wrapper { - int array[100]; -}; -spir_kernel void caller( - __global int* AccData, // arg1 of Accessor init function - range<1> AccR1, // arg2 of Accessor init function - range<1> AccR2, // arg3 of Accessor init function - id<1> I, // arg4 of Accessor init function - struct wrapper w_s // Pass the array wrapped in a struct -) -{ - // Local capture object - struct Capture local; - - // Reassemble capture object from parts - // Initialize array using existing clang Initialization mechanisms - local.array = w_s; - // Call accessor’s init function - Accessor::init(&local.outAcc, AccData, AccR1, AccR2, I); - - callee(&local, id<1> wi); -} -``` - -The sharp-eyed reviewer of `SemaSYCL.cpp` will notice that the array -is actually double-wrapped in structs. This was done simply to preserve -the interface to an existing function (`CreateAndAddPrmDsc`) which -processes each kernel caller parameter as a capture object member. -The object being added to a list in `CreateAndAddPrmDsc` is `Fld`, -which is expected to be a field of some struct. So a wrapped struct -cannot be passed to this function. A double-wrapped struct is needed -as shown below. This does not affect the generated code. - -```C++ -struct { - struct { - int array[100]; - } -} -``` - -This could be changed but it would mean changes to the `CreateAndAddPrmDsc` -implementation, to all its callers and to the place where the list created -by it is processed. -By wrapping the array twice, the inner, single-wrapped array appears as a -member of a struct and meets the requirements of the existing code. - -

Fix 2: Kernel Arguments that are Arrays of Accessors

- -Arrays of accessors are supported in a manner similar to that of a plain -Accessor. For each accessor array element, the four values required to -call its init function are passed as separate arguments to the kernel. -Reassembly within the kernel caller is serialized by accessor array element. - -

Source code fragment:

- -```C++ - myQueue.submit([&](handler &cgh) { - using Accessor = - accessor; - Accessor inAcc[2] = {in_buffer1.get_access(cgh), - in_buffer2.get_access(cgh)}; - auto outAcc = out_buffer.get_access(cgh); - - cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { - outAcc[index] = inAcc[0][index] + inAcc[1][index]; - }); - }); -``` - -

Integration header:

- -```C++ -static constexpr -const kernel_param_desc_t kernel_signatures[] = { - //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE20->18clES2_E6Worker - { kernel_param_kind_t::kind_accessor, 4062, 0 }, - { kernel_param_kind_t::kind_accessor, 4062, 32 }, - { kernel_param_kind_t::kind_accessor, 4062, 64 }, -}; -``` - -

Device code generated in pseudo-code form:

- -```C++ -struct Capture { - Accessor outAcc; - Accessor inAcc[2]; - () { - // Body - } -} - -spir_kernel void caller( - __global int* outAccData, // args of OutAcc - range<1> outAccR1, - range<1> outAccR2, - id<1> outI, - __global int* inAccData_0, // args of inAcc[0] - range<1> inAccR1_0, - range<1> inAccR2_0, - id<1> inI_0, - __global int* inAccData_1, // args of inAcc[1] - range<1> inAccR1_1, - range<1> inAccR2_1, - id<1> inI_1, -) -{ - // Local capture object - struct Capture local; - - // Reassemble capture object from parts - // Call outAcc accessor’s init function - Accessor::init(&local.outAcc, outAccData, outAccR1, outAccR2, outI); - - // Call inAcc[0] accessor’s init function - Accessor::init(&local.inAcc[0], inAccData_0, inAccR1_0, inAccR2_0, inI_0); - - // Call inAcc[1] accessor’s init function - Accessor::init(&local.inAcc[1], inAccData_1, inAccR1_1, inAccR2_1, inI_1); - - callee(&local, id<1> wi); -} -``` - -

Fix 3: Accessor Arrays within Structs

- -*Individual* Accessors within structs were already supported. -Struct parameters of kernels that are structs are traversed member -by member, recursively, to enumerate member structs that are one of -the SYCL special types: Accessors and Samplers. For each special -struct encountered in the scan, arguments of their init functions -are added as separate arguments to the kernel. -However, *arrays* of accessors within structs were not supported. -Building on the support for single Accessors within structs, -the extension to arrays of Accessors/Samplers within structs -is straightforward. Each element of such arrays is treated as -an individual object, and the arguments of its init function -are added to the kernel arguments in sequence. -Within the kernel caller function, the lambda object is reassembled -in a manner similar to other instances of Accessor arrays. - - -

Source code fragment:

- -```C++ - myQueue.submit([&](handler &cgh) { - using Accessor = - accessor; - struct S { - int m; - Accessor inAcc[2]; - } s = { 55, - {in_buffer1.get_access(cgh), - in_buffer2.get_access(cgh)} - }; - auto outAcc = out_buffer.get_access(cgh); - - cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { - outAcc[index] = s.m + s.inAcc[0][index] + s.inAcc[1][index]; - }); -}); -``` - -

Integration header:

- -```C++ -static constexpr -const kernel_param_desc_t kernel_signatures[] = { - //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE20->18clES2_E6Worker - { kernel_param_kind_t::kind_accessor, 4062, 0 }, - { kernel_param_kind_t::kind_std_layout, 72, 32 }, - { kernel_param_kind_t::kind_accessor, 4062, 40 }, - { kernel_param_kind_t::kind_accessor, 4062, 72 }, - -}; -``` - -

Device code generated in pseudo-code form:

- -```C++ -struct Capture { - Accessor outAcc; - struct S s; - () { - // Body - } -} - -spir_kernel void caller( - __global int* outAccData, // args of OutAcc - range<1> outAccR1, - range<1> outAccR2, - id<1> outI, - struct S s, // the struct S - __global int* inAccData_0, // args of s.inAcc[0] - range<1> inAccR1_0, - range<1> inAccR2_0, - id<1> inI_0, - __global int* inAccData_1, // args of s.inAcc[1] - range<1> inAccR1_1, - range<1> inAccR2_1, - id<1> inI_1, -) -{ - // Local capture object - struct Capture local; - - // Reassemble capture object from parts - // Copy struct argument contents to local copy - // Accessor array will be initialized by calling init functions - local.s = s; - - // Call outAcc accessor’s init function - Accessor::init( - &local.outAcc, outAccData, outAccR1, outAccR2, outI); - - // Call s.inAcc[0] accessor’s init function - Accessor::init( - &local.s.inAcc[0], inAccData_0, inAccR1_0, inAccR2_0, inI_0); - - // Call s.inAcc[1] accessor’s init function - Accessor::init( - &local.s.inAcc[1], inAccData_1, inAccR1_1, inAccR2_1, inI_1); - - callee(&local, id<1> wi); -} -``` diff --git a/sycl/doc/KernelParameterPassing.md b/sycl/doc/KernelParameterPassing.md index a6f31ec114482..fc71e4b173851 100755 --- a/sycl/doc/KernelParameterPassing.md +++ b/sycl/doc/KernelParameterPassing.md @@ -175,16 +175,18 @@ As described earlier, each variable captured by a lambda that comprises a SYCL kernel becomes a parameter of the kernel caller function. For arrays, simply allowing them through would result in a function parameter of array type. This is not supported in C++. -Therefore, the array needing capture is wrapped in a struct for -the purposes of passing to the device. Once received on the device -within its wrapper, the array is copied into the local capture object. -All references to the array within the kernel body are directed to -the non-wrapped array which is a member of the local capture object. +Therefore, the array needing capture is decomposed into its elements for +the purposes of passing to the device. Each array element is passed as a +separate parameter. The array elements received on the device +are copied into the array within the local capture object.

Source code fragment:

```C++ - int array[100]; + constexpr int num_items = 2; + int array[num_items]; + int output[num_items]; + auto outBuf = buffer(&output[0], num_items); myQueue.submit([&](handler &cgh) { @@ -200,10 +202,13 @@ the non-wrapped array which is a member of the local capture object. ```C++ static constexpr const kernel_param_desc_t kernel_signatures[] = { - //--- _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE16->18clES2_E6Worker + //--- _ZTSZZ1fRN2cl4sycl5queueEENK3$_0clERNS0_7handlerEE6Worker { kernel_param_kind_t::kind_accessor, 4062, 0 }, - { kernel_param_kind_t::kind_std_layout, 400, 32 }, + { kernel_param_kind_t::kind_std_layout, 4, 32 }, + { kernel_param_kind_t::kind_std_layout, 4, 36 }, + }; + ```

The changes to device code made to support this extension, in pseudo-code:

@@ -211,21 +216,19 @@ const kernel_param_desc_t kernel_signatures[] = { ```C++ struct Capture { sycl::accessor outAcc; - int array[100]; + int array[num_items]; () { // Body } } -struct wrapper { - int array[100]; -}; spir_kernel void caller( __global int* AccData, // arg1 of accessor init function range<1> AccR1, // arg2 of accessor init function range<1> AccR2, // arg3 of accessor init function id<1> I, // arg4 of accessor init function - struct wrapper w_s // Pass the array wrapped in a struct + int p_array_0; // Pass array element 0 + int p_array_1; // Pass array element 1 ) { // Local capture object @@ -233,7 +236,8 @@ spir_kernel void caller( // Reassemble capture object from parts // Initialize array using existing clang Initialization mechanisms - local.array = w_s; + local.array[0] = p_array_0; + local.array[1] = p_array_1; // Call accessor’s init function sycl::accessor::init(&local.outAcc, AccData, AccR1, AccR2, I); diff --git a/sycl/test/array_param/array-kernel-param-nested-run.cpp b/sycl/test/array_param/array-kernel-param-nested-run.cpp new file mode 100755 index 0000000000000..28b9469cda89a --- /dev/null +++ b/sycl/test/array_param/array-kernel-param-nested-run.cpp @@ -0,0 +1,135 @@ +// This test checks kernel execution with array parameters inside structs. + +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: env SYCL_DEVICE_TYPE=HOST %t.out +// RUN: %CPU_RUN_PLACEHOLDER %t.out +// RUN: %GPU_RUN_PLACEHOLDER %t.out +// RUN: %ACC_RUN_PLACEHOLDER %t.out +// XFAIL: * + +#include +#include + +using namespace cl::sycl; + +constexpr size_t c_num_items = 100; +range<1> num_items{c_num_items}; // range<1>(num_items) + +// Change if tests are added/removed +static int testCount = 1; +static int passCount; + +template +static bool verify_1D(const char *name, int X, T A, T A_ref) { + int ErrCnt = 0; + + for (int i = 0; i < X; i++) { + if (A_ref[i] != A[i]) { + if (++ErrCnt < 10) { + std::cout << name << " mismatch at " << i << ". Expected " << A_ref[i] + << " result is " << A[i] << "\n"; + } + } + } + + if (ErrCnt == 0) { + return true; + } + std::cout << " Failed. Failure rate: " << ErrCnt << "/" << X << "(" + << ErrCnt / (float)X * 100.f << "%)\n"; + return false; +} + +template +void init(T &A, int value, int increment) { + for (int i = 0; i < c_num_items; i++) { + A[i] = value; + value += increment; + } +} + +bool test_accessor_array_in_struct(queue &myQueue) { + std::array input1; + std::array input2; + std::array output; + std::array ref; + init(input1, 1, 1); + init(input2, 22, 1); + init(ref, 35, 2); + + auto in_buffer1 = buffer(input1.data(), num_items); + auto in_buffer2 = buffer(input2.data(), num_items); + auto out_buffer = buffer(output.data(), num_items); + + myQueue.submit([&](handler &cgh) { + using Accessor = + accessor; + struct S { + int w; + int x; + Accessor a[2]; + int y; + int z; + } S = { + 3, 3, {in_buffer1.get_access(cgh), in_buffer2.get_access(cgh)}, 7, 7}; + auto output_accessor = out_buffer.get_access(cgh); + + cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { + S.a[0][index]++; + S.a[1][index]++; + output_accessor[index] = S.a[0][index] + S.a[1][index] + S.x + S.y; + }); + }); + const auto HostAccessor = out_buffer.get_access(); + + return verify_1D("Accessor array in struct", c_num_items, output, ref); +} + +bool run_tests() { + queue Q([](exception_list L) { + for (auto ep : L) { + try { + std::rethrow_exception(ep); + } catch (std::exception &E) { + std::cout << "*** std exception caught:\n"; + std::cout << E.what(); + } catch (cl::sycl::exception const &E1) { + std::cout << "*** SYCL exception caught:\n"; + std::cout << E1.what(); + } + } + }); + + passCount = 0; + if (test_accessor_array_in_struct(Q)) { + ++passCount; + } + + auto D = Q.get_device(); + const char *devType = D.is_host() ? "Host" : D.is_cpu() ? "CPU" : "GPU"; + std::cout << passCount << " of " << testCount << " tests passed on " + << devType << "\n"; + + return (testCount == passCount); +} + +int main(int argc, char *argv[]) { + bool passed = true; + default_selector selector{}; + auto D = selector.select_device(); + const char *devType = D.is_host() ? "Host" : D.is_cpu() ? "CPU" : "GPU"; + std::cout << "Running on device " << devType << " (" + << D.get_info() << ")\n"; + try { + passed &= run_tests(); + } catch (exception e) { + std::cout << e.what(); + } + + if (!passed) { + std::cout << "FAILED\n"; + return 1; + } + std::cout << "PASSED\n"; + return 0; +} diff --git a/sycl/test/array_param/array-kernel-param-run.cpp b/sycl/test/array_param/array-kernel-param-run.cpp index 379066340714d..5ed29a410997e 100755 --- a/sycl/test/array_param/array-kernel-param-run.cpp +++ b/sycl/test/array_param/array-kernel-param-run.cpp @@ -5,7 +5,6 @@ // RUN: %CPU_RUN_PLACEHOLDER %t.out // RUN: %GPU_RUN_PLACEHOLDER %t.out // RUN: %ACC_RUN_PLACEHOLDER %t.out -// XFAIL: * #include #include @@ -16,7 +15,7 @@ constexpr size_t c_num_items = 100; range<1> num_items{c_num_items}; // range<1>(num_items) // Change if tests are added/removed -static int testCount = 5; +static int testCount = 4; static int passCount; template @@ -65,7 +64,8 @@ bool test_one_array(queue &myQueue) { output_accessor[index] = input1[index] + 1; }); }); - const auto HostAccessor = out_buffer.get_access(); + const auto HostAccessor = + out_buffer.get_access(); return verify_1D("One array", c_num_items, output, ref); } @@ -88,7 +88,8 @@ bool test_two_arrays(queue &myQueue) { output_accessor[index] = input1[index] + input2[index]; }); }); - const auto HostAccessor = out_buffer.get_access(); + const auto HostAccessor = + out_buffer.get_access(); return verify_1D("Two arrays", c_num_items, output, ref); } @@ -96,29 +97,36 @@ bool test_two_arrays(queue &myQueue) { bool test_accessor_arrays_1(queue &myQueue) { std::array input1; std::array input2; + int input3[c_num_items]; + int input4[c_num_items]; std::array ref; init(input1, 1, 1); init(input2, 22, 1); - init(ref, 24, 1); + init(input3, 5, 1); + init(input4, -7, 1); + init(ref, 22, 3); auto in_buffer1 = buffer(input1.data(), num_items); auto in_buffer2 = buffer(input2.data(), num_items); myQueue.submit([&](handler &cgh) { - using Accessor = - accessor; + using Accessor = accessor; Accessor a[2] = { in_buffer1.get_access(cgh), in_buffer2.get_access(cgh), }; - cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { - a[0][index] = a[1][index] + 2; - }); + cgh.parallel_for( + num_items, [=](cl::sycl::id<1> index) { + a[0][index] = a[1][index] + input3[index] + input4[index] + 2; + }); }); - const auto HostAccessor = in_buffer1.get_access(); + const auto HostAccessor = + in_buffer1.get_access(); - return verify_1D>("Accessor arrays 1", c_num_items, input1, ref); + return verify_1D>("Accessor arrays 1", + c_num_items, input1, ref); } bool test_accessor_arrays_2(queue &myQueue) { @@ -135,58 +143,24 @@ bool test_accessor_arrays_2(queue &myQueue) { auto out_buffer = buffer(output.data(), num_items); myQueue.submit([&](handler &cgh) { - using Accessor = - accessor; + using Accessor = accessor; Accessor a[4] = {in_buffer1.get_access(cgh), in_buffer2.get_access(cgh), in_buffer1.get_access(cgh), in_buffer2.get_access(cgh)}; auto output_accessor = out_buffer.get_access(cgh); - cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { - output_accessor[index] = a[0][index] + a[3][index]; - }); + cgh.parallel_for( + num_items, [=](cl::sycl::id<1> index) { + output_accessor[index] = a[0][index] + a[3][index]; + }); }); - const auto HostAccessor = out_buffer.get_access(); + const auto HostAccessor = + out_buffer.get_access(); - return verify_1D>("Accessor arrays 2", c_num_items, output, ref); -} - -bool test_accessor_array_in_struct(queue &myQueue) { - std::array input1; - std::array input2; - std::array output; - std::array ref; - init(input1, 1, 1); - init(input2, 22, 1); - init(ref, 35, 2); - - auto in_buffer1 = buffer(input1.data(), num_items); - auto in_buffer2 = buffer(input2.data(), num_items); - auto out_buffer = buffer(output.data(), num_items); - - myQueue.submit([&](handler &cgh) { - using Accessor = - accessor; - struct S { - int w; - int x; - Accessor a[2]; - int y; - int z; - } S = { - 3, 3, {in_buffer1.get_access(cgh), in_buffer2.get_access(cgh)}, 7, 7}; - auto output_accessor = out_buffer.get_access(cgh); - - cgh.parallel_for(num_items, [=](cl::sycl::id<1> index) { - S.a[0][index]++; - S.a[1][index]++; - output_accessor[index] = S.a[0][index] + S.a[1][index] + S.x + S.y; - }); - }); - const auto HostAccessor = out_buffer.get_access(); - - return verify_1D("Accessor array in struct", c_num_items, output, ref); + return verify_1D>("Accessor arrays 2", + c_num_items, output, ref); } bool run_tests() { @@ -217,9 +191,6 @@ bool run_tests() { if (test_accessor_arrays_2(Q)) { ++passCount; } - if (test_accessor_array_in_struct(Q)) { - ++passCount; - } auto D = Q.get_device(); const char *devType = D.is_host() ? "Host" : D.is_cpu() ? "CPU" : "GPU"; From a64b209fad7389c584fce1a60fa11da52b724ae8 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Fri, 26 Jun 2020 18:13:48 +0300 Subject: [PATCH 32/42] Add CodeGen test for accessor inheritance Signed-off-by: Elizabeth Andrews --- .../test/CodeGenSYCL/accessor_inheritance.cpp | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 clang/test/CodeGenSYCL/accessor_inheritance.cpp diff --git a/clang/test/CodeGenSYCL/accessor_inheritance.cpp b/clang/test/CodeGenSYCL/accessor_inheritance.cpp new file mode 100644 index 0000000000000..e197c339c1251 --- /dev/null +++ b/clang/test/CodeGenSYCL/accessor_inheritance.cpp @@ -0,0 +1,94 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s +#include + +struct Base { + int A, B; + cl::sycl::accessor AccField; +}; + +struct Captured : Base, + cl::sycl::accessor { + int C; +}; + +int main() { + Captured Obj; + cl::sycl::kernel_single_task( + [=]() { + Obj.use(); + }); + return 0; +} + +// Check kernel parameters +// CHECK: %[[RANGE_TYPE:"struct.*cl::sycl::range"]] +// CHECK: %[[ID_TYPE:"struct.*cl::sycl::id"]] +// CHECK: define spir_kernel void @_ZTSZ4mainE6kernel +// CHECK-SAME: i32 [[ARG_A:%[a-zA-Z0-9_]+]], +// CHECK-SAME: i32 [[ARG_B:%[a-zA-Z0-9_]+]], +// CHECK-SAME: i8 addrspace(1)* [[ACC1_DATA:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %[[RANGE_TYPE]]* byval(%[[RANGE_TYPE]]) align 4 [[ACC1_RANGE1:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %[[RANGE_TYPE]]* byval(%[[RANGE_TYPE]]) align 4 [[ACC1_RANGE2:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %[[ID_TYPE]]* byval(%[[ID_TYPE]]) align 4 [[ACC1_ID:%[a-zA-Z0-9_]+]], +// CHECK-SAME: i8 addrspace(1)* [[ACC2_DATA:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %[[RANGE_TYPE]]* byval(%[[RANGE_TYPE]]) align 4 [[ACC2_RANGE1:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %[[RANGE_TYPE]]* byval(%[[RANGE_TYPE]]) align 4 [[ACC2_RANGE2:%[a-zA-Z0-9_]+]], +// CHECK-SAME: %[[ID_TYPE]]* byval(%[[ID_TYPE]]) align 4 [[ACC2_ID:%[a-zA-Z0-9_]+]], +// CHECK-SAME: i32 [[ARG_C:%[a-zA-Z0-9_]+]]) + +// Allocas for kernel parameters +// CHECK: [[ARG_A]].addr = alloca i32 +// CHECK: [[ARG_B]].addr = alloca i32 +// CHECK: [[ACC1_DATA]].addr = alloca i8 addrspace(1)* +// CHECK: [[ACC2_DATA]].addr = alloca i8 addrspace(1)* +// CHECK: [[ARG_C]].addr = alloca i32 +// +// Lambda object alloca +// CHECK: [[KERNEL_OBJ:%[a-zA-Z0-9_]+]] = alloca %"class.{{.*}}.anon" +// +// Kernel argument stores +// CHECK: store i32 [[ARG_A]], i32* [[ARG_A]].addr +// CHECK: store i32 [[ARG_B]], i32* [[ARG_B]].addr +// CHECK: store i8 addrspace(1)* [[ACC1_DATA]], i8 addrspace(1)** [[ACC1_DATA]].addr +// CHECK: store i8 addrspace(1)* [[ACC2_DATA]], i8 addrspace(1)** [[ACC2_DATA]].addr +// CHECK: store i32 [[ARG_C]], i32* [[ARG_C]].addr +// +// Check A and B scalar fields initialization +// CHECK: [[GEP:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class._ZTSZ4mainE3$_0.anon", %"class._ZTSZ4mainE3$_0.anon"* [[KERNEL_OBJ]], i32 0, i32 0 +// CHECK: [[BITCAST:%[a-zA-Z0-9_]+]] = bitcast %struct{{.*}}Captured* [[GEP]] to %struct{{.*}}Base* +// CHECK: [[FIELD_A:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct{{.*}}Base, %struct{{.*}}Base* [[BITCAST]], i32 0, i32 0 +// CHECK: [[ARG_A_LOAD:%[a-zA-Z0-9_]+]] = load i32, i32* [[ARG_A]].addr +// CHECK: store i32 [[ARG_A_LOAD]], i32* [[FIELD_A]] +// CHECK: [[FIELD_B:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct{{.*}}Base, %struct{{.*}}Base* [[BITCAST]], i32 0, i32 1 +// CHECK: [[ARG_B_LOAD:%[a-zA-Z0-9_]+]] = load i32, i32* [[ARG_B]].addr +// CHECK: store i32 [[ARG_B_LOAD]], i32* [[FIELD_B]] +// +// Check accessors initialization +// CHECK: [[ACC_FIELD:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct{{.*}}Base, %struct{{.*}}Base* [[BITCAST]], i32 0, i32 2 +// CHECK: [[ACC1_AS_CAST:%[a-zA-Z0-9_]+]] = addrspacecast %"class{{.*}}cl::sycl::accessor"* [[ACC_FIELD]] to %"class{{.*}}cl::sycl::accessor" addrspace(4)* +// Default constructor call +// CHECK: call spir_func void @_ZN2cl4sycl8accessorIcLi1ELNS0_6access4modeE1024ELNS2_6targetE2014ELNS2_11placeholderE0EEC1Ev(%"class{{.*}}cl::sycl::accessor" addrspace(4)* [[ACC1_AS_CAST]]) +// CHECK: [[BITCAST1:%[a-zA-Z0-9_]+]] = bitcast %struct{{.*}}Captured* [[GEP]] to i8* +// CHECK: [[GEP1:%[a-zA-Z0-9_]+]] = getelementptr inbounds i8, i8* [[BITCAST1]], i64 20 +// CHECK: [[BITCAST2:%[a-zA-Z0-9_]+]] = bitcast i8* [[GEP1]] to %"class{{.*}}cl::sycl::accessor"* +// CHECK: [[ACC2_AS_CAST:%[a-zA-Z0-9_]+]] = addrspacecast %"class{{.*}}cl::sycl::accessor"* [[BITCAST2]] to %"class{{.*}}cl::sycl::accessor" addrspace(4)* +// Default constructor call +// CHECK: call spir_func void @_ZN2cl4sycl8accessorIcLi1ELNS0_6access4modeE1024ELNS2_6targetE2014ELNS2_11placeholderE0EEC2Ev(%"class{{.*}}cl::sycl::accessor" addrspace(4)* [[ACC2_AS_CAST]]) + +// CHECK C field initialization +// CHECK: [[FIELD_C:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct{{.*}}Captured, %struct{{.*}}Captured* [[GEP]], i32 0, i32 2 +// CHECK: [[ARG_C_LOAD:%[a-zA-Z0-9_]+]] = load i32, i32* [[ARG_C]].addr +// CHECK: store i32 [[ARG_C_LOAD]], i32* [[FIELD_C]] +// +// Check __init method calls +// CHECK: [[GEP2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class._ZTSZ4mainE3$_0.anon", %"class._ZTSZ4mainE3$_0.anon"* [[KERNEL_OBJ]], i32 0, i32 0 +// CHECK: [[BITCAST3:%[a-zA-Z0-9_]+]] = bitcast %struct{{.*}}Captured* [[GEP2]] to %struct{{.*}}Base* +// CHECK: [[ACC1_FIELD:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct{{.*}}Base, %struct{{.*}}Base* [[BITCAST3]], i32 0, i32 2 +// CHECK: [[ACC1_DATA_LOAD:%[a-zA-Z0-9_]+]] = load i8 addrspace(1)*, i8 addrspace(1)** [[ACC1_DATA]].addr +// CHECK: [[ACC1_AS_CAST1:%[a-zA-Z0-9_]+]] = addrspacecast %"class{{.*}}cl::sycl::accessor"* [[ACC1_FIELD]] to %"class{{.*}}cl::sycl::accessor" addrspace(4)* +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class{{.*}}cl::sycl::accessor" addrspace(4)* [[ACC1_AS_CAST1]], i8 addrspace(1)* [[ACC1_DATA_LOAD]] +// +// CHECK: [[GEP3:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class._ZTSZ4mainE3$_0.anon", %"class._ZTSZ4mainE3$_0.anon"* [[KERNEL_OBJ]], i32 0, i32 0 +// CHECK: [[ACC2_DATA_LOAD:%[a-zA-Z0-9_]+]] = load i8 addrspace(1)*, i8 addrspace(1)** [[ACC2_DATA]].addr +// CHECK: [[AS_CAST_CAPTURED:%[a-zA-Z0-9_]+]] = addrspacecast %struct{{.*}}Captured* [[GEP3]] to %"class{{.*}}cl::sycl::accessor" addrspace(4)* +// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class{{.*}}cl::sycl::accessor" addrspace(4)* [[AS_CAST_CAPTURED]], i8 addrspace(1)* [[ACC2_DATA_LOAD]] From 4c7dbd093e65c2d662aa0d810aacd1172031de30 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva Date: Fri, 26 Jun 2020 14:15:05 +0300 Subject: [PATCH 33/42] Add Sema AST test for accessor bases Signed-off-by: Elizabeth Andrews --- clang/test/SemaSYCL/accessor_inheritance.cpp | 65 ++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 clang/test/SemaSYCL/accessor_inheritance.cpp diff --git a/clang/test/SemaSYCL/accessor_inheritance.cpp b/clang/test/SemaSYCL/accessor_inheritance.cpp new file mode 100644 index 0000000000000..830711b4bab95 --- /dev/null +++ b/clang/test/SemaSYCL/accessor_inheritance.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -ast-dump %s | FileCheck %s +#include + +struct Base { + int A, B; + cl::sycl::accessor AccField; +}; + +struct Captured : Base, + cl::sycl::accessor { + int C; +}; + +int main() { + Captured Obj; + cl::sycl::kernel_single_task( + [=]() { + Obj.use(); + }); +} + +// Check kernel parameters +// CHECK: FunctionDecl {{.*}}kernel 'void (int, int, __global char *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global char *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, int)' +// CHECK: ParmVarDecl{{.*}} used _arg_A 'int' +// CHECK: ParmVarDecl{{.*}} used _arg_B 'int' +// CHECK: ParmVarDecl{{.*}} used _arg_AccField '__global char *' +// CHECK: ParmVarDecl{{.*}} used _arg_AccField 'cl::sycl::range<1>' +// CHECK: ParmVarDecl{{.*}} used _arg_AccField 'cl::sycl::range<1>' +// CHECK: ParmVarDecl{{.*}} used _arg_AccField 'cl::sycl::id<1>' +// CHECK: ParmVarDecl{{.*}} used _arg__base '__global char *' +// CHECK: ParmVarDecl{{.*}} used _arg__base 'cl::sycl::range<1>' +// CHECK: ParmVarDecl{{.*}} used _arg__base 'cl::sycl::range<1>' +// CHECK: ParmVarDecl{{.*}} used _arg__base 'cl::sycl::id<1>' +// CHECK: ParmVarDecl{{.*}} used _arg_C 'int' + +// Check lambda initialization +// CHECK: VarDecl {{.*}} used '(lambda at {{.*}}accessor_inheritance.cpp +// CHECK-NEXT: InitListExpr {{.*}} +// CHECK-NEXT: InitListExpr {{.*}} 'Captured' +// CHECK-NEXT: InitListExpr {{.*}} 'Base' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} '_arg_A' 'int' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} '_arg_B' 'int' +// CHECK-NEXT: CXXConstructExpr {{.*}} 'cl::sycl::accessor':'cl::sycl::accessor' 'void () noexcept' +// CHECK-NEXT: CXXConstructExpr {{.*}} 'cl::sycl::accessor':'cl::sycl::accessor' 'void () noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} '_arg_C' 'int' + +// Check __init calls +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}} .__init +// CHECK-NEXT: MemberExpr {{.*}} .AccField +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Base' lvalue +// CHECK-NEXT: MemberExpr {{.*}} 'Captured' lvalue . +// CHECK-NEXT: DeclRefExpr {{.*}}'(lambda at {{.*}}accessor_inheritance.cpp +// CHECK-NEXT: ImplicitCastExpr {{.*}} '__global char *' +// CHECK-NEXT: DeclRefExpr {{.*}} '__global char *' lvalue ParmVar {{.*}} '_arg_AccField' '__global char *' + +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr{{.*}} lvalue .__init +// CHECK-NEXT: MemberExpr{{.*}}'Captured' lvalue . +// CHECK-NEXT: DeclRefExpr {{.*}} '(lambda at {{.*}}accessor_inheritance.cpp +// CHECK-NEXT: ImplicitCastExpr {{.*}} '__global char *' +// CHECK-NEXT: DeclRefExpr {{.*}} '__global char *' lvalue ParmVar {{.*}} '_arg__base' '__global char *' From 6da194d550e4918ad02973706f40390a126e48c6 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Sun, 28 Jun 2020 19:05:56 -0700 Subject: [PATCH 34/42] ClangFormat changes Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 4fd961bca7685..4ac548a4a604a 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1470,15 +1470,15 @@ class SyclKernelBodyCreator const auto *RecordDecl = Ty->getAsCXXRecordDecl(); // TODO: VarEntity is initialized entity for KernelObjClone, I guess we need // to create new one when enter new struct. - InitializedEntity Entity = - InitializedEntity::InitializeMember(FD, &VarEntity); - // Initialize with the default constructor. - InitializationKind InitKind = - InitializationKind::CreateDefault(SourceLocation()); - InitializationSequence InitSeq(SemaRef, Entity, InitKind, None); - ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, None); - InitExprs.push_back(MemberInit.get()); - + InitializedEntity Entity = + InitializedEntity::InitializeMember(FD, &VarEntity); + // Initialize with the default constructor. + InitializationKind InitKind = + InitializationKind::CreateDefault(SourceLocation()); + InitializationSequence InitSeq(SemaRef, Entity, InitKind, None); + ExprResult MemberInit = InitSeq.Perform(SemaRef, Entity, InitKind, None); + InitExprs.push_back(MemberInit.get()); + createSpecialMethodCall(RecordDecl, MemberExprBases.back(), InitMethodName, FD); return true; @@ -1581,8 +1581,7 @@ class SyclKernelBodyCreator return true; } - bool enterStruct(const CXXRecordDecl *, FieldDecl *FD) final { - } + bool enterStruct(const CXXRecordDecl *, FieldDecl *FD) final {} bool enterStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { CXXCastPath BasePath; @@ -1597,7 +1596,7 @@ class SyclKernelBodyCreator MemberExprBases.push_back(Cast); } - void addStructInit(const CXXRecordDecl *RD){ + void addStructInit(const CXXRecordDecl *RD) { if (!RD) return; @@ -1618,7 +1617,6 @@ class SyclKernelBodyCreator SourceLocation()); ILE->setType(QualType(RD->getTypeForDecl(), 0)); InitExprs.push_back(ILE); - } bool leaveStruct(const CXXRecordDecl *, FieldDecl *FD) final { From 4afc3a306aa49f950a970e6ba1ebf42fadd42429 Mon Sep 17 00:00:00 2001 From: rdeodhar Date: Mon, 29 Jun 2020 12:33:41 -0700 Subject: [PATCH 35/42] Removed one redundant check. --- clang/lib/Sema/SemaSYCL.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index c29b191181621..b7ca9aa85739e 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1557,8 +1557,6 @@ class SyclKernelBodyCreator } bool nextElement(QualType ET) final { - if (ET->isScalarType()) - return true; ArraySubscriptExpr *LastArrayRef = dyn_cast(MemberExprBases.back()); MemberExprBases.pop_back(); From 1e5b3606960ab493fddfe5a8a088eca41eec3933 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Tue, 30 Jun 2020 09:16:19 -0700 Subject: [PATCH 36/42] Enable and fix array tests after merge. Signed-off-by: Elizabeth Andrews --- .../kernel-param-member-acc-array-ih.cpp | 2 - .../kernel-param-member-acc-array.cpp | 46 +++++++--------- .../test/CodeGenSYCL/struct_kernel_param.cpp | 8 ++- clang/test/SemaSYCL/array-kernel-param.cpp | 54 +++++++++---------- .../array-kernel-param-nested-run.cpp | 1 - 5 files changed, 49 insertions(+), 62 deletions(-) mode change 100755 => 100644 clang/test/SemaSYCL/array-kernel-param.cpp diff --git a/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp index 21726109a1be9..24d3bdd633a3d 100644 --- a/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp @@ -1,6 +1,5 @@ // RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv // RUN: FileCheck -input-file=%t.h %s -// XFAIL: * // This test checks the integration header when kernel argument // is a struct containing an Accessor array. @@ -21,7 +20,6 @@ // CHECK: static constexpr // CHECK-NEXT: const kernel_param_desc_t kernel_signatures[] = { // CHECK-NEXT: //--- _ZTSZ4mainE8kernel_C -// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 24, 0 }, // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 0 }, // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 12 }, // CHECK-EMPTY: diff --git a/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp b/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp index ae476edf08c2e..1b1b25dcd3ff4 100644 --- a/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-member-acc-array.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -fsycl -fsycl-is-device -I %S/Inputs -fsycl-int-header=%t.h -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s -// XFAIL: * // This test checks a kernel with struct parameter that contains an Accessor array. @@ -29,7 +28,6 @@ int main() { // CHECK kernel_C parameters // CHECK: define spir_kernel void @{{.*}}kernel_C -// CHECK-SAME: %struct.{{.*}}.struct_acc_t* byval(%struct.{{.*}}.struct_acc_t) align 4 [[STRUCT:%[a-zA-Z0-9_]+]], // CHECK-SAME: i32 addrspace(1)* [[MEM_ARG1:%[a-zA-Z0-9_]+]], // CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1:%[a-zA-Z0-9_]+1]], // CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1:%[a-zA-Z0-9_]+2]], @@ -54,32 +52,26 @@ int main() { // CHECK: [[MEM_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range" // CHECK: [[OFFSET2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id" -// Check init of local struct -// CHECK: [[L_STRUCT_ADDR:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[MEMCPY_DST:%[0-9a-zA-Z_]+]] = bitcast %struct.{{.*}}struct_acc_t* [[L_STRUCT_ADDR]] to i8* -// CHECK: [[MEMCPY_SRC:%[0-9a-zA-Z_]+]] = bitcast %struct.{{.*}}struct_acc_t* %{{[0-9a-zA-Z_]+}} to i8* -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST]], i8* align 4 [[MEMCPY_SRC]], i64 24, i1 false) - -// Check accessor array GEP for member_acc[0] -// CHECK: [[ACCESSOR_ARRAY1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[MEMBER1:%[a-zA-Z_]+]] = getelementptr inbounds %struct.{{.*}}.struct_acc_t, %struct.{{.*}}.struct_acc_t* [[ACCESSOR_ARRAY1]], i32 0, i32 0 -// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds [2 x %"class.{{.*}}.cl::sycl::accessor"], [2 x %"class.{{.*}}.cl::sycl::accessor"]* [[MEMBER1]], i64 0, i64 0 - -// Check load from kernel pointer argument alloca -// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}} +// Check loop which calls the default constructor for each element of accessor array is emitted. +// CHECK: [[GEP_LAMBDA:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[GEP_MEMBER_ACC:%[a-zA-Z_]+]] = getelementptr inbounds %struct.{{.*}}.struct_acc_t, %struct.{{.*}}.struct_acc_t* [[GEP_LAMBDA]], i32 0, i32 0 +// CHECK: [[ARRAY_BEGIN:%[a-zA-Z0-9._]*]] = getelementptr inbounds [2 x [[ACCESSOR:.*]]], [2 x [[ACCESSOR]]]* [[GEP_MEMBER_ACC]], i64 0, i64 0 +// CHECK: [[ARRAY_END:%[a-zA-Z0-9._]*]] = getelementptr inbounds [[ACCESSOR]], [[ACCESSOR]]* [[ARRAY_BEGIN]], i64 2 +// CHECK: br label %arrayctor.loop +// CHECK: arrayctor.loop: // Check acc[0] __init method call -// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z0]] to %"class{{.*}}accessor" addrspace(4)* -// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) - -// Check accessor array GEP for member_acc[1] -// CHECK: [[ACCESSOR_ARRAY2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 -// CHECK: [[MEMBER2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct.{{.*}}.struct_acc_t, %struct.{{.*}}.struct_acc_t* [[ACCESSOR_ARRAY2]], i32 0, i32 0 -// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds [2 x %"class.{{.*}}.cl::sycl::accessor"], [2 x %"class.{{.*}}.cl::sycl::accessor"]* [[MEMBER2]], i64 0, i64 1 - -// Check load from kernel pointer argument alloca -// CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}} +// CHECK: [[GEP_LAMBDA1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[GEP_MEMBER_ACC1:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct.{{.*}}.struct_acc_t, %struct.{{.*}}.struct_acc_t* [[GEP_LAMBDA1]], i32 0, i32 0 +// CHECK: [[ARRAY_IDX1:%[a-zA-Z0-9._]*]] = getelementptr inbounds [2 x [[ACCESSOR]]], [2 x [[ACCESSOR]]]* [[GEP_MEMBER_ACC1]], i64 0, i64 0 +// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr +// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast [[ACCESSOR]]* [[ARRAY_IDX1]] to [[ACCESSOR]] addrspace(4)* +// CHECK: call spir_func void @{{.*}}__init{{.*}}([[ACCESSOR]] addrspace(4)* [[ACC_CAST1]], i32 addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE1]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1]]) // Check acc[1] __init method call -// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)* -// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) +// CHECK: [[GEP_LAMBDA2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0 +// CHECK: [[GEP_MEMBER_ACC2:%[a-zA-Z0-9_]+]] = getelementptr inbounds %struct.{{.*}}.struct_acc_t, %struct.{{.*}}.struct_acc_t* [[GEP_LAMBDA2]], i32 0, i32 0 +// CHECK: [[ARRAY_IDX2:%[a-zA-Z0-9_]*]] = getelementptr inbounds [2 x [[ACCESSOR]]], [2 x [[ACCESSOR]]]* [[GEP_MEMBER_ACC2]], i64 0, i64 1 +// CHECK: [[MEM_LOAD2:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)** [[MEM_ARG1]].addr +// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast [[ACCESSOR]]* [[ARRAY_IDX2]] to [[ACCESSOR]] addrspace(4)* +// CHECK: call spir_func void @{{.*}}__init{{.*}}([[ACCESSOR]] addrspace(4)* [[ACC_CAST2]], i32 addrspace(1)* [[MEM_LOAD2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]]) diff --git a/clang/test/CodeGenSYCL/struct_kernel_param.cpp b/clang/test/CodeGenSYCL/struct_kernel_param.cpp index 8e6fbcec309dd..896cf8c54ec84 100644 --- a/clang/test/CodeGenSYCL/struct_kernel_param.cpp +++ b/clang/test/CodeGenSYCL/struct_kernel_param.cpp @@ -4,11 +4,15 @@ // CHECK: const kernel_param_desc_t kernel_signatures[] = { // CHECK-NEXT: //--- _ZTSZZ5test0vENK3$_0clERN2cl4sycl7handlerEE8MyKernel // CHECK-NEXT: { kernel_param_kind_t::kind_accessor, 4062, 0 }, -// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 24, 12 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 12 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 16 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 20 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 24 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 28 }, +// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 32 }, // CHECK-EMPTY: // CHECK-NEXT:}; - // This test checks if compiler accepts structures as kernel parameters. #include "sycl.hpp" diff --git a/clang/test/SemaSYCL/array-kernel-param.cpp b/clang/test/SemaSYCL/array-kernel-param.cpp old mode 100755 new mode 100644 index c8bdb390467a1..42c1ef42c2256 --- a/clang/test/SemaSYCL/array-kernel-param.cpp +++ b/clang/test/SemaSYCL/array-kernel-param.cpp @@ -20,7 +20,7 @@ int main() { Accessor acc[2]; int a[2]; struct struct_acc_t { - Accessor member_acc[4]; + Accessor member_acc[2]; } struct_acc; a_kernel( @@ -69,33 +69,27 @@ int main() { // CHECK: ImplicitCastExpr // CHECK: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} '_arg_' 'int' -// Correct and enable after struct members are extracted into separate parameters -// C HECK kernel_C parameters -// C HECK: FunctionDecl {{.*}}kernel_C{{.*}} 'void (struct {{.*}}, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' -// C HECK-NEXT: ParmVarDecl {{.*}} 'struct {{.*}}' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' -// C HECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// Check kernel_C parameters +// CHECK: FunctionDecl {{.*}}kernel_C{{.*}} 'void (__global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global int *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>)' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc '__global int *' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::range<1>' +// CHECK-NEXT: ParmVarDecl {{.*}} used _arg_member_acc 'cl::sycl::id<1>' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: VarDecl {{.*}} used '(lambda at {{.*}}array-kernel-param.cpp:37:7)' cinit +// CHECK-NEXT: InitListExpr {{.*}} '(lambda at {{.*}}array-kernel-param.cpp:37:7)' +// CHECK-NEXT: InitListExpr {{.*}} 'struct_acc_t' +// CHECK-NEXT: InitListExpr {{.*}} 'Accessor [2]' +// CHECK-NEXT: CXXConstructExpr {{.*}} 'Accessor [2]' +// CHECK-NEXT: CXXConstructExpr {{.*}} 'Accessor [2]' -// C HECK that four accessor init functions are called -// C HECK: CXXMemberCallExpr {{.*}} 'void' -// C HECK-NEXT: MemberExpr {{.*}}__init -// C HECK: CXXMemberCallExpr {{.*}} 'void' -// C HECK-NEXT: MemberExpr {{.*}}__init -// C HECK: CXXMemberCallExpr {{.*}} 'void' -// C HECK-NEXT: MemberExpr {{.*}}__init -// C HECK: CXXMemberCallExpr {{.*}} 'void' -// C HECK-NEXT: MemberExpr {{.*}}__init +// Check __init functions are called +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}}__init +// CHECK: CXXMemberCallExpr {{.*}} 'void' +// CHECK-NEXT: MemberExpr {{.*}}__init diff --git a/sycl/test/array_param/array-kernel-param-nested-run.cpp b/sycl/test/array_param/array-kernel-param-nested-run.cpp index 28b9469cda89a..b8fcd8c9dfc1f 100755 --- a/sycl/test/array_param/array-kernel-param-nested-run.cpp +++ b/sycl/test/array_param/array-kernel-param-nested-run.cpp @@ -5,7 +5,6 @@ // RUN: %CPU_RUN_PLACEHOLDER %t.out // RUN: %GPU_RUN_PLACEHOLDER %t.out // RUN: %ACC_RUN_PLACEHOLDER %t.out -// XFAIL: * #include #include From 4e1220a06e83822d000a2d818ce6a9483a26cc07 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Wed, 1 Jul 2020 12:58:06 -0700 Subject: [PATCH 37/42] Fix incorrect merge conflict resolution and ClangFormat error Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 22 +++++---------------- clang/test/CodeGenSYCL/sampler.cpp | 2 +- sycl/test/basic_tests/accessor/accessor.cpp | 2 -- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 60dfb1a0ec23c..7c68dad53f451 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1246,11 +1246,6 @@ class SyclKernelDeclCreator return true; } - // FIXME Remove this function when structs are replaced by their fields - bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - return true; - } - bool handleSyclHalfType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy); return true; @@ -1568,11 +1563,6 @@ class SyclKernelBodyCreator return true; } - // FIXME Remove this function when structs are replaced by their fields - bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - return true; - } - bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { if (dyn_cast(MemberExprBases.back())) createExprForScalarElement(FD); @@ -1581,8 +1571,6 @@ class SyclKernelBodyCreator return true; } - bool enterStruct(const CXXRecordDecl *, FieldDecl *FD) final {} - bool enterStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { CXXCastPath BasePath; QualType DerivedTy(RD->getTypeForDecl(), 0); @@ -1594,6 +1582,7 @@ class SyclKernelBodyCreator SemaRef.Context, BaseTy, CK_DerivedToBase, MemberExprBases.back(), /* CXXCastPath=*/&BasePath, VK_LValue); MemberExprBases.push_back(Cast); + return true; } void addStructInit(const CXXRecordDecl *RD) { @@ -1633,12 +1622,14 @@ class SyclKernelBodyCreator InitExprs.pop_back(); } } + return true; } bool leaveStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final { const CXXRecordDecl *BaseClass = BS.getType()->getAsCXXRecordDecl(); addStructInit(BaseClass); MemberExprBases.pop_back(); + return true; } bool enterField(const CXXRecordDecl *RD, FieldDecl *FD) final { @@ -1687,11 +1678,13 @@ class SyclKernelBodyCreator using SyclKernelFieldHandler::enterArray; using SyclKernelFieldHandler::enterField; + using SyclKernelFieldHandler::enterStruct; using SyclKernelFieldHandler::handleScalarType; using SyclKernelFieldHandler::handleSyclHalfType; using SyclKernelFieldHandler::handleSyclSamplerType; using SyclKernelFieldHandler::leaveArray; using SyclKernelFieldHandler::leaveField; + using SyclKernelFieldHandler::leaveStruct; }; class SyclKernelIntHeaderCreator @@ -1786,11 +1779,6 @@ class SyclKernelIntHeaderCreator return true; } - // FIXME Remove this function when structs are replaced by their fields - bool handleStructType(FieldDecl *FD, QualType FieldTy) final { - return true; - } - bool handleScalarType(FieldDecl *FD, QualType FieldTy) final { addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout); return true; diff --git a/clang/test/CodeGenSYCL/sampler.cpp b/clang/test/CodeGenSYCL/sampler.cpp index ab22e8a12717c..39755aa6b9aec 100644 --- a/clang/test/CodeGenSYCL/sampler.cpp +++ b/clang/test/CodeGenSYCL/sampler.cpp @@ -14,7 +14,7 @@ // CHECK: define spir_kernel void @{{[a-zA-Z0-9_]+}}(%opencl.sampler_t addrspace(2)* [[SAMPLER_ARG_WRAPPED:%[a-zA-Z0-9_]+]], i32 [[ARG_A:%[a-zA-Z0-9_]+]]) -// Check alloca +// Check alloca // CHECK: [[SAMPLER_ARG_WRAPPED]].addr = alloca %opencl.sampler_t addrspace(2)*, align 8 // CHECK: [[ARG_A]].addr = alloca i32, align 4 // CHECK: [[LAMBDA:%[0-9]+]] = alloca %"class.{{.*}}.anon.0", align 8 diff --git a/sycl/test/basic_tests/accessor/accessor.cpp b/sycl/test/basic_tests/accessor/accessor.cpp index 2a6d0bf1e97e7..ebb99a0f7abbe 100644 --- a/sycl/test/basic_tests/accessor/accessor.cpp +++ b/sycl/test/basic_tests/accessor/accessor.cpp @@ -513,10 +513,8 @@ int main() { sycl::buffer B(&cnst, sycl::range<1>(1)); sycl::buffer C(&data, sycl::range<1>(1)); - sycl::queue queue; queue.submit([&](sycl::handler &cgh) { - sycl::accessor AccA(A, cgh); From 5dcf420b33e7136c99f74600df3b5b792820e188 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Wed, 1 Jul 2020 15:55:35 -0700 Subject: [PATCH 38/42] ClangFormat changes Signed-off-by: Elizabeth Andrews --- sycl/test/functor/functor_inheritance.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sycl/test/functor/functor_inheritance.cpp b/sycl/test/functor/functor_inheritance.cpp index 8bedd4126695b..3a3d5218ef6ec 100644 --- a/sycl/test/functor/functor_inheritance.cpp +++ b/sycl/test/functor/functor_inheritance.cpp @@ -26,7 +26,7 @@ struct InnerField : public InnerFieldBase { }; struct Base { - Base (int _B, int _C, int _D) : B(_B), InnerObj(_C, _D) {} + Base(int _B, int _C, int _D) : B(_B), InnerObj(_C, _D) {} int B; InnerField InnerObj; }; @@ -35,7 +35,7 @@ struct Derived : public Base, public SecondBase { Derived( int _A, int _B, int _C, int _D, int _E, cl::sycl::accessor &_Acc) - : A(_A), Acc(_Acc), /*Out(_Out),*/ Base(_B, _C, _D), SecondBase(_E) {} + : A(_A), Acc(_Acc), /*Out(_Out),*/ Base(_B, _C, _D), SecondBase(_E) {} void operator()() { Acc[0] = this->A + this->B + this->InnerObj.C + this->InnerObj.D + this->E; } @@ -45,18 +45,17 @@ struct Derived : public Base, public SecondBase { }; int main() { - int A[] = { 10 }; + int A[] = {10}; { cl::sycl::queue Q; cl::sycl::buffer Buf(A, 1); Q.submit([&](cl::sycl::handler &cgh) { auto Acc = Buf.get_access(cgh); - Derived F = {1, 2, 3, 4, 5, Acc/*, Out*/}; + Derived F = {1, 2, 3, 4, 5, Acc /*, Out*/}; cgh.single_task(F); }); } assert(A[0] == 15); return 0; } - From d5f56b3fd57b8ff06798d2ad1c105657e48923b8 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Wed, 1 Jul 2020 18:21:27 -0700 Subject: [PATCH 39/42] Fix Windows test failure due to mangling Signed-off-by: Elizabeth Andrews --- clang/test/SemaSYCL/accessor_inheritance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/SemaSYCL/accessor_inheritance.cpp b/clang/test/SemaSYCL/accessor_inheritance.cpp index 830711b4bab95..17dafe7b4acdd 100644 --- a/clang/test/SemaSYCL/accessor_inheritance.cpp +++ b/clang/test/SemaSYCL/accessor_inheritance.cpp @@ -20,7 +20,7 @@ int main() { } // Check kernel parameters -// CHECK: FunctionDecl {{.*}}kernel 'void (int, int, __global char *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global char *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, int)' +// CHECK: FunctionDecl {{.*}}kernel{{.*}} 'void (int, int, __global char *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, __global char *, cl::sycl::range<1>, cl::sycl::range<1>, cl::sycl::id<1>, int)' // CHECK: ParmVarDecl{{.*}} used _arg_A 'int' // CHECK: ParmVarDecl{{.*}} used _arg_B 'int' // CHECK: ParmVarDecl{{.*}} used _arg_AccField '__global char *' From 91954fd0c248b339fb82d1bcfccf16f5f1b0120e Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Thu, 2 Jul 2020 09:14:45 -0700 Subject: [PATCH 40/42] Removed unused variable and modified comments Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index b87c9bfaf518a..5ea15483e9412 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -820,13 +820,16 @@ static void VisitRecordHelper(CXXRecordDecl *Owner, for (const auto &Base : Range) { (void)std::initializer_list{(handlers.enterField(Owner, Base), 0)...}; QualType BaseTy = Base.getType(); + // Handle accessor class as base if (Util::isSyclAccessorType(BaseTy)) { (void)std::initializer_list{ (handlers.handleSyclAccessorType(Base, BaseTy), 0)...}; } else if (Util::isSyclStreamType(BaseTy)) { + // Handle stream class as base (void)std::initializer_list{ (handlers.handleSyclStreamType(Base, BaseTy), 0)...}; } else + // For all other bases, visit the record VisitRecord(Owner, Base, BaseTy->getAsCXXRecordDecl(), handlers...); (void)std::initializer_list{(handlers.leaveField(Owner, Base), 0)...}; } @@ -1287,7 +1290,6 @@ class SyclKernelBodyCreator llvm::SmallVector MemberExprBases; uint64_t ArrayIndex; FunctionDecl *KernelCallerFunc; - bool ArrayState; // Using the statements/init expressions that we've created, this generates // the kernel body compound stmt. CompoundStmt needs to know its number of @@ -1505,8 +1507,7 @@ class SyclKernelBodyCreator KernelObjClone(createKernelObjClone(S.getASTContext(), DC.getKernelDecl(), KernelObj)), VarEntity(InitializedEntity::InitializeVariable(KernelObjClone)), - KernelObj(KernelObj), KernelCallerFunc(KernelCallerFunc), - ArrayState(false) { + KernelObj(KernelObj), KernelCallerFunc(KernelCallerFunc) { markParallelWorkItemCalls(); Stmt *DS = new (S.Context) DeclStmt(DeclGroupRef(KernelObjClone), @@ -1615,6 +1616,7 @@ class SyclKernelBodyCreator if (!Util::isSyclStreamType(FD->getType())) addStructInit(RD); // Pop out unused initializers created in handleSyclAccesorType + // for accessors inside stream class. else { for (const auto &Field : RD->fields()) { QualType FieldTy = Field->getType(); From 62ab84d6090c3494c44bce19f040f1b331bf95e1 Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Thu, 2 Jul 2020 10:56:42 -0700 Subject: [PATCH 41/42] Fix incorrect merge resolution Signed-off-by: Elizabeth Andrews --- clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp | 2 +- clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp | 2 +- clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) mode change 100755 => 100644 clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp diff --git a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp old mode 100755 new mode 100644 index f9dfd144079e1..8c2cfb2a1bd8b --- a/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-acc-array-ih.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv +// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -triple spir64-unknown-unknown-sycldevice -fsycl-int-header=%t.h %s -fsyntax-only // RUN: FileCheck -input-file=%t.h %s // This test checks the integration header generated when diff --git a/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp index 24d3bdd633a3d..f5f679f7d3650 100644 --- a/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-member-acc-array-ih.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv +// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -triple spir64-unknown-unknown-sycldevice -fsycl-int-header=%t.h %s -fsyntax-only // RUN: FileCheck -input-file=%t.h %s // This test checks the integration header when kernel argument diff --git a/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp b/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp index 349d540f22ebc..d4a5c8d5995a0 100755 --- a/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp +++ b/clang/test/CodeGenSYCL/kernel-param-pod-array-ih.cpp @@ -1,4 +1,5 @@ -// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o %T/kernel.spv +// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -triple spir64-unknown-unknown-sycldevice -fsycl-int-header=%t.h %s -fsyntax-only +// RUN: FileCheck -input-file=%t.h %s // This test checks the integration header generated for a kernel // with an argument that is a POD array. From a7ad39c54ff0ed05307cb7b37000caee766e19ed Mon Sep 17 00:00:00 2001 From: Elizabeth Andrews Date: Thu, 2 Jul 2020 16:15:22 -0700 Subject: [PATCH 42/42] ClangFormat Change Signed-off-by: Elizabeth Andrews --- clang/lib/Sema/SemaSYCL.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 18f65ea37d0c1..7f85562f94822 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1896,8 +1896,7 @@ void Sema::ConstructOpenCLKernel(FunctionDecl *KernelCallerFunc, ConstructingOpenCLKernel = true; VisitRecordBases(KernelObj, checker, kernel_decl, kernel_body, int_header); - VisitRecordFields(KernelObj, checker, kernel_decl, kernel_body, - int_header); + VisitRecordFields(KernelObj, checker, kernel_decl, kernel_body, int_header); ConstructingOpenCLKernel = false; }