Skip to content

[SYCL] Add support for union types as kernel parameter #2285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Aug 17, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7be969a
[SYCL] Add support for union
smanna12 Aug 7, 2020
6de6dff
add tests
smanna12 Aug 11, 2020
60f02ac
Fix Clang-format issue
smanna12 Aug 11, 2020
fc3999b
Fix Clang-format issue
smanna12 Aug 11, 2020
e71672c
Fix Clang-format issue
smanna12 Aug 11, 2020
c36ab3f
Merge remote-tracking branch 'intel_llvm/sycl' into UnionKernelArgument
smanna12 Aug 12, 2020
99e8b2a
update tests and code changes
smanna12 Aug 13, 2020
2e7b74b
Fix Clang format issue
smanna12 Aug 13, 2020
3e4d4fc
Fix Clang format issue
smanna12 Aug 13, 2020
d3a5172
Fix test
smanna12 Aug 13, 2020
7802eda
Update tests and patch based on review comments
smanna12 Aug 13, 2020
2ed64f3
Fix Clang-format issue
smanna12 Aug 13, 2020
e9c65c0
Fix runtime test failure and add new integration header test
smanna12 Aug 13, 2020
1376cad
Add diagnostic tests
smanna12 Aug 13, 2020
bc09151
Fix clang format issue
smanna12 Aug 13, 2020
5931e4a
Address review commensts
smanna12 Aug 14, 2020
fabd978
Address review comment and fix clang-format issues
smanna12 Aug 14, 2020
0e15676
Fix runtime test
smanna12 Aug 14, 2020
ede7a0b
Fix typo on runtime test
smanna12 Aug 14, 2020
7338c0d
Fix runtime test
smanna12 Aug 14, 2020
6a80b99
Fix runtime test
smanna12 Aug 14, 2020
fdfbe19
Add empty base case for windows failure
smanna12 Aug 14, 2020
03460a1
Fix sema codes
smanna12 Aug 14, 2020
f4450a6
Fix clang-format issue
smanna12 Aug 14, 2020
f772189
Fix clang-format issue and update source codes
smanna12 Aug 14, 2020
db9f49d
Fix clang-format issues
smanna12 Aug 14, 2020
347b21c
update test based on reiew
smanna12 Aug 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 127 additions & 4 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,13 @@ class KernelObjVisitor {
else if (ElementTy->isStructureOrClassType())
VisitRecord(Owner, ArrayField, ElementTy->getAsCXXRecordDecl(),
handlers...);
else if (ElementTy->isUnionType())
// TODO: This check is still necessary I think?! Array seems to handle
// this differently (see above) for structs I think.
// if (KF_FOR_EACH(handleUnionType, Field, FieldTy)) {
VisitUnion(Owner, ArrayField, ElementTy->getAsCXXRecordDecl(),
handlers...);
//}
else if (ElementTy->isArrayType())
VisitArrayElements(ArrayField, ElementTy, handlers...);
else if (ElementTy->isScalarType())
Expand Down Expand Up @@ -857,6 +864,40 @@ class KernelObjVisitor {
void VisitRecord(const CXXRecordDecl *Owner, ParentTy &Parent,
const CXXRecordDecl *Wrapper, Handlers &... handlers);

// Base case, only calls these when filtered.
template <typename... FilteredHandlers, typename ParentTy>
void VisitUnion(const CXXRecordDecl *Owner, ParentTy &Parent,
const CXXRecordDecl *Wrapper,
FilteredHandlers &... handlers) {
(void)std::initializer_list<int>{
(handlers.enterUnion(Owner, Parent), 0)...};
VisitRecordHelper(Wrapper, Wrapper->fields(), handlers...);
(void)std::initializer_list<int>{
(handlers.leaveUnion(Owner, Parent), 0)...};
}

template <typename... FilteredHandlers, typename ParentTy,
typename CurHandler, typename... Handlers>
std::enable_if_t<!CurHandler::VisitUnionBody>
VisitUnion(const CXXRecordDecl *Owner, ParentTy &Parent,
const CXXRecordDecl *Wrapper,
FilteredHandlers &... filtered_handlers, CurHandler &cur_handler,
Handlers &... handlers) {
VisitUnion<FilteredHandlers...>(Owner, Parent, Wrapper,
filtered_handlers..., handlers...);
}

template <typename... FilteredHandlers, typename ParentTy,
typename CurHandler, typename... Handlers>
std::enable_if_t<CurHandler::VisitUnionBody>
VisitUnion(const CXXRecordDecl *Owner, ParentTy &Parent,
const CXXRecordDecl *Wrapper,
FilteredHandlers &... filtered_handlers, CurHandler &cur_handler,
Handlers &... handlers) {
VisitUnion<FilteredHandlers..., CurHandler>(
Owner, Parent, Wrapper, filtered_handlers..., cur_handler, handlers...);
}

template <typename... Handlers>
void VisitRecordHelper(const CXXRecordDecl *Owner,
clang::CXXRecordDecl::base_class_const_range Range,
Expand Down Expand Up @@ -942,6 +983,11 @@ class KernelObjVisitor {
CXXRecordDecl *RD = FieldTy->getAsCXXRecordDecl();
VisitRecord(Owner, Field, RD, handlers...);
}
} else if (FieldTy->isUnionType()) {
if (KF_FOR_EACH(handleUnionType, Field, FieldTy)) {
CXXRecordDecl *RD = FieldTy->getAsCXXRecordDecl();
VisitUnion(Owner, Field, RD, handlers...);
}
} else if (FieldTy->isReferenceType())
KF_FOR_EACH(handleReferenceType, Field, FieldTy);
else if (FieldTy->isPointerType())
Expand Down Expand Up @@ -1005,6 +1051,7 @@ class SyclKernelFieldHandler {
}
virtual bool handleSyclHalfType(FieldDecl *, QualType) { return true; }
virtual bool handleStructType(FieldDecl *, QualType) { return true; }
virtual bool handleUnionType(FieldDecl *, QualType) { return true; }
virtual bool handleReferenceType(FieldDecl *, QualType) { return true; }
virtual bool handlePointerType(FieldDecl *, QualType) { return true; }
virtual bool handleArrayType(FieldDecl *, QualType) { return true; }
Expand All @@ -1024,6 +1071,8 @@ class SyclKernelFieldHandler {
virtual bool leaveStruct(const CXXRecordDecl *, const CXXBaseSpecifier &) {
return true;
}
virtual bool enterUnion(const CXXRecordDecl *, FieldDecl *) { return true; }
virtual bool leaveUnion(const CXXRecordDecl *, FieldDecl *) { return true; }

// The following are used for stepping through array elements.

Expand Down Expand Up @@ -1201,11 +1250,71 @@ class SyclKernelFieldChecker : public SyclKernelFieldHandler {
}
};

// A type to check the validity of passing union with accessor/sampler/stream
// member as a kernel argument types.
class SyclKernelUnionBodyChecker : public SyclKernelFieldHandler {
static constexpr const bool VisitUnionBody = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your problem is that SyclKernelFieldHandler needs this line as well, except =false instead.

I think that results in the union-filtering not actually doing anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think that results in the union-filtering not actually doing anything.
Yes, Thanks Erich.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find where it is done, that might be the reason why everything is decomposed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it should be in the base class, not just the other checker. WHich is why the tests are still wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

int UnionCount = 0;
bool IsInvalid = false;
DiagnosticsEngine &Diag;

public:
SyclKernelUnionBodyChecker(Sema &S)
: SyclKernelFieldHandler(S), Diag(S.getASTContext().getDiagnostics()) {}
bool isValid() { return !IsInvalid; }

bool enterUnion(const CXXRecordDecl *RD, FieldDecl *FD) {
++UnionCount;
return true;
}

bool leaveUnion(const CXXRecordDecl *RD, FieldDecl *FD) {
--UnionCount;
return true;
}

bool handlePointerType(FieldDecl *FD, QualType FieldTy) final {
if (UnionCount) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a NIT, so don't redo the patch just for this (but please do if you have to push another patch), but I'd prefer the body of this to be extracted into a private function. Something like:
bool checkType(SourceLocation Loc, QualType Ty) { if (UnionCount) { IsInvalid = true; DiagReport(Loc, diag::err_bad_union_kernel_param_members) << Ty); } return isValid(); }

Then, all of these other functions simply become:

return checkType(FD->getLocation() /* or BS.getBeginLoc()*/, FieldTy);

I want this, since I believe it will make the 'printing the diagnostic trail' much easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

IsInvalid = true;
Diag.Report(FD->getLocation(), diag::err_bad_kernel_param_type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need a new diagnostic that says about union members. This one is incorrect here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

<< FieldTy;
}
return isValid();
}

bool handleSyclAccessorType(FieldDecl *FD, QualType FieldTy) final {
if (UnionCount) {
IsInvalid = true;
Diag.Report(FD->getLocation(), diag::err_bad_kernel_param_type)
<< FieldTy;
}
return isValid();
}

bool handleSyclSamplerType(FieldDecl *FD, QualType FieldTy) final {
if (UnionCount) {
IsInvalid = true;
Diag.Report(FD->getLocation(), diag::err_bad_kernel_param_type)
<< FieldTy;
}
return isValid();
}
bool handleSyclStreamType(FieldDecl *FD, QualType FieldTy) final {
if (UnionCount) {
IsInvalid = true;
Diag.Report(FD->getLocation(), diag::err_bad_kernel_param_type)
<< FieldTy;
}
return isValid();
}
};

// A type to Create and own the FunctionDecl for the kernel.
class SyclKernelDeclCreator : public SyclKernelFieldHandler {
FunctionDecl *KernelDecl;
llvm::SmallVector<ParmVarDecl *, 8> Params;
SyclKernelFieldChecker &ArgChecker;
SyclKernelUnionBodyChecker &ArgChecker1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably call this 'UnionChecker'. Additionally, note that #2289 makes this part unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I will change that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, looking again, adding ArgChecker1 is completely unnecessary. ArgChecker itself is completely unnecessary here too (and likely should be removed). I remove it in #2289 (depending on when that gets in), but it seems to be a leftover from when it was necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like only place we can add this handler "SyclKernelUnionBodyChecker checker1(*this)" to ConstructOpenCLKernel.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I thought you would have noticed trying to debug why diagnostics weren't working right, but it needs to be added to these two lines instead:

Visitor.VisitRecordBases(KernelObj, checker, kernel_decl, kernel_body,
int_header);

Visitor.VisitRecordFields(KernelObj, checker, kernel_decl, kernel_body,
int_header);

Sema::ContextRAII FuncContext;
// Holds the last handled field's first parameter. This doesn't store an
// iterator as push_back invalidates iterators.
Expand Down Expand Up @@ -1340,12 +1449,13 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {

public:
SyclKernelDeclCreator(Sema &S, SyclKernelFieldChecker &ArgChecker,
StringRef Name, SourceLocation Loc, bool IsInline,
bool IsSIMDKernel)
SyclKernelUnionBodyChecker &ArgChecker1, StringRef Name,
SourceLocation Loc, bool IsInline, bool IsSIMDKernel)
: SyclKernelFieldHandler(S),
KernelDecl(createKernelDecl(S.getASTContext(), Name, Loc, IsInline,
IsSIMDKernel)),
ArgChecker(ArgChecker), FuncContext(SemaRef, KernelDecl) {}
ArgChecker(ArgChecker), ArgChecker1(ArgChecker1),
FuncContext(SemaRef, KernelDecl) {}

~SyclKernelDeclCreator() {
ASTContext &Ctx = SemaRef.getASTContext();
Expand Down Expand Up @@ -1416,6 +1526,10 @@ class SyclKernelDeclCreator : public SyclKernelFieldHandler {
return true;
}

bool handleUnionType(FieldDecl *FD, QualType FieldTy) final {
return handleScalarType(FD, FieldTy);
}

bool handleSyclHalfType(FieldDecl *FD, QualType FieldTy) final {
addParam(FD, FieldTy);
return true;
Expand Down Expand Up @@ -1751,6 +1865,10 @@ class SyclKernelBodyCreator : public SyclKernelFieldHandler {
return true;
}

bool handleUnionType(FieldDecl *FD, QualType FieldTy) final {
return handleScalarType(FD, FieldTy);
}

bool enterStruct(const CXXRecordDecl *RD, const CXXBaseSpecifier &BS) final {
CXXCastPath BasePath;
QualType DerivedTy(RD->getTypeForDecl(), 0);
Expand Down Expand Up @@ -1955,6 +2073,10 @@ class SyclKernelIntHeaderCreator : public SyclKernelFieldHandler {
return true;
}

bool handleUnionType(FieldDecl *FD, QualType FieldTy) final {
return handleScalarType(FD, FieldTy);
}

bool handleSyclStreamType(FieldDecl *FD, QualType FieldTy) final {
addParam(FD, FieldTy, SYCLIntegrationHeader::kind_std_layout);
return true;
Expand Down Expand Up @@ -2057,8 +2179,9 @@ void Sema::ConstructOpenCLKernel(FunctionDecl *KernelCallerFunc,
Diag(LC.getLocation(), diag::err_implicit_this_capture);
}
SyclKernelFieldChecker checker(*this);
SyclKernelUnionBodyChecker checker1(*this);
SyclKernelDeclCreator kernel_decl(
*this, checker, KernelName, KernelObj->getLocation(),
*this, checker, checker1, KernelName, KernelObj->getLocation(),
KernelCallerFunc->isInlined(), KernelCallerFunc->hasAttr<SYCLSimdAttr>());
SyclKernelBodyCreator kernel_body(*this, kernel_decl, KernelObj,
KernelCallerFunc);
Expand Down
70 changes: 70 additions & 0 deletions clang/test/CodeGenSYCL/union-kernel-param.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// RUN: %clang_cc1 -I %S/Inputs -fsycl -fsycl-is-device -triple spir64-unknown-unknown-sycldevice -fsycl-int-header=%t.h %s -o %t.out
// RUN: FileCheck -input-file=%t.h %s

// This test checks the integration header generated when
// the kernel argument is union.

// CHECK: #include <CL/sycl/detail/kernel_desc.hpp>

// CHECK: class MyKernel;

// 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: "_ZTSZZ5test0vENK3$_0clERN2cl4sycl7handlerEE8MyKernel"
// CHECK-NEXT: };

// CHECK: static constexpr
// CHECK-NEXT: 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, 12, 16 },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is right, is it? 'S' is just the union, right? We should be capturing that as a single std_layout, not as however many these are.

// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 16 },
// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 16 },
// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 16 },
// CHECK-NEXT: { kernel_param_kind_t::kind_std_layout, 4, 16 },
// 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-EMPTY:
// CHECK-NEXT:};

// CHECK: static constexpr
// CHECK-NEXT: const unsigned kernel_signature_start[] = {
// CHECK-NEXT: 0 // _ZTSZZ5test0vENK3$_0clERN2cl4sycl7handlerEE8MyKernel
// CHECK-NEXT: };

// CHECK: template <> struct KernelInfo<class MyKernel> {

#include "sycl.hpp"

using namespace cl::sycl;

union MyNestedUnion {
int FldArr[1];
float FldFloat;
};

union MyUnion {
int FldInt;
MyNestedUnion FldUnion;
int FldArr[3];
};

MyUnion GlobS;

bool test0() {
MyUnion S = GlobS;
MyUnion S0 = {0};
{
buffer<MyUnion, 1> Buf(&S0, range<1>(1));
queue myQueue;
myQueue.submit([&](handler &cgh) {
auto B = Buf.get_access<access::mode::write>(cgh);
cgh.single_task<class MyKernel>([=] { B; S; });
});
}
}
83 changes: 83 additions & 0 deletions clang/test/CodeGenSYCL/union-kernel-param1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// 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 if compiler accepts union as kernel parameters.

#include "sycl.hpp"

using namespace cl::sycl;

union MyUnion {
int FldInt;
int FldArr[3];
};

MyUnion GlobS;

void test0() {
MyUnion S = GlobS;
MyUnion S0 = {0};
{
buffer<MyUnion, 1> Buf(&S0, range<1>(1));
queue myQueue;
myQueue.submit([&](handler &cgh) {
auto B = Buf.get_access<access::mode::write>(cgh);
cgh.single_task<class MyKernel>([=] {B; S; });
});
}
}

// CHECK MyKernel parameters
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elizabethandrews can you take a look at this? The union should be a simple un-decomposed value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the Sema tests below, it looks like parameters are being passed individually as well? This in incorrect right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the test seems incorrect. I will update the test. Thanks Erich and Elizabeth.

// CHECK: define spir_kernel void @{{.*}}MyKernel
// CHECK-SAME: %union.{{.*}}.MyUnion addrspace(1)* [[MEM_ARG1:%[a-zA-Z0-9_]+]],
// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_ARG2:%[a-zA-Z0-9_]+1]],
// CHECK-SAME: %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[MEM_ARG3:%[a-zA-Z0-9_]+2]],
// CHECK-SAME: %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET1:%[a-zA-Z0-9_]+3]],
// CHECK-SAME: %union.{{.*}}.MyUnion* byval(%union.{{.*}}.MyUnion) align 4 [[MEM_ARG4:%[a-zA-Z0-9_]+4]],
// CHECK-SAME: i32 [[MEM1:%[a-zA-Z0-9_]+]],
// CHECK-SAME: i32 [[MEM2_Array1:%[a-zA-Z0-9_]+]],
// CHECK-SAME: i32 [[MEM2_Array2:%[a-zA-Z0-9_]+]],
// CHECK-SAME: i32 [[MEM2_Array3:%[a-zA-Z0-9_]+]]

// Check alloca for pointer arguments
// CHECK: [[MEM_ARG1]].addr{{[0-9]*}} = alloca %union._ZTS7MyUnion.MyUnion addrspace(1)*, align 8
// CHECK: [[MEM1]].addr{{[0-9]*}} = alloca i32, align 4
// CHECK: [[MEM2_Array1]].addr{{[0-9]*}} = alloca i32, align 4
// CHECK: %_arg_FldArr.addr6 = alloca i32, align 4
// CHECK: %_arg_FldArr.addr8 = alloca i32, align 4

// 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: [[ACC_RANGE2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::range"
// CHECK: [[OFFSET2:%[a-zA-Z0-9_.]+]] = alloca %"struct.{{.*}}.cl::sycl::id"

// CHECK: store i32 [[MEM1]], i32* [[MEM1]].addr{{[0-9]*}}, align 4
// CHECK: store i32 [[MEM2_Array1]], i32* [[MEM2_Array1]].addr{{[0-9]*}}, align 4
// CHECK: store i32 [[MEM2_Array2]], i32* %_arg_FldArr.addr6, align 4
// CHECK: store i32 [[MEM2_Array3]], i32* %_arg_FldArr.addr8, align 4
//
// CHECK: [[L_STRUCT_ADDR:%[a-zA-Z0-9_]+]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0
// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[L_STRUCT_ADDR]] to %"class{{.*}}accessor" addrspace(4)*
// CHECK: call spir_func void @{{.*}}MyUnion{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]])
// CHECK: [[Z0:%[a-zA-Z0-9_]*]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 1
// CHECK: [[MEMCPY_DST:%[0-9a-zA-Z_]+]] = bitcast %union.{{.*}}MyUnion* [[Z0]] to i8*
// CHECK: [[MEMCPY_SRC:%[0-9a-zA-Z_]+]] = bitcast %union.{{.*}}MyUnion* [[MEM_ARG4]] to i8*
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST]], i8* align 4 [[MEMCPY_SRC]], i64 12, i1 false)
// CHECK: [[Z1:%[a-zA-Z0-9_]*]] = getelementptr inbounds %"class.{{.*}}.anon", %"class.{{.*}}.anon"* [[LOCAL_OBJECT]], i32 0, i32 0

// Check load from kernel pointer argument alloca
// CHECK: [[MEM_LOAD1:%[a-zA-Z0-9_]+]] = load %union._ZTS7MyUnion.MyUnion addrspace(1)*, %union._ZTS7MyUnion.MyUnion addrspace(1)** [[MEM_ARG1]].addr{{[0-9]*}}, align 8
// CHECK: [[MEMCPY_DST1:%[0-9a-zA-Z_]+]] = bitcast %"struct.{{.*}}.cl::sycl::range"* [[ACC_RANGE1]] to i8*
// CHECK: [[MEMCPY_SRC1:%[0-9a-zA-Z_]+]] = bitcast %"struct.{{.*}}.cl::sycl::range"* [[MEM_ARG2]] to i8*
// call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST1]], i8* align 4 [[MEMCPY_SRC1]], i64 4, i1 false), !tbaa.struct [[ACC_CAST2:%[0-9]+]]
// CHECK: [[MEMCPY_DST2:%[0-9a-zA-Z_]+]] = bitcast %"struct.{{.*}}.cl::sycl::range"* [[ACC_RANGE2]] to i8*
// CHECK: [[MEMCPY_SRC2:%[0-9a-zA-Z_]+]] = bitcast %"struct.{{.*}}.cl::sycl::range"* [[MEM_ARG3]] to i8*
// call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST2]], i8* align 4 [[MEMCPY_SRC2]], i64 4, i1 false), !tbaa.struct [[ACC_CAST2:%[0-9]+]]

// Check __init method call
// CHECK: [[ACC_CAST1:%[0-9]+]] = addrspacecast %"class{{.*}}accessor"* [[Z1]] to %"class{{.*}}accessor" addrspace(4)*
// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.{{.*}}.cl::sycl::accessor" addrspace(4)* [[ACC_CAST1]], %union._ZTS7MyUnion.MyUnion addrspace(1)* [[MEM_LOAD1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE1]], %"struct.{{.*}}.cl::sycl::range"* byval({{.*}}) align 4 [[ACC_RANGE2]], %"struct.{{.*}}.cl::sycl::id"* byval({{.*}}) align 4 [[OFFSET2]])
// CHECK: [[ACC_CAST2:%[0-9]+]] = addrspacecast %"class{{.*}}.anon"* [[LOCAL_OBJECT]] to %"class{{.*}}.anon" addrspace(4)*
// CHECK: call spir_func void @{{.*}}(%"class.{{.*}}.anon" addrspace(4)* [[ACC_CAST2]])
Loading