diff mbox

[5/6] Generate more shrink-wrapping opportunities

Message ID 4D8A09E5.7070105@codesourcery.com
State New
Headers show

Commit Message

Bernd Schmidt March 23, 2011, 2:55 p.m. UTC
The first basic block contains insns to move incoming argument registers
to pseudos. When these pseudos live across calls, they get allocated to
call-saved registers. This in turns disables shrink-wrapping, since the
move instruction requires the prologue (saving the call-saved reg) to
occur before it.

This patch addresses the problem by moving such moves downwards through
the CFG until we find a place where the destination is used or the
incoming argument is clobbered.


Bernd
* function.c (prepare_shrink_wrap): New function.
	(thread_prologue_and_epilogue_insns): Call it.

Comments

Jeff Law March 23, 2011, 3:03 p.m. UTC | #1
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 03/23/11 08:55, Bernd Schmidt wrote:
> The first basic block contains insns to move incoming argument registers
> to pseudos. When these pseudos live across calls, they get allocated to
> call-saved registers. This in turns disables shrink-wrapping, since the
> move instruction requires the prologue (saving the call-saved reg) to
> occur before it.
> 
> This patch addresses the problem by moving such moves downwards through
> the CFG until we find a place where the destination is used or the
> incoming argument is clobbered.
FWIW, downward motion of the moves out of arg registers (or loads from
arg slots) is definitely a good thing.  This was a regular source of
unnecessary register pressure leading to spills in codes I've looked at.

I hope your sinking code works better than the quick and dirty one I
wrote but never contributed.

jeff
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJNigvCAAoJEBRtltQi2kC7/DcH/0OPNnWZD9qoqRwrpm/Zc5qN
zaCsSc9VYFuQa5Kh6dTd0md6ORfqSWFt6v0ygOXueYt7/bni4YsEA33N52dp3VVY
xg6R0m1XEmfg8Pcn0SzyBGGmAnprgn7XpRnbOLycAT11CjfNFN9jjdeXFYbSHiNu
NkvdtiKzz2HeucmvTBEZByN1mhP3/9DeQ3R6MM7uZ9xZFuA4rBfx8wfijxTYEg2d
3T52kiDzqcTBsD6Q5apAtNcFU6X7o1KS/eZsbno+nnMcc4z7lQ+6EQVfnBPfs9m2
GLb4ZNNzYCesczNHM+DyuJfQQVAkECKx0DcAGL8AivffUr9o05l4nGsS5D4Kip8=
=gALv
-----END PGP SIGNATURE-----
Bernd Schmidt March 23, 2011, 3:04 p.m. UTC | #2
On 03/23/2011 04:03 PM, Jeff Law wrote:
> On 03/23/11 08:55, Bernd Schmidt wrote:
>> The first basic block contains insns to move incoming argument registers
>> to pseudos. When these pseudos live across calls, they get allocated to
>> call-saved registers. This in turns disables shrink-wrapping, since the
>> move instruction requires the prologue (saving the call-saved reg) to
>> occur before it.
> 
>> This patch addresses the problem by moving such moves downwards through
>> the CFG until we find a place where the destination is used or the
>> incoming argument is clobbered.
> FWIW, downward motion of the moves out of arg registers (or loads from
> arg slots) is definitely a good thing.  This was a regular source of
> unnecessary register pressure leading to spills in codes I've looked at.
> 
> I hope your sinking code works better than the quick and dirty one I
> wrote but never contributed.

Sadly I'm doing it after register allocation, so it wouldn't help with
your problem.


