direct return optimization

Richard Henderson rth@twiddle.net
Sun Feb 6 05:07:00 GMT 2000


Do it via the CFG instead of ad hoc code in jump.  Actually, there's 
quite a bit more code in jump that could be removed, but it's harder
to untangle.

The functionality of the compiler should be largely unchanged, except
for those ports that implemented the return instruction with something
other than a plain (return), who will now win.


r~

        * flow.c (flow_delete_insn, make_edge, remove_edge): Export.
        * basic-block.h: Declare them.
        * emit-rtl.h (active_insn_p): New.
        (next_active_insn, prev_active_insn): Use it.
        * rtl.h: Declare it.
        * function.c (emit_return_into_block): New.
        (thread_prologue_and_epilogue_insns): Insert return insns instead
        of epilogues when possible.
        * jump.c (jump_optimize_1): Remove code to insert a return insn
        on the fallthru to the exit block.

Index: basic-block.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/basic-block.h,v
retrieving revision 1.49
diff -u -p -d -r1.49 basic-block.h
--- basic-block.h	2000/01/29 01:41:22	1.49
+++ basic-block.h	2000/02/06 12:55:27
@@ -222,7 +222,11 @@ extern void insert_insn_on_edge		PARAMS 
 extern void commit_edge_insertions	PARAMS ((void));
 extern void remove_fake_edges		PARAMS ((void));
 extern void add_noreturn_fake_exit_edges	PARAMS ((void));
+extern rtx flow_delete_insn		PARAMS ((rtx));
 extern void flow_delete_insn_chain	PARAMS ((rtx, rtx));
+extern void make_edge			PARAMS ((sbitmap *, basic_block,
+						 basic_block, int));
+extern void remove_edge			PARAMS ((edge));
 
 
 /* Structure to hold information for each natural loop.  */
Index: emit-rtl.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/emit-rtl.c,v
retrieving revision 1.107
diff -u -p -d -r1.107 emit-rtl.c
--- emit-rtl.c	2000/01/27 20:46:26	1.107
+++ emit-rtl.c	2000/02/06 12:55:28
@@ -2084,6 +2084,17 @@ prev_real_insn (insn)
    does not look inside SEQUENCEs.  Until reload has completed, this is the
    same as next_real_insn.  */
 
+int
+active_insn_p (insn)
+     rtx insn;
+{
+  return (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
+	  || (GET_CODE (insn) == INSN
+	      && (! reload_completed
+		  || (GET_CODE (PATTERN (insn)) != USE
+		      && GET_CODE (PATTERN (insn)) != CLOBBER))));
+}
+
 rtx
 next_active_insn (insn)
      rtx insn;
@@ -2091,12 +2102,7 @@ next_active_insn (insn)
   while (insn)
     {
       insn = NEXT_INSN (insn);
-      if (insn == 0
-	  || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
-	  || (GET_CODE (insn) == INSN
-	      && (! reload_completed
-		  || (GET_CODE (PATTERN (insn)) != USE
-		      && GET_CODE (PATTERN (insn)) != CLOBBER))))
+      if (insn == 0 || active_insn_p (insn))
 	break;
     }
 
@@ -2114,12 +2120,7 @@ prev_active_insn (insn)
   while (insn)
     {
       insn = PREV_INSN (insn);
-      if (insn == 0
-	  || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
-	  || (GET_CODE (insn) == INSN
-	      && (! reload_completed
-		  || (GET_CODE (PATTERN (insn)) != USE
-		      && GET_CODE (PATTERN (insn)) != CLOBBER))))
+      if (insn == 0 || active_insn_p (insn))
 	break;
     }
 
