https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119099
Bug ID: 119099
Summary: Compile-time hang in DCE
Product: gcc
Version: 15.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: rtl-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: alexey.merzlyakov at samsung dot com
Target Milestone: ---
Created attachment 60644
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=60644&action=edit
Reduced test-case
The following code c-reduced from csmith generated testcase, causes GCC to hang
at -O2 during the compile-time:
int a, b;
void func2(short);
void func1() {
while (1) {
int loc = 8;
while (1) {
func2(loc);
if (a)
loc = 3;
else if (b)
break;
loc |= a;
}
}
}
The hang appears on trunk and affects at least RISC-V 64/32, ARM 64/32,
powerpc64, MRISC32 targets.
Here is the example link on godbolt for RV64: https://godbolt.org/z/eWxfW3q1K
GCC hangs in ext-dce pass, where the "df_worklist_dataflow_doublequeue()" loops
forwever on the following basic blocks taken from double-worklists queue:
BB7->BB6->BB4->BB3->BB5->worklists swap->BB7->BB6->BB4->BB3->BB5->worklists
swap ...
Dataflow solver algorithm will serve each BB node while BB sets are still
changing. The key point for solver in deciding whether to continue or not - is
"changed" variable state, which is set by "con_fun_n" function. This function
takes a pointer to "ext_dce_rd_transfer_n()" for ext-dce case. It initializes
"livenow" variables bitmap by "livein[]" states, processes it and finally emits
back "livenow" to the "livein[current BB]". For the selected basic block,
"livein" state flip-flops each time when loop returns back to this BB
processing. E.g. in the example above for the BB=4:
Breakpoint 1, ext_dce_rd_transfer_n (bb_index=4) at ext-dce.cc:980
980 return true;
(gdb) p/x (&livein[bb_index])->first->next->bits
$8 = {0xf000030f0000000, 0xff0}
Continuing...
worklist <-> pedning swap
Breakpoint 2, df_worklist_dataflow_doublequeue (...) at df-core.cc:1097
1097 std::swap (pending, worklist);
Continuing...
(gdb) p/x (&livein[bb_index])->first->next->bits
$11 = {0xf000070f0000000, 0xff0}
Continuing...
So livein[4] bits flip 0xf000030f0000000 -> 0xf000070f0000000 ->
0xf000030f0000000 -> ... states forever.
In other words, ext-dce + df-core could be treated as finite-state machine,
whose states acting on "livenow" and "livein" bitmaps. On the given testcase
machine loops forever, flipping/flopping or widening/narrowing "livein" states
with "livenow". Dataflow solver algorith will never come to the null-worklist
state in this case.
The cornerstone place for all of the described above seems to be "livein"
state, which could be changed in two directions (widen or narrowed) and thus
allowing machine to loop forever. If so, the solution could be to allow
changing of "livein" bitmap state only in one direction: in our case - to
expand. It will cause "ext_dce_rd_transfer_n()" function to also guarantee the
dataflow solver algorithm to converge to its final state.
The proposed solution could be as follows below:
@@ -1094,8 +1094,13 @@ ext_dce_rd_transfer_n (int bb_index)
the generic dataflow code that something changed. */
if (!bitmap_equal_p (&livein[bb_index], livenow))
{
- bitmap_copy (&livein[bb_index], livenow);
- return true;
+ bitmap tmp = BITMAP_ALLOC (NULL);
+ bitmap_and (tmp, &livein[bb_index], livenow);
+ if (!bitmap_equal_p (tmp, livenow))
+ {
+ bitmap_ior_into (&livein[bb_index], livenow);
+ return true;
+ }
}
return false;
It allows "livein[bb_index]" to be only widened and returns true only if it is
being really changed.
The patch works for me locally, and passed simple internal GCC testing. But if
this idea to be considered positively to accept, I will to go with more serious
testing on different targets and prepare its final version ready for mail-list.