Skip to content

[SYCL] Wrap pointer fields inside a generated struct #2288

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 6 commits into from
Aug 14, 2020

Conversation

elizabethandrews
Copy link
Contributor

@elizabethandrews elizabethandrews commented Aug 10, 2020

Commit 0b2de9e implemented decomposition of kernel parameters
of struct type i.e. fields of a struct are now individually
passed as separate kernel arguments. This results in an error
for pointer fields of the struct, violating SYCL spec. Copy of structs
with pointers is allowed according to the spec (value of pointer is
undefined). This patch fixes this error by wrapping the pointer
fields in a '__wrapper_class' after the decomposition of struct type.

Signed-off-by: Elizabeth Andrews elizabeth.andrews@intel.com

Commit 0b2de9e implemented decomposition of kernel parameters
of struct type i.e. fields of a struct are now individually
passed as separate kernel arguments. This results in an error
for pointer fields, violating SYCL spec. Copy of structs with
pointers is allowed according to the spec (value of pointer is
undefined). This patch fixes this error by wrapping all pointer
fields in a 'wrapper_struct'.

Signed-off-by: Elizabeth Andrews <elizabeth.andrews@intel.com>
@jbrodman
Copy link
Contributor

Why are we doing this?

I worry this is going to cause a performance regression.

There only should've been an error decomposing structs if the integration header isn't updated to flag the pointers as kind_pointer instead of kind_std_layout.

Comment on lines 1396 to 1397
RecordDecl *WrapperClass =
SemaRef.getASTContext().buildImplicitRecord("wrapper_class");
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add __ prefix to the class name so this class won't conflict with user-defined classes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there value to making it an anonymous struct? Doesn't seem much value to making it named.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure. I can take a look.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't change this to use anonymous structs. I don't think it makes a difference in terms of functionality/behavior. Personally I feel naming it makes intention clear. I didn't see a problem with LLVM IR. It handles the names as wrapper_class, wrapper_class.0, etc

@elizabethandrews
Copy link
Contributor Author

There only should've been an error decomposing structs if the integration header isn't updated to flag the pointers as kind_pointer instead of kind_std_layout.

Can you elaborate on this @jbrodman ?

Why are we doing this?

This change was required because unused pointers in structs were causing an error in the backend when being passed as a direct kernel parameters (after decomposition fields are passed individually. Earlier they were passed inside the struct as a whole). According to the spec, this shouldn't throw an error.

I worry this is going to cause a performance regression

I can look into doing this only for user code where pointers are inside structs, instead of unconditionally doing it for all pointers. That way current USM functionality shouldn't be affected. I think.

Comment on lines 1396 to 1397
RecordDecl *WrapperClass =
SemaRef.getASTContext().buildImplicitRecord("wrapper_class");
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there value to making it an anonymous struct? Doesn't seem much value to making it named.

@jbrodman
Copy link
Contributor

There only should've been an error decomposing structs if the integration header isn't updated to flag the pointers as kind_pointer instead of kind_std_layout.

Can you elaborate on this @jbrodman ?

Why are we doing this?

This change was required because unused pointers in structs were causing an error in the backend when being passed as a direct kernel parameters (after decomposition fields are passed individually. Earlier they were passed inside the struct as a whole). According to the spec, this shouldn't throw an error.

I worry this is going to cause a performance regression

I can look into doing this only for user code where pointers are inside structs, instead of unconditionally doing it for all pointers. That way current USM functionality shouldn't be affected. I think.

At least in the past, pointers in structs tended to be treated as being generic and not global, and I know this was causing all sorts of problems. If we can make sure all the pointers remain global, even in structs, then that's probably ok?

@elizabethandrews
Copy link
Contributor Author

There only should've been an error decomposing structs if the integration header isn't updated to flag the pointers as kind_pointer instead of kind_std_layout.

Can you elaborate on this @jbrodman ?

Why are we doing this?

This change was required because unused pointers in structs were causing an error in the backend when being passed as a direct kernel parameters (after decomposition fields are passed individually. Earlier they were passed inside the struct as a whole). According to the spec, this shouldn't throw an error.

I worry this is going to cause a performance regression

