thread_prologue_epilogue_insns cleanup

Richard Henderson rth@cygnus.com
Fri Jan 28 14:22:00 GMT 2000


On Thu, Jan 27, 2000 at 07:55:48PM +0100, Jan Hubicka wrote:
> 	* function.c (thread_prologue_and_epilogue_insns): Rewrite epilogue
> 	emiting code.
> 	* flow.c (commit_edge_insertions): Call verify_flow_info when
> 	ENABLE_CHECKING.

Here's my replacement.  The important bit is rediscovering the
block boundaries after reload.  Otherwise it is possible to get
the epilogue emitted before the return value gets reloaded.

I have a follow-on patch for this that will do a much better
job of emitting return instructions midstream.  I do have concerns
about individual ports, however -- as far as I can tell this 
stuff has never been tested.  ix86_can_use_return_insn_p,
for instance, allows a return with a non-zero frame size,
without being prepared to adjust the stack.



r~

        * flow.c (find_basic_blocks): Remove do_cleanup argument.
        Break out that code ...
        (cleanup_cfg): ... here.
        (commit_one_edge_insertion): Detect a return instruction being
        emitted to an edge.  Emit a barrier following; clear fallthru.
        (commit_edge_insertions): Verify CFG consistency.
        * function.c (expand_function_start): Kill unused variable.
        (expand_function_end): Likewise.
        (thread_prologue_and_epilogue_insns): Use insert_insn_on_edge
        to insert the epilogue.

        * gcse.c (gcse_main): Adjust for find_basic_blocks change.
        (delete_null_pointer_checks): Likewise.
        * output.h: Likewise.
        * reg-stack.c (reg_to_stack): Likewise.
        * toplev.c (rest_of_compilation): Likewise.  Run
        thread_prologue_and_epilogue_insns after rebuilding the CFG.

