basic-block.h (find_sub_basic_block): Declare.

* basic-block.h (find_sub_basic_block): Declare.
	* flow.c (make_edges): New arguments MIN and MAX;
	(find_sub_basic_blocks): Revamp to use make_edges
	and purge_dead_edges.
	(find_basic_blocks): Update call of find_sub_basic_block.

	* recog.c (split_all_insns): Always expect CFG to be consistent;
	call find_sub_basic_blocks in case something has changed.
	* toplev.c (rest_of_compilation): Always call split_all_insns once CFG
	has been built.

	* basic-block.h (delete_noop_moves): Declare.
	* combine.c (combine_instructions): Call it.
	(recog_for_combine): Tolerate noop moves
	(distribute_notes): Force refresh when register dies at noop move.
	* flow.c (delete_noop_moves): Use BB structure; delete JUMP insns
	too.
	(life_analysis): Update delete_noop_moves call.
	(set_noop_p): Move too ...
	* rtlanal.c (noop_move_p): ... here.
	* rtl.h (noop_move_p): Declare.

	* basic-block.h (purge_all_dead_edges, purge_dead_edges): New functions.
	* toplev.c (rest_of_compilation): Conditionally call purge_all_dead_edges
	after combine.
	* gcse.c (cprop_cc0_jump, cprop_insn): New argument "basic_block".
	(cprop_jump): Likewise; call purge_dead_edges if substitution suceeded.

From-SVN: r44267
This commit is contained in:
Jan Hubicka 2001-07-23 16:08:12 +02:00 committed by Jan Hubicka
parent 4edc91ae51
commit 0005550b58
9 changed files with 247 additions and 143 deletions

View File

@ -1,3 +1,33 @@
Mon Jul 23 16:03:19 CEST 2001 Jan Hubicka <jh@suse.cz>
* basic-block.h (find_sub_basic_block): Declare.
* flow.c (make_edges): New arguments MIN and MAX;
(find_sub_basic_blocks): Revamp to use make_edges
and purge_dead_edges.
(find_basic_blocks): Update call of find_sub_basic_block.
* recog.c (split_all_insns): Always expect CFG to be consistent;
call find_sub_basic_blocks in case something has changed.
* toplev.c (rest_of_compilation): Always call split_all_insns once CFG
has been built.
* basic-block.h (delete_noop_moves): Declare.
* combine.c (combine_instructions): Call it.
(recog_for_combine): Tolerate noop moves
(distribute_notes): Force refresh when register dies at noop move.
* flow.c (delete_noop_moves): Use BB structure; delete JUMP insns
too.
(life_analysis): Update delete_noop_moves call.
(set_noop_p): Move too ...
* rtlanal.c (noop_move_p): ... here.
* rtl.h (noop_move_p): Declare.
* basic-block.h (purge_all_dead_edges, purge_dead_edges): New functions.
* toplev.c (rest_of_compilation): Conditionally call purge_all_dead_edges
after combine.
* gcse.c (cprop_cc0_jump, cprop_insn): New argument "basic_block".
(cprop_jump): Likewise; call purge_dead_edges if substitution suceeded.
2001-07-23 Hans-Peter Nilsson <hp@bitrange.com>
* reload.c (push_reload): Fix typo in comment.

View File

