#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "regs.h"
#include "output.h"
#include "insn-config.h"
#include "flags.h"
#include "recog.h"
#include "tm_p.h"
#include "target.h"
#include "emit-rtl.h"
#include "reload.h"
static void merge_memattrs (rtx, rtx);
static bool set_dest_equiv_p (rtx x, rtx y, struct equiv_info *info);
static bool set_dest_addr_equiv_p (rtx x, rtx y, struct equiv_info *info);
static void find_dying_inputs (struct equiv_info *info);
static bool resolve_input_conflict (struct equiv_info *info);
#define IMPOSSIBLE_MOVE_FACTOR 20000
void
merge_memattrs (rtx x, rtx y)
{
int i;
int j;
enum rtx_code code;
const char *fmt;
if (x == y)
return;
if (x == 0 || y == 0)
return;
code = GET_CODE (x);
if (code != GET_CODE (y))
return;
if (GET_MODE (x) != GET_MODE (y))
return;
if (code == MEM && MEM_ATTRS (x) != MEM_ATTRS (y))
{
if (! MEM_ATTRS (x))
MEM_ATTRS (y) = 0;
else if (! MEM_ATTRS (y))
MEM_ATTRS (x) = 0;
else
{
rtx mem_size;
if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y))
{
set_mem_alias_set (x, 0);
set_mem_alias_set (y, 0);
}
if (! mem_expr_equal_p (MEM_EXPR (x), MEM_EXPR (y)))
{
set_mem_expr (x, 0);
set_mem_expr (y, 0);
set_mem_offset (x, 0);
set_mem_offset (y, 0);
}
else if (MEM_OFFSET (x) != MEM_OFFSET (y))
{
set_mem_offset (x, 0);
set_mem_offset (y, 0);
}
if (!MEM_SIZE (x))
mem_size = NULL_RTX;
else if (!MEM_SIZE (y))
mem_size = NULL_RTX;
else
mem_size = GEN_INT (MAX (INTVAL (MEM_SIZE (x)),
INTVAL (MEM_SIZE (y))));
set_mem_size (x, mem_size);
set_mem_size (y, mem_size);
set_mem_align (x, MIN (MEM_ALIGN (x), MEM_ALIGN (y)));
set_mem_align (y, MEM_ALIGN (x));
}
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
switch (fmt[i])
{
case 'E':
if (XVECLEN (x, i) != XVECLEN (y, i))
return;
for (j = 0; j < XVECLEN (x, i); j++)
merge_memattrs (XVECEXP (x, i, j), XVECEXP (y, i, j));
break;
case 'e':
merge_memattrs (XEXP (x, i), XEXP (y, i));
}
}
return;
}
static int
assign_reg_reg_set (regset set, rtx reg, int value)
{
unsigned regno = REGNO (reg);
int nregs, i, old;
if (regno >= FIRST_PSEUDO_REGISTER)
{
gcc_assert (!reload_completed);
nregs = 1;
}
else
nregs = hard_regno_nregs[regno][GET_MODE (reg)];
for (old = 0, i = nregs; --i >= 0; regno++)
{
if ((value != 0) == REGNO_REG_SET_P (set, regno))
continue;
if (value)
old++, SET_REGNO_REG_SET (set, regno);
else
old--, CLEAR_REGNO_REG_SET (set, regno);
}
return old;
}
static inline void
struct_equiv_make_checkpoint (struct struct_equiv_checkpoint *p,
struct equiv_info *info)
{
*p = info->cur;
}
static void
struct_equiv_improve_checkpoint (struct struct_equiv_checkpoint *p,
struct equiv_info *info)
{
#ifdef HAVE_cc0
if (reg_mentioned_p (cc0_rtx, info->cur.x_start)
&& !sets_cc0_p (info->cur.x_start))
return;
#endif
if (info->cur.input_count >= IMPOSSIBLE_MOVE_FACTOR)
return;
if (info->input_cost >= 0
? (COSTS_N_INSNS(info->cur.ninsns - p->ninsns)
> info->input_cost * (info->cur.input_count - p->input_count))
: info->cur.ninsns > p->ninsns && !info->cur.input_count)
{
if (info->check_input_conflict && ! resolve_input_conflict (info))
return;
if (info->mode & STRUCT_EQUIV_FINAL)
confirm_change_group ();
else
cancel_changes (0);
struct_equiv_make_checkpoint (p, info);
}
}
static void
struct_equiv_restore_checkpoint (struct struct_equiv_checkpoint *p,
struct equiv_info *info)
{
info->cur.ninsns = p->ninsns;
info->cur.x_start = p->x_start;
info->cur.y_start = p->y_start;
info->cur.input_count = p->input_count;
info->cur.input_valid = p->input_valid;
while (info->cur.local_count > p->local_count)
{
info->cur.local_count--;
info->cur.version--;
if (REGNO_REG_SET_P (info->x_local_live,
REGNO (info->x_local[info->cur.local_count])))
{
assign_reg_reg_set (info->x_local_live,
info->x_local[info->cur.local_count], 0);
assign_reg_reg_set (info->y_local_live,
info->y_local[info->cur.local_count], 0);
info->cur.version--;
}
}
if (info->cur.version != p->version)
info->need_rerun = true;
}
static int
note_local_live (struct equiv_info *info, rtx x, rtx y, int rvalue)
{
unsigned x_regno = REGNO (x);
unsigned y_regno = REGNO (y);
int x_nominal_nregs = (x_regno >= FIRST_PSEUDO_REGISTER
? 1 : hard_regno_nregs[x_regno][GET_MODE (x)]);
int y_nominal_nregs = (y_regno >= FIRST_PSEUDO_REGISTER
? 1 : hard_regno_nregs[y_regno][GET_MODE (y)]);
int x_change = assign_reg_reg_set (info->x_local_live, x, rvalue);
int y_change = assign_reg_reg_set (info->y_local_live, y, rvalue);
gcc_assert (x_nominal_nregs && y_nominal_nregs);
gcc_assert (x_change * y_nominal_nregs == y_change * x_nominal_nregs);
if (y_change)
{
if (reload_completed)
{
unsigned x_regno ATTRIBUTE_UNUSED = REGNO (x);
unsigned y_regno = REGNO (y);
enum machine_mode x_mode = GET_MODE (x);
if (secondary_reload_class (0, REGNO_REG_CLASS (y_regno), x_mode, x)
!= NO_REGS
#ifdef SECONDARY_MEMORY_NEEDED
|| SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (y_regno),
REGNO_REG_CLASS (x_regno), x_mode)
#endif
)
y_change *= IMPOSSIBLE_MOVE_FACTOR;
}
info->cur.input_count += y_change;
info->cur.version++;
}
return x_change;
}
bool
rtx_equiv_p (rtx *xp, rtx y, int rvalue, struct equiv_info *info)
{
rtx x = *xp;
enum rtx_code code;
int length;
const char *format;
int i;
if (!y || !x)
return x == y;
code = GET_CODE (y);
if (code != REG && x == y)
return true;
if (GET_CODE (x) != code
|| GET_MODE (x) != GET_MODE (y))
return false;
switch (code)
{
case REG:
{
unsigned x_regno = REGNO (x);
unsigned y_regno = REGNO (y);
int x_common_live, y_common_live;
if (reload_completed
&& (x_regno >= FIRST_PSEUDO_REGISTER
|| y_regno >= FIRST_PSEUDO_REGISTER))
{
gcc_assert (!info->live_update);
return false;
}
#ifdef STACK_REGS
if (info->mode & CLEANUP_POST_REGSTACK
&& (IN_RANGE (x_regno, FIRST_STACK_REG, LAST_STACK_REG)
|| IN_RANGE (y_regno, FIRST_STACK_REG, LAST_STACK_REG)))
return x_regno == y_regno;
#endif
if (REGNO_REG_SET_P (info->x_local_live, x_regno))
{
if (!REGNO_REG_SET_P (info->y_local_live, y_regno))
return false;
}
else if (REGNO_REG_SET_P (info->y_local_live, y_regno))
return false;
else if (x_regno == y_regno)
{
if (!rvalue && info->cur.input_valid
&& (reg_overlap_mentioned_p (x, info->x_input)
|| reg_overlap_mentioned_p (x, info->y_input)))
return false;
if (info->live_update
&& assign_reg_reg_set (info->common_live, x, rvalue))
info->cur.version++;
return true;
}
x_common_live = REGNO_REG_SET_P (info->common_live, x_regno);
y_common_live = REGNO_REG_SET_P (info->common_live, y_regno);
if (x_common_live != y_common_live)
return false;
else if (x_common_live)
{
if (! rvalue || info->input_cost < 0 || no_new_pseudos)
return false;
if (info->live_update && !info->cur.input_valid)
{
info->cur.input_valid = true;
info->x_input = x;
info->y_input = y;
info->cur.input_count += optimize_size ? 2 : 1;
if (info->input_reg
&& GET_MODE (info->input_reg) != GET_MODE (info->x_input))
info->input_reg = NULL_RTX;
if (!info->input_reg)
info->input_reg = gen_reg_rtx (GET_MODE (info->x_input));
}
else if ((info->live_update
? ! info->cur.input_valid : ! info->x_input)
|| ! rtx_equal_p (x, info->x_input)
|| ! rtx_equal_p (y, info->y_input))
return false;
validate_change (info->cur.x_start, xp, info->input_reg, 1);
}
else
{
int x_nregs = (x_regno >= FIRST_PSEUDO_REGISTER
? 1 : hard_regno_nregs[x_regno][GET_MODE (x)]);
int y_nregs = (y_regno >= FIRST_PSEUDO_REGISTER
? 1 : hard_regno_nregs[y_regno][GET_MODE (y)]);
int size = GET_MODE_SIZE (GET_MODE (x));
enum machine_mode x_mode = GET_MODE (x);
unsigned x_regno_i, y_regno_i;
int x_nregs_i, y_nregs_i, size_i;
int local_count = info->cur.local_count;
for (i = local_count - 1; i >= 0; i--)
{
x_regno_i = REGNO (info->x_local[i]);
x_nregs_i = (x_regno_i >= FIRST_PSEUDO_REGISTER
? 1 : hard_regno_nregs[x_regno_i][GET_MODE (x)]);
y_regno_i = REGNO (info->y_local[i]);
y_nregs_i = (y_regno_i >= FIRST_PSEUDO_REGISTER
? 1 : hard_regno_nregs[y_regno_i][GET_MODE (y)]);
size_i = GET_MODE_SIZE (GET_MODE (info->x_local[i]));
if (info->live_update
&& (x_mode != GET_MODE (info->x_local[i])
? size >= size_i : size > size_i))
{
if ((x_regno <= x_regno_i
&& x_regno + x_nregs >= x_regno_i + x_nregs_i
&& x_nregs == y_nregs && x_nregs_i == y_nregs_i
&& x_regno - x_regno_i == y_regno - y_regno_i)
|| (x_regno == x_regno_i && y_regno == y_regno_i
&& x_nregs >= x_nregs_i && y_nregs >= y_nregs_i))
{
info->cur.local_count = --local_count;
info->x_local[i] = info->x_local[local_count];
info->y_local[i] = info->y_local[local_count];
continue;
}
}
else
{
if (x_regno >= x_regno_i
&& x_regno + x_nregs <= x_regno_i + x_nregs_i
&& x_nregs == y_nregs && x_nregs_i == y_nregs_i
&& x_regno - x_regno_i == y_regno - y_regno_i)
break;
if (x_regno == x_regno_i && y_regno == y_regno_i
&& x_nregs <= x_nregs_i && y_nregs <= y_nregs_i)
break;
}
if (x_regno + x_nregs > x_regno_i
&& x_regno_i + x_nregs_i > x_regno)
return false;
if (y_regno + y_nregs > y_regno_i
&& y_regno_i + y_nregs_i > y_regno)
return false;
}
if (i < 0)
{
if (!info->live_update
|| info->cur.local_count >= STRUCT_EQUIV_MAX_LOCAL)
return false;
info->x_local[info->cur.local_count] = x;
info->y_local[info->cur.local_count] = y;
info->cur.local_count++;
info->cur.version++;
}
note_local_live (info, x, y, rvalue);
}
return true;
}
case SET:
gcc_assert (rvalue < 0);
if(!set_dest_addr_equiv_p (SET_DEST (x), SET_DEST (y), info))
return false;
return rtx_equiv_p (&SET_SRC (x), SET_SRC (y), 1, info);
case PRE_MODIFY:
if (!rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 0, info))
return false;
return rtx_equiv_p (&XEXP (x, 1), XEXP (y, 1), 1, info);
case POST_MODIFY:
{
rtx x_dest0, x_dest1;
x_dest0 = XEXP (x, 0);
gcc_assert (REG_P (x_dest0));
if (!rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 0, info))
return false;
x_dest1 = XEXP (x, 0);
XEXP (x, 0) = x_dest0;
if (!rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 1, info))
return false;
gcc_assert (x_dest1 == XEXP (x, 0));
return rtx_equiv_p (&XEXP (x, 1), XEXP (y, 1), 1, info);
}
case CLOBBER:
gcc_assert (rvalue < 0);
return true;
case STRICT_LOW_PART:
case ZERO_EXTEND:
case SIGN_EXTEND:
{
rtx x_inner, y_inner;
enum rtx_code code;
int change;
if (rvalue)
break;
x_inner = XEXP (x, 0);
y_inner = XEXP (y, 0);
if (GET_MODE (x_inner) != GET_MODE (y_inner))
return false;
code = GET_CODE (x_inner);
if (code != GET_CODE (y_inner))
return false;
if (code == SUBREG)
{
if (SUBREG_BYTE (x_inner) != SUBREG_BYTE (y_inner))
return false;
x = x_inner;
x_inner = SUBREG_REG (x_inner);
y_inner = SUBREG_REG (y_inner);
if (GET_MODE (x_inner) != GET_MODE (y_inner))
return false;
code = GET_CODE (x_inner);
if (code != GET_CODE (y_inner))
return false;
}
if (code == MEM)
return true;
gcc_assert (code == REG);
if (! rtx_equiv_p (&XEXP (x, 0), y_inner, rvalue, info))
return false;
if (REGNO (x_inner) == REGNO (y_inner))
{
change = assign_reg_reg_set (info->common_live, x_inner, 1);
info->cur.version++;
}
else
change = note_local_live (info, x_inner, y_inner, 1);
gcc_assert (change);
return true;
}
case MEM:
return !rvalue || rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), rvalue, info);
case POST_INC: case POST_DEC: case PRE_INC: case PRE_DEC:
return (rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 0, info)
&& rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 1, info));
case PARALLEL:
gcc_assert (rvalue < 0 || GET_MODE (x) != VOIDmode);
break;
case LABEL_REF:
if (XEXP (y, 0) == info->y_label)
return (XEXP (x, 0) == info->x_label);
if (LABEL_REF_NONLOCAL_P (x) || LABEL_REF_NONLOCAL_P (y))
return XEXP (x, 0) == XEXP (y, 0);
return (next_real_insn (XEXP (x, 0))
== next_real_insn (XEXP (y, 0)));
case SYMBOL_REF:
return XSTR (x, 0) == XSTR (y, 0);
case CONST_INT:
case CODE_LABEL:
return false;
default:
break;
}
if (targetm.commutative_p (x, UNKNOWN))
return ((rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), rvalue, info)
&& rtx_equiv_p (&XEXP (x, 1), XEXP (y, 1), rvalue, info))
|| (rtx_equiv_p (&XEXP (x, 0), XEXP (y, 1), rvalue, info)
&& rtx_equiv_p (&XEXP (x, 1), XEXP (y, 0), rvalue, info)));
length = GET_RTX_LENGTH (code);
format = GET_RTX_FORMAT (code);
for (i = 0; i < length; ++i)
{
switch (format[i])
{
case 'w':
if (XWINT (x, i) != XWINT (y, i))
return false;
break;
case 'n':
case 'i':
if (XINT (x, i) != XINT (y, i))
return false;
break;
case 'V':
case 'E':
if (XVECLEN (x, i) != XVECLEN (y, i))
return false;
if (XVEC (x, i) != 0)
{
int j;
for (j = 0; j < XVECLEN (x, i); ++j)
{
if (! rtx_equiv_p (&XVECEXP (x, i, j), XVECEXP (y, i, j),
rvalue, info))
return false;
}
}
break;
case 'e':
if (! rtx_equiv_p (&XEXP (x, i), XEXP (y, i), rvalue, info))
return false;
break;
case 'S':
case 's':
if ((XSTR (x, i) || XSTR (y, i))
&& (! XSTR (x, i) || ! XSTR (y, i)
|| strcmp (XSTR (x, i), XSTR (y, i))))
return false;
break;
case 'u':
break;
case '0':
case 't':
break;
default:
gcc_unreachable ();
}
}
return true;
}
static bool
set_dest_equiv_p (rtx x, rtx y, struct equiv_info *info)
{
if (!x || !y)
return x == y;
if (GET_CODE (x) != GET_CODE (y))
return false;
else if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)
return rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 0, info);
else if (GET_CODE (x) == PARALLEL)
{
int j;
if (XVECLEN (x, 0) != XVECLEN (y, 0))
return false;
for (j = 0; j < XVECLEN (x, 0); ++j)
{
rtx xe = XVECEXP (x, 0, j);
rtx ye = XVECEXP (y, 0, j);
if (GET_CODE (xe) != GET_CODE (ye))
return false;
if ((GET_CODE (xe) == SET || GET_CODE (xe) == CLOBBER)
&& ! rtx_equiv_p (&XEXP (xe, 0), XEXP (ye, 0), 0, info))
return false;
}
}
return true;
}
static bool
set_dest_addr_equiv_p (rtx x, rtx y, struct equiv_info *info)
{
enum rtx_code code = GET_CODE (x);
int length;
const char *format;
int i;
if (code != GET_CODE (y))
return false;
if (code == MEM)
return rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 1, info);
length = GET_RTX_LENGTH (code);
format = GET_RTX_FORMAT (code);
for (i = 0; i < length; ++i)
{
switch (format[i])
{
case 'V':
case 'E':
if (XVECLEN (x, i) != XVECLEN (y, i))
return false;
if (XVEC (x, i) != 0)
{
int j;
for (j = 0; j < XVECLEN (x, i); ++j)
{
if (! set_dest_addr_equiv_p (XVECEXP (x, i, j),
XVECEXP (y, i, j), info))
return false;
}
}
break;
case 'e':
if (! set_dest_addr_equiv_p (XEXP (x, i), XEXP (y, i), info))
return false;
break;
default:
break;
}
}
return true;
}
static bool
death_notes_match_p (rtx i1 ATTRIBUTE_UNUSED, rtx i2 ATTRIBUTE_UNUSED,
struct equiv_info *info ATTRIBUTE_UNUSED)
{
#ifdef STACK_REGS
if ((info->mode & CLEANUP_POST_REGSTACK) && stack_regs_mentioned (i1))
{
rtx note;
HARD_REG_SET i1_regset, i2_regset;
CLEAR_HARD_REG_SET (i1_regset);
CLEAR_HARD_REG_SET (i2_regset);
for (note = REG_NOTES (i1); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD && STACK_REG_P (XEXP (note, 0)))
SET_HARD_REG_BIT (i1_regset, REGNO (XEXP (note, 0)));
for (note = REG_NOTES (i2); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD && STACK_REG_P (XEXP (note, 0)))
{
unsigned regno = REGNO (XEXP (note, 0));
int i;
for (i = info->cur.local_count - 1; i >= 0; i--)
if (regno == REGNO (info->y_local[i]))
{
regno = REGNO (info->x_local[i]);
break;
}
SET_HARD_REG_BIT (i2_regset, regno);
}
GO_IF_HARD_REG_EQUAL (i1_regset, i2_regset, done);
return false;
done:
;
}
#endif
return true;
}
bool
insns_match_p (rtx i1, rtx i2, struct equiv_info *info)
{
int rvalue_change_start;
struct struct_equiv_checkpoint before_rvalue_change;
if (GET_CODE (i1) != GET_CODE (i2))
return false;
info->cur.x_start = i1;
info->cur.y_start = i2;
if (CALL_P (i1))
{
if (SIBLING_CALL_P (i1) != SIBLING_CALL_P (i2)
|| ! set_dest_equiv_p (PATTERN (i1), PATTERN (i2), info)
|| ! set_dest_equiv_p (CALL_INSN_FUNCTION_USAGE (i1),
CALL_INSN_FUNCTION_USAGE (i2), info)
|| ! rtx_equiv_p (&CALL_INSN_FUNCTION_USAGE (i1),
CALL_INSN_FUNCTION_USAGE (i2), -1, info))
{
cancel_changes (0);
return false;
}
}
else if (INSN_P (i1))
{
if (! set_dest_equiv_p (PATTERN (i1), PATTERN (i2), info))
{
cancel_changes (0);
return false;
}
}
rvalue_change_start = num_validated_changes ();
struct_equiv_make_checkpoint (&before_rvalue_change, info);
if (! INSN_P (i1)
|| (!bitmap_bit_p (info->equiv_used, info->cur.ninsns)
&& rtx_equiv_p (&PATTERN (i1), PATTERN (i2), -1, info)
&& death_notes_match_p (i1, i2, info)
&& verify_changes (0)))
return true;
if (!reload_completed)
{
rtx equiv1, equiv2;
cancel_changes (rvalue_change_start);
struct_equiv_restore_checkpoint (&before_rvalue_change, info);
equiv1 = find_reg_equal_equiv_note (i1);
equiv2 = find_reg_equal_equiv_note (i2);
if (equiv1 && equiv2
&& (! reload_completed
|| (CONSTANT_P (XEXP (equiv1, 0))
&& rtx_equal_p (XEXP (equiv1, 0), XEXP (equiv2, 0)))))
{
rtx s1 = single_set (i1);
rtx s2 = single_set (i2);
if (s1 != 0 && s2 != 0)
{
validate_change (i1, &SET_SRC (s1), XEXP (equiv1, 0), 1);
validate_change (i2, &SET_SRC (s2), XEXP (equiv2, 0), 1);
if (rtx_equiv_p (&PATTERN (i1), PATTERN (i2), -1, info)
&& death_notes_match_p (i1, i2, info)
&& verify_changes (0))
{
bitmap_set_bit (info->equiv_used, info->cur.ninsns);
return true;
}
}
}
}
cancel_changes (0);
return false;
}
bool
struct_equiv_init (int mode, struct equiv_info *info)
{
if ((info->x_block->flags | info->y_block->flags) & BB_DIRTY)
update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
(PROP_DEATH_NOTES
| ((mode & CLEANUP_POST_REGSTACK)
? PROP_POST_REGSTACK : 0)));
if (!REG_SET_EQUAL_P (info->x_block->il.rtl->global_live_at_end,
info->y_block->il.rtl->global_live_at_end))
{
#ifdef STACK_REGS
unsigned rn;
if (!(mode & CLEANUP_POST_REGSTACK))
return false;
for (rn = FIRST_STACK_REG; rn <= LAST_STACK_REG; rn++)
{
CLEAR_REGNO_REG_SET (info->x_block->il.rtl->global_live_at_end, rn);
CLEAR_REGNO_REG_SET (info->y_block->il.rtl->global_live_at_end, rn);
}
if (!REG_SET_EQUAL_P (info->x_block->il.rtl->global_live_at_end,
info->y_block->il.rtl->global_live_at_end))
#endif
return false;
}
info->mode = mode;
if (mode & STRUCT_EQUIV_START)
{
info->x_input = info->y_input = info->input_reg = NULL_RTX;
info->equiv_used = ALLOC_REG_SET (®_obstack);
info->check_input_conflict = false;
}
info->had_input_conflict = false;
info->cur.ninsns = info->cur.version = 0;
info->cur.local_count = info->cur.input_count = 0;
info->cur.x_start = info->cur.y_start = NULL_RTX;
info->x_label = info->y_label = NULL_RTX;
info->need_rerun = false;
info->live_update = true;
info->cur.input_valid = false;
info->common_live = ALLOC_REG_SET (®_obstack);
info->x_local_live = ALLOC_REG_SET (®_obstack);
info->y_local_live = ALLOC_REG_SET (®_obstack);
COPY_REG_SET (info->common_live, info->x_block->il.rtl->global_live_at_end);
struct_equiv_make_checkpoint (&info->best_match, info);
return true;
}
static void
struct_equiv_merge (rtx xi, rtx yi, struct equiv_info *info)
{
rtx equiv1, equiv2;
merge_memattrs (xi, yi);
info->live_update = false;
equiv1 = find_reg_equal_equiv_note (xi);
equiv2 = find_reg_equal_equiv_note (yi);
if (equiv1 && !equiv2)
remove_note (xi, equiv1);
else if (!equiv1 && equiv2)
remove_note (yi, equiv2);
else if (equiv1 && equiv2
&& !rtx_equiv_p (&XEXP (equiv1, 0), XEXP (equiv2, 0),
1, info))
{
remove_note (xi, equiv1);
remove_note (yi, equiv2);
}
info->live_update = true;
}
int
struct_equiv_block_eq (int mode, struct equiv_info *info)
{
rtx x_stop, y_stop;
rtx xi, yi;
int i;
if (mode & STRUCT_EQUIV_START)
{
x_stop = BB_HEAD (info->x_block);
y_stop = BB_HEAD (info->y_block);
if (!x_stop || !y_stop)
return 0;
}
else
{
x_stop = info->cur.x_start;
y_stop = info->cur.y_start;
}
if (!struct_equiv_init (mode, info))
gcc_unreachable ();
xi = BB_END (info->x_block);
if (onlyjump_p (xi)
|| (returnjump_p (xi) && !side_effects_p (PATTERN (xi))))
{
info->cur.x_start = xi;
xi = PREV_INSN (xi);
}
yi = BB_END (info->y_block);
if (onlyjump_p (yi)
|| (returnjump_p (yi) && !side_effects_p (PATTERN (yi))))
{
info->cur.y_start = yi;
if (!simplejump_p (yi) && !returnjump_p (yi) && info->cur.x_start)
info->cur.ninsns++;
yi = PREV_INSN (yi);
}
if (mode & STRUCT_EQUIV_MATCH_JUMPS)
{
gcc_assert (!info->cur.x_start == !info->cur.y_start);
if (info->cur.x_start)
{
if (any_condjump_p (info->cur.x_start)
? !condjump_equiv_p (info, false)
: !insns_match_p (info->cur.x_start, info->cur.y_start, info))
gcc_unreachable ();
}
else if (any_condjump_p (xi) && any_condjump_p (yi))
{
info->cur.x_start = xi;
info->cur.y_start = yi;
xi = PREV_INSN (xi);
yi = PREV_INSN (yi);
info->cur.ninsns++;
if (!condjump_equiv_p (info, false))
gcc_unreachable ();
}
if (info->cur.x_start && info->mode & STRUCT_EQUIV_FINAL)
struct_equiv_merge (info->cur.x_start, info->cur.y_start, info);
}
struct_equiv_improve_checkpoint (&info->best_match, info);
info->x_end = xi;
info->y_end = yi;
if (info->cur.x_start != x_stop)
for (;;)
{
while (!INSN_P (xi) && xi != x_stop)
xi = PREV_INSN (xi);
while (!INSN_P (yi) && yi != y_stop)
yi = PREV_INSN (yi);
if (!insns_match_p (xi, yi, info))
break;
if (INSN_P (xi))
{
if (info->mode & STRUCT_EQUIV_FINAL)
struct_equiv_merge (xi, yi, info);
info->cur.ninsns++;
struct_equiv_improve_checkpoint (&info->best_match, info);
}
if (xi == x_stop || yi == y_stop)
{
if (info->best_match.x_start != info->cur.x_start
&& (xi == BB_HEAD (info->x_block)
|| yi == BB_HEAD (info->y_block)))
{
info->cur.ninsns++;
struct_equiv_improve_checkpoint (&info->best_match, info);
info->cur.ninsns--;
if (info->best_match.ninsns > info->cur.ninsns)
info->best_match.ninsns = info->cur.ninsns;
}
break;
}
xi = PREV_INSN (xi);
yi = PREV_INSN (yi);
}
cancel_changes (0);
struct_equiv_restore_checkpoint (&info->best_match, info);
if (info->cur.ninsns)
{
xi = info->cur.x_start;
yi = info->cur.y_start;
while (xi != x_stop && !INSN_P (PREV_INSN (xi)))
xi = PREV_INSN (xi);
while (yi != y_stop && !INSN_P (PREV_INSN (yi)))
yi = PREV_INSN (yi);
info->cur.x_start = xi;
info->cur.y_start = yi;
}
if (!info->cur.input_valid)
info->x_input = info->y_input = info->input_reg = NULL_RTX;
if (!info->need_rerun)
{
find_dying_inputs (info);
if (info->mode & STRUCT_EQUIV_FINAL)
{
if (info->check_input_conflict && ! resolve_input_conflict (info))
gcc_unreachable ();
}
else
{
bool input_conflict = info->had_input_conflict;
if (!input_conflict
&& info->dying_inputs > 1
&& bitmap_intersect_p (info->x_local_live, info->y_local_live))
{
regset_head clobbered_regs;
INIT_REG_SET (&clobbered_regs);
for (i = 0; i < info->cur.local_count; i++)
{
if (assign_reg_reg_set (&clobbered_regs, info->y_local[i], 0))
{
input_conflict = true;
break;
}
assign_reg_reg_set (&clobbered_regs, info->x_local[i], 1);
}
CLEAR_REG_SET (&clobbered_regs);
}
if (input_conflict && !info->check_input_conflict)
info->need_rerun = true;
info->check_input_conflict = input_conflict;
}
}
if (info->mode & STRUCT_EQUIV_NEED_FULL_BLOCK
&& (info->cur.x_start != x_stop || info->cur.y_start != y_stop))
return 0;
return info->cur.ninsns;
}
static void
find_dying_inputs (struct equiv_info *info)
{
int i;
info->dying_inputs = 0;
for (i = info->cur.local_count-1; i >=0; i--)
{
rtx x = info->x_local[i];
unsigned regno = REGNO (x);
int nregs = (regno >= FIRST_PSEUDO_REGISTER
? 1 : hard_regno_nregs[regno][GET_MODE (x)]);
for (info->local_rvalue[i] = false; nregs > 0; regno++, --nregs)
if (REGNO_REG_SET_P (info->x_local_live, regno))
{
info->dying_inputs++;
info->local_rvalue[i] = true;
break;
}
}
}
static bool
resolve_input_conflict (struct equiv_info *info)
{
int i, j, end;
int nswaps = 0;
rtx save_x_local[STRUCT_EQUIV_MAX_LOCAL];
rtx save_y_local[STRUCT_EQUIV_MAX_LOCAL];
find_dying_inputs (info);
if (info->dying_inputs <= 1)
return true;
memcpy (save_x_local, info->x_local, sizeof save_x_local);
memcpy (save_y_local, info->y_local, sizeof save_y_local);
end = info->cur.local_count - 1;
for (i = 0; i <= end; i++)
{
int max_swaps = end - i;
if (!info->local_rvalue[i])
continue;
for (j = i + 1; j <= end; j++)
{
rtx tmp;
if (!info->local_rvalue[j])
continue;
if (!reg_overlap_mentioned_p (info->x_local[i], info->y_local[j]))
continue;
if (--max_swaps < 0)
{
memcpy (info->x_local, save_x_local, sizeof save_x_local);
memcpy (info->y_local, save_y_local, sizeof save_y_local);
return false;
}
nswaps++;
tmp = info->x_local[i];
info->x_local[i] = info->x_local[j];
info->x_local[j] = tmp;
tmp = info->y_local[i];
info->y_local[i] = info->y_local[j];
info->y_local[j] = tmp;
j = i;
}
}
info->had_input_conflict = true;
if (dump_file && nswaps)
fprintf (dump_file, "Resolved input conflict, %d %s.\n",
nswaps, nswaps == 1 ? "swap" : "swaps");
return true;
}