Skip to content
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

Move self_logand to the logical_and callable #1959

Merged
merged 52 commits into from
Jan 15, 2025
Merged

Move self_logand to the logical_and callable #1959

merged 52 commits into from
Jan 15, 2025

Conversation

SadiinsoSnowfall
Copy link
Collaborator

No description provided.

@SadiinsoSnowfall SadiinsoSnowfall marked this pull request as draft September 11, 2024 17:27
@SadiinsoSnowfall SadiinsoSnowfall force-pushed the callable-logand branch 4 times, most recently from af4d016 to b0012c1 Compare September 19, 2024 08:28
@SadiinsoSnowfall SadiinsoSnowfall marked this pull request as ready for review September 26, 2024 14:56
@SadiinsoSnowfall
Copy link
Collaborator Author

While attempting to fix this callable for the "no simd" configurations, I encountered the following problem:
Say we want to compile the following function:

auto test(logical<short> a, logical<wide<int>> b) {
    return logical_and(a, b);
}

Because of the rules defined by common_logical_t, this should return a logical<wide<int>>, as logical<wide> always takes priority. However, in "no simd" mode, this isn't the case and the logical_and callable attempts to return a logical<wide<short>>, failing to compile. This is because in this mode, the logical_and function is mapped over each element of the b wide value. In doing so, it is passed two logical_scalar_values of type logical<short> and logical<int> at every step. The problem being that in this case, the logical<short> type takes precedence.

The current fix involves using a convert operation before returning the result from the callable. This is a no-op in "simd" mode as the computed type and the returned type do match. A better fix to this problem would be to directly pass the computed type, in this case common_logical_t<T, U> to the map function called in the callable dispatch implementations.

@jfalcou
Copy link
Owner

jfalcou commented Sep 30, 2024

A better fix to this problem would be to directly pass the computed type, in this case common_logical_t<T, U> to the map function called in the callable dispatch implementations.

This would also improve compile time by not having a convoluted map result computation.
@DenisYaroshevskiy maybe I should fix map outside of this PR then we can update this one and so we don't have the convert laying around.

@SadiinsoSnowfall SadiinsoSnowfall marked this pull request as draft October 16, 2024 10:01
@SadiinsoSnowfall
Copy link
Collaborator Author

waiting on the map2 branch

@SadiinsoSnowfall
Copy link
Collaborator Author

After some talking we decided to change the behaviour of common_logical such that the returned type is always a logical of the type of the first of its parameter. This should be mirroring what is done for bitwise callables and fix the problem we were encountering with the callables using common_logical.

This is a breaking change.

@SadiinsoSnowfall SadiinsoSnowfall force-pushed the callable-logand branch 2 times, most recently from 73fbf38 to 6090b71 Compare November 29, 2024 11:08
@SadiinsoSnowfall SadiinsoSnowfall marked this pull request as ready for review December 2, 2024 18:58
@jfalcou
Copy link
Owner

jfalcou commented Dec 5, 2024

@DenisYaroshevskiy looks like it works. Your opinion is welcome so we can move forward.

Copy link
Collaborator

@DenisYaroshevskiy DenisYaroshevskiy left a comment

Choose a reason for hiding this comment

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

I don't like that there is a mess with conversions.
We do two or more conversions per operation.

I would like the logical callable to handle that.

I am ok if that happens with the "smaller logical result type"

include/eve/arch/cpu/logical_wide.hpp Outdated Show resolved Hide resolved
include/eve/module/core/regular/logical_and.hpp Outdated Show resolved Hide resolved
@SadiinsoSnowfall
Copy link
Collaborator Author

Note: this PR now also changes the following callables: logical_notand, logical_andnot, logical_or, logical_notor, logical_ornot, logical_xor as they have a dependency on the return type of logical_and and had hardcoded return type in their generic impl.

