Re: [PATCH 1/2] profile-count: Handle signed values in profile_probability::apply

Kyrylo Tkachov <[email protected]> Tue, 4 Aug 2026 08:12:43 +0000
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Since this changes profile-count perhaps Honza is best placed to look at it=
?
Adding to CC.
Thanks,
Kyrill

> On 31 Jul 2026, at 14:49, Kyrylo Tkachov <[email protected]> wrote:
>=20
> Ping.
> Thanks,
> Kyrill
>=20
>> On 24 Jul 2026, at 14:27, Kyrylo Tkachov <[email protected]> wrote:
>>=20
>> Ping.
>> Thanks,
>> Kyrill
>>=20
>>> On 17 Jul 2026, at 15:25, Kyrylo Tkachov <[email protected]> wrote:
>>>=20
>>> From: Kyrylo Tkachov <[email protected]>
>>>=20
>>> profile_probability::apply uses RDIV, which adds half the denominator
>>> before division.  This does not round negative values correctly.
>>> Multiplying the signed input by the fixed-point probability can also
>>> overflow for large inputs.
>>>=20
>>> Scale the unsigned magnitude with safe_scale_64bit, then restore the si=
gn.
>>> Return the input directly for a unit probability so that the minimum
>>> gcov_type value remains representable.  Preserve the existing truncatio=
n
>>> for uninitialized probabilities.
>>>=20
>>> Add selftests for signed rounding, zero and unit probabilities,
>>> uninitialized probabilities, and the minimum and maximum gcov_type valu=
es.
>>>=20
>>> Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux.
>>> Ok for trunk?
>>>=20
>>> Thanks,
>>> Kyrill
>>>=20
>>> gcc/ChangeLog:
>>>=20
>>> * profile-count.h (profile_probability::apply): Scale an unsigned
>>> magnitude with safe_scale_64bit.
>>> * profile-count.cc (test_profile_probability_apply): New.
>>> (profile_count_cc_tests): New.
>>> * selftest.h (profile_count_cc_tests): Declare.
>>> * selftest-run-tests.cc (selftest::run_tests): Run it.
>>>=20
>>> Signed-off-by: Kyrylo Tkachov <[email protected]>
>>> ---
>>> gcc/profile-count.cc      | 47 +++++++++++++++++++++++++++++++++++++++
>>> gcc/profile-count.h       | 26 +++++++++++++++++++++-
>>> gcc/selftest-run-tests.cc |  1 +
>>> gcc/selftest.h            |  1 +
>>> 4 files changed, 74 insertions(+), 1 deletion(-)
>>>=20
>>> diff --git a/gcc/profile-count.cc b/gcc/profile-count.cc
>>> index 2cce9caa772..9be209efda2 100644
>>> --- a/gcc/profile-count.cc
>>> +++ b/gcc/profile-count.cc
>>> @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
>>> #include "wide-int.h"
>>> #include "sreal.h"
>>> #include "profile.h"
>>> +#include "selftest.h"
>>>=20
>>> /* Names from profile_quality enum values.  */
>>>=20
>>> @@ -595,3 +596,49 @@ profile_count::force_nonzero () const
>>>   }
>>> return ret;
>>> }
>>> +
>>> +#if CHECKING_P
>>> +
>>> +namespace selftest {
>>> +
>>> +/* Verify profile_probability::apply.  */
>>> +
>>> +static void
>>> +test_profile_probability_apply ()
>>> +{
>>> +  const gcov_type min =3D INTTYPE_MINIMUM (gcov_type);
>>> +  const gcov_type max =3D INTTYPE_MAXIMUM (gcov_type);
>>> +  profile_probability quarter =3D profile_probability::guessed_always =
() / 4;
>>> +  profile_probability even =3D profile_probability::even ();
>>> +
>>> +  ASSERT_EQ (1, quarter.apply (3));
>>> +  ASSERT_EQ (-1, quarter.apply (-3));
>>> +  ASSERT_EQ (2, even.apply (3));
>>> +  ASSERT_EQ (-2, even.apply (-3));
>>> +  ASSERT_EQ (0, even.apply (0));
>>> +
>>> +  ASSERT_EQ (0, profile_probability::never ().apply (min));
>>> +  ASSERT_EQ (min, profile_probability::always ().apply (min));
>>> +  ASSERT_EQ (max, profile_probability::always ().apply (max));
>>> +  ASSERT_EQ (min, profile_probability::guessed_always ().apply (min));
>>> +  ASSERT_EQ (min / 2, even.apply (min));
>>> +  ASSERT_EQ (max / 2 + 1, even.apply (max));
>>> +
>>> +  profile_probability uninitialized =3D profile_probability::uninitial=
ized ();
>>> +  ASSERT_EQ (1, uninitialized.apply (3));
>>> +  ASSERT_EQ (-1, uninitialized.apply (-3));
>>> +  ASSERT_EQ (min / 2, uninitialized.apply (min));
>>> +  ASSERT_EQ (max / 2, uninitialized.apply (max));
>>> +}
>>> +
>>> +/* Run all of the selftests within this file.  */
>>> +
>>> +void
>>> +profile_count_cc_tests ()
>>> +{
>>> +  test_profile_probability_apply ();
>>> +}
>>> +
>>> +} // namespace selftest
>>> +
>>> +#endif
>>> diff --git a/gcc/profile-count.h b/gcc/profile-count.h
>>> index b424ecba3ea..76325d0fdfa 100644
>>> --- a/gcc/profile-count.h
>>> +++ b/gcc/profile-count.h
>>> @@ -513,11 +513,35 @@ public:
>>>     return ret;
>>>   }
>>>=20
>>> +  /* Return VAL scaled by this probability.  Round initialized probabi=
lities
>>> +     to the nearest integer, with halfway values away from zero.  Trea=
t an
>>> +     uninitialized probability as one half and truncate toward zero.  =
*/
>>> gcov_type apply (gcov_type val) const
>>>   {
>>>     if (*this =3D=3D uninitialized ())
>>> return val / 2;
>>> -      return RDIV (val * m_val, max_probability);
>>> +
>>> +      /* A unit probability leaves VAL unchanged.  Return it directly =
because
>>> + the magnitude of the minimum gcov_type value is one greater than the
>>> + maximum gcov_type value.  */
>>> +      if (m_val =3D=3D max_probability)
>>> + return val;
>>> +
>>> +      /* Convert to unsigned before negating so that the minimum gcov_=
type
>>> + value has a representable magnitude.  Scale the magnitude with
>>> + overflow-safe arithmetic, then restore the sign.  */
>>> +      gcov_type_unsigned magnitude
>>> + =3D val < 0 ? -(gcov_type_unsigned) val : (gcov_type_unsigned) val;
>>> +      uint64_t scaled;
>>> +      bool scaled_p
>>> + =3D safe_scale_64bit (magnitude, m_val, max_probability, &scaled);
>>> +      /* The scaled result fits in uint64_t.  With the unit case handl=
ed above,
>>> + it also fits in the nonnegative range of gcov_type.  */
>>> +      gcc_checking_assert (scaled_p);
>>> +      gcc_checking_assert
>>> + (scaled <=3D (gcov_type_unsigned) INTTYPE_MAXIMUM (gcov_type));
>>> +
>>> +      return val < 0 ? -(gcov_type) scaled : (gcov_type) scaled;
>>>   }
>>>=20
>>> /* Return 1-*THIS.  */
>>> diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc
>>> index e39a94f8688..a281a239b10 100644
>>> --- a/gcc/selftest-run-tests.cc
>>> +++ b/gcc/selftest-run-tests.cc
>>> @@ -72,6 +72,7 @@ selftest::run_tests ()
>>> wide_int_cc_tests ();
>>> ggc_tests_cc_tests ();
>>> sreal_cc_tests ();
>>> +  profile_count_cc_tests ();
>>> fibonacci_heap_cc_tests ();
>>> typed_splay_tree_cc_tests ();
>>> opt_suggestions_cc_tests ();
>>> diff --git a/gcc/selftest.h b/gcc/selftest.h
>>> index 8891d0b7b6f..f9dacd69af7 100644
>>> --- a/gcc/selftest.h
>>> +++ b/gcc/selftest.h
>>> @@ -248,6 +248,7 @@ extern void path_coverage_cc_tests ();
>>> extern void predict_cc_tests ();
>>> extern void pretty_print_cc_tests ();
>>> extern void pretty_print_token_buffer_cc_tests ();
>>> +extern void profile_count_cc_tests ();
>>> extern void pub_sub_cc_tests ();
>>> extern void range_op_tests ();
>>> extern void range_tests ();
>>> --=20
>>> 2.50.1 (Apple Git-155)
>>>=20
>>=20
>=20