- do rough calc of how many regs are needed in each block, and a rough
calc of how many regs are available in each class and use that to
throttle back the code in cases where RTX_COST is minimal.
- - dead store elimination
- a store to the same address as a load does not kill the load if the
source of the store is also the destination of the load. Handling this
allows more load motion, particularly out of loops.
#define obstack_chunk_alloc gmalloc
#define obstack_chunk_free free
-/* Maximum number of passes to perform. */
-#define MAX_PASSES 1
-
/* Propagate flow information through back edges and thus enable PRE's
moving loop invariant calculations out of loops.
substitutions.
PRE is quite expensive in complicated functions because the DFA can take
- awhile to converge. Hence we only perform one pass. Macro MAX_PASSES can
+ awhile to converge. Hence we only perform one pass. The parameter max-gcse-passes can
be modified if one wants to experiment.
**********************
/* This array parallels modify_mem_list, but is kept canonicalized. */
static rtx * canon_modify_mem_list;
-
-/* For each block, non-zero if memory is set in that block.
- This is computed during hash table computation and is used by
- expr_killed_p and compute_transp.
- ??? Handling of memory is very simple, we don't make any attempt
- to optimize things (later).
- ??? This can be computed by compute_sets since the information
- doesn't change. */
-static char *mem_set_in_block;
-
/* Various variables for statistics gathering. */
/* Memory used in a pass.
static void compute_local_properties PARAMS ((sbitmap *, sbitmap *, sbitmap *,
int));
static void compute_cprop_data PARAMS ((void));
-static void find_used_regs PARAMS ((rtx));
+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));
a high connectivity will take a long time and is unlikely to be
particularly useful.
- In normal circumstances a cfg should have about twice has many edges
+ In normal circumstances a cfg should have about twice as many edges
as blocks. But we do not want to punish small functions which have
a couple switch statements. So we require a relatively large number
of basic blocks and the ratio of edges to blocks to be high. */
max_pass_bytes = 0;
gcse_obstack_bottom = gcse_alloc (1);
changed = 1;
- while (changed && pass < MAX_PASSES)
+ while (changed && pass < MAX_GCSE_PASSES)
{
changed = 0;
if (file)
pass, pass > 1 ? "es" : "", max_pass_bytes);
}
- obstack_free (&gcse_obstack, NULL_PTR);
+ obstack_free (&gcse_obstack, NULL);
free_reg_set_mem ();
/* We are finished with alias. */
end_alias_analysis ();
#else
reg = gen_rtx_REG ((enum machine_mode) i, LAST_VIRTUAL_REGISTER + 1);
insn = emit_insn (gen_rtx_SET (VOIDmode, reg, reg));
- if (recog (PATTERN (insn), insn, NULL_PTR) >= 0)
+ if (recog (PATTERN (insn), insn, NULL) >= 0)
can_copy_p[i] = 1;
#endif
}
/* Allocate vars to track sets of regs, memory per block. */
reg_set_in_block = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks,
max_gcse_regno);
- mem_set_in_block = (char *) gmalloc (n_basic_blocks);
/* Allocate array to keep a list of insns which modify memory in each
basic block. */
modify_mem_list = (rtx *) gmalloc (n_basic_blocks * sizeof (rtx *));
free (reg_set_bitmap);
- free (reg_set_in_block);
- free (mem_set_in_block);
+ sbitmap_vector_free (reg_set_in_block);
/* re-Cache any INSN_LIST nodes we have allocated. */
{
int i;
free_reg_set_mem ()
{
free (reg_set_table);
- obstack_free (®_set_obstack, NULL_PTR);
+ obstack_free (®_set_obstack, NULL);
}
/* Record REGNO in the reg_set table. */
static int *reg_first_set;
static int *reg_last_set;
-/* While computing "first/last set" info, this is the CUID of first/last insn
- to set memory or -1 if not set. `mem_last_set' is also used when
- performing GCSE to record whether memory has been set since the beginning
- of the block.
-
- Note that handling of memory is very simple, we don't make any attempt
- to optimize things (later). */
-static int mem_first_set;
-static int mem_last_set;
/* See whether X, the source of a set, is something we want to consider for
GCSE. */
if (load_killed_in_block_p (BLOCK_FOR_INSN (insn), INSN_CUID (insn),
x, avail_p))
return 0;
- if (avail_p && mem_last_set != NEVER_SET
- && mem_last_set >= INSN_CUID (insn))
- return 0;
- else if (! avail_p && mem_first_set != NEVER_SET
- && mem_first_set < INSN_CUID (insn))
- return 0;
else
return oprs_unchanged_p (XEXP (x, 0), insn, avail_p);
/* Is SET_SRC something we want to gcse? */
&& want_to_gcse_p (src)
/* Don't CSE a nop. */
- && ! set_noop_p (pat))
+ && ! set_noop_p (pat)
+ /* Don't GCSE if it has attached REG_EQUIV note.
+ At this point this only function parameters should have
+ REG_EQUIV notes and if the argument slot is used somewhere
+ explicitely, it means address of parameter has been taken,
+ so we should not extend the lifetime of the pseudo. */
+ && ((note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) == 0
+ || GET_CODE (XEXP (note, 0)) != MEM))
{
/* An expression is not anticipatable if its operands are
modified before this insn or if this is not the only SET in
alloc_INSN_LIST (dest, canon_modify_mem_list[BLOCK_NUM (insn)]);
}
-/* Record memory first/last/block set information for INSN. */
/* Record memory modification information for INSN. We do not actually care
about the memory location(s) that are set, or even how they are set (consider
a CALL_INSN). We merely need to record which insns modify memory. */
record_last_mem_set_info (insn)
rtx insn;
{
- if (mem_first_set == NEVER_SET)
- mem_first_set = INSN_CUID (insn);
-
- mem_last_set = INSN_CUID (insn);
- mem_set_in_block[BLOCK_NUM (insn)] = 1;
+ /* load_killed_in_block_p will handle the case of calls clobbering
+ everything. */
modify_mem_list[BLOCK_NUM (insn)] =
alloc_INSN_LIST (insn, modify_mem_list[BLOCK_NUM (insn)]);
/* While we compute the hash table we also compute a bit array of which
registers are set in which blocks.
- We also compute which blocks set memory, in the absence of aliasing
- support [which is TODO].
??? This isn't needed during const/copy propagation, but it's cheap to
compute. Later. */
sbitmap_vector_zero (reg_set_in_block, n_basic_blocks);
- memset ((char *) mem_set_in_block, 0, n_basic_blocks);
/* re-Cache any INSN_LIST nodes we have allocated. */
{
/* First pass over the instructions records information used to
determine when registers and memory are first and last set.
- ??? The mem_set_in_block and hard-reg reg_set_in_block computation
+ ??? hard-reg reg_set_in_block computation
could be moved to compute_sets since they currently don't change. */
for (i = 0; i < max_gcse_regno; i++)
reg_first_set[i] = reg_last_set[i] = NEVER_SET;
- mem_first_set = NEVER_SET;
- mem_last_set = NEVER_SET;
for (insn = BLOCK_HEAD (bb);
insn && insn != NEXT_INSN (BLOCK_END (bb));
if (GET_CODE (insn) == CALL_INSN)
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if ((call_used_regs[regno]
- && regno != STACK_POINTER_REGNUM
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && regno != HARD_FRAME_POINTER_REGNUM
-#endif
-#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
-#endif
-#if !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
- && ! (regno == PIC_OFFSET_TABLE_REGNUM && flag_pic)
-#endif
-
- && regno != FRAME_POINTER_REGNUM)
- || global_regs[regno])
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
record_last_reg_set_info (insn, regno);
if (! CONST_CALL_P (insn))
/* Also keep a record of the last instruction to modify memory.
For now this is very trivial, we only record whether any memory
location has been modified. */
- mem_last_set = 0;
{
int i;
if (load_killed_in_block_p (BLOCK_FOR_INSN (insn),
INSN_CUID (insn), x, 0))
return 0;
- if (mem_last_set != 0)
- return 0;
else
return oprs_not_set_p (XEXP (x, 0), insn);
mark_call (insn)
rtx insn;
{
- mem_last_set = INSN_CUID (insn);
if (! CONST_CALL_P (insn))
record_last_mem_set_info (insn);
}
else if (GET_CODE (dest) == MEM)
record_last_mem_set_info (insn);
- if (GET_CODE (dest) == REG)
- SET_BIT (reg_set_bitmap, REGNO (dest));
- else if (GET_CODE (dest) == MEM)
- mem_last_set = INSN_CUID (insn);
-
if (GET_CODE (SET_SRC (pat)) == CALL)
mark_call (insn);
}
if (GET_CODE (clob) == REG)
SET_BIT (reg_set_bitmap, REGNO (clob));
else
- mem_last_set = INSN_CUID (insn);
- if (GET_CODE (clob) == REG)
- SET_BIT (reg_set_bitmap, REGNO (clob));
- else
record_last_mem_set_info (insn);
}
static void
free_rd_mem ()
{
- free (rd_kill);
- free (rd_gen);
- free (reaching_defs);
- free (rd_out);
+ sbitmap_vector_free (rd_kill);
+ sbitmap_vector_free (rd_gen);
+ sbitmap_vector_free (reaching_defs);
+ sbitmap_vector_free (rd_out);
}
/* Add INSN to the kills of BB. REGNO, set in BB, is killed by INSN. */
if (GET_CODE (insn) == CALL_INSN)
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- {
- if ((call_used_regs[regno]
- && regno != STACK_POINTER_REGNUM
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && regno != HARD_FRAME_POINTER_REGNUM
-#endif
-#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && ! (regno == ARG_POINTER_REGNUM
- && fixed_regs[regno])
-#endif
-#if !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
- && ! (regno == PIC_OFFSET_TABLE_REGNUM && flag_pic)
-#endif
- && regno != FRAME_POINTER_REGNUM)
- || global_regs[regno])
- handle_rd_kill_set (insn, regno, BASIC_BLOCK (bb));
- }
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+ handle_rd_kill_set (insn, regno, BASIC_BLOCK (bb));
}
if (GET_CODE (pat) == PARALLEL)
static void
free_avail_expr_mem ()
{
- free (ae_kill);
- free (ae_gen);
- free (ae_in);
- free (ae_out);
+ sbitmap_vector_free (ae_kill);
+ sbitmap_vector_free (ae_gen);
+ sbitmap_vector_free (ae_in);
+ sbitmap_vector_free (ae_out);
}
/* Compute the set of available expressions generated in each basic block. */
case MEM:
if (load_killed_in_block_p (bb, get_max_uid () + 1, x, 0))
return 1;
- if (mem_set_in_block[bb->index])
- return 1;
else
return expr_killed_p (XEXP (x, 0), bb);
rtx insn;
struct expr *expr;
{
- rtx pat, insn_computes_expr;
+ rtx pat, insn_computes_expr, expr_set;
rtx to;
struct reg_set *this_reg;
int found_setting, use_src;
insn_computes_expr = computing_insn (expr, insn);
if (insn_computes_expr == NULL)
return 0;
+ expr_set = single_set (insn_computes_expr);
+ if (!expr_set)
+ abort ();
found_setting = 0;
use_src = 0;
/* At this point we know only one computation of EXPR outside of this
block reaches this insn. Now try to find a register that the
expression is computed into. */
- if (GET_CODE (SET_SRC (PATTERN (insn_computes_expr))) == REG)
+ if (GET_CODE (SET_SRC (expr_set)) == REG)
{
/* This is the case when the available expression that reaches
here has already been handled as an available expression. */
unsigned int regnum_for_replacing
- = REGNO (SET_SRC (PATTERN (insn_computes_expr)));
+ = REGNO (SET_SRC (expr_set));
/* If the register was created by GCSE we can't use `reg_set_table',
however we know it's set only once. */
if (!found_setting)
{
unsigned int regnum_for_replacing
- = REGNO (SET_DEST (PATTERN (insn_computes_expr)));
+ = REGNO (SET_DEST (expr_set));
/* This shouldn't happen. */
if (regnum_for_replacing >= max_gcse_regno)
{
pat = PATTERN (insn);
if (use_src)
- to = SET_SRC (PATTERN (insn_computes_expr));
+ to = SET_SRC (expr_set);
else
- to = SET_DEST (PATTERN (insn_computes_expr));
+ to = SET_DEST (expr_set);
changed = validate_change (insn, &SET_SRC (pat), to, 0);
/* We should be able to ignore the return code from validate_change but
replace all uses of REGB with REGN. */
rtx new_insn;
- to = gen_reg_rtx (GET_MODE (SET_DEST (PATTERN (insn_computes_expr))));
+ to = gen_reg_rtx (GET_MODE (SET_DEST (expr_set)));
/* Generate the new insn. */
/* ??? If the change fails, we return 0, even though we created
an insn. I think this is ok. */
new_insn
= emit_insn_after (gen_rtx_SET (VOIDmode, to,
- SET_DEST (PATTERN
- (insn_computes_expr))),
+ SET_DEST (expr_set)),
insn_computes_expr);
/* Keep block number table up to date. */
static void
free_cprop_mem ()
{
- free (cprop_pavloc);
- free (cprop_absaltered);
- free (cprop_avin);
- free (cprop_avout);
+ sbitmap_vector_free (cprop_pavloc);
+ sbitmap_vector_free (cprop_absaltered);
+ sbitmap_vector_free (cprop_avin);
+ sbitmap_vector_free (cprop_avout);
}
/* For each block, compute whether X is transparent. X is either an
list_entry = XEXP (list_entry, 1);
}
}
- if (set_p)
- {
- for (bb = 0; bb < n_basic_blocks; bb++)
- if (mem_set_in_block[bb])
- SET_BIT (bmap[bb], indx);
- }
- else
- {
- for (bb = 0; bb < n_basic_blocks; bb++)
- if (mem_set_in_block[bb])
- RESET_BIT (bmap[bb], indx);
- }
x = XEXP (x, 0);
goto repeat;
This doesn't hurt anything but it will slow things down. */
static void
-find_used_regs (x)
- rtx x;
+find_used_regs (xptr, data)
+ rtx *xptr;
+ void *data ATTRIBUTE_UNUSED;
{
int i, j;
enum rtx_code code;
const char *fmt;
+ rtx x = *xptr;
/* repeat is used to turn tail-recursion into iteration since GCC
can't do it when there's no return value. */
repeat:
-
if (x == 0)
return;
code = GET_CODE (x);
- switch (code)
+ if (REG_P (x))
{
- case REG:
if (reg_use_count == MAX_USES)
return;
reg_use_table[reg_use_count].reg_rtx = x;
reg_use_count++;
- return;
-
- case MEM:
- x = XEXP (x, 0);
- goto repeat;
-
- case PC:
- case CC0:
- case CONST:
- case CONST_INT:
- case CONST_DOUBLE:
- case SYMBOL_REF:
- case LABEL_REF:
- case CLOBBER:
- case ADDR_VEC:
- case ADDR_DIFF_VEC:
- case ASM_INPUT: /*FIXME*/
- return;
-
- case SET:
- if (GET_CODE (SET_DEST (x)) == MEM)
- find_used_regs (SET_DEST (x));
- x = SET_SRC (x);
- goto repeat;
-
- default:
- break;
}
/* Recursively scan the operands of this expression. */
goto repeat;
}
- find_used_regs (XEXP (x, i));
+ find_used_regs (&XEXP (x, i), data);
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
- find_used_regs (XVECEXP (x, i, j));
+ find_used_regs (&XVECEXP (x, i, j), data);
}
}
int success = 0;
rtx set = single_set (insn);
- /* If this is a single set, try to simplify the source of the set given
- our substitution. We could perhaps try this for multiple SETs, but
- it probably won't buy us anything. */
- if (set != 0)
+ success = validate_replace_src (from, to, insn);
+
+ /* If above failed and this is a single set, try to simplify the source of
+ the set given our substitution. We could perhaps try this for multiple
+ SETs, but it probably won't buy us anything. */
+ if (!success && set != 0)
{
src = simplify_replace_rtx (SET_SRC (set), from, to);
- /* Try this two ways: first just replace SET_SRC. If that doesn't
- work and this is a PARALLEL, try to replace the whole pattern
- with a new SET. */
- if (validate_change (insn, &SET_SRC (set), src, 0))
- success = 1;
- else if (GET_CODE (PATTERN (insn)) == PARALLEL
- && validate_change (insn, &PATTERN (insn),
- gen_rtx_SET (VOIDmode, SET_DEST (set),
- src),
- 0))
+ if (!rtx_equal_p (src, SET_SRC (set))
+ && validate_change (insn, &SET_SRC (set), src, 0))
success = 1;
}
- /* Otherwise, try to do a global replacement within the insn. */
- if (!success)
- success = validate_replace_src (from, to, insn);
-
/* If we've failed to do replacement, have a single SET, and don't already
have a note, add a REG_EQUAL note to not lose information. */
if (!success && note == 0 && set != 0)
int changed = 0;
rtx note;
- /* Only propagate into SETs. Note that a conditional jump is a
- SET with pc_rtx as the destination. */
- if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
+ if (!INSN_P (insn))
return 0;
reg_use_count = 0;
- find_used_regs (PATTERN (insn));
+ note_uses (&PATTERN (insn), find_used_regs, NULL);
note = find_reg_equal_equiv_note (insn);
/* We may win even when propagating constants into notes. */
if (note)
- find_used_regs (XEXP (note, 0));
+ find_used_regs (&XEXP (note, 0), NULL);
for (reg_used = ®_use_table[0]; reg_use_count > 0;
reg_used++, reg_use_count--)
static void
free_pre_mem ()
{
- free (transp);
- free (comp);
+ sbitmap_vector_free (transp);
+ sbitmap_vector_free (comp);
/* ANTLOC and AE_KILL are freed just after pre_lcm finishes. */
if (pre_optimal)
- free (pre_optimal);
+ sbitmap_vector_free (pre_optimal);
if (pre_redundant)
- free (pre_redundant);
+ sbitmap_vector_free (pre_redundant);
if (pre_insert_map)
- free (pre_insert_map);
+ sbitmap_vector_free (pre_insert_map);
if (pre_delete_map)
- free (pre_delete_map);
-
+ sbitmap_vector_free (pre_delete_map);
if (ae_in)
- free (ae_in);
+ sbitmap_vector_free (ae_in);
if (ae_out)
- free (ae_out);
+ sbitmap_vector_free (ae_out);
transp = comp = NULL;
pre_optimal = pre_redundant = pre_insert_map = pre_delete_map = NULL;
edge_list = pre_edge_lcm (gcse_file, n_exprs, transp, comp, antloc,
ae_kill, &pre_insert_map, &pre_delete_map);
- free (antloc);
+ sbitmap_vector_free (antloc);
antloc = NULL;
- free (ae_kill);
+ sbitmap_vector_free (ae_kill);
ae_kill = NULL;
free (trapping_expr);
}
if (REGNO (XEXP (XEXP (p, 0), 0)) >= FIRST_PSEUDO_REGISTER)
abort ();
+ /* We only care about registers which can hold function
+ arguments. */
+ if (! FUNCTION_ARG_REGNO_P (REGNO (XEXP (XEXP (p, 0), 0))))
+ continue;
+
SET_HARD_REG_BIT (parm_regs, REGNO (XEXP (XEXP (p, 0), 0)));
nparm_regs++;
}
}
}
- free (inserted);
+ sbitmap_vector_free (inserted);
return did_insert;
}
if (!set)
abort ();
- new_insn = emit_insn_after (gen_rtx_SET (VOIDmode, reg, SET_DEST (set)),
- insn);
+ new_insn = emit_insn_after (gen_move_insn (reg, SET_DEST (set)), insn);
/* Keep block number table up to date. */
set_block_for_new_insns (new_insn, bb);
a high connectivity will take a long time and is unlikely to be
particularly useful.
- In normal circumstances a cfg should have about twice has many edges
+ In normal circumstances a cfg should have about twice as many edges
as blocks. But we do not want to punish small functions which have
a couple switch statements. So we require a relatively large number
of basic blocks and the ratio of edges to blocks to be high. */
free (block_reg);
/* Free bitmaps. */
- free (npi.nonnull_local);
- free (npi.nonnull_killed);
- free (nonnull_avin);
- free (nonnull_avout);
+ sbitmap_vector_free (npi.nonnull_local);
+ sbitmap_vector_free (npi.nonnull_killed);
+ sbitmap_vector_free (nonnull_avin);
+ sbitmap_vector_free (nonnull_avout);
}
/* Code Hoisting variables and subroutines. */
static void
free_code_hoist_mem ()
{
- free (antloc);
- free (transp);
- free (comp);
+ sbitmap_vector_free (antloc);
+ sbitmap_vector_free (transp);
+ sbitmap_vector_free (comp);
- free (hoist_vbein);
- free (hoist_vbeout);
- free (hoist_exprs);
- free (transpout);
+ sbitmap_vector_free (hoist_vbein);
+ sbitmap_vector_free (hoist_vbeout);
+ sbitmap_vector_free (hoist_exprs);
+ sbitmap_vector_free (transpout);
- free (dominators);
+ sbitmap_vector_free (dominators);
}
/* Compute the very busy expressions at entry/exit from each block.
if (GET_CODE (insn) == CALL_INSN)
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if ((call_used_regs[regno]
- && regno != STACK_POINTER_REGNUM
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && regno != HARD_FRAME_POINTER_REGNUM
-#endif
-#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
-#endif
-#if defined (PIC_OFFSET_TABLE_REGNUM) && !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
- && ! (regno == PIC_OFFSET_TABLE_REGNUM && flag_pic)
-#endif
-
- && regno != FRAME_POINTER_REGNUM)
- || global_regs[regno])
- SET_BIT (reg_set_in_block[bb], regno);
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+ SET_BIT (reg_set_in_block[bb], regno);
}
pat = PATTERN (insn);
free_ldst_mems ();
if (ae_gen)
- free (ae_gen);
+ sbitmap_vector_free (ae_gen);
if (ae_kill)
- free (ae_kill);
+ sbitmap_vector_free (ae_kill);
if (transp)
- free (transp);
+ sbitmap_vector_free (transp);
if (st_antloc)
- free (st_antloc);
+ sbitmap_vector_free (st_antloc);
if (pre_insert_map)
- free (pre_insert_map);
+ sbitmap_vector_free (pre_insert_map);
if (pre_delete_map)
- free (pre_delete_map);
+ sbitmap_vector_free (pre_delete_map);
if (reg_set_in_block)
- free (reg_set_in_block);
+ sbitmap_vector_free (reg_set_in_block);
ae_gen = ae_kill = transp = st_antloc = NULL;
pre_insert_map = pre_delete_map = reg_set_in_block = NULL;
num_stores = compute_store_table ();
if (num_stores == 0)
{
- free (reg_set_in_block);
+ sbitmap_vector_free (reg_set_in_block);
end_alias_analysis ();
return;
}