@ -597,10 +597,14 @@ extern void debug_regset PARAMS ((regset));
extern void allocate_reg_life_data PARAMS ((void));
extern void allocate_bb_life_data PARAMS ((void));
extern void find_unreachable_blocks PARAMS ((void));
extern void delete_noop_moves PARAMS ((rtx));
extern basic_block redirect_edge_and_branch_force PARAMS ((edge, basic_block));
extern bool redirect_edge_and_branch PARAMS ((edge, basic_block));
extern rtx block_label PARAMS ((basic_block));
extern bool forwarder_block_p PARAMS ((basic_block));
extern void purge_all_dead_edges PARAMS ((void));
extern void purge_dead_edges PARAMS ((basic_block));
extern void find_sub_basic_blocks PARAMS ((basic_block));
/* This function is always defined so it can be called from the

View File

@ -713,6 +713,8 @@ combine_instructions (f, nregs)
}
}
delete_noop_moves (f);
if (need_refresh)
{
compute_bb_for_insn (get_max_uid ());
@ -9598,8 +9600,12 @@ recog_for_combine (pnewpat, insn, pnotes)
old_notes = REG_NOTES (insn);
REG_NOTES (insn) = 0;
/* Is the result of combination a valid instruction? */
insn_code_number = recog (pat, insn, &num_clobbers_to_add);
/* Is the result of combination a valid instruction?
Recognize all noop sets, these will be killed by followup pass. */
if (GET_CODE (pat) == SET && set_noop_p (pat))
insn_code_number = INT_MAX;
else
insn_code_number = recog (pat, insn, &num_clobbers_to_add);
/* If it isn't, there is the possibility that we previously had an insn
that clobbered some register as a side effect, but the combined
@ -9624,7 +9630,11 @@ recog_for_combine (pnewpat, insn, pnotes)
if (pos == 1)
pat = XVECEXP (pat, 0, 0);
insn_code_number = recog (pat, insn, &num_clobbers_to_add);
/* Recognize all noop sets, these will be killed by followup pass. */
if (GET_CODE (pat) == SET && set_noop_p (pat))
insn_code_number = INT_MAX;
else
insn_code_number = recog (pat, insn, &num_clobbers_to_add);
}
REG_NOTES (insn) = old_notes;
@ -12325,10 +12335,16 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
is still a REG_DEAD note, but we have hit the beginning
of the block. If the existing life info says the reg
was dead, there's nothing left to do. Otherwise, we'll
need to do a global life update after combine. */
if (REG_NOTE_KIND (note) == REG_DEAD && place == 0
&& REGNO_REG_SET_P (bb->global_live_at_start,
REGNO (XEXP (note, 0))))
need to do a global life update after combine.
Similary we need to update in case insn is an dead set
we are about to remove soon.
*/
if (REG_NOTE_KIND (note) == REG_DEAD
&& ((place && noop_move_p (place))
|| (place == 0
&& REGNO_REG_SET_P (bb->global_live_at_start,
REGNO (XEXP (note, 0))))))
{
SET_BIT (refresh_blocks, this_basic_block);
need_refresh = 1;

View File

@ -374,7 +374,7 @@ static int flow_find_cross_jump PARAMS ((int, basic_block, basic_block,
static int count_basic_blocks PARAMS ((rtx));
static void find_basic_blocks_1 PARAMS ((rtx));
static rtx find_label_refs PARAMS ((rtx, rtx));
static void make_edges PARAMS ((rtx));
static void make_edges PARAMS ((rtx, int, int));
static void make_label_edge PARAMS ((sbitmap *, basic_block,
rtx, int));
static void make_eh_edge PARAMS ((sbitmap *, basic_block, rtx));
@ -401,8 +401,6 @@ static void tidy_fallthru_edges PARAMS ((void));
static int verify_wide_reg_1 PARAMS ((rtx *, void *));
static void verify_wide_reg PARAMS ((int, rtx, rtx));
static void verify_local_live_at_start PARAMS ((regset, basic_block));
static int noop_move_p PARAMS ((rtx));
static void delete_noop_moves PARAMS ((rtx));
static void notice_stack_pointer_modification_1 PARAMS ((rtx, rtx, void *));
static void notice_stack_pointer_modification PARAMS ((rtx));
static void mark_reg PARAMS ((rtx, void *));
@ -483,7 +481,6 @@ static void flow_loop_tree_node_add PARAMS ((struct loop *, struct loop *));
static void flow_loops_tree_build PARAMS ((struct loops *));
static int flow_loop_level_compute PARAMS ((struct loop *, int));
static int flow_loops_level_compute PARAMS ((struct loops *));
static void find_sub_basic_blocks PARAMS ((basic_block));
/* Find basic blocks of the current function.
F is the first insn of the function and NREGS the number of register
@ -543,7 +540,7 @@ find_basic_blocks (f, nregs, file)
compute_bb_for_insn (max_uid);
/* Discover the edges of our cfg. */
make_edges (label_value_list);
make_edges (label_value_list, 0, n_basic_blocks - 1);
/* Do very simple cleanup now, for the benefit of code that runs between
here and cleanup_cfg, e.g. thread_prologue_and_epilogue_insns. */
@ -705,39 +702,30 @@ find_label_refs (f, lvl)
/* Assume that someone emitted code with control flow instructions to the
basic block. Update the data structure. */
static void
void
find_sub_basic_blocks (bb)
basic_block bb;
{
rtx first_insn = bb->head, insn;
rtx insn = bb->head;
rtx end = bb->end;
edge succ_list = bb->succ;
rtx jump_insn = NULL_RTX;
int created = 0;
int barrier = 0;
edge falltru = 0;
basic_block first_bb = bb, last_bb;
int i;
basic_block first_bb = bb;
if (GET_CODE (first_insn) == LABEL_REF)
first_insn = NEXT_INSN (first_insn);
first_insn = NEXT_INSN (first_insn);
bb->succ = NULL;
if (insn == bb->end)
return;
if (GET_CODE (insn) == CODE_LABEL)
insn = NEXT_INSN (insn);
insn = first_insn;
/* Scan insn chain and try to find new basic block boundaries. */
while (insn != end)
while (1)
{
enum rtx_code code = GET_CODE (insn);
switch (code)
{
case JUMP_INSN:
/* We need some special care for those expressions. */
if (GET_CODE (PATTERN (insn)) == ADDR_VEC
|| GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
abort();
jump_insn = insn;
break;
case BARRIER:
if (!jump_insn)
abort ();
@ -749,8 +737,7 @@ find_sub_basic_blocks (bb)
if (jump_insn)
bb->end = jump_insn;
bb = falltru->dest;
if (barrier)
remove_edge (falltru);
remove_edge (falltru);
barrier = 0;
jump_insn = 0;
created = 1;
@ -758,6 +745,7 @@ find_sub_basic_blocks (bb)
make_edge (NULL, ENTRY_BLOCK_PTR, bb, 0);
break;
case INSN:
case JUMP_INSN:
/* In case we've previously split insn on the JUMP_INSN, move the
block header to proper place. */
if (jump_insn)
@ -765,42 +753,39 @@ find_sub_basic_blocks (bb)
falltru = split_block (bb, PREV_INSN (insn));
bb->end = jump_insn;
bb = falltru->dest;
if (barrier)
abort ();
remove_edge (falltru);
jump_insn = 0;
}
/* We need some special care for those expressions. */
if (GET_CODE (insn) == JUMP_INSN)
{
if (GET_CODE (PATTERN (insn)) == ADDR_VEC
|| GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
abort();
jump_insn = insn;
}
break;
default:
break;
}
if (insn == end)
break;
insn = NEXT_INSN (insn);
}
/* Last basic block must end in the original BB end. */
if (jump_insn)
abort ();
/* Wire in the original edges for last basic block. */
if (created)
{
bb->succ = succ_list;
while (succ_list)
succ_list->src = bb, succ_list = succ_list->succ_next;
}
else
bb->succ = succ_list;
/* In case we've got barrier at the end of new insn stream, put it
outside basic block. */
if (GET_CODE (bb->end) == BARRIER)
bb->end = PREV_INSN (bb->end);
/* We've possibly replaced the conditional jump by conditional jump
followed by cleanup at fallthru edge, so the outgoing edges may
be dead. */
purge_dead_edges (bb);
/* Now re-scan and wire in all edges. This expect simple (conditional)
jumps at the end of each new basic blocks. */
last_bb = bb;
for (i = first_bb->index; i < last_bb->index; i++)
{
bb = BASIC_BLOCK (i);
if (GET_CODE (bb->end) == JUMP_INSN)
{
mark_jump_label (PATTERN (bb->end), bb->end, 0);
make_label_edge (NULL, bb, JUMP_LABEL (bb->end), 0);
}
insn = NEXT_INSN (insn);
}
make_edges (NULL, first_bb->index, bb->index - 1);
}
/* Find all basic blocks of the function whose first insn is F.
@ -1166,7 +1151,7 @@ clear_edges ()
n_edges = 0;
}
/* Identify the edges between basic blocks.
/* Identify the edges between basic blocks MIN to MAX.
NONLOCAL_LABEL_LIST is a list of non-local labels in the function. Blocks
that are otherwise unreachable may be reachable with a non-local goto.
@ -1175,8 +1160,9 @@ clear_edges ()
the list of exception regions active at the end of the basic block. */
static void
make_edges (label_value_list)
make_edges (label_value_list, min, max)
rtx label_value_list;
int min, max;
{
int i;
sbitmap *edge_cache = NULL;
@ -1196,7 +1182,7 @@ make_edges (label_value_list)
/* By nature of the way these get numbered, block 0 is always the entry. */
make_edge (edge_cache, ENTRY_BLOCK_PTR, BASIC_BLOCK (0), EDGE_FALLTHRU);
for (i = 0; i < n_basic_blocks; ++i)
for (i = min; i <= max; ++i)
{
basic_block bb = BASIC_BLOCK (i);
rtx insn, x;
@ -4317,58 +4303,28 @@ free_basic_block_vars (keep_head_end_p)
}
}
/* Return nonzero if an insn consists only of SETs, each of which only sets a
value to itself. */
static int
noop_move_p (insn)
rtx insn;
{
rtx pat = PATTERN (insn);
/* Insns carrying these notes are useful later on. */
if (find_reg_note (insn, REG_EQUAL, NULL_RTX))
return 0;
if (GET_CODE (pat) == SET && set_noop_p (pat))
return 1;
if (GET_CODE (pat) == PARALLEL)
{
int i;
/* If nothing but SETs of registers to themselves,
this insn can also be deleted. */
for (i = 0; i < XVECLEN (pat, 0); i++)
{
rtx tem = XVECEXP (pat, 0, i);
if (GET_CODE (tem) == USE
|| GET_CODE (tem) == CLOBBER)
continue;
if (GET_CODE (tem) != SET || ! set_noop_p (tem))
return 0;
}
return 1;
}
return 0;
}
/* Delete any insns that copy a register to itself. */
static void
void
delete_noop_moves (f)
rtx f;
rtx f ATTRIBUTE_UNUSED;
{
rtx insn;
for (insn = f; insn; insn = NEXT_INSN (insn))
int i;
rtx insn, next;
basic_block bb;
for (i = 0; i < n_basic_blocks; i++)
{
if (GET_CODE (insn) == INSN && noop_move_p (insn))
bb = BASIC_BLOCK (i);
for (insn = bb->head; insn != NEXT_INSN (bb->end); insn = next)
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
next = NEXT_INSN (insn);
if (INSN_P (insn) && noop_move_p (insn))
{
if (insn == bb->end)
bb->end = PREV_INSN (insn);
flow_delete_insn (insn);
}
}
}
}
@ -9814,3 +9770,69 @@ init_flow ()
flow_firstobj = (char *) obstack_alloc (&flow_obstack, 0);
}
}
/* Assume that the preceeding pass has possibly eliminated jump instructions
or converted the unconditional jumps. Eliminate the edges from CFG. */
void
purge_dead_edges (bb)
basic_block bb;
{
edge e, next;
rtx insn = bb->end;
if (GET_CODE (insn) == JUMP_INSN && !simplejump_p (insn))
return;
if (GET_CODE (insn) == JUMP_INSN)
{
for (e = bb->succ; e; e = next)
{
next = e->succ_next;
if (e->dest == EXIT_BLOCK_PTR || e->dest->head != JUMP_LABEL (insn))
remove_edge (e);
}
if (bb->succ && bb->succ->succ_next)
abort ();
if (!bb->succ)
return;
bb->succ->probability = REG_BR_PROB_BASE;
bb->succ->count = bb->count;
if (rtl_dump_file)
fprintf (rtl_dump_file, "Purged edges from bb %i\n", bb->index);
return;
}
/* If we don't see a jump insn, we don't know exactly why the block would
have been broken at this point. Look for a simple, non-fallthru edge,
as these are only created by conditional branches. If we find such an
edge we know that there used to be a jump here and can then safely
remove all non-fallthru edges. */
for (e = bb->succ; e && (e->flags & (EDGE_COMPLEX | EDGE_FALLTHRU));
e = e->succ_next);
if (!e)
return;
for (e = bb->succ; e; e = next)
{
next = e->succ_next;
if (!(e->flags & EDGE_FALLTHRU))
remove_edge (e);
}
if (!bb->succ || bb->succ->succ_next)
abort ();
bb->succ->probability = REG_BR_PROB_BASE;
bb->succ->count = bb->count;
if (rtl_dump_file)
fprintf (rtl_dump_file, "Purged non-fallthru edges from bb %i\n",
bb->index);
return;
}
/* Search all basic blocks for potentionally dead edges and purge them. */
void
purge_all_dead_edges ()
{
int i;
for (i = 0; i < n_basic_blocks; i++)
purge_dead_edges (BASIC_BLOCK (i));
}

View File

@ -605,14 +605,14 @@ static void compute_cprop_data PARAMS ((void));
static void find_used_regs PARAMS ((rtx *, void *));
static int try_replace_reg PARAMS ((rtx, rtx, rtx));
static struct expr *find_avail_set PARAMS ((int, rtx));
static int cprop_jump PARAMS ((rtx, rtx, rtx));
static int cprop_jump PARAMS ((basic_block, rtx, rtx, rtx));
#ifdef HAVE_cc0
static int cprop_cc0_jump PARAMS ((rtx, struct reg_use *, rtx));
static int cprop_cc0_jump PARAMS ((basic_block, rtx, struct reg_use *, rtx));
#endif
static void mems_conflict_for_gcse_p PARAMS ((rtx, rtx, void *));
static int load_killed_in_block_p PARAMS ((basic_block, int, rtx, int));
static void canon_list_insert PARAMS ((rtx, rtx, void *));
static int cprop_insn PARAMS ((rtx, int));
static int cprop_insn PARAMS ((basic_block, rtx, int));
static int cprop PARAMS ((int));
static int one_cprop_pass PARAMS ((int, int));
static void alloc_pre_mem PARAMS ((int, int));
@ -4015,10 +4015,11 @@ find_avail_set (regno, insn)
nonzero if a change was made. We know INSN has just a SET. */
static int
cprop_jump (insn, from, src)
cprop_jump (bb, insn, from, src)
rtx insn;
rtx from;
rtx src;
basic_block bb;
{
rtx set = PATTERN (insn);
rtx new = simplify_replace_rtx (SET_SRC (set), from, src);
@ -4059,6 +4060,7 @@ cprop_jump (insn, from, src)
print_rtl (gcse_file, src);
fprintf (gcse_file, "\n");
}
purge_dead_edges (bb);
return 1;
}
@ -4072,7 +4074,8 @@ cprop_jump (insn, from, src)
Returns nonzero if a change was made. */
static int
cprop_cc0_jump (insn, reg_used, src)
cprop_cc0_jump (bb, insn, reg_used, src)
basic_block bb;
rtx insn;
struct reg_use *reg_used;
rtx src;
@ -4083,7 +4086,7 @@ cprop_cc0_jump (insn, reg_used, src)
rtx new_src = simplify_replace_rtx (SET_SRC (PATTERN (insn)),
reg_used->reg_rtx, src);
if (! cprop_jump (jump, cc0_rtx, new_src))
if (! cprop_jump (bb, jump, cc0_rtx, new_src))
return 0;
/* If we succeeded, delete the cc0 setter. */
@ -4099,7 +4102,8 @@ cprop_cc0_jump (insn, reg_used, src)
The result is non-zero if a change was made. */
static int
cprop_insn (insn, alter_jumps)
cprop_insn (bb, insn, alter_jumps)
basic_block bb;
rtx insn;
int alter_jumps;
{
@ -4183,7 +4187,7 @@ cprop_insn (insn, alter_jumps)
&& GET_CODE (insn) == JUMP_INSN
&& condjump_p (insn)
&& ! simplejump_p (insn))
changed |= cprop_jump (insn, reg_used->reg_rtx, src);
changed |= cprop_jump (bb, insn, reg_used->reg_rtx, src);
#ifdef HAVE_cc0
/* Similar code for machines that use a pair of CC0 setter and
@ -4252,7 +4256,7 @@ cprop (alter_jumps)
insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
changed |= cprop_insn (insn, alter_jumps);
changed |= cprop_insn (BASIC_BLOCK (bb), insn, alter_jumps);
/* Keep track of everything modified by this insn. */
/* ??? Need to be careful w.r.t. mods done to INSN. Don't

View File

@ -2725,22 +2725,6 @@ split_all_insns (upd_life)
int changed;
int i;
if (!upd_life)
{
rtx next, insn;
for (insn = get_insns (); insn ; insn = next)
{
rtx last;
/* Can't use `next_real_insn' because that might go across
CODE_LABELS and short-out basic blocks. */
next = NEXT_INSN (insn);
last = split_insn (insn);
}
return;
}
blocks = sbitmap_alloc (n_basic_blocks);
sbitmap_zero (blocks);
changed = 0;
@ -2775,12 +2759,21 @@ split_all_insns (upd_life)
abort ();
}
if (changed && upd_life)
if (changed)
{
compute_bb_for_insn (get_max_uid ());
for (i = 0; i < n_basic_blocks; i++)
find_sub_basic_blocks (BASIC_BLOCK (i));
}
if (changed && upd_life)
{
count_or_remove_death_notes (blocks, 1);
update_life_info (blocks, UPDATE_LIFE_LOCAL, PROP_DEATH_NOTES);
}
#ifdef ENABLE_CHECKING
verify_flow_info ();
#endif
sbitmap_free (blocks);
}