Index: flow.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/flow.c,v
retrieving revision 1.210
diff -c -p -d -r1.210 flow.c
*** flow.c	2000/01/28 21:21:49	1.210
--- flow.c	2000/01/28 21:52:50
*************** int flow_loop_outside_edge_p		PARAMS ((c
*** 372,382 ****
     numbers in use.  */
  
  void
! find_basic_blocks (f, nregs, file, do_cleanup)
       rtx f;
       int nregs ATTRIBUTE_UNUSED;
       FILE *file ATTRIBUTE_UNUSED;
-      int do_cleanup;
  {
    int max_uid;
  
--- 372,381 ----
     numbers in use.  */
  
  void
! find_basic_blocks (f, nregs, file)
       rtx f;
       int nregs ATTRIBUTE_UNUSED;
       FILE *file ATTRIBUTE_UNUSED;
  {
    int max_uid;
  
*************** find_basic_blocks (f, nregs, file, do_cl
*** 425,447 ****
    compute_bb_for_insn (max_uid);
  
    /* Discover the edges of our cfg.  */
- 
    record_active_eh_regions (f);
    make_edges (label_value_list);
- 
-   /* Delete unreachable blocks, then merge blocks when possible.  */
- 
-   if (do_cleanup)
-     {
-       delete_unreachable_blocks ();
-       move_stray_eh_region_notes ();
-       record_active_eh_regions (f);
-       if (optimize)
- 	try_merge_blocks ();
-     }
- 
-   /* Mark critical edges.  */
- 
    mark_critical_edges ();
  
    /* Kill the data we won't maintain.  */
--- 424,431 ----
*************** find_basic_blocks_1 (f)
*** 740,745 ****
--- 724,742 ----
    return label_value_list;
  }
  
+ /* Tidy the CFG by deleting unreachable code and whatnot.  */
+ 
+ void
+ cleanup_cfg (f)
+      rtx f;
+ {
+   delete_unreachable_blocks ();
+   move_stray_eh_region_notes ();
+   record_active_eh_regions (f);
+   try_merge_blocks ();
+   mark_critical_edges ();
+ }
+ 
  /* Create a new basic block consisting of the instructions between
     HEAD and END inclusive.  Reuses the note and basic block struct
     in BB_NOTE, if any.  */
*************** static void
*** 1552,1560 ****
  commit_one_edge_insertion (e)
       edge e;
  {
!   rtx before = NULL_RTX, after = NULL_RTX, tmp;
    basic_block bb;
  
    /* Figure out where to put these things.  If the destination has
       one predecessor, insert there.  Except for the exit block.  */
    if (e->dest->pred->pred_next == NULL
--- 1549,1561 ----
  commit_one_edge_insertion (e)
       edge e;
  {
!   rtx before = NULL_RTX, after = NULL_RTX, insns, tmp;
    basic_block bb;
  
+   /* Pull the insns off the edge now since the edge might go away.  */
+   insns = e->insns;
+   e->insns = NULL_RTX;
+ 
    /* Figure out where to put these things.  If the destination has
       one predecessor, insert there.  Except for the exit block.  */
    if (e->dest->pred->pred_next == NULL
*************** commit_one_edge_insertion (e)
*** 1611,1638 ****
      }
  
    /* Now that we've found the spot, do the insertion.  */
-   tmp = e->insns;
-   e->insns = NULL_RTX;
  
    /* Set the new block number for these insns, if structure is allocated.  */
    if (basic_block_for_insn)
      {
        rtx i;
!       for (i = tmp; i != NULL_RTX; i = NEXT_INSN (i))
  	set_block_for_insn (i, bb);
      }
  
    if (before)
      {
!       emit_insns_before (tmp, before);
        if (before == bb->head)
! 	bb->head = tmp;
      }
    else
      {
!       tmp = emit_insns_after (tmp, after);
        if (after == bb->end)
! 	bb->end = tmp;
      }
  }
  
--- 1612,1661 ----
      }
  
    /* Now that we've found the spot, do the insertion.  */
  
    /* Set the new block number for these insns, if structure is allocated.  */
    if (basic_block_for_insn)
      {
        rtx i;
!       for (i = insns; i != NULL_RTX; i = NEXT_INSN (i))
  	set_block_for_insn (i, bb);
      }
  
    if (before)
      {
!       emit_insns_before (insns, before);
        if (before == bb->head)
! 	bb->head = insns;
      }
    else
      {
!       rtx last = emit_insns_after (insns, after);
        if (after == bb->end)
! 	{
! 	  bb->end = last;
! 
! 	  if (GET_CODE (last) == JUMP_INSN)
! 	    {
! 	      if (returnjump_p (last))
! 		{
! 		  /* ??? Remove all outgoing edges from BB and add one
! 		     for EXIT.  This is not currently a problem because
! 		     this only happens for the (single) epilogue, which
! 		     already has a fallthru edge to EXIT.  */
! 
! 		  e = bb->succ;
! 		  if (e->dest != EXIT_BLOCK_PTR
! 		      || e->succ_next != NULL
! 		      || (e->flags & EDGE_FALLTHRU) == 0)
! 		    abort ();
! 		  e->flags &= ~EDGE_FALLTHRU;
! 
! 		  emit_barrier_after (last);
! 		}
! 	      else
! 		abort ();
! 	    }
! 	}
      }
  }
  
*************** commit_edge_insertions ()
*** 1644,1649 ****
--- 1667,1676 ----
    int i;
    basic_block bb;
  
+ #ifdef ENABLE_CHECKING
+   verify_flow_info ();
+ #endif
+  
    i = -1;
    bb = ENTRY_BLOCK_PTR;
    while (1)
Index: function.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/function.c,v
retrieving revision 1.152
diff -c -p -d -r1.152 function.c
*** function.c	2000/01/27 20:46:26	1.152
--- function.c	2000/01/28 21:52:51
*************** expand_function_start (subr, parms_have_
*** 5867,5873 ****
       tree subr;
       int parms_have_cleanups;
  {
-   register int i;
    tree tem;
    rtx last_ptr = NULL_RTX;
  
--- 5867,5872 ----
*************** expand_function_end (filename, line, end
*** 6171,6177 ****
       int line;
       int end_bindings;
  {
-   register int i;
    tree link;
  
  #ifdef TRAMPOLINE_TEMPLATE
--- 6170,6175 ----
*************** thread_prologue_and_epilogue_insns (f)
*** 6546,6557 ****
       rtx f ATTRIBUTE_UNUSED;
  {
    int insertted = 0;
  
  #ifdef HAVE_prologue
    if (HAVE_prologue)
      {
-       rtx seq;
- 
        start_sequence ();
        seq = gen_prologue();
        emit_insn (seq);
--- 6544,6555 ----
       rtx f ATTRIBUTE_UNUSED;
  {
    int insertted = 0;
+   edge e;
+   rtx seq;
  
  #ifdef HAVE_prologue
    if (HAVE_prologue)
      {
        start_sequence ();
        seq = gen_prologue();
        emit_insn (seq);
*************** thread_prologue_and_epilogue_insns (f)
*** 6581,6709 ****
      }
  #endif
  
  #ifdef HAVE_epilogue
    if (HAVE_epilogue)
      {
!       edge e;
!       basic_block bb = 0;
!       rtx tail = get_last_insn ();
! 
!       /* ??? This is gastly.  If function returns were not done via uses,
! 	 but via mark_regs_live_at_end, we could use insert_insn_on_edge
! 	 and all of this uglyness would go away.  */
! 
!       switch (optimize)
! 	{
! 	default:
! 	  /* If the exit block has no non-fake predecessors, we don't
! 	     need an epilogue.  Furthermore, only pay attention to the
! 	     fallthru predecessors; if (conditional) return insns were
! 	     generated, by definition we do not need to emit epilogue
! 	     insns.  */
! 
! 	  for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
! 	    if ((e->flags & EDGE_FAKE) == 0
! 		&& (e->flags & EDGE_FALLTHRU) != 0)
! 	      break;
! 	  if (e == NULL)
! 	    break;
! 
! 	  /* We can't handle multiple epilogues -- if one is needed,
! 	     we won't be able to place it multiple times.
! 
! 	     ??? Fix epilogue expanders to not assume they are the
! 	     last thing done compiling the function.  Either that
! 	     or copy_rtx each insn.
! 
! 	     ??? Blah, it's not a simple expression to assert that
! 	     we've exactly one fallthru exit edge.  */
! 
! 	  bb = e->src;
! 	  tail = bb->end;
! 
! 	  /* ??? If the last insn of the basic block is a jump, then we
! 	     are creating a new basic block.  Wimp out and leave these
! 	     insns outside any block.  */
! 	  if (GET_CODE (tail) == JUMP_INSN)
! 	    bb = 0;
! 
! 	  /* FALLTHRU */
! 	case 0:
! 	  {
! 	    rtx prev, seq, first_use;
! 
! 	    /* Move the USE insns at the end of a function onto a list.  */
! 	    prev = tail;
! 	    if (GET_CODE (prev) == BARRIER
! 		|| GET_CODE (prev) == NOTE)
! 	      prev = prev_nonnote_insn (prev);
! 
! 	    first_use = 0;
! 	    if (prev
! 		&& GET_CODE (prev) == INSN
! 		&& GET_CODE (PATTERN (prev)) == USE)
! 	      {
! 		/* If the end of the block is the use, grab hold of something
! 		   else so that we emit barriers etc in the right place.  */
! 		if (prev == tail)
! 		  {
! 		    do 
! 		      tail = PREV_INSN (tail);
! 		    while (GET_CODE (tail) == INSN
! 			   && GET_CODE (PATTERN (tail)) == USE);
! 		  }
! 
! 		do
! 		  {
! 		    rtx use = prev;
! 		    prev = prev_nonnote_insn (prev);
! 
! 		    remove_insn (use);
! 		    if (first_use)
! 		      {
! 			NEXT_INSN (use) = first_use;
! 			PREV_INSN (first_use) = use;
! 		      }
! 		    else
! 		      NEXT_INSN (use) = NULL_RTX;
! 		    first_use = use;
! 		  }
! 		while (prev
! 		       && GET_CODE (prev) == INSN
! 		       && GET_CODE (PATTERN (prev)) == USE);
! 	      }
  
! 	    /* The last basic block ends with a NOTE_INSN_EPILOGUE_BEG, the
! 	       epilogue insns, the USE insns at the end of a function,
! 	       the jump insn that returns, and then a BARRIER.  */
  
! 	    if (GET_CODE (tail) != BARRIER)
! 	      {
! 		prev = next_nonnote_insn (tail);
! 		if (!prev || GET_CODE (prev) != BARRIER)
! 		  emit_barrier_after (tail);
! 	      }
  
! 	    seq = gen_epilogue ();
! 	    prev = tail;
! 	    tail = emit_jump_insn_after (seq, tail);
  
! 	    /* Insert the USE insns immediately before the return insn, which
! 	       must be the last instruction emitted in the sequence.  */
! 	    if (first_use)
! 	      emit_insns_before (first_use, tail);
! 	    emit_note_after (NOTE_INSN_EPILOGUE_BEG, prev);
  
! 	    /* Update the tail of the basic block.  */
! 	    if (bb)
! 	      bb->end = tail;
  
! 	    /* Retain a map of the epilogue insns.  */
! 	    epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail);
! 	  }
! 	}
      }
  #endif
  
    if (insertted)
      commit_edge_insertions ();
--- 6579,6625 ----
      }
  #endif
  
+   /* If the exit block has no non-fake predecessors, we don't need
+      an epilogue.  */
+   for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
+     if ((e->flags & EDGE_FAKE) == 0)
+       break;
+   if (e == NULL)
+     goto epilogue_done;
+ 
  #ifdef HAVE_epilogue
    if (HAVE_epilogue)
      {
!       /* Find the edge that falls through to EXIT.  Other edges may exist
! 	 due to RETURN instructions, but those don't need epilogues.
! 	 There really shouldn't be a mixture -- either all should have
! 	 been converted or none, however...  */
  
!       for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
! 	if (e->flags & EDGE_FALLTHRU)
! 	  break;
!       if (e == NULL)
! 	goto epilogue_done;
  
!       start_sequence ();
!       emit_note (NULL, NOTE_INSN_EPILOGUE_BEG);
  
!       seq = gen_epilogue ();
!       emit_jump_insn (seq);
  
!       /* Retain a map of the epilogue insns.  */
!       if (GET_CODE (seq) != SEQUENCE)
! 	seq = get_insns ();
!       epilogue = record_insns (seq);
  
!       seq = gen_sequence ();
!       end_sequence();
  
!       insert_insn_on_edge (seq, e);
!       insertted = 1;
      }
  #endif
+ epilogue_done:
  
    if (insertted)
      commit_edge_insertions ();
Index: gcse.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/gcse.c,v
retrieving revision 1.77
diff -c -p -d -r1.77 gcse.c
*** gcse.c	2000/01/15 20:46:21	1.77
--- gcse.c	2000/01/28 21:52:51
*************** gcse_main (f, file)
*** 673,679 ****
    /* Identify the basic block information for this function, including
       successors and predecessors.  */
    max_gcse_regno = max_reg_num ();
!   find_basic_blocks (f, max_gcse_regno, file, 1);
  
    if (file)
      dump_flow_info (file);
--- 673,680 ----
    /* Identify the basic block information for this function, including
       successors and predecessors.  */
    max_gcse_regno = max_reg_num ();
!   find_basic_blocks (f, max_gcse_regno, file);
!   cleanup_cfg (f);
  
    if (file)
      dump_flow_info (file);
*************** delete_null_pointer_checks (f)
*** 5201,5207 ****
    struct null_pointer_info npi;
  
    /* First break the program into basic blocks.  */
!   find_basic_blocks (f, max_reg_num (), NULL, 1);
  
    /* If we have only a single block, then there's nothing to do.  */
    if (n_basic_blocks <= 1)
--- 5202,5209 ----
    struct null_pointer_info npi;
  
    /* First break the program into basic blocks.  */
!   find_basic_blocks (f, max_reg_num (), NULL);
!   cleanup_cfg (f);
  
    /* If we have only a single block, then there's nothing to do.  */
    if (n_basic_blocks <= 1)
Index: output.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/output.h,v
retrieving revision 1.41
diff -c -p -d -r1.41 output.h
*** output.h	2000/01/17 15:37:03	1.41
--- output.h	2000/01/28 21:52:51
*************** extern void allocate_for_life_analysis	P
*** 131,137 ****
  extern int regno_uninitialized		PARAMS ((int));
  extern int regno_clobbered_at_setjmp	PARAMS ((int));
  extern void dump_flow_info		PARAMS ((FILE *));
! extern void find_basic_blocks         PARAMS ((rtx, int, FILE *, int));
  extern void calculate_loop_depth      PARAMS ((FILE *));
  extern void free_basic_block_vars     PARAMS ((int));
  extern void set_block_num             PARAMS ((rtx, int));
--- 131,138 ----
  extern int regno_uninitialized		PARAMS ((int));
  extern int regno_clobbered_at_setjmp	PARAMS ((int));
  extern void dump_flow_info		PARAMS ((FILE *));
! extern void find_basic_blocks		PARAMS ((rtx, int, FILE *));
! extern void cleanup_cfg			PARAMS ((rtx));
  extern void calculate_loop_depth      PARAMS ((FILE *));
  extern void free_basic_block_vars     PARAMS ((int));
  extern void set_block_num             PARAMS ((rtx, int));
Index: reg-stack.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/reg-stack.c,v
retrieving revision 1.53
diff -c -p -d -r1.53 reg-stack.c
*** reg-stack.c	2000/01/24 18:58:32	1.53
--- reg-stack.c	2000/01/28 21:52:51
*************** reg_to_stack (first, file)
*** 430,436 ****
  
    /* Ok, floating point instructions exist.  If not optimizing, 
       build the CFG and run life analysis.  */
!   find_basic_blocks (first, max_reg_num (), file, 0);
    count_or_remove_death_notes (NULL, 1);
    life_analysis (first, max_reg_num (), file, 0);
  
--- 430,436 ----
  
    /* Ok, floating point instructions exist.  If not optimizing, 
       build the CFG and run life analysis.  */
!   find_basic_blocks (first, max_reg_num (), file);
    count_or_remove_death_notes (NULL, 1);
    life_analysis (first, max_reg_num (), file, 0);
  
Index: toplev.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/toplev.c,v
retrieving revision 1.283
diff -c -p -d -r1.283 toplev.c
*** toplev.c	2000/01/27 20:46:26	1.283
--- toplev.c	2000/01/28 21:52:52
*************** rest_of_compilation (decl)
*** 3216,3222 ****
    TIMEVAR
      (flow_time,
       {
!        find_basic_blocks (insns, max_reg_num (), rtl_dump_file, 1);
         if (optimize)
  	 calculate_loop_depth (rtl_dump_file);
         life_analysis (insns, max_reg_num (), rtl_dump_file, 1);
--- 3216,3223 ----
    TIMEVAR
      (flow_time,
       {
!        find_basic_blocks (insns, max_reg_num (), rtl_dump_file);
!        cleanup_cfg (insns);
         if (optimize)
  	 calculate_loop_depth (rtl_dump_file);
         life_analysis (insns, max_reg_num (), rtl_dump_file, 1);
*************** rest_of_compilation (decl)
*** 3384,3396 ****
    if (rebuild_label_notes_after_reload)
      TIMEVAR (jump_time, rebuild_jump_labels (insns));
  
-   /* On some machines, the prologue and epilogue code, or parts thereof,
-      can be represented as RTL.  Doing so lets us schedule insns between
-      it and the rest of the code and also allows delayed branch
-      scheduling to operate in the epilogue.  */
- 
-   thread_prologue_and_epilogue_insns (insns);
- 
    /* If optimizing and we are performing instruction scheduling after
       reload, then go ahead and split insns now since we are about to
       recompute flow information anyway.
--- 3385,3390 ----
*************** rest_of_compilation (decl)
*** 3412,3423 ****
    if (flow2_dump)
      open_dump_file (".14.flow2", decl_printable_name (decl, 2));
    
    if (optimize)
      {
        TIMEVAR
  	(flow2_time,
  	 {
! 	   find_basic_blocks (insns, max_reg_num (), rtl_dump_file, 1);
  	   life_analysis (insns, max_reg_num (), rtl_dump_file, 1);
  	 });
  
--- 3406,3428 ----
    if (flow2_dump)
      open_dump_file (".14.flow2", decl_printable_name (decl, 2));
    
+   TIMEVAR (flow2_time,
+ 	   {
+ 	     find_basic_blocks (insns, max_reg_num (), rtl_dump_file);
+ 	   });
+ 
+   /* On some machines, the prologue and epilogue code, or parts thereof,
+      can be represented as RTL.  Doing so lets us schedule insns between
+      it and the rest of the code and also allows delayed branch
+      scheduling to operate in the epilogue.  */
+   thread_prologue_and_epilogue_insns (insns);
+ 
    if (optimize)
      {
        TIMEVAR
  	(flow2_time,
  	 {
! 	   cleanup_cfg (insns);
  	   life_analysis (insns, max_reg_num (), rtl_dump_file, 1);
  	 });
  


More information about the Gcc-patches mailing list