On 8/4/2026 3:54 AM, [email protected] wrote:
From: Kyrylo Tkachov <[email protected]>
MIN (MAX (X, Y + CST), Y) is Y for a positive CST: the inner select is at
least Y + CST, which is strictly above Y where the type cannot wrap, so the
outer select always takes Y. The dual holds for MAX over MIN with a
negative offset. Both leave the whole nest dead.
int f (int x, int y)
{
int a = x > y + 7 ? x : y + 7;
return a < y ? a : y;
}
aarch64 -O2 before:
add w2, w1, 7
cmp w2, w0
csel w0, w2, w0, ge
cmp w0, w1
csel w0, w0, w1, le
after:
mov w0, w1
The shape arises after inlining, when a clamp helper is instantiated with a
lower bound that the caller has already pushed above the upper one. The
existing min/max-with-offset rules only cover the case where the two selects
name the same operand, so the nest survived.
Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk?
Thanks,
Kyrill
gcc/ChangeLog:
* match.pd (MIN (MAX (X, Y + CST), Y)): New simplification.
(MAX (MIN (X, Y + CST), Y)): Likewise.
gcc/testsuite/ChangeLog:
* gcc.dg/tree-ssa/minmax-offset-absorb-1.c: New test.
Generally OK. Just one testcase question.
+
+/* Must not fold: the offset has the wrong sign for the outer select. */
+int keep2 (int x, int y)
+{
+ int a = x > y - 7 ? x : y - 7;
+ return a < y ? a : y;
+}
So very happy to see both positive and negative tests. This one
probably isn't testing what you think since it appears the MAX_EXPR is
never discovered. We discover the MIN_EXPR, but the MAX is still in
if-then form. So not really sure the scans are testing this in the way
you want. Not sure if you want to adjust that or not. For reference
the IL looks like this:
_1 = y_3(D) + -6;
if (_1 > x_4(D))
goto <bb 4>; [50.00%]
else
goto <bb 3>; [50.00%]
;; succ: 4 [50.0% (guessed)] count:536870912 (estimated
locally, freq 0.5000) (TRUE_VALUE,EXECUTABLE)
;; 3 [50.0% (guessed)] count:536870912 (estimated
locally, freq 0.5000) (FALSE_VALUE,EXECUTABLE)
;; basic block 3, loop depth 0, count 536870912 (estimated locally,
freq 0.5000), maybe hot
;; prev block 2, next block 4, flags: (NEW, REACHABLE, VISITED)
;; pred: 2 [50.0% (guessed)] count:536870912 (estimated
locally, freq 0.5000) (FALSE_VALUE,EXECUTABLE)
_8 = MIN_EXPR <y_3(D), x_4(D)>;
goto <bb 5>; [100.00%]
;; succ: 5 [always] count:536870912 (estimated locally, freq
0.5000) (FALLTHRU,EXECUTABLE)
;; basic block 4, loop depth 0, count 536870912 (estimated locally,
freq 0.5000), maybe hot
;; prev block 3, next block 5, flags: (NEW, REACHABLE, VISITED)
;; pred: 2 [50.0% (guessed)] count:536870912 (estimated
locally, freq 0.5000) (TRUE_VALUE,EXECUTABLE)
a_5 = y_3(D) + -7;
;; succ: 5 [always] count:536870912 (estimated locally, freq
0.5000) (FALLTHRU,EXECUTABLE)
;; basic block 5, loop depth 0, count 1073741824 (estimated locally,
freq 1.0000), maybe hot
;; prev block 4, next block 1, flags: (NEW, REACHABLE, VISITED)
;; pred: 4 [always] count:536870912 (estimated locally, freq
0.5000) (FALLTHRU,EXECUTABLE)
;; 3 [always] count:536870912 (estimated locally, freq
0.5000) (FALLTHRU,EXECUTABLE)
# prephitmp_9 = PHI <a_5(4), _8(3)>
return prephitmp_9;
Jeff