diff mbox

[7/7] ira.c validate_equiv_mem

Message ID 20160321014311.GJ22605@bubble.grove.modra.org
State New
Headers show

Commit Message

Alan Modra March 21, 2016, 1:43 a.m. UTC
This function is used to validate REG_EQUIV notes generated by ira,
and to validate potential insn combines performed by ira.  The two
conditions are not exactly the same, with reload being more
restrictive.  Separate them so more combines/moves can occur.

For example, this sequence from cfgexpand.c:expand_gimple_cond
	callq  _Z18update_bb_for_insnP15basic_block_def
	mov    0x10(%rbx),%rdi
	mov    0x0(%rip),%rbp        # x_rtl+0x34
	callq  _Z9safe_as_aIP8rtx_insn7rtx_defET_PT0_
	mov    %r13,%rdx
	mov    %rbp,%rsi
	mov    %rax,%rdi
	callq  _Z18create_basic_blockP7rtx_defS0_P15basic_block_def

becomes
	callq  _Z18update_bb_for_insnP15basic_block_def
	mov    0x10(%rbx),%rdi
	callq  _Z9safe_as_aIP8rtx_insn7rtx_defET_PT0_
	mov    0x0(%rip),%rsi        # x_rtl+0x34
	mov    %r13,%rdx
	mov    %rax,%rdi
	callq  _Z18create_basic_blockP7rtx_defS0_P15basic_block_def


	* ira.c (enum valid_equiv): New.
	(validate_equiv_mem): Return enum.
	(update_equiv_mem): Create replacement in more cases.
	(add_store_equivs): Update validate_equiv_mem call.

Comments

Bernd Schmidt March 23, 2016, 12:03 a.m. UTC | #1
On 03/21/2016 02:43 AM, Alan Modra wrote:
>
> +enum valid_equiv { valid_none, valid_combine, valid_reload };
> +

Might be worth documenting that each step represents a superset of the 
previous one.

> +	  ret = valid_combine;
> +	  if (! MEM_READONLY_P (memref)
> +	      && ! RTL_CONST_OR_PURE_CALL_P (insn))
> +	    return valid_none;
> +	}

The gcc style is actually not to have a space after unary "!". None of 
the code in this file follows that, but I think you may want to change 
that as you modify things in your patches, and have new code follow the 
recommended style.

> @@ -3536,7 +3557,8 @@ update_equiv_regs (void)
>   		{
>   		  /* Note that the statement below does not affect the priority
>   		     in local-alloc!  */
> -		  REG_LIVE_LENGTH (regno) *= 2;
> +		  if (note)
> +		    REG_LIVE_LENGTH (regno) *= 2;

That's a very suspicious comment. It would be worth testing whether 
REG_LIVE_LENGTH has any effect on our current register allocation at 
all, and remove this code if not.

Otherwise looks good for stage 1.


Bernd
Alan Modra March 23, 2016, 10:50 p.m. UTC | #2
On Wed, Mar 23, 2016 at 01:03:18AM +0100, Bernd Schmidt wrote:
> On 03/21/2016 02:43 AM, Alan Modra wrote:
> >
> >+enum valid_equiv { valid_none, valid_combine, valid_reload };
> >+
> 
> Might be worth documenting that each step represents a superset of the
> previous one.

Fixed.

> >+	  ret = valid_combine;
> >+	  if (! MEM_READONLY_P (memref)
> >+	      && ! RTL_CONST_OR_PURE_CALL_P (insn))
> >+	    return valid_none;
> >+	}
> 
> The gcc style is actually not to have a space after unary "!". None of the
> code in this file follows that, but I think you may want to change that as
> you modify things in your patches, and have new code follow the recommended
> style.

Fixed.

> >@@ -3536,7 +3557,8 @@ update_equiv_regs (void)
> >  		{
> >  		  /* Note that the statement below does not affect the priority
> >  		     in local-alloc!  */
> >-		  REG_LIVE_LENGTH (regno) *= 2;
> >+		  if (note)
> >+		    REG_LIVE_LENGTH (regno) *= 2;
> 
> That's a very suspicious comment. It would be worth testing whether
> REG_LIVE_LENGTH has any effect on our current register allocation at all,
> and remove this code if not.

Yes, REG_LIVE_LENGTH is used in just one place in the whole of gcc,
and that's the test in update_equiv_regs just above the code you
quote.
	      /* Don't mess with things live during setjmp.  */
	      if (REG_LIVE_LENGTH (regno) >= 0 && optimize)

That could be replaced with
	      if (optimize && !bitmap_bit_p (setjmp_crosses, regno))
and outside the loop
  bitmap setjmp_crosses = regstat_get_setjmp_crosses ();

For now, I've removed the REG_LIVE_LENGTH adjustment from patch 7/7.
I'll also prepare a patch to delete REG_LIVE_LENGTH everywhere.
diff mbox

Patch

diff --git a/gcc/ira.c b/gcc/ira.c
index aa721fa..ff0b941 100644
--- a/gcc/ira.c
+++ b/gcc/ira.c
@@ -2951,23 +2951,28 @@  validate_equiv_mem_from_store (rtx dest, const_rtx set ATTRIBUTE_UNUSED,
     info->equiv_mem_modified = true;
 }
 
+enum valid_equiv { valid_none, valid_combine, valid_reload };
+
 /* Verify that no store between START and the death of REG invalidates
    MEMREF.  MEMREF is invalidated by modifying a register used in MEMREF,
    by storing into an overlapping memory location, or with a non-const
    CALL_INSN.
 
-   Return 1 if MEMREF remains valid.  */
-static int
+   Return VALID_RELOAD if MEMREF remains valid for reload,
+   VALID_COMBINE if valid for combine_and_move_insns, and
+   VALID_NONE otherwise.  */
+static enum valid_equiv
 validate_equiv_mem (rtx_insn *start, rtx reg, rtx memref)
 {
   rtx_insn *insn;
   rtx note;
   struct equiv_mem_data info = { memref, false };
+  enum valid_equiv ret = valid_reload;
 
   /* If the memory reference has side effects or is volatile, it isn't a
      valid equivalence.  */
   if (side_effects_p (memref))
-    return 0;
+    return valid_none;
 
   for (insn = start; insn; insn = NEXT_INSN (insn))
     {
@@ -2975,19 +2980,27 @@  validate_equiv_mem (rtx_insn *start, rtx reg, rtx memref)
 	continue;
 
       if (find_reg_note (insn, REG_DEAD, reg))
-	return 1;
+	return ret;
 
-      /* This used to ignore readonly memory and const/pure calls.  The problem
-	 is the equivalent form may reference a pseudo which gets assigned a
-	 call clobbered hard reg.  When we later replace REG with its
-	 equivalent form, the value in the call-clobbered reg has been
-	 changed and all hell breaks loose.  */
       if (CALL_P (insn))
-	return 0;
+	{
+	  /* We can combine a reg def from one insn into a reg use in
+	     another over a call if the memory is readonly or the call
+	     const/pure.  However, we can't set reg_equiv notes up for
+	     reload over any call.  The problem is the equivalent form
+	     may reference a pseudo which gets assigned a call
+	     clobbered hard reg.  When we later replace REG with its
+	     equivalent form, the value in the call-clobbered reg has
+	     been changed and all hell breaks loose.  */
+	  ret = valid_combine;
+	  if (! MEM_READONLY_P (memref)
+	      && ! RTL_CONST_OR_PURE_CALL_P (insn))
+	    return valid_none;
+	}
 
       note_stores (PATTERN (insn), validate_equiv_mem_from_store, &info);
       if (info.equiv_mem_modified)
-	return 0;
+	return valid_none;
 
       /* If a register mentioned in MEMREF is modified via an
 	 auto-increment, we lose the equivalence.  Do the same if one
@@ -2999,10 +3012,10 @@  validate_equiv_mem (rtx_insn *start, rtx reg, rtx memref)
 	     || REG_NOTE_KIND (note) == REG_DEAD)
 	    && REG_P (XEXP (note, 0))
 	    && reg_overlap_mentioned_p (XEXP (note, 0), memref))
-	  return 0;
+	  return valid_none;
     }
 
-  return 0;
+  return valid_none;
 }
 
 /* Returns zero if X is known to be invariant.  */
@@ -3510,24 +3523,32 @@  update_equiv_regs (void)
 	     note.  */
 	  note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
 
-	  if (note == NULL_RTX && REG_BASIC_BLOCK (regno) >= NUM_FIXED_BLOCKS
-	      && MEM_P (SET_SRC (set))
-	      && validate_equiv_mem (insn, dest, SET_SRC (set)))
-	    note = set_unique_reg_note (insn, REG_EQUIV, copy_rtx (SET_SRC (set)));
-
+	  rtx replacement = NULL_RTX;
 	  if (note)
+	    replacement = XEXP (note, 0);
+	  else if (REG_BASIC_BLOCK (regno) >= NUM_FIXED_BLOCKS
+		   && MEM_P (SET_SRC (set)))
 	    {
-	      int regno = REGNO (dest);
-	      rtx x = XEXP (note, 0);
+	      enum valid_equiv validity;
+	      validity = validate_equiv_mem (insn, dest, SET_SRC (set));
+	      if (validity != valid_none)
+		{
+		  replacement = copy_rtx (SET_SRC (set));
+		  if (validity == valid_reload)
+		    note = set_unique_reg_note (insn, REG_EQUIV, replacement);
+		}
+	    }
 
-	      /* If we haven't done so, record for reload that this is an
-		 equivalencing insn.  */
-	      if (!reg_equiv[regno].is_arg_equivalence)
-		ira_reg_equiv[regno].init_insns
-		  = gen_rtx_INSN_LIST (VOIDmode, insn,
-				       ira_reg_equiv[regno].init_insns);
+	  /* If we haven't done so, record for reload that this is an
+	     equivalencing insn.  */
+	  if (note && !reg_equiv[regno].is_arg_equivalence)
+	    ira_reg_equiv[regno].init_insns
+	      = gen_rtx_INSN_LIST (VOIDmode, insn,
+				   ira_reg_equiv[regno].init_insns);
 
-	      reg_equiv[regno].replacement = x;
+	  if (replacement)
+	    {
+	      reg_equiv[regno].replacement = replacement;
 	      reg_equiv[regno].src_p = &SET_SRC (set);
 	      reg_equiv[regno].loop_depth = (short) loop_depth;
 
@@ -3536,7 +3557,8 @@  update_equiv_regs (void)
 		{
 		  /* Note that the statement below does not affect the priority
 		     in local-alloc!  */
-		  REG_LIVE_LENGTH (regno) *= 2;
+		  if (note)
+		    REG_LIVE_LENGTH (regno) *= 2;
 
 		  /* If the register is referenced exactly twice, meaning it is
 		     set once and used once, indicate that the reference may be
@@ -3548,7 +3570,7 @@  update_equiv_regs (void)
 		     calls.  */
 
 		  if (REG_N_REFS (regno) == 2
-		      && (rtx_equal_p (x, src)
+		      && (rtx_equal_p (replacement, src)
 			  || ! equiv_init_varies_p (src))
 		      && NONJUMP_INSN_P (insn)
 		      && equiv_init_movable_p (PATTERN (insn), regno))
@@ -3598,7 +3620,7 @@  add_store_equivs (void)
 	     For all other cases the luids should be valid.  */
 	  && DF_INSN_LUID (init_insn) < DF_INSN_LUID (insn)
 	  && ! find_reg_note (init_insn, REG_EQUIV, NULL_RTX)
-	  && validate_equiv_mem (init_insn, src, dest)
+	  && validate_equiv_mem (init_insn, src, dest) == valid_reload
 	  && ! memref_used_between_p (dest, init_insn, insn)
 	  /* Attaching a REG_EQUIV note will fail if INIT_INSN has
 	     multiple sets.  */