diff mbox

ENTRY_VALUE fixes (PR debug/48203)

Message ID 20110320125738.GQ30899@tyan-ft48-01.lab.bos.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek March 20, 2011, 12:57 p.m. UTC
Hi!

This patch fixes a few problems:
1) on arm bootstrap fails, because expansion creates ENTRY_VALUE
   of a MEM with virtual reg address, which then is changed for
   ap + constant and dwarf2out doesn't expect that.
   As in most passes ENTRY_VALUE is treated like a black box,
   we shouldn't accept anything but hard registers or
   MEMs with hard register addresses
2) DW_OP_GNU_entry_value 2 <DW_OP_fbreg 16> is something we should
   avoid
3) the var-tracking and cselib changes were added to make sure
   var-tracking can easily map ENTRY_VALUEs created by cfgexpand
   back to the canonical VALUEs holding the parameter.  This means
   they can expand even to the original argument registers if they
   haven't been modified or at least in the part of the function
   where they haven't been modified.  E.g. on x86_64-linux
   on the attached testcase we no longer generate any DW_OP_GNU_entry_value
   ops, as the registers aren't clobbered.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2011-03-20  Jakub Jelinek  <jakub@redhat.com>

	PR debug/48203
	* cfgexpand.c (expand_debug_expr) <case SSA_NAME>: Only
	create ENTRY_VALUE if incoming or address of incoming's MEM
	is a hard REG.
	* dwarf2out.c (mem_loc_descriptor): Don't emit
	DW_OP_GNU_entry_value of DW_OP_fbreg.
	* var-tracking.c (vt_add_function_parameter): Ensure cselib_lookup
	on ENTRY_VALUE is able to find the canonical parameter VALUE.
	* cselib.c (rtx_equal_for_cselib_1) <case ENTRY_VALUE>: Use
	rtx_equal_p instead of rtx_equal_for_cselib_1 to compare
	ENTRY_VALUE_EXPs.
	(cselib_hash_rtx) <case ENTRY_VALUE>: If ENTRY_VALUE_EXP
	is a REG_P or MEM_P with REG_P address, compute hash directly
	instead of calling cselib_hash_rtx on ENTRY_VALUE_EXP.
	(preserve_only_constants): Don't clear VALUES forwaring
	ENTRY_VALUE to some other VALUE.

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


	Jakub

Comments

Richard Henderson March 28, 2011, 4:58 p.m. UTC | #1
On 03/20/2011 05:57 AM, Jakub Jelinek wrote:
> 	* cfgexpand.c (expand_debug_expr) <case SSA_NAME>: Only
> 	create ENTRY_VALUE if incoming or address of incoming's MEM
> 	is a hard REG.
> 	* dwarf2out.c (mem_loc_descriptor): Don't emit
> 	DW_OP_GNU_entry_value of DW_OP_fbreg.

Ok.

> 	* var-tracking.c (vt_add_function_parameter): Ensure cselib_lookup
> 	on ENTRY_VALUE is able to find the canonical parameter VALUE.

I don't really understand what's going on here.  Whatever it is, it
could definitely use some more commentary; there's almost nothing in
the surrounding context.  I also suggest pulling some of this out into
a new function.  You've got 2 exact copies here in the patch, and 2
more that are nearly the same already in the code.

> 	* cselib.c (rtx_equal_for_cselib_1) <case ENTRY_VALUE>: Use
> 	rtx_equal_p instead of rtx_equal_for_cselib_1 to compare
> 	ENTRY_VALUE_EXPs.

Ok.

> 	(cselib_hash_rtx) <case ENTRY_VALUE>: If ENTRY_VALUE_EXP
> 	is a REG_P or MEM_P with REG_P address, compute hash directly
> 	instead of calling cselib_hash_rtx on ENTRY_VALUE_EXP.

Why?

> 	(preserve_only_constants): Don't clear VALUES forwaring
> 	ENTRY_VALUE to some other VALUE.

Ok with a comment.  I guess the reasoning being that even though this
value isn't "constant", it's function invariant?


r~
Jakub Jelinek March 28, 2011, 5:32 p.m. UTC | #2
On Mon, Mar 28, 2011 at 09:58:38AM -0700, Richard Henderson wrote:
> > 	* var-tracking.c (vt_add_function_parameter): Ensure cselib_lookup
> > 	on ENTRY_VALUE is able to find the canonical parameter VALUE.
> 
> I don't really understand what's going on here.  Whatever it is, it
> could definitely use some more commentary; there's almost nothing in
> the surrounding context.  I also suggest pulling some of this out into
> a new function.  You've got 2 exact copies here in the patch, and 2
> more that are nearly the same already in the code.

As cfgexpand.c creates ENTRY_VALUEs (where the first hunk of this patch
applies), when vt_initialization uses cselib to lookup those ENTRY_VALUEs,
without the patch it will find a newly created cselib_val whose
only location is the ENTRY_VALUE.  Which means it will always use
DW_OP_GNU_entry_value in the end.  Sometimes that is the only usable choice,
but e.g. the first function in gcc.dg/pr48203.c shows we can do better,
because all the entry values are also held in the corresponding registers
(the function doesn't clobber them), so we can instead of
DW_OP_GNU_entry_value just refer to the registers itself, either in the
whole function, or at least as long as the aren't clobbered by something
else.  The extra cselib_lookup calls give us those values that hash
as those ENTRY_VALUE will hash and by telling that the value is live
in the parameter's VALUE vt_expand_loc_callback will find it.

Say vt_add_function_parameter is called for first parameter in %rdi,
cselib_lookup_from_insn gives us VALUE 2:2 for it.  We add
(entry_value:DI (reg:DI %rdi)) to list of locations for that VALUE.
The second cselib_lookup_from_insn gives us VALUE 3:217, which will
have locations (value:DI 2:2) (the one we've injected there) and
(entry_value:DI (reg:DI %rdi)).  Then when (entry_value:DI (reg:DI %rdi))
appears in some DEBUG_INSNs, it will give (value:DI 3:217)
and vt_expand_loc_callback will use the first location for it
(i.e. (value:DI 2:2) ) and either it will see the value is still live
in %rdi, some other register or memory, or, if nowhere else, in
(entry_value:DI (reg:DI %rdi)).  The 3 cselib.c changes from this
patch ensure that the hash value for (entry_value:DI (reg:DI %rdi))
will always be the same and (value:DI 3:217) will never be flushed from
the hash table, even when e.g. on next bb's boundary we flush
all registers from the hash table, or e.g. when movl $123, %edi
is seen in the insns.

I could of course create another hash table and map ENTRY_VALUEs through
that hash table back to the parameter VALUEs, but cselib already has
a hash table which we can use and furthermore the ENTRY_VALUEs could
be embedded in other RTLs.

I will look into creating helper inlines to reduce code duplication.

> > 	* cselib.c (rtx_equal_for_cselib_1) <case ENTRY_VALUE>: Use
> > 	rtx_equal_p instead of rtx_equal_for_cselib_1 to compare
> > 	ENTRY_VALUE_EXPs.
> 
> Ok.
> 
> > 	(cselib_hash_rtx) <case ENTRY_VALUE>: If ENTRY_VALUE_EXP
> > 	is a REG_P or MEM_P with REG_P address, compute hash directly
> > 	instead of calling cselib_hash_rtx on ENTRY_VALUE_EXP.
> 
> Why?

Because REG and MEM hash based on their current value in cselib:
    case MEM:
    case REG:
      e = cselib_lookup (x, GET_MODE (x), create, memmode);
      if (! e)
        return 0;

      return e->hash;
In ENTRY_VALUEs which are function invariant I want it to hash always
the same.

> > 	(preserve_only_constants): Don't clear VALUES forwaring
> > 	ENTRY_VALUE to some other VALUE.
> 
> Ok with a comment.  I guess the reasoning being that even though this
> value isn't "constant", it's function invariant?

Yeah.  Perhaps preserve_only_constants could be renamed
to preserve_only_function_invariants or similar (and likewise for
cselib_preserve_constants and CSELIB_PRESERVE_CONSTANTS).

	Jakub
Richard Henderson March 28, 2011, 5:50 p.m. UTC | #3
On 03/28/2011 10:32 AM, Jakub Jelinek wrote:
> Say vt_add_function_parameter is called for first parameter in %rdi,
> cselib_lookup_from_insn gives us VALUE 2:2 for it.  We add
> (entry_value:DI (reg:DI %rdi)) to list of locations for that VALUE.
> The second cselib_lookup_from_insn gives us VALUE 3:217, which will
> have locations (value:DI 2:2) (the one we've injected there) and
> (entry_value:DI (reg:DI %rdi)).  Then when (entry_value:DI (reg:DI %rdi))
> appears in some DEBUG_INSNs, it will give (value:DI 3:217)
> and vt_expand_loc_callback will use the first location for it
> (i.e. (value:DI 2:2) ) and either it will see the value is still live
> in %rdi, some other register or memory, or, if nowhere else, in
> (entry_value:DI (reg:DI %rdi)).  The 3 cselib.c changes from this
> patch ensure that the hash value for (entry_value:DI (reg:DI %rdi))
> will always be the same and (value:DI 3:217) will never be flushed from
> the hash table, even when e.g. on next bb's boundary we flush
> all registers from the hash table, or e.g. when movl $123, %edi
> is seen in the insns.

Ok, thanks for the explanation.  All the changes make sense now.

> I will look into creating helper inlines to reduce code duplication.

Please.  You can do this as a follow-up if you prefer.

>>> 	(cselib_hash_rtx) <case ENTRY_VALUE>: If ENTRY_VALUE_EXP
>>> 	is a REG_P or MEM_P with REG_P address, compute hash directly
>>> 	instead of calling cselib_hash_rtx on ENTRY_VALUE_EXP.
>>
>> Why?
> 
> Because REG and MEM hash based on their current value in cselib:
>     case MEM:
>     case REG:
>       e = cselib_lookup (x, GET_MODE (x), create, memmode);
>       if (! e)
>         return 0;
> 
>       return e->hash;
> In ENTRY_VALUEs which are function invariant I want it to hash always
> the same.

Ok, with something akin to this as a comment.


r~
diff mbox

Patch

--- gcc/cfgexpand.c.jj	2011-03-17 09:37:59.000000000 +0100
+++ gcc/cfgexpand.c	2011-03-19 17:45:32.000000000 +0100
@@ -3182,8 +3182,10 @@  expand_debug_expr (tree exp)
 		    rtx incoming = DECL_INCOMING_RTL (SSA_NAME_VAR (exp));
 		    if (incoming
 			&& GET_MODE (incoming) != BLKmode
-			&& (REG_P (incoming)
-			    || (MEM_P (incoming) && REG_P (XEXP (incoming, 0)))))
+			&& ((REG_P (incoming) && HARD_REGISTER_P (incoming))
+			    || (MEM_P (incoming)
+				&& REG_P (XEXP (incoming, 0))
+				&& HARD_REGISTER_P (XEXP (incoming, 0)))))
 		      {
 			op0 = gen_rtx_ENTRY_VALUE (GET_MODE (incoming));
 			ENTRY_VALUE_EXP (op0) = incoming;
--- gcc/dwarf2out.c.jj	2011-03-19 17:28:32.000000000 +0100
+++ gcc/dwarf2out.c	2011-03-19 17:48:42.000000000 +0100
@@ -13877,7 +13877,7 @@  mem_loc_descriptor (rtx rtl, enum machin
 	  dw_loc_descr_ref ref
 	    = mem_loc_descriptor (ENTRY_VALUE_EXP (rtl), GET_MODE (rtl),
 				  VAR_INIT_STATUS_INITIALIZED);
-	  if (ref == NULL)
+	  if (ref == NULL || ref->dw_loc_opc == DW_OP_fbreg)
 	    return NULL;
 	  mem_loc_result->dw_loc_oprnd1.v.val_loc = ref;
 	}
--- gcc/var-tracking.c.jj	2011-03-19 16:20:26.000000000 +0100
+++ gcc/var-tracking.c	2011-03-19 18:49:48.000000000 +0100
@@ -8472,7 +8472,7 @@  vt_add_function_parameter (tree parm)
 			 VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
       if (dv_is_value_p (dv))
 	{
-	  cselib_val *val = CSELIB_VAL_PTR (dv_as_value (dv));
+	  cselib_val *val = CSELIB_VAL_PTR (dv_as_value (dv)), *val2;
 	  struct elt_loc_list *el;
 	  el = (struct elt_loc_list *)
 	    ggc_alloc_cleared_atomic (sizeof (*el));
@@ -8481,6 +8481,23 @@  vt_add_function_parameter (tree parm)
 	  ENTRY_VALUE_EXP (el->loc) = incoming;
 	  el->setting_insn = get_insns ();
 	  val->locs = el;
+	  val2 = cselib_lookup_from_insn (el->loc, GET_MODE (incoming),
+					  true, VOIDmode, get_insns ());
+	  if (val2
+	      && val2 != val
+	      && val2->locs
+	      && rtx_equal_p (val2->locs->loc, el->loc))
+	    {
+	      struct elt_loc_list *el2;
+
+	      preserve_value (val2);
+	      el2 = (struct elt_loc_list *)
+		ggc_alloc_cleared_atomic (sizeof (*el2));
+	      el2->next = val2->locs;
+	      el2->loc = dv_as_value (dv);
+	      el2->setting_insn = get_insns ();
+	      val2->locs = el2;
+	    }
 	  if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE
 	      && INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (parm))))
 	    {
@@ -8499,6 +8516,24 @@  vt_add_function_parameter (tree parm)
 		  ENTRY_VALUE_EXP (el->loc) = mem;
 		  el->setting_insn = get_insns ();
 		  val->locs = el;
+		  val2 = cselib_lookup_from_insn (el->loc, GET_MODE (incoming),
+						  true, VOIDmode,
+						  get_insns ());
+		  if (val2
+		      && val2 != val
+		      && val2->locs
+		      && rtx_equal_p (val2->locs->loc, el->loc))
+		    {
+		      struct elt_loc_list *el2;
+
+		      preserve_value (val2);
+		      el2 = (struct elt_loc_list *)
+			ggc_alloc_cleared_atomic (sizeof (*el2));
+		      el2->next = val2->locs;
+		      el2->loc = val->val_rtx;
+		      el2->setting_insn = get_insns ();
+		      val2->locs = el2;
+		    }
 		}
 	    }
 	}
--- gcc/cselib.c.jj	2011-03-16 18:30:12.000000000 +0100
+++ gcc/cselib.c	2011-03-19 19:58:54.000000000 +0100
@@ -329,6 +329,12 @@  preserve_only_constants (void **x, void 
 	    return 1;
 	}
     }
+  if (v->locs != NULL
+      && v->locs->next != NULL
+      && v->locs->next->next == NULL
+      && GET_CODE (v->locs->next->loc) == ENTRY_VALUE
+      && GET_CODE (v->locs->loc) == VALUE)
+    return 1;
 
   htab_clear_slot (cselib_hash_table, x);
   return 1;
@@ -804,8 +810,7 @@  rtx_equal_for_cselib_1 (rtx x, rtx y, en
 	     == DEBUG_IMPLICIT_PTR_DECL (y);
 
     case ENTRY_VALUE:
-      return rtx_equal_for_cselib_1 (ENTRY_VALUE_EXP (x), ENTRY_VALUE_EXP (y),
-				     memmode);
+      return rtx_equal_p (ENTRY_VALUE_EXP (x), ENTRY_VALUE_EXP (y));
 
     case LABEL_REF:
       return XEXP (x, 0) == XEXP (y, 0);
@@ -954,7 +959,17 @@  cselib_hash_rtx (rtx x, int create, enum
       return hash ? hash : (unsigned int) DEBUG_IMPLICIT_PTR;
 
     case ENTRY_VALUE:
-      hash += cselib_hash_rtx (ENTRY_VALUE_EXP (x), create, memmode);
+      if (REG_P (ENTRY_VALUE_EXP (x)))
+	hash += (unsigned int) REG
+		+ (unsigned int) GET_MODE (ENTRY_VALUE_EXP (x))
+		+ (unsigned int) REGNO (ENTRY_VALUE_EXP (x));
+      else if (MEM_P (ENTRY_VALUE_EXP (x))
+	       && REG_P (XEXP (ENTRY_VALUE_EXP (x), 0)))
+	hash += (unsigned int) MEM
+		+ (unsigned int) GET_MODE (XEXP (ENTRY_VALUE_EXP (x), 0))
+		+ (unsigned int) REGNO (XEXP (ENTRY_VALUE_EXP (x), 0));
+      else
+	hash += cselib_hash_rtx (ENTRY_VALUE_EXP (x), create, memmode);
       return hash ? hash : (unsigned int) ENTRY_VALUE;
 
     case CONST_INT:
--- gcc/testsuite/gcc.dg/pr48203.c.jj	2011-03-19 18:06:29.000000000 +0100
+++ gcc/testsuite/gcc.dg/pr48203.c	2011-03-19 18:06:01.000000000 +0100
@@ -0,0 +1,51 @@ 
+/* PR debug/48203 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -g" } */
+
+volatile int v;
+
+void
+foo (long a, long b, long c, long d, long e, long f, long g, long h,
+     long i, long j, long k, long l, long m, long n, long o, long p)
+{
+  long a2 = a;
+  long b2 = b;
+  long c2 = c;
+  long d2 = d;
+  long e2 = e;
+  long f2 = f;
+  long g2 = g;
+  long h2 = h;
+  long i2 = i;
+  long j2 = j;
+  long k2 = k;
+  long l2 = l;
+  long m2 = m;
+  long n2 = n;
+  long o2 = o;
+  long p2 = p;
+  v++;
+}
+
+void
+bar (int a, int b, int c, int d, int e, int f, int g, int h,
+     int i, int j, int k, int l, int m, int n, int o, int p)
+{
+  int a2 = a;
+  int b2 = b;
+  int c2 = c;
+  int d2 = d;
+  int e2 = e;
+  int f2 = f;
+  int g2 = g;
+  int h2 = h;
+  int i2 = i;
+  int j2 = j;
+  int k2 = k;
+  int l2 = l;
+  int m2 = m;
+  int n2 = n;
+  int o2 = o;
+  int p2 = p;
+  v++;
+}