Index: flow.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/flow.c,v
retrieving revision 1.216
diff -u -p -d -r1.216 flow.c
--- flow.c	2000/02/04 21:30:21	1.216
+++ flow.c	2000/02/06 12:55:29
@@ -285,8 +285,6 @@ static rtx find_basic_blocks_1		PARAMS (
 static void create_basic_block		PARAMS ((int, rtx, rtx, rtx));
 static void clear_edges			PARAMS ((void));
 static void make_edges			PARAMS ((rtx));
-static void make_edge			PARAMS ((sbitmap *, basic_block,
-						 basic_block, int));
 static void make_label_edge		PARAMS ((sbitmap *, basic_block,
 						 rtx, int));
 static void make_eh_edge		PARAMS ((sbitmap *, eh_nesting_info *,
@@ -302,7 +300,6 @@ static void delete_eh_regions		PARAMS ((
 static int can_delete_note_p		PARAMS ((rtx));
 static int delete_block			PARAMS ((basic_block));
 static void expunge_block		PARAMS ((basic_block));
-static rtx flow_delete_insn		PARAMS ((rtx));
 static int can_delete_label_p		PARAMS ((rtx));
 static int merge_blocks_move_predecessor_nojumps PARAMS ((basic_block,
 							  basic_block));
@@ -346,7 +343,6 @@ static void count_reg_sets_1		PARAMS ((r
 static void count_reg_sets		PARAMS ((rtx));
 static void count_reg_references	PARAMS ((rtx));
 static void invalidate_mems_from_autoinc PARAMS ((rtx));
-static void remove_edge			PARAMS ((edge));
 static void remove_fake_successors	PARAMS ((basic_block));
 static void flow_nodes_print	PARAMS ((const char *, const sbitmap, FILE *));
 static void flow_exits_print PARAMS ((const char *, const edge *, int, FILE *));
@@ -1056,7 +1052,7 @@ make_edges (label_value_list)
 /* Create an edge between two basic blocks.  FLAGS are auxiliary information
    about the edge that is accumulated between calls.  */
 
-static void
+void
 make_edge (edge_cache, src, dst, flags)
      sbitmap *edge_cache;
      basic_block src, dst;
@@ -1982,7 +1978,7 @@ expunge_block (b)
 
 /* Delete INSN by patching it out.  Return the next insn.  */
 
-static rtx
+rtx
 flow_delete_insn (insn)
      rtx insn;
 {
@@ -6215,7 +6211,7 @@ find_edge_index (edge_list, pred, succ)
 }
 
 /* This function will remove an edge from the flow graph.  */
-static void
+void
 remove_edge (e)
      edge e;
 {
Index: function.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/function.c,v
retrieving revision 1.154
diff -u -p -d -r1.154 function.c
--- function.c	2000/02/04 21:30:22	1.154
+++ function.c	2000/02/06 12:55:31
@@ -271,6 +271,7 @@ static int all_blocks		PARAMS ((tree, tr
    can always export `prologue_epilogue_contains'.  */
 static int *record_insns	PARAMS ((rtx)) ATTRIBUTE_UNUSED;
 static int contains		PARAMS ((rtx, int *));
+static void emit_return_into_block PARAMS ((basic_block));
 static void put_addressof_into_stack PARAMS ((rtx, struct hash_table *));
 static boolean purge_addressof_1 PARAMS ((rtx *, rtx, int, int, 
 					  struct hash_table *));
@@ -6577,6 +6578,27 @@ prologue_epilogue_contains (insn)
   return 0;
 }
 
+/* Insert gen_return at the end of block BB.  This also means updating
+   block_for_insn appropriately.  */
+
+static void
+emit_return_into_block (bb)
+     basic_block bb;
+{
+  rtx p, end;
+
+  end = emit_jump_insn_after (gen_return (), bb->end);
+  p = NEXT_INSN (bb->end); 
+  while (1)
+    {
+      set_block_for_insn (p, bb);
+      if (p == end)
+	break;
+      p = NEXT_INSN (p);
+    }
+  bb->end = end;
+}
+
 /* Generate the prologue and epilogue RTL if the machine supports it.  Thread
    this into place with notes indicating where the prologue ends and where
    the epilogue begins.  Update the basic block information when possible.  */
@@ -6629,6 +6651,93 @@ thread_prologue_and_epilogue_insns (f)
   if (e == NULL)
     goto epilogue_done;
 
+#ifdef HAVE_return
+  if (optimize && HAVE_return)
+    {
+      /* If we're allowed to generate a simple return instruction,
+	 then by definition we don't need a full epilogue.  Examine
+	 the block that falls through to EXIT.   If it does not 
+	 contain any code, examine its predecessors and try to 
+	 emit (conditional) return instructions.  */
+
+      basic_block last;
+      edge e_next;
+      rtx label;
+
+      for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
+	if (e->flags & EDGE_FALLTHRU)
+	  break;
+      if (e == NULL)
+	goto epilogue_done;
+      last = e->src;
+
+      /* Verify that there are no active instructions in the last block.  */
+      label = last->end;
+      while (label && GET_CODE (label) != CODE_LABEL)
+	{
+	  if (active_insn_p (label))
+	    break;
+	  label = PREV_INSN (label);
+	}
+
+      if (last->head == label && GET_CODE (label) == CODE_LABEL)
+	{
+	  for (e = last->pred; e ; e = e_next)
+	    {
+	      basic_block bb = e->src;
+	      rtx jump;
+
+	      e_next = e->pred_next;
+	      if (bb == ENTRY_BLOCK_PTR)
+		continue;
+
+	      jump = bb->end;
+	      if (GET_CODE (jump) != JUMP_INSN)
+		continue;
+
+	      /* If we have an unconditional jump, we can replace that
+		 with a simple return instruction.  */
+	      if (simplejump_p (jump))
+		{
+		  emit_return_into_block (bb);
+		  flow_delete_insn (jump);
+		}
+
+	      /* If we have a conditional jump, we can try to replace
+		 that with a conditional return instruction.  */
+	      else if (condjump_p (jump))
+		{
+		  rtx ret, *loc;
+
+		  ret = SET_SRC (PATTERN (jump));
+		  if (GET_CODE (XEXP (ret, 1)) == LABEL_REF)
+		    loc = &XEXP (ret, 1);
+		  else
+		    loc = &XEXP (ret, 2);
+		  ret = gen_rtx_RETURN (VOIDmode);
+
+		  if (! validate_change (jump, loc, ret, 0))
+		    continue;
+		  if (JUMP_LABEL (jump))
+		    LABEL_NUSES (JUMP_LABEL (jump))--;
+		}
+	      else
+		continue;
+
+	      /* Fix up the CFG for the successful change we just made.  */
+	      remove_edge (e);
+	      make_edge (NULL, bb, EXIT_BLOCK_PTR, 0);
+	    }
+	}
+
+      /* Emit a return insn for the exit fallthru block.  Whether
+	 this is still reachable will be determined later.  */
+
+      emit_barrier_after (last->end);
+      emit_return_into_block (last);
+      goto epilogue_done;
+    }
+#endif
 #ifdef HAVE_epilogue
   if (HAVE_epilogue)
     {
Index: jump.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/jump.c,v
retrieving revision 1.106
diff -u -p -d -r1.106 jump.c
--- jump.c	2000/02/04 17:51:11	1.106
+++ jump.c	2000/02/06 12:55:32
@@ -232,25 +232,6 @@ jump_optimize_1 (f, cross_jump, noop_mov
 
   last_insn = delete_unreferenced_labels (f);
 
-#ifdef HAVE_return
-  if (optimize && HAVE_return)
-    {
-      /* If we fall through to the epilogue, see if we can insert a RETURN insn
-	 in front of it.  If the machine allows it at this point (we might be
-	 after reload for a leaf routine), it will improve optimization for it
-	 to be there.  */
-      insn = get_last_insn ();
-      while (insn && GET_CODE (insn) == NOTE)
-	insn = PREV_INSN (insn);
-
-      if (insn && GET_CODE (insn) != BARRIER)
-	{
-	  emit_jump_insn (gen_return ());
-	  emit_barrier ();
-	}
-    }
-#endif
-
   if (noop_moves)
     delete_noop_moves (f);
 
@@ -2141,26 +2122,6 @@ jump_optimize_1 (f, cross_jump, noop_mov
 	  last_note = insn;
 	}
   }
-
-#ifdef HAVE_return
-  if (HAVE_return)
-    {
-      /* If we fall through to the epilogue, see if we can insert a RETURN insn
-	 in front of it.  If the machine allows it at this point (we might be
-	 after reload for a leaf routine), it will improve optimization for it
-	 to be there.  We do this both here and at the start of this pass since
-	 the RETURN might have been deleted by some of our optimizations.  */
-      insn = get_last_insn ();
-      while (insn && GET_CODE (insn) == NOTE)
-	insn = PREV_INSN (insn);
-
-      if (insn && GET_CODE (insn) != BARRIER)
-	{
-	  emit_jump_insn (gen_return ());
-	  emit_barrier ();
-	}
-    }
-#endif
 
   /* CAN_REACH_END is persistent for each function.  Once set it should
      not be cleared.  This is especially true for the case where we
Index: rtl.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/rtl.h,v
retrieving revision 1.165
diff -u -p -d -r1.165 rtl.h
--- rtl.h	2000/02/06 03:40:46	1.165
+++ rtl.h	2000/02/06 12:55:32
@@ -1051,6 +1051,7 @@ extern rtx prev_real_insn		PARAMS ((rtx)
 extern rtx next_real_insn		PARAMS ((rtx));
 extern rtx prev_active_insn		PARAMS ((rtx));
 extern rtx next_active_insn		PARAMS ((rtx));
+extern int active_insn_p		PARAMS ((rtx));
 extern rtx prev_label			PARAMS ((rtx));
 extern rtx next_label			PARAMS ((rtx));
 extern rtx next_cc0_user		PARAMS ((rtx));


More information about the Gcc-patches mailing list