Bernd
Jeff Law March 23, 2011, 3:18 p.m. UTC | #3
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 03/23/11 09:04, Bernd Schmidt wrote:
> On 03/23/2011 04:03 PM, Jeff Law wrote:
>> On 03/23/11 08:55, Bernd Schmidt wrote:
>>> The first basic block contains insns to move incoming argument registers
>>> to pseudos. When these pseudos live across calls, they get allocated to
>>> call-saved registers. This in turns disables shrink-wrapping, since the
>>> move instruction requires the prologue (saving the call-saved reg) to
>>> occur before it.
>>
>>> This patch addresses the problem by moving such moves downwards through
>>> the CFG until we find a place where the destination is used or the
>>> incoming argument is clobbered.
>> FWIW, downward motion of the moves out of arg registers (or loads from
>> arg slots) is definitely a good thing.  This was a regular source of
>> unnecessary register pressure leading to spills in codes I've looked at.
>>
>> I hope your sinking code works better than the quick and dirty one I
>> wrote but never contributed.
> 
> Sadly I'm doing it after register allocation, so it wouldn't help with
> your problem.
That'll still help :-)  I can run your sinking code, then use the
existing IRA callbacks to attempt to allocate any pseudos which didn't
previously get hard regs.  It's actually quite easy.

jeff
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJNig9CAAoJEBRtltQi2kC7I3kIAJ6manYH+/ZFcdQwfxroN0im
U0Oc18wbEz5VZg3SXnw7wEm6uRPKxYLS9/t2bMo0xLY2cHCWZx2QLH9g9O09lnUv
EU5lG46H7oIJuwhuC8osvsJUbLfQL5PkGQJdF4mfab1uk/Et5RSo8wfna7HDhTXU
b8WJltvS2ZJQIggSFxXtM101eq2/oiiU286WC8wdqlbq0lgWotBcxhHNuZeO/LEj
NeGX91kcFDl8RcwQNeT2a2G+JC0i5tc1S4C3d9pGgUiqDWpmjx74WrlRpzpkF8EF
7kfYm48xY5hPcmUcZ7vJU5Aq3Ik7U2rM6aO9H3dYoFTAOmcgs22Rbj6FoDMrUXE=
=N5L7
-----END PGP SIGNATURE-----
Jeff Law March 31, 2011, 1:23 p.m. UTC | #4
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 03/23/11 08:55, Bernd Schmidt wrote:
> The first basic block contains insns to move incoming argument registers
> to pseudos. When these pseudos live across calls, they get allocated to
> call-saved registers. This in turns disables shrink-wrapping, since the
> move instruction requires the prologue (saving the call-saved reg) to
> occur before it.
> 
> This patch addresses the problem by moving such moves downwards through
> the CFG until we find a place where the destination is used or the
> incoming argument is clobbered.
OK.  At some point I'll probably want to move this code to run
immediately after IRA completes and extend it in a few ways, but for now
it's OK as it stands.

jeff
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJNlIA5AAoJEBRtltQi2kC7DeIH+weQ1mb1whdL6gD18RmLeBvR
cMtbAHMOz6Hyt8vojAjyXEFJtVPGDv9tbr3/GiKZiOamBhXLVy38YCh9dCZDaE/K
+sqSCSPSc9/N0poUpeYY/Am5HWp/rBXbY0+vUxnaS3rCTGAiOSrzpw4/VR23MTLN
mVTa4lhz4KPifFIigpvGoNS+n1MZkUfeg7CoMXMvVl516bTwuLHB0VcSWV7qud8w
E4uIqHngf8k1rSLvGOsRy08a2tXxJBIRolfTI8bYZLFXG9EhsIQnR5e664ySBgyj
gVPVBSTT+zpbpkF12f8qJ/PLO1JWZlS7gDTwS+lGhEcWq9hmO3+rP/C5Z7XXsYQ=
=ippB
-----END PGP SIGNATURE-----
diff mbox

Patch

Index: gcc/function.c
===================================================================
--- gcc.orig/function.c
+++ gcc/function.c
@@ -5299,6 +5299,127 @@  requires_stack_frame_p (rtx insn)
       return true;
   return false;
 }