Also:

  • changed test.hpp to add cartesian and cartesian_square, previously used in both logical_and and common_value unit tests.
  • hotfixed operator< in kumi, this fix from Joel will get into kumi mainline.
  • fixed friends functions to work with the new common_logical behaviour (they will be cleaned up in futures PRs).
  • changed the logical_xxx unit tests to perform some more cross-type tests and ensure that the new common_logical behaviour is used.
  • fixed hz_device to make it work with the new common_logical behaviour.

The new behaviour is the following: given two types A and B, both either bool or logical_value, the type returned from common_logical will be:

  • bool if both A and B are bools
  • A if B is a bool (or vice versa)
  • A if it is a simd_value and B isn't.
  • B if it is a simd_value and A isn't.
  • If both A and B are simd_values or scalar_values, the return type will be:
    • The smallest of the two
    • The "most unsigned" one
    • The "most integral" one
    • The first one (in the case both have the same size, signedness and integral-ness)

Copy link
Collaborator

@DenisYaroshevskiy DenisYaroshevskiy left a comment

Choose a reason for hiding this comment

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

quick review

include/eve/arch/cpu/logical.hpp Show resolved Hide resolved
include/eve/arch/cpu/logical_wide.hpp Outdated Show resolved Hide resolved
include/eve/concept/value.hpp Outdated Show resolved Hide resolved
include/eve/deps/kumi/tuple.hpp Outdated Show resolved Hide resolved
test/test.hpp Show resolved Hide resolved
}

template<typename T, typename U>
using priority_t = eve::as_logical_t<decltype(find_priority_type<T, U>())>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm iffy about this:

It seems like you rewrote a chunk of common_logical a second time and compare them.

Is this a fare description?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought that comparing common_logical_t to itself would not be that useful. But without knowing exactly what types we are currently processing, it is difficult to know what should be the output of common_logical_t. If you want I can try to discriminate between floats, signed integrals and unsigned integrals where applicable to make the desired output more clear.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like there is only two usages of priority_t.

Just be explicit about the things you test, don't try to write a function.

Writing a function to validate input is a good idea only if you can somehow write that function in a much easier way than the original.

Copy link
Owner

Choose a reason for hiding this comment

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

those two usage are actually used in the test doign a cartesian product of tests over all combo of scalar.
This is not something we can verify easily without missing stuff.

test/unit/module/core/logical_ops.hpp Outdated Show resolved Hide resolved
include/eve/traits/overload/default_behaviors.hpp Outdated Show resolved Hide resolved
include/eve/module/core/regular/is_equal.hpp Outdated Show resolved Hide resolved
{ return EVE_DISPATCH_CALL(a, b); }
template<typename T, typename U>
constexpr EVE_FORCEINLINE common_logical_t<T, U> operator()(T a, U b) const noexcept
requires (same_lanes_or_scalar<T, U> && !arithmetic_simd_value<T> && !arithmetic_simd_value<U>)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a very weird requirement. Can't you just require relaxed_logical_value on T and U?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

See the comment in the file.

Copy link
Collaborator

Choose a reason for hiding this comment

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

what comment.
Like - ok - this is an unacceptable requirement for an interface. We need something reasonable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This has been updated following our decision to disallow passing non-logical scalar values as parameters to logical_xxx callables.

if constexpr( scalar_value<T> && scalar_value<U> ) return r_t(a && b);
else return a && b;
if constexpr (relaxed_logical_scalar_value<T>) return a && b;
else return bit_cast(a.bits() & b.bits(), as<T>{});
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand this else. You are in cpu right?

Is this like what - logical_value && is_wide_logical? Can you be more explicit about that, this is very confusing.

At least a comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added a comment to clarify.

{ return EVE_DISPATCH_CALL(a, b); }
template<typename T, typename U>
constexpr EVE_FORCEINLINE common_logical_t<T, U> operator()(T a, U b) const noexcept
requires (same_lanes_or_scalar<T, U> && !arithmetic_simd_value<T> && !arithmetic_simd_value<U>)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment here about the requirements.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I updated the answer to the review.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is still not ok for an interface.