I can look into doing this only for user code where pointers are inside structs, instead of unconditionally doing it for all pointers. That way current USM functionality shouldn't be affected. I think.

At least in the past, pointers in structs tended to be treated as being generic and not global, and I know this was causing all sorts of problems. If we can make sure all the pointers remain global, even in structs, then that's probably ok?

At the moment, they are yes. Struct decomposition implemented this by 'accident'.

@jbrodman
Copy link
Contributor

There only should've been an error decomposing structs if the integration header isn't updated to flag the pointers as kind_pointer instead of kind_std_layout.

Can you elaborate on this @jbrodman ?

Why are we doing this?

This change was required because unused pointers in structs were causing an error in the backend when being passed as a direct kernel parameters (after decomposition fields are passed individually. Earlier they were passed inside the struct as a whole). According to the spec, this shouldn't throw an error.

I worry this is going to cause a performance regression

I can look into doing this only for user code where pointers are inside structs, instead of unconditionally doing it for all pointers. That way current USM functionality shouldn't be affected. I think.

At least in the past, pointers in structs tended to be treated as being generic and not global, and I know this was causing all sorts of problems. If we can make sure all the pointers remain global, even in structs, then that's probably ok?

At the moment, they are yes. Struct decomposition implemented this by 'accident'.

Ok that's probably a net positive then :)

@elizabethandrews
Copy link
Contributor Author

There only should've been an error decomposing structs if the integration header isn't updated to flag the pointers as kind_pointer instead of kind_std_layout.

Can you elaborate on this @jbrodman ?

Why are we doing this?

This change was required because unused pointers in structs were causing an error in the backend when being passed as a direct kernel parameters (after decomposition fields are passed individually. Earlier they were passed inside the struct as a whole). According to the spec, this shouldn't throw an error.

I worry this is going to cause a performance regression

I can look into doing this only for user code where pointers are inside structs, instead of unconditionally doing it for all pointers. That way current USM functionality shouldn't be affected. I think.

At least in the past, pointers in structs tended to be treated as being generic and not global, and I know this was causing all sorts of problems. If we can make sure all the pointers remain global, even in structs, then that's probably ok?

At the moment, they are yes. Struct decomposition implemented this by 'accident'.

There seems to be other issues though. Bunch of USM tests are failing with 'invalid value'. Need to debug this.

@elizabethandrews elizabethandrews marked this pull request as draft August 10, 2020 15:42
@elizabethandrews elizabethandrews changed the title [SYCL] Wrap all pointers inside a generated struct [WIP][SYCL] Wrap all pointers inside a generated struct Aug 10, 2020
@bader
Copy link
Contributor

bader commented Aug 10, 2020

I guess the problem with such pointers and that there are buffers, which runtime can set as kernel arguments, so the solution is to handle them as USM pointers. I expect that runtime just sets host-side pointer value as kernel argument in this case and it should work w/o wrapping the pointer into a struct. Right?

Another option - pointer value can be passed in unsigned integer with size == sizeof (pointer type), but it also prevents compiler memory analysis, so it's better to preserve the type.

@elizabethandrews
Copy link
Contributor Author

I guess the problem with such pointers and that there are buffers, which runtime can set as kernel arguments, so the solution is to handle them as USM pointers. I expect that runtime just sets host-side pointer value as kernel argument in this case and it should work w/o wrapping the pointer into a struct. Right?

Right now those are wrapped in structures as well. From a front-end perspective I don't think we can tell which a USM pointer is vs an invalid pointer, so everything is getting wrapped to avoid the runtime error. I think we can avoid wrapping pointers which are directly captured. That should solve this I think.

@bader
Copy link
Contributor

bader commented Aug 11, 2020

I guess the problem with such pointers and that there are buffers, which runtime can set as kernel arguments, so the solution is to handle them as USM pointers. I expect that runtime just sets host-side pointer value as kernel argument in this case and it should work w/o wrapping the pointer into a struct. Right?

Right now those are wrapped in structures as well. From a front-end perspective I don't think we can tell which a USM pointer is vs an invalid pointer, so everything is getting wrapped to avoid the runtime error. I think we can avoid wrapping pointers which are directly captured. That should solve this I think.