+
+/* Look for sets of call-saved registers in the first block of the
+   function, and move them down into successor blocks if the register
+   is used only on one path.  This exposes more opportunities for
+   shrink-wrapping.
+   These kinds of sets often occur when incoming argument registers are
+   moved to call-saved registers because their values are live across
+   one or more calls during the function.  */
+
+static void
+prepare_shrink_wrap (basic_block entry_block)
+{
+  rtx insn, curr;
+  FOR_BB_INSNS_SAFE (entry_block, insn, curr)
+    {
+      basic_block next_bb;
+      edge e, live_edge;
+      edge_iterator ei;
+      rtx set, scan;
+      unsigned destreg, srcreg;
+
+      if (!NONDEBUG_INSN_P (insn))
+	continue;
+      set = single_set (insn);
+      if (!set)
+	continue;
+
+      if (!REG_P (SET_SRC (set)) || !REG_P (SET_DEST (set)))
+	continue;
+      srcreg = REGNO (SET_SRC (set));
+      destreg = REGNO (SET_DEST (set));
+      if (hard_regno_nregs[srcreg][GET_MODE (SET_SRC (set))] > 1
+	  || hard_regno_nregs[destreg][GET_MODE (SET_DEST (set))] > 1)
+	continue;
+
+      next_bb = entry_block;
+      scan = insn;
+
+      for (;;)
+	{
+	  live_edge = NULL;
+	  FOR_EACH_EDGE (e, ei, next_bb->succs)
+	    {
+	      if (REGNO_REG_SET_P (df_get_live_in (e->dest), destreg))
+		{
+		  if (live_edge)
+		    {
+		      live_edge = NULL;
+		      break;
+		    }
+		  live_edge = e;
+		}
+	    }
+	  if (!live_edge)
+	    break;
+	  /* We can sometimes encounter dead code.  Don't try to move it
+	     into the exit block.  */
+	  if (live_edge->dest == EXIT_BLOCK_PTR)
+	    break;
+	  if (EDGE_COUNT (live_edge->dest->preds) > 1)
+	    break;
+	  while (scan != BB_END (next_bb))
+	    {
+	      scan = NEXT_INSN (scan);
+	      if (NONDEBUG_INSN_P (scan))
+		{
+		  rtx link;
+		  HARD_REG_SET set_regs;
+
+		  CLEAR_HARD_REG_SET (set_regs);
+		  note_stores (PATTERN (scan), record_hard_reg_sets,
+			       &set_regs);
+		  if (CALL_P (scan))
+		    IOR_HARD_REG_SET (set_regs, call_used_reg_set);
+		  for (link = REG_NOTES (scan); link; link = XEXP (link, 1))
+		    if (REG_NOTE_KIND (link) == REG_INC)
+		      record_hard_reg_sets (XEXP (link, 0), NULL, &set_regs);
+
+		  if (TEST_HARD_REG_BIT (set_regs, srcreg)
+		      || reg_referenced_p (SET_DEST (set),
+					   PATTERN (scan)))
+		    {
+		      scan = NULL_RTX;
+		      break;
+		    }
+		  if (CALL_P (scan))
+		    {
+		      rtx link = CALL_INSN_FUNCTION_USAGE (scan);
+		      while (link)
+			{
+			  rtx tmp = XEXP (link, 0);
+			  if (GET_CODE (tmp) == USE
+			      && reg_referenced_p (SET_DEST (set), tmp))
+			    break;
+			  link = XEXP (link, 1);
+			}
+		      if (link)
+			{
+			  scan = NULL_RTX;
+			  break;
+			}
+		    }
+		}
+	    }
+	  if (!scan)
+	    break;
+	  next_bb = live_edge->dest;
+	}
+
+      if (next_bb != entry_block)
+	{
+	  rtx after = BB_HEAD (next_bb);
+	  while (!NOTE_P (after)
+		 || NOTE_KIND (after) != NOTE_INSN_BASIC_BLOCK)
+	    after = NEXT_INSN (after);
+	  emit_insn_after (PATTERN (insn), after);
+	  delete_insn (insn);
+	}
+    }
+}
+
 #endif
 
 #ifdef HAVE_return
@@ -5499,6 +5620,8 @@  thread_prologue_and_epilogue_insns (void
       bitmap_head bb_antic_flags;
       bitmap_head bb_on_list;
 
+      prepare_shrink_wrap (entry_edge->dest);
+
       bitmap_initialize (&bb_antic_flags, &bitmap_default_obstack);
       bitmap_initialize (&bb_on_list, &bitmap_default_obstack);