[2/2] ifcvt: Make average_cost independent of arm order [PR125557]
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_check--master-arm |
success
|
Test passed
|
Commit Message
From: Kyrylo Tkachov <ktkachov@nvidia.com>
average_cost computes
ELSE_COST + P * (THEN_COST - ELSE_COST)
profile_probability::apply now rounds signed initialized values to the nearest
integer, with halfway values away from zero. That signed rounding can still
give different integer costs when equivalent CFG arms are reversed. Unknown
probabilities have the same issue for odd cost differences because apply
truncates them toward zero.
Write the documented weighted average as
ELSE_COST + P * (THEN_COST - ELSE_COST)
when THEN_COST is at least ELSE_COST, and as
THEN_COST + (1 - P) * (ELSE_COST - THEN_COST)
otherwise. Both forms start with the cheaper arm and scale a nonnegative
cost difference by the probability of the costlier arm. Reversing the CFG
arms therefore keeps the same base, magnitude, probability, and rounded
result. Cast the costlier arm to gcov_type before subtraction.
Add an x86 test with the same costs and probabilities represented using
reversed CFG arms. The scaled cost difference is an exact halfway value.
The apply-only compiler makes opposite profitability decisions for the two
orientations. With this change both forms convert to conditional moves.
Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux.
Ok for trunk?
Thanks,
Kyrill
gcc/ChangeLog:
PR tree-optimization/125557
* ifcvt.cc (average_cost): Scale a nonnegative cost difference using
the probability of the more expensive arm.
gcc/testsuite/ChangeLog:
PR tree-optimization/125557
* gcc.target/i386/ifcvt-average-cost-1.c: New test.
Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com>
---
gcc/ifcvt.cc | 13 ++++++--
.../gcc.target/i386/ifcvt-average-cost-1.c | 32 +++++++++++++++++++
2 files changed, 43 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/i386/ifcvt-average-cost-1.c
Comments
Ping.
Thanks,
Kyrill
> On 17 Jul 2026, at 15:25, Kyrylo Tkachov <ktkachov@nvidia.com> wrote:
>
> From: Kyrylo Tkachov <ktkachov@nvidia.com>
>
> average_cost computes
>
> ELSE_COST + P * (THEN_COST - ELSE_COST)
>
> profile_probability::apply now rounds signed initialized values to the nearest
> integer, with halfway values away from zero. That signed rounding can still
> give different integer costs when equivalent CFG arms are reversed. Unknown
> probabilities have the same issue for odd cost differences because apply
> truncates them toward zero.
>
> Write the documented weighted average as
>
> ELSE_COST + P * (THEN_COST - ELSE_COST)
>
> when THEN_COST is at least ELSE_COST, and as
>
> THEN_COST + (1 - P) * (ELSE_COST - THEN_COST)
>
> otherwise. Both forms start with the cheaper arm and scale a nonnegative
> cost difference by the probability of the costlier arm. Reversing the CFG
> arms therefore keeps the same base, magnitude, probability, and rounded
> result. Cast the costlier arm to gcov_type before subtraction.
>
> Add an x86 test with the same costs and probabilities represented using
> reversed CFG arms. The scaled cost difference is an exact halfway value.
> The apply-only compiler makes opposite profitability decisions for the two
> orientations. With this change both forms convert to conditional moves.
>
> Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux.
> Ok for trunk?
> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
> PR tree-optimization/125557
> * ifcvt.cc (average_cost): Scale a nonnegative cost difference using
> the probability of the more expensive arm.
>
> gcc/testsuite/ChangeLog:
>
> PR tree-optimization/125557
> * gcc.target/i386/ifcvt-average-cost-1.c: New test.
>
> Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com>
> ---
> gcc/ifcvt.cc | 13 ++++++--
> .../gcc.target/i386/ifcvt-average-cost-1.c | 32 +++++++++++++++++++
> 2 files changed, 43 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/i386/ifcvt-average-cost-1.c
>
> diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
> index 6a76fec66cb..928483a347c 100644
> --- a/gcc/ifcvt.cc
> +++ b/gcc/ifcvt.cc
> @@ -4309,11 +4309,20 @@ bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost)
> /* Compute average of two given costs weighted by relative probabilities
> of respective basic blocks in an IF-THEN-ELSE. E is the IF-THEN edge.
> With P as the probability to take the IF-THEN branch, return
> - P * THEN_COST + (1 - P) * ELSE_COST. */
> + P * THEN_COST + (1 - P) * ELSE_COST. Evaluate this as
> + ELSE_COST + P * (THEN_COST - ELSE_COST) when THEN_COST >= ELSE_COST, and
> + THEN_COST + (1 - P) * (ELSE_COST - THEN_COST) otherwise. Both forms pass
> + a nonnegative value to profile_probability::apply and make its rounding
> + independent of the CFG arm order. */
> static unsigned
> average_cost (unsigned then_cost, unsigned else_cost, edge e)
> {
> - return else_cost + e->probability.apply ((signed) (then_cost - else_cost));
> + if (then_cost < else_cost)
> + return then_cost
> + + e->probability.invert ().apply ((gcov_type) else_cost - then_cost);
> +
> + return else_cost
> + + e->probability.apply ((gcov_type) then_cost - else_cost);
> }
>
> /* Given a simple IF-THEN-JOIN or IF-THEN-ELSE-JOIN block, attempt to convert
> diff --git a/gcc/testsuite/gcc.target/i386/ifcvt-average-cost-1.c b/gcc/testsuite/gcc.target/i386/ifcvt-average-cost-1.c
> new file mode 100644
> index 00000000000..98ca361cb69
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/ifcvt-average-cost-1.c
> @@ -0,0 +1,32 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target lp64 } */
> +/* { dg-options "-O2 -mtune=generic --param=max-rtl-if-conversion-unpredictable-cost=0 -fdump-rtl-ce1" } */
> +
> +/* These functions describe the same branch probabilities and arm costs with
> + the arms reversed. The scaled cost difference is exactly halfway between
> + two integers. */
> +long
> +then_cheaper (long c, long a, long b)
> +{
> + long x;
> + if (__builtin_expect_with_probability (c != 0, 0, 0.9375))
> + x = a ^ b;
> + else
> + x = b * 3 + 1;
> + return x;
> +}
> +
> +long
> +then_costlier (long c, long a, long b)
> +{
> + long x;
> + if (__builtin_expect_with_probability (c == 0, 1, 0.9375))
> + x = b * 3 + 1;
> + else
> + x = a ^ b;
> + return x;
> +}
> +
> +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through noce_try_cmove_arith" 2 "ce1" } } */
> +/* { dg-final { scan-assembler-times {\tcmov} 2 } } */
> +/* { dg-final { scan-assembler-not {\tj(e|ne)\t} } } */
> --
> 2.50.1 (Apple Git-155)
>
On 7/24/2026 6:27 AM, Kyrylo Tkachov wrote:
> Ping.
> Thanks,
> Kyrill
>
>> On 17 Jul 2026, at 15:25, Kyrylo Tkachov <ktkachov@nvidia.com> wrote:
>>
>> From: Kyrylo Tkachov <ktkachov@nvidia.com>
>>
>> average_cost computes
>>
>> ELSE_COST + P * (THEN_COST - ELSE_COST)
>>
>> profile_probability::apply now rounds signed initialized values to the nearest
>> integer, with halfway values away from zero. That signed rounding can still
>> give different integer costs when equivalent CFG arms are reversed. Unknown
>> probabilities have the same issue for odd cost differences because apply
>> truncates them toward zero.
>>
>> Write the documented weighted average as
>>
>> ELSE_COST + P * (THEN_COST - ELSE_COST)
>>
>> when THEN_COST is at least ELSE_COST, and as
>>
>> THEN_COST + (1 - P) * (ELSE_COST - THEN_COST)
>>
>> otherwise. Both forms start with the cheaper arm and scale a nonnegative
>> cost difference by the probability of the costlier arm. Reversing the CFG
>> arms therefore keeps the same base, magnitude, probability, and rounded
>> result. Cast the costlier arm to gcov_type before subtraction.
>>
>> Add an x86 test with the same costs and probabilities represented using
>> reversed CFG arms. The scaled cost difference is an exact halfway value.
>> The apply-only compiler makes opposite profitability decisions for the two
>> orientations. With this change both forms convert to conditional moves.
>>
>> Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux.
>> Ok for trunk?
>> Thanks,
>> Kyrill
>>
>> gcc/ChangeLog:
>>
>> PR tree-optimization/125557
>> * ifcvt.cc (average_cost): Scale a nonnegative cost difference using
>> the probability of the more expensive arm.
>>
>> gcc/testsuite/ChangeLog:
>>
>> PR tree-optimization/125557
>> * gcc.target/i386/ifcvt-average-cost-1.c: New test.
>>
>> Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com>
OK.
jeff
On 7/24/2026 6:27 AM, Kyrylo Tkachov wrote:
>> On 17 Jul 2026, at 15:25, Kyrylo Tkachov <ktkachov@nvidia.com> wrote:
>>
>> From: Kyrylo Tkachov <ktkachov@nvidia.com>
>>
>> average_cost computes
>>
>> ELSE_COST + P * (THEN_COST - ELSE_COST)
>>
>> profile_probability::apply now rounds signed initialized values to the nearest
>> integer, with halfway values away from zero. That signed rounding can still
>> give different integer costs when equivalent CFG arms are reversed. Unknown
>> probabilities have the same issue for odd cost differences because apply
>> truncates them toward zero.
>>
>> Write the documented weighted average as
>>
>> ELSE_COST + P * (THEN_COST - ELSE_COST)
>>
>> when THEN_COST is at least ELSE_COST, and as
>>
>> THEN_COST + (1 - P) * (ELSE_COST - THEN_COST)
>>
>> otherwise. Both forms start with the cheaper arm and scale a nonnegative
>> cost difference by the probability of the costlier arm. Reversing the CFG
>> arms therefore keeps the same base, magnitude, probability, and rounded
>> result. Cast the costlier arm to gcov_type before subtraction.
>>
>> Add an x86 test with the same costs and probabilities represented using
>> reversed CFG arms. The scaled cost difference is an exact halfway value.
>> The apply-only compiler makes opposite profitability decisions for the two
>> orientations. With this change both forms convert to conditional moves.
>>
>> Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux.
>> Ok for trunk?
>> Thanks,
>> Kyrill
>>
>> gcc/ChangeLog:
>>
>> PR tree-optimization/125557
>> * ifcvt.cc (average_cost): Scale a nonnegative cost difference using
>> the probability of the more expensive arm.
>>
>> gcc/testsuite/ChangeLog:
>>
>> PR tree-optimization/125557
>> * gcc.target/i386/ifcvt-average-cost-1.c: New test.
>>
>> Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com>
OK.
jeff
> On 25 Jul 2026, at 04:09, Jeffrey Law <jeffrey.law@oss.qualcomm.com> wrote:
>
>
>
> On 7/24/2026 6:27 AM, Kyrylo Tkachov wrote:
>>> On 17 Jul 2026, at 15:25, Kyrylo Tkachov <ktkachov@nvidia.com> wrote:
>>>
>>> From: Kyrylo Tkachov <ktkachov@nvidia.com>
>>>
>>> average_cost computes
>>>
>>> ELSE_COST + P * (THEN_COST - ELSE_COST)
>>>
>>> profile_probability::apply now rounds signed initialized values to the nearest
>>> integer, with halfway values away from zero. That signed rounding can still
>>> give different integer costs when equivalent CFG arms are reversed. Unknown
>>> probabilities have the same issue for odd cost differences because apply
>>> truncates them toward zero.
>>>
>>> Write the documented weighted average as
>>>
>>> ELSE_COST + P * (THEN_COST - ELSE_COST)
>>>
>>> when THEN_COST is at least ELSE_COST, and as
>>>
>>> THEN_COST + (1 - P) * (ELSE_COST - THEN_COST)
>>>
>>> otherwise. Both forms start with the cheaper arm and scale a nonnegative
>>> cost difference by the probability of the costlier arm. Reversing the CFG
>>> arms therefore keeps the same base, magnitude, probability, and rounded
>>> result. Cast the costlier arm to gcov_type before subtraction.
>>>
>>> Add an x86 test with the same costs and probabilities represented using
>>> reversed CFG arms. The scaled cost difference is an exact halfway value.
>>> The apply-only compiler makes opposite profitability decisions for the two
>>> orientations. With this change both forms convert to conditional moves.
>>>
>>> Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux.
>>> Ok for trunk?
>>> Thanks,
>>> Kyrill
>>>
>>> gcc/ChangeLog:
>>>
>>> PR tree-optimization/125557
>>> * ifcvt.cc (average_cost): Scale a nonnegative cost difference using
>>> the probability of the more expensive arm.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> PR tree-optimization/125557
>>> * gcc.target/i386/ifcvt-average-cost-1.c: New test.
>>>
>>> Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com>
> OK.
Thanks for the review Jeff. You seem to have ok’ed this patch twice.
Did you meant to ok patch [1/2] by any chance?
https://gcc.gnu.org/pipermail/gcc-patches/2026-July/724378.html
Kyrill
> jeff
@@ -4309,11 +4309,20 @@ bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost)
/* Compute average of two given costs weighted by relative probabilities
of respective basic blocks in an IF-THEN-ELSE. E is the IF-THEN edge.
With P as the probability to take the IF-THEN branch, return
- P * THEN_COST + (1 - P) * ELSE_COST. */
+ P * THEN_COST + (1 - P) * ELSE_COST. Evaluate this as
+ ELSE_COST + P * (THEN_COST - ELSE_COST) when THEN_COST >= ELSE_COST, and
+ THEN_COST + (1 - P) * (ELSE_COST - THEN_COST) otherwise. Both forms pass
+ a nonnegative value to profile_probability::apply and make its rounding
+ independent of the CFG arm order. */
static unsigned
average_cost (unsigned then_cost, unsigned else_cost, edge e)
{
- return else_cost + e->probability.apply ((signed) (then_cost - else_cost));
+ if (then_cost < else_cost)
+ return then_cost
+ + e->probability.invert ().apply ((gcov_type) else_cost - then_cost);
+
+ return else_cost
+ + e->probability.apply ((gcov_type) then_cost - else_cost);
}
/* Given a simple IF-THEN-JOIN or IF-THEN-ELSE-JOIN block, attempt to convert
new file mode 100644
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-O2 -mtune=generic --param=max-rtl-if-conversion-unpredictable-cost=0 -fdump-rtl-ce1" } */
+
+/* These functions describe the same branch probabilities and arm costs with
+ the arms reversed. The scaled cost difference is exactly halfway between
+ two integers. */
+long
+then_cheaper (long c, long a, long b)
+{
+ long x;
+ if (__builtin_expect_with_probability (c != 0, 0, 0.9375))
+ x = a ^ b;
+ else
+ x = b * 3 + 1;
+ return x;
+}
+
+long
+then_costlier (long c, long a, long b)
+{
+ long x;
+ if (__builtin_expect_with_probability (c == 0, 1, 0.9375))
+ x = b * 3 + 1;
+ else
+ x = a ^ b;
+ return x;
+}
+
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through noce_try_cmove_arith" 2 "ce1" } } */
+/* { dg-final { scan-assembler-times {\tcmov} 2 } } */
+/* { dg-final { scan-assembler-not {\tj(e|ne)\t} } } */