#include "config.h"
#include "system.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 "ssa.h"
#include "timevar.h"
#include "obstack.h"
#include "splay-tree.h"
#ifndef EXIT_IGNORE_STACK
#define EXIT_IGNORE_STACK 0
#endif
#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 LOCAL_REGNO
#define LOCAL_REGNO(REGNO) 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) ((x) == reverse_condition (y))
#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;
int (*lang_missing_noreturn_ok_p) PARAMS ((tree));
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;
};
static int ndead;
#define MAX_MEM_SET_LIST_LEN 100
static int verify_wide_reg_1 PARAMS ((rtx *, void *));
static void verify_wide_reg PARAMS ((int, basic_block));
static void verify_local_live_at_start PARAMS ((regset, basic_block));
static void notice_stack_pointer_modification_1 PARAMS ((rtx, rtx, void *));
static void notice_stack_pointer_modification PARAMS ((rtx));
static void mark_reg PARAMS ((rtx, void *));
static void mark_regs_live_at_end PARAMS ((regset));
static int set_phi_alternative_reg PARAMS ((rtx, int, int, void *));
static void calculate_global_regs_live PARAMS ((sbitmap, sbitmap, int));
static void propagate_block_delete_insn PARAMS ((rtx));
static rtx propagate_block_delete_libcall PARAMS ((rtx, rtx));
static int insn_dead_p PARAMS ((struct propagate_block_info *,
rtx, int, rtx));
static int libcall_dead_p PARAMS ((struct propagate_block_info *,
rtx, rtx));
static void mark_set_regs PARAMS ((struct propagate_block_info *,
rtx, rtx));
static void mark_set_1 PARAMS ((struct propagate_block_info *,
enum rtx_code, rtx, rtx,
rtx, int));
static int find_regno_partial PARAMS ((rtx *, void *));
#ifdef HAVE_conditional_execution
static int mark_regno_cond_dead PARAMS ((struct propagate_block_info *,
int, rtx));
static void free_reg_cond_life_info PARAMS ((splay_tree_value));
static int flush_reg_cond_reg_1 PARAMS ((splay_tree_node, void *));
static void flush_reg_cond_reg PARAMS ((struct propagate_block_info *,
int));
static rtx elim_reg_cond PARAMS ((rtx, unsigned int));
static rtx ior_reg_cond PARAMS ((rtx, rtx, int));
static rtx not_reg_cond PARAMS ((rtx));
static rtx and_reg_cond PARAMS ((rtx, rtx, int));
#endif
#ifdef AUTO_INC_DEC
static void attempt_auto_inc PARAMS ((struct propagate_block_info *,
rtx, rtx, rtx, rtx, rtx));
static void find_auto_inc PARAMS ((struct propagate_block_info *,
rtx, rtx));
static int try_pre_increment_1 PARAMS ((struct propagate_block_info *,
rtx));
static int try_pre_increment PARAMS ((rtx, rtx, HOST_WIDE_INT));
#endif
static void mark_used_reg PARAMS ((struct propagate_block_info *,
rtx, rtx, rtx));
static void mark_used_regs PARAMS ((struct propagate_block_info *,
rtx, rtx, rtx));
void dump_flow_info PARAMS ((FILE *));
void debug_flow_info PARAMS ((void));
static void add_to_mem_set_list PARAMS ((struct propagate_block_info *,
rtx));
static int invalidate_mems_from_autoinc PARAMS ((rtx *, void *));
static void invalidate_mems_from_set PARAMS ((struct propagate_block_info *,
rtx));
static void clear_log_links PARAMS ((sbitmap));
void
check_function_return_warnings ()
{
if (warn_missing_noreturn
&& !TREE_THIS_VOLATILE (cfun->decl)
&& EXIT_BLOCK_PTR->pred == NULL
&& (lang_missing_noreturn_ok_p
&& !lang_missing_noreturn_ok_p (cfun->decl)))
warning ("function might be possible candidate for attribute `noreturn'");
if (TREE_THIS_VOLATILE (cfun->decl)
&& EXIT_BLOCK_PTR->pred != NULL)
warning ("`noreturn' function does return");
else if (warn_return_type
&& cfun->x_clobber_return_insn != NULL
&& EXIT_BLOCK_PTR->pred != NULL)
{
int max_uid = get_max_uid ();
if (INSN_UID (cfun->x_clobber_return_insn) < max_uid)
{
rtx insn;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (insn == cfun->x_clobber_return_insn)
{
warning ("control reaches end of non-void function");
break;
}
}
}
}
rtx
first_insn_after_basic_block_note (block)
basic_block block;
{
rtx insn;
insn = block->head;
if (insn == NULL_RTX)
return NULL_RTX;
if (GET_CODE (insn) == CODE_LABEL)
insn = NEXT_INSN (insn);
if (!NOTE_INSN_BASIC_BLOCK_P (insn))
abort ();
return NEXT_INSN (insn);
}
void
life_analysis (f, file, flags)
rtx f;
FILE *file;
int flags;
{
int i;
#ifdef ELIMINABLE_REGS
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
bitmap_initialize (&subregs_of_mode, 1);
#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 (f);
if (! reload_completed)
notice_stack_pointer_modification (f);
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));
update_life_info (NULL, UPDATE_LIFE_GLOBAL, flags);
if (optimize && (flags & PROP_SCAN_DEAD_STORES))
end_alias_analysis ();
if (file)
dump_flow_info (file);
free_basic_block_vars (1);
delete_dead_jumptables ();
}
static int
verify_wide_reg_1 (px, pregno)
rtx *px;
void *pregno;
{
rtx x = *px;
unsigned int regno = *(int *) pregno;
if (GET_CODE (x) == REG && REGNO (x) == regno)
{
if (GET_MODE_BITSIZE (GET_MODE (x)) <= BITS_PER_WORD)
return 2;
return 1;
}
return 0;
}
static void
verify_wide_reg (regno, bb)
int regno;
basic_block bb;
{
rtx head = bb->head, end = bb->end;
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 (rtl_dump_file)
{
fprintf (rtl_dump_file, "Register %d died unexpectedly.\n", regno);
dump_bb (bb, rtl_dump_file);
}
abort ();
}
static void
verify_local_live_at_start (new_live_at_start, bb)
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 (rtl_dump_file)
{
fprintf (rtl_dump_file,
"live_at_start mismatch in bb %d, aborting\nNew:\n",
bb->index);
debug_bitmap_file (rtl_dump_file, new_live_at_start);
fputs ("Old:\n", rtl_dump_file);
dump_bb (bb, rtl_dump_file);
}
abort ();
}
}
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 (rtl_dump_file)
{
fprintf (rtl_dump_file,
"Register %d died unexpectedly.\n", i);
dump_bb (bb, rtl_dump_file);
}
abort ();
}
verify_wide_reg (i, bb);
});
}
}
int
update_life_info (blocks, extent, prop_flags)
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;
timevar_push ((extent == UPDATE_LIFE_LOCAL || blocks)
? TV_LIFE_UPDATE : TV_LIFE);
if ((prop_flags & PROP_ALLOW_CFG_CHANGES)
&& (extent == UPDATE_LIFE_LOCAL || blocks))
abort ();
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);
}
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;
}
});
}
timevar_pop ((extent == UPDATE_LIFE_LOCAL || blocks)
? TV_LIFE_UPDATE : TV_LIFE);
if (ndead && rtl_dump_file)
fprintf (rtl_dump_file, "deleted %i dead insns\n", ndead);
return ndead;
}
int
update_life_info_in_dirty_blocks (extent, prop_flags)
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 (keep_head_end_p)
int keep_head_end_p;
{
if (! keep_head_end_p)
{
if (basic_block_info)
{
clear_edges ();
VARRAY_FREE (basic_block_info);
}
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 (f)
rtx f ATTRIBUTE_UNUSED;
{
rtx insn, next;
basic_block bb;
int nnoops = 0;
FOR_EACH_BB (bb)
{
for (insn = bb->head; insn != NEXT_INSN (bb->end); 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 && rtl_dump_file)
fprintf (rtl_dump_file, "deleted %i noop moves", nnoops);
return nnoops;
}
void
delete_dead_jumptables ()
{
rtx insn, next;
for (insn = get_insns (); insn; insn = next)
{
next = NEXT_INSN (insn);
if (GET_CODE (insn) == CODE_LABEL
&& LABEL_NUSES (insn) == LABEL_PRESERVE_P (insn)
&& GET_CODE (next) == JUMP_INSN
&& (GET_CODE (PATTERN (next)) == ADDR_VEC
|| GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC))
{
if (rtl_dump_file)
fprintf (rtl_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 (x, pat, data)
rtx x;
rtx pat ATTRIBUTE_UNUSED;
void *data ATTRIBUTE_UNUSED;
{
if (x == stack_pointer_rtx
|| (GET_CODE (x) == MEM
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == 'a'
&& XEXP (XEXP (x, 0), 0) == stack_pointer_rtx))
current_function_sp_is_unchanging = 0;
}
static void
notice_stack_pointer_modification (f)
rtx f;
{
rtx insn;
current_function_sp_is_unchanging = !current_function_calls_alloca;
if (! current_function_sp_is_unchanging)
return;
for (insn = f; insn; insn = NEXT_INSN (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 (reg, xset)
rtx reg;
void *xset;
{
regset set = (regset) xset;
int regno = REGNO (reg);
if (GET_MODE (reg) == BLKmode)
abort ();
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 (set)
regset set;
{
unsigned int i;
if ((HAVE_epilogue && reload_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 (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 && reload_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 || ! reload_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 || ! reload_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 int
set_phi_alternative_reg (insn, dest_regno, src_regno, data)
rtx insn ATTRIBUTE_UNUSED;
int dest_regno ATTRIBUTE_UNUSED;
int src_regno;
void *data;
{
regset live = (regset) data;
SET_REGNO_REG_SET (live, src_regno);
return 0;
}
static void
calculate_global_regs_live (blocks_in, blocks_out, flags)
sbitmap blocks_in, 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)
if (bb->aux)
abort ();
#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 = (basic_block *) 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;
bb = *qhead++;
if (qhead == qend)
qhead = queue;
bb->aux = NULL;
CLEAR_REG_SET (new_live_at_end);
if (bb->succ)
for (e = bb->succ; e; e = e->succ_next)
{
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 (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 (in_ssa_form)
for_each_successor_phi (bb, &set_phi_alternative_reg,
new_live_at_end);
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 (e = bb->pred; e; e = e->pred_next)
{
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 (ptr, data)
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 (GET_CODE (XEXP (*ptr, 0)) == REG && REGNO (XEXP (*ptr, 0)) == reg)
{
param->retval = XEXP (*ptr, 0);
return 1;
}
break;
case SUBREG:
if (GET_CODE (SUBREG_REG (*ptr)) == REG
&& REGNO (SUBREG_REG (*ptr)) == reg)
{
param->retval = SUBREG_REG (*ptr);
return 1;
}
break;
default:
break;
}
return 0;
}
int
initialize_uninitialized_subregs ()
{
rtx insn;
edge e;
int reg, did_something = 0;
find_regno_partial_param param;
for (e = ENTRY_BLOCK_PTR->succ; e; e = e->succ_next)
{
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)
{
insn = gen_move_insn (param.retval,
CONST0_RTX (GET_MODE (param.retval)));
insert_insn_on_edge (insn, e);
did_something = 1;
}
}
});
}
if (did_something)
commit_edge_insertions ();
return did_something;
}
void
allocate_bb_life_data ()
{
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 ()
{
int i;
max_regno = max_reg_num ();
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_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN;
}
}
static void
propagate_block_delete_insn (insn)
rtx insn;
{
rtx inote = find_reg_note (insn, REG_LABEL, NULL_RTX);
if (inote && GET_CODE (inote) == CODE_LABEL)
{
rtx label = XEXP (inote, 0);
rtx next;
if (LABEL_NUSES (label) == 1 + LABEL_PRESERVE_P (label)
&& (next = next_nonnote_insn (label)) != NULL
&& GET_CODE (next) == JUMP_INSN
&& (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 ( insn, note)
rtx insn, 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 (pbi, 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
&& GET_CODE (SET_DEST (x)) == REG
&& (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);
else
{
rtx note;
if (GET_CODE (insn) == CALL_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 (GET_CODE (insn) == CALL_INSN)
{
int i;
rtx note, cond;
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);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
{
mark_set_1 (pbi, CLOBBER, 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 && GET_CODE (insn) == CALL_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))
if (GET_CODE (XEXP (note, 0)) == USE)
mark_used_regs (pbi, XEXP (XEXP (note, 0), 0),
cond, insn);
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);
}
}
if (flags & PROP_REG_INFO)
EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
{ REG_LIVE_LENGTH (i)++; });
return prev;
}
struct propagate_block_info *
init_propagate_block_info (bb, live, local_set, cond_local_set, flags)
basic_block bb;
regset live, local_set, 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;
if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
pbi->reg_next_use = (rtx *) 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 (GET_CODE (bb->end) == JUMP_INSN
&& any_condjump_p (bb->end))
{
regset_head diff_head;
regset diff = INITIALIZE_REG_SET (diff_head);
basic_block bb_true, bb_false;
rtx cond_true, cond_false, set_src;
int i;
bb_true = bb->succ->dest;
if (bb->succ->succ_next != NULL)
{
bb_false = bb->succ->succ_next->dest;
if (bb->succ->flags & EDGE_FALLTHRU)
{
basic_block t = bb_false;
bb_false = bb_true;
bb_true = t;
}
else if (! (bb->succ->succ_next->flags & EDGE_FALLTHRU))
abort ();
}
else
{
if (JUMP_LABEL (bb->end) != bb_true->head)
abort ();
bb_false = bb_true;
}
set_src = SET_SRC (pc_set (bb->end));
cond_true = XEXP (set_src, 0);
cond_false = gen_rtx_fmt_ee (reverse_condition (GET_CODE (cond_true)),
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;
}
if (bitmap_operation (diff, bb_true->global_live_at_start,
bb_false->global_live_at_start, BITMAP_XOR))
{
rtx reg = XEXP (cond_true, 0);
if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg);
if (GET_CODE (reg) != REG)
abort ();
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 = (struct reg_cond_life_info *) 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)
&& (bb->succ == NULL
|| (bb->succ->succ_next == NULL
&& bb->succ->dest == EXIT_BLOCK_PTR
&& ! current_function_calls_eh_return)))
{
rtx insn, set;
for (insn = bb->end; insn != bb->head; insn = PREV_INSN (insn))
if (GET_CODE (insn) == INSN
&& (set = single_set (insn))
&& GET_CODE (SET_DEST (set)) == MEM)
{
rtx mem = SET_DEST (set);
rtx canon_mem = canon_rtx (mem);
if (RTX_UNCHANGING_P (canon_mem))
continue;
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 (pbi)
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->reg_next_use)
free (pbi->reg_next_use);
free (pbi);
}
int
propagate_block (bb, live, local_set, cond_local_set, flags)
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;; insn = prev)
{
if ((flags & PROP_REG_INFO)
&& GET_CODE (insn) == CALL_INSN
&& find_reg_note (insn, REG_SETJMP, NULL))
IOR_REG_SET (regs_live_at_setjmp, pbi->reg_live);
prev = propagate_one_insn (pbi, insn);
changed |= NEXT_INSN (prev) != insn;
if (insn == bb->head)
break;
}
free_propagate_block_info (pbi);
return changed;
}
static int
insn_dead_p (pbi, x, call_ok, notes)
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 (GET_CODE (r) == MEM)
{
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 (GET_CODE (r) == REG)
{
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 && GET_CODE (XEXP (x, 0)) == REG
&& REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER
&& ! REGNO_REG_SET_P (pbi->reg_live, REGNO (XEXP (x, 0))))
return 1;
return 0;
}
static int
libcall_dead_p (pbi, note, insn)
struct propagate_block_info *pbi;
rtx note;
rtx insn;
{
rtx x = single_set (insn);
if (x)
{
rtx r = SET_SRC (x);
if (GET_CODE (r) == REG)
{
rtx call = XEXP (note, 0);
rtx call_pat;
int i;
while (call != insn && GET_CODE (call) != CALL_INSN)
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_uninitialized (regno)
unsigned int regno;
{
if (n_basic_blocks == 0
|| (regno < FIRST_PSEUDO_REGISTER
&& (global_regs[regno]
|| fixed_regs[regno]
|| FUNCTION_ARG_REGNO_P (regno))))
return 0;
return REGNO_REG_SET_P (ENTRY_BLOCK_PTR->next_bb->global_live_at_start, regno);
}
int
regno_clobbered_at_setjmp (regno)
int regno;
{
if (n_basic_blocks == 0)
return 0;
return ((REG_N_SETS (regno) > 1
|| REGNO_REG_SET_P (ENTRY_BLOCK_PTR->next_bb->global_live_at_start, regno))
&& REGNO_REG_SET_P (regs_live_at_setjmp, regno));
}
static void
add_to_mem_set_list (pbi, mem)
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 (px, data)
rtx *px;
void *data;
{
rtx x = *px;
struct propagate_block_info *pbi = data;
if (GET_RTX_CLASS (GET_CODE (x)) == 'a')
{
invalidate_mems_from_set (pbi, XEXP (x, 0));
return -1;
}
return 0;
}
static void
invalidate_mems_from_set (pbi, exp)
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 (pbi, x, insn)
struct propagate_block_info *pbi;
rtx x, insn;
{
rtx cond = NULL_RTX;
rtx link;
enum rtx_code code;
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, pbi->flags);
}
retry:
switch (code = GET_CODE (x))
{
case SET:
case CLOBBER:
mark_set_1 (pbi, code, SET_DEST (x), cond, insn, pbi->flags);
return;
case COND_EXEC:
cond = COND_EXEC_TEST (x);
x = COND_EXEC_CODE (x);
goto retry;
case PARALLEL:
{
int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
rtx sub = XVECEXP (x, 0, i);
switch (code = GET_CODE (sub))
{
case COND_EXEC:
if (cond != NULL_RTX)
abort ();
cond = COND_EXEC_TEST (sub);
sub = COND_EXEC_CODE (sub);
if (GET_CODE (sub) != SET && GET_CODE (sub) != CLOBBER)
break;
case SET:
case CLOBBER:
mark_set_1 (pbi, code, SET_DEST (sub), cond, insn, pbi->flags);
break;
default:
break;
}
}
break;
}
default:
break;
}
}
static void
mark_set_1 (pbi, code, reg, cond, insn, flags)
struct propagate_block_info *pbi;
enum rtx_code code;
rtx reg, cond, 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 (GET_CODE (reg) == MEM)
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 (GET_CODE (SUBREG_REG (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 (GET_CODE (reg) == REG)
invalidate_mems_from_set (pbi, reg);
if (insn && GET_CODE (reg) == MEM)
for_each_rtx (&PATTERN (insn), invalidate_mems_from_autoinc, pbi);
if (GET_CODE (reg) == MEM && ! side_effects_p (reg)
&& ! cond)
add_to_mem_set_list (pbi, canon_rtx (reg));
}
if (GET_CODE (reg) == 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;
}
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))
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))))
CLEAR_REGNO_REG_SET (pbi->reg_live, i);
}
}
else if (GET_CODE (reg) == REG)
{
if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
pbi->reg_next_use[regno_first] = 0;
}
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 (pbi, regno, cond)
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 = (struct reg_cond_life_info *) 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 (value)
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 (node, data)
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 if (rcli->condition == const1_rtx)
abort ();
return 0;
}
static void
flush_reg_cond_reg (pbi, regno)
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 (old, x, add)
rtx old, x;
int add;
{
rtx op0, op1;
if (GET_RTX_CLASS (GET_CODE (old)) == '<')
{
if (GET_RTX_CLASS (GET_CODE (x)) == '<'
&& REVERSE_CONDEXEC_PREDICATES_P (GET_CODE (x), GET_CODE (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:
abort ();
}
}
static rtx
not_reg_cond (x)
rtx x;
{
enum rtx_code x_code;
if (x == const0_rtx)
return const1_rtx;
else if (x == const1_rtx)
return const0_rtx;
x_code = GET_CODE (x);
if (x_code == NOT)
return XEXP (x, 0);
if (GET_RTX_CLASS (x_code) == '<'
&& GET_CODE (XEXP (x, 0)) == REG)
{
if (XEXP (x, 1) != const0_rtx)
abort ();
return gen_rtx_fmt_ee (reverse_condition (x_code),
VOIDmode, XEXP (x, 0), const0_rtx);
}
return gen_rtx_NOT (0, x);
}
static rtx
and_reg_cond (old, x, add)
rtx old, x;
int add;
{
rtx op0, op1;
if (GET_RTX_CLASS (GET_CODE (old)) == '<')
{
if (GET_RTX_CLASS (GET_CODE (x)) == '<'
&& GET_CODE (x) == reverse_condition (GET_CODE (old))
&& 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:
abort ();
}
}
static rtx
elim_reg_cond (x, regno)
rtx x;
unsigned int regno;
{
rtx op0, op1;
if (GET_RTX_CLASS (GET_CODE (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:
abort ();
}
}
#endif
#ifdef AUTO_INC_DEC
static void
attempt_auto_inc (pbi, inc, insn, mem, incr, incr_reg)
struct propagate_block_info *pbi;
rtx inc, insn, mem, incr, 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;
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 (GET_CODE (q) == REG
&& ! 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 (pbi->bb->head == insn)
pbi->bb->head = insns;
if (GET_CODE (PREV_INSN (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);
SET_REGNO_REG_SET (pbi->reg_live, regno);
for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
if (GET_CODE (temp) == CALL_INSN)
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));
if (! validate_change (incr, &SET_SRC (set), incr_reg, 0))
abort ();
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)
CLEAR_REGNO_REG_SET (pbi->reg_live, REGNO (XEXP (note, 0)));
}
PUT_CODE (incr, NOTE);
NOTE_LINE_NUMBER (incr) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (incr) = 0;
}
if (regno >= FIRST_PSEUDO_REGISTER)
{
REG_FREQ (regno) += REG_FREQ_FROM_BB (pbi->bb);
REG_N_SETS (regno)++;
}
}
static void
find_auto_inc (pbi, x, insn)
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 (GET_CODE (insn) == JUMP_INSN)
return;
if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT)
offset = INTVAL (XEXP (addr, 1)), addr = XEXP (addr, 0);
if (GET_CODE (addr) != REG)
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 (GET_CODE (inc_val) == REG
&& ! 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 (pbi, reg, cond, insn)
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)++;
}
}
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 = (struct reg_cond_life_info *) 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 (pbi, x, cond, insn)
struct propagate_block_info *pbi;
rtx x, cond, 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 (GET_CODE (XEXP (x, 0)) == MEM)
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 (GET_CODE (SUBREG_REG (x)) == REG
&& REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
bitmap_set_bit (&subregs_of_mode, REGNO (SUBREG_REG (x))
* MAX_MACHINE_MODE
+ GET_MODE (x));
#endif
x = SUBREG_REG (x);
if (GET_CODE (x) != REG)
goto retry;
case REG:
mark_used_reg (pbi, x, cond, insn);
return;
case SET:
{
rtx testreg = SET_DEST (x);
int mark_dest = 0;
if (GET_CODE (testreg) == MEM)
{
#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 (GET_CODE (testreg) == SUBREG
&& GET_CODE (SUBREG_REG (testreg)) == REG
&& REGNO (SUBREG_REG (testreg)) >= FIRST_PSEUDO_REGISTER)
bitmap_set_bit (&subregs_of_mode, REGNO (SUBREG_REG (testreg))
* MAX_MACHINE_MODE
+ GET_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)
|| (GET_CODE (testreg) == REG
&& (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:
if (cond != NULL_RTX)
abort ();
mark_used_regs (pbi, COND_EXEC_TEST (x), NULL_RTX, insn);
cond = COND_EXEC_TEST (x);
x = COND_EXEC_CODE (x);
goto retry;
case PHI:
return;
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 (pbi, insn)
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 (insn, reg, amount)
rtx insn, 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 (GET_CODE (insn) == JUMP_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 (x, reg, plusconst)
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 (r, outf)
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 (r)
regset r;
{
dump_regset (r, stderr);
putc ('\n', stderr);
}
void
recompute_reg_usage (f, loop_step)
rtx f ATTRIBUTE_UNUSED;
int loop_step ATTRIBUTE_UNUSED;
{
allocate_reg_life_data ();
update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO);
}
int
count_or_remove_death_notes (blocks, kill)
sbitmap blocks;
int kill;
{
int count = 0;
basic_block bb;
FOR_EACH_BB_REVERSE (bb)
{
rtx insn;
if (blocks && ! TEST_BIT (blocks, bb->index))
continue;
for (insn = bb->head;; 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 (GET_CODE (XEXP (link, 0)) == REG)
{
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)
break;
}
}
return count;
}
static void
clear_log_links (blocks)
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; insn != NEXT_INSN (bb->end);
insn = NEXT_INSN (insn))
if (INSN_P (insn))
free_INSN_LIST_list (&LOG_LINKS (insn));
});
}
void
reg_set_to_hard_reg_set (to, from)
HARD_REG_SET *to;
bitmap from;
{
int i;
EXECUTE_IF_SET_IN_BITMAP
(from, 0, i,
{
if (i >= FIRST_PSEUDO_REGISTER)
return;
SET_HARD_REG_BIT (*to, i);
});
}