#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"
#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
int flow2_completed;
int max_regno;
varray_type reg_n_info;
int regset_bytes;
int regset_size;
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;
#define MAX_MEM_SET_LIST_LEN 100
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);
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 (FILE *file, 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->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 (file)
dump_flow_info (file);
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);
}
fatal_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->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);
}
fatal_error ("internal consistency failure");
}
}
else
{
int i;
XOR_REG_SET (new_live_at_start, bb->global_live_at_start);
EXECUTE_IF_SET_IN_REG_SET (new_live_at_start, 0, i,
{
if (REGNO_REG_SET_P (bb->global_live_at_start, i))
{
if (dump_file)
{
fprintf (dump_file,
"Register %d died unexpectedly.\n", i);
dump_bb (bb, dump_file, 0);
}
fatal_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;
regset_head tmp_head;
int i;
int stabilized_prop_flags = prop_flags;
basic_block bb;
tmp = INITIALIZE_REG_SET (tmp_head);
ndead = 0;
if ((prop_flags & PROP_REG_INFO) && !reg_deaths)
reg_deaths = xcalloc (sizeof (*reg_deaths), 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->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->global_live_at_start);
CLEAR_REG_SET (bb->global_live_at_end);
}
}
if (extent == UPDATE_LIFE_GLOBAL_RM_NOTES)
count_or_remove_death_notes (blocks, 1);
}
if (prop_flags & PROP_LOG_LINKS)
clear_log_links (blocks);
if (blocks)
{
EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i,
{
bb = BASIC_BLOCK (i);
COPY_REG_SET (tmp, bb->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->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)
{
EXECUTE_IF_SET_IN_REG_SET (ENTRY_BLOCK_PTR->global_live_at_end,
FIRST_PSEUDO_REGISTER, i,
{ REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL; });
EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp,
FIRST_PSEUDO_REGISTER, i,
{
if (regno_reg_rtx[i] != 0)
{
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 (extent == UPDATE_LIFE_LOCAL)
{
if (bb->flags & BB_DIRTY)
{
SET_BIT (update_life_blocks, bb->index);
n++;
}
}
else
{
SET_BIT (update_life_blocks, bb->index);
if (bb->flags & BB_DIRTY)
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;
ENTRY_BLOCK_PTR->aux = NULL;
ENTRY_BLOCK_PTR->global_live_at_end = NULL;
EXIT_BLOCK_PTR->aux = NULL;
EXIT_BLOCK_PTR->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", nnoops);
return nnoops;
}
void
delete_dead_jumptables (void)
{
rtx insn, next;
for (insn = get_insns (); 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))
{
if (dump_file)
fprintf (dump_file, "Dead jumptable %i removed\n", INSN_UID (insn));
delete_insn (NEXT_INSN (insn));
delete_insn (insn);
next = NEXT_INSN (next);
}
}
}
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_call;
regset_head tmp_head, invalidated_by_call_head;
regset_head new_live_at_end_head;
int i;
#ifdef ENABLE_CHECKING
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
gcc_assert (!bb->aux);
#endif
tmp = INITIALIZE_REG_SET (tmp_head);
new_live_at_end = INITIALIZE_REG_SET (new_live_at_end_head);
invalidated_by_call = INITIALIZE_REG_SET (invalidated_by_call_head);
for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
SET_REGNO_REG_SET (invalidated_by_call, i);
queue = xmalloc ((n_basic_blocks + 2) * sizeof (*queue));
qtail = queue;
qhead = qend = queue + n_basic_blocks + 2;
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;
}
}
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;
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_operation (tmp, sb->global_live_at_start,
invalidated_by_call, BITMAP_AND_COMPL);
IOR_REG_SET (new_live_at_end, tmp);
}
else
IOR_REG_SET (new_live_at_end, sb->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->global_live_at_end, new_live_at_end);
continue;
}
if (bb->local_set == NULL)
{
bb->local_set = OBSTACK_ALLOC_REG_SET (&flow_obstack);
bb->cond_local_set = OBSTACK_ALLOC_REG_SET (&flow_obstack);
rescan = 1;
}
else
{
CLEAR_REG_SET (tmp);
rescan = bitmap_operation (tmp, bb->global_live_at_end,
new_live_at_end, BITMAP_AND_COMPL);
if (! rescan)
{
CLEAR_REG_SET (tmp);
rescan = bitmap_operation (tmp, new_live_at_end,
bb->cond_local_set, BITMAP_AND);
}
if (! rescan)
{
CLEAR_REG_SET (tmp);
changed = bitmap_operation (tmp, bb->global_live_at_end,
new_live_at_end, BITMAP_XOR);
if (! changed)
continue;
rescan = bitmap_operation (tmp, tmp, bb->local_set,
BITMAP_AND_COMPL);
}
}
if (blocks_out)
SET_BIT (blocks_out, bb->index);
if (! rescan)
{
bitmap_operation (tmp, new_live_at_end, bb->global_live_at_end,
BITMAP_AND_COMPL);
COPY_REG_SET (bb->global_live_at_end, new_live_at_end);
changed = bitmap_operation (bb->global_live_at_start,
bb->global_live_at_start,
tmp, BITMAP_IOR);
if (! changed)
continue;
}
else
{
COPY_REG_SET (bb->global_live_at_end, new_live_at_end);
propagate_block (bb, new_live_at_end, bb->local_set,
bb->cond_local_set, flags);
if (REG_SET_EQUAL_P (bb->global_live_at_start, new_live_at_end))
continue;
COPY_REG_SET (bb->global_live_at_start, new_live_at_end);
}
FOR_EACH_EDGE (e, ei, bb->preds)
{
basic_block pb = e->src;
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_call);
if (blocks_out)
{
EXECUTE_IF_SET_IN_SBITMAP (blocks_out, 0, i,
{
basic_block bb = BASIC_BLOCK (i);
FREE_REG_SET (bb->local_set);
FREE_REG_SET (bb->cond_local_set);
});
}
else
{
FOR_EACH_BB (bb)
{
FREE_REG_SET (bb->local_set);
FREE_REG_SET (bb->cond_local_set);
}
}
free (queue);
}
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;
}
int
initialize_uninitialized_subregs (void)
{
rtx insn;
edge e;
int reg, did_something = 0;
find_regno_partial_param param;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
{
basic_block bb = e->dest;
regset map = bb->global_live_at_start;
EXECUTE_IF_SET_IN_REG_SET (map,
FIRST_PSEUDO_REGISTER, reg,
{
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;
}
void
allocate_bb_life_data (void)
{
basic_block bb;
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
{
bb->global_live_at_start = OBSTACK_ALLOC_REG_SET (&flow_obstack);
bb->global_live_at_end = OBSTACK_ALLOC_REG_SET (&flow_obstack);
}
regs_live_at_setjmp = OBSTACK_ALLOC_REG_SET (&flow_obstack);
}
void
allocate_reg_life_data (void)
{
int i;
max_regno = max_reg_num ();
gcc_assert (!reg_deaths);
reg_deaths = xcalloc (sizeof (*reg_deaths), 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_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;
int 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);
mark_set_regs (pbi, PATTERN (insn), 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
{
rtx note;
if (CALL_P (insn) && (flags & PROP_REG_INFO))
EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
{ REG_N_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->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);
if ((flags & PROP_EQUAL_NOTES)
&& ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX))
|| (note = find_reg_note (insn, REG_EQUIV, NULL_RTX))))
mark_used_regs (pbi, XEXP (note, 0), 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 = xmalloc (sizeof (*pbi));
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 = xcalloc (max_reg_num (), sizeof (rtx));
else
pbi->reg_next_use = NULL;
pbi->new_set = BITMAP_XMALLOC ();
#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_XMALLOC ();
if (JUMP_P (BB_END (bb))
&& any_condjump_p (BB_END (bb)))
{
regset_head diff_head;
regset diff = INITIALIZE_REG_SET (diff_head);
basic_block bb_true, bb_false;
int i;
bb_true = EDGE_SUCC (bb, 0)->dest;
if (EDGE_COUNT (bb->succs) > 1)
{
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;
}
if (bitmap_operation (diff, bb_true->global_live_at_start,
bb_false->global_live_at_start, BITMAP_XOR))
{
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));
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,
{
struct reg_cond_life_info *rcli;
rtx cond;
rcli = xmalloc (sizeof (*rcli));
if (REGNO_REG_SET_P (bb_true->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
|| (EDGE_COUNT (bb->succs) == 1
&& EDGE_SUCC (bb, 0)->dest == 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_XFREE (pbi->new_set);
#ifdef HAVE_conditional_execution
splay_tree_delete (pbi->reg_cond_dead);
BITMAP_XFREE (pbi->reg_cond_reg);
#endif
if (pbi->flags & PROP_REG_INFO)
{
int num = pbi->insn_num;
int i;
EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
{ 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)
{
int i;
EXECUTE_IF_SET_IN_REG_SET (live, 0, i,
{ 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;
}
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))
{
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);
}
return insn_dead_p (pbi, call_pat, 1, REG_NOTES (call));
}
}
return 1;
}
int
regno_clobbered_at_setjmp (int regno)
{
if (n_basic_blocks == 0)
return 0;
return ((REG_N_SETS (regno) > 1
|| REGNO_REG_SET_P (ENTRY_BLOCK_PTR->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 < MAX_MEM_SET_LIST_LEN)
{
#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_overlap_mentioned_p (exp, XEXP (temp, 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 ZERO_EXTRACT:
case SIGN_EXTRACT:
case STRICT_LOW_PART:
do
reg = XEXP (reg, 0);
while (GET_CODE (reg) == SUBREG
|| GET_CODE (reg) == ZERO_EXTRACT
|| GET_CODE (reg) == SIGN_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))
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)
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);
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)
{
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn));
}
}
else
{
if (flags & PROP_DEATH_NOTES)
{
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))
{
REG_LIVE_LENGTH (i) += pbi->insn_num - reg_deaths[i];
reg_deaths[i] = 0;
}
CLEAR_REGNO_REG_SET (pbi->reg_live, i);
}
}
}
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)
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 = xmalloc (sizeof (*rcli));
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)++;
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))
{
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)
&& ! 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));
}
}
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 = xmalloc (sizeof (*rcli));
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
mark_used_regs (struct propagate_block_info *pbi, rtx x, rtx cond, rtx insn)
{
RTX_CODE code;
int regno;
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 testreg = SET_DEST (x);
int mark_dest = 0;
if (MEM_P (testreg))
{
#ifdef AUTO_INC_DEC
if (flags & PROP_AUTOINC)
find_auto_inc (pbi, testreg, insn);
#endif
mark_used_regs (pbi, XEXP (testreg, 0), cond, insn);
mark_used_regs (pbi, SET_SRC (x), cond, insn);
return;
}
while (GET_CODE (testreg) == STRICT_LOW_PART
|| GET_CODE (testreg) == ZERO_EXTRACT
|| GET_CODE (testreg) == SIGN_EXTRACT
|| GET_CODE (testreg) == SUBREG)
{
#ifdef CANNOT_CHANGE_MODE_CLASS
if ((flags & PROP_REG_INFO) && GET_CODE (testreg) == SUBREG)
record_subregs_of_mode (testreg);
#endif
if (GET_CODE (testreg) == SUBREG
&& !((REG_BYTES (SUBREG_REG (testreg))
+ UNITS_PER_WORD - 1) / UNITS_PER_WORD
> (REG_BYTES (testreg)
+ UNITS_PER_WORD - 1) / UNITS_PER_WORD))
;
else
mark_dest = 1;
testreg = XEXP (testreg, 0);
}
if ((GET_CODE (testreg) == PARALLEL
&& GET_MODE (testreg) == BLKmode)
|| (REG_P (testreg)
&& (regno = REGNO (testreg),
! (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, SET_DEST (x), cond, insn);
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)
{
int i;
if (r == NULL)
{
fputs (" (nil)", outf);
return;
}
EXECUTE_IF_SET_IN_REG_SET (r, 0, i,
{
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);
}
void
recompute_reg_usage (rtx f ATTRIBUTE_UNUSED, int loop_step ATTRIBUTE_UNUSED)
{
allocate_reg_life_data ();
update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO | PROP_DEATH_NOTES);
}
int
count_or_remove_death_notes (sbitmap blocks, int kill)
{
int count = 0;
int i;
basic_block bb;
if (blocks)
{
EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i,
{
count += count_or_remove_death_notes_bb (BASIC_BLOCK (i), 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)
{
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;
int i;
if (!blocks)
{
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (INSN_P (insn))
free_INSN_LIST_list (&LOG_LINKS (insn));
}
else
EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i,
{
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)
{
int 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);
}
}