Bug 118879 - Missed optimisation: std::in_range simpler size check
Summary: Missed optimisation: std::in_range simpler size check
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: unknown
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: easyhack, missed-optimization
Depends on:
Blocks:
 
Reported: 2025-02-14 16:33 UTC by Thiago Macieira
Modified: 2026-03-14 05:09 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2025-02-14 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Thiago Macieira 2025-02-14 16:33:29 UTC
In:

#include <utility>

int f1(long long v)
{
    return std::in_range<int>(v) ? v : 0;
}

int f2(long long v)
{
    return v == int(v) ? v : 0;
}

GCC generates for the std::in_range version:
        movl    $2147483648, %edx
        movl    $4294967295, %ecx
        movq    %rdi, %rax
        addq    %rdi, %rdx
        cmpq    %rdx, %rcx
        movl    $0, %edx
        cmovb   %rdx, %rax

Whereas for the second function, which operates identically, it generates a sign-extension from the 32-bit component:
        movslq  %edi, %rax
        cmpq    %rdi, %rax
        movl    $0, %eax
        cmove   %edi, %eax

Clang generates that second for both functions (using the libstdc++ content). https://gcc.godbolt.org/z/nqfMKejoe
Comment 1 Jonathan Wakely 2025-02-14 16:59:24 UTC
The libstdc++ std::in_range does:

  template<typename _Res, typename _Tp>
    constexpr bool
    in_range(_Tp __t) noexcept
    {
      static_assert(__is_standard_integer<_Res>::value);
      static_assert(__is_standard_integer<_Tp>::value);
      using __gnu_cxx::__int_traits;

      if constexpr (is_signed_v<_Tp> == is_signed_v<_Res>)
	return __int_traits<_Res>::__min <= __t
	  && __t <= __int_traits<_Res>::__max;
      else if constexpr (is_signed_v<_Tp>)
	return __t >= 0
	  && make_unsigned_t<_Tp>(__t) <= __int_traits<_Res>::__max;
      else
	return __t <= make_unsigned_t<_Res>(__int_traits<_Res>::__max);
    }


In C++17 the result of int(v) was impl-defined, but since C++20 it's well-defined, and std::in_range was only added in C++20. So we could change the first branch to just return __t == _Ret(__t).
Comment 2 Drea Pinski 2025-02-14 18:58:01 UTC
```
int f0(long long v)
{
    unsigned long long t = v;
    t += 0x80'00'00'00ull;
    if (t <= 0xff'ff'ff'ff)
    return v;
    return 0;
}
```
Comment 3 Drea Pinski 2025-02-14 19:04:00 UTC
(In reply to Andrew Pinski from comment #2)
> ```
> int f0(long long v)
> {
>     unsigned long long t = v;
>     t += 0x80'00'00'00ull;
>     if (t <= 0xff'ff'ff'ff)
>     return v;
>     return 0;
> }
> ```


So something like:
```
(simplify
 (le (plus @0 INTEGER_CST@2) INTEGER_CST@3))
 (if (exact_log2 (@2) != -1 && @3 == mask(exact_log2 (@2)+1))
  (with {
   tree otype = TREE_TYPE (@0);
   tree inner_type = signed_int_type (exact_log2 (@2)+1);
   }
  (eq (convert:otype (convert:itype @0)) @0))
```

ISo mine.
Comment 4 Drea Pinski 2026-03-14 05:09:59 UTC
.