What is "invalid pointer"? I think the compiler assumes that any pointer other than "accessor pointer" is a USM pointer.
If this approach is aligned with what we do USM pointers, I'm okay with it.
But the question is why do we handle USM pointers by wrapping them into a structure? This seems unnecessary. Runtime can pass USM pointer to a kernel directly via clSetKernelArgMemPointerINTEL function.

@elizabethandrews
Copy link
Contributor Author

I guess the problem with such pointers and that there are buffers, which runtime can set as kernel arguments, so the solution is to handle them as USM pointers. I expect that runtime just sets host-side pointer value as kernel argument in this case and it should work w/o wrapping the pointer into a struct. Right?

Right now those are wrapped in structures as well. From a front-end perspective I don't think we can tell which a USM pointer is vs an invalid pointer, so everything is getting wrapped to avoid the runtime error. I think we can avoid wrapping pointers which are directly captured. That should solve this I think.

What is "invalid pointer"? I think the compiler assumes that any pointer other than "accessor pointer" is a USM pointer.
If this approach is aligned with what we do USM pointers, I'm okay with it.
But the question is why do we handle USM pointers by wrapping them into a structure? This seems unnecessary. Runtime can pass USM pointer to a kernel directly via clSetKernelArgMemPointerINTEL function.

Because in the frontend we cannot distinguish between USM and non-USM pointer (invalid pointer). And unused non-USM pointers inside structs which are now being passed directly as kernel argument (after struct decomposition) is causing an error in backend when it should not(Spec allows pointers inside structs).

The proposed solution for this problem was to just wrap all pointers inside a struct and pass them. Upon thinking of this more, I think this is incorrect. We still need to throw an error if an invalid pointer is directly captured. So I'm modifying this patch to not wrap these.

Only pointer fields i.e. fields of struct type kernel arguments
are wrapped. Patch also reverts a few test changes I made in
previous commit, and adds a test for integration header
generation.

Signed-off-by: Elizabeth Andrews <elizabeth.andrews@intel.com>
Signed-off-by: Elizabeth Andrews <elizabeth.andrews@intel.com>
@elizabethandrews elizabethandrews marked this pull request as ready for review August 13, 2020 13:33
@elizabethandrews elizabethandrews changed the title [WIP][SYCL] Wrap all pointers inside a generated struct [SYCL] Wrap all pointers inside a generated struct Aug 13, 2020
Copy link
Contributor

@Fznamznon Fznamznon left a comment

Choose a reason for hiding this comment

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

@elizabethandrews
Copy link
Contributor Author

elizabethandrews commented Aug 13, 2020

This one https://github.com/intel/llvm/blob/sycl/clang/test/CodeGenSYCL/pointers-in-structs.cpp will likely fail after your change.

Yes it did. I modified the test in this commit.

EDIT: 928a7ff

Signed-off-by: Elizabeth Andrews <elizabeth.andrews@intel.com>
1. Correct a comment.
2. Renamed test and wrote description for it.
3. Modified test to use headers and changed RUN line.
Copy link
Contributor

@Fznamznon Fznamznon left a comment

Choose a reason for hiding this comment

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

FE changes LGTM, thanks

@elizabethandrews
Copy link
Contributor Author

@intel/llvm-reviewers-runtime This PR has a runtime test. Could you please review?

@elizabethandrews elizabethandrews changed the title [SYCL] Wrap all pointers inside a generated struct [SYCL] Wrap pointer fields inside a generated struct Aug 13, 2020
Copy link
Contributor

@s-kanaev s-kanaev left a comment

Choose a reason for hiding this comment

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

The test looks good.

@bader bader merged commit 926eb32 into intel:sycl Aug 14, 2020
jsji pushed a commit that referenced this pull request Feb 1, 2024
…2288)

The translator failed assertion with V->user_empty() during regularize function when shl i1 or lshr i1 result is used. E.g.

%2 = shl i1 %0 %1
store %2, ptr addrspace(1) @G.1, align 1

Instruction shl i1 is converted to lshr i32 which arithmetic have the same behavior.

Original commit:
KhronosGroup/SPIRV-LLVM-Translator@239fbd4
Chenyang-L pushed a commit that referenced this pull request Feb 18, 2025
[NFC] Update documentation to detail match files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants