[PATCH] Fix ICE in dwarf2out's mem_loc_descriptor (PR middle-end/37275)

Jakub Jelinek jakub@redhat.com
Mon Sep 8 12:25:00 GMT 2008


Hi!

The following testcase ICEs on i686, because a read-only variable
which just reads a field from __thread aggregate is equivalenced to
the field in that aggregate and so it's DECL_RTL is:
(mem/s/f/c:SI (plus:SI (plus:SI (unspec:SI [
                    (const_int 0 [0x0])
                ] 18)
            (reg:SI 3 bx [122]))
        (const_int 16 [0x10])) [3 thr.t+0 S4 A32])
where
(insn 13 241 10 task.i:101 (set (reg:SI 3 bx [122])
        (mem/u/c:SI (const:SI (unspec:SI [
                        (symbol_ref:SI ("thr") [flags 0x62] <var_decl 0x7ffff3223320 thr>)
                    ] 8)) [17 S4 A8])) 47 {*movsi_1} (expr_list:REG_EQUIV (mem/u/c:SI (const:SI (unspec:SI [
                        (symbol_ref:SI ("thr") [flags 0x62] <var_decl 0x7ffff3223320 thr>)
                    ] 8)) [17 S4 A8])
        (nil)))
insn preceedes all uses of that variable.  Unfortunately,
targetm.delegitimize_address can't help in this case, as
it doesn't know what %ebx contains and without looking that up
it can't form a DW_OP_GNU_push_tls_address (we'd need some
relocation which would give us negative offset of first byte
of that library's TLS block within the whole program's TLS block).
So, this patch does accept UNSPEC in mem_loc_descriptor (returning
NULL aka unknown).  I've noticed a bunch of places where a recursive
{,mem_}loc_descriptor{,_from_tree_1} call could return NULL, but the
caller doesn't bother checking it and happily creates invalid debug info and
fixed that too.  Additionally, I've added a function that handles these
TLS addresses using MEM_EXPR/MEM_OFFSET if they are available.

Ok for trunk?

2008-09-08  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/37275
	* dwarf2out.c (tls_mem_loc_descriptor): New function.
	(mem_loc_descriptor): Use it for MEM.  For PLUS fail if second
	mem_loc_descriptor failed.  Accept UNSPEC.
	(loc_descriptor): Use tls_mem_loc_descriptor for MEM.  For PARALLEL
	fail if one of the loc_descriptor calls for pieces failed.
	(loc_descriptor_from_tree_1): Handle even DECL_EXTERNAL __thread vars,
	as long as they bind locally.  For COMPONENT_REF, ARRAY_REF etc. fail
	if loc_descriptor_from_tree_1 on offset failed.

	* gcc.target/i386/pr37275.c: New test.

--- gcc/dwarf2out.c.jj	2008-09-05 12:56:32.000000000 +0200
+++ gcc/dwarf2out.c	2008-09-08 11:54:22.000000000 +0200
@@ -9887,6 +9887,48 @@ concatn_mem_loc_descriptor (rtx concatn,
   return cc_loc_result;
 }
 
+/* Try to handle TLS MEMs, for which mem_loc_descriptor on XEXP (mem, 0)
+   failed.  */
+
+static dw_loc_descr_ref
+tls_mem_loc_descriptor (rtx mem)
+{
+  tree base;
+  dw_loc_descr_ref loc_result, loc_result2;
+
+  if (MEM_EXPR (mem) == NULL_TREE || MEM_OFFSET (mem) == NULL_RTX)
+    return NULL;
+
+  base = get_base_address (MEM_EXPR (mem));
+  if (base == NULL
+      || TREE_CODE (base) != VAR_DECL
+      || !DECL_THREAD_LOCAL_P (base))
+    return NULL;
+
+  loc_result = loc_descriptor_from_tree_1 (MEM_EXPR (mem), 2);
+  if (loc_result == NULL)
+    return NULL;
+
+  if (INTVAL (MEM_OFFSET (mem)))
+    {
+      if (INTVAL (MEM_OFFSET (mem)) >= 0)
+	add_loc_descr (&loc_result,
+		       new_loc_descr (DW_OP_plus_uconst,
+				      INTVAL (MEM_OFFSET (mem)), 0));
+      else
+	{
+	  loc_result2 = mem_loc_descriptor (MEM_OFFSET (mem), GET_MODE (mem),
+					    VAR_INIT_STATUS_INITIALIZED);
+	  if (loc_result2 == 0)
+	    return NULL;
+	  add_loc_descr (&loc_result, loc_result2);
+	  add_loc_descr (&loc_result, new_loc_descr (DW_OP_plus, 0, 0));
+	}
+    }
+
+  return loc_result;
+}
+
 /* The following routine converts the RTL for a variable or parameter
    (resident in memory) into an equivalent Dwarf representation of a
    mechanism for getting the address of that same variable onto the top of a
@@ -9960,6 +10002,8 @@ mem_loc_descriptor (rtx rtl, enum machin
     case MEM:
       mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
 					   VAR_INIT_STATUS_INITIALIZED);
+      if (mem_loc_result == NULL)
+	mem_loc_result = tls_mem_loc_descriptor (rtl);
       if (mem_loc_result != 0)
 	add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0));
       break;
@@ -10042,9 +10086,12 @@ mem_loc_descriptor (rtx rtl, enum machin
 					  INTVAL (XEXP (rtl, 1)), 0));
 	  else
 	    {
-	      add_loc_descr (&mem_loc_result,
-			     mem_loc_descriptor (XEXP (rtl, 1), mode,
-						 VAR_INIT_STATUS_INITIALIZED));
+	      dw_loc_descr_ref mem_loc_result2
+		= mem_loc_descriptor (XEXP (rtl, 1), mode,
+				      VAR_INIT_STATUS_INITIALIZED);
+	      if (mem_loc_result2 == 0)
+		break;
+	      add_loc_descr (&mem_loc_result, mem_loc_result2);
 	      add_loc_descr (&mem_loc_result,
 			     new_loc_descr (DW_OP_plus, 0, 0));
 	    }
@@ -10094,6 +10141,12 @@ mem_loc_descriptor (rtx rtl, enum machin
 						   VAR_INIT_STATUS_INITIALIZED);
       break;
 
+    case UNSPEC:
+      /* If delegitimize_address couldn't do anything with the UNSPEC, we
+	 can't express it in the debug info.  This can happen e.g. with some
+	 TLS UNSPECs.  */
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -10190,6 +10243,8 @@ loc_descriptor (rtx rtl, enum var_init_s
     case MEM:
       loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
 				       initialized);
+      if (loc_result == NULL)
+	loc_result = tls_mem_loc_descriptor (rtl);
       break;
 
     case CONCAT:
@@ -10222,6 +10277,8 @@ loc_descriptor (rtx rtl, enum var_init_s
 	/* Create the first one, so we have something to add to.  */
 	loc_result = loc_descriptor (XEXP (RTVEC_ELT (par_elems, 0), 0),
 				     initialized);
+	if (loc_result == NULL)
+	  return NULL;
 	mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, 0), 0));
 	add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode));
 	for (i = 1; i < num_elem; i++)
@@ -10230,6 +10287,8 @@ loc_descriptor (rtx rtl, enum var_init_s
 
 	    temp = loc_descriptor (XEXP (RTVEC_ELT (par_elems, i), 0),
 				   initialized);
+	    if (temp == NULL)
+	      return NULL;
 	    add_loc_descr (&loc_result, temp);
 	    mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, i), 0));
 	    add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode));
@@ -10308,7 +10367,7 @@ loc_descriptor_from_tree_1 (tree loc, in
 	       /* The way DW_OP_GNU_push_tls_address is specified, we
 	     	  can only look up addresses of objects in the current
 	     	  module.  */
-	      if (DECL_EXTERNAL (loc))
+	      if (DECL_EXTERNAL (loc) && !targetm.binds_local_p (loc))
 		return 0;
 	      first_op = INTERNAL_DW_OP_tls_addr;
 	      second_op = DW_OP_GNU_push_tls_address;
@@ -10430,7 +10489,10 @@ loc_descriptor_from_tree_1 (tree loc, in
 	if (offset != NULL_TREE)
 	  {
 	    /* Variable offset.  */
-	    add_loc_descr (&ret, loc_descriptor_from_tree_1 (offset, 0));
+	    ret1 = loc_descriptor_from_tree_1 (offset, 0);
+	    if (ret1 == 0)
+	      return 0;
+	    add_loc_descr (&ret, ret1);
 	    add_loc_descr (&ret, new_loc_descr (DW_OP_plus, 0, 0));
 	  }
 
--- gcc/testsuite/gcc.target/i386/pr37275.c.jj	2008-09-08 12:50:54.000000000 +0200
+++ gcc/testsuite/gcc.target/i386/pr37275.c	2008-09-08 12:50:15.000000000 +0200
@@ -0,0 +1,137 @@
+/* PR middle-end/37275 */
+/* { dg-do compile { target ilp32 } } */
+/* { dg-options "-g -dA -O2 -march=i686 -fstack-protector" } */
+
+typedef __SIZE_TYPE__ size_t;
+extern void *memcpy (void *, const void *, size_t);
+extern void *malloc (size_t);
+
+typedef int A;
+
+struct B
+{
+  int x;
+};
+
+struct C
+{
+  struct F *c1;
+  void *c2;
+};
+
+enum D
+{
+  D0,
+  D1
+};
+
+struct E
+{
+  struct E *e1;
+  struct E *e2;
+  struct B e3;
+  void (*fn) (void *);
+  void *fn_data;
+  enum D e4;
+  _Bool e5;
+  _Bool e6;
+};
+
+struct F
+{
+  unsigned f1;
+  A f2;
+  int f3;
+};
+
+struct G
+{
+  void (*fn) (void *data);
+  void *data;
+  struct C g1;
+  struct E *t;
+};
+
+extern void fn1 (A * m);
+static inline void
+fn2 (A *x)
+{
+  if (!__sync_bool_compare_and_swap (x, 0, 1))
+    fn1 (x);
+}
+
+extern __thread struct G thr __attribute__ ((visibility ("hidden")));
+static inline struct G *
+fn3 (void)
+{
+  return &thr;
+}
+
+extern struct B *fn4 (void);
+extern struct B a;
+
+static inline struct B *
+fn5 (_Bool x)
+{
+  struct E *t = fn3 ()->t;
+  if (t)
+    return &t->e3;
+  else if (x)
+    return fn4 ();
+  else
+    return &a;
+}
+
+void
+fn6 (struct E *t, struct E *e1_t,
+		struct B *prev_e3)
+{
+  t->e1 = e1_t;
+  t->e3 = *prev_e3;
+  t->e4 = D0;
+  t->e5 = 0;
+  t->e6 = 0;
+  t->e2 = ((void *) 0);
+}
+
+void
+test (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *), long x, long y, _Bool z)
+{
+  struct G *thr = fn3 ();
+  struct F *c1 = thr->g1.c1;
+  if (!z || c1 == 0 || (unsigned) c1->f3 > 64 * c1->f1)
+    {
+      struct E t;
+
+      fn6 (&t, thr->t, fn5 (0));
+      if (thr->t)
+	t.e6 = thr->t->e6;
+      thr->t = &t;
+      if (__builtin_expect (cpyfn != ((void *) 0), 0))
+	{
+	  char buf[x + y - 1];
+	  char *arg = (char *) (((unsigned long) buf + y - 1)
+				& ~(unsigned long) (y - 1));
+	  cpyfn (arg, data);
+	  fn (arg);
+	}
+    }
+  else
+    {
+      struct E *t;
+      struct E *e1 = thr->t;
+      char *arg;
+
+      t = malloc (sizeof (*t) + x + y - 1);
+      arg = (char *) (((unsigned long) (t + 1) + y - 1)
+		      & ~(unsigned long) (y - 1));
+      fn6 (t, e1, fn5 (0));
+      thr->t = t;
+      if (cpyfn)
+	cpyfn (arg, data);
+      else
+	memcpy (arg, data, x);
+      thr->t = e1;
+      fn2 (&c1->f2);
+    }
+}

	Jakub



More information about the Gcc-patches mailing list