and global constant/copy propagation for GNU compiler.
Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
/* TODO
- reordering of memory allocation and freeing to be more space efficient
- 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.
#include "function.h"
#include "expr.h"
#include "ggc.h"
+#include "params.h"
#include "obstack.h"
#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));
+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 ((int, int, rtx, int));
+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));
static void free_pre_mem PARAMS ((void));
static void compute_pre_data PARAMS ((void));
-static int pre_expr_reaches_here_p PARAMS ((int, struct expr *, int));
-static void insert_insn_end_bb PARAMS ((struct expr *, int, int));
+static int pre_expr_reaches_here_p PARAMS ((basic_block, struct expr *,
+ basic_block));
+static void insert_insn_end_bb PARAMS ((struct expr *, basic_block, int));
static void pre_insert_copy_insn PARAMS ((struct expr *, rtx));
static void pre_insert_copies PARAMS ((void));
static int pre_delete PARAMS ((void));
static void free_code_hoist_mem PARAMS ((void));
static void compute_code_hoist_vbeinout PARAMS ((void));
static void compute_code_hoist_data PARAMS ((void));
-static int hoist_expr_reaches_here_p PARAMS ((int, int, int, char *));
+static int hoist_expr_reaches_here_p PARAMS ((basic_block, int, basic_block,
+ char *));
static void hoist_code PARAMS ((void));
static int one_code_hoisting_pass PARAMS ((void));
static void alloc_rd_mem PARAMS ((int, int));
static void free_rd_mem PARAMS ((void));
-static void handle_rd_kill_set PARAMS ((rtx, int, int));
+static void handle_rd_kill_set PARAMS ((rtx, int, basic_block));
static void compute_kill_rd PARAMS ((void));
static void compute_rd PARAMS ((void));
static void alloc_avail_expr_mem PARAMS ((int, int));
static void free_avail_expr_mem PARAMS ((void));
static void compute_ae_gen PARAMS ((void));
-static int expr_killed_p PARAMS ((rtx, int));
+static int expr_killed_p PARAMS ((rtx, basic_block));
static void compute_ae_kill PARAMS ((sbitmap *, sbitmap *));
static int expr_reaches_here_p PARAMS ((struct occr *, struct expr *,
- int, int));
+ basic_block, int));
static rtx computing_insn PARAMS ((struct expr *, rtx));
static int def_reaches_here_p PARAMS ((rtx, rtx));
static int can_disregard_other_sets PARAMS ((struct reg_set **, rtx, int));
static rtx process_insert_insn PARAMS ((struct expr *));
static int pre_edge_insert PARAMS ((struct edge_list *, struct expr **));
static int expr_reaches_here_p_work PARAMS ((struct occr *, struct expr *,
- int, int, char *));
-static int pre_expr_reaches_here_p_work PARAMS ((int, struct expr *,
- int, char *));
+ basic_block, int, char *));
+static int pre_expr_reaches_here_p_work PARAMS ((basic_block, struct expr *,
+ basic_block, char *));
static struct ls_expr * ldst_entry PARAMS ((rtx));
static void free_ldst_entry PARAMS ((struct ls_expr *));
static void free_ldst_mems PARAMS ((void));
static void trim_ld_motion_mems PARAMS ((void));
static void update_ld_motion_stores PARAMS ((struct expr *));
static void reg_set_info PARAMS ((rtx, rtx, void *));
-static int store_ops_ok PARAMS ((rtx, int));
+static int store_ops_ok PARAMS ((rtx, basic_block));
static void find_moveable_store PARAMS ((rtx));
static int compute_store_table PARAMS ((void));
static int load_kills_store PARAMS ((rtx, rtx));
static int find_loads PARAMS ((rtx, rtx));
static int store_killed_in_insn PARAMS ((rtx, rtx));
-static int store_killed_after PARAMS ((rtx, rtx, int));
-static int store_killed_before PARAMS ((rtx, rtx, int));
+static int store_killed_after PARAMS ((rtx, rtx, basic_block));
+static int store_killed_before PARAMS ((rtx, rtx, basic_block));
static void build_store_vectors PARAMS ((void));
-static void insert_insn_start_bb PARAMS ((rtx, int));
+static void insert_insn_start_bb PARAMS ((rtx, basic_block));
static int insert_store PARAMS ((struct ls_expr *, edge));
-static void replace_store_insn PARAMS ((rtx, rtx, int));
-static void delete_store PARAMS ((struct ls_expr *, int));
+static void replace_store_insn PARAMS ((rtx, rtx, basic_block));
+static void delete_store PARAMS ((struct ls_expr *,
+ basic_block));
static void free_store_memory PARAMS ((void));
static void store_motion PARAMS ((void));
\f
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. */
return 0;
}
+ /* If allocating memory for the cprop bitmap would take up too much
+ storage it's better just to disable the optimization. */
+ if ((n_basic_blocks
+ * SBITMAP_SET_SIZE (max_gcse_regno)
+ * sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
+ {
+ if (warn_disabled_optimization)
+ warning ("GCSE disabled: %d basic blocks and %d registers",
+ n_basic_blocks, max_gcse_regno);
+
+ return 0;
+ }
+
/* See what modes support reg/reg copy operations. */
if (! can_copy_init_p)
{
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. */
\f
/* Hash table support. */
-/* For each register, the cuid of the first/last insn in the block to set it,
- or -1 if not set. */
+/* For each register, the cuid of the first/last insn in the block
+ that set it, or -1 if not set. */
#define NEVER_SET -1
-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.
+struct reg_avail_info
+{
+ int last_bb;
+ int first_set;
+ int last_set;
+};
+
+static struct reg_avail_info *reg_avail_info;
+static int current_bb;
- 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. */
switch (code)
{
case REG:
- if (avail_p)
- return (reg_last_set[REGNO (x)] == NEVER_SET
- || reg_last_set[REGNO (x)] < INSN_CUID (insn));
- else
- return (reg_first_set[REGNO (x)] == NEVER_SET
- || reg_first_set[REGNO (x)] >= INSN_CUID (insn));
+ {
+ struct reg_avail_info *info = ®_avail_info[REGNO (x)];
+
+ if (info->last_bb != current_bb)
+ return 1;
+ if (avail_p)
+ return info->last_set < INSN_CUID (insn);
+ else
+ return info->first_set >= INSN_CUID (insn);
+ }
case MEM:
- if (load_killed_in_block_p (BLOCK_NUM (insn), INSN_CUID (insn),
+ if (load_killed_in_block_p (BASIC_BLOCK (current_bb), 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);
static int
load_killed_in_block_p (bb, uid_limit, x, avail_p)
- int bb;
+ basic_block bb;
int uid_limit;
rtx x;
int avail_p;
{
- rtx list_entry = modify_mem_list[bb];
+ rtx list_entry = modify_mem_list[bb->index];
while (list_entry)
{
rtx setter;
/* 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
this insn. */
int antic_p = oprs_anticipatable_p (src, insn) && single_set (insn);
/* An expression is not available if its operands are
- subsequently modified, including this insn. */
- int avail_p = oprs_available_p (src, insn);
+ subsequently modified, including this insn. It's also not
+ available if this is a branch, because we can't insert
+ a set after the branch. */
+ int avail_p = (oprs_available_p (src, insn)
+ && ! JUMP_P (insn));
insert_expr_in_table (src, GET_MODE (dest), insn, antic_p, avail_p);
}
/* Record register first/last/block set information for REGNO in INSN.
- reg_first_set records the first place in the block where the register
+ first_set records the first place in the block where the register
is set and is used to compute "anticipatability".
- reg_last_set records the last place in the block where the register
+ last_set records the last place in the block where the register
is set and is used to compute "availability".
+ last_bb records the block for which first_set and last_set are
+ valid, as a quick test to invalidate them.
+
reg_set_in_block records whether the register is set in the block
and is used to compute "transparency". */
rtx insn;
int regno;
{
- if (reg_first_set[regno] == NEVER_SET)
- reg_first_set[regno] = INSN_CUID (insn);
+ struct reg_avail_info *info = ®_avail_info[regno];
+ int cuid = INSN_CUID (insn);
- reg_last_set[regno] = INSN_CUID (insn);
- SET_BIT (reg_set_in_block[BLOCK_NUM (insn)], regno);
+ info->last_set = cuid;
+ if (info->last_bb != current_bb)
+ {
+ info->last_bb = current_bb;
+ info->first_set = cuid;
+ SET_BIT (reg_set_in_block[current_bb], regno);
+ }
}
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)]);
{
/* Note that traversals of this loop (other than for free-ing)
will break after encountering a CALL_INSN. So, there's no
- need to insert a pair of items, as canon_list_insert does. */
+ need to insert a pair of items, as canon_list_insert does. */
canon_modify_mem_list[BLOCK_NUM (insn)] =
alloc_INSN_LIST (insn, canon_modify_mem_list[BLOCK_NUM (insn)]);
}
compute_hash_table (set_p)
int set_p;
{
- int bb;
+ unsigned int i;
/* 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. */
{
}
}
/* Some working arrays used to track first and last set in each block. */
- /* ??? One could use alloca here, but at some size a threshold is crossed
- beyond which one should use malloc. Are we at that threshold here? */
- reg_first_set = (int *) gmalloc (max_gcse_regno * sizeof (int));
- reg_last_set = (int *) gmalloc (max_gcse_regno * sizeof (int));
+ reg_avail_info = (struct reg_avail_info*)
+ gmalloc (max_gcse_regno * sizeof (struct reg_avail_info));
- for (bb = 0; bb < n_basic_blocks; bb++)
+ for (i = 0; i < max_gcse_regno; ++i)
+ reg_avail_info[i].last_bb = NEVER_SET;
+
+ for (current_bb = 0; current_bb < n_basic_blocks; current_bb++)
{
rtx insn;
unsigned int regno;
int in_libcall_block;
- unsigned int i;
/* 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));
+ for (insn = BLOCK_HEAD (current_bb);
+ insn && insn != NEXT_INSN (BLOCK_END (current_bb));
insn = NEXT_INSN (insn))
{
-#ifdef NON_SAVING_SETJMP
- if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
- {
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- record_last_reg_set_info (insn, regno);
- continue;
- }
-#endif
-
if (! INSN_P (insn))
continue;
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)
+ bool clobbers_all = false;
+#ifdef NON_SAVING_SETJMP
+ if (NON_SAVING_SETJMP
+ && find_reg_note (insn, REG_SETJMP, NULL_RTX))
+ clobbers_all = true;
#endif
- && regno != FRAME_POINTER_REGNUM)
- || global_regs[regno])
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (clobbers_all
+ || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
record_last_reg_set_info (insn, regno);
- if (! CONST_CALL_P (insn))
- record_last_mem_set_info (insn);
+ mark_call (insn);
}
note_stores (PATTERN (insn), record_last_set_info, insn);
/* The next pass builds the hash table. */
- for (insn = BLOCK_HEAD (bb), in_libcall_block = 0;
- insn && insn != NEXT_INSN (BLOCK_END (bb));
+ for (insn = BLOCK_HEAD (current_bb), in_libcall_block = 0;
+ insn && insn != NEXT_INSN (BLOCK_END (current_bb));
insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
- in_libcall_block = 1;
- else if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
- in_libcall_block = 0;
- hash_scan_insn (insn, set_p, in_libcall_block);
+ in_libcall_block = 1;
+ else if (set_p && find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ in_libcall_block = 0;
+ hash_scan_insn (insn, set_p, in_libcall_block);
+ if (!set_p && find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ in_libcall_block = 0;
}
}
- free (reg_first_set);
- free (reg_last_set);
-
- /* Catch bugs early. */
- reg_first_set = reg_last_set = 0;
+ free (reg_avail_info);
+ reg_avail_info = NULL;
}
/* Allocate space for the set hash table.
/* 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;
return 1;
case MEM:
- if (load_killed_in_block_p (BLOCK_NUM (insn), INSN_CUID (insn), x, 0))
- return 0;
- if (mem_last_set != 0)
+ if (load_killed_in_block_p (BLOCK_FOR_INSN (insn),
+ INSN_CUID (insn), x, 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))
+ if (! CONST_OR_PURE_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. */
static void
handle_rd_kill_set (insn, regno, bb)
rtx insn;
- int regno, bb;
+ int regno;
+ basic_block bb;
{
struct reg_set *this_reg;
for (this_reg = reg_set_table[regno]; this_reg; this_reg = this_reg ->next)
if (BLOCK_NUM (this_reg->insn) != BLOCK_NUM (insn))
- SET_BIT (rd_kill[bb], INSN_CUID (this_reg->insn));
+ SET_BIT (rd_kill[bb->index], INSN_CUID (this_reg->insn));
}
/* Compute the set of kill's for reaching definitions. */
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, 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)
&& GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == REG)
handle_rd_kill_set (insn,
REGNO (XEXP (XVECEXP (pat, 0, i), 0)),
- bb);
+ BASIC_BLOCK (bb));
}
}
else if (GET_CODE (pat) == SET && GET_CODE (SET_DEST (pat)) == REG)
/* Each setting of this register outside of this block
must be marked in the set of kills in this block. */
- handle_rd_kill_set (insn, REGNO (SET_DEST (pat)), bb);
+ handle_rd_kill_set (insn, REGNO (SET_DEST (pat)), BASIC_BLOCK (bb));
}
}
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. */
static int
expr_killed_p (x, bb)
rtx x;
- int bb;
+ basic_block bb;
{
int i, j;
enum rtx_code code;
switch (code)
{
case REG:
- return TEST_BIT (reg_set_in_block[bb], REGNO (x));
+ return TEST_BIT (reg_set_in_block[bb->index], REGNO (x));
case MEM:
if (load_killed_in_block_p (bb, get_max_uid () + 1, x, 0))
return 1;
- if (mem_set_in_block[bb])
- return 1;
else
return expr_killed_p (XEXP (x, 0), bb);
if (TEST_BIT (ae_gen[bb], expr->bitmap_index))
continue;
- if (expr_killed_p (expr->expr, bb))
+ if (expr_killed_p (expr->expr, BASIC_BLOCK (bb)))
SET_BIT (ae_kill[bb], expr->bitmap_index);
}
}
expr_reaches_here_p_work (occr, expr, bb, check_self_loop, visited)
struct occr *occr;
struct expr *expr;
- int bb;
+ basic_block bb;
int check_self_loop;
char *visited;
{
edge pred;
- for (pred = BASIC_BLOCK(bb)->pred; pred != NULL; pred = pred->pred_next)
+ for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
{
- int pred_bb = pred->src->index;
+ basic_block pred_bb = pred->src;
- if (visited[pred_bb])
+ if (visited[pred_bb->index])
/* This predecessor has already been visited. Nothing to do. */
;
else if (pred_bb == bb)
{
/* BB loops on itself. */
if (check_self_loop
- && TEST_BIT (ae_gen[pred_bb], expr->bitmap_index)
- && BLOCK_NUM (occr->insn) == pred_bb)
+ && TEST_BIT (ae_gen[pred_bb->index], expr->bitmap_index)
+ && BLOCK_NUM (occr->insn) == pred_bb->index)
return 1;
- visited[pred_bb] = 1;
+ visited[pred_bb->index] = 1;
}
/* Ignore this predecessor if it kills the expression. */
- else if (TEST_BIT (ae_kill[pred_bb], expr->bitmap_index))
- visited[pred_bb] = 1;
+ else if (TEST_BIT (ae_kill[pred_bb->index], expr->bitmap_index))
+ visited[pred_bb->index] = 1;
/* Does this predecessor generate this expression? */
- else if (TEST_BIT (ae_gen[pred_bb], expr->bitmap_index))
+ else if (TEST_BIT (ae_gen[pred_bb->index], expr->bitmap_index))
{
/* Is this the occurrence we're looking for?
Note that there's only one generating occurrence per block
so we just need to check the block number. */
- if (BLOCK_NUM (occr->insn) == pred_bb)
+ if (BLOCK_NUM (occr->insn) == pred_bb->index)
return 1;
- visited[pred_bb] = 1;
+ visited[pred_bb->index] = 1;
}
/* Neither gen nor kill. */
else
{
- visited[pred_bb] = 1;
+ visited[pred_bb->index] = 1;
if (expr_reaches_here_p_work (occr, expr, pred_bb, check_self_loop,
visited))
}
/* This wrapper for expr_reaches_here_p_work() is to ensure that any
- memory allocated for that function is returned. */
+ memory allocated for that function is returned. */
static int
expr_reaches_here_p (occr, expr, bb, check_self_loop)
struct occr *occr;
struct expr *expr;
- int bb;
+ basic_block bb;
int check_self_loop;
{
int rval;
struct expr *expr;
rtx insn;
{
- int bb = BLOCK_NUM (insn);
+ basic_block bb = BLOCK_FOR_INSN (insn);
if (expr->avail_occr->next == NULL)
{
- if (BLOCK_NUM (expr->avail_occr->insn) == bb)
+ if (BLOCK_FOR_INSN (expr->avail_occr->insn) == bb)
/* The available expression is actually itself
(i.e. a loop in the flow graph) so do nothing. */
return NULL;
for (occr = expr->avail_occr; occr != NULL; occr = occr->next)
{
- if (BLOCK_NUM (occr->insn) == bb)
+ if (BLOCK_FOR_INSN (occr->insn) == bb)
{
/* The expression is generated in this block.
The only time we care about this is when the expression
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. */
- set_block_num (new_insn, BLOCK_NUM (insn_computes_expr));
+ set_block_for_new_insns (new_insn, BLOCK_FOR_INSN (insn_computes_expr));
/* Keep register set table up to date. */
record_one_set (REGNO (to), new_insn);
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)
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);
print_rtl (gcse_file, src);
fprintf (gcse_file, "\n");
}
+ purge_dead_edges (bb);
return 1;
}
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;
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. */
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;
{
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. */
+ /* 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--)
struct expr *set;
/* Ignore registers created by GCSE.
- We do this because ... */
+ We do this because ... */
if (regno >= max_gcse_regno)
continue;
&& 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
&& GET_CODE (NEXT_INSN (insn)) == JUMP_INSN
&& condjump_p (NEXT_INSN (insn))
&& ! simplejump_p (NEXT_INSN (insn))
- && cprop_cc0_jump (insn, reg_used, src))
+ && cprop_cc0_jump (bb, insn, reg_used, src))
{
changed = 1;
break;
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
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);
}
static int
pre_expr_reaches_here_p_work (occr_bb, expr, bb, visited)
- int occr_bb;
+ basic_block occr_bb;
struct expr *expr;
- int bb;
+ basic_block bb;
char *visited;
{
edge pred;
- for (pred = BASIC_BLOCK (bb)->pred; pred != NULL; pred = pred->pred_next)
+ for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
{
- int pred_bb = pred->src->index;
+ basic_block pred_bb = pred->src;
if (pred->src == ENTRY_BLOCK_PTR
/* Has predecessor has already been visited? */
- || visited[pred_bb])
+ || visited[pred_bb->index])
;/* Nothing to do. */
/* Does this predecessor generate this expression? */
- else if (TEST_BIT (comp[pred_bb], expr->bitmap_index))
+ else if (TEST_BIT (comp[pred_bb->index], expr->bitmap_index))
{
/* Is this the occurrence we're looking for?
Note that there's only one generating occurrence per block
if (occr_bb == pred_bb)
return 1;
- visited[pred_bb] = 1;
+ visited[pred_bb->index] = 1;
}
/* Ignore this predecessor if it kills the expression. */
- else if (! TEST_BIT (transp[pred_bb], expr->bitmap_index))
- visited[pred_bb] = 1;
+ else if (! TEST_BIT (transp[pred_bb->index], expr->bitmap_index))
+ visited[pred_bb->index] = 1;
/* Neither gen nor kill. */
else
{
- visited[pred_bb] = 1;
+ visited[pred_bb->index] = 1;
if (pre_expr_reaches_here_p_work (occr_bb, expr, pred_bb, visited))
return 1;
}
}
/* The wrapper for pre_expr_reaches_here_work that ensures that any
- memory allocated for that function is returned. */
+ memory allocated for that function is returned. */
static int
pre_expr_reaches_here_p (occr_bb, expr, bb)
- int occr_bb;
+ basic_block occr_bb;
struct expr *expr;
- int bb;
+ basic_block bb;
{
int rval;
char *visited = (char *) xcalloc (n_basic_blocks, 1);
static void
insert_insn_end_bb (expr, bb, pre)
struct expr *expr;
- int bb;
+ basic_block bb;
int pre;
{
- rtx insn = BLOCK_END (bb);
+ rtx insn = bb->end;
rtx new_insn;
rtx reg = expr->reaching_reg;
int regno = REGNO (reg);
}
#endif
/* FIXME: What if something in cc0/jump uses value set in new insn? */
- new_insn = emit_block_insn_before (pat, insn, BASIC_BLOCK (bb));
+ new_insn = emit_block_insn_before (pat, insn, bb);
}
/* Likewise if the last insn is a call, as will happen in the presence
of exception handling. */
else if (GET_CODE (insn) == CALL_INSN)
{
- HARD_REG_SET parm_regs;
- int nparm_regs;
- rtx p;
-
/* Keeping in mind SMALL_REGISTER_CLASSES and parameters in registers,
we search backward and place the instructions before the first
parameter is loaded. Do this for everyone for consistency and a
Check this. */
if (pre
- && !TEST_BIT (antloc[bb], expr->bitmap_index)
- && !TEST_BIT (transp[bb], expr->bitmap_index))
+ && !TEST_BIT (antloc[bb->index], expr->bitmap_index)
+ && !TEST_BIT (transp[bb->index], expr->bitmap_index))
abort ();
/* Since different machines initialize their parameter registers
in different orders, assume nothing. Collect the set of all
parameter registers. */
- CLEAR_HARD_REG_SET (parm_regs);
- nparm_regs = 0;
- for (p = CALL_INSN_FUNCTION_USAGE (insn); p ; p = XEXP (p, 1))
- if (GET_CODE (XEXP (p, 0)) == USE
- && GET_CODE (XEXP (XEXP (p, 0), 0)) == REG)
- {
- if (REGNO (XEXP (XEXP (p, 0), 0)) >= FIRST_PSEUDO_REGISTER)
- abort ();
-
- SET_HARD_REG_BIT (parm_regs, REGNO (XEXP (XEXP (p, 0), 0)));
- nparm_regs++;
- }
+ insn = find_first_parameter_load (insn, bb->head);
- /* Search backward for the first set of a register in this set. */
- while (nparm_regs && BLOCK_HEAD (bb) != insn)
- {
- insn = PREV_INSN (insn);
- p = single_set (insn);
- if (p && GET_CODE (SET_DEST (p)) == REG
- && REGNO (SET_DEST (p)) < FIRST_PSEUDO_REGISTER
- && TEST_HARD_REG_BIT (parm_regs, REGNO (SET_DEST (p))))
- {
- CLEAR_HARD_REG_BIT (parm_regs, REGNO (SET_DEST (p)));
- nparm_regs--;
- }
- }
-
/* If we found all the parameter loads, then we want to insert
before the first parameter load.
|| NOTE_INSN_BASIC_BLOCK_P (insn))
insn = NEXT_INSN (insn);
- new_insn = emit_block_insn_before (pat, insn, BASIC_BLOCK (bb));
+ new_insn = emit_block_insn_before (pat, insn, bb);
}
else
{
new_insn = emit_insn_after (pat, insn);
- BLOCK_END (bb) = new_insn;
+ bb->end = new_insn;
}
/* Keep block number table up to date.
{
rtx insn = XVECEXP (pat, 0, i);
- set_block_num (insn, bb);
+ set_block_for_insn (insn, bb);
if (INSN_P (insn))
add_label_notes (PATTERN (insn), new_insn);
else
{
add_label_notes (SET_SRC (pat), new_insn);
- set_block_num (new_insn, bb);
+ set_block_for_new_insns (new_insn, bb);
/* Keep register set table up to date. */
record_one_set (regno, new_insn);
if (gcse_file)
{
fprintf (gcse_file, "PRE/HOIST: end of bb %d, insn %d, ",
- bb, INSN_UID (new_insn));
+ bb->index, INSN_UID (new_insn));
fprintf (gcse_file, "copying expression %d to reg %d\n",
expr->bitmap_index, regno);
}
for (e = 0; e < num_edges; e++)
{
int indx;
- basic_block pred = INDEX_EDGE_PRED_BB (edge_list, e);
- int bb = pred->index;
+ basic_block bb = INDEX_EDGE_PRED_BB (edge_list, e);
for (i = indx = 0; i < set_size; i++, indx += SBITMAP_ELT_BITS)
{
if (gcse_file)
{
fprintf (gcse_file, "PRE/HOIST: edge (%d,%d), ",
- bb,
+ bb->index,
INDEX_EDGE_SUCC_BB (edge_list, e)->index);
fprintf (gcse_file, "copy expression %d\n",
expr->bitmap_index);
}
}
- free (inserted);
+ sbitmap_vector_free (inserted);
return did_insert;
}
int indx = expr->bitmap_index;
rtx set = single_set (insn);
rtx new_insn;
- int bb = BLOCK_NUM (insn);
+ basic_block bb = BLOCK_FOR_INSN (insn);
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_num (new_insn, bb);
+ set_block_for_new_insns (new_insn, bb);
/* Keep register set table up to date. */
record_one_set (regno, new_insn);
- if (insn == BLOCK_END (bb))
- BLOCK_END (bb) = new_insn;
+ if (insn == bb->end)
+ bb->end = new_insn;
gcse_create_count++;
"PRE: bb %d, insn %d, copy expression %d in insn %d to reg %d\n",
BLOCK_NUM (insn), INSN_UID (new_insn), indx,
INSN_UID (insn), regno);
+ update_ld_motion_stores (expr);
}
/* Copy available expressions that reach the redundant expression
continue;
/* Or if the expression doesn't reach the deleted one. */
- if (! pre_expr_reaches_here_p (BLOCK_NUM (avail->insn), expr,
- BLOCK_NUM (occr->insn)))
+ if (! pre_expr_reaches_here_p (BLOCK_FOR_INSN (avail->insn),
+ expr,
+ BLOCK_FOR_INSN (occr->insn)))
continue;
/* Copy the result of avail to reaching_reg. */
{
rtx insn = occr->insn;
rtx set;
- int bb = BLOCK_NUM (insn);
+ basic_block bb = BLOCK_FOR_INSN (insn);
- if (TEST_BIT (pre_delete_map[bb], indx))
+ if (TEST_BIT (pre_delete_map[bb->index], indx))
{
set = single_set (insn);
if (! set)
"PRE: redundant insn %d (expression %d) in ",
INSN_UID (insn), indx);
fprintf (gcse_file, "bb %d, reaching reg is %d\n",
- bb, REGNO (expr->reaching_reg));
+ bb->index, REGNO (expr->reaching_reg));
}
}
}
We no longer ignore such label references (see LABEL_REF handling in
mark_jump_label for additional information). */
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LABEL, XEXP (x, 0),
+ REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_LABEL, XEXP (x, 0),
REG_NOTES (insn));
if (LABEL_P (XEXP (x, 0)))
LABEL_NUSES (XEXP (x, 0))++;
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.
static int
hoist_expr_reaches_here_p (expr_bb, expr_index, bb, visited)
- int expr_bb;
+ basic_block expr_bb;
int expr_index;
- int bb;
+ basic_block bb;
char *visited;
{
edge pred;
visited = xcalloc (n_basic_blocks, 1);
}
- for (pred = BASIC_BLOCK (bb)->pred; pred != NULL; pred = pred->pred_next)
+ for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
{
- int pred_bb = pred->src->index;
+ basic_block pred_bb = pred->src;
if (pred->src == ENTRY_BLOCK_PTR)
break;
- else if (visited[pred_bb])
+ else if (visited[pred_bb->index])
continue;
/* Does this predecessor generate this expression? */
- else if (TEST_BIT (comp[pred_bb], expr_index))
+ else if (TEST_BIT (comp[pred_bb->index], expr_index))
break;
- else if (! TEST_BIT (transp[pred_bb], expr_index))
+ else if (! TEST_BIT (transp[pred_bb->index], expr_index))
break;
/* Not killed. */
else
{
- visited[pred_bb] = 1;
+ visited[pred_bb->index] = 1;
if (! hoist_expr_reaches_here_p (expr_bb, expr_index,
pred_bb, visited))
break;
Keep track of how many times this expression is hoistable
from a dominated block into BB. */
- if (hoist_expr_reaches_here_p (bb, i, dominated, NULL))
+ if (hoist_expr_reaches_here_p (BASIC_BLOCK (bb), i,
+ BASIC_BLOCK (dominated), NULL))
hoistable++;
}
dominated block. Now we have to determine if the
expresion would reach the dominated block if it was
placed at the end of BB. */
- if (hoist_expr_reaches_here_p (bb, i, dominated, NULL))
+ if (hoist_expr_reaches_here_p (BASIC_BLOCK (bb), i,
+ BASIC_BLOCK (dominated), NULL))
{
struct expr *expr = index_map[i];
struct occr *occr = expr->antic_occr;
occr->deleted_p = 1;
if (!insn_inserted_p)
{
- insert_insn_end_bb (index_map[i], bb, 0);
+ insert_insn_end_bb (index_map[i],
+ BASIC_BLOCK (bb), 0);
insn_inserted_p = 1;
}
}
{
ptr = ldst_entry (dest);
- if (GET_CODE (src) != MEM)
+ if (GET_CODE (src) != MEM
+ && GET_CODE (src) != ASM_OPERANDS)
ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
else
ptr->invalid = 1;
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
rtx reg = expr->reaching_reg;
- rtx copy, i;
+ rtx copy, new;
/* If we've already copied it, continue. */
if (expr->reaching_reg == src)
}
copy = gen_move_insn ( reg, SET_SRC (pat));
- i = emit_insn_before (copy, insn);
- record_one_set (REGNO (reg), i);
- set_block_num (i, BLOCK_NUM (insn));
+ new = emit_insn_before (copy, insn);
+ record_one_set (REGNO (reg), new);
+ set_block_for_new_insns (new, BLOCK_FOR_INSN (insn));
SET_SRC (pat) = reg;
/* un-recognize this pattern since it's probably different now. */
static int
store_ops_ok (x, bb)
rtx x;
- int bb;
+ basic_block bb;
{
int i;
enum rtx_code code;
case REG:
/* If a reg has changed after us in this
block, the operand has been killed. */
- return TEST_BIT (reg_set_in_block[bb], REGNO (x));
+ return TEST_BIT (reg_set_in_block[bb->index], REGNO (x));
case MEM:
x = XEXP (x, 0);
struct ls_expr * ptr;
rtx dest = PATTERN (insn);
- if (GET_CODE (dest) != SET)
+ if (GET_CODE (dest) != SET
+ || GET_CODE (SET_SRC (dest)) == ASM_OPERANDS)
return;
dest = SET_DEST (dest);
insn && insn != PREV_INSN (BLOCK_HEAD (bb));
insn = PREV_INSN (insn))
{
-#ifdef NON_SAVING_SETJMP
- if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
- {
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- SET_BIT (reg_set_in_block[bb], regno);
- continue;
- }
-#endif
- /* Ignore anything that is not a normal insn. */
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ /* Ignore anything that is not a normal insn. */
+ if (! INSN_P (insn))
continue;
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)
+ bool clobbers_all = false;
+#ifdef NON_SAVING_SETJMP
+ if (NON_SAVING_SETJMP
+ && find_reg_note (insn, REG_SETJMP, NULL_RTX))
+ clobbers_all = true;
#endif
- && regno != FRAME_POINTER_REGNUM)
- || global_regs[regno])
- SET_BIT (reg_set_in_block[bb], regno);
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (clobbers_all
+ || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+ SET_BIT (reg_set_in_block[bb], regno);
}
pat = PATTERN (insn);
int i,j;
int ret = 0;
+ if (!x)
+ return 0;
+
if (GET_CODE (x) == SET)
x = SET_SRC (x);
if (GET_CODE (insn) == CALL_INSN)
{
- if (CONST_CALL_P (insn))
+ if (CONST_OR_PURE_CALL_P (insn))
return 0;
else
return 1;
static int
store_killed_after (x, insn, bb)
rtx x, insn;
- int bb;
+ basic_block bb;
{
- rtx last = BLOCK_END (bb);
+ rtx last = bb->end;
if (insn == last)
return 0;
static int
store_killed_before (x, insn, bb)
rtx x, insn;
- int bb;
+ basic_block bb;
{
- rtx first = BLOCK_HEAD (bb);
+ rtx first = bb->head;
if (insn == first)
return store_killed_in_insn (x, insn);
static void
build_store_vectors ()
{
- int bb;
+ basic_block bb;
+ int b;
rtx insn, st;
struct ls_expr * ptr;
for (st = store_list; st != NULL; st = XEXP (st, 1))
{
insn = XEXP (st, 0);
- bb = BLOCK_NUM (insn);
+ bb = BLOCK_FOR_INSN (insn);
if (!store_killed_after (ptr->pattern, insn, bb))
{
the block), and replace it with this one). We'll copy the
old SRC expression to an unused register in case there
are any side effects. */
- if (TEST_BIT (ae_gen[bb], ptr->index))
+ if (TEST_BIT (ae_gen[bb->index], ptr->index))
{
/* Find previous store. */
rtx st;
for (st = AVAIL_STORE_LIST (ptr); st ; st = XEXP (st, 1))
- if (BLOCK_NUM (XEXP (st, 0)) == bb)
+ if (BLOCK_FOR_INSN (XEXP (st, 0)) == bb)
break;
if (st)
{
continue;
}
}
- SET_BIT (ae_gen[bb], ptr->index);
+ SET_BIT (ae_gen[bb->index], ptr->index);
AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn,
AVAIL_STORE_LIST (ptr));
}
sbitmap_vector_zero (transp, n_basic_blocks);
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
- for (bb = 0; bb < n_basic_blocks; bb++)
+ for (b = 0; b < n_basic_blocks; b++)
{
- if (store_killed_after (ptr->pattern, BLOCK_HEAD (bb), bb))
+ if (store_killed_after (ptr->pattern, BLOCK_HEAD (b), BASIC_BLOCK (b)))
{
- /* The anticipatable expression is not killed if it's gen'd. */
+ /* The anticipatable expression is not killed if it's gen'd. */
/*
We leave this check out for now. If we have a code sequence
in a block which looks like:
gcc.c-torture/execute/960311-1.c with -O3
If we always kill it in this case, we'll sometimes do
uneccessary work, but it shouldn't actually hurt anything.
- if (!TEST_BIT (ae_gen[bb], ptr->index)). */
- SET_BIT (ae_kill[bb], ptr->index);
+ if (!TEST_BIT (ae_gen[b], ptr->index)). */
+ SET_BIT (ae_kill[b], ptr->index);
}
else
- SET_BIT (transp[bb], ptr->index);
+ SET_BIT (transp[b], ptr->index);
}
/* Any block with no exits calls some non-returning function, so
static void
insert_insn_start_bb (insn, bb)
rtx insn;
- int bb;
+ basic_block bb;
{
/* Insert at start of successor block. */
- rtx prev = PREV_INSN (BLOCK_HEAD (bb));
- rtx before = BLOCK_HEAD (bb);
+ rtx prev = PREV_INSN (bb->head);
+ rtx before = bb->head;
while (before != 0)
{
if (GET_CODE (before) != CODE_LABEL
|| NOTE_LINE_NUMBER (before) != NOTE_INSN_BASIC_BLOCK))
break;
prev = before;
- if (prev == BLOCK_END (bb))
+ if (prev == bb->end)
break;
before = NEXT_INSN (before);
}
insn = emit_insn_after (insn, prev);
- if (prev == BLOCK_END (bb))
- BLOCK_END (bb) = insn;
- while (insn != prev)
- {
- set_block_num (insn, bb);
- insn = PREV_INSN (insn);
- }
+ if (prev == bb->end)
+ bb->end = insn;
+
+ set_block_for_new_insns (insn, bb);
if (gcse_file)
{
fprintf (gcse_file, "STORE_MOTION insert store at start of BB %d:\n",
- bb);
+ bb->index);
print_inline_rtx (gcse_file, insn, 6);
fprintf (gcse_file, "\n");
}
edge e;
{
rtx reg, insn;
- int bb;
+ basic_block bb;
edge tmp;
/* We did all the deleted before this insert, so if we didn't delete a
/* If we are inserting this expression on ALL predecessor edges of a BB,
insert it at the start of the BB, and reset the insert bits on the other
edges so we don;t try to insert it on the other edges. */
- bb = e->dest->index;
+ bb = e->dest;
for (tmp = e->dest->pred; tmp ; tmp = tmp->pred_next)
{
int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
/* If tmp is NULL, we found an insertion on every edge, blank the
insertion vector for these edges, and insert at the start of the BB. */
- if (!tmp && bb != EXIT_BLOCK)
+ if (!tmp && bb != EXIT_BLOCK_PTR)
{
for (tmp = e->dest->pred; tmp ; tmp = tmp->pred_next)
{
static void
replace_store_insn (reg, del, bb)
rtx reg, del;
- int bb;
+ basic_block bb;
{
rtx insn;
insn = gen_move_insn (reg, SET_SRC (PATTERN (del)));
insn = emit_insn_after (insn, del);
- set_block_num (insn, bb);
+ set_block_for_new_insns (insn, bb);
if (gcse_file)
{
fprintf (gcse_file,
- "STORE_MOTION delete insn in BB %d:\n ", bb);
+ "STORE_MOTION delete insn in BB %d:\n ", bb->index);
print_inline_rtx (gcse_file, del, 6);
fprintf(gcse_file, "\nSTORE MOTION replaced with insn:\n ");
print_inline_rtx (gcse_file, insn, 6);
fprintf(gcse_file, "\n");
}
- if (BLOCK_END (bb) == del)
- BLOCK_END (bb) = insn;
+ if (bb->end == del)
+ bb->end = insn;
- if (BLOCK_HEAD (bb) == del)
- BLOCK_HEAD (bb) = insn;
+ if (bb->head == del)
+ bb->head = insn;
delete_insn (del);
}
static void
delete_store (expr, bb)
struct ls_expr * expr;
- int bb;
+ basic_block bb;
{
rtx reg, i, del;
for (i = AVAIL_STORE_LIST (expr); i; i = XEXP (i, 1))
{
del = XEXP (i, 0);
- if (BLOCK_NUM (del) == bb)
+ if (BLOCK_FOR_INSN (del) == bb)
{
/* We know there is only one since we deleted redundant
ones during the available computation. */
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;
}
{
for (x = 0; x < n_basic_blocks; x++)
if (TEST_BIT (pre_delete_map[x], ptr->index))
- delete_store (ptr, x);
+ delete_store (ptr, BASIC_BLOCK (x));
for (x = 0; x < NUM_EDGES (edge_list); x++)
if (TEST_BIT (pre_insert_map[x], ptr->index))