include/eve/concept/scalar.hpp Outdated Show resolved Hide resolved
}
(std::make_index_sequence<sizeof...(Ts)-1>());
return res;
(std::make_index_sequence<sizeof...(Us)-1>());
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's with this kumi change? This should probably go in upstream first, no? Or do we need to upgrade in lockstep?

Copy link
Owner

Choose a reason for hiding this comment

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

upstreal is fixed already as externals CI pass.
This is probably some noise and should be turned back to Ts.

include/eve/detail/hz_device.hpp Show resolved Hide resolved
{ return EVE_DISPATCH_CALL(a, b); }
template<typename T, typename U>
constexpr EVE_FORCEINLINE common_logical_t<T, U> operator()(T a, U b) const noexcept
requires (same_lanes_or_scalar<T, U> && !arithmetic_simd_value<T> && !arithmetic_simd_value<U>)
Copy link
Collaborator

Choose a reason for hiding this comment

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

what comment.
Like - ok - this is an unacceptable requirement for an interface. We need something reasonable.

{ return EVE_DISPATCH_CALL(a, b); }
template<typename T, typename U>
constexpr EVE_FORCEINLINE common_logical_t<T, U> operator()(T a, U b) const noexcept
requires (same_lanes_or_scalar<T, U> && !arithmetic_simd_value<T> && !arithmetic_simd_value<U>)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is still not ok for an interface.

include/eve/module/core/regular/is_less.hpp Outdated Show resolved Hide resolved
include/eve/traits/is_logical.hpp Outdated Show resolved Hide resolved
include/eve/concept/scalar.hpp Outdated Show resolved Hide resolved
include/eve/concept/value.hpp Outdated Show resolved Hide resolved
auto const order = [&]<typename Index>(Index i)
{
auto y_less_x_prev = rhs[i] < lhs[i];
auto x_less_y = lhs[index_t<Index::value+1>{}] < rhs[index_t<Index::value+1>{}];
res = res || (x_less_y && !y_less_x_prev);
return (x_less_y && !y_less_x_prev);
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's with the kumi changes.

Copy link
Owner

Choose a reason for hiding this comment

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

these are for doign a proper fold instead of the bad code from before. This is coming from KUMI head

Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be nice as a separate change, why is this here? (don't bother now)

include/eve/module/core/regular/logical_and.hpp Outdated Show resolved Hide resolved
include/eve/traits/common_value.hpp Outdated Show resolved Hide resolved
include/eve/traits/overload/default_behaviors.hpp Outdated Show resolved Hide resolved
test/unit/module/core/logical_test.hpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@DenisYaroshevskiy DenisYaroshevskiy left a comment

Choose a reason for hiding this comment

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

Almost there!

auto const order = [&]<typename Index>(Index i)
{
auto y_less_x_prev = rhs[i] < lhs[i];
auto x_less_y = lhs[index_t<Index::value+1>{}] < rhs[index_t<Index::value+1>{}];
res = res || (x_less_y && !y_less_x_prev);
return (x_less_y && !y_less_x_prev);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be nice as a separate change, why is this here? (don't bother now)

include/eve/module/core/regular/impl/logical_andnot.hpp Outdated Show resolved Hide resolved
TTS_CASE("eve::common_logical on boolean x boolean")
{
TTS_TYPE_IS((eve::common_logical_t<bool, bool>), bool);
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit:

common_logical_t is always simmetrical. Extract a function:

template <typename L, typename R, typename Res>
void common_logical_res_test()

And use that one. This way you halve the number of test cases you need to write.

You can also put all "logical/non-logical" common logical stuff in there.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This should be fixed, let me know

test/unit/meta/traits/common_logical.cpp Outdated Show resolved Hide resolved
test/unit/module/core/logical_test.hpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@DenisYaroshevskiy DenisYaroshevskiy left a comment

Choose a reason for hiding this comment

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

Please address that french file

include/eve/module/core/detail/generic/ex.toml Outdated Show resolved Hide resolved
@jfalcou jfalcou merged commit 0e06d4d into main Jan 15, 2025
32 checks passed
@jfalcou jfalcou deleted the callable-logand branch January 15, 2025 16:51
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.

3 participants