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