Fix ICE during RTL CFI generation pass

Message ID 1634069.gzBkOT923G@polaris
State New
Headers show
Series
  • Fix ICE during RTL CFI generation pass
Related show

Commit Message

Eric Botcazou Oct. 13, 2018, 5:32 p.m.
This fixes an assertion failure compiling the Ada Web Server package (soap/
soap-message-xml.adb to be precise) at -O2 -gnatn -fPIC on x86/Linux:

+===========================GNAT BUG DETECTED==============================+
| Pro 20.0w (20181010-90) (i686-pc-linux-gnu) GCC error:                   |
| in connect_traces, at dwarf2cfi.c:2825                                   |
| Error detected around /byram.a/gnatmail/sandbox/gcc-head/x86-linux/      |
| aws_bootstrap-gcc-head/src/src/soap/soap-message-xml.adb:914:8           |

The ICE is extremely elusive and all my attempts at reducing the code failed.

The assertion is meant to check that, if a trace contains an insn that can 
throw internally, then the incoming args_size values from preceding traces are 
all the same.  That's not the case here because cross-jumping commonalized an 
insn restoring the stack pointer from the frame pointer, i.e setting args_size 
to 0, and redirected a jump to just before the insn.  Now, before this insn, 
the args_size values need not match, for example if you have on one path a 
call whose post-call stack adjustments have not been emitted (because the 
stack pointer is restored just after in any case) and on another path a call 
whose post-call stack adjustments have been emitted as usual.

So the attached patch relaxes the assertion by allowing the case where an insn 
sets the args_size in the current trace before the first insn that can throw 
internally.  That's OK since DW_CFA_GNU_args_size notes are emitted only on 
this first insn or after it, and not at the start of the trace.

Tested on x86/Linux and x86-64/Linux, applied on the mainline.


2018-10-13  Eric Botcazou  <ebotcazou@adacore.com>

	* dwarf2cfi.c (struct dw_trace_info): Add args_size_defined_for_eh.
	(notice_args_size): Set it in the current trace if no insn that can
	throw internally has been seen yet.
	(connect_traces): When connecting args_size between traces, allow the
	incoming values not to match if there is an insn setting it before the
	first insn that can throw internally; in that case, force the creation
	of a CFI note on this latter insn.

-- 
Eric Botcazou

Patch

Index: dwarf2cfi.c
===================================================================
--- dwarf2cfi.c	(revision 264986)
+++ dwarf2cfi.c	(working copy)
@@ -147,6 +147,9 @@  struct dw_trace_info
 
   /* True if we've seen different values incoming to beg_true_args_size.  */
   bool args_size_undefined;
+
+  /* True if we've seen an insn with a REG_ARGS_SIZE note before EH_HEAD.  */
+  bool args_size_defined_for_eh;
 };
 
 
@@ -942,6 +945,9 @@  notice_args_size (rtx_insn *insn)
   if (note == NULL)
     return;
 
+  if (!cur_trace->eh_head)
+    cur_trace->args_size_defined_for_eh = true;
+
   args_size = get_args_size (note);
   delta = args_size - cur_trace->end_true_args_size;
   if (known_eq (delta, 0))
@@ -2820,11 +2826,17 @@  connect_traces (void)
 
 	  if (ti->switch_sections)
 	    prev_args_size = 0;
+
 	  if (ti->eh_head == NULL)
 	    continue;
-	  gcc_assert (!ti->args_size_undefined);
 
-	  if (maybe_ne (ti->beg_delay_args_size, prev_args_size))
+	  /* We require either the incoming args_size values to match or the
+	     presence of an insn setting it before the first EH insn.  */
+	  gcc_assert (!ti->args_size_undefined || ti->args_size_defined_for_eh);
+
+	  /* In the latter case, we force the creation of a CFI note.  */
+	  if (ti->args_size_undefined
+	      || maybe_ne (ti->beg_delay_args_size, prev_args_size))
 	    {
 	      /* ??? Search back to previous CFI note.  */
 	      add_cfi_insn = PREV_INSN (ti->eh_head);