Bug 81900

Summary: [8 Regression] GCC trunk miscompiles Perl / __sigsetjmp issue
Product: gcc Reporter: pipcet
Component: tree-optimizationAssignee: Richard Biener <rguenth>
Status: RESOLVED FIXED    
Severity: normal CC: mikpelinux
Priority: P3 Keywords: wrong-code
Version: 8.0   
Target Milestone: 8.0   
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed: 2017-08-21 00:00:00
Attachments: creduce-minimized test case
executable testcase

Description pipcet 2017-08-19 11:33:08 UTC
Created attachment 42007 [details]
creduce-minimized test case

Hi,

I'm having trouble running a GCC/trunk-compiled Perl; I first noticed
the problem on my WebAssembly port, but I think I can reproduce it on
x86_64 built from the standard GCC tree. I also think this is a GCC
bug.

After some editing, I've managed to generate a (somewhat) minimized
testcase:

Perl_croak() __attribute__((noreturn));
*Perl_sv_gets();
a() {
  __sigsetjmp();
  char *b;
  if ((b = Perl_sv_gets()) == 0)
    Perl_croak("No Perl script found in input\n");
  if (*b == '#')
    __asm__("" : : ""("badbad"));
}

The intended behaviour is that Perl_sv_gets() can return a NULL
pointer, in which case the pointer isn't dereferenced and Perl_croak()
is called instead.  However, the assembly output I get is:

	xorl	%eax, %eax
	call	__sigsetjmp
	xorl	%eax, %eax
	call	Perl_sv_gets
	cmpb	$35, (%rax)

The call to Perl_croak has been removed, and the return value of
Perl_sv_gets() is unconditionally dereferenced.  That's wrong, right?

I'm compiling with:

$ ~/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/xgcc -B /home/pip/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/ -v -S -DPERL_CORE -fwrapv -std=c89 -O3 -fno-strict-aliasing -w -Wfatal-errors testcase.i -o tmp.s || exit 1
Reading specs from /home/pip/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/specs
COLLECT_GCC=/home/pip/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/xgcc
Target: x86_64-pc-linux-gnu
Configured with: ./configure --target=x86_64-pc-linux-gnu --disable-bootstrap --enable-languages=c
Thread model: posix
gcc version 8.0.0 20170819 (experimental) (GCC) 
COLLECT_GCC_OPTIONS='-B' '/home/pip/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/' '-v' '-S' '-D' 'PERL_CORE' '-fwrapv' '-std=c90' '-O3' '-fno-strict-aliasing' '-w' '-Wfatal-errors' '-o' 'tmp.s' '-mtune=generic' '-march=x86-64'
 /home/pip/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/cc1 -fpreprocessed testcase.i -quiet -dumpbase testcase.i -mtune=generic -march=x86-64 -auxbase-strip tmp.s -O3 -Wfatal-errors -w -std=c90 -version -fwrapv -fno-strict-aliasing -o tmp.s
GNU C89 (GCC) version 8.0.0 20170819 (experimental) (x86_64-pc-linux-gnu)
	compiled by GNU C version 6.4.0 20170724, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.3, isl version none
GGC heuristics: --param ggc-min-expand=30 --param ggc-min-heapsize=4096
GNU C89 (GCC) version 8.0.0 20170819 (experimental) (x86_64-pc-linux-gnu)
	compiled by GNU C version 6.4.0 20170724, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.3, isl version none
GGC heuristics: --param ggc-min-expand=30 --param ggc-min-heapsize=4096
Compiler executable checksum: d531daafd687d9500452e3b103d32887
COMPILER_PATH=/home/pip/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/
LIBRARY_PATH=/home/pip/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/:/lib/x86_64-linux-gnu/:/lib/../lib64/:/usr/lib/x86_64-linux-gnu/:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-B' '/home/pip/git/asmjs/subrepos/gcc/host-x86_64-pc-linux-gnu/gcc/' '-v' '-S' '-D' 'PERL_CORE' '-fwrapv' '-std=c90' '-O3' '-fno-strict-aliasing' '-w' '-Wfatal-errors' '-o' 'tmp.s' '-mtune=generic' '-march=x86-64'




The call to sigsetjmp appears to be necessary to trigger the bug.

Sorry if this is a duplicate or a known issue; right now, I don't have
the time to investigate further. The call appears to be eliminated in a tree optimization pass, so I'm tentatively choosing that component.
Comment 1 Mikael Pettersson 2017-08-19 15:58:19 UTC
Created attachment 42008 [details]
executable testcase

Standalone executable testcase, exits 0 when compiled correctly, aborts or exits 1 otherwise.  Fails for me with gcc-8 on x86_64-linux but works on same with gcc-7, gcc-6, and gcc-5.
Comment 2 Mikael Pettersson 2017-08-19 19:23:24 UTC
Started with r250767.
Comment 3 pipcet 2017-08-21 09:13:17 UTC
I've investigated some more, and Mikael's bisection appears to confirm my investigation:

