expand: Fix up find_bb_boundaries [PR98331]
authorJakub Jelinek <jakub@redhat.com>
Fri, 29 Jan 2021 09:30:09 +0000 (10:30 +0100)
committerJakub Jelinek <jakub@redhat.com>
Fri, 29 Jan 2021 09:30:09 +0000 (10:30 +0100)
When expansion emits some control flow insns etc. inside of a former GIMPLE
basic block, find_bb_boundaries needs to split it into multiple basic
blocks.
The code needs to ignore debug insns in decisions how many splits to do or
where in between some non-debug insns the split should be done, but it can
decide where to put debug insns if they can be kept and otherwise throws
them away (they can't stay outside of basic blocks).
On the following testcase, we end up in the bb from expander with
control flow insn
debug insns
barrier
some other insn
(the some other insn is effectively dead after __builtin_unreachable and
we'll optimize that out later).
Without debug insns, we'd do the split when encountering some other insn
and split after PREV_INSN (some other insn), i.e. after barrier (and the
splitting code then moves the barrier in between basic blocks).
But if there are debug insns, we actually split before the first debug insn
that appeared after the control flow insn, so after control flow insn,
and get a basic block that starts with debug insns and then has a barrier
in the middle that nothing moves it out of the bb.  This leads to ICEs and
even if it wouldn't, different behavior from -g0.
The reason for treating debug insns that way is a different case, e.g.
control flow insn
debug insns
some other insn
or even
control flow insn
barrier
debug insns
some other insn
where splitting before the first such debug insn allows us to keep them
while otherwise we would have to drop them on the floor, and in those
situations we behave the same with -g0 and -g.

So, the following patch fixes it by resetting debug_insn not just when
splitting the blocks (it is set only after seeing a control flow insn and
before splitting for it if needed), but also when seeing a barrier,
which effectively means we always throw away debug insns after a control
flow insn and before following barrier if any, but there is no way around
that, control flow insn must be the last in the bb (BB_END) and BARRIER
after it, debug insns aren't allowed outside of bb.
We still handle the other cases fine (when there is no barrier or when
debug insns appear only after the barrier).

2021-01-29  Jakub Jelinek  <jakub@redhat.com>

PR debug/98331
* cfgbuild.c (find_bb_boundaries): Reset debug_insn when seeing
a BARRIER.

* gcc.dg/pr98331.c: New test.

gcc/cfgbuild.c
gcc/testsuite/gcc.dg/pr98331.c [new file with mode: 0644]

index 7a582d9413993ad289373bb8c22658157dd6b226..41654ec8ed601f2b2cea3cac02a4e4f89630dc22 100644 (file)
@@ -545,6 +545,7 @@ find_bb_boundaries (basic_block bb)
             if the barrier were preceded by a control_flow_insn_p insn.  */
          if (!flow_transfer_insn)
            flow_transfer_insn = prev_nonnote_nondebug_insn_bb (insn);
+         debug_insn = NULL;
        }
 
       if (control_flow_insn_p (insn))
diff --git a/gcc/testsuite/gcc.dg/pr98331.c b/gcc/testsuite/gcc.dg/pr98331.c
new file mode 100644 (file)
index 0000000..951b758
--- /dev/null
@@ -0,0 +1,18 @@
+/* PR debug/98331 */
+/* { dg-do compile } */
+/* { dg-options "-g -O2 -fcompare-debug" } */
+/* { dg-additional-options "-march=x86-64" { target { i?86-*-* x86_64-*-* } } } */
+
+void bar (const char *);
+unsigned long long x;
+
+void
+foo (void)
+{
+  int a = 1;
+  bar ("foo");
+  int b = 2;
+  __atomic_fetch_add (&x, 1, 0);
+  int c = 3;
+  __builtin_unreachable ();
+}