On Wed, 30 Apr 2025 at 09:55, Luc Grosheintz <luc.groshei...@gmail.com> wrote:
> On 4/30/25 4:37 AM, Tomasz Kaminski wrote:
> > On Tue, Apr 29, 2025 at 11:52 PM Jonathan Wakely <jwak...@redhat.com> wrote:
> >> On Tue, 29 Apr 2025 at 14:55, Tomasz Kaminski <tkami...@redhat.com> wrote:
> >>> On Tue, Apr 29, 2025 at 2:55 PM Luc Grosheintz <luc.groshei...@gmail.com>
> >> wrote:
> >>>> +      template<typename _OIndexType, size_t... _OExtents>
> >>>> +       friend constexpr bool
> >>>> +       operator==(const extents& __self,
> >>>> +                  const extents<_OIndexType, _OExtents...>& __other)
> >> noexcept
> >>>> +       {
> >>>> +         if constexpr (!_S_is_compatible_extents<_OExtents...>())
> >>>> +           return false;
> >>>
> >>> We can add here:
> >>>                 // N.B. if extents are compatible and static, it implies
> >> that they are equal
> >>>                 else if constexpr ((_Extents != dynamic_extent) && ...)
> >>>                     return true;
> >>
> >> Can't we have a case where _OExtents==dynamic_extent and
> >> _Extents!=dynamic_extents, which would mean they're compatible, but
> >> not equal?
> >>
> > Ah, indeed the conditions needs to be:
> > (_Extents != dynamic_extent && _Extents == _OExtents) && ...
> >
>
> The reason I didn't follow through with this is because when looking
> at the generated code I saw no advantage of the additional constexpr
> branch. Therefore, I preferred the simplicity of Tomasz first proposal,
> i.e. the current implementation.

Thanks for checking it. So the runtime 'for' loop is able to optimize
to a constant, because the .extent(i) calls are constants in the cases
that the constexpr-if branch would handle.

> Would you still like me to add the additional `else if constexpr`
> branch?

No, we can always add it later if it turns out to help (e.g. when
sizeof...(_Extents) is large, if the optimizer gives up on the 'for'
loop).


> Here's the output of objdump when compiling with `-O2`:
>
> bool same1(const std::extents<int, 1, 2, 3>& e1,
>             const std::extents<int, 1, 2, 3>& e2)
> { return e1 == e2; }
>     0:  b8 01 00 00 00          mov    $0x1,%eax
>     5:  c3                      ret
>
> bool same2(const std::extents<int, 2, 2, 3>& e1,
>             const std::extents<int, 1, 2, 3>& e2)
> { return e1 == e2; }
>     0:  31 c0                   xor    %eax,%eax
>     2:  c3                      ret
>
> bool same3(const std::extents<int, 2, dyn, 3>& e1,
>             const std::extents<int, 1, 2, 3>& e2)
> { return e1 == e2; }
>     0:  31 c0                   xor    %eax,%eax
>     2:  c3                      ret
>
> bool same4(const std::extents<int, 2, dyn, 3>& e1,
>             const std::extents<int, dyn, 2, 4>& e2)
> { return e1 == e2; }
>     0:  31 c0                   xor    %eax,%eax
>     2:  c3                      ret
>
> bool same5(const std::extents<int, dyn>& e1,
>             const std::extents<int, dyn, dyn>& e2)
> { return e1 == e2; }
>     0:  31 c0                   xor    %eax,%eax
>     2:  c3                      ret
>
>
> >>
> >>
> >>>>
> >>>> +         else
> >>>> +           {
> >>>> +             for (size_t __i = 0; __i < __self.rank(); ++__i)
> >>>> +               if (!cmp_equal(__self.extent(__i), __other.extent(__i)))
> >>>> +                 return false;
> >>>> +             return true;
> >>>> +           }
> >>>> +       }
> >>>> +
> >>>> +    private:
> >>>> +      using _S_storage = __mdspan::_ExtentsStorage<
> >>>> +       _IndexType, array<size_t, sizeof...(_Extents)>{_Extents...}>;
> >>>> +      [[no_unique_address]] _S_storage _M_dynamic_extents;
> >>>> +
> >>>> +      template<typename _OIndexType, size_t... _OExtents>
> >>>> +       friend class extents;
> >>>> +    };
> >>>> +
> >>>> +  namespace __mdspan
> >>>> +  {
> >>>> +    template<typename _IndexType, size_t... _Counts>
> >>>> +      auto __build_dextents_type(integer_sequence<size_t, _Counts...>)
> >>>> +       -> extents<_IndexType, ((void) _Counts, dynamic_extent)...>;
> >>>> +
> >>>> +    template<typename _Tp>
> >>>> +      consteval size_t
> >>>> +      __dynamic_extent() { return dynamic_extent; }
> >>>> +  }
> >>>> +
> >>>> +  template<typename _IndexType, size_t _Rank>
> >>>> +    using dextents =
> >> decltype(__mdspan::__build_dextents_type<_IndexType>(
> >>>> +       make_index_sequence<_Rank>()));
> >>>> +
> >>>> +  template<typename... _Integrals>
> >>>> +    requires (is_convertible_v<_Integrals, size_t> && ...)
> >>>> +    explicit extents(_Integrals...) ->
> >>>> +      extents<size_t, __mdspan::__dynamic_extent<_Integrals>()...>;
> >>>>
> >>>>   _GLIBCXX_END_NAMESPACE_VERSION
> >>>>   }
> >>>> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
> >> std.cc.in
> >>>> index 930a489ff44..0df27cd7e7d 100644
> >>>> --- a/libstdc++-v3/src/c++23/std.cc.in
> >>>> +++ b/libstdc++-v3/src/c++23/std.cc.in
> >>>> @@ -1833,7 +1833,11 @@ export namespace std
> >>>>     }
> >>>>   }
> >>>>
> >>>> -// FIXME <mdspan>
> >>>> +// <mdspan>
> >>>> +{
> >>>> +  using std::extents;
> >>>> +  // FIXME layout_*, default_accessor and mdspan
> >>>> +}
> >>>>
> >>>>   // 20.2 <memory>
> >>>>   export namespace std
> >>>> --
> >>>> 2.49.0
> >>>>
> >>
> >>
> >
>

Reply via email to