The problem appears to be this chunk:

@@ -2418,7 +2423,9 @@ compute_antic (void)
   inverted_post_order_compute (&postorder);

   auto_sbitmap worklist (last_basic_block_for_fn (cfun) + 1);
-  bitmap_ones (worklist);
+  bitmap_clear (worklist);
+  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
+    bitmap_set_bit (worklist, e->src->index);
   while (changed)
     { 
       if (dump_file && (dump_flags & TDF_DETAILS))


It assumes there are always edges from function calls to the exit block, when the function call might exit nonlocally. However, cfganal.c:add_noreturn_fake_exit_edges is never called for functions that call setjmp, so such bbs are, wrongly, never visited by compute_antic.


The brute-force method of disabling the tree/PRE pass for functions that call setjmp() appears to work and fix the issue, but I'm not sure that's the right way to go.
Comment 4 Richard Biener 2017-08-21 09:47:57 UTC
Mine.
Comment 5 Richard Biener 2017-08-21 10:03:43 UTC
So while there is one simple issue with the iteration, fix:

@@ -2119,14 +2170,13 @@ static sbitmap has_abnormal_preds;
 static bool
 compute_antic_aux (basic_block block, bool block_has_abnormal_pred_edge)
 {
-  bool changed = false;
   bitmap_set_t S, old, ANTIC_OUT;
   bitmap_iterator bi;
   unsigned int bii;
   edge e;
   edge_iterator ei;
-  bool was_visited = BB_VISITED (block);
 
+  bool changed = ! BB_VISITED (block);
   old = ANTIC_OUT = S = NULL;
   BB_VISITED (block) = 1;
 
@@ -2217,7 +2267,7 @@ compute_antic_aux (basic_block block, bo
   /* clean (ANTIC_IN (block)) is defered to after the iteration converged
      because it can cause non-convergence, see for example PR81181.  */
 
-  if (!was_visited || !bitmap_set_equal (old, ANTIC_IN (block)))
+  if (!bitmap_set_equal (old, ANTIC_IN (block)))
     changed = true;
 
  maybe_dump_sets:

there is another issue as we think *_7 is antic and thus hoist the dereference
before the _7 == 0 check which will optimize that away as not necessary in
a later pass.
Comment 6 Richard Biener 2017-08-21 10:16:17 UTC
Ah ...

@@ -2396,9 +2446,6 @@ compute_antic (void)
        if (e->flags & EDGE_ABNORMAL)
          {
            bitmap_set_bit (has_abnormal_preds, block->index);
-
-           /* We also anticipate nothing.  */
-           BB_VISITED (block) = 1;
            break;
          }
Comment 7 pipcet 2017-08-21 12:11:35 UTC
I can confirm that fixes things here, thank you!
Comment 8 Richard Biener 2017-08-21 13:18:56 UTC
Fixed.
Comment 9 Richard Biener 2017-08-21 13:19:12 UTC
Author: rguenth
Date: Mon Aug 21 13:18:35 2017
New Revision: 251226

URL: https://gcc.gnu.org/viewcvs?rev=251226&root=gcc&view=rev
Log:
2017-08-21  Richard Biener  <rguenther@suse.de>

	PR tree-optimization/81900
	* tree-ssa-pre.c (compute_antic_aux): Properly compute changed
	for blocks with abnormal predecessors.
	(compute_antic): Do not set visited flag prematurely.

	* gcc.dg/torture/pr81900.c: New testcase.

Added:
    trunk/gcc/testsuite/gcc.dg/torture/pr81900.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/testsuite/ChangeLog
    trunk/gcc/tree-ssa-pre.c
Comment 10 Yui NARUSE 2017-08-28 15:34:54 UTC
*** Bug 81954 has been marked as a duplicate of this bug. ***
Comment 11 Aldy Hernandez 2017-09-13 17:14:39 UTC
Author: aldyh
Date: Wed Sep 13 17:14:07 2017
New Revision: 252505

URL: https://gcc.gnu.org/viewcvs?rev=252505&root=gcc&view=rev
Log:
2017-08-21  Richard Biener  <rguenther@suse.de>

	PR tree-optimization/81900
	* tree-ssa-pre.c (compute_antic_aux): Properly compute changed
	for blocks with abnormal predecessors.
	(compute_antic): Do not set visited flag prematurely.

	* gcc.dg/torture/pr81900.c: New testcase.

Added:
    branches/range-gen2/gcc/testsuite/gcc.dg/torture/pr81900.c
Modified:
    branches/range-gen2/gcc/ChangeLog
    branches/range-gen2/gcc/testsuite/ChangeLog
    branches/range-gen2/gcc/tree-ssa-pre.c