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

Consider implicit conversions to fixed_size #26

Open
mattkretz opened this Issue Feb 6, 2017 · 3 comments

Comments

Projects
None yet
2 participants
@mattkretz
Copy link
Owner

mattkretz commented Feb 6, 2017

This is an extract from our discussion in #10:

Btw, I'm beginning to slightly warm up to the idea to have the "to_fixed" conversion as an implicit one, similar to the situation that the conversion of double to the more general complex is implicit.

If we have an implicit conversion from datapar<T, Abi> to datapar<T, fixed_size>, I think we support both of your user groups. People using abi_for_size already get different types on SSE vs. AVX and thus are non-portable on the type.

@jensmaurer: Is your conclusion, that I should (re)add this to the paper? I'm not sure where you stand.

IMHO any conversion involving fixed_size, equal size(), and non-narrowing element type conversion can be implicit. The downside, that one might produce less efficient code through implicitly requiring more SIMD registers is mitigated by the final assignment, since it's typically a narrowing conversion if widening happened.
The unportable argument is, by intent of fixed_size, not very important. (Since fixed_size is the "larger footgun" wrt. ABI differences.)

@mattkretz mattkretz added the P0214 label Feb 6, 2017

@mattkretz

This comment has been minimized.

Copy link
Owner

mattkretz commented Feb 6, 2017

Oh and note that the current paper has the following implicit datapar ctor:

template <class U> datapar(const datapar<U, datapar_abi::fixed_size<size()>> &x);
Remarks: This constructor shall not participate in overload resolution unless
• abi_type equals datapar_abi::fixed_size<size()>,
• and every possible value of U can be represented with type value_type,
• and, if both U and value_type are integral, the integer conversion rank [N4618, (4.15)] of value_type is greater than the integer conversion rank of U.
Effects: Constructs an object where the i-th element equals static_cast<T>(x[i]) for all i ∈ [0,
size()).

Edit: fixed long ambiguity.

mattkretz added a commit that referenced this issue Feb 6, 2017

Fix long ambiguity in conversion ctor
Refs: gh-26

Signed-off-by: Matthias Kretz <kretz@kde.org>

@mattkretz mattkretz added this to Awaiting Feedback in P0214R3 Feb 6, 2017

@jensmaurer

This comment has been minimized.

Copy link
Collaborator

jensmaurer commented Feb 6, 2017

Is your conclusion, that I should (re)add this to the paper? I'm not sure where you stand.

Neither am I.

std::datapar<int, 4> x = std::datapar(17); // on SSE

is as non-portable as it can get, but converting to std::datapar<int, N> (where N is deduced / defined by ::size()) actually works very well, and is a natural interface for things such as frexp or intpow.

Regarding the imlicit constructor: That effectively does a widening conversion within the same fixed_size<N>, right? Which is somewhat dangerous in that it doubles (or so) the number of SIMD registers required, but fixed_size<N> is already abstracted away from number of SIMD registers, so I think that's ok.

The downside, that one might produce less efficient code through implicitly requiring more SIMD registers is mitigated by the final assignment, since it's typically a narrowing conversion if widening happened.

I don't understand that. Herb wants people to use "auto" all over the place.

@mattkretz mattkretz added this to Awaiting Feedback in P0214R4 Feb 6, 2017

@mattkretz mattkretz removed this from Awaiting Feedback in P0214R3 Feb 6, 2017

@mattkretz mattkretz added the undecided label Feb 7, 2017

@mattkretz

This comment has been minimized.

Copy link
Owner

mattkretz commented Feb 7, 2017

std::fixed_size_datapar<int, 4> x = std::datapar(17); // on SSE
is as non-portable as it can get, but converting to std::datapar<int, N> (where N is deduced / defined by ::size()) actually works very well, and is a natural interface for things such as frexp or intpow.

So you don't know how to weigh the two issues? Convenience against allowing some non-portable conversions? It really depends a lot on how the users sets up his types. If he creates a list of aliases and exclusively works with those. He's going to be happy about the convenience. And I believe, this is the user we want to support.

Just a thought how to guide our users to do the right thing:

template <typename T, typename MainType> using best_abi_datapar =
  datapar<T, conditional_t<is_same<T, MainType>::value,
                           datapar_abi::native<T>,
                           datapar_abi::fixed_size<T,
                             datapar_size_v<MainType, datapar_abi::native<MainType>>>>>;

// now users can do:
template <class T> datapar = std::best_abi_datapar<T, float>;

If we allow more implicit conversions (non-narrowing on the element type) then abi_for_size instead of fixed_size might be even nicer. (Because of the difference in function argument passing - register vs. stack - that many implementations will likely use.)

Regarding the imlicit constructor: That effectively does a widening conversion within the same fixed_size<N>, right? Which is somewhat dangerous in that it doubles (or so) the number of SIMD registers required, but fixed_size<N> is already abstracted away from number of SIMD registers, so I think that's ok.

Yes. That's what I meant with:

The downside, that one might produce less efficient code through implicitly requiring more SIMD registers is mitigated by the final assignment, since it's typically a narrowing conversion if widening happened.

I don't understand that. Herb wants people to use "auto" all over the place.

But there has to be some final conversion to a well-defined type. E.g.

datapar<int> foo(datapar<int> x, datapar<double, fixed_size<datapar<int>::size()>> y) {
  auto tmp = x * y;
  return tmp;
}

If foo returns auto then that result will be assigned somewhere to a non-auto variable. (Exception: the final use is a horizontal reduction or subscripting, in which case the implicit conversions of the builtin types hide the issue)

@mattkretz mattkretz referenced this issue Feb 7, 2017

Open

loosen mask implicit conversions rules #3

1 of 2 tasks complete

@mattkretz mattkretz removed this from Awaiting Feedback in P0214R4 Mar 8, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment