[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 Andrew Macleod changed: What|Removed |Added Resolution|--- |FIXED Status|ASSIGNED|RESOLVED --- Comment #13 from Andrew Macleod --- fixed
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 --- Comment #12 from GCC Commits --- The master branch has been updated by Andrew Macleod : https://gcc.gnu.org/g:4b6bbc51a6acfa1692c7143592f01b28eed57da1 commit r16-8612-g4b6bbc51a6acfa1692c7143592f01b28eed57da1 Author: Andrew MacLeod Date: Mon Apr 13 13:37:36 2026 -0400 Register equivalences only once per statement. When a copy statement is rewritten, do not register a new equivalence between the LHS and RHS. PR tree-optimization/124809 gcc/ * value-relation.cc (equiv_oracle::equiv_oracle): Allocate lhs equivalence bitmap. (relation_oracle::record): Check if LHS has already created an equivalence record. * value-relation.h (relation_oracle::relation_oracle): New. (relation_oracle::m_lhs_equiv_set_p): New. gcc/testsuite/ * gcc.dg/pr124809.c: New.
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809
Andrew Macleod changed:
What|Removed |Added
Assignee|unassigned at gcc dot gnu.org |amacleod at redhat dot
com
Status|NEW |ASSIGNED
--- Comment #11 from Andrew Macleod ---
Created attachment 64196
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=64196&action=edit
proopsed patch
DOM rewrites a statement using valid local equivalences, but ranger does not
understans that this is replacing an existing equivalence rather than adding a
new one.
Given:
c = a;
if (a == b)
d = c;
...
if (c == b)
Ranger infers {a, c, d} as an equivalence class. Since c and b are unrelated,
the final condition is not foldable.
DOM rewrites:
d = c;
to:
d = b;
based on a == b and c == a, producing:
c = a;
if (a == b)
d = b;
...
if (c == b)
This transformation is valid, but DOM then re-runs Ranger on the updated
statement. Ranger sees d = b and adds a new equivalence, expanding the set to
{a, b, c, d}.
This is incorrect. The original equivalence from d = c was not removed, so d
now incorrectly bridges the two sets. As a result, Ranger folds if (c == b) to
true.
The core issue is that equivalences are only added, never removed. After
rewriting, the old equivalence must no longer apply, but there is currently no
mechanism to retract it.
This problem does not exist in VRP or other places because they either
a) do no revisit the statement once it is rewritten
b) save all the rewrites until the end.
I have a Fix which tracks whether an SSA name on the LHS has already had an
equivalence registered *as a LHS*, and only allows this once per stmt. This is
cheap and prevents the second equivalence from being registered. It does not
affect other equivalences being registered against the SSA_NAME from other
locations.
This is conservative but ensures this situation does not happen.
Currently running through the testsuite.
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 --- Comment #10 from Andrew Macleod --- (In reply to Richard Biener from comment #9) > (In reply to Andrew Macleod from comment #8) > > ick. We don't remove relations once they are added. Never been a need. > > > > IN this program we have: > > > > > >a.0_1 = a; > > _2 = (int) a.0_1; > > _3 = (int) g_16(D); > > _4 = _2 != _3; > > _20 = (short unsigned int) _4; > > if (a.0_1 != _20) > > goto ; [5.50%] > > else > > goto ; [94.50%] > > > >[local count: 55807731]: > > j_18 = a.0_1 + _20; > > k_19 = (int) j_18; > > if (a.0_1 == 0) > > goto ; [50.00%] > > else > > goto ; [50.00%] > > > >[local count: 27903866]: > > _21 = k_19 >> _2; > > goto ; [100.00%] > > > >[local count: 958878295]: > > > > <<--- Partial equivalence > > if (d.10_9 != 0) > > goto ; [50.00%] > > else > > goto ; [50.00%] > > > > at some point we register a partial equivalence of 16 bits between a.6_5 and > > a.0_1 > > (Partial equivalences are also used between values of the same precision but > > with difference signs as it represents a 16 bit equivalence, not a value.) > > > > DOM continues omn its merry way, and decides at some point that on the else > > side of > > if (a.0_1 != _20) > > that a.0_1 == _20, and as thats the only way to reach bb6, it rewrites > > that statement > > Optimizing block #6 > > > > 1>>> STMT 1 = a.0_1 le_expr _20 > > 1>>> STMT 1 = a.0_1 ge_expr _20 > > 1>>> STMT 1 = a.0_1 eq_expr _20 > > 1>>> STMT 0 = a.0_1 ne_expr _20 > > 0>>> COPY a.0_1 = _20 > > Optimizing statement a.6_5 = (short int) a.0_1; > > Replaced 'a.0_1' with variable '_20' > > > > The next time ranger processes that statement, it registers a partial (bit) > > equivalence between > > a.6_5 and _20 > > but we do NOT delete the previous one... which means we now think that there > > is an equivalence between a.6_5, _20, and a.0_1 > > Which there is not. > > Not sure if I follow the above correctly, but shouldn't there only > be a partial equivalence between a.6_5 (signed short) and a.0_1 > (unsinged short) and _20? And isn't that correct? > These are global equivalences... when we see a.6_5 = (short int) a.0_1; Its globally true that a.6_5 and a.0_1 are 16 bit identical. When it is re-written by DOM into a.6_5 = (short int) _20 And re-processed, the relation oracle now registers a global bit equivalence between a.6_5 and _20, which then transitively creates a global equivalence between a.0_1 and _20... THAT transitive equivalence is *not* global in nature. At the moment, they all share a partial equivalence set.. so there is no direct way to "disable" this feature. VRP never encounters this because ranger never revisits a statement once its is processed fully and rewriiten. It Looks like DOM is using it for constant propagation and is revisiting it after it rewrites the statement. This may also have something to do with the path ranger which DOM uses.. I'm not as familiar with how it sets and resets things... I'll get back to you in a bit. > > Only one of those equivalences with a.6_5 can exist. > > Either the old one needs to be deleted, or the new one not added. > > > > I'm thinking about it...?
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 --- Comment #9 from Richard Biener --- (In reply to Andrew Macleod from comment #8) > ick. We don't remove relations once they are added. Never been a need. > > IN this program we have: > > >a.0_1 = a; > _2 = (int) a.0_1; > _3 = (int) g_16(D); > _4 = _2 != _3; > _20 = (short unsigned int) _4; > if (a.0_1 != _20) > goto ; [5.50%] > else > goto ; [94.50%] > >[local count: 55807731]: > j_18 = a.0_1 + _20; > k_19 = (int) j_18; > if (a.0_1 == 0) > goto ; [50.00%] > else > goto ; [50.00%] > >[local count: 27903866]: > _21 = k_19 >> _2; > goto ; [100.00%] > >[local count: 958878295]: > > <<--- Partial equivalence > if (d.10_9 != 0) > goto ; [50.00%] > else > goto ; [50.00%] > > at some point we register a partial equivalence of 16 bits between a.6_5 and > a.0_1 > (Partial equivalences are also used between values of the same precision but > with difference signs as it represents a 16 bit equivalence, not a value.) > > DOM continues omn its merry way, and decides at some point that on the else > side of > if (a.0_1 != _20) > that a.0_1 == _20, and as thats the only way to reach bb6, it rewrites > that statement > Optimizing block #6 > > 1>>> STMT 1 = a.0_1 le_expr _20 > 1>>> STMT 1 = a.0_1 ge_expr _20 > 1>>> STMT 1 = a.0_1 eq_expr _20 > 1>>> STMT 0 = a.0_1 ne_expr _20 > 0>>> COPY a.0_1 = _20 > Optimizing statement a.6_5 = (short int) a.0_1; > Replaced 'a.0_1' with variable '_20' > > The next time ranger processes that statement, it registers a partial (bit) > equivalence between > a.6_5 and _20 > but we do NOT delete the previous one... which means we now think that there > is an equivalence between a.6_5, _20, and a.0_1 > Which there is not. Not sure if I follow the above correctly, but shouldn't there only be a partial equivalence between a.6_5 (signed short) and a.0_1 (unsinged short) and _20? And isn't that correct? > Only one of those equivalences with a.6_5 can exist. > Either the old one needs to be deleted, or the new one not added. > > I'm thinking about it...?
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 --- Comment #8 from Andrew Macleod --- ick. We don't remove relations once they are added. Never been a need. IN this program we have: a.0_1 = a; _2 = (int) a.0_1; _3 = (int) g_16(D); _4 = _2 != _3; _20 = (short unsigned int) _4; if (a.0_1 != _20) goto ; [5.50%] else goto ; [94.50%] [local count: 55807731]: j_18 = a.0_1 + _20; k_19 = (int) j_18; if (a.0_1 == 0) goto ; [50.00%] else goto ; [50.00%] [local count: 27903866]: _21 = k_19 >> _2; goto ; [100.00%] [local count: 958878295]: <<--- Partial equivalence if (d.10_9 != 0) goto ; [50.00%] else goto ; [50.00%] at some point we register a partial equivalence of 16 bits between a.6_5 and a.0_1 (Partial equivalences are also used between values of the same precision but with difference signs as it represents a 16 bit equivalence, not a value.) DOM continues omn its merry way, and decides at some point that on the else side of if (a.0_1 != _20) that a.0_1 == _20, and as thats the only way to reach bb6, it rewrites that statement Optimizing block #6 1>>> STMT 1 = a.0_1 le_expr _20 1>>> STMT 1 = a.0_1 ge_expr _20 1>>> STMT 1 = a.0_1 eq_expr _20 1>>> STMT 0 = a.0_1 ne_expr _20 0>>> COPY a.0_1 = _20 Optimizing statement a.6_5 = (short int) a.0_1; Replaced 'a.0_1' with variable '_20' The next time ranger processes that statement, it registers a partial (bit) equivalence between a.6_5 and _20 but we do NOT delete the previous one... which means we now think that there is an equivalence between a.6_5, _20, and a.0_1 Which there is not. Only one of those equivalences with a.6_5 can exist. Either the old one needs to be deleted, or the new one not added. I'm thinking about it...?
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 --- Comment #7 from Andrew Macleod --- Yeah, I'm having a look.
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809
--- Comment #5 from Jakub Jelinek ---
Slightly cleaned up:
unsigned short a, c, d;
short
foo (short g, short h)
{
return h;
}
short
bar (short g, short h)
{
return g;
}
__attribute__((noipa)) void
baz (...)
{
__builtin_abort ();
}
__attribute__((noinline)) int
qux (signed char g)
{
unsigned short j;
int b = 0, k;
while (d < 8)
{
if ((b = a != g) ^ a)
{
j = b + a;
k = j;
return a ? 0 : k >> a;
}
b = foo (d && c, a);
}
baz (b);
return 100;
}
__attribute__((noinline)) int
fred (signed char g)
{
unsigned short j;
int b = 0, k;
while (d < 8)
{
if ((b = a != g) ^ a)
{
j = b + a;
k = j;
return a ? 0 : k >> a;
}
b = bar (a, d && c);
}
baz (b);
return 100;
}
int
main ()
{
if (qux (1) != 1 || fred (1) != 1)
__builtin_abort ();
}
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809
--- Comment #6 from Jakub Jelinek ---
And the same with some auto var renames, so that there is no confusion for what
is from which function:
unsigned short a, c, d;
short
foo (short g, short h)
{
return h;
}
short
bar (short g, short h)
{
return g;
}
__attribute__((noipa)) void
baz (int b)
{
__builtin_abort ();
}
__attribute__((noinline)) int
qux (signed char g)
{
unsigned short j;
int b = 0, k;
while (d < 8)
{
if ((b = a != g) ^ a)
{
j = b + a;
k = j;
return a ? 0 : k >> a;
}
b = foo (d && c, a);
}
baz (b);
return 100;
}
__attribute__((noinline)) int
fred (signed char p)
{
unsigned short o;
int m = 0, n;
while (d < 8)
{
if ((m = a != p) ^ a)
{
o = m + a;
n = o;
return a ? 0 : n >> a;
}
m = bar (a, d && c);
}
baz (m);
return 100;
}
int
main ()
{
if (qux (1) != 1 || fred (1) != 1)
__builtin_abort ();
}
At runtime in both cases a is 0 and b/m are 1, so the right shift is 1.
Figuring out that it is >> 0 is correct, and that because it is in ?: last
operand
b + a or m + a is equal to just b or m and that it is [0, 1] is correct, but
somehow it only thinks about [0, 0] and not [0, 1].
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 --- Comment #4 from Richard Biener --- DOM2 comes up with e = 0; from # RANGE [irange] int [0, 65535] MASK 0x VALUE 0x0 k_23 = (int) j_22; if (a.0_1 == 0) goto ; [50.00%] else goto ; [50.00%] [local count: 27903866]: iftmp.3_25 = k_23 >> _2; [local count: 55807731]: # iftmp.3_14 = PHI e = iftmp.3_14; somehow via Global Exported: j_22 = [irange] short unsigned int [0, 0][2, 2] MASK 0x3 VALUE 0x0 Global Exported: k_23 = [irange] int [0, 0][2, 2] MASK 0x3 VALUE 0x0 Andrew?
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 Andrew Pinski changed: What|Removed |Added Target||x86_64 aarch64 Priority|P3 |P1
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 Andrew Pinski changed: What|Removed |Added Attachment #64167|0 |1 is obsolete|| --- Comment #3 from Andrew Pinski --- Created attachment 64168 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=64168&action=edit better testcase This one is better beca use it does not need defines.
[Bug tree-optimization/124809] [16 Regression] wrong code at -O1 with "-fno-tree-ch -fno-tree-dce" on x86_64-linux-gnu since r16-5322
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124809 Jakub Jelinek changed: What|Removed |Added Target Milestone|--- |16.0 CC||amacleod at redhat dot com, ||jakub at gcc dot gnu.org Summary|wrong code at -O1 with |[16 Regression] wrong code |"-fno-tree-ch |at -O1 with "-fno-tree-ch |-fno-tree-dce" on |-fno-tree-dce" on |x86_64-linux-gnu|x86_64-linux-gnu since ||r16-5322 Ever confirmed|0 |1 Last reconfirmed||2026-04-07 Status|UNCONFIRMED |NEW --- Comment #2 from Jakub Jelinek --- Started with r16-5322-g71f41d9b3ac80f428de61486f2cec9604c4d729e
