#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "insn-config.h"
#include "regs.h"
#include "flags.h"
#include "output.h"
#include "function.h"
#include "except.h"
#include "toplev.h"
#include "recog.h"
#include "expr.h"
#include "timevar.h"
#include "obstack.h"
#include "splay-tree.h"
#include "tree-pass.h"
#include "params.h"
#include "reload.h"
#ifndef HAVE_epilogue
#define HAVE_epilogue 0
#endif
#ifndef HAVE_prologue
#define HAVE_prologue 0
#endif
#ifndef HAVE_sibcall_epilogue
#define HAVE_sibcall_epilogue 0
#endif
#ifndef EPILOGUE_USES
#define EPILOGUE_USES(REGNO) 0
#endif
#ifndef EH_USES
#define EH_USES(REGNO) 0
#endif
#ifdef HAVE_conditional_execution
#ifndef REVERSE_CONDEXEC_PREDICATES_P
#define REVERSE_CONDEXEC_PREDICATES_P(x, y) \
(GET_CODE ((x)) == reversed_comparison_code ((y), NULL))
#endif
#endif
#define MAX_LIVENESS_ROUNDS 20
int flow2_completed;
int max_regno;
VEC(reg_info_p,heap) *reg_n_info;
static regset regs_live_at_setjmp;
rtx regs_may_share;
static HARD_REG_SET elim_reg_set;
struct reg_cond_life_info
{
rtx condition;
rtx orig_condition;
rtx stores;
};
struct propagate_block_info
{
basic_block bb;
regset reg_live;
regset new_set;
rtx *reg_next_use;
rtx mem_set_list;
regset local_set;
regset cond_local_set;
#ifdef HAVE_conditional_execution
splay_tree reg_cond_dead;
regset reg_cond_reg;
#endif
int mem_set_list_len;
int cc0_live;
int flags;
int insn_num;
};
static int ndead;
static int *reg_deaths;
static int normal_flow = 1;
static int verify_wide_reg_1 (rtx *, void *);
static void verify_wide_reg (int, basic_block);
static void verify_local_live_at_start (regset, basic_block);
static void notice_stack_pointer_modification_1 (rtx, rtx, void *);
static void notice_stack_pointer_modification (void);
static void mark_reg (rtx, void *);
static void mark_regs_live_at_end (regset);
static void calculate_global_regs_live (sbitmap, sbitmap, int);
static void propagate_block_delete_insn (rtx);
static rtx propagate_block_delete_libcall (rtx, rtx);
static int insn_dead_p (struct propagate_block_info *, rtx, int, rtx);
static int libcall_dead_p (struct propagate_block_info *, rtx, rtx);
static void mark_set_regs (struct propagate_block_info *, rtx, rtx);
static void mark_set_1 (struct propagate_block_info *, enum rtx_code, rtx,
rtx, rtx, int);
static int find_regno_partial (rtx *, void *);
#ifdef HAVE_conditional_execution
static int mark_regno_cond_dead (struct propagate_block_info *, int, rtx);
static void free_reg_cond_life_info (splay_tree_value);
static int flush_reg_cond_reg_1 (splay_tree_node, void *);
static void flush_reg_cond_reg (struct propagate_block_info *, int);
static rtx elim_reg_cond (rtx, unsigned int);
static rtx ior_reg_cond (rtx, rtx, int);
static rtx not_reg_cond (rtx);
static rtx and_reg_cond (rtx, rtx, int);
#endif
#ifdef AUTO_INC_DEC
static void attempt_auto_inc (struct propagate_block_info *, rtx, rtx, rtx,
rtx, rtx);
static void find_auto_inc (struct propagate_block_info *, rtx, rtx);
static int try_pre_increment_1 (struct propagate_block_info *, rtx);
static int try_pre_increment (rtx, rtx, HOST_WIDE_INT);
#endif
static void mark_used_reg (struct propagate_block_info *, rtx, rtx, rtx);
static void mark_used_regs (struct propagate_block_info *, rtx, rtx, rtx);
void debug_flow_info (void);
static void add_to_mem_set_list (struct propagate_block_info *, rtx);
static int invalidate_mems_from_autoinc (rtx *, void *);
static void invalidate_mems_from_set (struct propagate_block_info *, rtx);
static void clear_log_links (sbitmap);
static int count_or_remove_death_notes_bb (basic_block, int);
static void allocate_bb_life_data (void);
static void maybe_uses_pic_offset_table_rtx (struct propagate_block_info *pbi, rtx reg,
rtx cond ATTRIBUTE_UNUSED, rtx insn);
rtx
first_insn_after_basic_block_note (basic_block block)
{
rtx insn;
insn = BB_HEAD (block);
if (insn == NULL_RTX)
return NULL_RTX;
if (LABEL_P (insn))
insn = NEXT_INSN (insn);
gcc_assert (NOTE_INSN_BASIC_BLOCK_P (insn));
return NEXT_INSN (insn);
}
void
life_analysis (int flags)
{
#ifdef ELIMINABLE_REGS
int i;
static const struct {const int from, to; } eliminables[] = ELIMINABLE_REGS;
#endif
CLEAR_HARD_REG_SET (elim_reg_set);
#ifdef ELIMINABLE_REGS
for (i = 0; i < (int) ARRAY_SIZE (eliminables); i++)
SET_HARD_REG_BIT (elim_reg_set, eliminables[i].from);
#else
SET_HARD_REG_BIT (elim_reg_set, FRAME_POINTER_REGNUM);
#endif
#ifdef CANNOT_CHANGE_MODE_CLASS
if (flags & PROP_REG_INFO)
init_subregs_of_mode ();
#endif
if (! optimize)
flags &= ~(PROP_LOG_LINKS | PROP_AUTOINC | PROP_ALLOW_CFG_CHANGES);
if (reload_completed)
flags &= ~(PROP_REG_INFO | PROP_AUTOINC);
if (optimize && (flags & PROP_SCAN_DEAD_STORES))
init_alias_analysis ();
delete_noop_moves ();
if (! reload_completed)
notice_stack_pointer_modification ();
allocate_reg_life_data ();
allocate_bb_life_data ();
mark_regs_live_at_end (EXIT_BLOCK_PTR->il.rtl->global_live_at_start);
if (flags & PROP_REG_INFO)
{
memset (regs_ever_live, 0, sizeof (regs_ever_live));
memset (regs_asm_clobbered, 0, sizeof (regs_asm_clobbered));
}
update_life_info (NULL, UPDATE_LIFE_GLOBAL, flags);
if (reg_deaths)
{
free (reg_deaths);
reg_deaths = NULL;
}
if (optimize && (flags & PROP_SCAN_DEAD_STORES))
end_alias_analysis ();
if (dump_file)
dump_flow_info (dump_file, dump_flags);
delete_dead_jumptables ();
}
static int
verify_wide_reg_1 (rtx *px, void *pregno)
{
rtx x = *px;
unsigned int regno = *(int *) pregno;
if (REG_P (x) && REGNO (x) == regno)
{
if (GET_MODE_BITSIZE (GET_MODE (x)) <= BITS_PER_WORD)
return 2;
return 1;
}
return 0;
}
static void
verify_wide_reg (int regno, basic_block bb)
{
rtx head = BB_HEAD (bb), end = BB_END (bb);
while (1)
{
if (INSN_P (head))
{
int r = for_each_rtx (&PATTERN (head), verify_wide_reg_1, ®no);
if (r == 1)
return;
if (r == 2)
break;
}
if (head == end)
break;
head = NEXT_INSN (head);
}
if (dump_file)
{
fprintf (dump_file, "Register %d died unexpectedly.\n", regno);
dump_bb (bb, dump_file, 0);
}
internal_error ("internal consistency failure");
}
static void
verify_local_live_at_start (regset new_live_at_start, basic_block bb)
{
if (reload_completed)
{
if (! REG_SET_EQUAL_P (new_live_at_start,
bb->il.rtl->global_live_at_start))
{
if (dump_file)
{
fprintf (dump_file,
"live_at_start mismatch in bb %d, aborting\nNew:\n",
bb->index);
debug_bitmap_file (dump_file, new_live_at_start);
fputs ("Old:\n", dump_file);
dump_bb (bb, dump_file, 0);
}
internal_error ("internal consistency failure");
}
}
else
{
unsigned i;
reg_set_iterator rsi;
XOR_REG_SET (new_live_at_start, bb->il.rtl->global_live_at_start);
EXECUTE_IF_SET_IN_REG_SET (new_live_at_start, 0, i, rsi)
{
if (i != PIC_OFFSET_TABLE_REGNUM)
{
if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, i))
{
if (dump_file)
{
fprintf (dump_file,
"Register %d died unexpectedly.\n", i);
dump_bb (bb, dump_file, 0);
}
internal_error ("internal consistency failure");
}
verify_wide_reg (i, bb);
}
}
}
}
int
update_life_info (sbitmap blocks, enum update_life_extent extent,
int prop_flags)
{
regset tmp;
unsigned i = 0;
int stabilized_prop_flags = prop_flags;
basic_block bb;
tmp = ALLOC_REG_SET (®_obstack);
ndead = 0;
if ((prop_flags & PROP_REG_INFO) && !reg_deaths)
reg_deaths = XCNEWVEC (int, max_regno);
timevar_push ((extent == UPDATE_LIFE_LOCAL || blocks)
? TV_LIFE_UPDATE : TV_LIFE);
gcc_assert (!(prop_flags & PROP_ALLOW_CFG_CHANGES)
|| (extent != UPDATE_LIFE_LOCAL && !blocks));
if (extent != UPDATE_LIFE_LOCAL)
{
for ( ; ; )
{
int changed = 0;
calculate_global_regs_live (blocks, blocks,
prop_flags & (PROP_SCAN_DEAD_CODE
| PROP_SCAN_DEAD_STORES
| PROP_ALLOW_CFG_CHANGES));
if ((prop_flags & (PROP_KILL_DEAD_CODE | PROP_ALLOW_CFG_CHANGES))
!= (PROP_KILL_DEAD_CODE | PROP_ALLOW_CFG_CHANGES))
break;
FOR_EACH_BB_REVERSE (bb)
{
COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end);
changed |= propagate_block (bb, tmp, NULL, NULL,
prop_flags & (PROP_SCAN_DEAD_CODE
| PROP_SCAN_DEAD_STORES
| PROP_KILL_DEAD_CODE));
}
stabilized_prop_flags
&= ~(PROP_SCAN_DEAD_CODE | PROP_SCAN_DEAD_STORES
| PROP_KILL_DEAD_CODE);
if (! changed)
break;
cleanup_cfg (CLEANUP_EXPENSIVE);
FOR_EACH_BB (bb)
{
CLEAR_REG_SET (bb->il.rtl->global_live_at_start);
CLEAR_REG_SET (bb->il.rtl->global_live_at_end);
}
}
if (extent == UPDATE_LIFE_GLOBAL_RM_NOTES)
count_or_remove_death_notes (blocks,
prop_flags & PROP_POST_REGSTACK ? -1 : 1);
}
else
{
if (prop_flags & PROP_DEATH_NOTES)
count_or_remove_death_notes (blocks,
prop_flags & PROP_POST_REGSTACK ? -1 : 1);
}
if (prop_flags & PROP_LOG_LINKS)
clear_log_links (blocks);
if (blocks)
{
sbitmap_iterator sbi;
EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi)
{
bb = BASIC_BLOCK (i);
if (bb)
{
COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end);
propagate_block (bb, tmp, NULL, NULL, stabilized_prop_flags);
if (extent == UPDATE_LIFE_LOCAL)
verify_local_live_at_start (tmp, bb);
}
};
}
else
{
FOR_EACH_BB_REVERSE (bb)
{
COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end);
propagate_block (bb, tmp, NULL, NULL, stabilized_prop_flags);
if (extent == UPDATE_LIFE_LOCAL)
verify_local_live_at_start (tmp, bb);
}
}
FREE_REG_SET (tmp);
if (prop_flags & PROP_REG_INFO)
{
reg_set_iterator rsi;
EXECUTE_IF_SET_IN_REG_SET (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end,
FIRST_PSEUDO_REGISTER, i, rsi)
REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL;
EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp,
FIRST_PSEUDO_REGISTER, i, rsi)
{
if (regno_reg_rtx[i] != 0)
{
if (normal_flow)
REG_LIVE_LENGTH (i) = -1;
REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN;
}
}
}
if (reg_deaths)
{
free (reg_deaths);
reg_deaths = NULL;
}
timevar_pop ((extent == UPDATE_LIFE_LOCAL || blocks)
? TV_LIFE_UPDATE : TV_LIFE);
if (ndead && dump_file)
fprintf (dump_file, "deleted %i dead insns\n", ndead);
return ndead;
}
int
update_life_info_in_dirty_blocks (enum update_life_extent extent, int prop_flags)
{
sbitmap update_life_blocks = sbitmap_alloc (last_basic_block);
int n = 0;
basic_block bb;
int retval = 0;
sbitmap_zero (update_life_blocks);
FOR_EACH_BB (bb)
{
if (bb->flags & BB_DIRTY)
{
SET_BIT (update_life_blocks, bb->index);
n++;
}
}
if (n)
retval = update_life_info (update_life_blocks, extent, prop_flags);
sbitmap_free (update_life_blocks);
return retval;
}
void
free_basic_block_vars (void)
{
if (basic_block_info)
{
clear_edges ();
basic_block_info = NULL;
}
n_basic_blocks = 0;
last_basic_block = 0;
n_edges = 0;
label_to_block_map = NULL;
ENTRY_BLOCK_PTR->aux = NULL;
ENTRY_BLOCK_PTR->il.rtl->global_live_at_end = NULL;
EXIT_BLOCK_PTR->aux = NULL;
EXIT_BLOCK_PTR->il.rtl->global_live_at_start = NULL;
}
int
delete_noop_moves (void)
{
rtx insn, next;
basic_block bb;
int nnoops = 0;
FOR_EACH_BB (bb)
{
for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = next)
{
next = NEXT_INSN (insn);
if (INSN_P (insn) && noop_move_p (insn))
{
rtx note;
if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX))
&& XEXP (note, 0) != insn)
{
rtx new_libcall_insn = next_real_insn (insn);
rtx retval_note = find_reg_note (XEXP (note, 0),
REG_RETVAL, NULL_RTX);
REG_NOTES (new_libcall_insn)
= gen_rtx_INSN_LIST (REG_LIBCALL, XEXP (note, 0),
REG_NOTES (new_libcall_insn));
XEXP (retval_note, 0) = new_libcall_insn;
}
delete_insn_and_edges (insn);
nnoops++;
}
}
}
if (nnoops && dump_file)
fprintf (dump_file, "deleted %i noop moves\n", nnoops);
return nnoops;
}
void
delete_dead_jumptables (void)
{
basic_block bb;
FOR_EACH_BB (bb)
{
rtx insn, next;
for (insn = NEXT_INSN (BB_END (bb));
insn && !NOTE_INSN_BASIC_BLOCK_P (insn);
insn = next)
{
next = NEXT_INSN (insn);
if (LABEL_P (insn)
&& LABEL_NUSES (insn) == LABEL_PRESERVE_P (insn)
&& JUMP_P (next)
&& (GET_CODE (PATTERN (next)) == ADDR_VEC
|| GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC))
{
rtx label = insn, jump = next;
if (dump_file)
fprintf (dump_file, "Dead jumptable %i removed\n",
INSN_UID (insn));
next = NEXT_INSN (next);
delete_insn (jump);
delete_insn (label);
}
}
}
}
static void
notice_stack_pointer_modification_1 (rtx x, rtx pat ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
if (x == stack_pointer_rtx
|| (MEM_P (x)
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == RTX_AUTOINC
&& XEXP (XEXP (x, 0), 0) == stack_pointer_rtx))
current_function_sp_is_unchanging = 0;
}
static void
notice_stack_pointer_modification (void)
{
basic_block bb;
rtx insn;
current_function_sp_is_unchanging = !current_function_calls_alloca;
if (! current_function_sp_is_unchanging)
return;
FOR_EACH_BB (bb)
FOR_BB_INSNS (bb, insn)
{
if (INSN_P (insn))
{
note_stores (PATTERN (insn),
notice_stack_pointer_modification_1,
NULL);
if (! current_function_sp_is_unchanging)
return;
}
}
}
static void
mark_reg (rtx reg, void *xset)
{
regset set = (regset) xset;
int regno = REGNO (reg);
gcc_assert (GET_MODE (reg) != BLKmode);
SET_REGNO_REG_SET (set, regno);
if (regno < FIRST_PSEUDO_REGISTER)
{
int n = hard_regno_nregs[regno][GET_MODE (reg)];
while (--n > 0)
SET_REGNO_REG_SET (set, regno + n);
}
}
static void
mark_regs_live_at_end (regset set)
{
unsigned int i;
if ((HAVE_epilogue && epilogue_completed)
|| ! EXIT_IGNORE_STACK
|| (! FRAME_POINTER_REQUIRED
&& ! current_function_calls_alloca
&& flag_omit_frame_pointer)
|| current_function_sp_is_unchanging)
{
SET_REGNO_REG_SET (set, STACK_POINTER_REGNUM);
}
if (! reload_completed || frame_pointer_needed)
{
SET_REGNO_REG_SET (set, FRAME_POINTER_REGNUM);
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
if (! LOCAL_REGNO (HARD_FRAME_POINTER_REGNUM))
SET_REGNO_REG_SET (set, HARD_FRAME_POINTER_REGNUM);
#endif
}
#ifndef PIC_OFFSET_TABLE_REG_CALL_CLOBBERED
if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
&& fixed_regs[PIC_OFFSET_TABLE_REGNUM])
SET_REGNO_REG_SET (set, PIC_OFFSET_TABLE_REGNUM);
#endif
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (global_regs[i] || EPILOGUE_USES (i))
SET_REGNO_REG_SET (set, i);
if (HAVE_epilogue && epilogue_completed)
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (regs_ever_live[i] && ! LOCAL_REGNO (i)
&& ! TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
SET_REGNO_REG_SET (set, i);
}
#ifdef EH_RETURN_DATA_REGNO
if (reload_completed && current_function_calls_eh_return)
for (i = 0; ; ++i)
{
unsigned regno = EH_RETURN_DATA_REGNO(i);
if (regno == INVALID_REGNUM)
break;
SET_REGNO_REG_SET (set, regno);
}
#endif
#ifdef EH_RETURN_STACKADJ_RTX
if ((! HAVE_epilogue || ! epilogue_completed)
&& current_function_calls_eh_return)
{
rtx tmp = EH_RETURN_STACKADJ_RTX;
if (tmp && REG_P (tmp))
mark_reg (tmp, set);
}
#endif
#ifdef EH_RETURN_HANDLER_RTX
if ((! HAVE_epilogue || ! epilogue_completed)
&& current_function_calls_eh_return)
{
rtx tmp = EH_RETURN_HANDLER_RTX;
if (tmp && REG_P (tmp))
mark_reg (tmp, set);
}
#endif
diddle_return_value (mark_reg, set);
}
static void
calculate_global_regs_live (sbitmap blocks_in, sbitmap blocks_out, int flags)
{
basic_block *queue, *qhead, *qtail, *qend, bb;
regset tmp, new_live_at_end, invalidated_by_eh_edge;
regset registers_made_dead;
bool failure_strategy_required = false;
int *block_accesses;
regset *local_sets;
regset *cond_local_sets;
unsigned int i;
#ifdef ENABLE_CHECKING
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
gcc_assert (!bb->aux);
#endif
tmp = ALLOC_REG_SET (®_obstack);
new_live_at_end = ALLOC_REG_SET (®_obstack);
invalidated_by_eh_edge = ALLOC_REG_SET (®_obstack);
registers_made_dead = ALLOC_REG_SET (®_obstack);
for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
SET_REGNO_REG_SET (invalidated_by_eh_edge, i);
#ifdef EH_RETURN_DATA_REGNO
for (i = 0; ; ++i)
{
unsigned regno = EH_RETURN_DATA_REGNO (i);
if (regno == INVALID_REGNUM)
break;
SET_REGNO_REG_SET (invalidated_by_eh_edge, regno);
}
#endif
local_sets = XCNEWVEC (bitmap, last_basic_block);
cond_local_sets = XCNEWVEC (bitmap, last_basic_block);
queue = XNEWVEC (basic_block, n_basic_blocks + 1);
qtail = queue;
qhead = qend = queue + n_basic_blocks;
if (blocks_in)
{
FOR_EACH_BB (bb)
if (TEST_BIT (blocks_in, bb->index))
{
*--qhead = bb;
bb->aux = bb;
}
}
else
{
FOR_EACH_BB (bb)
{
*--qhead = bb;
bb->aux = bb;
}
}
block_accesses = XCNEWVEC (int, last_basic_block);
ENTRY_BLOCK_PTR->aux = EXIT_BLOCK_PTR->aux = NULL;
if (blocks_out)
sbitmap_zero (blocks_out);
while (qhead != qtail)
{
int rescan, changed;
basic_block bb;
edge e;
edge_iterator ei;
bb = *qhead++;
if (qhead == qend)
qhead = queue;
bb->aux = NULL;
if (bb != ENTRY_BLOCK_PTR)
{
int max_liveness_rounds =
MAX (MAX_LIVENESS_ROUNDS, cfun->max_loop_depth);
block_accesses[bb->index]++;
if (block_accesses[bb->index] > max_liveness_rounds)
failure_strategy_required = true;
}
CLEAR_REG_SET (new_live_at_end);
if (EDGE_COUNT (bb->succs) > 0)
FOR_EACH_EDGE (e, ei, bb->succs)
{
basic_block sb = e->dest;
if (e->flags & EDGE_EH)
bitmap_ior_and_compl_into (new_live_at_end,
sb->il.rtl->global_live_at_start,
invalidated_by_eh_edge);
else
IOR_REG_SET (new_live_at_end, sb->il.rtl->global_live_at_start);
if (e->flags & EDGE_EH)
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (EH_USES (i))
SET_REGNO_REG_SET (new_live_at_end, i);
}
else
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (EH_USES (i))
SET_REGNO_REG_SET (new_live_at_end, i);
}
SET_REGNO_REG_SET (new_live_at_end, STACK_POINTER_REGNUM);
if (! reload_completed)
{
SET_REGNO_REG_SET (new_live_at_end, FRAME_POINTER_REGNUM);
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
if (fixed_regs[ARG_POINTER_REGNUM])
SET_REGNO_REG_SET (new_live_at_end, ARG_POINTER_REGNUM);
#endif
if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
&& fixed_regs[PIC_OFFSET_TABLE_REGNUM])
SET_REGNO_REG_SET (new_live_at_end, PIC_OFFSET_TABLE_REGNUM);
}
if (bb == ENTRY_BLOCK_PTR)
{
COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end);
continue;
}
if (local_sets[bb->index] == NULL)
{
local_sets[bb->index] = ALLOC_REG_SET (®_obstack);
cond_local_sets[bb->index] = ALLOC_REG_SET (®_obstack);
rescan = 1;
}
else
{
rescan = bitmap_intersect_compl_p (bb->il.rtl->global_live_at_end,
new_live_at_end);
if (!rescan)
{
regset cond_local_set;
cond_local_set = cond_local_sets[bb->index];
rescan = bitmap_intersect_p (new_live_at_end, cond_local_set);
}
if (!rescan)
{
regset local_set;
bitmap_xor (tmp, bb->il.rtl->global_live_at_end, new_live_at_end);
if (bitmap_empty_p (tmp))
continue;
local_set = local_sets[bb->index];
rescan = bitmap_intersect_p (tmp, local_set);
}
}
if (blocks_out)
SET_BIT (blocks_out, bb->index);
if (! rescan)
{
changed = bitmap_ior_and_compl_into (bb->il.rtl->global_live_at_start,
new_live_at_end,
bb->il.rtl->global_live_at_end);
COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end);
if (! changed)
continue;
}
else
{
COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end);
propagate_block (bb, new_live_at_end,
local_sets[bb->index],
cond_local_sets[bb->index],
flags);
if (REG_SET_EQUAL_P (bb->il.rtl->global_live_at_start,
new_live_at_end))
continue;
if (failure_strategy_required)
{
bitmap_and_compl (tmp, bb->il.rtl->global_live_at_start,
new_live_at_end);
if (!bitmap_empty_p (tmp))
{
bool pbb_changed;
basic_block pbb;
pbb_changed = bitmap_ior_into (registers_made_dead, tmp);
gcc_assert (pbb_changed);
FOR_EACH_BB (pbb)
{
pbb_changed = false;
pbb_changed
|= bitmap_and_compl_into
(pbb->il.rtl->global_live_at_start,
registers_made_dead);
pbb_changed
|= bitmap_and_compl_into
(pbb->il.rtl->global_live_at_end,
registers_made_dead);
if (!pbb_changed)
continue;
if (blocks_out)
SET_BIT (blocks_out, pbb->index);
if (local_sets[pbb->index])
{
FREE_REG_SET (local_sets[pbb->index]);
FREE_REG_SET (cond_local_sets[pbb->index]);
local_sets[pbb->index] = 0;
}
if (pbb->aux == NULL)
{
*qtail++ = pbb;
if (qtail == qend)
qtail = queue;
pbb->aux = pbb;
}
}
continue;
}
}
COPY_REG_SET (bb->il.rtl->global_live_at_start, new_live_at_end);
}
FOR_EACH_EDGE (e, ei, bb->preds)
{
basic_block pb = e->src;
gcc_assert ((e->flags & EDGE_FAKE) == 0);
if (pb->aux == NULL)
{
*qtail++ = pb;
if (qtail == qend)
qtail = queue;
pb->aux = pb;
}
}
}
FREE_REG_SET (tmp);
FREE_REG_SET (new_live_at_end);
FREE_REG_SET (invalidated_by_eh_edge);
FREE_REG_SET (registers_made_dead);
if (blocks_out)
{
sbitmap_iterator sbi;
EXECUTE_IF_SET_IN_SBITMAP (blocks_out, 0, i, sbi)
{
basic_block bb = BASIC_BLOCK (i);
FREE_REG_SET (local_sets[bb->index]);
FREE_REG_SET (cond_local_sets[bb->index]);
};
}
else
{
FOR_EACH_BB (bb)
{
FREE_REG_SET (local_sets[bb->index]);
FREE_REG_SET (cond_local_sets[bb->index]);
}
}
free (block_accesses);
free (queue);
free (cond_local_sets);
free (local_sets);
}
typedef struct {
unsigned regno_to_find;
rtx retval;
} find_regno_partial_param;
static int
find_regno_partial (rtx *ptr, void *data)
{
find_regno_partial_param *param = (find_regno_partial_param *)data;
unsigned reg = param->regno_to_find;
param->retval = NULL_RTX;
if (*ptr == NULL_RTX)
return 0;
switch (GET_CODE (*ptr))
{
case ZERO_EXTRACT:
case SIGN_EXTRACT:
case STRICT_LOW_PART:
if (REG_P (XEXP (*ptr, 0)) && REGNO (XEXP (*ptr, 0)) == reg)
{
param->retval = XEXP (*ptr, 0);
return 1;
}
break;
case SUBREG:
if (REG_P (SUBREG_REG (*ptr))
&& REGNO (SUBREG_REG (*ptr)) == reg)
{
param->retval = SUBREG_REG (*ptr);
return 1;
}
break;
default:
break;
}
return 0;
}
static int
initialize_uninitialized_subregs (void)
{
rtx insn;
edge e;
unsigned reg, did_something = 0;
find_regno_partial_param param;
edge_iterator ei;
#ifndef TARGET_MUST_INIT_SUBREG
return 0;
#endif
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
{
basic_block bb = e->dest;
regset map = bb->il.rtl->global_live_at_start;
reg_set_iterator rsi;
EXECUTE_IF_SET_IN_REG_SET (map, FIRST_PSEUDO_REGISTER, reg, rsi)
{
int uid = REGNO_FIRST_UID (reg);
rtx i;
for (i = get_insns (); i && INSN_UID (i) != uid; i = NEXT_INSN (i))
;
if (i != NULL_RTX)
{
param.regno_to_find = reg;
for_each_rtx (&i, find_regno_partial, ¶m);
if (param.retval != NULL_RTX)
{
start_sequence ();
emit_move_insn (param.retval,
CONST0_RTX (GET_MODE (param.retval)));
insn = get_insns ();
end_sequence ();
insert_insn_on_edge (insn, e);
did_something = 1;
}
}
}
}
if (did_something)
commit_edge_insertions ();
return did_something;
}
static void
allocate_bb_life_data (void)
{
basic_block bb;
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
{
if (bb->il.rtl->global_live_at_start)
{
CLEAR_REG_SET (bb->il.rtl->global_live_at_start);
CLEAR_REG_SET (bb->il.rtl->global_live_at_end);
}
else
{
bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack);
bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack);
}
}
regs_live_at_setjmp = ALLOC_REG_SET (®_obstack);
}
void
allocate_reg_life_data (void)
{
int i;
max_regno = max_reg_num ();
gcc_assert (!reg_deaths);
reg_deaths = XCNEWVEC (int, max_regno);
allocate_reg_info (max_regno, FALSE, FALSE);
for (i = 0; i < max_regno; i++)
{
REG_N_SETS (i) = 0;
REG_N_REFS (i) = 0;
REG_N_DEATHS (i) = 0;
REG_N_CALLS_CROSSED (i) = 0;
REG_N_THROWING_CALLS_CROSSED (i) = 0;
if (normal_flow)
REG_LIVE_LENGTH (i) = 0;
REG_FREQ (i) = 0;
REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN;
}
}
static void
propagate_block_delete_insn (rtx insn)
{
rtx inote = find_reg_note (insn, REG_LABEL, NULL_RTX);
if (inote && LABEL_P (inote))
{
rtx label = XEXP (inote, 0);
rtx next;
if (LABEL_NUSES (label) == 1 + LABEL_PRESERVE_P (label)
&& (next = next_nonnote_insn (label)) != NULL
&& JUMP_P (next)
&& (GET_CODE (PATTERN (next)) == ADDR_VEC
|| GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC))
{
rtx pat = PATTERN (next);
int diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC;
int len = XVECLEN (pat, diff_vec_p);
int i;
for (i = 0; i < len; i++)
LABEL_NUSES (XEXP (XVECEXP (pat, diff_vec_p, i), 0))--;
delete_insn_and_edges (next);
ndead++;
}
}
delete_insn_and_edges (insn);
ndead++;
}
static rtx
propagate_block_delete_libcall (rtx insn, rtx note)
{
rtx first = XEXP (note, 0);
rtx before = PREV_INSN (first);
delete_insn_chain_and_edges (first, insn);
ndead++;
return before;
}
rtx
propagate_one_insn (struct propagate_block_info *pbi, rtx insn)
{
rtx prev = PREV_INSN (insn);
int flags = pbi->flags;
int insn_is_dead = 0;
int libcall_is_dead = 0;
rtx note;
unsigned i;
if (! INSN_P (insn))
return prev;
note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
if (flags & PROP_SCAN_DEAD_CODE)
{
insn_is_dead = insn_dead_p (pbi, PATTERN (insn), 0, REG_NOTES (insn));
libcall_is_dead = (insn_is_dead && note != 0
&& libcall_dead_p (pbi, note, insn));
}
if ((flags & PROP_KILL_DEAD_CODE) && insn_is_dead)
{
if (reload_completed
&& !(TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
&& (TYPE_RETURNS_STACK_DEPRESSED
(TREE_TYPE (current_function_decl))))
&& (((HAVE_epilogue || HAVE_prologue)
&& prologue_epilogue_contains (insn))
|| (HAVE_sibcall_epilogue
&& sibcall_epilogue_contains (insn)))
&& find_reg_note (insn, REG_MAYBE_DEAD, NULL_RTX) == 0)
fatal_insn ("Attempt to delete prologue/epilogue insn:", insn);
pbi->flags |= PROP_DEAD_INSN;
mark_set_regs (pbi, PATTERN (insn), insn);
pbi->flags &= ~PROP_DEAD_INSN;
pbi->cc0_live = 0;
if (libcall_is_dead)
prev = propagate_block_delete_libcall (insn, note);
else
{
if (note)
{
rtx libcall_note;
libcall_note
= find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL_RTX);
remove_note (XEXP (note, 0), libcall_note);
}
note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
if (note)
{
rtx retval_note;
retval_note
= find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX);
remove_note (XEXP (note, 0), retval_note);
}
propagate_block_delete_insn (insn);
}
return prev;
}
#ifdef AUTO_INC_DEC
{
rtx x = single_set (insn);
if ((flags & PROP_AUTOINC)
&& x != 0
&& REG_P (SET_DEST (x))
&& (GET_CODE (SET_SRC (x)) == PLUS
|| GET_CODE (SET_SRC (x)) == MINUS)
&& XEXP (SET_SRC (x), 0) == SET_DEST (x)
&& GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
&& try_pre_increment_1 (pbi, insn))
return prev;
}
#endif
CLEAR_REG_SET (pbi->new_set);
if (libcall_is_dead)
{
mark_set_regs (pbi, PATTERN (insn), insn);
insn = XEXP (note, 0);
return PREV_INSN (insn);
}
else if (GET_CODE (PATTERN (insn)) == SET
&& SET_DEST (PATTERN (insn)) == stack_pointer_rtx
&& GET_CODE (SET_SRC (PATTERN (insn))) == PLUS
&& XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx
&& GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == CONST_INT)
{
invalidate_mems_from_set (pbi, stack_pointer_rtx);
mark_set_regs (pbi, PATTERN (insn), insn);
}
else
{
if (CALL_P (insn) && (flags & PROP_REG_INFO))
{
reg_set_iterator rsi;
EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi)
REG_N_CALLS_CROSSED (i)++;
if (can_throw_internal (insn))
EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi)
REG_N_THROWING_CALLS_CROSSED (i)++;
}
mark_set_regs (pbi, PATTERN (insn), insn);
if (CALL_P (insn))
{
regset live_at_end;
bool sibcall_p;
rtx note, cond;
int i;
cond = NULL_RTX;
if (GET_CODE (PATTERN (insn)) == COND_EXEC)
cond = COND_EXEC_TEST (PATTERN (insn));
if (! CONST_OR_PURE_CALL_P (insn))
{
free_EXPR_LIST_list (&pbi->mem_set_list);
pbi->mem_set_list_len = 0;
}
else
invalidate_mems_from_set (pbi, stack_pointer_rtx);
for (note = CALL_INSN_FUNCTION_USAGE (insn);
note;
note = XEXP (note, 1))
if (GET_CODE (XEXP (note, 0)) == CLOBBER)
mark_set_1 (pbi, CLOBBER, XEXP (XEXP (note, 0), 0),
cond, insn, pbi->flags);
sibcall_p = SIBLING_CALL_P (insn);
live_at_end = EXIT_BLOCK_PTR->il.rtl->global_live_at_start;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)
&& ! (sibcall_p
&& REGNO_REG_SET_P (live_at_end, i)
&& ! refers_to_regno_p (i, i+1,
current_function_return_rtx,
(rtx *) 0)))
{
enum rtx_code code = global_regs[i] ? SET : CLOBBER;
mark_set_1 (pbi, code, regno_reg_rtx[i], cond, insn,
pbi->flags & ~(PROP_DEATH_NOTES | PROP_REG_INFO));
}
}
pbi->cc0_live = 0;
if (! insn_is_dead)
mark_used_regs (pbi, PATTERN (insn), NULL_RTX, insn);
#ifdef AUTO_INC_DEC
prev = PREV_INSN (insn);
#endif
if (! insn_is_dead && CALL_P (insn))
{
int i;
rtx note, cond;
cond = NULL_RTX;
if (GET_CODE (PATTERN (insn)) == COND_EXEC)
cond = COND_EXEC_TEST (PATTERN (insn));
for (note = CALL_INSN_FUNCTION_USAGE (insn);
note;
note = XEXP (note, 1))
mark_used_regs (pbi, XEXP (XEXP (note, 0), 0), cond, insn);
if ((flags & PROP_REG_INFO)
&& !REGNO_REG_SET_P (pbi->reg_live, STACK_POINTER_REGNUM))
reg_deaths[STACK_POINTER_REGNUM] = pbi->insn_num;
SET_REGNO_REG_SET (pbi->reg_live, STACK_POINTER_REGNUM);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (global_regs[i])
mark_used_reg (pbi, regno_reg_rtx[i], cond, insn);
}
}
pbi->insn_num++;
return prev;
}
struct propagate_block_info *
init_propagate_block_info (basic_block bb, regset live, regset local_set,
regset cond_local_set, int flags)
{
struct propagate_block_info *pbi = XNEW (struct propagate_block_info);
pbi->bb = bb;
pbi->reg_live = live;
pbi->mem_set_list = NULL_RTX;
pbi->mem_set_list_len = 0;
pbi->local_set = local_set;
pbi->cond_local_set = cond_local_set;
pbi->cc0_live = 0;
pbi->flags = flags;
pbi->insn_num = 0;
if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
pbi->reg_next_use = XCNEWVEC (rtx, max_reg_num ());
else
pbi->reg_next_use = NULL;
pbi->new_set = BITMAP_ALLOC (NULL);
#ifdef HAVE_conditional_execution
pbi->reg_cond_dead = splay_tree_new (splay_tree_compare_ints, NULL,
free_reg_cond_life_info);
pbi->reg_cond_reg = BITMAP_ALLOC (NULL);
if (JUMP_P (BB_END (bb))
&& any_condjump_p (BB_END (bb)))
{
regset diff = ALLOC_REG_SET (®_obstack);
basic_block bb_true, bb_false;
unsigned i;
bb_true = EDGE_SUCC (bb, 0)->dest;
if (!single_succ_p (bb))
{
bb_false = EDGE_SUCC (bb, 1)->dest;
if (EDGE_SUCC (bb, 0)->flags & EDGE_FALLTHRU)
{
basic_block t = bb_false;
bb_false = bb_true;
bb_true = t;
}
else
gcc_assert (EDGE_SUCC (bb, 1)->flags & EDGE_FALLTHRU);
}
else
{
gcc_assert (JUMP_LABEL (BB_END (bb)) == BB_HEAD (bb_true));
bb_false = bb_true;
}
bitmap_xor (diff, bb_true->il.rtl->global_live_at_start,
bb_false->il.rtl->global_live_at_start);
if (!bitmap_empty_p (diff))
{
rtx set_src = SET_SRC (pc_set (BB_END (bb)));
rtx cond_true = XEXP (set_src, 0);
rtx reg = XEXP (cond_true, 0);
enum rtx_code inv_cond;
if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg);
inv_cond = reversed_comparison_code (cond_true, BB_END (bb));
if (inv_cond != UNKNOWN
&& REG_P (reg)
&& XEXP (cond_true, 1) == const0_rtx)
{
rtx cond_false
= gen_rtx_fmt_ee (inv_cond,
GET_MODE (cond_true), XEXP (cond_true, 0),
XEXP (cond_true, 1));
reg_set_iterator rsi;
if (GET_CODE (XEXP (set_src, 1)) == PC)
{
rtx t = cond_false;
cond_false = cond_true;
cond_true = t;
}
SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (reg));
EXECUTE_IF_SET_IN_REG_SET (diff, 0, i, rsi)
{
struct reg_cond_life_info *rcli;
rtx cond;
rcli = XNEW (struct reg_cond_life_info);
if (REGNO_REG_SET_P (bb_true->il.rtl->global_live_at_start,
i))
cond = cond_false;
else
cond = cond_true;
rcli->condition = cond;
rcli->stores = const0_rtx;
rcli->orig_condition = cond;
splay_tree_insert (pbi->reg_cond_dead, i,
(splay_tree_value) rcli);
}
}
}
FREE_REG_SET (diff);
}
#endif
if (optimize
&& ! (TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
&& (TYPE_RETURNS_STACK_DEPRESSED
(TREE_TYPE (current_function_decl))))
&& (flags & PROP_SCAN_DEAD_STORES)
&& (EDGE_COUNT (bb->succs) == 0
|| (single_succ_p (bb)
&& single_succ (bb) == EXIT_BLOCK_PTR
&& ! current_function_calls_eh_return)))
{
rtx insn, set;
for (insn = BB_END (bb); insn != BB_HEAD (bb); insn = PREV_INSN (insn))
if (NONJUMP_INSN_P (insn)
&& (set = single_set (insn))
&& MEM_P (SET_DEST (set)))
{
rtx mem = SET_DEST (set);
rtx canon_mem = canon_rtx (mem);
if (XEXP (canon_mem, 0) == frame_pointer_rtx
|| (GET_CODE (XEXP (canon_mem, 0)) == PLUS
&& XEXP (XEXP (canon_mem, 0), 0) == frame_pointer_rtx
&& GET_CODE (XEXP (XEXP (canon_mem, 0), 1)) == CONST_INT))
add_to_mem_set_list (pbi, canon_mem);
}
}
return pbi;
}
void
free_propagate_block_info (struct propagate_block_info *pbi)
{
free_EXPR_LIST_list (&pbi->mem_set_list);
BITMAP_FREE (pbi->new_set);
#ifdef HAVE_conditional_execution
splay_tree_delete (pbi->reg_cond_dead);
BITMAP_FREE (pbi->reg_cond_reg);
#endif
if (pbi->flags & PROP_REG_INFO)
{
int num = pbi->insn_num;
unsigned i;
reg_set_iterator rsi;
EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi)
{
if (normal_flow)
REG_LIVE_LENGTH (i) += num - reg_deaths[i];
reg_deaths[i] = 0;
}
}
if (pbi->reg_next_use)
free (pbi->reg_next_use);
free (pbi);
}
int
propagate_block (basic_block bb, regset live, regset local_set,
regset cond_local_set, int flags)
{
struct propagate_block_info *pbi;
rtx insn, prev;
int changed;
pbi = init_propagate_block_info (bb, live, local_set, cond_local_set, flags);
if (flags & PROP_REG_INFO)
{
unsigned i;
reg_set_iterator rsi;
EXECUTE_IF_SET_IN_REG_SET (live, 0, i, rsi)
REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL;
}
changed = 0;
for (insn = BB_END (bb); ; insn = prev)
{
if ((flags & PROP_REG_INFO)
&& CALL_P (insn)
&& find_reg_note (insn, REG_SETJMP, NULL))
IOR_REG_SET (regs_live_at_setjmp, pbi->reg_live);
prev = propagate_one_insn (pbi, insn);
if (!prev)
changed |= insn != get_insns ();
else
changed |= NEXT_INSN (prev) != insn;
if (insn == BB_HEAD (bb))
break;
}
#ifdef EH_RETURN_DATA_REGNO
if (bb_has_eh_pred (bb))
{
unsigned int i;
for (i = 0; ; ++i)
{
unsigned regno = EH_RETURN_DATA_REGNO (i);
if (regno == INVALID_REGNUM)
break;
if (pbi->local_set)
{
CLEAR_REGNO_REG_SET (pbi->cond_local_set, regno);
SET_REGNO_REG_SET (pbi->local_set, regno);
}
if (REGNO_REG_SET_P (pbi->reg_live, regno))
SET_REGNO_REG_SET (pbi->new_set, regno);
regs_ever_live[regno] = 1;
}
}
#endif
free_propagate_block_info (pbi);
return changed;
}
static int
insn_dead_p (struct propagate_block_info *pbi, rtx x, int call_ok,
rtx notes ATTRIBUTE_UNUSED)
{
enum rtx_code code = GET_CODE (x);
if (flag_non_call_exceptions && may_trap_p (x))
return 0;
#ifdef AUTO_INC_DEC
for (; notes; notes = XEXP (notes, 1))
{
if (REG_NOTE_KIND (notes) == REG_INC)
{
int regno = REGNO (XEXP (notes, 0));
if ((regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
|| REGNO_REG_SET_P (pbi->reg_live, regno))
return 0;
}
}
#endif
if (code == SET)
{
rtx r = SET_DEST (x);
#ifdef HAVE_cc0
if (GET_CODE (r) == CC0)
return ! pbi->cc0_live;
#endif
if (GET_CODE (SET_SRC (x)) == CALL)
{
if (! call_ok)
return 0;
}
else if (volatile_refs_p (SET_SRC (x)))
return 0;
if (MEM_P (r))
{
rtx temp, canon_r;
if (MEM_VOLATILE_P (r) || GET_MODE (r) == BLKmode)
return 0;
canon_r = canon_rtx (r);
for (temp = pbi->mem_set_list; temp != 0; temp = XEXP (temp, 1))
if (anti_dependence (r, XEXP (temp, 0)))
{
rtx mem = XEXP (temp, 0);
if (rtx_equal_p (XEXP (canon_r, 0), XEXP (mem, 0))
&& (GET_MODE_SIZE (GET_MODE (canon_r))
<= GET_MODE_SIZE (GET_MODE (mem))))
return 1;
#ifdef AUTO_INC_DEC
if (GET_MODE (mem) == GET_MODE (r)
&& (GET_CODE (XEXP (mem, 0)) == POST_DEC
|| GET_CODE (XEXP (mem, 0)) == POST_INC
|| GET_CODE (XEXP (mem, 0)) == POST_MODIFY)
&& GET_MODE (XEXP (mem, 0)) == GET_MODE (r)
&& rtx_equal_p (XEXP (XEXP (mem, 0), 0), XEXP (r, 0)))
return 1;
#endif
}
}
else
{
while (GET_CODE (r) == SUBREG
|| GET_CODE (r) == STRICT_LOW_PART
|| GET_CODE (r) == ZERO_EXTRACT)
r = XEXP (r, 0);
if (REG_P (r))
{
int regno = REGNO (r);
if (REGNO_REG_SET_P (pbi->reg_live, regno))
return 0;
if (regno < FIRST_PSEUDO_REGISTER)
{
int n = hard_regno_nregs[regno][GET_MODE (r)];
while (--n > 0)
if (REGNO_REG_SET_P (pbi->reg_live, regno+n))
return 0;
}
if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
return 0;
if (regno == STACK_POINTER_REGNUM)
return 0;
if (regno == FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
return 0;
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
if (regno == HARD_FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
return 0;
#endif
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
if (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
return 0;
#endif
return 1;
}
}
}
else if (code == PARALLEL)
{
int i = XVECLEN (x, 0);
for (i--; i >= 0; i--)
if (GET_CODE (XVECEXP (x, 0, i)) != CLOBBER
&& GET_CODE (XVECEXP (x, 0, i)) != USE
&& ! insn_dead_p (pbi, XVECEXP (x, 0, i), call_ok, NULL_RTX))
return 0;
return 1;
}
else if (code == CLOBBER)
{
if (REG_P (XEXP (x, 0))
&& (REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER
|| reload_completed)
&& ! REGNO_REG_SET_P (pbi->reg_live, REGNO (XEXP (x, 0))))
return 1;
}
return 0;
}
static int
libcall_dead_p (struct propagate_block_info *pbi, rtx note, rtx insn)
{
rtx x = single_set (insn);
if (x)
{
rtx r = SET_SRC (x);
if (REG_P (r) || GET_CODE (r) == SUBREG)
{
rtx call = XEXP (note, 0);
rtx call_pat;
int i;
while (call != insn && !CALL_P (call))
call = NEXT_INSN (call);
if (call == insn)
return 0;
call_pat = PATTERN (call);
if (GET_CODE (call_pat) == PARALLEL)
{
for (i = XVECLEN (call_pat, 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (call_pat, 0, i)) == SET
&& GET_CODE (SET_SRC (XVECEXP (call_pat, 0, i))) == CALL)
break;
if (i < 0)
return 0;
call_pat = XVECEXP (call_pat, 0, i);
}
if (! insn_dead_p (pbi, call_pat, 1, REG_NOTES (call)))
return 0;
while ((insn = PREV_INSN (insn)) != call)
{
if (! INSN_P (insn))
continue;
if (! insn_dead_p (pbi, PATTERN (insn), 0, REG_NOTES (insn)))
return 0;
}
return 1;
}
}
return 0;
}
int
regno_clobbered_at_setjmp (int regno)
{
if (n_basic_blocks == NUM_FIXED_BLOCKS)
return 0;
return ((REG_N_SETS (regno) > 1
|| REGNO_REG_SET_P (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end,
regno))
&& REGNO_REG_SET_P (regs_live_at_setjmp, regno));
}
static void
add_to_mem_set_list (struct propagate_block_info *pbi, rtx mem)
{
rtx i;
if (GET_MODE (mem) == BLKmode)
return;
for (i = pbi->mem_set_list; i ; i = XEXP (i, 1))
{
rtx e = XEXP (i, 0);
if (rtx_equal_p (XEXP (mem, 0), XEXP (e, 0)))
{
if (GET_MODE_SIZE (GET_MODE (mem)) > GET_MODE_SIZE (GET_MODE (e)))
{
#ifdef AUTO_INC_DEC
if (pbi->flags & PROP_AUTOINC)
PUT_MODE (e, GET_MODE (mem));
else
#endif
XEXP (i, 0) = mem;
}
return;
}
}
if (pbi->mem_set_list_len < PARAM_VALUE (PARAM_MAX_FLOW_MEMORY_LOCATIONS))
{
#ifdef AUTO_INC_DEC
if (pbi->flags & PROP_AUTOINC)
mem = shallow_copy_rtx (mem);
#endif
pbi->mem_set_list = alloc_EXPR_LIST (0, mem, pbi->mem_set_list);
pbi->mem_set_list_len++;
}
}
static int
invalidate_mems_from_autoinc (rtx *px, void *data)
{
rtx x = *px;
struct propagate_block_info *pbi = data;
if (GET_RTX_CLASS (GET_CODE (x)) == RTX_AUTOINC)
{
invalidate_mems_from_set (pbi, XEXP (x, 0));
return -1;
}
return 0;
}
static void
invalidate_mems_from_set (struct propagate_block_info *pbi, rtx exp)
{
rtx temp = pbi->mem_set_list;
rtx prev = NULL_RTX;
rtx next;
while (temp)
{
next = XEXP (temp, 1);
if ((REG_P (exp) && reg_overlap_mentioned_p (exp, XEXP (temp, 0)))
|| (MEM_P (exp)
&& reg_overlap_mentioned_p (exp, XEXP (XEXP (temp, 0), 0))))
{
if (prev)
XEXP (prev, 1) = next;
else
pbi->mem_set_list = next;
free_EXPR_LIST_node (temp);
pbi->mem_set_list_len--;
}
else
prev = temp;
temp = next;
}
}
static void
mark_set_regs (struct propagate_block_info *pbi, rtx x, rtx insn)
{
rtx cond = NULL_RTX;
rtx link;
enum rtx_code code;
int flags = pbi->flags;
if (insn)
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
{
if (REG_NOTE_KIND (link) == REG_INC)
mark_set_1 (pbi, SET, XEXP (link, 0),
(GET_CODE (x) == COND_EXEC
? COND_EXEC_TEST (x) : NULL_RTX),
insn, flags);
}
retry:
switch (code = GET_CODE (x))
{
case SET:
if (GET_CODE (XEXP (x, 1)) == ASM_OPERANDS)
flags |= PROP_ASM_SCAN;
case CLOBBER:
mark_set_1 (pbi, code, SET_DEST (x), cond, insn, flags);
return;
case COND_EXEC:
cond = COND_EXEC_TEST (x);
x = COND_EXEC_CODE (x);
goto retry;
case PARALLEL:
{
int i;
for (i = 0; i < XVECLEN (x, 0); i++)
{
rtx sub = XVECEXP (x, 0, i);
switch (code = GET_CODE (sub))
{
case COND_EXEC:
gcc_assert (!cond);
cond = COND_EXEC_TEST (sub);
sub = COND_EXEC_CODE (sub);
if (GET_CODE (sub) == SET)
goto mark_set;
if (GET_CODE (sub) == CLOBBER)
goto mark_clob;
break;
case SET:
mark_set:
if (GET_CODE (XEXP (sub, 1)) == ASM_OPERANDS)
flags |= PROP_ASM_SCAN;
case CLOBBER:
mark_clob:
mark_set_1 (pbi, code, SET_DEST (sub), cond, insn, flags);
break;
case ASM_OPERANDS:
flags |= PROP_ASM_SCAN;
break;
default:
break;
}
}
break;
}
default:
break;
}
}
static void
mark_set_1 (struct propagate_block_info *pbi, enum rtx_code code, rtx reg, rtx cond, rtx insn, int flags)
{
int regno_first = -1, regno_last = -1;
unsigned long not_dead = 0;
int i;
switch (GET_CODE (reg))
{
case PARALLEL:
for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
if (XEXP (XVECEXP (reg, 0, i), 0) != 0)
mark_set_1 (pbi, code, XEXP (XVECEXP (reg, 0, i), 0), cond, insn,
flags);
return;
case SIGN_EXTRACT:
gcc_unreachable ();
case ZERO_EXTRACT:
case STRICT_LOW_PART:
do
reg = XEXP (reg, 0);
while (GET_CODE (reg) == SUBREG
|| GET_CODE (reg) == ZERO_EXTRACT
|| GET_CODE (reg) == STRICT_LOW_PART);
if (MEM_P (reg))
break;
not_dead = (unsigned long) REGNO_REG_SET_P (pbi->reg_live, REGNO (reg));
case REG:
regno_last = regno_first = REGNO (reg);
if (regno_first < FIRST_PSEUDO_REGISTER)
regno_last += hard_regno_nregs[regno_first][GET_MODE (reg)] - 1;
break;
case SUBREG:
if (REG_P (SUBREG_REG (reg)))
{
enum machine_mode outer_mode = GET_MODE (reg);
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (reg));
regno_last = regno_first = REGNO (SUBREG_REG (reg));
if (regno_first < FIRST_PSEUDO_REGISTER)
{
regno_first += subreg_regno_offset (regno_first, inner_mode,
SUBREG_BYTE (reg),
outer_mode);
regno_last = (regno_first
+ hard_regno_nregs[regno_first][outer_mode] - 1);
reg = gen_rtx_REG (outer_mode, regno_first);
}
else
{
if (((GET_MODE_SIZE (outer_mode)
+ UNITS_PER_WORD - 1) / UNITS_PER_WORD)
< ((GET_MODE_SIZE (inner_mode)
+ UNITS_PER_WORD - 1) / UNITS_PER_WORD))
not_dead = (unsigned long) REGNO_REG_SET_P (pbi->reg_live,
regno_first);
reg = SUBREG_REG (reg);
}
}
else
reg = SUBREG_REG (reg);
break;
default:
break;
}
if (optimize && (flags & PROP_SCAN_DEAD_STORES))
{
if (REG_P (reg) || MEM_P (reg))
invalidate_mems_from_set (pbi, reg);
if (insn && MEM_P (reg))
for_each_rtx (&PATTERN (insn), invalidate_mems_from_autoinc, pbi);
if (MEM_P (reg) && ! side_effects_p (reg)
&& ! cond)
add_to_mem_set_list (pbi, canon_rtx (reg));
}
if (REG_P (reg)
&& ! (regno_first == FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
&& ! (regno_first == HARD_FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
#endif
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& ! (regno_first == ARG_POINTER_REGNUM && fixed_regs[regno_first])
#endif
)
{
int some_was_live = 0, some_was_dead = 0;
for (i = regno_first; i <= regno_last; ++i)
{
int needed_regno = REGNO_REG_SET_P (pbi->reg_live, i);
if (pbi->local_set)
{
CLEAR_REGNO_REG_SET (pbi->cond_local_set, i);
if (cond != NULL_RTX
&& ! REGNO_REG_SET_P (pbi->local_set, i))
SET_REGNO_REG_SET (pbi->cond_local_set, i);
else
SET_REGNO_REG_SET (pbi->local_set, i);
}
if (code != CLOBBER || needed_regno)
SET_REGNO_REG_SET (pbi->new_set, i);
some_was_live |= needed_regno;
some_was_dead |= ! needed_regno;
}
#ifdef HAVE_conditional_execution
if (some_was_live && ! not_dead
&& regno_first != STACK_POINTER_REGNUM)
{
for (i = regno_first; i <= regno_last; ++i)
if (! mark_regno_cond_dead (pbi, i, cond))
not_dead |= ((unsigned long) 1) << (i - regno_first);
}
#endif
if (flags & (PROP_LOG_LINKS | PROP_REG_INFO
| PROP_DEATH_NOTES | PROP_AUTOINC))
{
rtx y;
int blocknum = pbi->bb->index;
y = NULL_RTX;
if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
{
y = pbi->reg_next_use[regno_first];
for (i = regno_first; i <= regno_last; ++i)
pbi->reg_next_use[i] = 0;
}
if (flags & PROP_REG_INFO)
{
for (i = regno_first; i <= regno_last; ++i)
{
REG_N_SETS (i) += 1;
REG_N_REFS (i) += 1;
REG_FREQ (i) += REG_FREQ_FROM_BB (pbi->bb);
if (normal_flow)
REG_LIVE_LENGTH (i) += 1;
}
if (regno_first < FIRST_PSEUDO_REGISTER)
{
for (i = regno_first; i <= regno_last; i++)
regs_ever_live[i] = 1;
if (flags & PROP_ASM_SCAN)
for (i = regno_first; i <= regno_last; i++)
regs_asm_clobbered[i] = 1;
}
else
{
if (REG_BASIC_BLOCK (regno_first) == REG_BLOCK_UNKNOWN)
REG_BASIC_BLOCK (regno_first) = blocknum;
else if (REG_BASIC_BLOCK (regno_first) != blocknum)
REG_BASIC_BLOCK (regno_first) = REG_BLOCK_GLOBAL;
}
}
if (! some_was_dead)
{
if (flags & PROP_LOG_LINKS)
{
if (y && (BLOCK_NUM (y) == blocknum)
&& (regno_first >= FIRST_PSEUDO_REGISTER
|| (asm_noperands (PATTERN (y)) < 0
&& ! ((CALL_P (insn)
|| CALL_P (y))
&& global_regs[regno_first]))))
LOG_LINKS (y) = alloc_INSN_LIST (insn, LOG_LINKS (y));
}
}
else if (not_dead)
;
else if (! some_was_live)
{
if (flags & PROP_REG_INFO)
REG_N_DEATHS (regno_first) += 1;
if (flags & PROP_DEATH_NOTES
#ifdef STACK_REGS
&& (!(flags & PROP_POST_REGSTACK)
|| !IN_RANGE (REGNO (reg), FIRST_STACK_REG,
LAST_STACK_REG))
#endif
)
{
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn));
}
}
else
{
if (flags & PROP_DEATH_NOTES
#ifdef STACK_REGS
&& (!(flags & PROP_POST_REGSTACK)
|| !IN_RANGE (REGNO (reg), FIRST_STACK_REG,
LAST_STACK_REG))
#endif
)
{
for (i = regno_first; i <= regno_last; ++i)
if (! REGNO_REG_SET_P (pbi->reg_live, i))
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_UNUSED,
regno_reg_rtx[i],
REG_NOTES (insn));
}
}
}
if (some_was_live
&& regno_first != STACK_POINTER_REGNUM)
{
for (i = regno_first; i <= regno_last; ++i)
if (!(not_dead & (((unsigned long) 1) << (i - regno_first))))
{
if ((pbi->flags & PROP_REG_INFO)
&& REGNO_REG_SET_P (pbi->reg_live, i))
{
if (normal_flow)
REG_LIVE_LENGTH (i) += pbi->insn_num - reg_deaths[i];
reg_deaths[i] = 0;
}
CLEAR_REGNO_REG_SET (pbi->reg_live, i);
}
if (flags & PROP_DEAD_INSN)
emit_insn_after (gen_rtx_CLOBBER (VOIDmode, reg), insn);
}
}
else if (REG_P (reg))
{
if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
pbi->reg_next_use[regno_first] = 0;
if ((flags & PROP_REG_INFO) != 0
&& (flags & PROP_ASM_SCAN) != 0
&& regno_first < FIRST_PSEUDO_REGISTER)
{
for (i = regno_first; i <= regno_last; i++)
regs_asm_clobbered[i] = 1;
}
}
else if (GET_CODE (reg) == SCRATCH)
{
if (flags & PROP_DEATH_NOTES
#ifdef STACK_REGS
&& (!(flags & PROP_POST_REGSTACK)
|| !IN_RANGE (REGNO (reg), FIRST_STACK_REG, LAST_STACK_REG))
#endif
)
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn));
}
}
#ifdef HAVE_conditional_execution
static int
mark_regno_cond_dead (struct propagate_block_info *pbi, int regno, rtx cond)
{
if (REGNO_REG_SET_P (pbi->reg_cond_reg, regno))
flush_reg_cond_reg (pbi, regno);
if (cond == NULL_RTX)
splay_tree_remove (pbi->reg_cond_dead, regno);
else
{
splay_tree_node node;
struct reg_cond_life_info *rcli;
rtx ncond;
node = splay_tree_lookup (pbi->reg_cond_dead, regno);
if (node == NULL)
{
rcli = XNEW (struct reg_cond_life_info);
rcli->condition = cond;
rcli->stores = cond;
rcli->orig_condition = const0_rtx;
splay_tree_insert (pbi->reg_cond_dead, regno,
(splay_tree_value) rcli);
SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (XEXP (cond, 0)));
return 0;
}
else
{
rcli = (struct reg_cond_life_info *) node->value;
ncond = rcli->condition;
ncond = ior_reg_cond (ncond, cond, 1);
if (rcli->stores == const0_rtx)
rcli->stores = cond;
else if (rcli->stores != const1_rtx)
rcli->stores = ior_reg_cond (rcli->stores, cond, 1);
if (ncond == const1_rtx
|| (ncond == rcli->orig_condition && rcli->stores == const1_rtx))
splay_tree_remove (pbi->reg_cond_dead, regno);
else
{
rcli->condition = ncond;
SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (XEXP (cond, 0)));
return 0;
}
}
}
return 1;
}
static void
free_reg_cond_life_info (splay_tree_value value)
{
struct reg_cond_life_info *rcli = (struct reg_cond_life_info *) value;
free (rcli);
}
static int
flush_reg_cond_reg_1 (splay_tree_node node, void *data)
{
struct reg_cond_life_info *rcli;
int *xdata = (int *) data;
unsigned int regno = xdata[0];
if (xdata[1] >= (int) node->key)
return 0;
rcli = (struct reg_cond_life_info *) node->value;
rcli->condition = elim_reg_cond (rcli->condition, regno);
if (rcli->stores != const0_rtx && rcli->stores != const1_rtx)
rcli->stores = elim_reg_cond (rcli->stores, regno);
if (rcli->condition == const0_rtx)
{
xdata[1] = node->key;
return -1;
}
else
gcc_assert (rcli->condition != const1_rtx);
return 0;
}
static void
flush_reg_cond_reg (struct propagate_block_info *pbi, int regno)
{
int pair[2];
pair[0] = regno;
pair[1] = -1;
while (splay_tree_foreach (pbi->reg_cond_dead,
flush_reg_cond_reg_1, pair) == -1)
splay_tree_remove (pbi->reg_cond_dead, pair[1]);
CLEAR_REGNO_REG_SET (pbi->reg_cond_reg, regno);
}
static rtx
ior_reg_cond (rtx old, rtx x, int add)
{
rtx op0, op1;
if (COMPARISON_P (old))
{
if (COMPARISON_P (x)
&& REVERSE_CONDEXEC_PREDICATES_P (x, old)
&& REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0)))
return const1_rtx;
if (GET_CODE (x) == GET_CODE (old)
&& REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0)))
return old;
if (! add)
return NULL;
return gen_rtx_IOR (0, old, x);
}
switch (GET_CODE (old))
{
case IOR:
op0 = ior_reg_cond (XEXP (old, 0), x, 0);
op1 = ior_reg_cond (XEXP (old, 1), x, 0);
if (op0 != NULL || op1 != NULL)
{
if (op0 == const0_rtx)
return op1 ? op1 : gen_rtx_IOR (0, XEXP (old, 1), x);
if (op1 == const0_rtx)
return op0 ? op0 : gen_rtx_IOR (0, XEXP (old, 0), x);
if (op0 == const1_rtx || op1 == const1_rtx)
return const1_rtx;
if (op0 == NULL)
op0 = gen_rtx_IOR (0, XEXP (old, 0), x);
else if (rtx_equal_p (x, op0))
return old;
if (op1 == NULL)
op1 = gen_rtx_IOR (0, XEXP (old, 1), x);
else if (rtx_equal_p (x, op1))
return old;
return gen_rtx_IOR (0, op0, op1);
}
if (! add)
return NULL;
return gen_rtx_IOR (0, old, x);
case AND:
op0 = ior_reg_cond (XEXP (old, 0), x, 0);
op1 = ior_reg_cond (XEXP (old, 1), x, 0);
if (op0 != NULL || op1 != NULL)
{
if (op0 == const1_rtx)
return op1 ? op1 : gen_rtx_IOR (0, XEXP (old, 1), x);
if (op1 == const1_rtx)
return op0 ? op0 : gen_rtx_IOR (0, XEXP (old, 0), x);
if (op0 == const0_rtx || op1 == const0_rtx)
return const0_rtx;
if (op0 == NULL)
op0 = gen_rtx_IOR (0, XEXP (old, 0), x);
else if (rtx_equal_p (x, op0))
return op0;
if (op1 == NULL)
op1 = gen_rtx_IOR (0, XEXP (old, 1), x);
else if (rtx_equal_p (x, op1))
return op1;
return gen_rtx_AND (0, op0, op1);
}
if (! add)
return NULL;
return gen_rtx_IOR (0, old, x);
case NOT:
op0 = and_reg_cond (XEXP (old, 0), not_reg_cond (x), 0);
if (op0 != NULL)
return not_reg_cond (op0);
if (! add)
return NULL;
return gen_rtx_IOR (0, old, x);
default:
gcc_unreachable ();
}
}
static rtx
not_reg_cond (rtx x)
{
if (x == const0_rtx)
return const1_rtx;
else if (x == const1_rtx)
return const0_rtx;
if (GET_CODE (x) == NOT)
return XEXP (x, 0);
if (COMPARISON_P (x)
&& REG_P (XEXP (x, 0)))
{
gcc_assert (XEXP (x, 1) == const0_rtx);
return gen_rtx_fmt_ee (reversed_comparison_code (x, NULL),
VOIDmode, XEXP (x, 0), const0_rtx);
}
return gen_rtx_NOT (0, x);
}
static rtx
and_reg_cond (rtx old, rtx x, int add)
{
rtx op0, op1;
if (COMPARISON_P (old))
{
if (COMPARISON_P (x)
&& GET_CODE (x) == reversed_comparison_code (old, NULL)
&& REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0)))
return const0_rtx;
if (GET_CODE (x) == GET_CODE (old)
&& REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0)))
return old;
if (! add)
return NULL;
return gen_rtx_AND (0, old, x);
}
switch (GET_CODE (old))
{
case IOR:
op0 = and_reg_cond (XEXP (old, 0), x, 0);
op1 = and_reg_cond (XEXP (old, 1), x, 0);
if (op0 != NULL || op1 != NULL)
{
if (op0 == const0_rtx)
return op1 ? op1 : gen_rtx_AND (0, XEXP (old, 1), x);
if (op1 == const0_rtx)
return op0 ? op0 : gen_rtx_AND (0, XEXP (old, 0), x);
if (op0 == const1_rtx || op1 == const1_rtx)
return const1_rtx;
if (op0 == NULL)
op0 = gen_rtx_AND (0, XEXP (old, 0), x);
else if (rtx_equal_p (x, op0))
return op0;
if (op1 == NULL)
op1 = gen_rtx_AND (0, XEXP (old, 1), x);
else if (rtx_equal_p (x, op1))
return op1;
return gen_rtx_IOR (0, op0, op1);
}
if (! add)
return NULL;
return gen_rtx_AND (0, old, x);
case AND:
op0 = and_reg_cond (XEXP (old, 0), x, 0);
op1 = and_reg_cond (XEXP (old, 1), x, 0);
if (op0 != NULL || op1 != NULL)
{
if (op0 == const1_rtx)
return op1 ? op1 : gen_rtx_AND (0, XEXP (old, 1), x);
if (op1 == const1_rtx)
return op0 ? op0 : gen_rtx_AND (0, XEXP (old, 0), x);
if (op0 == const0_rtx || op1 == const0_rtx)
return const0_rtx;
if (op0 == NULL)
op0 = gen_rtx_AND (0, XEXP (old, 0), x);
else if (rtx_equal_p (x, op0))
return old;
if (op1 == NULL)
op1 = gen_rtx_AND (0, XEXP (old, 1), x);
else if (rtx_equal_p (x, op1))
return old;
return gen_rtx_AND (0, op0, op1);
}
if (! add)
return NULL;
return gen_rtx_AND (0, old, x);
case NOT:
op0 = ior_reg_cond (XEXP (old, 0), not_reg_cond (x), 0);
if (op0 != NULL)
return not_reg_cond (op0);
if (! add)
return NULL;
return gen_rtx_AND (0, old, x);
default:
gcc_unreachable ();
}
}
static rtx
elim_reg_cond (rtx x, unsigned int regno)
{
rtx op0, op1;
if (COMPARISON_P (x))
{
if (REGNO (XEXP (x, 0)) == regno)
return const0_rtx;
return x;
}
switch (GET_CODE (x))
{
case AND:
op0 = elim_reg_cond (XEXP (x, 0), regno);
op1 = elim_reg_cond (XEXP (x, 1), regno);
if (op0 == const0_rtx || op1 == const0_rtx)
return const0_rtx;
if (op0 == const1_rtx)
return op1;
if (op1 == const1_rtx)
return op0;
if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
return x;
return gen_rtx_AND (0, op0, op1);
case IOR:
op0 = elim_reg_cond (XEXP (x, 0), regno);
op1 = elim_reg_cond (XEXP (x, 1), regno);
if (op0 == const1_rtx || op1 == const1_rtx)
return const1_rtx;
if (op0 == const0_rtx)
return op1;
if (op1 == const0_rtx)
return op0;
if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
return x;
return gen_rtx_IOR (0, op0, op1);
case NOT:
op0 = elim_reg_cond (XEXP (x, 0), regno);
if (op0 == const0_rtx)
return const1_rtx;
if (op0 == const1_rtx)
return const0_rtx;
if (op0 != XEXP (x, 0))
return not_reg_cond (op0);
return x;
default:
gcc_unreachable ();
}
}
#endif
#ifdef AUTO_INC_DEC
static void
attempt_auto_inc (struct propagate_block_info *pbi, rtx inc, rtx insn,
rtx mem, rtx incr, rtx incr_reg)
{
int regno = REGNO (incr_reg);
rtx set = single_set (incr);
rtx q = SET_DEST (set);
rtx y = SET_SRC (set);
int opnum = XEXP (y, 0) == incr_reg ? 0 : 1;
int changed;
if (count_occurrences (PATTERN (insn), incr_reg, 1) != 1)
return;
if (dead_or_set_p (incr, incr_reg)
&& (regno >= FIRST_PSEUDO_REGISTER
|| ! TEST_HARD_REG_BIT (elim_reg_set, regno)))
{
if (! validate_change (insn, &XEXP (mem, 0), inc, 0))
return;
}
else if (REG_P (q)
&& ! reg_used_between_p (q, PREV_INSN (insn), incr)
&& ! reg_set_between_p (q, PREV_INSN (insn), incr))
{
rtx insns, temp;
start_sequence ();
emit_move_insn (q, incr_reg);
insns = get_insns ();
end_sequence ();
XEXP (inc, 0) = q;
validate_change (insn, &XEXP (mem, 0), inc, 1);
validate_change (incr, &XEXP (y, opnum), q, 1);
if (! apply_change_group ())
return;
emit_insn_before (insns, insn);
if (BB_HEAD (pbi->bb) == insn)
BB_HEAD (pbi->bb) = insns;
if (NONJUMP_INSN_P (PREV_INSN (insn))
&& GET_CODE (PATTERN (PREV_INSN (insn))) == SET
&& SET_SRC (PATTERN (PREV_INSN (insn))) == incr_reg)
pbi->reg_next_use[regno] = PREV_INSN (insn);
else
pbi->reg_next_use[regno] = 0;
incr_reg = q;
regno = REGNO (q);
if ((pbi->flags & PROP_REG_INFO)
&& !REGNO_REG_SET_P (pbi->reg_live, regno))
reg_deaths[regno] = pbi->insn_num;
SET_REGNO_REG_SET (pbi->reg_live, regno);
for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
if (CALL_P (temp))
{
REG_N_CALLS_CROSSED (regno)++;
if (can_throw_internal (temp))
REG_N_THROWING_CALLS_CROSSED (regno)++;
}
clear_reg_alias_info (q);
}
else
return;
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, incr_reg, REG_NOTES (insn));
changed = validate_change (incr, &SET_SRC (set), incr_reg, 0);
gcc_assert (changed);
if (REGNO (SET_DEST (set)) == REGNO (incr_reg))
{
rtx note;
while ((note = find_reg_note (incr, REG_DEAD, NULL_RTX)) != NULL_RTX)
{
remove_note (incr, note);
if (XEXP (note, 0) != incr_reg)
{
unsigned int regno = REGNO (XEXP (note, 0));
if ((pbi->flags & PROP_REG_INFO)
&& REGNO_REG_SET_P (pbi->reg_live, regno))
{
if (normal_flow)
REG_LIVE_LENGTH (regno) += pbi->insn_num - reg_deaths[regno];
reg_deaths[regno] = 0;
}
CLEAR_REGNO_REG_SET (pbi->reg_live, REGNO (XEXP (note, 0)));
}
}
SET_INSN_DELETED (incr);
}
if (regno >= FIRST_PSEUDO_REGISTER)
{
REG_FREQ (regno) += REG_FREQ_FROM_BB (pbi->bb);
REG_N_SETS (regno)++;
}
}
static void
find_auto_inc (struct propagate_block_info *pbi, rtx x, rtx insn)
{
rtx addr = XEXP (x, 0);
HOST_WIDE_INT offset = 0;
rtx set, y, incr, inc_val;
int regno;
int size = GET_MODE_SIZE (GET_MODE (x));
if (JUMP_P (insn))
return;
if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT)
offset = INTVAL (XEXP (addr, 1)), addr = XEXP (addr, 0);
if (!REG_P (addr))
return;
regno = REGNO (addr);
incr = pbi->reg_next_use[regno];
if (incr == 0 || BLOCK_NUM (incr) != BLOCK_NUM (insn))
return;
set = single_set (incr);
if (set == 0 || GET_CODE (set) != SET)
return;
y = SET_SRC (set);
if (GET_CODE (y) != PLUS)
return;
if (REG_P (XEXP (y, 0)) && REGNO (XEXP (y, 0)) == REGNO (addr))
inc_val = XEXP (y, 1);
else if (REG_P (XEXP (y, 1)) && REGNO (XEXP (y, 1)) == REGNO (addr))
inc_val = XEXP (y, 0);
else
return;
if (GET_CODE (inc_val) == CONST_INT)
{
if (HAVE_POST_INCREMENT
&& (INTVAL (inc_val) == size && offset == 0))
attempt_auto_inc (pbi, gen_rtx_POST_INC (Pmode, addr), insn, x,
incr, addr);
else if (HAVE_POST_DECREMENT
&& (INTVAL (inc_val) == -size && offset == 0))
attempt_auto_inc (pbi, gen_rtx_POST_DEC (Pmode, addr), insn, x,
incr, addr);
else if (HAVE_PRE_INCREMENT
&& (INTVAL (inc_val) == size && offset == size))
attempt_auto_inc (pbi, gen_rtx_PRE_INC (Pmode, addr), insn, x,
incr, addr);
else if (HAVE_PRE_DECREMENT
&& (INTVAL (inc_val) == -size && offset == -size))
attempt_auto_inc (pbi, gen_rtx_PRE_DEC (Pmode, addr), insn, x,
incr, addr);
else if (HAVE_POST_MODIFY_DISP && offset == 0)
attempt_auto_inc (pbi, gen_rtx_POST_MODIFY (Pmode, addr,
gen_rtx_PLUS (Pmode,
addr,
inc_val)),
insn, x, incr, addr);
else if (HAVE_PRE_MODIFY_DISP && offset == INTVAL (inc_val))
attempt_auto_inc (pbi, gen_rtx_PRE_MODIFY (Pmode, addr,
gen_rtx_PLUS (Pmode,
addr,
inc_val)),
insn, x, incr, addr);
}
else if (REG_P (inc_val)
&& ! reg_set_between_p (inc_val, PREV_INSN (insn),
NEXT_INSN (incr)))
{
if (HAVE_POST_MODIFY_REG && offset == 0)
attempt_auto_inc (pbi, gen_rtx_POST_MODIFY (Pmode, addr,
gen_rtx_PLUS (Pmode,
addr,
inc_val)),
insn, x, incr, addr);
}
}
#endif
static void
mark_used_reg (struct propagate_block_info *pbi, rtx reg,
rtx cond ATTRIBUTE_UNUSED, rtx insn)
{
unsigned int regno_first, regno_last, i;
int some_was_live, some_was_dead, some_not_set;
regno_last = regno_first = REGNO (reg);
if (regno_first < FIRST_PSEUDO_REGISTER)
regno_last += hard_regno_nregs[regno_first][GET_MODE (reg)] - 1;
some_was_live = some_was_dead = 0;
for (i = regno_first; i <= regno_last; ++i)
{
int needed_regno = REGNO_REG_SET_P (pbi->reg_live, i);
some_was_live |= needed_regno;
some_was_dead |= ! needed_regno;
}
some_not_set = 0;
for (i = regno_first; i <= regno_last; ++i)
some_not_set |= ! REGNO_REG_SET_P (pbi->new_set, i);
if (pbi->flags & (PROP_LOG_LINKS | PROP_AUTOINC))
{
pbi->reg_next_use[regno_first] = insn;
}
if (pbi->flags & PROP_REG_INFO)
{
if (regno_first < FIRST_PSEUDO_REGISTER)
{
if (! (TEST_HARD_REG_BIT (elim_reg_set, regno_first)
&& (regno_first == FRAME_POINTER_REGNUM
|| regno_first == ARG_POINTER_REGNUM)))
for (i = regno_first; i <= regno_last; ++i)
regs_ever_live[i] = 1;
}
else
{
int blocknum = pbi->bb->index;
if (REG_BASIC_BLOCK (regno_first) == REG_BLOCK_UNKNOWN)
REG_BASIC_BLOCK (regno_first) = blocknum;
else if (REG_BASIC_BLOCK (regno_first) != blocknum)
REG_BASIC_BLOCK (regno_first) = REG_BLOCK_GLOBAL;
REG_FREQ (regno_first) += REG_FREQ_FROM_BB (pbi->bb);
REG_N_REFS (regno_first)++;
}
for (i = regno_first; i <= regno_last; ++i)
if (! REGNO_REG_SET_P (pbi->reg_live, i))
{
gcc_assert (!reg_deaths[i]);
reg_deaths[i] = pbi->insn_num;
}
}
if ((pbi->flags & (PROP_DEATH_NOTES | PROP_REG_INFO))
&& some_was_dead
&& some_not_set)
{
if (regno_first != regno_last)
for (i = regno_first; i <= regno_last; ++i)
some_was_live |= REGNO_REG_SET_P (pbi->new_set, i);
if (! some_was_live)
{
if ((pbi->flags & PROP_DEATH_NOTES)
#ifdef STACK_REGS
&& (!(pbi->flags & PROP_POST_REGSTACK)
|| !IN_RANGE (REGNO (reg), FIRST_STACK_REG, LAST_STACK_REG))
#endif
&& ! find_regno_note (insn, REG_DEAD, regno_first))
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_DEAD, reg, REG_NOTES (insn));
if (pbi->flags & PROP_REG_INFO)
REG_N_DEATHS (regno_first)++;
}
else
{
for (i = regno_first; i <= regno_last; ++i)
if (! REGNO_REG_SET_P (pbi->reg_live, i)
&& ! dead_or_set_regno_p (insn, i))
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_DEAD,
regno_reg_rtx[i],
REG_NOTES (insn));
}
}
if (flag_pic
&& regno_first >= FIRST_PSEUDO_REGISTER)
maybe_uses_pic_offset_table_rtx (pbi, reg, cond, insn);
for (i = regno_first; i <= regno_last; ++i)
{
#ifdef HAVE_conditional_execution
int this_was_live = REGNO_REG_SET_P (pbi->reg_live, i);
#endif
SET_REGNO_REG_SET (pbi->reg_live, i);
#ifdef HAVE_conditional_execution
if (cond != NULL_RTX)
{
splay_tree_node node;
struct reg_cond_life_info *rcli;
rtx ncond;
if (this_was_live)
{
node = splay_tree_lookup (pbi->reg_cond_dead, i);
if (node == NULL)
{
}
else
{
rcli = (struct reg_cond_life_info *) node->value;
ncond = rcli->condition;
ncond = and_reg_cond (ncond, not_reg_cond (cond), 1);
if (ncond == const0_rtx)
splay_tree_remove (pbi->reg_cond_dead, i);
else
{
rcli->condition = ncond;
SET_REGNO_REG_SET (pbi->reg_cond_reg,
REGNO (XEXP (cond, 0)));
}
}
}
else
{
rcli = XNEW (struct reg_cond_life_info);
rcli->condition = not_reg_cond (cond);
rcli->stores = const0_rtx;
rcli->orig_condition = const0_rtx;
splay_tree_insert (pbi->reg_cond_dead, i,
(splay_tree_value) rcli);
SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (XEXP (cond, 0)));
}
}
else if (this_was_live)
{
splay_tree_remove (pbi->reg_cond_dead, i);
}
#endif
}
}
static void
maybe_uses_pic_offset_table_rtx (struct propagate_block_info *pbi ATTRIBUTE_UNUSED,
rtx reg ATTRIBUTE_UNUSED,
rtx cond ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED)
{
#ifndef ENABLE_LLVM
int found;
unsigned int regno = REGNO (reg);
extern sbitmap pic_rtx_inheritance;
if (!pic_rtx_inheritance)
return;
if (regno < FIRST_PSEUDO_REGISTER
&& regno == PIC_OFFSET_TABLE_REGNUM)
{
current_function_uses_pic_offset_table = 1;
mark_used_reg (pbi, pic_offset_table_rtx, cond, insn);
return;
}
found = TEST_BIT (pic_rtx_inheritance, regno);
if (found)
mark_used_reg (pbi, pic_offset_table_rtx, cond, insn);
#else
gcc_assert(0);
#endif
}
static bool
mark_used_dest_regs (struct propagate_block_info *pbi, rtx x, rtx cond, rtx insn)
{
int regno;
bool mark_dest = false;
rtx dest = x;
if (GET_CODE (x) == EXPR_LIST
&& GET_CODE (XEXP (x, 1)) == CONST_INT)
x = XEXP (x, 0);
if (x == NULL_RTX)
return false;
if (MEM_P (x))
{
#ifdef AUTO_INC_DEC
if (pbi->flags & PROP_AUTOINC)
find_auto_inc (pbi, x, insn);
#endif
mark_used_regs (pbi, XEXP (x, 0), cond, insn);
return true;
}
while (GET_CODE (x) == STRICT_LOW_PART
|| GET_CODE (x) == ZERO_EXTRACT
|| GET_CODE (x) == SUBREG)
{
#ifdef CANNOT_CHANGE_MODE_CLASS
if ((pbi->flags & PROP_REG_INFO) && GET_CODE (x) == SUBREG)
record_subregs_of_mode (x);
#endif
if (GET_CODE (x) == SUBREG
&& !((REG_BYTES (SUBREG_REG (x))
+ UNITS_PER_WORD - 1) / UNITS_PER_WORD
> (REG_BYTES (x)
+ UNITS_PER_WORD - 1) / UNITS_PER_WORD))
;
else
mark_dest = true;
x = XEXP (x, 0);
}
if (REG_P (x)
&& (regno = REGNO (x),
!(regno == FRAME_POINTER_REGNUM
&& (!reload_completed || frame_pointer_needed)))
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
&& !(regno == HARD_FRAME_POINTER_REGNUM
&& (!reload_completed || frame_pointer_needed))
#endif
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& !(regno == ARG_POINTER_REGNUM && fixed_regs[regno])
#endif
)
{
if (mark_dest)
mark_used_regs (pbi, dest, cond, insn);
return true;
}
return false;
}
static void
mark_used_regs (struct propagate_block_info *pbi, rtx x, rtx cond, rtx insn)
{
RTX_CODE code;
int flags = pbi->flags;
retry:
if (!x)
return;
code = GET_CODE (x);
switch (code)
{
case LABEL_REF:
case SYMBOL_REF:
case CONST_INT:
case CONST:
case CONST_DOUBLE:
case CONST_VECTOR:
case PC:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return;
#ifdef HAVE_cc0
case CC0:
pbi->cc0_live = 1;
return;
#endif
case CLOBBER:
if (MEM_P (XEXP (x, 0)))
mark_used_regs (pbi, XEXP (XEXP (x, 0), 0), cond, insn);
return;
case MEM:
if (optimize && (flags & PROP_SCAN_DEAD_STORES))
{
if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
;
else
{
rtx temp = pbi->mem_set_list;
rtx prev = NULL_RTX;
rtx next;
while (temp)
{
next = XEXP (temp, 1);
if (anti_dependence (XEXP (temp, 0), x))
{
if (prev)
XEXP (prev, 1) = next;
else
pbi->mem_set_list = next;
free_EXPR_LIST_node (temp);
pbi->mem_set_list_len--;
}
else
prev = temp;
temp = next;
}
}
if (insn)
for_each_rtx (&PATTERN (insn), invalidate_mems_from_autoinc, pbi);
}
#ifdef AUTO_INC_DEC
if (flags & PROP_AUTOINC)
find_auto_inc (pbi, x, insn);
#endif
break;
case SUBREG:
#ifdef CANNOT_CHANGE_MODE_CLASS
if (flags & PROP_REG_INFO)
record_subregs_of_mode (x);
#endif
x = SUBREG_REG (x);
if (!REG_P (x))
goto retry;
case REG:
mark_used_reg (pbi, x, cond, insn);
return;
case SET:
{
rtx dest = SET_DEST (x);
int i;
bool ret = false;
if (GET_CODE (dest) == PARALLEL)
for (i = 0; i < XVECLEN (dest, 0); i++)
ret |= mark_used_dest_regs (pbi, XVECEXP (dest, 0, i), cond, insn);
else
ret = mark_used_dest_regs (pbi, dest, cond, insn);
if (ret)
{
mark_used_regs (pbi, SET_SRC (x), cond, insn);
return;
}
}
break;
case ASM_OPERANDS:
case UNSPEC_VOLATILE:
case TRAP_IF:
case ASM_INPUT:
{
if (code != ASM_OPERANDS || MEM_VOLATILE_P (x))
{
free_EXPR_LIST_list (&pbi->mem_set_list);
pbi->mem_set_list_len = 0;
}
if (code == ASM_OPERANDS)
{
int j;
for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++)
mark_used_regs (pbi, ASM_OPERANDS_INPUT (x, j), cond, insn);
}
break;
}
case COND_EXEC:
gcc_assert (!cond);
mark_used_regs (pbi, COND_EXEC_TEST (x), NULL_RTX, insn);
cond = COND_EXEC_TEST (x);
x = COND_EXEC_CODE (x);
goto retry;
default:
break;
}
{
const char * const fmt = GET_RTX_FORMAT (code);
int i;
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (i == 0)
{
x = XEXP (x, 0);
goto retry;
}
mark_used_regs (pbi, XEXP (x, i), cond, insn);
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
mark_used_regs (pbi, XVECEXP (x, i, j), cond, insn);
}
}
}
}
#ifdef AUTO_INC_DEC
static int
try_pre_increment_1 (struct propagate_block_info *pbi, rtx insn)
{
rtx x = single_set (insn);
HOST_WIDE_INT amount = ((GET_CODE (SET_SRC (x)) == PLUS ? 1 : -1)
* INTVAL (XEXP (SET_SRC (x), 1)));
int regno = REGNO (SET_DEST (x));
rtx y = pbi->reg_next_use[regno];
if (y != 0
&& SET_DEST (x) != stack_pointer_rtx
&& BLOCK_NUM (y) == BLOCK_NUM (insn)
&& ! dead_or_set_p (y, SET_DEST (x))
&& try_pre_increment (y, SET_DEST (x), amount))
{
propagate_block_delete_insn (insn);
if (regno >= FIRST_PSEUDO_REGISTER)
{
REG_FREQ (regno) += REG_FREQ_FROM_BB (pbi->bb);
REG_N_SETS (regno)++;
}
invalidate_mems_from_set (pbi, SET_DEST (x));
return 1;
}
return 0;
}
static int
try_pre_increment (rtx insn, rtx reg, HOST_WIDE_INT amount)
{
rtx use;
int pre_ok = 0;
int post_ok = 0;
int do_post = 0;
if (HAVE_PRE_INCREMENT && amount > 0)
pre_ok = 1;
if (HAVE_POST_INCREMENT && amount > 0)
post_ok = 1;
if (HAVE_PRE_DECREMENT && amount < 0)
pre_ok = 1;
if (HAVE_POST_DECREMENT && amount < 0)
post_ok = 1;
if (! (pre_ok || post_ok))
return 0;
if (JUMP_P (insn))
return 0;
use = 0;
if (pre_ok)
use = find_use_as_address (PATTERN (insn), reg, 0);
if (post_ok && (use == 0 || use == (rtx) (size_t) 1))
{
use = find_use_as_address (PATTERN (insn), reg, -amount);
do_post = 1;
}
if (use == 0 || use == (rtx) (size_t) 1)
return 0;
if (GET_MODE_SIZE (GET_MODE (use)) != (amount > 0 ? amount : - amount))
return 0;
if (! validate_change (insn, &XEXP (use, 0),
gen_rtx_fmt_e (amount > 0
? (do_post ? POST_INC : PRE_INC)
: (do_post ? POST_DEC : PRE_DEC),
Pmode, reg), 0))
return 0;
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, reg, REG_NOTES (insn));
return 1;
}
#endif
rtx
find_use_as_address (rtx x, rtx reg, HOST_WIDE_INT plusconst)
{
enum rtx_code code = GET_CODE (x);
const char * const fmt = GET_RTX_FORMAT (code);
int i;
rtx value = 0;
rtx tem;
if (code == MEM && XEXP (x, 0) == reg && plusconst == 0)
return x;
if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS
&& XEXP (XEXP (x, 0), 0) == reg
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (x, 0), 1)) == plusconst)
return x;
if (code == SIGN_EXTRACT || code == ZERO_EXTRACT)
{
if (find_use_as_address (XEXP (x, 0), reg, 0) != 0)
return (rtx) (size_t) 1;
}
if (x == reg)
return (rtx) (size_t) 1;
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
tem = find_use_as_address (XEXP (x, i), reg, plusconst);
if (value == 0)
value = tem;
else if (tem != 0)
return (rtx) (size_t) 1;
}
else if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
{
tem = find_use_as_address (XVECEXP (x, i, j), reg, plusconst);
if (value == 0)
value = tem;
else if (tem != 0)
return (rtx) (size_t) 1;
}
}
}
return value;
}
void
dump_regset (regset r, FILE *outf)
{
unsigned i;
reg_set_iterator rsi;
if (r == NULL)
{
fputs (" (nil)", outf);
return;
}
EXECUTE_IF_SET_IN_REG_SET (r, 0, i, rsi)
{
fprintf (outf, " %d", i);
if (i < FIRST_PSEUDO_REGISTER)
fprintf (outf, " [%s]",
reg_names[i]);
}
}
void
debug_regset (regset r)
{
dump_regset (r, stderr);
putc ('\n', stderr);
}
static unsigned int
recompute_reg_usage (void)
{
allocate_reg_life_data ();
#ifdef TARGET_POWERPC
if (flag_tree_pre)
{
basic_block bb;
FOR_EACH_BB (bb)
{
CLEAR_REG_SET (bb->il.rtl->global_live_at_start);
CLEAR_REG_SET (bb->il.rtl->global_live_at_end);
}
update_life_info (NULL, UPDATE_LIFE_GLOBAL, PROP_FINAL);
}
else
#endif
update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO | PROP_DEATH_NOTES);
if (dump_file)
dump_flow_info (dump_file, dump_flags);
return 0;
}
static bool gate_flow_lite (void);
static bool gate_flow_lite (void)
{
#ifdef TARGET_386
return !!optimize;
#else
return 0;
#endif
}
static unsigned int flow_lite (void);
static unsigned int
flow_lite (void)
{
#ifndef ENABLE_LLVM
extern sbitmap pic_rtx_inheritance;
if (PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
{
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 0;
CLEAR_HARD_REG_BIT (call_fixed_reg_set, PIC_OFFSET_TABLE_REGNUM);
CLEAR_HARD_REG_BIT (fixed_reg_set, PIC_OFFSET_TABLE_REGNUM);
CLEAR_HARD_REG_BIT (call_used_reg_set, PIC_OFFSET_TABLE_REGNUM);
}
normal_flow = 0;
count_or_remove_death_notes ((sbitmap)0, 1);
life_analysis (PROP_FINAL);
if (pic_rtx_inheritance)
{
sbitmap_free (pic_rtx_inheritance);
pic_rtx_inheritance = NULL;
}
normal_flow = 1;
#else
gcc_assert(0);
#endif
return 0;
}
struct tree_opt_pass pass_recompute_reg_usage =
{
"life2",
NULL,
recompute_reg_usage,
NULL,
NULL,
0,
0,
0,
0,
0,
0,
TODO_dump_func,
'f'
};
struct tree_opt_pass pass_life3 =
{
"life3",
gate_flow_lite,
flow_lite,
NULL,
NULL,
0,
0,
0,
0,
0,
0,
TODO_dump_func,
'f'
};
int
count_or_remove_death_notes (sbitmap blocks, int kill)
{
int count = 0;
unsigned int i = 0;
basic_block bb;
if (blocks)
{
sbitmap_iterator sbi;
EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi)
{
basic_block bb = BASIC_BLOCK (i);
if (bb)
count += count_or_remove_death_notes_bb (bb, kill);
};
}
else
{
FOR_EACH_BB (bb)
{
count += count_or_remove_death_notes_bb (bb, kill);
}
}
return count;
}
static int
count_or_remove_death_notes_bb (basic_block bb, int kill)
{
int count = 0;
rtx insn;
for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
{
rtx *pprev = ®_NOTES (insn);
rtx link = *pprev;
while (link)
{
switch (REG_NOTE_KIND (link))
{
case REG_DEAD:
if (REG_P (XEXP (link, 0)))
{
rtx reg = XEXP (link, 0);
int n;
if (REGNO (reg) >= FIRST_PSEUDO_REGISTER)
n = 1;
else
n = hard_regno_nregs[REGNO (reg)][GET_MODE (reg)];
count += n;
}
case REG_UNUSED:
if (kill > 0
|| (kill
#ifdef STACK_REGS
&& (!REG_P (XEXP (link, 0))
|| !IN_RANGE (REGNO (XEXP (link, 0)),
FIRST_STACK_REG, LAST_STACK_REG))
#endif
))
{
rtx next = XEXP (link, 1);
free_EXPR_LIST_node (link);
*pprev = link = next;
break;
}
default:
pprev = &XEXP (link, 1);
link = *pprev;
break;
}
}
}
if (insn == BB_END (bb))
break;
}
return count;
}
static void
clear_log_links (sbitmap blocks)
{
rtx insn;
if (!blocks)
{
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (INSN_P (insn))
free_INSN_LIST_list (&LOG_LINKS (insn));
}
else
{
unsigned int i = 0;
sbitmap_iterator sbi;
EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi)
{
basic_block bb = BASIC_BLOCK (i);
for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
insn = NEXT_INSN (insn))
if (INSN_P (insn))
free_INSN_LIST_list (&LOG_LINKS (insn));
}
}
}
void
reg_set_to_hard_reg_set (HARD_REG_SET *to, bitmap from)
{
unsigned i;
bitmap_iterator bi;
EXECUTE_IF_SET_IN_BITMAP (from, 0, i, bi)
{
if (i >= FIRST_PSEUDO_REGISTER)
return;
SET_HARD_REG_BIT (*to, i);
}
}
static bool
gate_remove_death_notes (void)
{
return flag_profile_values;
}
static unsigned int
rest_of_handle_remove_death_notes (void)
{
count_or_remove_death_notes (NULL, 1);
return 0;
}
struct tree_opt_pass pass_remove_death_notes =
{
"ednotes",
gate_remove_death_notes,
rest_of_handle_remove_death_notes,
NULL,
NULL,
0,
0,
0,
0,
0,
0,
0,
0
};
static unsigned int
rest_of_handle_life (void)
{
regclass_init ();
life_analysis (PROP_FINAL);
if (optimize)
cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE | CLEANUP_LOG_LINKS
| (flag_thread_jumps ? CLEANUP_THREADING : 0));
if (extra_warnings)
{
setjmp_vars_warning (DECL_INITIAL (current_function_decl));
setjmp_args_warning ();
}
if (optimize)
{
if (initialize_uninitialized_subregs ())
{
allocate_reg_life_data ();
update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES,
PROP_LOG_LINKS | PROP_REG_INFO | PROP_DEATH_NOTES);
}
}
no_new_pseudos = 1;
return 0;
}
struct tree_opt_pass pass_life =
{
"life1",
NULL,
rest_of_handle_life,
NULL,
NULL,
0,
TV_FLOW,
0,
0,
0,
TODO_verify_flow,
TODO_dump_func |
TODO_ggc_collect,
'f'
};
static unsigned int
rest_of_handle_flow2 (void)
{
#ifndef ENABLE_LLVM
#ifndef STACK_REGS
if (optimize > 0)
#endif
split_all_insns (0);
if (flag_branch_target_load_optimize)
branch_target_load_optimize (epilogue_completed);
if (optimize)
cleanup_cfg (CLEANUP_EXPENSIVE);
thread_prologue_and_epilogue_insns (get_insns ());
#endif
epilogue_completed = 1;
flow2_completed = 1;
return 0;
}
struct tree_opt_pass pass_flow2 =
{
"flow2",
NULL,
rest_of_handle_flow2,
NULL,
NULL,
0,
TV_FLOW2,
0,
0,
0,
TODO_verify_flow,
TODO_dump_func |
TODO_ggc_collect,
'w'
};