-
Notifications
You must be signed in to change notification settings - Fork 798
[SYCL] Enforce constraints from sycl_ext_oneapi_reduction_properties
#16238
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,20 @@ struct initialize_to_identity_key | |
| }; | ||
| inline constexpr initialize_to_identity_key::value_t initialize_to_identity; | ||
|
|
||
| namespace detail { | ||
| struct reduction_property_check_anchor {}; | ||
| } // namespace detail | ||
|
|
||
| template <> | ||
| struct is_property_key_of<deterministic_key, | ||
| detail::reduction_property_check_anchor> | ||
| : std::true_type {}; | ||
|
|
||
| template <> | ||
| struct is_property_key_of<initialize_to_identity_key, | ||
| detail::reduction_property_check_anchor> | ||
| : std::true_type {}; | ||
|
|
||
| } // namespace experimental | ||
| } // namespace oneapi | ||
| } // namespace ext | ||
|
|
@@ -83,9 +97,17 @@ template <typename BinaryOperation> | |
| struct IsDeterministicOperator<DeterministicOperatorWrapper<BinaryOperation>> | ||
| : std::true_type {}; | ||
|
|
||
| template <typename PropertyList> | ||
| inline constexpr bool is_valid_reduction_prop_list = | ||
| ext::oneapi::experimental::detail::all_are_properties_of_v< | ||
| ext::oneapi::experimental::detail::reduction_property_check_anchor, | ||
| PropertyList>; | ||
|
|
||
| } // namespace detail | ||
|
|
||
| template <typename BufferT, typename BinaryOperation, typename PropertyList> | ||
| template <typename BufferT, typename BinaryOperation, typename PropertyList, | ||
| typename = std::enable_if_t< | ||
| detail::is_valid_reduction_prop_list<PropertyList>>> | ||
| auto reduction(BufferT vars, handler &cgh, BinaryOperation combiner, | ||
| PropertyList properties) { | ||
|
||
| detail::CheckReductionIdentity<typename BufferT::value_type, BinaryOperation>( | ||
|
|
@@ -95,16 +117,20 @@ auto reduction(BufferT vars, handler &cgh, BinaryOperation combiner, | |
| return reduction(vars, cgh, WrappedOp, RuntimeProps); | ||
| } | ||
|
|
||
| template <typename T, typename BinaryOperation, typename PropertyList> | ||
| template <typename T, typename BinaryOperation, typename PropertyList, | ||
| typename = std::enable_if_t< | ||
| detail::is_valid_reduction_prop_list<PropertyList>>> | ||
| auto reduction(T *var, BinaryOperation combiner, PropertyList properties) { | ||
| detail::CheckReductionIdentity<T, BinaryOperation>(properties); | ||
| auto WrappedOp = detail::WrapOp(combiner, properties); | ||
| auto RuntimeProps = detail::GetReductionPropertyList(properties); | ||
| return reduction(var, WrappedOp, RuntimeProps); | ||
| } | ||
|
|
||
| template <typename T, size_t Extent, typename BinaryOperation, | ||
| typename PropertyList> | ||
| template < | ||
| typename T, size_t Extent, typename BinaryOperation, typename PropertyList, | ||
| typename = | ||
| std::enable_if_t<detail::is_valid_reduction_prop_list<PropertyList>>> | ||
| auto reduction(span<T, Extent> vars, BinaryOperation combiner, | ||
| PropertyList properties) { | ||
| detail::CheckReductionIdentity<T, BinaryOperation>(properties); | ||
|
|
@@ -113,7 +139,9 @@ auto reduction(span<T, Extent> vars, BinaryOperation combiner, | |
| return reduction(vars, WrappedOp, RuntimeProps); | ||
| } | ||
|
|
||
| template <typename BufferT, typename BinaryOperation, typename PropertyList> | ||
| template <typename BufferT, typename BinaryOperation, typename PropertyList, | ||
| typename = std::enable_if_t< | ||
| detail::is_valid_reduction_prop_list<PropertyList>>> | ||
| auto reduction(BufferT vars, handler &cgh, | ||
| const typename BufferT::value_type &identity, | ||
| BinaryOperation combiner, PropertyList properties) { | ||
|
|
@@ -122,16 +150,20 @@ auto reduction(BufferT vars, handler &cgh, | |
| return reduction(vars, cgh, identity, WrappedOp, RuntimeProps); | ||
| } | ||
|
|
||
| template <typename T, typename BinaryOperation, typename PropertyList> | ||
| template <typename T, typename BinaryOperation, typename PropertyList, | ||
| typename = std::enable_if_t< | ||
| detail::is_valid_reduction_prop_list<PropertyList>>> | ||
| auto reduction(T *var, const T &identity, BinaryOperation combiner, | ||
| PropertyList properties) { | ||
| auto WrappedOp = detail::WrapOp(combiner, properties); | ||
| auto RuntimeProps = detail::GetReductionPropertyList(properties); | ||
| return reduction(var, identity, WrappedOp, RuntimeProps); | ||
| } | ||
|
|
||
| template <typename T, size_t Extent, typename BinaryOperation, | ||
| typename PropertyList> | ||
| template < | ||
| typename T, size_t Extent, typename BinaryOperation, typename PropertyList, | ||
| typename = | ||
| std::enable_if_t<detail::is_valid_reduction_prop_list<PropertyList>>> | ||
| auto reduction(span<T, Extent> vars, const T &identity, | ||
| BinaryOperation combiner, PropertyList properties) { | ||
| auto WrappedOp = detail::WrapOp(combiner, properties); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple -fsyntax-only -Xclang -verify -Xclang -verify-ignore-unexpected=note %s | ||
|
|
||
| #include <sycl/sycl.hpp> | ||
|
|
||
| int main() { | ||
| int *r = nullptr; | ||
| // Must not use `sycl_ext_oneapi_reduction_properties`'s overloads: | ||
| std::ignore = | ||
| sycl::reduction(r, sycl::plus<int>{}, | ||
| sycl::property::reduction::initialize_to_identity{}); | ||
|
|
||
| namespace sycl_exp = sycl::ext::oneapi::experimental; | ||
| std::ignore = | ||
| sycl::reduction(r, sycl::plus<int>{}, | ||
| sycl_exp::properties(sycl_exp::initialize_to_identity)); | ||
|
Comment on lines
+13
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we augment the test for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? The change here is about "negative" behavior, this particular line acts less like a test/check and more like "context"/"back-reference". Positive behavior tests are expected to have been added in the original PR introducing these properties.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. Looks good then. Also, please update the PR description to summarize the changes in this PR.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think title already says it all. What else do you want to see there?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not clear from the title what "Constraints"/"Negative behavior" you are referring to.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's probably language barrier :) In the absence of "some" I'd just assume "all" by default :)
You haven't answered my question:
I can't imagine the answer for that would be copy-pasting them all into this PR. And if we agree that we wouldn't do that for such more complex case, I can't see how a simpler one deserves a longer PR description.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, if the constraints were to be slightly different, we could still try to summarize them, without being overly specific.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That would be "implement constraints from ", wouldn't it?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Depends on how different the constraints are. If they are just "slightly" different, I would have come up with a better commit message than "implement constraints". If they are very different, to the extent of being unrelated, I would have tried to break PRs into smaller ones. For the record, this is not a blocker from me since changes in SYCL RT looks good. So, if you feel strongly about not adding a commit message, please feel free to proceed with the merge.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I feel strongly about [NOT] quoting the spec :) |
||
|
|
||
| // Not a property list: | ||
| // expected-error@+2 {{no matching function for call to 'reduction'}} | ||
| std::ignore = | ||
| sycl::reduction(r, sycl::plus<int>{}, sycl_exp::initialize_to_identity); | ||
|
|
||
| // Not a reduction property: | ||
| // expected-error@+2 {{no matching function for call to 'reduction'}} | ||
| std::ignore = | ||
| sycl::reduction(r, sycl::plus<int>{}, | ||
| sycl_exp::properties(sycl_exp::initialize_to_identity, | ||
| sycl_exp::full_group)); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little worried by this pattern, because it adds a template argument that is not present in the specification. I know this works, but I don't if it's legal from a SYCL specification perspective.
I'd like to hear @gmlueck's opinion about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, it seems like you could avoid adding a new template parameter by instead adding
enable_ifto thehandlerparameter.In general, implementing constraints seems a bit hacky in C++17 because you need to use SFINAE, and this sometimes requires adding a new template parameter or even changing a non-templated member function into a templated one. I don't know if we have a strict set of rules about whether this is completely legal. We know that changing a conversion operator from non-templated to templated changes the semantics, so we don't do this. (@aelovikov-intel has used a "mix-in" technique to avoid this for the
vecclass.) For the other cases, I'd advise that we avoid adding new template parameters if there is an alternative.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember seeing such an approach anywhere in the codebase, do you have some pointers?
I thought we're adding that extra parameter almost in every corner of the project right now...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you do something like this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That isn't the question. What I'm saying is that I'm not aware of any place in the codebase where we use such pattern instead of extra template parameter. Why should we do anything differently here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's just general concern that adding another template parameter might cause some code to break.
@Pennycook, @rolandschulz: Didn't we see some previous example where adding a new template parameter to
accessorcaused something to break, even though the template parameter had a default value? Maybe I'm misremembering?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, there are quite a few places (e.g., in the group algorithms) where we use
enable_ifon the return type instead of using an extra template parameter:llvm/sycl/include/sycl/ext/oneapi/experimental/user_defined_reductions.hpp
Lines 46 to 49 in e9cbb87
I think you're referring to this issue: #10057. Even if the same problem can't actually be triggered for this overload, the fact we hit this unexpected issue with
accessordoes make me think that it would be safer to avoid adding extra templates just in case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @aelovikov-intel, this is not an uncommon pattern for SFINAE on public APIs. If we believe this to be problematic, we may have to address this in other places as well, but the solution of SFINAE'ing an argument assumes that the methods have arguments, which holds in this case, but may not hold for all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be wrong about this, but if we wanted to be consistent, couldn't we always implement SFINAE using the return type? Every function has a return type (but sometimes that type is
void).I appreciate it's a bit ugly to employ in a case like this where the implementation was previously relying on a deduced return type, but I think it's doable. Is there a reason that we couldn't implement this as something like:
As an added bonus, all the SFINAE would be in a place where we could legally replace it with a
requiresstatement when we switch to C++20:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally prefer SFINAE on return type, with the exceptions like this one when it's deduced :) ...and ctors.