View File

@ -1387,6 +1387,7 @@ extern int reg_set_p PARAMS ((rtx, rtx));
extern rtx single_set_2 PARAMS ((rtx, rtx));
extern int multiple_sets PARAMS ((rtx));
extern int set_noop_p PARAMS ((rtx));
extern int noop_move_p PARAMS ((rtx));
extern rtx find_last_value PARAMS ((rtx, rtx *, rtx, int));
extern int refers_to_regno_p PARAMS ((unsigned int, unsigned int,
rtx, rtx *));

View File

@ -1020,6 +1020,45 @@ set_noop_p (set)
return (GET_CODE (src) == REG && GET_CODE (dst) == REG
&& REGNO (src) == REGNO (dst));
}
/* Return nonzero if an insn consists only of SETs, each of which only sets a
value to itself. */
int
noop_move_p (insn)
rtx insn;
{
rtx pat = PATTERN (insn);
/* Insns carrying these notes are useful later on. */
if (find_reg_note (insn, REG_EQUAL, NULL_RTX))
return 0;
if (GET_CODE (pat) == SET && set_noop_p (pat))
return 1;
if (GET_CODE (pat) == PARALLEL)
{
int i;
/* If nothing but SETs of registers to themselves,
this insn can also be deleted. */
for (i = 0; i < XVECLEN (pat, 0); i++)
{
rtx tem = XVECEXP (pat, 0, i);
if (GET_CODE (tem) == USE
|| GET_CODE (tem) == CLOBBER)
continue;
if (GET_CODE (tem) != SET || ! set_noop_p (tem))
return 0;
}
return 1;
}
return 0;
}
/* Return the last thing that X was assigned from before *PINSN. If VALID_TO
is not NULL_RTX then verify that the object is not modified up to VALID_TO.

View File

@ -3265,7 +3265,7 @@ rest_of_compilation (decl)
timevar_pop (TV_JUMP);
timevar_push (TV_FLOW);
find_basic_blocks (insns, max_reg_num (), rtl_dump_file);
purge_all_dead_edges ();
cleanup_cfg (CLEANUP_EXPENSIVE);
/* Blimey. We've got to have the CFG up to date for the call to
@ -3441,16 +3441,6 @@ rest_of_compilation (decl)
timevar_pop (TV_RELOAD_CSE_REGS);
}
/* If optimizing, then go ahead and split insns now since we are about
to recompute flow information anyway. */
if (optimize > 0)
{
int old_labelnum = max_label_num ();
split_all_insns (0);
rebuild_label_notes_after_reload |= old_labelnum != max_label_num ();
}
/* Register allocation and reloading may have turned an indirect jump into
a direct jump. If so, we must rebuild the JUMP_LABEL fields of
jumping instructions. */
@ -3470,6 +3460,11 @@ rest_of_compilation (decl)
open_dump_file (DFI_flow2, decl);
find_basic_blocks (insns, max_reg_num (), rtl_dump_file);
/* If optimizing, then go ahead and split insns now. */
if (optimize > 0)
split_all_insns (0);
cleanup_cfg (0);
/* On some machines, the prologue and epilogue code, or parts thereof,