#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "machmode.h"
#include "real.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "except.h"
#include "function.h"
#include "insn-config.h"
#include "insn-attr.h"
#include "expr.h"
#include "optabs.h"
#include "libfuncs.h"
#include "recog.h"
#include "reload.h"
#include "output.h"
#include "typeclass.h"
#include "toplev.h"
#include "ggc.h"
#include "langhooks.h"
#include "intl.h"
#include "tm_p.h"
#include "tree-iterator.h"
#include "tree-pass.h"
#include "tree-flow.h"
#include "target.h"
#include "timevar.h"
#ifdef PUSH_ROUNDING
#ifndef PUSH_ARGS_REVERSED
#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
#define PUSH_ARGS_REVERSED
#endif
#endif
#endif
#ifndef STACK_PUSH_CODE
#ifdef STACK_GROWS_DOWNWARD
#define STACK_PUSH_CODE PRE_DEC
#else
#define STACK_PUSH_CODE PRE_INC
#endif
#endif
int cse_not_expected;
struct move_by_pieces
{
rtx to;
rtx to_addr;
int autinc_to;
int explicit_inc_to;
rtx from;
rtx from_addr;
int autinc_from;
int explicit_inc_from;
unsigned HOST_WIDE_INT len;
HOST_WIDE_INT offset;
int reverse;
};
struct store_by_pieces
{
rtx to;
rtx to_addr;
int autinc_to;
int explicit_inc_to;
unsigned HOST_WIDE_INT len;
HOST_WIDE_INT offset;
rtx (*constfun) (void *, HOST_WIDE_INT, enum machine_mode);
void *constfundata;
int reverse;
};
static unsigned HOST_WIDE_INT move_by_pieces_ninsns (unsigned HOST_WIDE_INT,
unsigned int,
unsigned int);
static void move_by_pieces_1 (rtx (*) (rtx, ...), enum machine_mode,
struct move_by_pieces *);
static bool block_move_libcall_safe_for_call_parm (void);
static bool emit_block_move_via_movmem (rtx, rtx, rtx, unsigned);
static rtx emit_block_move_via_libcall (rtx, rtx, rtx, bool);
static tree emit_block_move_libcall_fn (int);
static void emit_block_move_via_loop (rtx, rtx, rtx, unsigned);
static rtx clear_by_pieces_1 (void *, HOST_WIDE_INT, enum machine_mode);
static void clear_by_pieces (rtx, unsigned HOST_WIDE_INT, unsigned int);
static void store_by_pieces_1 (struct store_by_pieces *, unsigned int);
static void store_by_pieces_2 (rtx (*) (rtx, ...), enum machine_mode,
struct store_by_pieces *);
static rtx clear_storage_via_libcall (rtx, rtx, bool);
static tree clear_storage_libcall_fn (int);
static rtx compress_float_constant (rtx, rtx);
static rtx get_subtarget (rtx);
static void store_constructor_field (rtx, unsigned HOST_WIDE_INT,
HOST_WIDE_INT, enum machine_mode,
tree, tree, int, int);
static void store_constructor (tree, rtx, int, HOST_WIDE_INT);
static rtx store_field (rtx, HOST_WIDE_INT, HOST_WIDE_INT, enum machine_mode,
tree, tree, int);
static unsigned HOST_WIDE_INT highest_pow2_factor_for_target (tree, tree);
static int is_aligning_offset (tree, tree);
static void expand_operands (tree, tree, rtx, rtx*, rtx*,
enum expand_modifier);
static rtx reduce_to_bit_field_precision (rtx, rtx, tree);
static rtx do_store_flag (tree, rtx, enum machine_mode, int);
#ifdef PUSH_ROUNDING
static void emit_single_push_insn (enum machine_mode, rtx, tree, rtx);
#endif
static void do_tablejump (rtx, enum machine_mode, rtx, rtx, rtx);
static rtx const_vector_from_tree (tree);
static void write_complex_part (rtx, rtx, bool);
static rtx look_for_bytemanip (tree, rtx);
static char direct_load[NUM_MACHINE_MODES];
static char direct_store[NUM_MACHINE_MODES];
static bool float_extend_from_mem[NUM_MACHINE_MODES][NUM_MACHINE_MODES];
#ifdef ENABLE_LLVM
#undef MOVE_BY_PIECES_P
#define MOVE_BY_PIECES_P(SIZE, ALIGN) (0*(SIZE)*(ALIGN))
#endif
#ifndef MOVE_BY_PIECES_P
#define MOVE_BY_PIECES_P(SIZE, ALIGN) \
(move_by_pieces_ninsns (SIZE, ALIGN, MOVE_MAX_PIECES + 1) \
< (unsigned int) MOVE_RATIO)
#endif
#ifndef CLEAR_BY_PIECES_P
#define CLEAR_BY_PIECES_P(SIZE, ALIGN) \
(move_by_pieces_ninsns (SIZE, ALIGN, STORE_MAX_PIECES + 1) \
< (unsigned int) CLEAR_RATIO)
#endif
#ifndef STORE_BY_PIECES_P
#define STORE_BY_PIECES_P(SIZE, ALIGN) \
(move_by_pieces_ninsns (SIZE, ALIGN, STORE_MAX_PIECES + 1) \
< (unsigned int) MOVE_RATIO)
#endif
enum insn_code movmem_optab[NUM_MACHINE_MODES];
enum insn_code setmem_optab[NUM_MACHINE_MODES];
enum insn_code cmpstr_optab[NUM_MACHINE_MODES];
enum insn_code cmpstrn_optab[NUM_MACHINE_MODES];
enum insn_code cmpmem_optab[NUM_MACHINE_MODES];
enum insn_code sync_add_optab[NUM_MACHINE_MODES];
enum insn_code sync_sub_optab[NUM_MACHINE_MODES];
enum insn_code sync_ior_optab[NUM_MACHINE_MODES];
enum insn_code sync_and_optab[NUM_MACHINE_MODES];
enum insn_code sync_xor_optab[NUM_MACHINE_MODES];
enum insn_code sync_nand_optab[NUM_MACHINE_MODES];
enum insn_code sync_old_add_optab[NUM_MACHINE_MODES];
enum insn_code sync_old_sub_optab[NUM_MACHINE_MODES];
enum insn_code sync_old_ior_optab[NUM_MACHINE_MODES];
enum insn_code sync_old_and_optab[NUM_MACHINE_MODES];
enum insn_code sync_old_xor_optab[NUM_MACHINE_MODES];
enum insn_code sync_old_nand_optab[NUM_MACHINE_MODES];
enum insn_code sync_new_add_optab[NUM_MACHINE_MODES];
enum insn_code sync_new_sub_optab[NUM_MACHINE_MODES];
enum insn_code sync_new_ior_optab[NUM_MACHINE_MODES];
enum insn_code sync_new_and_optab[NUM_MACHINE_MODES];
enum insn_code sync_new_xor_optab[NUM_MACHINE_MODES];
enum insn_code sync_new_nand_optab[NUM_MACHINE_MODES];
enum insn_code sync_compare_and_swap[NUM_MACHINE_MODES];
enum insn_code sync_compare_and_swap_cc[NUM_MACHINE_MODES];
enum insn_code sync_lock_test_and_set[NUM_MACHINE_MODES];
enum insn_code sync_lock_release[NUM_MACHINE_MODES];
#ifndef SLOW_UNALIGNED_ACCESS
#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) STRICT_ALIGNMENT
#endif
void
init_expr_once (void)
{
rtx insn, pat;
enum machine_mode mode;
int num_clobbers;
rtx mem, mem1;
rtx reg;
mem = gen_rtx_MEM (VOIDmode, stack_pointer_rtx);
mem1 = gen_rtx_MEM (VOIDmode, frame_pointer_rtx);
reg = gen_rtx_REG (VOIDmode, -1);
insn = rtx_alloc (INSN);
pat = gen_rtx_SET (0, NULL_RTX, NULL_RTX);
PATTERN (insn) = pat;
for (mode = VOIDmode; (int) mode < NUM_MACHINE_MODES;
mode = (enum machine_mode) ((int) mode + 1))
{
int regno;
direct_load[(int) mode] = direct_store[(int) mode] = 0;
PUT_MODE (mem, mode);
PUT_MODE (mem1, mode);
PUT_MODE (reg, mode);
if (mode != VOIDmode && mode != BLKmode)
for (regno = 0; regno < FIRST_PSEUDO_REGISTER
&& (direct_load[(int) mode] == 0 || direct_store[(int) mode] == 0);
regno++)
{
if (! HARD_REGNO_MODE_OK (regno, mode))
continue;
REGNO (reg) = regno;
SET_SRC (pat) = mem;
SET_DEST (pat) = reg;
if (recog (pat, insn, &num_clobbers) >= 0)
direct_load[(int) mode] = 1;
SET_SRC (pat) = mem1;
SET_DEST (pat) = reg;
if (recog (pat, insn, &num_clobbers) >= 0)
direct_load[(int) mode] = 1;
SET_SRC (pat) = reg;
SET_DEST (pat) = mem;
if (recog (pat, insn, &num_clobbers) >= 0)
direct_store[(int) mode] = 1;
SET_SRC (pat) = reg;
SET_DEST (pat) = mem1;
if (recog (pat, insn, &num_clobbers) >= 0)
direct_store[(int) mode] = 1;
}
}
mem = gen_rtx_MEM (VOIDmode, gen_rtx_raw_REG (Pmode, 10000));
for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
{
enum machine_mode srcmode;
for (srcmode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); srcmode != mode;
srcmode = GET_MODE_WIDER_MODE (srcmode))
{
enum insn_code ic;
ic = can_extend_p (mode, srcmode, 0);
if (ic == CODE_FOR_nothing)
continue;
PUT_MODE (mem, srcmode);
if ((*insn_data[ic].operand[1].predicate) (mem, srcmode))
float_extend_from_mem[mode][srcmode] = true;
}
}
}
void
init_expr (void)
{
cfun->expr = ggc_alloc_cleared (sizeof (struct expr_status));
}
void
convert_move (rtx to, rtx from, int unsignedp)
{
enum machine_mode to_mode = GET_MODE (to);
enum machine_mode from_mode = GET_MODE (from);
int to_real = SCALAR_FLOAT_MODE_P (to_mode);
int from_real = SCALAR_FLOAT_MODE_P (from_mode);
enum insn_code code;
rtx libcall;
enum rtx_code equiv_code = (unsignedp < 0 ? UNKNOWN
: (unsignedp ? ZERO_EXTEND : SIGN_EXTEND));
gcc_assert (to_real == from_real);
gcc_assert (to_mode != BLKmode);
gcc_assert (from_mode != BLKmode);
if (to == from)
return;
if (GET_CODE (from) == SUBREG && SUBREG_PROMOTED_VAR_P (from)
&& (GET_MODE_SIZE (GET_MODE (SUBREG_REG (from)))
>= GET_MODE_SIZE (to_mode))
&& SUBREG_PROMOTED_UNSIGNED_P (from) == unsignedp)
from = gen_lowpart (to_mode, from), from_mode = to_mode;
gcc_assert (GET_CODE (to) != SUBREG || !SUBREG_PROMOTED_VAR_P (to));
if (to_mode == from_mode
|| (from_mode == VOIDmode && CONSTANT_P (from)))
{
emit_move_insn (to, from);
return;
}
if (VECTOR_MODE_P (to_mode) || VECTOR_MODE_P (from_mode))
{
gcc_assert (GET_MODE_BITSIZE (from_mode) == GET_MODE_BITSIZE (to_mode));
if (VECTOR_MODE_P (to_mode))
from = simplify_gen_subreg (to_mode, from, GET_MODE (from), 0);
else
to = simplify_gen_subreg (from_mode, to, GET_MODE (to), 0);
emit_move_insn (to, from);
return;
}
if (GET_CODE (to) == CONCAT && GET_CODE (from) == CONCAT)
{
convert_move (XEXP (to, 0), XEXP (from, 0), unsignedp);
convert_move (XEXP (to, 1), XEXP (from, 1), unsignedp);
return;
}
if (to_real)
{
rtx value, insns;
convert_optab tab;
gcc_assert ((GET_MODE_PRECISION (from_mode)
!= GET_MODE_PRECISION (to_mode))
|| (DECIMAL_FLOAT_MODE_P (from_mode)
!= DECIMAL_FLOAT_MODE_P (to_mode)));
if (GET_MODE_PRECISION (from_mode) == GET_MODE_PRECISION (to_mode))
tab = DECIMAL_FLOAT_MODE_P (from_mode) ? trunc_optab : sext_optab;
else if (GET_MODE_PRECISION (from_mode) < GET_MODE_PRECISION (to_mode))
tab = sext_optab;
else
tab = trunc_optab;
code = tab->handlers[to_mode][from_mode].insn_code;
if (code != CODE_FOR_nothing)
{
emit_unop_insn (code, to, from,
tab == sext_optab ? FLOAT_EXTEND : FLOAT_TRUNCATE);
return;
}
libcall = tab->handlers[to_mode][from_mode].libfunc;
gcc_assert (libcall);
start_sequence ();
value = emit_library_call_value (libcall, NULL_RTX, LCT_CONST, to_mode,
1, from, from_mode);
insns = get_insns ();
end_sequence ();
emit_libcall_block (insns, to, value,
tab == trunc_optab ? gen_rtx_FLOAT_TRUNCATE (to_mode,
from)
: gen_rtx_FLOAT_EXTEND (to_mode, from));
return;
}
if (GET_MODE_CLASS (to_mode) == MODE_PARTIAL_INT)
{
enum machine_mode full_mode
= smallest_mode_for_size (GET_MODE_BITSIZE (to_mode), MODE_INT);
gcc_assert (trunc_optab->handlers[to_mode][full_mode].insn_code
!= CODE_FOR_nothing);
if (full_mode != from_mode)
from = convert_to_mode (full_mode, from, unsignedp);
emit_unop_insn (trunc_optab->handlers[to_mode][full_mode].insn_code,
to, from, UNKNOWN);
return;
}
if (GET_MODE_CLASS (from_mode) == MODE_PARTIAL_INT)
{
rtx new_from;
enum machine_mode full_mode
= smallest_mode_for_size (GET_MODE_BITSIZE (from_mode), MODE_INT);
gcc_assert (sext_optab->handlers[full_mode][from_mode].insn_code
!= CODE_FOR_nothing);
if (to_mode == full_mode)
{
emit_unop_insn (sext_optab->handlers[full_mode][from_mode].insn_code,
to, from, UNKNOWN);
return;
}
new_from = gen_reg_rtx (full_mode);
emit_unop_insn (sext_optab->handlers[full_mode][from_mode].insn_code,
new_from, from, UNKNOWN);
from_mode = full_mode;
from = new_from;
}
if (GET_MODE_BITSIZE (from_mode) < GET_MODE_BITSIZE (to_mode)
&& GET_MODE_BITSIZE (to_mode) > BITS_PER_WORD)
{
rtx insns;
rtx lowpart;
rtx fill_value;
rtx lowfrom;
int i;
enum machine_mode lowpart_mode;
int nwords = CEIL (GET_MODE_SIZE (to_mode), UNITS_PER_WORD);
if ((code = can_extend_p (to_mode, from_mode, unsignedp))
!= CODE_FOR_nothing)
{
if (optimize > 0 && GET_CODE (from) == SUBREG)
from = force_reg (from_mode, from);
emit_unop_insn (code, to, from, equiv_code);
return;
}
else if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD
&& ((code = can_extend_p (to_mode, word_mode, unsignedp))
!= CODE_FOR_nothing))
{
if (REG_P (to))
{
if (reg_overlap_mentioned_p (to, from))
from = force_reg (from_mode, from);
emit_insn (gen_rtx_CLOBBER (VOIDmode, to));
}
convert_move (gen_lowpart (word_mode, to), from, unsignedp);
emit_unop_insn (code, to,
gen_lowpart (word_mode, to), equiv_code);
return;
}
start_sequence ();
if (reg_overlap_mentioned_p (to, from))
from = force_reg (from_mode, from);
if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD)
lowpart_mode = word_mode;
else
lowpart_mode = from_mode;
lowfrom = convert_to_mode (lowpart_mode, from, unsignedp);
lowpart = gen_lowpart (lowpart_mode, to);
emit_move_insn (lowpart, lowfrom);
if (unsignedp)
fill_value = const0_rtx;
else
{
#ifdef HAVE_slt
if (HAVE_slt
&& insn_data[(int) CODE_FOR_slt].operand[0].mode == word_mode
&& STORE_FLAG_VALUE == -1)
{
emit_cmp_insn (lowfrom, const0_rtx, NE, NULL_RTX,
lowpart_mode, 0);
fill_value = gen_reg_rtx (word_mode);
emit_insn (gen_slt (fill_value));
}
else
#endif
{
fill_value
= expand_shift (RSHIFT_EXPR, lowpart_mode, lowfrom,
size_int (GET_MODE_BITSIZE (lowpart_mode) - 1),
NULL_RTX, 0);
fill_value = convert_to_mode (word_mode, fill_value, 1);
}
}
for (i = GET_MODE_SIZE (lowpart_mode) / UNITS_PER_WORD; i < nwords; i++)
{
int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
rtx subword = operand_subword (to, index, 1, to_mode);
gcc_assert (subword);
if (fill_value != subword)
emit_move_insn (subword, fill_value);
}
insns = get_insns ();
end_sequence ();
emit_no_conflict_block (insns, to, from, NULL_RTX,
gen_rtx_fmt_e (equiv_code, to_mode, copy_rtx (from)));
return;
}
if (GET_MODE_BITSIZE (from_mode) > BITS_PER_WORD
&& GET_MODE_BITSIZE (to_mode) <= BITS_PER_WORD)
{
if (!((MEM_P (from)
&& ! MEM_VOLATILE_P (from)
&& direct_load[(int) to_mode]
&& ! mode_dependent_address_p (XEXP (from, 0)))
|| REG_P (from)
|| GET_CODE (from) == SUBREG))
from = force_reg (from_mode, from);
convert_move (to, gen_lowpart (word_mode, from), 0);
return;
}
if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode)
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (to_mode),
GET_MODE_BITSIZE (from_mode)))
{
if (!((MEM_P (from)
&& ! MEM_VOLATILE_P (from)
&& direct_load[(int) to_mode]
&& ! mode_dependent_address_p (XEXP (from, 0)))
|| REG_P (from)
|| GET_CODE (from) == SUBREG))
from = force_reg (from_mode, from);
if (REG_P (from) && REGNO (from) < FIRST_PSEUDO_REGISTER
&& ! HARD_REGNO_MODE_OK (REGNO (from), to_mode))
from = copy_to_reg (from);
emit_move_insn (to, gen_lowpart (to_mode, from));
return;
}
if (GET_MODE_BITSIZE (to_mode) > GET_MODE_BITSIZE (from_mode))
{
if ((code = can_extend_p (to_mode, from_mode, unsignedp))
!= CODE_FOR_nothing)
{
emit_unop_insn (code, to, from, equiv_code);
return;
}
else
{
enum machine_mode intermediate;
rtx tmp;
tree shift_amount;
for (intermediate = from_mode; intermediate != VOIDmode;
intermediate = GET_MODE_WIDER_MODE (intermediate))
if (((can_extend_p (to_mode, intermediate, unsignedp)
!= CODE_FOR_nothing)
|| (GET_MODE_SIZE (to_mode) < GET_MODE_SIZE (intermediate)
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (to_mode),
GET_MODE_BITSIZE (intermediate))))
&& (can_extend_p (intermediate, from_mode, unsignedp)
!= CODE_FOR_nothing))
{
convert_move (to, convert_to_mode (intermediate, from,
unsignedp), unsignedp);
return;
}
shift_amount = build_int_cst (NULL_TREE,
GET_MODE_BITSIZE (to_mode)
- GET_MODE_BITSIZE (from_mode));
from = gen_lowpart (to_mode, force_reg (from_mode, from));
tmp = expand_shift (LSHIFT_EXPR, to_mode, from, shift_amount,
to, unsignedp);
tmp = expand_shift (RSHIFT_EXPR, to_mode, tmp, shift_amount,
to, unsignedp);
if (tmp != to)
emit_move_insn (to, tmp);
return;
}
}
if (trunc_optab->handlers[to_mode][from_mode].insn_code != CODE_FOR_nothing)
{
emit_unop_insn (trunc_optab->handlers[to_mode][from_mode].insn_code,
to, from, UNKNOWN);
return;
}
if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode))
{
rtx temp = force_reg (to_mode, gen_lowpart (to_mode, from));
emit_move_insn (to, temp);
return;
}
gcc_unreachable ();
}
rtx
convert_to_mode (enum machine_mode mode, rtx x, int unsignedp)
{
return convert_modes (mode, VOIDmode, x, unsignedp);
}
rtx
convert_modes (enum machine_mode mode, enum machine_mode oldmode, rtx x, int unsignedp)
{
rtx temp;
if (GET_CODE (x) == SUBREG && SUBREG_PROMOTED_VAR_P (x)
&& GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) >= GET_MODE_SIZE (mode)
&& SUBREG_PROMOTED_UNSIGNED_P (x) == unsignedp)
x = gen_lowpart (mode, x);
if (GET_MODE (x) != VOIDmode)
oldmode = GET_MODE (x);
if (mode == oldmode)
return x;
if (unsignedp && GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT
&& GET_CODE (x) == CONST_INT && INTVAL (x) < 0)
{
HOST_WIDE_INT val = INTVAL (x);
if (oldmode != VOIDmode
&& HOST_BITS_PER_WIDE_INT > GET_MODE_BITSIZE (oldmode))
{
int width = GET_MODE_BITSIZE (oldmode);
val &= ((HOST_WIDE_INT) 1 << width) - 1;
}
return immed_double_const (val, (HOST_WIDE_INT) 0, mode);
}
if ((GET_CODE (x) == CONST_INT
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
|| (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_CLASS (oldmode) == MODE_INT
&& (GET_CODE (x) == CONST_DOUBLE
|| (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (oldmode)
&& ((MEM_P (x) && ! MEM_VOLATILE_P (x)
&& direct_load[(int) mode])
|| (REG_P (x)
&& (! HARD_REGISTER_P (x)
|| HARD_REGNO_MODE_OK (REGNO (x), mode))
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
GET_MODE_BITSIZE (GET_MODE (x)))))))))
{
if (GET_CODE (x) == CONST_INT && oldmode != VOIDmode
&& GET_MODE_SIZE (mode) > GET_MODE_SIZE (oldmode))
{
HOST_WIDE_INT val = INTVAL (x);
int width = GET_MODE_BITSIZE (oldmode);
val &= ((HOST_WIDE_INT) 1 << width) - 1;
if (! unsignedp
&& (val & ((HOST_WIDE_INT) 1 << (width - 1))))
val |= (HOST_WIDE_INT) (-1) << width;
return gen_int_mode (val, mode);
}
return gen_lowpart (mode, x);
}
if (VECTOR_MODE_P (mode) && GET_MODE (x) == VOIDmode)
{
gcc_assert (GET_MODE_BITSIZE (mode) == GET_MODE_BITSIZE (oldmode));
return simplify_gen_subreg (mode, x, oldmode, 0);
}
temp = gen_reg_rtx (mode);
convert_move (temp, x, unsignedp);
return temp;
}
#define STORE_MAX_PIECES MIN (MOVE_MAX_PIECES, 2 * sizeof (HOST_WIDE_INT))
int
can_move_by_pieces (unsigned HOST_WIDE_INT len,
unsigned int align ATTRIBUTE_UNUSED)
{
return MOVE_BY_PIECES_P (len, align);
}
rtx
move_by_pieces (rtx to, rtx from, unsigned HOST_WIDE_INT len,
unsigned int align, int endp)
{
struct move_by_pieces data;
rtx to_addr, from_addr = XEXP (from, 0);
unsigned int max_size = MOVE_MAX_PIECES + 1;
enum machine_mode mode = VOIDmode, tmode;
enum insn_code icode;
align = MIN (to ? MEM_ALIGN (to) : align, MEM_ALIGN (from));
data.offset = 0;
data.from_addr = from_addr;
if (to)
{
to_addr = XEXP (to, 0);
data.to = to;
data.autinc_to
= (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC
|| GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC);
data.reverse
= (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC);
}
else
{
to_addr = NULL_RTX;
data.to = NULL_RTX;
data.autinc_to = 1;
#ifdef STACK_GROWS_DOWNWARD
data.reverse = 1;
#else
data.reverse = 0;
#endif
}
data.to_addr = to_addr;
data.from = from;
data.autinc_from
= (GET_CODE (from_addr) == PRE_INC || GET_CODE (from_addr) == PRE_DEC
|| GET_CODE (from_addr) == POST_INC
|| GET_CODE (from_addr) == POST_DEC);
data.explicit_inc_from = 0;
data.explicit_inc_to = 0;
if (data.reverse) data.offset = len;
data.len = len;
if (!(data.autinc_from && data.autinc_to)
&& move_by_pieces_ninsns (len, align, max_size) > 2)
{
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) < max_size)
mode = tmode;
if (USE_LOAD_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_from)
{
data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len));
data.autinc_from = 1;
data.explicit_inc_from = -1;
}
if (USE_LOAD_POST_INCREMENT (mode) && ! data.autinc_from)
{
data.from_addr = copy_addr_to_reg (from_addr);
data.autinc_from = 1;
data.explicit_inc_from = 1;
}
if (!data.autinc_from && CONSTANT_P (from_addr))
data.from_addr = copy_addr_to_reg (from_addr);
if (USE_STORE_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_to)
{
data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len));
data.autinc_to = 1;
data.explicit_inc_to = -1;
}
if (USE_STORE_POST_INCREMENT (mode) && ! data.reverse && ! data.autinc_to)
{
data.to_addr = copy_addr_to_reg (to_addr);
data.autinc_to = 1;
data.explicit_inc_to = 1;
}
if (!data.autinc_to && CONSTANT_P (to_addr))
data.to_addr = copy_addr_to_reg (to_addr);
}
tmode = mode_for_size (MOVE_MAX_PIECES * BITS_PER_UNIT, MODE_INT, 1);
if (align >= GET_MODE_ALIGNMENT (tmode))
align = GET_MODE_ALIGNMENT (tmode);
else
{
enum machine_mode xmode;
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT), xmode = tmode;
tmode != VOIDmode;
xmode = tmode, tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) > MOVE_MAX_PIECES
|| SLOW_UNALIGNED_ACCESS (tmode, align))
break;
align = MAX (align, GET_MODE_ALIGNMENT (xmode));
}
while (max_size > 1)
{
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) < max_size)
mode = tmode;
if (mode == VOIDmode)
break;
icode = mov_optab->handlers[(int) mode].insn_code;
if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode))
move_by_pieces_1 (GEN_FCN (icode), mode, &data);
max_size = GET_MODE_SIZE (mode);
}
gcc_assert (!data.len);
if (endp)
{
rtx to1;
gcc_assert (!data.reverse);
if (data.autinc_to)
{
if (endp == 2)
{
if (HAVE_POST_INCREMENT && data.explicit_inc_to > 0)
emit_insn (gen_add2_insn (data.to_addr, constm1_rtx));
else
data.to_addr = copy_addr_to_reg (plus_constant (data.to_addr,
-1));
}
to1 = adjust_automodify_address (data.to, QImode, data.to_addr,
data.offset);
}
else
{
if (endp == 2)
--data.offset;
to1 = adjust_address (data.to, QImode, data.offset);
}
return to1;
}
else
return data.to;
}
static unsigned HOST_WIDE_INT
move_by_pieces_ninsns (unsigned HOST_WIDE_INT l, unsigned int align,
unsigned int max_size)
{
unsigned HOST_WIDE_INT n_insns = 0;
enum machine_mode tmode;
tmode = mode_for_size (MOVE_MAX_PIECES * BITS_PER_UNIT, MODE_INT, 1);
if (align >= GET_MODE_ALIGNMENT (tmode))
align = GET_MODE_ALIGNMENT (tmode);
else
{
enum machine_mode tmode, xmode;
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT), xmode = tmode;
tmode != VOIDmode;
xmode = tmode, tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) > MOVE_MAX_PIECES
|| SLOW_UNALIGNED_ACCESS (tmode, align))
break;
align = MAX (align, GET_MODE_ALIGNMENT (xmode));
}
while (max_size > 1)
{
enum machine_mode mode = VOIDmode;
enum insn_code icode;
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) < max_size)
mode = tmode;
if (mode == VOIDmode)
break;
icode = mov_optab->handlers[(int) mode].insn_code;
if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode))
n_insns += l / GET_MODE_SIZE (mode), l %= GET_MODE_SIZE (mode);
max_size = GET_MODE_SIZE (mode);
}
gcc_assert (!l);
return n_insns;
}
static void
move_by_pieces_1 (rtx (*genfun) (rtx, ...), enum machine_mode mode,
struct move_by_pieces *data)
{
unsigned int size = GET_MODE_SIZE (mode);
rtx to1 = NULL_RTX, from1;
while (data->len >= size)
{
if (data->reverse)
data->offset -= size;
if (data->to)
{
if (data->autinc_to)
to1 = adjust_automodify_address (data->to, mode, data->to_addr,
data->offset);
else
to1 = adjust_address (data->to, mode, data->offset);
}
if (data->autinc_from)
from1 = adjust_automodify_address (data->from, mode, data->from_addr,
data->offset);
else
from1 = adjust_address (data->from, mode, data->offset);
if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0)
emit_insn (gen_add2_insn (data->to_addr,
GEN_INT (-(HOST_WIDE_INT)size)));
if (HAVE_PRE_DECREMENT && data->explicit_inc_from < 0)
emit_insn (gen_add2_insn (data->from_addr,
GEN_INT (-(HOST_WIDE_INT)size)));
if (data->to)
emit_insn ((*genfun) (to1, from1));
else
{
#ifdef PUSH_ROUNDING
emit_single_push_insn (mode, from1, NULL, NULL_RTX);
#else
gcc_unreachable ();
#endif
}
if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0)
emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size)));
if (HAVE_POST_INCREMENT && data->explicit_inc_from > 0)
emit_insn (gen_add2_insn (data->from_addr, GEN_INT (size)));
if (! data->reverse)
data->offset += size;
data->len -= size;
}
}
rtx
emit_block_move (rtx x, rtx y, rtx size, enum block_op_methods method)
{
bool may_use_call;
rtx retval = 0;
unsigned int align;
switch (method)
{
case BLOCK_OP_NORMAL:
case BLOCK_OP_TAILCALL:
may_use_call = true;
break;
case BLOCK_OP_CALL_PARM:
may_use_call = block_move_libcall_safe_for_call_parm ();
NO_DEFER_POP;
break;
case BLOCK_OP_NO_LIBCALL:
may_use_call = false;
break;
default:
gcc_unreachable ();
}
align = MIN (MEM_ALIGN (x), MEM_ALIGN (y));
gcc_assert (MEM_P (x));
gcc_assert (MEM_P (y));
gcc_assert (size);
x = adjust_address (x, BLKmode, 0);
y = adjust_address (y, BLKmode, 0);
if (GET_CODE (size) == CONST_INT)
{
if (INTVAL (size) == 0)
return 0;
x = shallow_copy_rtx (x);
y = shallow_copy_rtx (y);
set_mem_size (x, size);
set_mem_size (y, size);
}
if (GET_CODE (size) == CONST_INT && MOVE_BY_PIECES_P (INTVAL (size), align))
move_by_pieces (x, y, INTVAL (size), align, 0);
else if (emit_block_move_via_movmem (x, y, size, align))
;
else if (may_use_call)
retval = emit_block_move_via_libcall (x, y, size,
method == BLOCK_OP_TAILCALL);
else
emit_block_move_via_loop (x, y, size, align);
if (method == BLOCK_OP_CALL_PARM)
OK_DEFER_POP;
return retval;
}
static bool
block_move_libcall_safe_for_call_parm (void)
{
if (PUSH_ARGS)
return true;
#if defined (REG_PARM_STACK_SPACE) && defined (OUTGOING_REG_PARM_STACK_SPACE)
{
tree fn = emit_block_move_libcall_fn (false);
(void) fn;
if (REG_PARM_STACK_SPACE (fn) != 0)
return false;
}
#endif
{
CUMULATIVE_ARGS args_so_far;
tree fn, arg;
fn = emit_block_move_libcall_fn (false);
INIT_CUMULATIVE_ARGS (args_so_far, TREE_TYPE (fn), NULL_RTX, 0, 3);
arg = TYPE_ARG_TYPES (TREE_TYPE (fn));
for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg))
{
enum machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
rtx tmp = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
if (!tmp || !REG_P (tmp))
return false;
if (targetm.calls.arg_partial_bytes (&args_so_far, mode, NULL, 1))
return false;
FUNCTION_ARG_ADVANCE (args_so_far, mode, NULL_TREE, 1);
}
}
return true;
}
static bool
emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align)
{
rtx opalign = GEN_INT (align / BITS_PER_UNIT);
int save_volatile_ok = volatile_ok;
enum machine_mode mode;
volatile_ok = 1;
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
{
enum insn_code code = movmem_optab[(int) mode];
insn_operand_predicate_fn pred;
if (code != CODE_FOR_nothing
&& ((GET_CODE (size) == CONST_INT
&& ((unsigned HOST_WIDE_INT) INTVAL (size)
<= (GET_MODE_MASK (mode) >> 1)))
|| GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
&& ((pred = insn_data[(int) code].operand[0].predicate) == 0
|| (*pred) (x, BLKmode))
&& ((pred = insn_data[(int) code].operand[1].predicate) == 0
|| (*pred) (y, BLKmode))
&& ((pred = insn_data[(int) code].operand[3].predicate) == 0
|| (*pred) (opalign, VOIDmode)))
{
rtx op2;
rtx last = get_last_insn ();
rtx pat;
op2 = convert_to_mode (mode, size, 1);
pred = insn_data[(int) code].operand[2].predicate;
if (pred != 0 && ! (*pred) (op2, mode))
op2 = copy_to_mode_reg (mode, op2);
pat = GEN_FCN ((int) code) (x, y, op2, opalign);
if (pat)
{
emit_insn (pat);
volatile_ok = save_volatile_ok;
return true;
}
else
delete_insns_since (last);
}
}
volatile_ok = save_volatile_ok;
return false;
}
static rtx
emit_block_move_via_libcall (rtx dst, rtx src, rtx size, bool tailcall)
{
rtx dst_addr, src_addr;
tree call_expr, arg_list, fn, src_tree, dst_tree, size_tree;
enum machine_mode size_mode;
rtx retval;
dst_addr = copy_to_mode_reg (Pmode, XEXP (dst, 0));
src_addr = copy_to_mode_reg (Pmode, XEXP (src, 0));
dst_addr = convert_memory_address (ptr_mode, dst_addr);
src_addr = convert_memory_address (ptr_mode, src_addr);
dst_tree = make_tree (ptr_type_node, dst_addr);
src_tree = make_tree (ptr_type_node, src_addr);
size_mode = TYPE_MODE (sizetype);
size = convert_to_mode (size_mode, size, 1);
size = copy_to_mode_reg (size_mode, size);
size_tree = make_tree (sizetype, size);
fn = emit_block_move_libcall_fn (true);
arg_list = tree_cons (NULL_TREE, size_tree, NULL_TREE);
arg_list = tree_cons (NULL_TREE, src_tree, arg_list);
arg_list = tree_cons (NULL_TREE, dst_tree, arg_list);
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arg_list, NULL_TREE);
CALL_EXPR_TAILCALL (call_expr) = tailcall;
retval = expand_normal (call_expr);
return retval;
}
static GTY(()) tree block_move_fn;
void
init_block_move_fn (const char *asmspec)
{
if (!block_move_fn)
{
tree args, fn;
fn = get_identifier ("memcpy");
args = build_function_type_list (ptr_type_node, ptr_type_node,
const_ptr_type_node, sizetype,
NULL_TREE);
fn = build_decl (FUNCTION_DECL, fn, args);
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
TREE_NOTHROW (fn) = 1;
DECL_VISIBILITY (fn) = VISIBILITY_DEFAULT;
DECL_VISIBILITY_SPECIFIED (fn) = 1;
block_move_fn = fn;
}
if (asmspec)
set_user_assembler_name (block_move_fn, asmspec);
}
static tree
emit_block_move_libcall_fn (int for_call)
{
static bool emitted_extern;
if (!block_move_fn)
init_block_move_fn (NULL);
if (for_call && !emitted_extern)
{
emitted_extern = true;
make_decl_rtl (block_move_fn);
assemble_external (block_move_fn);
}
return block_move_fn;
}
static void
emit_block_move_via_loop (rtx x, rtx y, rtx size,
unsigned int align ATTRIBUTE_UNUSED)
{
rtx cmp_label, top_label, iter, x_addr, y_addr, tmp;
enum machine_mode iter_mode;
iter_mode = GET_MODE (size);
if (iter_mode == VOIDmode)
iter_mode = word_mode;
top_label = gen_label_rtx ();
cmp_label = gen_label_rtx ();
iter = gen_reg_rtx (iter_mode);
emit_move_insn (iter, const0_rtx);
x_addr = force_operand (XEXP (x, 0), NULL_RTX);
y_addr = force_operand (XEXP (y, 0), NULL_RTX);
do_pending_stack_adjust ();
emit_jump (cmp_label);
emit_label (top_label);
tmp = convert_modes (Pmode, iter_mode, iter, true);
x_addr = gen_rtx_PLUS (Pmode, x_addr, tmp);
y_addr = gen_rtx_PLUS (Pmode, y_addr, tmp);
x = change_address (x, QImode, x_addr);
y = change_address (y, QImode, y_addr);
emit_move_insn (x, y);
tmp = expand_simple_binop (iter_mode, PLUS, iter, const1_rtx, iter,
true, OPTAB_LIB_WIDEN);
if (tmp != iter)
emit_move_insn (iter, tmp);
emit_label (cmp_label);
emit_cmp_and_jump_insns (iter, size, LT, NULL_RTX, iter_mode,
true, top_label);
}
void
move_block_to_reg (int regno, rtx x, int nregs, enum machine_mode mode)
{
int i;
#ifdef HAVE_load_multiple
rtx pat;
rtx last;
#endif
if (nregs == 0)
return;
if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x))
x = validize_mem (force_const_mem (mode, x));
#ifdef HAVE_load_multiple
if (HAVE_load_multiple)
{
last = get_last_insn ();
pat = gen_load_multiple (gen_rtx_REG (word_mode, regno), x,
GEN_INT (nregs));
if (pat)
{
emit_insn (pat);
return;
}
else
delete_insns_since (last);
}
#endif
for (i = 0; i < nregs; i++)
emit_move_insn (gen_rtx_REG (word_mode, regno + i),
operand_subword_force (x, i, mode));
}
void
move_block_from_reg (int regno, rtx x, int nregs)
{
int i;
if (nregs == 0)
return;
#ifdef HAVE_store_multiple
if (HAVE_store_multiple)
{
rtx last = get_last_insn ();
rtx pat = gen_store_multiple (x, gen_rtx_REG (word_mode, regno),
GEN_INT (nregs));
if (pat)
{
emit_insn (pat);
return;
}
else
delete_insns_since (last);
}
#endif
for (i = 0; i < nregs; i++)
{
rtx tem = operand_subword (x, i, 1, BLKmode);
gcc_assert (tem);
emit_move_insn (tem, gen_rtx_REG (word_mode, regno + i));
}
}
rtx
gen_group_rtx (rtx orig)
{
int i, length;
rtx *tmps;
gcc_assert (GET_CODE (orig) == PARALLEL);
length = XVECLEN (orig, 0);
tmps = alloca (sizeof (rtx) * length);
i = XEXP (XVECEXP (orig, 0, 0), 0) ? 0 : 1;
if (i)
tmps[0] = 0;
for (; i < length; i++)
{
enum machine_mode mode = GET_MODE (XEXP (XVECEXP (orig, 0, i), 0));
rtx offset = XEXP (XVECEXP (orig, 0, i), 1);
tmps[i] = gen_rtx_EXPR_LIST (VOIDmode, gen_reg_rtx (mode), offset);
}
return gen_rtx_PARALLEL (GET_MODE (orig), gen_rtvec_v (length, tmps));
}
static void
emit_group_load_1 (rtx *tmps, rtx dst, rtx orig_src, tree type, int ssize)
{
rtx src;
int start, i;
enum machine_mode m = GET_MODE (orig_src);
gcc_assert (GET_CODE (dst) == PARALLEL);
if (m != VOIDmode
&& !SCALAR_INT_MODE_P (m)
&& !MEM_P (orig_src)
&& GET_CODE (orig_src) != CONCAT)
{
enum machine_mode imode = int_mode_for_mode (GET_MODE (orig_src));
if (imode == BLKmode)
src = assign_stack_temp (GET_MODE (orig_src), ssize, 0);
else
src = gen_reg_rtx (imode);
if (imode != BLKmode)
src = gen_lowpart (GET_MODE (orig_src), src);
emit_move_insn (src, orig_src);
if (imode != BLKmode)
src = gen_lowpart (imode, src);
emit_group_load_1 (tmps, dst, src, type, ssize);
return;
}
if (XEXP (XVECEXP (dst, 0, 0), 0))
start = 0;
else
start = 1;
for (i = start; i < XVECLEN (dst, 0); i++)
{
enum machine_mode mode = GET_MODE (XEXP (XVECEXP (dst, 0, i), 0));
HOST_WIDE_INT bytepos = INTVAL (XEXP (XVECEXP (dst, 0, i), 1));
unsigned int bytelen = GET_MODE_SIZE (mode);
int shift = 0;
if (ssize >= 0 && bytepos + (HOST_WIDE_INT) bytelen > ssize)
{
if (
#ifdef BLOCK_REG_PADDING
BLOCK_REG_PADDING (GET_MODE (orig_src), type, i == start)
== (BYTES_BIG_ENDIAN ? upward : downward)
#else
BYTES_BIG_ENDIAN
#endif
)
shift = (bytelen - (ssize - bytepos)) * BITS_PER_UNIT;
bytelen = ssize - bytepos;
gcc_assert (bytelen > 0);
}
src = orig_src;
if (!MEM_P (orig_src)
&& (!CONSTANT_P (orig_src)
|| (GET_MODE (orig_src) != mode
&& GET_MODE (orig_src) != VOIDmode)))
{
if (GET_MODE (orig_src) == VOIDmode)
src = gen_reg_rtx (mode);
else
src = gen_reg_rtx (GET_MODE (orig_src));
emit_move_insn (src, orig_src);
}
if (MEM_P (src)
&& (! SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (src))
|| MEM_ALIGN (src) >= GET_MODE_ALIGNMENT (mode))
&& bytepos * BITS_PER_UNIT % GET_MODE_ALIGNMENT (mode) == 0
&& bytelen == GET_MODE_SIZE (mode))
{
tmps[i] = gen_reg_rtx (mode);
emit_move_insn (tmps[i], adjust_address (src, mode, bytepos));
}
else if (COMPLEX_MODE_P (mode)
&& GET_MODE (src) == mode
&& bytelen == GET_MODE_SIZE (mode))
tmps[i] = src;
else if (GET_CODE (src) == CONCAT)
{
unsigned int slen = GET_MODE_SIZE (GET_MODE (src));
unsigned int slen0 = GET_MODE_SIZE (GET_MODE (XEXP (src, 0)));
if ((bytepos == 0 && bytelen == slen0)
|| (bytepos != 0 && bytepos + bytelen <= slen))
{
tmps[i] = XEXP (src, bytepos / slen0);
if (! CONSTANT_P (tmps[i])
&& (!REG_P (tmps[i]) || GET_MODE (tmps[i]) != mode))
tmps[i] = extract_bit_field (tmps[i], bytelen * BITS_PER_UNIT,
(bytepos % slen0) * BITS_PER_UNIT,
1, NULL_RTX, mode, mode);
}
else
{
rtx mem;
gcc_assert (!bytepos);
mem = assign_stack_temp (GET_MODE (src), slen, 0);
emit_move_insn (mem, src);
tmps[i] = extract_bit_field (mem, bytelen * BITS_PER_UNIT,
0, 1, NULL_RTX, mode, mode);
}
}
else if (VECTOR_MODE_P (GET_MODE (dst))
&& REG_P (src))
{
int slen = GET_MODE_SIZE (GET_MODE (src));
rtx mem;
mem = assign_stack_temp (GET_MODE (src), slen, 0);
emit_move_insn (mem, src);
tmps[i] = adjust_address (mem, mode, (int) bytepos);
}
else if (CONSTANT_P (src) && GET_MODE (dst) != BLKmode
&& XVECLEN (dst, 0) > 1)
tmps[i] = simplify_gen_subreg (mode, src, GET_MODE(dst), bytepos);
else if (CONSTANT_P (src)
|| (REG_P (src) && GET_MODE (src) == mode))
tmps[i] = src;
else
tmps[i] = extract_bit_field (src, bytelen * BITS_PER_UNIT,
bytepos * BITS_PER_UNIT, 1, NULL_RTX,
mode, mode);
if (shift)
tmps[i] = expand_shift (LSHIFT_EXPR, mode, tmps[i],
build_int_cst (NULL_TREE, shift), tmps[i], 0);
}
}
void
emit_group_load (rtx dst, rtx src, tree type, int ssize)
{
rtx *tmps;
int i;
tmps = alloca (sizeof (rtx) * XVECLEN (dst, 0));
emit_group_load_1 (tmps, dst, src, type, ssize);
for (i = 0; i < XVECLEN (dst, 0); i++)
{
rtx d = XEXP (XVECEXP (dst, 0, i), 0);
if (d == NULL)
continue;
emit_move_insn (d, tmps[i]);
}
}
rtx
emit_group_load_into_temps (rtx parallel, rtx src, tree type, int ssize)
{
rtvec vec;
int i;
vec = rtvec_alloc (XVECLEN (parallel, 0));
emit_group_load_1 (&RTVEC_ELT (vec, 0), parallel, src, type, ssize);
for (i = 0; i < XVECLEN (parallel, 0); i++)
{
rtx e = XVECEXP (parallel, 0, i);
rtx d = XEXP (e, 0);
if (d)
{
d = force_reg (GET_MODE (d), RTVEC_ELT (vec, i));
e = alloc_EXPR_LIST (REG_NOTE_KIND (e), d, XEXP (e, 1));
}
RTVEC_ELT (vec, i) = e;
}
return gen_rtx_PARALLEL (GET_MODE (parallel), vec);
}
void
emit_group_move (rtx dst, rtx src)
{
int i;
gcc_assert (GET_CODE (src) == PARALLEL
&& GET_CODE (dst) == PARALLEL
&& XVECLEN (src, 0) == XVECLEN (dst, 0));
for (i = XEXP (XVECEXP (src, 0, 0), 0) ? 0 : 1; i < XVECLEN (src, 0); i++)
emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0),
XEXP (XVECEXP (src, 0, i), 0));
}
rtx
emit_group_move_into_temps (rtx src)
{
rtvec vec = rtvec_alloc (XVECLEN (src, 0));
int i;
for (i = 0; i < XVECLEN (src, 0); i++)
{
rtx e = XVECEXP (src, 0, i);
rtx d = XEXP (e, 0);
if (d)
e = alloc_EXPR_LIST (REG_NOTE_KIND (e), copy_to_reg (d), XEXP (e, 1));
RTVEC_ELT (vec, i) = e;
}
return gen_rtx_PARALLEL (GET_MODE (src), vec);
}
void
emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED, int ssize)
{
rtx *tmps, dst;
int start, finish, i;
enum machine_mode m = GET_MODE (orig_dst);
gcc_assert (GET_CODE (src) == PARALLEL);
if (!SCALAR_INT_MODE_P (m)
&& !MEM_P (orig_dst) && GET_CODE (orig_dst) != CONCAT)
{
enum machine_mode imode = int_mode_for_mode (GET_MODE (orig_dst));
if (imode == BLKmode)
dst = assign_stack_temp (GET_MODE (orig_dst), ssize, 0);
else
dst = gen_reg_rtx (imode);
emit_group_store (dst, src, type, ssize);
if (imode != BLKmode)
dst = gen_lowpart (GET_MODE (orig_dst), dst);
emit_move_insn (orig_dst, dst);
return;
}
if (XEXP (XVECEXP (src, 0, 0), 0))
start = 0;
else
start = 1;
finish = XVECLEN (src, 0);
tmps = alloca (sizeof (rtx) * finish);
for (i = start; i < finish; i++)
{
rtx reg = XEXP (XVECEXP (src, 0, i), 0);
if (!REG_P (reg) || REGNO (reg) < FIRST_PSEUDO_REGISTER)
{
tmps[i] = gen_reg_rtx (GET_MODE (reg));
emit_move_insn (tmps[i], reg);
}
else
tmps[i] = reg;
}
dst = orig_dst;
if (GET_CODE (dst) == PARALLEL)
{
rtx temp;
if (rtx_equal_p (dst, src))
return;
temp = assign_stack_temp (GET_MODE (dst), ssize, 0);
emit_group_store (temp, src, type, ssize);
emit_group_load (dst, temp, type, ssize);
return;
}
else if (!MEM_P (dst) && GET_CODE (dst) != CONCAT)
{
enum machine_mode outer = GET_MODE (dst);
enum machine_mode inner;
HOST_WIDE_INT bytepos;
bool done = false;
rtx temp;
if (!REG_P (dst) || REGNO (dst) < FIRST_PSEUDO_REGISTER)
dst = gen_reg_rtx (outer);
if (start < finish)
{
inner = GET_MODE (tmps[start]);
bytepos = subreg_lowpart_offset (inner, outer);
if (INTVAL (XEXP (XVECEXP (src, 0, start), 1)) == bytepos)
{
temp = simplify_gen_subreg (outer, tmps[start],
inner, 0);
if (temp)
{
emit_move_insn (dst, temp);
done = true;
start++;
}
}
}
if (!done
&& start < finish - 1)
{
inner = GET_MODE (tmps[finish - 1]);
bytepos = subreg_lowpart_offset (inner, outer);
if (INTVAL (XEXP (XVECEXP (src, 0, finish - 1), 1)) == bytepos)
{
temp = simplify_gen_subreg (outer, tmps[finish - 1],
inner, 0);
if (temp)
{
emit_move_insn (dst, temp);
done = true;
finish--;
}
}
}
if (!done)
emit_move_insn (dst, CONST0_RTX (outer));
}
for (i = start; i < finish; i++)
{
HOST_WIDE_INT bytepos = INTVAL (XEXP (XVECEXP (src, 0, i), 1));
enum machine_mode mode = GET_MODE (tmps[i]);
unsigned int bytelen = GET_MODE_SIZE (mode);
rtx dest = dst;
if (ssize >= 0 && bytepos + (HOST_WIDE_INT) bytelen > ssize)
{
if (
#ifdef BLOCK_REG_PADDING
BLOCK_REG_PADDING (GET_MODE (orig_dst), type, i == start)
== (BYTES_BIG_ENDIAN ? upward : downward)
#else
BYTES_BIG_ENDIAN
#endif
)
{
int shift = (bytelen - (ssize - bytepos)) * BITS_PER_UNIT;
tmps[i] = expand_shift (RSHIFT_EXPR, mode, tmps[i],
build_int_cst (NULL_TREE, shift),
tmps[i], 0);
}
bytelen = ssize - bytepos;
}
if (GET_CODE (dst) == CONCAT)
{
if (bytepos + bytelen <= GET_MODE_SIZE (GET_MODE (XEXP (dst, 0))))
dest = XEXP (dst, 0);
else if (bytepos >= GET_MODE_SIZE (GET_MODE (XEXP (dst, 0))))
{
bytepos -= GET_MODE_SIZE (GET_MODE (XEXP (dst, 0)));
dest = XEXP (dst, 1);
}
else
{
gcc_assert (bytepos == 0 && XVECLEN (src, 0));
dest = assign_stack_temp (GET_MODE (dest),
GET_MODE_SIZE (GET_MODE (dest)), 0);
emit_move_insn (adjust_address (dest, GET_MODE (tmps[i]), bytepos),
tmps[i]);
dst = dest;
break;
}
}
if (MEM_P (dest)
&& (! SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (dest))
|| MEM_ALIGN (dest) >= GET_MODE_ALIGNMENT (mode))
&& bytepos * BITS_PER_UNIT % GET_MODE_ALIGNMENT (mode) == 0
&& bytelen == GET_MODE_SIZE (mode))
emit_move_insn (adjust_address (dest, mode, bytepos), tmps[i]);
else
store_bit_field (dest, bytelen * BITS_PER_UNIT, bytepos * BITS_PER_UNIT,
mode, tmps[i], NULL_TREE);
}
if (orig_dst != dst)
emit_move_insn (orig_dst, dst);
}
rtx
copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type)
{
unsigned HOST_WIDE_INT bytes = int_size_in_bytes (type);
rtx src = NULL, dst = NULL;
unsigned HOST_WIDE_INT bitsize = MIN (TYPE_ALIGN (type), BITS_PER_WORD);
unsigned HOST_WIDE_INT bitpos, xbitpos, padding_correction = 0;
if (tgtblk == 0)
{
tgtblk = assign_temp (build_qualified_type (type,
(TYPE_QUALS (type)
| TYPE_QUAL_CONST)),
0, 1, 1);
preserve_temp_slots (tgtblk);
}
if (GET_MODE (srcreg) != BLKmode
&& GET_MODE_SIZE (GET_MODE (srcreg)) < UNITS_PER_WORD)
srcreg = convert_to_mode (word_mode, srcreg, TYPE_UNSIGNED (type));
if (bytes % UNITS_PER_WORD != 0
&& (targetm.calls.return_in_msb (type)
? !BYTES_BIG_ENDIAN
: BYTES_BIG_ENDIAN))
padding_correction
= (BITS_PER_WORD - ((bytes % UNITS_PER_WORD) * BITS_PER_UNIT));
for (bitpos = 0, xbitpos = padding_correction;
bitpos < bytes * BITS_PER_UNIT;
bitpos += bitsize, xbitpos += bitsize)
{
if (xbitpos % BITS_PER_WORD == 0
|| xbitpos == padding_correction)
src = operand_subword_force (srcreg, xbitpos / BITS_PER_WORD,
GET_MODE (srcreg));
if (bitpos % BITS_PER_WORD == 0)
dst = operand_subword (tgtblk, bitpos / BITS_PER_WORD, 1, BLKmode);
store_bit_field (dst, bitsize, bitpos % BITS_PER_WORD, word_mode,
extract_bit_field (src, bitsize,
xbitpos % BITS_PER_WORD, 1,
NULL_RTX, word_mode, word_mode),
NULL_TREE);
}
return tgtblk;
}
void
use_reg (rtx *call_fusage, rtx reg)
{
gcc_assert (REG_P (reg) && REGNO (reg) < FIRST_PSEUDO_REGISTER);
*call_fusage
= gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_USE (VOIDmode, reg), *call_fusage);
}
void
use_regs (rtx *call_fusage, int regno, int nregs)
{
int i;
gcc_assert (regno + nregs <= FIRST_PSEUDO_REGISTER);
for (i = 0; i < nregs; i++)
use_reg (call_fusage, regno_reg_rtx[regno + i]);
}
void
use_group_regs (rtx *call_fusage, rtx regs)
{
int i;
for (i = 0; i < XVECLEN (regs, 0); i++)
{
rtx reg = XEXP (XVECEXP (regs, 0, i), 0);
if (reg != 0 && REG_P (reg))
use_reg (call_fusage, reg);
}
}
int
can_store_by_pieces (unsigned HOST_WIDE_INT len,
rtx (*constfun) (void *, HOST_WIDE_INT, enum machine_mode),
void *constfundata, unsigned int align)
{
unsigned HOST_WIDE_INT l;
unsigned int max_size;
HOST_WIDE_INT offset = 0;
enum machine_mode mode, tmode;
enum insn_code icode;
int reverse;
rtx cst;
if (len == 0)
return 1;
if (! STORE_BY_PIECES_P (len, align))
return 0;
tmode = mode_for_size (STORE_MAX_PIECES * BITS_PER_UNIT, MODE_INT, 1);
if (align >= GET_MODE_ALIGNMENT (tmode))
align = GET_MODE_ALIGNMENT (tmode);
else
{
enum machine_mode xmode;
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT), xmode = tmode;
tmode != VOIDmode;
xmode = tmode, tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) > STORE_MAX_PIECES
|| SLOW_UNALIGNED_ACCESS (tmode, align))
break;
align = MAX (align, GET_MODE_ALIGNMENT (xmode));
}
for (reverse = 0;
reverse <= (HAVE_PRE_DECREMENT || HAVE_POST_DECREMENT);
reverse++)
{
l = len;
mode = VOIDmode;
max_size = STORE_MAX_PIECES + 1;
while (max_size > 1)
{
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) < max_size)
mode = tmode;
if (mode == VOIDmode)
break;
icode = mov_optab->handlers[(int) mode].insn_code;
if (icode != CODE_FOR_nothing
&& align >= GET_MODE_ALIGNMENT (mode))
{
unsigned int size = GET_MODE_SIZE (mode);
while (l >= size)
{
if (reverse)
offset -= size;
cst = (*constfun) (constfundata, offset, mode);
if (!LEGITIMATE_CONSTANT_P (cst))
return 0;
if (!reverse)
offset += size;
l -= size;
}
}
max_size = GET_MODE_SIZE (mode);
}
gcc_assert (!l);
}
return 1;
}
rtx
store_by_pieces (rtx to, unsigned HOST_WIDE_INT len,
rtx (*constfun) (void *, HOST_WIDE_INT, enum machine_mode),
void *constfundata, unsigned int align, int endp)
{
struct store_by_pieces data;
if (len == 0)
{
gcc_assert (endp != 2);
return to;
}
gcc_assert (STORE_BY_PIECES_P (len, align));
data.constfun = constfun;
data.constfundata = constfundata;
data.len = len;
data.to = to;
store_by_pieces_1 (&data, align);
if (endp)
{
rtx to1;
gcc_assert (!data.reverse);
if (data.autinc_to)
{
if (endp == 2)
{
if (HAVE_POST_INCREMENT && data.explicit_inc_to > 0)
emit_insn (gen_add2_insn (data.to_addr, constm1_rtx));
else
data.to_addr = copy_addr_to_reg (plus_constant (data.to_addr,
-1));
}
to1 = adjust_automodify_address (data.to, QImode, data.to_addr,
data.offset);
}
else
{
if (endp == 2)
--data.offset;
to1 = adjust_address (data.to, QImode, data.offset);
}
return to1;
}
else
return data.to;
}
static void
clear_by_pieces (rtx to, unsigned HOST_WIDE_INT len, unsigned int align)
{
struct store_by_pieces data;
if (len == 0)
return;
data.constfun = clear_by_pieces_1;
data.constfundata = NULL;
data.len = len;
data.to = to;
store_by_pieces_1 (&data, align);
}
static rtx
clear_by_pieces_1 (void *data ATTRIBUTE_UNUSED,
HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
return const0_rtx;
}
static void
store_by_pieces_1 (struct store_by_pieces *data ATTRIBUTE_UNUSED,
unsigned int align ATTRIBUTE_UNUSED)
{
rtx to_addr = XEXP (data->to, 0);
unsigned int max_size = STORE_MAX_PIECES + 1;
enum machine_mode mode = VOIDmode, tmode;
enum insn_code icode;
data->offset = 0;
data->to_addr = to_addr;
data->autinc_to
= (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC
|| GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC);
data->explicit_inc_to = 0;
data->reverse
= (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC);
if (data->reverse)
data->offset = data->len;
if (!data->autinc_to
&& move_by_pieces_ninsns (data->len, align, max_size) > 2)
{
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) < max_size)
mode = tmode;
if (USE_STORE_PRE_DECREMENT (mode) && data->reverse && ! data->autinc_to)
{
data->to_addr = copy_addr_to_reg (plus_constant (to_addr, data->len));
data->autinc_to = 1;
data->explicit_inc_to = -1;
}
if (USE_STORE_POST_INCREMENT (mode) && ! data->reverse
&& ! data->autinc_to)
{
data->to_addr = copy_addr_to_reg (to_addr);
data->autinc_to = 1;
data->explicit_inc_to = 1;
}
if ( !data->autinc_to && CONSTANT_P (to_addr))
data->to_addr = copy_addr_to_reg (to_addr);
}
tmode = mode_for_size (STORE_MAX_PIECES * BITS_PER_UNIT, MODE_INT, 1);
if (align >= GET_MODE_ALIGNMENT (tmode))
align = GET_MODE_ALIGNMENT (tmode);
else
{
enum machine_mode xmode;
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT), xmode = tmode;
tmode != VOIDmode;
xmode = tmode, tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) > STORE_MAX_PIECES
|| SLOW_UNALIGNED_ACCESS (tmode, align))
break;
align = MAX (align, GET_MODE_ALIGNMENT (xmode));
}
while (max_size > 1)
{
for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
if (GET_MODE_SIZE (tmode) < max_size)
mode = tmode;
if (mode == VOIDmode)
break;
icode = mov_optab->handlers[(int) mode].insn_code;
if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode))
store_by_pieces_2 (GEN_FCN (icode), mode, data);
max_size = GET_MODE_SIZE (mode);
}
gcc_assert (!data->len);
}
static void
store_by_pieces_2 (rtx (*genfun) (rtx, ...), enum machine_mode mode,
struct store_by_pieces *data)
{
unsigned int size = GET_MODE_SIZE (mode);
rtx to1, cst;
while (data->len >= size)
{
if (data->reverse)
data->offset -= size;
if (data->autinc_to)
to1 = adjust_automodify_address (data->to, mode, data->to_addr,
data->offset);
else
to1 = adjust_address (data->to, mode, data->offset);
if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0)
emit_insn (gen_add2_insn (data->to_addr,
GEN_INT (-(HOST_WIDE_INT) size)));
cst = (*data->constfun) (data->constfundata, data->offset, mode);
emit_insn ((*genfun) (to1, cst));
if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0)
emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size)));
if (! data->reverse)
data->offset += size;
data->len -= size;
}
}
rtx
clear_storage (rtx object, rtx size, enum block_op_methods method)
{
enum machine_mode mode = GET_MODE (object);
unsigned int align;
gcc_assert (method == BLOCK_OP_NORMAL || method == BLOCK_OP_TAILCALL);
if (mode != BLKmode
&& GET_CODE (size) == CONST_INT
&& INTVAL (size) == (HOST_WIDE_INT) GET_MODE_SIZE (mode))
{
rtx zero = CONST0_RTX (mode);
if (zero != NULL)
{
emit_move_insn (object, zero);
return NULL;
}
if (COMPLEX_MODE_P (mode))
{
zero = CONST0_RTX (GET_MODE_INNER (mode));
if (zero != NULL)
{
write_complex_part (object, zero, 0);
write_complex_part (object, zero, 1);
return NULL;
}
}
}
if (size == const0_rtx)
return NULL;
align = MEM_ALIGN (object);
if (GET_CODE (size) == CONST_INT
&& CLEAR_BY_PIECES_P (INTVAL (size), align))
clear_by_pieces (object, INTVAL (size), align);
else if (set_storage_via_setmem (object, size, const0_rtx, align))
;
else
return clear_storage_via_libcall (object, size,
method == BLOCK_OP_TAILCALL);
return NULL;
}
static rtx
clear_storage_via_libcall (rtx object, rtx size, bool tailcall)
{
tree call_expr, arg_list, fn, object_tree, size_tree;
enum machine_mode size_mode;
rtx retval;
object = copy_to_mode_reg (Pmode, XEXP (object, 0));
size_mode = TYPE_MODE (sizetype);
size = convert_to_mode (size_mode, size, 1);
size = copy_to_mode_reg (size_mode, size);
object_tree = make_tree (ptr_type_node, object);
size_tree = make_tree (sizetype, size);
fn = clear_storage_libcall_fn (true);
arg_list = tree_cons (NULL_TREE, size_tree, NULL_TREE);
arg_list = tree_cons (NULL_TREE, integer_zero_node, arg_list);
arg_list = tree_cons (NULL_TREE, object_tree, arg_list);
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arg_list, NULL_TREE);
CALL_EXPR_TAILCALL (call_expr) = tailcall;
retval = expand_normal (call_expr);
return retval;
}
static GTY(()) tree block_clear_fn;
void
init_block_clear_fn (const char *asmspec)
{
if (!block_clear_fn)
{
tree fn, args;
fn = get_identifier ("memset");
args = build_function_type_list (ptr_type_node, ptr_type_node,
integer_type_node, sizetype,
NULL_TREE);
fn = build_decl (FUNCTION_DECL, fn, args);
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
TREE_NOTHROW (fn) = 1;
DECL_VISIBILITY (fn) = VISIBILITY_DEFAULT;
DECL_VISIBILITY_SPECIFIED (fn) = 1;
block_clear_fn = fn;
}
if (asmspec)
set_user_assembler_name (block_clear_fn, asmspec);
}
static tree
clear_storage_libcall_fn (int for_call)
{
static bool emitted_extern;
if (!block_clear_fn)
init_block_clear_fn (NULL);
if (for_call && !emitted_extern)
{
emitted_extern = true;
make_decl_rtl (block_clear_fn);
assemble_external (block_clear_fn);
}
return block_clear_fn;
}
bool
set_storage_via_setmem (rtx object, rtx size, rtx val, unsigned int align)
{
rtx opalign = GEN_INT (align / BITS_PER_UNIT);
enum machine_mode mode;
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
{
enum insn_code code = setmem_optab[(int) mode];
insn_operand_predicate_fn pred;
if (code != CODE_FOR_nothing
&& ((GET_CODE (size) == CONST_INT
&& ((unsigned HOST_WIDE_INT) INTVAL (size)
<= (GET_MODE_MASK (mode) >> 1)))
|| GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
&& ((pred = insn_data[(int) code].operand[0].predicate) == 0
|| (*pred) (object, BLKmode))
&& ((pred = insn_data[(int) code].operand[3].predicate) == 0
|| (*pred) (opalign, VOIDmode)))
{
rtx opsize, opchar;
enum machine_mode char_mode;
rtx last = get_last_insn ();
rtx pat;
opsize = convert_to_mode (mode, size, 1);
pred = insn_data[(int) code].operand[1].predicate;
if (pred != 0 && ! (*pred) (opsize, mode))
opsize = copy_to_mode_reg (mode, opsize);
opchar = val;
char_mode = insn_data[(int) code].operand[2].mode;
if (char_mode != VOIDmode)
{
opchar = convert_to_mode (char_mode, opchar, 1);
pred = insn_data[(int) code].operand[2].predicate;
if (pred != 0 && ! (*pred) (opchar, char_mode))
opchar = copy_to_mode_reg (char_mode, opchar);
}
pat = GEN_FCN ((int) code) (object, opsize, opchar, opalign);
if (pat)
{
emit_insn (pat);
return true;
}
else
delete_insns_since (last);
}
}
return false;
}
static void
write_complex_part (rtx cplx, rtx val, bool imag_p)
{
enum machine_mode cmode;
enum machine_mode imode;
unsigned ibitsize;
if (GET_CODE (cplx) == CONCAT)
{
emit_move_insn (XEXP (cplx, imag_p), val);
return;
}
cmode = GET_MODE (cplx);
imode = GET_MODE_INNER (cmode);
ibitsize = GET_MODE_BITSIZE (imode);
if (MEM_P (cplx))
{
emit_move_insn (adjust_address_nv (cplx, imode,
imag_p ? GET_MODE_SIZE (imode) : 0),
val);
return;
}
if (ibitsize >= BITS_PER_WORD
|| (REG_P (cplx)
&& REGNO (cplx) < FIRST_PSEUDO_REGISTER
&& hard_regno_nregs[REGNO (cplx)][cmode] % 2 == 0))
{
rtx part = simplify_gen_subreg (imode, cplx, cmode,
imag_p ? GET_MODE_SIZE (imode) : 0);
if (part)
{
emit_move_insn (part, val);
return;
}
else
gcc_assert (MEM_P (cplx) && ibitsize < BITS_PER_WORD);
}
store_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0, imode, val,
NULL_TREE);
}
static rtx
read_complex_part (rtx cplx, bool imag_p)
{
enum machine_mode cmode, imode;
unsigned ibitsize;
if (GET_CODE (cplx) == CONCAT)
return XEXP (cplx, imag_p);
cmode = GET_MODE (cplx);
imode = GET_MODE_INNER (cmode);
ibitsize = GET_MODE_BITSIZE (imode);
if (MEM_P (cplx) && GET_CODE (XEXP (cplx, 0)) == SYMBOL_REF)
{
tree decl = SYMBOL_REF_DECL (XEXP (cplx, 0));
if (decl && TREE_CODE (decl) == COMPLEX_CST)
{
tree part = imag_p ? TREE_IMAGPART (decl) : TREE_REALPART (decl);
if (CONSTANT_CLASS_P (part))
return expand_expr (part, NULL_RTX, imode, EXPAND_NORMAL);
}
}
if (MEM_P (cplx))
return adjust_address_nv (cplx, imode,
imag_p ? GET_MODE_SIZE (imode) : 0);
if (ibitsize >= BITS_PER_WORD
|| (REG_P (cplx)
&& REGNO (cplx) < FIRST_PSEUDO_REGISTER
&& hard_regno_nregs[REGNO (cplx)][cmode] % 2 == 0))
{
rtx ret = simplify_gen_subreg (imode, cplx, cmode,
imag_p ? GET_MODE_SIZE (imode) : 0);
if (ret)
return ret;
else
gcc_assert (MEM_P (cplx) && ibitsize < BITS_PER_WORD);
}
return extract_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0,
true, NULL_RTX, imode, imode);
}
static rtx
emit_move_change_mode (enum machine_mode new_mode,
enum machine_mode old_mode, rtx x, bool force)
{
rtx ret;
if (MEM_P (x))
{
if (reload_in_progress)
{
ret = adjust_address_nv (x, new_mode, 0);
copy_replacements (x, ret);
}
else
ret = adjust_address (x, new_mode, 0);
}
else
{
if (force)
ret = simplify_gen_subreg (new_mode, x, old_mode, 0);
else
ret = simplify_subreg (new_mode, x, old_mode, 0);
}
return ret;
}
static rtx
emit_move_via_integer (enum machine_mode mode, rtx x, rtx y, bool force)
{
enum machine_mode imode;
enum insn_code code;
imode = int_mode_for_mode (mode);
if (imode == BLKmode)
return NULL_RTX;
code = mov_optab->handlers[imode].insn_code;
if (code == CODE_FOR_nothing)
return NULL_RTX;
x = emit_move_change_mode (imode, mode, x, force);
if (x == NULL_RTX)
return NULL_RTX;
y = emit_move_change_mode (imode, mode, y, force);
if (y == NULL_RTX)
return NULL_RTX;
return emit_insn (GEN_FCN (code) (x, y));
}
static rtx
emit_move_resolve_push (enum machine_mode mode, rtx x)
{
enum rtx_code code = GET_CODE (XEXP (x, 0));
HOST_WIDE_INT adjust;
rtx temp;
adjust = GET_MODE_SIZE (mode);
#ifdef PUSH_ROUNDING
adjust = PUSH_ROUNDING (adjust);
#endif
if (code == PRE_DEC || code == POST_DEC)
adjust = -adjust;
else if (code == PRE_MODIFY || code == POST_MODIFY)
{
rtx expr = XEXP (XEXP (x, 0), 1);
HOST_WIDE_INT val;
gcc_assert (GET_CODE (expr) == PLUS || GET_CODE (expr) == MINUS);
gcc_assert (GET_CODE (XEXP (expr, 1)) == CONST_INT);
val = INTVAL (XEXP (expr, 1));
if (GET_CODE (expr) == MINUS)
val = -val;
gcc_assert (adjust == val || adjust == -val);
adjust = val;
}
temp = expand_simple_binop (Pmode, PLUS, stack_pointer_rtx,
GEN_INT (adjust), stack_pointer_rtx,
0, OPTAB_LIB_WIDEN);
if (temp != stack_pointer_rtx)
emit_move_insn (stack_pointer_rtx, temp);
switch (code)
{
case PRE_INC:
case PRE_DEC:
case PRE_MODIFY:
temp = stack_pointer_rtx;
break;
case POST_INC:
case POST_DEC:
case POST_MODIFY:
temp = plus_constant (stack_pointer_rtx, -adjust);
break;
default:
gcc_unreachable ();
}
return replace_equiv_address (x, temp);
}
static rtx
emit_move_complex_push (enum machine_mode mode, rtx x, rtx y)
{
enum machine_mode submode = GET_MODE_INNER (mode);
bool imag_first;
#ifdef PUSH_ROUNDING
unsigned int submodesize = GET_MODE_SIZE (submode);
if (PUSH_ROUNDING (submodesize) != submodesize)
{
x = emit_move_resolve_push (mode, x);
return emit_move_insn (x, y);
}
#endif
switch (GET_CODE (XEXP (x, 0)))
{
case PRE_DEC:
case POST_DEC:
imag_first = true;
break;
case PRE_INC:
case POST_INC:
imag_first = false;
break;
default:
gcc_unreachable ();
}
emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
read_complex_part (y, imag_first));
return emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
read_complex_part (y, !imag_first));
}
static rtx
emit_move_complex (enum machine_mode mode, rtx x, rtx y)
{
bool try_int;
if (push_operand (x, mode))
return emit_move_complex_push (mode, x, y);
if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
&& mov_optab->handlers[GET_MODE_INNER (mode)].insn_code != CODE_FOR_nothing)
try_int = false;
else if (GET_CODE (x) == CONCAT || GET_CODE (y) == CONCAT)
try_int = false;
else if (register_operand (x, mode) && register_operand (y, mode))
try_int = true;
else if ((MEM_P (x) ? !CONSTANT_P (y) : MEM_P (y))
&& (!STRICT_ALIGNMENT
|| get_mode_alignment (mode) == BIGGEST_ALIGNMENT))
try_int = true;
else
try_int = false;
if (try_int)
{
rtx ret;
if (MEM_P (x) && MEM_P (y))
{
emit_block_move (x, y, GEN_INT (GET_MODE_SIZE (mode)),
BLOCK_OP_NO_LIBCALL);
return get_last_insn ();
}
ret = emit_move_via_integer (mode, x, y, true);
if (ret)
return ret;
}
if (!reload_completed && !reload_in_progress
&& REG_P (x) && !reg_overlap_mentioned_p (x, y))
emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
write_complex_part (x, read_complex_part (y, false), false);
write_complex_part (x, read_complex_part (y, true), true);
return get_last_insn ();
}
static rtx
emit_move_ccmode (enum machine_mode mode, rtx x, rtx y)
{
rtx ret;
if (mode != CCmode)
{
enum insn_code code = mov_optab->handlers[CCmode].insn_code;
if (code != CODE_FOR_nothing)
{
x = emit_move_change_mode (CCmode, mode, x, true);
y = emit_move_change_mode (CCmode, mode, y, true);
return emit_insn (GEN_FCN (code) (x, y));
}
}
ret = emit_move_via_integer (mode, x, y, false);
gcc_assert (ret != NULL);
return ret;
}
static bool
undefined_operand_subword_p (rtx op, int i)
{
enum machine_mode innermode, innermostmode;
int offset;
if (GET_CODE (op) != SUBREG)
return false;
innermode = GET_MODE (op);
innermostmode = GET_MODE (SUBREG_REG (op));
offset = i * UNITS_PER_WORD + SUBREG_BYTE (op);
if (SUBREG_BYTE (op) == 0
&& GET_MODE_SIZE (innermostmode) < GET_MODE_SIZE (innermode))
{
int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (innermode));
if (WORDS_BIG_ENDIAN)
offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
offset += difference % UNITS_PER_WORD;
}
if (offset >= GET_MODE_SIZE (innermostmode)
|| offset <= -GET_MODE_SIZE (word_mode))
return true;
return false;
}
static rtx
emit_move_multi_word (enum machine_mode mode, rtx x, rtx y)
{
rtx last_insn = 0;
rtx seq, inner;
bool need_clobber;
int i;
gcc_assert (GET_MODE_SIZE (mode) >= UNITS_PER_WORD);
if (push_operand (x, mode))
x = emit_move_resolve_push (mode, x);
if (reload_in_progress && MEM_P (x)
&& (inner = find_replacement (&XEXP (x, 0))) != XEXP (x, 0))
x = replace_equiv_address_nv (x, inner);
if (reload_in_progress && MEM_P (y)
&& (inner = find_replacement (&XEXP (y, 0))) != XEXP (y, 0))
y = replace_equiv_address_nv (y, inner);
start_sequence ();
need_clobber = false;
for (i = 0;
i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
i++)
{
rtx xpart = operand_subword (x, i, 1, mode);
rtx ypart;
if (undefined_operand_subword_p (y, i))
continue;
ypart = operand_subword (y, i, 1, mode);
if (ypart == 0 && CONSTANT_P (y))
{
y = use_anchored_address (force_const_mem (mode, y));
ypart = operand_subword (y, i, 1, mode);
}
else if (ypart == 0)
ypart = operand_subword_force (y, i, mode);
gcc_assert (xpart && ypart);
need_clobber |= (GET_CODE (xpart) == SUBREG);
last_insn = emit_move_insn (xpart, ypart);
}
seq = get_insns ();
end_sequence ();
if (x != y
&& ! (reload_in_progress || reload_completed)
&& need_clobber != 0)
emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
emit_insn (seq);
return last_insn;
}
rtx
emit_move_insn_1 (rtx x, rtx y)
{
enum machine_mode mode = GET_MODE (x);
enum insn_code code;
gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
code = mov_optab->handlers[mode].insn_code;
if (code != CODE_FOR_nothing)
return emit_insn (GEN_FCN (code) (x, y));
if (COMPLEX_MODE_P (mode))
return emit_move_complex (mode, x, y);
if (GET_MODE_CLASS (mode) == MODE_DECIMAL_FLOAT)
{
rtx result = emit_move_via_integer (mode, x, y, true);
if (result)
return result;
else
return emit_move_multi_word (mode, x, y);
}
if (GET_MODE_CLASS (mode) == MODE_CC)
return emit_move_ccmode (mode, x, y);
if (!CONSTANT_P (y) || GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
{
rtx ret = emit_move_via_integer (mode, x, y, false);
if (ret)
return ret;
}
return emit_move_multi_word (mode, x, y);
}
rtx
emit_move_insn (rtx x, rtx y)
{
enum machine_mode mode = GET_MODE (x);
rtx y_cst = NULL_RTX;
rtx last_insn, set;
gcc_assert (mode != BLKmode
&& (GET_MODE (y) == mode || GET_MODE (y) == VOIDmode));
if (CONSTANT_P (y))
{
if (optimize
&& SCALAR_FLOAT_MODE_P (GET_MODE (x))
&& (last_insn = compress_float_constant (x, y)))
return last_insn;
y_cst = y;
if (!LEGITIMATE_CONSTANT_P (y))
{
y = force_const_mem (mode, y);
if (!y)
y = y_cst;
else
y = use_anchored_address (y);
}
}
if (MEM_P (x)
&& ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
&& ! push_operand (x, GET_MODE (x)))
|| (flag_force_addr
&& CONSTANT_ADDRESS_P (XEXP (x, 0)))))
x = validize_mem (x);
if (MEM_P (y)
&& (! memory_address_p (GET_MODE (y), XEXP (y, 0))
|| (flag_force_addr
&& CONSTANT_ADDRESS_P (XEXP (y, 0)))))
y = validize_mem (y);
gcc_assert (mode != BLKmode);
last_insn = emit_move_insn_1 (x, y);
if (y_cst && REG_P (x)
&& (set = single_set (last_insn)) != NULL_RTX
&& SET_DEST (set) == x
&& ! rtx_equal_p (y_cst, SET_SRC (set)))
set_unique_reg_note (last_insn, REG_EQUAL, y_cst);
return last_insn;
}
static rtx
compress_float_constant (rtx x, rtx y)
{
enum machine_mode dstmode = GET_MODE (x);
enum machine_mode orig_srcmode = GET_MODE (y);
enum machine_mode srcmode;
REAL_VALUE_TYPE r;
int oldcost, newcost;
REAL_VALUE_FROM_CONST_DOUBLE (r, y);
if (LEGITIMATE_CONSTANT_P (y))
oldcost = rtx_cost (y, SET);
else
oldcost = rtx_cost (force_const_mem (dstmode, y), SET);
for (srcmode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (orig_srcmode));
srcmode != orig_srcmode;
srcmode = GET_MODE_WIDER_MODE (srcmode))
{
enum insn_code ic;
rtx trunc_y, last_insn;
ic = can_extend_p (dstmode, srcmode, 0);
if (ic == CODE_FOR_nothing)
continue;
if (! exact_real_truncate (srcmode, &r))
continue;
trunc_y = CONST_DOUBLE_FROM_REAL_VALUE (r, srcmode);
if (LEGITIMATE_CONSTANT_P (trunc_y))
{
if (! (*insn_data[ic].operand[1].predicate) (trunc_y, srcmode))
continue;
newcost = rtx_cost (gen_rtx_FLOAT_EXTEND (dstmode, trunc_y), SET);
if (oldcost < newcost)
continue;
}
else if (float_extend_from_mem[dstmode][srcmode])
{
trunc_y = force_const_mem (srcmode, trunc_y);
newcost = rtx_cost (gen_rtx_FLOAT_EXTEND (dstmode, trunc_y), SET);
if (oldcost < newcost)
continue;
trunc_y = validize_mem (trunc_y);
}
else
continue;
trunc_y = force_reg (srcmode, trunc_y);
emit_unop_insn (ic, x, trunc_y, UNKNOWN);
last_insn = get_last_insn ();
if (REG_P (x))
set_unique_reg_note (last_insn, REG_EQUAL, y);
return last_insn;
}
return NULL_RTX;
}
rtx
push_block (rtx size, int extra, int below)
{
rtx temp;
size = convert_modes (Pmode, ptr_mode, size, 1);
if (CONSTANT_P (size))
anti_adjust_stack (plus_constant (size, extra));
else if (REG_P (size) && extra == 0)
anti_adjust_stack (size);
else
{
temp = copy_to_mode_reg (Pmode, size);
if (extra != 0)
temp = expand_binop (Pmode, add_optab, temp, GEN_INT (extra),
temp, 0, OPTAB_LIB_WIDEN);
anti_adjust_stack (temp);
}
#ifndef STACK_GROWS_DOWNWARD
if (0)
#else
if (1)
#endif
{
temp = virtual_outgoing_args_rtx;
if (extra != 0 && below)
temp = plus_constant (temp, extra);
}
else
{
if (GET_CODE (size) == CONST_INT)
temp = plus_constant (virtual_outgoing_args_rtx,
-INTVAL (size) - (below ? 0 : extra));
else if (extra != 0 && !below)
temp = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
negate_rtx (Pmode, plus_constant (size, extra)));
else
temp = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
negate_rtx (Pmode, size));
}
return memory_address (GET_CLASS_NARROWEST_MODE (MODE_INT), temp);
}
#ifdef PUSH_ROUNDING
static void
emit_single_push_insn (enum machine_mode mode, rtx x, tree type, rtx args_so_far)
{
rtx dest_addr;
unsigned rounded_size = PUSH_ROUNDING (GET_MODE_SIZE (mode));
rtx dest;
enum insn_code icode;
insn_operand_predicate_fn pred;
stack_pointer_delta += PUSH_ROUNDING (GET_MODE_SIZE (mode));
if (args_so_far != NULL_RTX && GET_CODE (args_so_far) == CONST_INT)
{
int offset = INTVAL (args_so_far);
unsigned int unit_stack_boundary = cfun->preferred_stack_boundary / BITS_PER_UNIT;
if (!(offset % unit_stack_boundary) && (stack_pointer_delta % unit_stack_boundary))
{
int delta = unit_stack_boundary - (stack_pointer_delta % unit_stack_boundary);
expand_simple_binop (Pmode, PLUS, stack_pointer_rtx,
GEN_INT (-delta), stack_pointer_rtx, 1, OPTAB_DIRECT);
stack_pointer_delta += delta;
}
}
icode = push_optab->handlers[(int) mode].insn_code;
if (icode != CODE_FOR_nothing)
{
if (((pred = insn_data[(int) icode].operand[0].predicate)
&& !((*pred) (x, mode))))
x = force_reg (mode, x);
emit_insn (GEN_FCN (icode) (x));
return;
}
if (GET_MODE_SIZE (mode) == rounded_size)
dest_addr = gen_rtx_fmt_e (STACK_PUSH_CODE, Pmode, stack_pointer_rtx);
else if (FUNCTION_ARG_PADDING (mode, type) == downward)
{
unsigned padding_size = rounded_size - GET_MODE_SIZE (mode);
HOST_WIDE_INT offset;
emit_move_insn (stack_pointer_rtx,
expand_binop (Pmode,
#ifdef STACK_GROWS_DOWNWARD
sub_optab,
#else
add_optab,
#endif
stack_pointer_rtx,
GEN_INT (rounded_size),
NULL_RTX, 0, OPTAB_LIB_WIDEN));
offset = (HOST_WIDE_INT) padding_size;
#ifdef STACK_GROWS_DOWNWARD
if (STACK_PUSH_CODE == POST_DEC)
offset += (HOST_WIDE_INT) rounded_size;
#else
if (STACK_PUSH_CODE == POST_INC)
offset -= (HOST_WIDE_INT) rounded_size;
#endif
dest_addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
}
else
{
#ifdef STACK_GROWS_DOWNWARD
dest_addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
GEN_INT (-(HOST_WIDE_INT) rounded_size));
#else
dest_addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
GEN_INT (rounded_size));
#endif
dest_addr = gen_rtx_PRE_MODIFY (Pmode, stack_pointer_rtx, dest_addr);
}
dest = gen_rtx_MEM (mode, dest_addr);
if (type != 0)
{
set_mem_attributes (dest, type, 1);
if (flag_optimize_sibling_calls)
set_mem_alias_set (dest, 0);
}
emit_move_insn (dest, x);
}
#endif
void
emit_push_insn (rtx x, enum machine_mode mode, tree type, rtx size,
unsigned int align, int partial, rtx reg, int extra,
rtx args_addr, rtx args_so_far, int reg_parm_stack_space,
rtx alignment_pad)
{
rtx xinner;
enum direction stack_direction
#ifdef STACK_GROWS_DOWNWARD
= downward;
#else
= upward;
#endif
enum direction where_pad = FUNCTION_ARG_PADDING (mode, type);
if (STACK_PUSH_CODE == POST_DEC)
if (where_pad != none)
where_pad = (where_pad == downward ? upward : downward);
xinner = x;
if (mode == BLKmode)
{
rtx temp;
int used;
int offset;
int skip;
offset = partial % (PARM_BOUNDARY / BITS_PER_UNIT);
used = partial - offset;
gcc_assert (size);
if (partial != 0)
xinner = adjust_address (xinner, BLKmode, used);
skip = (reg_parm_stack_space == 0) ? 0 : used;
#ifdef PUSH_ROUNDING
if (args_addr == 0
&& PUSH_ARGS
&& GET_CODE (size) == CONST_INT
&& skip == 0
&& MEM_ALIGN (xinner) >= align
&& (MOVE_BY_PIECES_P ((unsigned) INTVAL (size) - used, align))
&& ((! SLOW_UNALIGNED_ACCESS (word_mode, align))
|| align >= BIGGEST_ALIGNMENT
|| (PUSH_ROUNDING (align / BITS_PER_UNIT)
== (align / BITS_PER_UNIT)))
&& PUSH_ROUNDING (INTVAL (size)) == INTVAL (size))
{
if (extra && args_addr == 0
&& where_pad != none && where_pad != stack_direction)
anti_adjust_stack (GEN_INT (extra));
move_by_pieces (NULL, xinner, INTVAL (size) - used, align, 0);
}
else
#endif
{
rtx target;
if (partial != 0)
{
if (GET_CODE (size) == CONST_INT)
size = GEN_INT (INTVAL (size) - used);
else
size = expand_binop (GET_MODE (size), sub_optab, size,
GEN_INT (used), NULL_RTX, 0,
OPTAB_LIB_WIDEN);
}
if (! args_addr)
{
temp = push_block (size, extra, where_pad == downward);
extra = 0;
}
else if (GET_CODE (args_so_far) == CONST_INT)
temp = memory_address (BLKmode,
plus_constant (args_addr,
skip + INTVAL (args_so_far)));
else
temp = memory_address (BLKmode,
plus_constant (gen_rtx_PLUS (Pmode,
args_addr,
args_so_far),
skip));
if (!ACCUMULATE_OUTGOING_ARGS)
{
if (reg_mentioned_p (virtual_stack_dynamic_rtx, temp)
|| reg_mentioned_p (virtual_outgoing_args_rtx, temp))
temp = copy_to_reg (temp);
}
target = gen_rtx_MEM (BLKmode, temp);
set_mem_align (target, align);
emit_block_move (target, xinner, size, BLOCK_OP_CALL_PARM);
}
}
else if (partial > 0)
{
int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD;
int i;
int not_stack;
int offset = partial % (PARM_BOUNDARY / BITS_PER_UNIT);
int args_offset = INTVAL (args_so_far);
int skip;
if (extra && args_addr == 0
&& where_pad != none && where_pad != stack_direction)
anti_adjust_stack (GEN_INT (extra));
if (args_addr == 0)
offset = 0;
not_stack = (partial - offset) / UNITS_PER_WORD;
offset /= UNITS_PER_WORD;
skip = (reg_parm_stack_space == 0) ? 0 : not_stack;
if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x))
x = validize_mem (force_const_mem (mode, x));
if ((REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER
&& GET_MODE_CLASS (GET_MODE (x)) != MODE_INT))
x = copy_to_reg (x);
#ifndef PUSH_ARGS_REVERSED
for (i = not_stack; i < size; i++)
#else
for (i = size - 1; i >= not_stack; i--)
#endif
if (i >= not_stack + offset)
emit_push_insn (operand_subword_force (x, i, mode),
word_mode, NULL_TREE, NULL_RTX, align, 0, NULL_RTX,
0, args_addr,
GEN_INT (args_offset + ((i - not_stack + skip)
* UNITS_PER_WORD)),
reg_parm_stack_space, alignment_pad);
}
else
{
rtx addr;
rtx dest;
if (extra && args_addr == 0
&& where_pad != none && where_pad != stack_direction)
anti_adjust_stack (GEN_INT (extra));
#ifdef PUSH_ROUNDING
if (args_addr == 0 && PUSH_ARGS)
emit_single_push_insn (mode, x, type, args_so_far);
else
#endif
{
if (GET_CODE (args_so_far) == CONST_INT)
addr
= memory_address (mode,
plus_constant (args_addr,
INTVAL (args_so_far)));
else
addr = memory_address (mode, gen_rtx_PLUS (Pmode, args_addr,
args_so_far));
dest = gen_rtx_MEM (mode, addr);
set_mem_align (dest, align);
emit_move_insn (dest, x);
}
}
if (partial > 0 && reg != 0)
{
if (GET_CODE (reg) == PARALLEL)
emit_group_load (reg, x, type, -1);
else
{
gcc_assert (partial % UNITS_PER_WORD == 0);
move_block_to_reg (REGNO (reg), x, partial / UNITS_PER_WORD, mode);
}
}
if (extra && args_addr == 0 && where_pad == stack_direction)
anti_adjust_stack (GEN_INT (extra));
if (alignment_pad && args_addr == 0)
anti_adjust_stack (alignment_pad);
}
static rtx
get_subtarget (rtx x)
{
return (optimize
|| x == 0
|| !REG_P (x)
|| REGNO (x) < FIRST_PSEUDO_REGISTER
? 0 : x);
}
static bool
optimize_bitfield_assignment_op (unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitpos,
enum machine_mode mode1, rtx str_rtx,
tree to, tree src)
{
enum machine_mode str_mode = GET_MODE (str_rtx);
unsigned int str_bitsize = GET_MODE_BITSIZE (str_mode);
tree op0, op1;
rtx value, result;
optab binop;
if (mode1 != VOIDmode
|| bitsize >= BITS_PER_WORD
|| str_bitsize > BITS_PER_WORD
|| TREE_SIDE_EFFECTS (to)
|| TREE_THIS_VOLATILE (to))
return false;
STRIP_NOPS (src);
if (!BINARY_CLASS_P (src)
|| TREE_CODE (TREE_TYPE (src)) != INTEGER_TYPE)
return false;
op0 = TREE_OPERAND (src, 0);
op1 = TREE_OPERAND (src, 1);
STRIP_NOPS (op0);
if (!operand_equal_p (to, op0, 0))
return false;
if (MEM_P (str_rtx))
{
unsigned HOST_WIDE_INT offset1;
if (str_bitsize == 0 || str_bitsize > BITS_PER_WORD)
str_mode = word_mode;
str_mode = get_best_mode (bitsize, bitpos,
MEM_ALIGN (str_rtx), str_mode, 0);
if (str_mode == VOIDmode)
return false;
str_bitsize = GET_MODE_BITSIZE (str_mode);
offset1 = bitpos;
bitpos %= str_bitsize;
offset1 = (offset1 - bitpos) / BITS_PER_UNIT;
str_rtx = adjust_address (str_rtx, str_mode, offset1);
}
else if (!REG_P (str_rtx) && GET_CODE (str_rtx) != SUBREG)
return false;
if (bitsize >= str_bitsize)
return false;
if (bitpos + bitsize > str_bitsize)
return false;
if (BYTES_BIG_ENDIAN)
bitpos = str_bitsize - bitpos - bitsize;
switch (TREE_CODE (src))
{
case PLUS_EXPR:
case MINUS_EXPR:
if (bitpos + bitsize != str_bitsize
&& (bitsize != 1 || TREE_CODE (op1) != INTEGER_CST))
break;
value = expand_expr (op1, NULL_RTX, str_mode, 0);
value = convert_modes (str_mode,
TYPE_MODE (TREE_TYPE (op1)), value,
TYPE_UNSIGNED (TREE_TYPE (op1)));
if (MEM_P (str_rtx))
{
str_rtx = shallow_copy_rtx (str_rtx);
set_mem_alias_set (str_rtx, 0);
set_mem_expr (str_rtx, 0);
}
binop = TREE_CODE (src) == PLUS_EXPR ? add_optab : sub_optab;
if (bitsize == 1 && bitpos + bitsize != str_bitsize)
{
value = expand_and (str_mode, value, const1_rtx, NULL);
binop = xor_optab;
}
value = expand_shift (LSHIFT_EXPR, str_mode, value,
build_int_cst (NULL_TREE, bitpos),
NULL_RTX, 1);
result = expand_binop (str_mode, binop, str_rtx,
value, str_rtx, 1, OPTAB_WIDEN);
if (result != str_rtx)
emit_move_insn (str_rtx, result);
return true;
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
if (TREE_CODE (op1) != INTEGER_CST)
break;
value = expand_expr (op1, NULL_RTX, GET_MODE (str_rtx), 0);
value = convert_modes (GET_MODE (str_rtx),
TYPE_MODE (TREE_TYPE (op1)), value,
TYPE_UNSIGNED (TREE_TYPE (op1)));
if (MEM_P (str_rtx))
{
str_rtx = shallow_copy_rtx (str_rtx);
set_mem_alias_set (str_rtx, 0);
set_mem_expr (str_rtx, 0);
}
binop = TREE_CODE (src) == BIT_IOR_EXPR ? ior_optab : xor_optab;
if (bitpos + bitsize != GET_MODE_BITSIZE (GET_MODE (str_rtx)))
{
rtx mask = GEN_INT (((unsigned HOST_WIDE_INT) 1 << bitsize)
- 1);
value = expand_and (GET_MODE (str_rtx), value, mask,
NULL_RTX);
}
value = expand_shift (LSHIFT_EXPR, GET_MODE (str_rtx), value,
build_int_cst (NULL_TREE, bitpos),
NULL_RTX, 1);
result = expand_binop (GET_MODE (str_rtx), binop, str_rtx,
value, str_rtx, 1, OPTAB_WIDEN);
if (result != str_rtx)
emit_move_insn (str_rtx, result);
return true;
default:
break;
}
return false;
}
void
expand_assignment (tree to, tree from)
{
rtx to_rtx = 0;
rtx result;
if (TREE_CODE (to) == ERROR_MARK)
{
result = expand_normal (from);
return;
}
if (operand_equal_p (to, from, 0))
return;
if (handled_component_p (to)
|| TREE_CODE (TREE_TYPE (to)) == ARRAY_TYPE)
{
enum machine_mode mode1;
HOST_WIDE_INT bitsize, bitpos;
tree offset;
int unsignedp;
int volatilep = 0;
tree tem;
push_temp_slots ();
tem = get_inner_reference (to, &bitsize, &bitpos, &offset, &mode1,
&unsignedp, &volatilep, true);
to_rtx = expand_normal (tem);
if (offset != 0)
{
rtx offset_rtx;
if (!MEM_P (to_rtx))
{
gcc_assert (TREE_CODE (offset) == INTEGER_CST);
expand_builtin_trap ();
to_rtx = gen_rtx_MEM (BLKmode, const0_rtx);
}
offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, EXPAND_SUM);
#ifdef POINTERS_EXTEND_UNSIGNED
if (GET_MODE (offset_rtx) != Pmode)
offset_rtx = convert_to_mode (Pmode, offset_rtx, 0);
#else
if (GET_MODE (offset_rtx) != ptr_mode)
offset_rtx = convert_to_mode (ptr_mode, offset_rtx, 0);
#endif
if (MEM_P (to_rtx)
&& GET_MODE (to_rtx) == BLKmode
&& GET_MODE (XEXP (to_rtx, 0)) != VOIDmode
&& bitsize > 0
&& (bitpos % bitsize) == 0
&& (bitsize % GET_MODE_ALIGNMENT (mode1)) == 0
&& MEM_ALIGN (to_rtx) == GET_MODE_ALIGNMENT (mode1))
{
to_rtx = adjust_address (to_rtx, mode1, bitpos / BITS_PER_UNIT);
bitpos = 0;
}
to_rtx = offset_address (to_rtx, offset_rtx,
highest_pow2_factor_for_target (to,
offset));
}
if (GET_CODE (to_rtx) == CONCAT)
{
if (TREE_CODE (TREE_TYPE (from)) == COMPLEX_TYPE)
{
gcc_assert (bitpos == 0);
result = store_expr (from, to_rtx, false);
}
else
{
gcc_assert (bitpos == 0 || bitpos == GET_MODE_BITSIZE (mode1));
result = store_expr (from, XEXP (to_rtx, bitpos != 0), false);
}
}
else
{
if (MEM_P (to_rtx))
{
to_rtx = shallow_copy_rtx (to_rtx);
set_mem_attributes_minus_bitpos (to_rtx, to, 0, bitpos);
if (volatilep)
MEM_VOLATILE_P (to_rtx) = 1;
if (component_uses_parent_alias_set (to))
MEM_KEEP_ALIAS_SET_P (to_rtx) = 1;
}
if (optimize_bitfield_assignment_op (bitsize, bitpos, mode1,
to_rtx, to, from))
result = NULL;
else
result = store_field (to_rtx, bitsize, bitpos, mode1, from,
TREE_TYPE (tem), get_alias_set (to));
}
if (result)
preserve_temp_slots (result);
free_temp_slots ();
pop_temp_slots ();
return;
}
if (TREE_CODE (from) == CALL_EXPR && ! aggregate_value_p (from, from)
&& TREE_CODE (TYPE_SIZE (TREE_TYPE (from))) == INTEGER_CST
&& ! ((TREE_CODE (to) == VAR_DECL || TREE_CODE (to) == PARM_DECL)
&& REG_P (DECL_RTL (to))))
{
rtx value;
push_temp_slots ();
value = expand_normal (from);
if (to_rtx == 0)
to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);
if (GET_CODE (to_rtx) == PARALLEL)
emit_group_load (to_rtx, value, TREE_TYPE (from),
int_size_in_bytes (TREE_TYPE (from)));
else if (GET_MODE (to_rtx) == BLKmode)
emit_block_move (to_rtx, value, expr_size (from), BLOCK_OP_NORMAL);
else
{
if (POINTER_TYPE_P (TREE_TYPE (to)))
value = convert_memory_address (GET_MODE (to_rtx), value);
emit_move_insn (to_rtx, value);
}
preserve_temp_slots (to_rtx);
free_temp_slots ();
pop_temp_slots ();
return;
}
if (to_rtx == 0)
to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);
if (TREE_CODE (to) == RESULT_DECL
&& (REG_P (to_rtx) || GET_CODE (to_rtx) == PARALLEL))
{
rtx temp;
push_temp_slots ();
temp = expand_expr (from, 0, GET_MODE (to_rtx), 0);
if (GET_CODE (to_rtx) == PARALLEL)
emit_group_load (to_rtx, temp, TREE_TYPE (from),
int_size_in_bytes (TREE_TYPE (from)));
else
emit_move_insn (to_rtx, temp);
preserve_temp_slots (to_rtx);
free_temp_slots ();
pop_temp_slots ();
return;
}
if (TREE_CODE (to) == RESULT_DECL && TREE_CODE (from) == INDIRECT_REF
&& current_function_returns_struct
&& !current_function_returns_pcc_struct)
{
rtx from_rtx, size;
push_temp_slots ();
size = expr_size (from);
from_rtx = expand_normal (from);
emit_library_call (memmove_libfunc, LCT_NORMAL,
VOIDmode, 3, XEXP (to_rtx, 0), Pmode,
XEXP (from_rtx, 0), Pmode,
convert_to_mode (TYPE_MODE (sizetype),
size, TYPE_UNSIGNED (sizetype)),
TYPE_MODE (sizetype));
preserve_temp_slots (to_rtx);
free_temp_slots ();
pop_temp_slots ();
return;
}
push_temp_slots ();
result = store_expr (from, to_rtx, 0);
preserve_temp_slots (result);
free_temp_slots ();
pop_temp_slots ();
return;
}
rtx
store_expr (tree exp, rtx target, int call_param_p)
{
rtx temp;
rtx alt_rtl = NULL_RTX;
int dont_return_target = 0;
if (VOID_TYPE_P (TREE_TYPE (exp)))
{
gcc_assert (!call_param_p);
expand_expr (exp, const0_rtx, VOIDmode, 0);
return NULL_RTX;
}
if (TREE_CODE (exp) == COMPOUND_EXPR)
{
expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
return store_expr (TREE_OPERAND (exp, 1), target, call_param_p);
}
else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
{
rtx lab1 = gen_label_rtx (), lab2 = gen_label_rtx ();
do_pending_stack_adjust ();
NO_DEFER_POP;
jumpifnot (TREE_OPERAND (exp, 0), lab1);
store_expr (TREE_OPERAND (exp, 1), target, call_param_p);
emit_jump_insn (gen_jump (lab2));
emit_barrier ();
emit_label (lab1);
store_expr (TREE_OPERAND (exp, 2), target, call_param_p);
emit_label (lab2);
OK_DEFER_POP;
return NULL_RTX;
}
else if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
{
rtx inner_target = 0;
if (INTEGRAL_TYPE_P (TREE_TYPE (exp))
&& TREE_TYPE (TREE_TYPE (exp)) == 0
&& (!lang_hooks.reduce_bit_field_operations
|| (GET_MODE_PRECISION (GET_MODE (target))
== TYPE_PRECISION (TREE_TYPE (exp)))))
{
if (TYPE_UNSIGNED (TREE_TYPE (exp))
!= SUBREG_PROMOTED_UNSIGNED_P (target))
exp = fold_convert
(lang_hooks.types.signed_or_unsigned_type
(SUBREG_PROMOTED_UNSIGNED_P (target), TREE_TYPE (exp)), exp);
exp = fold_convert (lang_hooks.types.type_for_mode
(GET_MODE (SUBREG_REG (target)),
SUBREG_PROMOTED_UNSIGNED_P (target)),
exp);
inner_target = SUBREG_REG (target);
}
temp = expand_expr (exp, inner_target, VOIDmode,
call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
{
temp = convert_modes (GET_MODE (target), TYPE_MODE (TREE_TYPE (exp)),
temp, SUBREG_PROMOTED_UNSIGNED_P (target));
temp = convert_modes (GET_MODE (SUBREG_REG (target)),
GET_MODE (target), temp,
SUBREG_PROMOTED_UNSIGNED_P (target));
}
convert_move (SUBREG_REG (target), temp,
SUBREG_PROMOTED_UNSIGNED_P (target));
return NULL_RTX;
}
else
{
temp = expand_expr_real (exp, target, GET_MODE (target),
(call_param_p
? EXPAND_STACK_PARM : EXPAND_NORMAL),
&alt_rtl);
if (!(target && REG_P (target)
&& REGNO (target) < FIRST_PSEUDO_REGISTER)
&& !(MEM_P (target) && MEM_VOLATILE_P (target))
&& ! rtx_equal_p (temp, target)
&& CONSTANT_P (temp))
dont_return_target = 1;
}
if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode
&& TREE_CODE (exp) != ERROR_MARK
&& GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
temp = convert_modes (GET_MODE (target), TYPE_MODE (TREE_TYPE (exp)),
temp, TYPE_UNSIGNED (TREE_TYPE (exp)));
if ((! rtx_equal_p (temp, target)
|| (temp != target && (side_effects_p (temp)
|| side_effects_p (target))))
&& TREE_CODE (exp) != ERROR_MARK
&& !(alt_rtl && rtx_equal_p (alt_rtl, target))
&& expr_size (exp) != const0_rtx)
{
if (GET_MODE (temp) != GET_MODE (target)
&& GET_MODE (temp) != VOIDmode)
{
int unsignedp = TYPE_UNSIGNED (TREE_TYPE (exp));
if (dont_return_target)
{
temp = convert_to_mode (GET_MODE (target), temp, unsignedp);
emit_move_insn (target, temp);
}
else if (GET_MODE (target) == BLKmode)
emit_block_move (target, temp, expr_size (exp),
(call_param_p
? BLOCK_OP_CALL_PARM
: BLOCK_OP_NORMAL));
else
convert_move (target, temp, unsignedp);
}
else if (GET_MODE (temp) == BLKmode && TREE_CODE (exp) == STRING_CST)
{
rtx size = expr_size (exp);
if (GET_CODE (size) == CONST_INT
&& INTVAL (size) < TREE_STRING_LENGTH (exp))
emit_block_move (target, temp, size,
(call_param_p
? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
else
{
tree copy_size
= size_binop (MIN_EXPR,
make_tree (sizetype, size),
size_int (TREE_STRING_LENGTH (exp)));
rtx copy_size_rtx
= expand_expr (copy_size, NULL_RTX, VOIDmode,
(call_param_p
? EXPAND_STACK_PARM : EXPAND_NORMAL));
rtx label = 0;
copy_size_rtx = convert_to_mode (ptr_mode, copy_size_rtx,
TYPE_UNSIGNED (sizetype));
emit_block_move (target, temp, copy_size_rtx,
(call_param_p
? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
if (GET_CODE (copy_size_rtx) == CONST_INT)
{
size = plus_constant (size, -INTVAL (copy_size_rtx));
target = adjust_address (target, BLKmode,
INTVAL (copy_size_rtx));
}
else
{
size = expand_binop (TYPE_MODE (sizetype), sub_optab, size,
copy_size_rtx, NULL_RTX, 0,
OPTAB_LIB_WIDEN);
#ifdef POINTERS_EXTEND_UNSIGNED
if (GET_MODE (copy_size_rtx) != Pmode)
copy_size_rtx = convert_to_mode (Pmode, copy_size_rtx,
TYPE_UNSIGNED (sizetype));
#endif
target = offset_address (target, copy_size_rtx,
highest_pow2_factor (copy_size));
label = gen_label_rtx ();
emit_cmp_and_jump_insns (size, const0_rtx, LT, NULL_RTX,
GET_MODE (size), 0, label);
}
if (size != const0_rtx)
clear_storage (target, size, BLOCK_OP_NORMAL);
if (label)
emit_label (label);
}
}
else if (GET_CODE (target) == PARALLEL)
emit_group_load (target, temp, TREE_TYPE (exp),
int_size_in_bytes (TREE_TYPE (exp)));
else if (GET_MODE (temp) == BLKmode)
emit_block_move (target, temp, expr_size (exp),
(call_param_p
? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
else
{
temp = force_operand (temp, target);
if (temp != target)
emit_move_insn (target, temp);
}
}
return NULL_RTX;
}
static bool
categorize_ctor_elements_1 (tree ctor, HOST_WIDE_INT *p_nz_elts,
HOST_WIDE_INT *p_elt_count,
bool *p_must_clear)
{
unsigned HOST_WIDE_INT idx;
HOST_WIDE_INT nz_elts, elt_count;
tree value, purpose;
bool const_from_elts_p = constructor_static_from_elts_p (ctor);
bool const_p = const_from_elts_p ? true : TREE_STATIC (ctor);
nz_elts = 0;
elt_count = 0;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), idx, purpose, value)
{
HOST_WIDE_INT mult;
mult = 1;
if (purpose && TREE_CODE (purpose) == RANGE_EXPR)
{
tree lo_index = TREE_OPERAND (purpose, 0);
tree hi_index = TREE_OPERAND (purpose, 1);
if (host_integerp (lo_index, 1) && host_integerp (hi_index, 1))
mult = (tree_low_cst (hi_index, 1)
- tree_low_cst (lo_index, 1) + 1);
}
switch (TREE_CODE (value))
{
case CONSTRUCTOR:
{
HOST_WIDE_INT nz = 0, ic = 0;
bool const_elt_p
= categorize_ctor_elements_1 (value, &nz, &ic, p_must_clear);
nz_elts += mult * nz;
elt_count += mult * ic;
if (const_from_elts_p && const_p)
const_p = const_elt_p;
}
break;
case INTEGER_CST:
case REAL_CST:
if (!initializer_zerop (value))
nz_elts += mult;
elt_count += mult;
break;
case STRING_CST:
nz_elts += mult * TREE_STRING_LENGTH (value);
elt_count += mult * TREE_STRING_LENGTH (value);
break;
case COMPLEX_CST:
if (!initializer_zerop (TREE_REALPART (value)))
nz_elts += mult;
if (!initializer_zerop (TREE_IMAGPART (value)))
nz_elts += mult;
elt_count += mult;
break;
case VECTOR_CST:
{
tree v;
for (v = TREE_VECTOR_CST_ELTS (value); v; v = TREE_CHAIN (v))
{
if (!initializer_zerop (TREE_VALUE (v)))
nz_elts += mult;
elt_count += mult;
}
}
break;
default:
nz_elts += mult;
elt_count += mult;
if (const_from_elts_p && const_p)
const_p = initializer_constant_valid_p (value, TREE_TYPE (value))
!= NULL_TREE;
break;
}
}
if (!*p_must_clear
&& (TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE
|| TREE_CODE (TREE_TYPE (ctor)) == QUAL_UNION_TYPE))
{
tree init_sub_type;
bool clear_this = true;
if (!VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (ctor)))
{
gcc_assert (VEC_length (constructor_elt, CONSTRUCTOR_ELTS (ctor))
== 1);
init_sub_type = TREE_TYPE (VEC_index (constructor_elt,
CONSTRUCTOR_ELTS (ctor),
0)->value);
if (simple_cst_equal (TYPE_SIZE (TREE_TYPE (ctor)),
TYPE_SIZE (init_sub_type)) == 1)
{
if (elt_count == count_type_elements (init_sub_type, false))
clear_this = false;
}
}
*p_must_clear = clear_this;
}
*p_nz_elts += nz_elts;
*p_elt_count += elt_count;
return const_p;
}
bool
categorize_ctor_elements (tree ctor, HOST_WIDE_INT *p_nz_elts,
HOST_WIDE_INT *p_elt_count,
bool *p_must_clear)
{
*p_nz_elts = 0;
*p_elt_count = 0;
*p_must_clear = false;
return
categorize_ctor_elements_1 (ctor, p_nz_elts, p_elt_count, p_must_clear);
}
HOST_WIDE_INT
count_type_elements (tree type, bool allow_flexarr)
{
const HOST_WIDE_INT max = ~((HOST_WIDE_INT)1 << (HOST_BITS_PER_WIDE_INT-1));
switch (TREE_CODE (type))
{
case ARRAY_TYPE:
{
tree telts = array_type_nelts (type);
if (telts && host_integerp (telts, 1))
{
HOST_WIDE_INT n = tree_low_cst (telts, 1) + 1;
HOST_WIDE_INT m = count_type_elements (TREE_TYPE (type), false);
if (n == 0)
return 0;
else if (max / n > m)
return n * m;
}
return -1;
}
case RECORD_TYPE:
{
HOST_WIDE_INT n = 0, t;
tree f;
for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
if (TREE_CODE (f) == FIELD_DECL)
{
t = count_type_elements (TREE_TYPE (f), false);
if (t < 0)
{
tree tf = TREE_TYPE (f);
if (allow_flexarr
&& TREE_CHAIN (f) == NULL
&& TREE_CODE (tf) == ARRAY_TYPE
&& TYPE_DOMAIN (tf)
&& TYPE_MIN_VALUE (TYPE_DOMAIN (tf))
&& integer_zerop (TYPE_MIN_VALUE (TYPE_DOMAIN (tf)))
&& !TYPE_MAX_VALUE (TYPE_DOMAIN (tf))
&& int_size_in_bytes (type) >= 0)
break;
return -1;
}
n += t;
}
return n;
}
case UNION_TYPE:
case QUAL_UNION_TYPE:
{
HOST_WIDE_INT n = int_size_in_bytes (type);
if (n < 0)
return -1;
return n / UNITS_PER_WORD;
}
case COMPLEX_TYPE:
return 2;
case VECTOR_TYPE:
return TYPE_VECTOR_SUBPARTS (type);
case INTEGER_TYPE:
case REAL_TYPE:
case ENUMERAL_TYPE:
case BOOLEAN_TYPE:
case POINTER_TYPE:
case BLOCK_POINTER_TYPE:
case OFFSET_TYPE:
case REFERENCE_TYPE:
return 1;
case VOID_TYPE:
case METHOD_TYPE:
case FUNCTION_TYPE:
case LANG_TYPE:
default:
gcc_unreachable ();
}
}
static int
mostly_zeros_p (tree exp)
{
if (TREE_CODE (exp) == CONSTRUCTOR)
{
HOST_WIDE_INT nz_elts, count, elts;
bool must_clear;
categorize_ctor_elements (exp, &nz_elts, &count, &must_clear);
if (must_clear)
return 1;
elts = count_type_elements (TREE_TYPE (exp), false);
return nz_elts < elts / 4;
}
return initializer_zerop (exp);
}
static int
all_zeros_p (tree exp)
{
if (TREE_CODE (exp) == CONSTRUCTOR)
{
HOST_WIDE_INT nz_elts, count;
bool must_clear;
categorize_ctor_elements (exp, &nz_elts, &count, &must_clear);
return nz_elts == 0;
}
return initializer_zerop (exp);
}
static void
store_constructor_field (rtx target, unsigned HOST_WIDE_INT bitsize,
HOST_WIDE_INT bitpos, enum machine_mode mode,
tree exp, tree type, int cleared, int alias_set)
{
if (TREE_CODE (exp) == CONSTRUCTOR
&& bitpos % BITS_PER_UNIT == 0
&& (bitsize > 0 && bitsize % BITS_PER_UNIT == 0)
&& (bitpos == 0 || MEM_P (target)))
{
if (MEM_P (target))
target
= adjust_address (target,
GET_MODE (target) == BLKmode
|| 0 != (bitpos
% GET_MODE_ALIGNMENT (GET_MODE (target)))
? BLKmode : VOIDmode, bitpos / BITS_PER_UNIT);
if (MEM_P (target) && ! MEM_KEEP_ALIAS_SET_P (target)
&& MEM_ALIAS_SET (target) != 0)
{
target = copy_rtx (target);
set_mem_alias_set (target, alias_set);
}
store_constructor (exp, target, cleared, bitsize / BITS_PER_UNIT);
}
else
store_field (target, bitsize, bitpos, mode, exp, type, alias_set);
}
static void
store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
{
tree type = TREE_TYPE (exp);
#ifdef WORD_REGISTER_OPERATIONS
HOST_WIDE_INT exp_size = int_size_in_bytes (type);
#endif
switch (TREE_CODE (type))
{
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
{
unsigned HOST_WIDE_INT idx;
tree field, value;
if (size == 0 || cleared)
cleared = 1;
else if ((TREE_CODE (type) == UNION_TYPE
|| TREE_CODE (type) == QUAL_UNION_TYPE)
&& ! CONSTRUCTOR_ELTS (exp))
{
clear_storage (target, expr_size (exp), BLOCK_OP_NORMAL);
cleared = 1;
}
else if (REG_P (target) && TREE_STATIC (exp)
&& GET_MODE_SIZE (GET_MODE (target)) <= UNITS_PER_WORD)
{
emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
cleared = 1;
}
else if (size > 0
&& (((int)VEC_length (constructor_elt, CONSTRUCTOR_ELTS (exp))
!= fields_length (type))
|| mostly_zeros_p (exp))
&& (!REG_P (target)
|| ((HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (target))
== size)))
{
clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
cleared = 1;
}
if (! cleared)
emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), idx, field, value)
{
enum machine_mode mode;
HOST_WIDE_INT bitsize;
HOST_WIDE_INT bitpos = 0;
tree offset;
rtx to_rtx = target;
if (field == 0)
continue;
if (cleared && initializer_zerop (value))
continue;
if (host_integerp (DECL_SIZE (field), 1))
bitsize = tree_low_cst (DECL_SIZE (field), 1);
else
bitsize = -1;
mode = DECL_MODE (field);
if (DECL_BIT_FIELD (field))
mode = VOIDmode;
offset = DECL_FIELD_OFFSET (field);
if (host_integerp (offset, 0)
&& host_integerp (bit_position (field), 0))
{
bitpos = int_bit_position (field);
offset = 0;
}
else
bitpos = tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 0);
if (offset)
{
rtx offset_rtx;
offset
= SUBSTITUTE_PLACEHOLDER_IN_EXPR (offset,
make_tree (TREE_TYPE (exp),
target));
offset_rtx = expand_normal (offset);
gcc_assert (MEM_P (to_rtx));
#ifdef POINTERS_EXTEND_UNSIGNED
if (GET_MODE (offset_rtx) != Pmode)
offset_rtx = convert_to_mode (Pmode, offset_rtx, 0);
#else
if (GET_MODE (offset_rtx) != ptr_mode)
offset_rtx = convert_to_mode (ptr_mode, offset_rtx, 0);
#endif
to_rtx = offset_address (to_rtx, offset_rtx,
highest_pow2_factor (offset));
}
#ifdef WORD_REGISTER_OPERATIONS
if (REG_P (target)
&& bitsize < BITS_PER_WORD
&& bitpos % BITS_PER_WORD == 0
&& GET_MODE_CLASS (mode) == MODE_INT
&& TREE_CODE (value) == INTEGER_CST
&& exp_size >= 0
&& bitpos + BITS_PER_WORD <= exp_size * BITS_PER_UNIT)
{
tree type = TREE_TYPE (value);
if (TYPE_PRECISION (type) < BITS_PER_WORD)
{
type = lang_hooks.types.type_for_size
(BITS_PER_WORD, TYPE_UNSIGNED (type));
value = fold_convert (type, value);
}
if (BYTES_BIG_ENDIAN)
value
= fold_build2 (LSHIFT_EXPR, type, value,
build_int_cst (type,
BITS_PER_WORD - bitsize));
bitsize = BITS_PER_WORD;
mode = word_mode;
}
#endif
if (MEM_P (to_rtx) && !MEM_KEEP_ALIAS_SET_P (to_rtx)
&& DECL_NONADDRESSABLE_P (field))
{
to_rtx = copy_rtx (to_rtx);
MEM_KEEP_ALIAS_SET_P (to_rtx) = 1;
}
store_constructor_field (to_rtx, bitsize, bitpos, mode,
value, type, cleared,
get_alias_set (TREE_TYPE (field)));
}
break;
}
case ARRAY_TYPE:
{
tree value, index;
unsigned HOST_WIDE_INT i;
int need_to_clear;
tree domain;
tree elttype = TREE_TYPE (type);
int const_bounds_p;
HOST_WIDE_INT minelt = 0;
HOST_WIDE_INT maxelt = 0;
domain = TYPE_DOMAIN (type);
const_bounds_p = (TYPE_MIN_VALUE (domain)
&& TYPE_MAX_VALUE (domain)
&& host_integerp (TYPE_MIN_VALUE (domain), 0)
&& host_integerp (TYPE_MAX_VALUE (domain), 0));
if (const_bounds_p)
{
minelt = tree_low_cst (TYPE_MIN_VALUE (domain), 0);
maxelt = tree_low_cst (TYPE_MAX_VALUE (domain), 0);
}
if (cleared)
need_to_clear = 0;
else if (REG_P (target) && TREE_STATIC (exp))
need_to_clear = 1;
else
{
unsigned HOST_WIDE_INT idx;
tree index, value;
HOST_WIDE_INT count = 0, zero_count = 0;
need_to_clear = ! const_bounds_p;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), idx, index, value)
{
HOST_WIDE_INT this_node_count;
if (need_to_clear)
break;
if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR)
{
tree lo_index = TREE_OPERAND (index, 0);
tree hi_index = TREE_OPERAND (index, 1);
if (! host_integerp (lo_index, 1)
|| ! host_integerp (hi_index, 1))
{
need_to_clear = 1;
break;
}
this_node_count = (tree_low_cst (hi_index, 1)
- tree_low_cst (lo_index, 1) + 1);
}
else
this_node_count = 1;
count += this_node_count;
if (mostly_zeros_p (value))
zero_count += this_node_count;
}
if (! need_to_clear
&& (count < maxelt - minelt + 1
|| 4 * zero_count >= 3 * count))
need_to_clear = 1;
}
if (need_to_clear && size > 0)
{
if (REG_P (target))
emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
else
clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
cleared = 1;
}
if (!cleared && REG_P (target))
emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), i, index, value)
{
enum machine_mode mode;
HOST_WIDE_INT bitsize;
HOST_WIDE_INT bitpos;
int unsignedp;
rtx xtarget = target;
if (cleared && initializer_zerop (value))
continue;
unsignedp = TYPE_UNSIGNED (elttype);
mode = TYPE_MODE (elttype);
if (mode == BLKmode)
bitsize = (host_integerp (TYPE_SIZE (elttype), 1)
? tree_low_cst (TYPE_SIZE (elttype), 1)
: -1);
else
bitsize = GET_MODE_BITSIZE (mode);
if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR)
{
tree lo_index = TREE_OPERAND (index, 0);
tree hi_index = TREE_OPERAND (index, 1);
rtx index_r, pos_rtx;
HOST_WIDE_INT lo, hi, count;
tree position;
if (const_bounds_p
&& host_integerp (lo_index, 0)
&& host_integerp (hi_index, 0)
&& (lo = tree_low_cst (lo_index, 0),
hi = tree_low_cst (hi_index, 0),
count = hi - lo + 1,
(!MEM_P (target)
|| count <= 2
|| (host_integerp (TYPE_SIZE (elttype), 1)
&& (tree_low_cst (TYPE_SIZE (elttype), 1) * count
<= 40 * 8)))))
{
lo -= minelt; hi -= minelt;
for (; lo <= hi; lo++)
{
bitpos = lo * tree_low_cst (TYPE_SIZE (elttype), 0);
if (MEM_P (target)
&& !MEM_KEEP_ALIAS_SET_P (target)
&& TREE_CODE (type) == ARRAY_TYPE
&& TYPE_NONALIASED_COMPONENT (type))
{
target = copy_rtx (target);
MEM_KEEP_ALIAS_SET_P (target) = 1;
}
store_constructor_field
(target, bitsize, bitpos, mode, value, type, cleared,
get_alias_set (elttype));
}
}
else
{
rtx loop_start = gen_label_rtx ();
rtx loop_end = gen_label_rtx ();
tree exit_cond;
expand_normal (hi_index);
unsignedp = TYPE_UNSIGNED (domain);
index = build_decl (VAR_DECL, NULL_TREE, domain);
index_r
= gen_reg_rtx (promote_mode (domain, DECL_MODE (index),
&unsignedp, 0));
SET_DECL_RTL (index, index_r);
store_expr (lo_index, index_r, 0);
do_pending_stack_adjust ();
emit_label (loop_start);
position =
fold_convert (ssizetype,
fold_build2 (MINUS_EXPR,
TREE_TYPE (index),
index,
TYPE_MIN_VALUE (domain)));
position =
size_binop (MULT_EXPR, position,
fold_convert (ssizetype,
TYPE_SIZE_UNIT (elttype)));
pos_rtx = expand_normal (position);
xtarget = offset_address (target, pos_rtx,
highest_pow2_factor (position));
xtarget = adjust_address (xtarget, mode, 0);
if (TREE_CODE (value) == CONSTRUCTOR)
store_constructor (value, xtarget, cleared,
bitsize / BITS_PER_UNIT);
else
store_expr (value, xtarget, 0);
exit_cond = build2 (LT_EXPR, integer_type_node,
index, hi_index);
jumpif (exit_cond, loop_end);
expand_assignment (index,
build2 (PLUS_EXPR, TREE_TYPE (index),
index, integer_one_node));
emit_jump (loop_start);
emit_label (loop_end);
}
}
else if ((index != 0 && ! host_integerp (index, 0))
|| ! host_integerp (TYPE_SIZE (elttype), 1))
{
tree position;
if (index == 0)
index = ssize_int (1);
if (minelt)
index = fold_convert (ssizetype,
fold_build2 (MINUS_EXPR,
TREE_TYPE (index),
index,
TYPE_MIN_VALUE (domain)));
position =
size_binop (MULT_EXPR, index,
fold_convert (ssizetype,
TYPE_SIZE_UNIT (elttype)));
xtarget = offset_address (target,
expand_normal (position),
highest_pow2_factor (position));
xtarget = adjust_address (xtarget, mode, 0);
store_expr (value, xtarget, 0);
}
else
{
if (index != 0)
bitpos = ((tree_low_cst (index, 0) - minelt)
* tree_low_cst (TYPE_SIZE (elttype), 1));
else
bitpos = (i * tree_low_cst (TYPE_SIZE (elttype), 1));
if (MEM_P (target) && !MEM_KEEP_ALIAS_SET_P (target)
&& TREE_CODE (type) == ARRAY_TYPE
&& TYPE_NONALIASED_COMPONENT (type))
{
target = copy_rtx (target);
MEM_KEEP_ALIAS_SET_P (target) = 1;
}
store_constructor_field (target, bitsize, bitpos, mode, value,
type, cleared, get_alias_set (elttype));
}
}
break;
}
case VECTOR_TYPE:
{
unsigned HOST_WIDE_INT idx;
constructor_elt *ce;
int i;
int need_to_clear;
int icode = 0;
tree elttype = TREE_TYPE (type);
int elt_size = tree_low_cst (TYPE_SIZE (elttype), 1);
enum machine_mode eltmode = TYPE_MODE (elttype);
HOST_WIDE_INT bitsize;
HOST_WIDE_INT bitpos;
rtvec vector = NULL;
unsigned n_elts;
gcc_assert (eltmode != BLKmode);
n_elts = TYPE_VECTOR_SUBPARTS (type);
if (REG_P (target) && VECTOR_MODE_P (GET_MODE (target)))
{
enum machine_mode mode = GET_MODE (target);
icode = (int) vec_init_optab->handlers[mode].insn_code;
if (icode != CODE_FOR_nothing)
{
unsigned int i;
vector = rtvec_alloc (n_elts);
for (i = 0; i < n_elts; i++)
RTVEC_ELT (vector, i) = CONST0_RTX (GET_MODE_INNER (mode));
}
}
if (cleared)
need_to_clear = 0;
else if (REG_P (target) && TREE_STATIC (exp))
need_to_clear = 1;
else
{
unsigned HOST_WIDE_INT count = 0, zero_count = 0;
tree value;
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value)
{
int n_elts_here = tree_low_cst
(int_const_binop (TRUNC_DIV_EXPR,
TYPE_SIZE (TREE_TYPE (value)),
TYPE_SIZE (elttype), 0), 1);
count += n_elts_here;
if (mostly_zeros_p (value))
zero_count += n_elts_here;
}
need_to_clear = (count < n_elts || 4 * zero_count >= 3 * count);
}
if (need_to_clear && size > 0 && !vector)
{
if (REG_P (target))
emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
else
clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
cleared = 1;
}
if (!cleared && !vector && REG_P (target))
emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
for (idx = 0, i = 0;
VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (exp), idx, ce);
idx++, i += bitsize / elt_size)
{
HOST_WIDE_INT eltpos;
tree value = ce->value;
bitsize = tree_low_cst (TYPE_SIZE (TREE_TYPE (value)), 1);
if (cleared && initializer_zerop (value))
continue;
if (ce->index)
eltpos = tree_low_cst (ce->index, 1);
else
eltpos = i;
if (vector)
{
gcc_assert (TREE_CODE (TREE_TYPE (value)) != VECTOR_TYPE);
RTVEC_ELT (vector, eltpos)
= expand_normal (value);
}
else
{
enum machine_mode value_mode =
TREE_CODE (TREE_TYPE (value)) == VECTOR_TYPE
? TYPE_MODE (TREE_TYPE (value))
: eltmode;
bitpos = eltpos * elt_size;
store_constructor_field (target, bitsize, bitpos,
value_mode, value, type,
cleared, get_alias_set (elttype));
}
}
if (vector)
emit_insn (GEN_FCN (icode)
(target,
gen_rtx_PARALLEL (GET_MODE (target), vector)));
break;
}
default:
gcc_unreachable ();
}
}
static rtx
store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
enum machine_mode mode, tree exp, tree type, int alias_set)
{
HOST_WIDE_INT width_mask = 0;
if (TREE_CODE (exp) == ERROR_MARK)
return const0_rtx;
if (bitsize == 0)
return expand_expr (exp, const0_rtx, VOIDmode, 0);
else if (bitsize >= 0 && bitsize < HOST_BITS_PER_WIDE_INT)
width_mask = ((HOST_WIDE_INT) 1 << bitsize) - 1;
if (mode == BLKmode
&& (REG_P (target) || GET_CODE (target) == SUBREG))
{
rtx object = assign_temp (type, 0, 1, 1);
rtx blk_object = adjust_address (object, BLKmode, 0);
if (bitsize != (HOST_WIDE_INT) GET_MODE_BITSIZE (GET_MODE (target)))
emit_move_insn (object, target);
store_field (blk_object, bitsize, bitpos, mode, exp, type, alias_set);
emit_move_insn (target, object);
return blk_object;
}
if (GET_CODE (target) == CONCAT)
{
gcc_assert (!bitpos);
return store_expr (exp, target, 0);
}
if (mode == VOIDmode
|| (mode != BLKmode && ! direct_store[(int) mode]
&& GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
&& GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
|| REG_P (target)
|| GET_CODE (target) == SUBREG
|| (mode != BLKmode
&& ((((MEM_ALIGN (target) < GET_MODE_ALIGNMENT (mode))
|| bitpos % GET_MODE_ALIGNMENT (mode))
&& SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (target)))
|| (bitpos % BITS_PER_UNIT != 0)))
|| (bitsize >= 0
&& TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) == INTEGER_CST
&& compare_tree_int (TYPE_SIZE (TREE_TYPE (exp)), bitsize) != 0))
{
rtx temp;
if (TREE_CODE (exp) == NOP_EXPR)
{
tree type = TREE_TYPE (exp);
if (INTEGRAL_TYPE_P (type)
&& TYPE_PRECISION (type) < GET_MODE_BITSIZE (TYPE_MODE (type))
&& bitsize == TYPE_PRECISION (type))
{
type = TREE_TYPE (TREE_OPERAND (exp, 0));
if (INTEGRAL_TYPE_P (type) && TYPE_PRECISION (type) >= bitsize)
exp = TREE_OPERAND (exp, 0);
}
}
temp = expand_normal (exp);
if (BYTES_BIG_ENDIAN && GET_MODE_CLASS (GET_MODE (temp)) == MODE_INT
&& bitsize < (HOST_WIDE_INT) GET_MODE_BITSIZE (GET_MODE (temp))
&& TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
temp = expand_shift (RSHIFT_EXPR, GET_MODE (temp), temp,
size_int (GET_MODE_BITSIZE (GET_MODE (temp))
- bitsize),
NULL_RTX, 1);
if (mode != VOIDmode && mode != BLKmode
&& mode != TYPE_MODE (TREE_TYPE (exp)))
temp = convert_modes (mode, TYPE_MODE (TREE_TYPE (exp)), temp, 1);
if (GET_MODE (target) == BLKmode && GET_MODE (temp) == BLKmode)
{
gcc_assert (MEM_P (target) && MEM_P (temp)
&& !(bitpos % BITS_PER_UNIT));
target = adjust_address (target, VOIDmode, bitpos / BITS_PER_UNIT);
emit_block_move (target, temp,
GEN_INT ((bitsize + BITS_PER_UNIT - 1)
/ BITS_PER_UNIT),
BLOCK_OP_NORMAL);
return const0_rtx;
}
store_bit_field (target, bitsize, bitpos, mode, temp, type);
return const0_rtx;
}
else
{
rtx to_rtx = adjust_address (target, mode, bitpos / BITS_PER_UNIT);
if (to_rtx == target)
to_rtx = copy_rtx (to_rtx);
MEM_SET_IN_STRUCT_P (to_rtx, 1);
if (!MEM_KEEP_ALIAS_SET_P (to_rtx) && MEM_ALIAS_SET (to_rtx) != 0)
set_mem_alias_set (to_rtx, alias_set);
return store_expr (exp, to_rtx, 0);
}
}
tree
get_inner_reference (tree exp, HOST_WIDE_INT *pbitsize,
HOST_WIDE_INT *pbitpos, tree *poffset,
enum machine_mode *pmode, int *punsignedp,
int *pvolatilep, bool keep_aligning)
{
tree size_tree = 0;
enum machine_mode mode = VOIDmode;
tree offset = size_zero_node;
tree bit_offset = bitsize_zero_node;
tree tem;
if (TREE_CODE (exp) == COMPONENT_REF)
{
size_tree = DECL_SIZE (TREE_OPERAND (exp, 1));
if (! DECL_BIT_FIELD (TREE_OPERAND (exp, 1)))
mode = DECL_MODE (TREE_OPERAND (exp, 1));
*punsignedp = DECL_UNSIGNED (TREE_OPERAND (exp, 1));
}
else if (TREE_CODE (exp) == BIT_FIELD_REF)
{
size_tree = TREE_OPERAND (exp, 1);
*punsignedp = BIT_FIELD_REF_UNSIGNED (exp);
}
else
{
mode = TYPE_MODE (TREE_TYPE (exp));
*punsignedp = TYPE_UNSIGNED (TREE_TYPE (exp));
if (mode == BLKmode)
size_tree = TYPE_SIZE (TREE_TYPE (exp));
else
*pbitsize = GET_MODE_BITSIZE (mode);
}
if (size_tree != 0)
{
if (! host_integerp (size_tree, 1))
mode = BLKmode, *pbitsize = -1;
else
*pbitsize = tree_low_cst (size_tree, 1);
}
while (1)
{
switch (TREE_CODE (exp))
{
case BIT_FIELD_REF:
bit_offset = size_binop (PLUS_EXPR, bit_offset,
TREE_OPERAND (exp, 2));
break;
case COMPONENT_REF:
{
tree field_bit_offset;
tree field = TREE_OPERAND (exp, 1);
tree this_offset = component_ref_field_offset (exp);
if (this_offset == 0)
break;
offset = size_binop (PLUS_EXPR, offset, this_offset);
field_bit_offset = objc_v2_bitfield_ivar_bitpos (exp);
if (!field_bit_offset)
field_bit_offset = DECL_FIELD_BIT_OFFSET (field);
bit_offset = size_binop (PLUS_EXPR, bit_offset,
field_bit_offset);
}
break;
case ARRAY_REF:
case ARRAY_RANGE_REF:
{
tree index = TREE_OPERAND (exp, 1);
tree low_bound, unit_size;
#if ENABLE_LLVM
if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) != ARRAY_TYPE)
goto done;
#endif
low_bound = array_ref_low_bound (exp);
unit_size = array_ref_element_size (exp);
if (! integer_zerop (low_bound))
index = fold_build2 (MINUS_EXPR, TREE_TYPE (index),
index, low_bound);
offset = size_binop (PLUS_EXPR, offset,
size_binop (MULT_EXPR,
fold_convert (sizetype, index),
unit_size));
}
break;
case REALPART_EXPR:
break;
case IMAGPART_EXPR:
bit_offset = size_binop (PLUS_EXPR, bit_offset,
bitsize_int (*pbitsize));
break;
case VIEW_CONVERT_EXPR:
if (keep_aligning && STRICT_ALIGNMENT
&& (TYPE_ALIGN (TREE_TYPE (exp))
> TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0))))
&& (TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0)))
< BIGGEST_ALIGNMENT)
&& (TYPE_ALIGN_OK (TREE_TYPE (exp))
|| TYPE_ALIGN_OK (TREE_TYPE (TREE_OPERAND (exp, 0)))))
goto done;
break;
default:
goto done;
}
if (TREE_THIS_VOLATILE (exp))
*pvolatilep = 1;
exp = TREE_OPERAND (exp, 0);
}
done:
if (host_integerp (offset, 0)
&& 0 != (tem = size_binop (MULT_EXPR,
fold_convert (bitsizetype, offset),
bitsize_unit_node))
&& 0 != (tem = size_binop (PLUS_EXPR, tem, bit_offset))
&& host_integerp (tem, 0))
*pbitpos = tree_low_cst (tem, 0), *poffset = 0;
else
*pbitpos = tree_low_cst (bit_offset, 0), *poffset = offset;
*pmode = mode;
return exp;
}
bool
contains_packed_reference (tree exp)
{
bool packed_p = false;
while (1)
{
switch (TREE_CODE (exp))
{
case COMPONENT_REF:
{
tree field = TREE_OPERAND (exp, 1);
packed_p = DECL_PACKED (field)
|| TYPE_PACKED (TREE_TYPE (field))
|| TYPE_PACKED (TREE_TYPE (exp));
if (packed_p)
goto done;
}
break;
case BIT_FIELD_REF:
case ARRAY_REF:
case ARRAY_RANGE_REF:
case REALPART_EXPR:
case IMAGPART_EXPR:
case VIEW_CONVERT_EXPR:
break;
default:
goto done;
}
exp = TREE_OPERAND (exp, 0);
}
done:
return packed_p;
}
tree
array_ref_element_size (tree exp)
{
tree aligned_size = TREE_OPERAND (exp, 3);
tree elmt_type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0)));
if (aligned_size)
{
if (TREE_TYPE (aligned_size) != sizetype)
aligned_size = fold_convert (sizetype, aligned_size);
return size_binop (MULT_EXPR, aligned_size,
size_int (TYPE_ALIGN_UNIT (elmt_type)));
}
else
return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_SIZE_UNIT (elmt_type), exp);
}
tree
array_ref_low_bound (tree exp)
{
tree domain_type = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (exp, 0)));
if (TREE_OPERAND (exp, 2))
return TREE_OPERAND (exp, 2);
if (domain_type && TYPE_MIN_VALUE (domain_type))
return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_MIN_VALUE (domain_type), exp);
return build_int_cst (TREE_TYPE (TREE_OPERAND (exp, 1)), 0);
}
tree
array_ref_up_bound (tree exp)
{
tree domain_type = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (exp, 0)));
if (domain_type && TYPE_MAX_VALUE (domain_type))
return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_MAX_VALUE (domain_type), exp);
return NULL_TREE;
}
tree
component_ref_field_offset (tree exp)
{
tree aligned_offset = TREE_OPERAND (exp, 2);
tree field = TREE_OPERAND (exp, 1);
tree offset = objc_v2_component_ref_field_offset (exp);
if (offset)
return offset;
if (aligned_offset)
{
if (TREE_TYPE (aligned_offset) != sizetype)
aligned_offset = fold_convert (sizetype, aligned_offset);
return size_binop (MULT_EXPR, aligned_offset,
size_int (DECL_OFFSET_ALIGN (field) / BITS_PER_UNIT));
}
else
return SUBSTITUTE_PLACEHOLDER_IN_EXPR (DECL_FIELD_OFFSET (field), exp);
}
int
handled_component_p (tree t)
{
switch (TREE_CODE (t))
{
case BIT_FIELD_REF:
case COMPONENT_REF:
case ARRAY_REF:
case ARRAY_RANGE_REF:
case VIEW_CONVERT_EXPR:
case REALPART_EXPR:
case IMAGPART_EXPR:
#ifdef ENABLE_LLVM
if (TREE_CODE (t) == ARRAY_REF &&
TREE_CODE (TREE_TYPE (TREE_OPERAND(t, 0))) != ARRAY_TYPE)
return 0;
#endif
return 1;
default:
return 0;
}
}
rtx
force_operand (rtx value, rtx target)
{
rtx op1, op2;
rtx subtarget = get_subtarget (target);
enum rtx_code code = GET_CODE (value);
if (code == SUBREG
&& !REG_P (SUBREG_REG (value))
&& !MEM_P (SUBREG_REG (value)))
{
value = simplify_gen_subreg (GET_MODE (value),
force_reg (GET_MODE (SUBREG_REG (value)),
force_operand (SUBREG_REG (value),
NULL_RTX)),
GET_MODE (SUBREG_REG (value)),
SUBREG_BYTE (value));
code = GET_CODE (value);
}
if ((code == PLUS || code == MINUS)
&& XEXP (value, 0) == pic_offset_table_rtx
&& (GET_CODE (XEXP (value, 1)) == SYMBOL_REF
|| GET_CODE (XEXP (value, 1)) == LABEL_REF
|| GET_CODE (XEXP (value, 1)) == CONST))
{
if (!subtarget)
subtarget = gen_reg_rtx (GET_MODE (value));
emit_move_insn (subtarget, value);
return subtarget;
}
if (ARITHMETIC_P (value))
{
op2 = XEXP (value, 1);
if (!CONSTANT_P (op2) && !(REG_P (op2) && op2 != subtarget))
subtarget = 0;
if (code == MINUS && GET_CODE (op2) == CONST_INT)
{
code = PLUS;
op2 = negate_rtx (GET_MODE (value), op2);
}
if (code == PLUS && GET_CODE (op2) == CONST_INT
&& GET_CODE (XEXP (value, 0)) == PLUS
&& REG_P (XEXP (XEXP (value, 0), 0))
&& REGNO (XEXP (XEXP (value, 0), 0)) >= FIRST_VIRTUAL_REGISTER
&& REGNO (XEXP (XEXP (value, 0), 0)) <= LAST_VIRTUAL_REGISTER)
{
rtx temp = expand_simple_binop (GET_MODE (value), code,
XEXP (XEXP (value, 0), 0), op2,
subtarget, 0, OPTAB_LIB_WIDEN);
return expand_simple_binop (GET_MODE (value), code, temp,
force_operand (XEXP (XEXP (value,
0), 1), 0),
target, 0, OPTAB_LIB_WIDEN);
}
op1 = force_operand (XEXP (value, 0), subtarget);
op2 = force_operand (op2, NULL_RTX);
switch (code)
{
case MULT:
return expand_mult (GET_MODE (value), op1, op2, target, 1);
case DIV:
if (!INTEGRAL_MODE_P (GET_MODE (value)))
return expand_simple_binop (GET_MODE (value), code, op1, op2,
target, 1, OPTAB_LIB_WIDEN);
else
return expand_divmod (0,
FLOAT_MODE_P (GET_MODE (value))
? RDIV_EXPR : TRUNC_DIV_EXPR,
GET_MODE (value), op1, op2, target, 0);
break;
case MOD:
return expand_divmod (1, TRUNC_MOD_EXPR, GET_MODE (value), op1, op2,
target, 0);
break;
case UDIV:
return expand_divmod (0, TRUNC_DIV_EXPR, GET_MODE (value), op1, op2,
target, 1);
break;
case UMOD:
return expand_divmod (1, TRUNC_MOD_EXPR, GET_MODE (value), op1, op2,
target, 1);
break;
case ASHIFTRT:
return expand_simple_binop (GET_MODE (value), code, op1, op2,
target, 0, OPTAB_LIB_WIDEN);
break;
default:
return expand_simple_binop (GET_MODE (value), code, op1, op2,
target, 1, OPTAB_LIB_WIDEN);
}
}
if (UNARY_P (value))
{
if (!target)
target = gen_reg_rtx (GET_MODE (value));
op1 = force_operand (XEXP (value, 0), NULL_RTX);
switch (code)
{
case ZERO_EXTEND:
case SIGN_EXTEND:
case TRUNCATE:
case FLOAT_EXTEND:
case FLOAT_TRUNCATE:
convert_move (target, op1, code == ZERO_EXTEND);
return target;
case FIX:
case UNSIGNED_FIX:
expand_fix (target, op1, code == UNSIGNED_FIX);
return target;
case FLOAT:
case UNSIGNED_FLOAT:
expand_float (target, op1, code == UNSIGNED_FLOAT);
return target;
default:
return expand_simple_unop (GET_MODE (value), code, op1, target, 0);
}
}
#ifdef INSN_SCHEDULING
if (GET_CODE (value) == SUBREG && MEM_P (SUBREG_REG (value))
&& (GET_MODE_SIZE (GET_MODE (value))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (value)))))
value
= simplify_gen_subreg (GET_MODE (value),
force_reg (GET_MODE (SUBREG_REG (value)),
force_operand (SUBREG_REG (value),
NULL_RTX)),
GET_MODE (SUBREG_REG (value)),
SUBREG_BYTE (value));
#endif
return value;
}
int
safe_from_p (rtx x, tree exp, int top_p)
{
rtx exp_rtl = 0;
int i, nops;
if (x == 0
|| (top_p && TREE_TYPE (exp) != 0 && COMPLETE_TYPE_P (TREE_TYPE (exp))
&& TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST
&& (TREE_CODE (TREE_TYPE (exp)) != ARRAY_TYPE
|| TYPE_ARRAY_MAX_SIZE (TREE_TYPE (exp)) == NULL_TREE
|| TREE_CODE (TYPE_ARRAY_MAX_SIZE (TREE_TYPE (exp)))
!= INTEGER_CST)
&& GET_MODE (x) == BLKmode)
|| (MEM_P (x)
&& (XEXP (x, 0) == virtual_outgoing_args_rtx
|| (GET_CODE (XEXP (x, 0)) == PLUS
&& XEXP (XEXP (x, 0), 0) == virtual_outgoing_args_rtx))))
return 1;
if (GET_CODE (x) == SUBREG)
{
x = SUBREG_REG (x);
if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
return 0;
}
switch (TREE_CODE_CLASS (TREE_CODE (exp)))
{
case tcc_declaration:
exp_rtl = DECL_RTL_IF_SET (exp);
break;
case tcc_constant:
return 1;
case tcc_exceptional:
if (TREE_CODE (exp) == TREE_LIST)
{
while (1)
{
if (TREE_VALUE (exp) && !safe_from_p (x, TREE_VALUE (exp), 0))
return 0;
exp = TREE_CHAIN (exp);
if (!exp)
return 1;
if (TREE_CODE (exp) != TREE_LIST)
return safe_from_p (x, exp, 0);
}
}
else if (TREE_CODE (exp) == CONSTRUCTOR)
{
constructor_elt *ce;
unsigned HOST_WIDE_INT idx;
for (idx = 0;
VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (exp), idx, ce);
idx++)
if ((ce->index != NULL_TREE && !safe_from_p (x, ce->index, 0))
|| !safe_from_p (x, ce->value, 0))
return 0;
return 1;
}
else if (TREE_CODE (exp) == ERROR_MARK)
return 1;
else
return 0;
case tcc_statement:
return (TREE_CODE (exp) != DECL_EXPR
|| TREE_CODE (DECL_EXPR_DECL (exp)) != VAR_DECL
|| !DECL_INITIAL (DECL_EXPR_DECL (exp))
|| safe_from_p (x, DECL_INITIAL (DECL_EXPR_DECL (exp)), 0));
case tcc_binary:
case tcc_comparison:
if (!safe_from_p (x, TREE_OPERAND (exp, 1), 0))
return 0;
case tcc_unary:
return safe_from_p (x, TREE_OPERAND (exp, 0), 0);
case tcc_expression:
case tcc_reference:
switch (TREE_CODE (exp))
{
case ADDR_EXPR:
if (staticp (TREE_OPERAND (exp, 0))
|| TREE_STATIC (exp)
|| safe_from_p (x, TREE_OPERAND (exp, 0), 0))
return 1;
exp = TREE_OPERAND (exp, 0);
if (DECL_P (exp))
{
if (!DECL_RTL_SET_P (exp)
|| !MEM_P (DECL_RTL (exp)))
return 0;
else
exp_rtl = XEXP (DECL_RTL (exp), 0);
}
break;
case MISALIGNED_INDIRECT_REF:
case ALIGN_INDIRECT_REF:
case INDIRECT_REF:
if (MEM_P (x)
&& alias_sets_conflict_p (MEM_ALIAS_SET (x),
get_alias_set (exp)))
return 0;
break;
case CALL_EXPR:
if ((REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
|| MEM_P (x))
return 0;
break;
case WITH_CLEANUP_EXPR:
case CLEANUP_POINT_EXPR:
gcc_unreachable ();
case SAVE_EXPR:
return safe_from_p (x, TREE_OPERAND (exp, 0), 0);
default:
break;
}
if (exp_rtl)
break;
nops = TREE_CODE_LENGTH (TREE_CODE (exp));
for (i = 0; i < nops; i++)
if (TREE_OPERAND (exp, i) != 0
&& ! safe_from_p (x, TREE_OPERAND (exp, i), 0))
return 0;
if ((unsigned int) TREE_CODE (exp)
>= (unsigned int) LAST_AND_UNUSED_TREE_CODE
&& !lang_hooks.safe_from_p (x, exp))
return 0;
break;
case tcc_type:
gcc_unreachable ();
}
if (exp_rtl)
{
if (GET_CODE (exp_rtl) == SUBREG)
{
exp_rtl = SUBREG_REG (exp_rtl);
if (REG_P (exp_rtl)
&& REGNO (exp_rtl) < FIRST_PSEUDO_REGISTER)
return 0;
}
return ! (rtx_equal_p (x, exp_rtl)
|| (MEM_P (x) && MEM_P (exp_rtl)
&& true_dependence (exp_rtl, VOIDmode, x,
rtx_addr_varies_p)));
}
return 1;
}
unsigned HOST_WIDE_INT
highest_pow2_factor (tree exp)
{
unsigned HOST_WIDE_INT c0, c1;
switch (TREE_CODE (exp))
{
case INTEGER_CST:
if (TREE_CONSTANT_OVERFLOW (exp))
return BIGGEST_ALIGNMENT;
else
{
c0 = TREE_INT_CST_LOW (exp);
c0 &= -c0;
return c0 ? c0 : BIGGEST_ALIGNMENT;
}
break;
case PLUS_EXPR: case MINUS_EXPR: case MIN_EXPR: case MAX_EXPR:
c0 = highest_pow2_factor (TREE_OPERAND (exp, 0));
c1 = highest_pow2_factor (TREE_OPERAND (exp, 1));
return MIN (c0, c1);
case MULT_EXPR:
c0 = highest_pow2_factor (TREE_OPERAND (exp, 0));
c1 = highest_pow2_factor (TREE_OPERAND (exp, 1));
return c0 * c1;
case ROUND_DIV_EXPR: case TRUNC_DIV_EXPR: case FLOOR_DIV_EXPR:
case CEIL_DIV_EXPR:
if (integer_pow2p (TREE_OPERAND (exp, 1))
&& host_integerp (TREE_OPERAND (exp, 1), 1))
{
c0 = highest_pow2_factor (TREE_OPERAND (exp, 0));
c1 = tree_low_cst (TREE_OPERAND (exp, 1), 1);
return MAX (1, c0 / c1);
}
break;
case NON_LVALUE_EXPR: case NOP_EXPR: case CONVERT_EXPR:
case SAVE_EXPR:
return highest_pow2_factor (TREE_OPERAND (exp, 0));
case COMPOUND_EXPR:
return highest_pow2_factor (TREE_OPERAND (exp, 1));
case COND_EXPR:
c0 = highest_pow2_factor (TREE_OPERAND (exp, 1));
c1 = highest_pow2_factor (TREE_OPERAND (exp, 2));
return MIN (c0, c1);
default:
break;
}
return 1;
}
static unsigned HOST_WIDE_INT
highest_pow2_factor_for_target (tree target, tree exp)
{
unsigned HOST_WIDE_INT target_align, factor;
factor = highest_pow2_factor (exp);
if (TREE_CODE (target) == COMPONENT_REF)
target_align = DECL_ALIGN_UNIT (TREE_OPERAND (target, 1));
else
target_align = TYPE_ALIGN_UNIT (TREE_TYPE (target));
return MAX (factor, target_align);
}
void
expand_var (tree var)
{
if (DECL_EXTERNAL (var))
return;
if (TREE_STATIC (var))
var = DECL_ORIGIN (var);
if (TREE_STATIC (var)
? !TREE_ASM_WRITTEN (var)
: !DECL_RTL_SET_P (var))
{
if (TREE_CODE (var) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (var))
;
else if (lang_hooks.expand_decl (var))
;
else if (TREE_CODE (var) == VAR_DECL && !TREE_STATIC (var))
expand_decl (var);
else if (TREE_CODE (var) == VAR_DECL && TREE_STATIC (var))
rest_of_decl_compilation (var, 0, 0);
else
gcc_assert (TREE_CODE (var) == TYPE_DECL
|| TREE_CODE (var) == CONST_DECL
|| TREE_CODE (var) == FUNCTION_DECL
|| TREE_CODE (var) == LABEL_DECL);
}
}
static void
expand_operands (tree exp0, tree exp1, rtx target, rtx *op0, rtx *op1,
enum expand_modifier modifier)
{
if (! safe_from_p (target, exp1, 1))
target = 0;
if (operand_equal_p (exp0, exp1, 0))
{
*op0 = expand_expr (exp0, target, VOIDmode, modifier);
*op1 = copy_rtx (*op0);
}
else
{
if (flag_evaluation_order && TREE_SIDE_EFFECTS (exp1))
exp0 = save_expr (exp0);
*op0 = expand_expr (exp0, target, VOIDmode, modifier);
*op1 = expand_expr (exp1, NULL_RTX, VOIDmode, modifier);
}
}
static rtx
expand_expr_constant (tree exp, int defer, enum expand_modifier modifier)
{
rtx mem;
mem = output_constant_def (exp, defer);
if (modifier != EXPAND_INITIALIZER)
mem = use_anchored_address (mem);
return mem;
}
static rtx
expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
enum expand_modifier modifier)
{
rtx result, subtarget;
tree inner, offset;
HOST_WIDE_INT bitsize, bitpos;
int volatilep, unsignedp;
enum machine_mode mode1;
if (TREE_CODE (exp) == CONSTRUCTOR
|| CONSTANT_CLASS_P (exp))
return XEXP (expand_expr_constant (exp, 0, modifier), 0);
switch (TREE_CODE (exp))
{
case INDIRECT_REF:
return expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier);
case CONST_DECL:
return expand_expr_addr_expr_1 (DECL_INITIAL (exp), target,
tmode, modifier);
case REALPART_EXPR:
offset = 0;
bitpos = 0;
inner = TREE_OPERAND (exp, 0);
break;
case IMAGPART_EXPR:
offset = 0;
bitpos = GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (exp)));
inner = TREE_OPERAND (exp, 0);
break;
default:
if (DECL_P (exp)
|| TREE_CODE (exp) >= LAST_AND_UNUSED_TREE_CODE)
{
result = expand_expr (exp, target, tmode,
modifier == EXPAND_INITIALIZER
? EXPAND_INITIALIZER : EXPAND_CONST_ADDRESS);
gcc_assert (MEM_P (result));
result = XEXP (result, 0);
if (DECL_P (exp) && !TREE_USED (exp) == 0)
{
assemble_external (exp);
TREE_USED (exp) = 1;
}
if (modifier != EXPAND_INITIALIZER
&& modifier != EXPAND_CONST_ADDRESS)
result = force_operand (result, target);
return result;
}
inner = get_inner_reference (exp, &bitsize, &bitpos, &offset,
&mode1, &unsignedp, &volatilep, false);
break;
}
gcc_assert (inner != exp);
subtarget = offset || bitpos ? NULL_RTX : target;
result = expand_expr_addr_expr_1 (inner, subtarget, tmode, modifier);
if (offset)
{
rtx tmp;
if (modifier != EXPAND_NORMAL)
result = force_operand (result, NULL);
tmp = expand_expr (offset, NULL, tmode, EXPAND_NORMAL);
result = convert_memory_address (tmode, result);
tmp = convert_memory_address (tmode, tmp);
if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
result = gen_rtx_PLUS (tmode, result, tmp);
else
{
subtarget = bitpos ? NULL_RTX : target;
result = expand_simple_binop (tmode, PLUS, result, tmp, subtarget,
1, OPTAB_LIB_WIDEN);
}
}
if (bitpos)
{
gcc_assert ((bitpos % BITS_PER_UNIT) == 0);
result = plus_constant (result, bitpos / BITS_PER_UNIT);
if (modifier < EXPAND_SUM)
result = force_operand (result, target);
}
return result;
}
static rtx
expand_expr_addr_expr (tree exp, rtx target, enum machine_mode tmode,
enum expand_modifier modifier)
{
enum machine_mode rmode;
rtx result;
if (tmode == VOIDmode)
tmode = TYPE_MODE (TREE_TYPE (exp));
if (tmode != Pmode && tmode != ptr_mode)
tmode = Pmode;
result = expand_expr_addr_expr_1 (TREE_OPERAND (exp, 0), target,
tmode, modifier);
rmode = GET_MODE (result);
if (rmode == VOIDmode)
rmode = tmode;
if (rmode != tmode)
result = convert_memory_address (tmode, result);
return result;
}
static rtx expand_expr_real_1 (tree, rtx, enum machine_mode,
enum expand_modifier, rtx *);
rtx
expand_expr_real (tree exp, rtx target, enum machine_mode tmode,
enum expand_modifier modifier, rtx *alt_rtl)
{
int rn = -1;
rtx ret, last = NULL;
#ifdef ENABLE_LLVM
return 0;
#endif
if (TREE_CODE (exp) == ERROR_MARK
|| TREE_CODE (TREE_TYPE (exp)) == ERROR_MARK)
{
ret = CONST0_RTX (tmode);
return ret ? ret : const0_rtx;
}
if (flag_non_call_exceptions)
{
rn = lookup_stmt_eh_region (exp);
if (rn >= 0)
last = get_last_insn ();
}
if (cfun && cfun->ib_boundaries_block && EXPR_HAS_LOCATION (exp))
{
location_t saved_location = input_location;
input_location = EXPR_LOCATION (exp);
emit_line_note (input_location);
record_block_change (TREE_BLOCK (exp));
ret = expand_expr_real_1 (exp, target, tmode, modifier, alt_rtl);
input_location = saved_location;
}
else
{
ret = expand_expr_real_1 (exp, target, tmode, modifier, alt_rtl);
}
if (rn >= 0)
{
rtx insn;
for (insn = next_real_insn (last); insn;
insn = next_real_insn (insn))
{
if (! find_reg_note (insn, REG_EH_REGION, NULL_RTX)
&& GET_CODE (PATTERN (insn)) != CLOBBER
&& GET_CODE (PATTERN (insn)) != USE
&& (CALL_P (insn) || may_trap_p (PATTERN (insn))))
{
REG_NOTES (insn) = alloc_EXPR_LIST (REG_EH_REGION, GEN_INT (rn),
REG_NOTES (insn));
}
}
}
return ret;
}
static rtx
expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
enum expand_modifier modifier, rtx *alt_rtl)
{
rtx op0, op1, temp, decl_rtl;
tree type = TREE_TYPE (exp);
int unsignedp;
enum machine_mode mode;
enum tree_code code = TREE_CODE (exp);
optab this_optab;
rtx subtarget, original_target;
int ignore;
tree context, subexp0, subexp1;
bool reduce_bit_field = false;
#define REDUCE_BIT_FIELD(expr) (reduce_bit_field && !ignore \
? reduce_to_bit_field_precision ((expr), \
target, \
type) \
: (expr))
mode = TYPE_MODE (type);
unsignedp = TYPE_UNSIGNED (type);
if (lang_hooks.reduce_bit_field_operations
&& TREE_CODE (type) == INTEGER_TYPE
&& GET_MODE_PRECISION (mode) > TYPE_PRECISION (type))
{
reduce_bit_field = true;
if (modifier == EXPAND_STACK_PARM)
target = 0;
}
subtarget = get_subtarget (target);
original_target = target;
ignore = (target == const0_rtx
|| ((code == NON_LVALUE_EXPR || code == NOP_EXPR
|| code == CONVERT_EXPR || code == COND_EXPR
|| code == VIEW_CONVERT_EXPR)
&& TREE_CODE (type) == VOID_TYPE));
if (ignore)
{
if (! TREE_SIDE_EFFECTS (exp))
return const0_rtx;
if (TREE_THIS_VOLATILE (exp)
&& TREE_CODE (exp) != FUNCTION_DECL
&& mode != VOIDmode && mode != BLKmode
&& modifier != EXPAND_CONST_ADDRESS)
{
temp = expand_expr (exp, NULL_RTX, VOIDmode, modifier);
if (MEM_P (temp))
temp = copy_to_reg (temp);
return const0_rtx;
}
if (TREE_CODE_CLASS (code) == tcc_unary
|| code == COMPONENT_REF || code == INDIRECT_REF)
return expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
modifier);
else if (TREE_CODE_CLASS (code) == tcc_binary
|| TREE_CODE_CLASS (code) == tcc_comparison
|| code == ARRAY_REF || code == ARRAY_RANGE_REF)
{
expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier);
expand_expr (TREE_OPERAND (exp, 1), const0_rtx, VOIDmode, modifier);
return const0_rtx;
}
else if (code == BIT_FIELD_REF)
{
expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier);
expand_expr (TREE_OPERAND (exp, 1), const0_rtx, VOIDmode, modifier);
expand_expr (TREE_OPERAND (exp, 2), const0_rtx, VOIDmode, modifier);
return const0_rtx;
}
target = 0;
}
switch (code)
{
case LABEL_DECL:
{
tree function = decl_function_context (exp);
temp = label_rtx (exp);
temp = gen_rtx_LABEL_REF (Pmode, temp);
if (function != current_function_decl
&& function != 0)
LABEL_REF_NONLOCAL_P (temp) = 1;
temp = gen_rtx_MEM (FUNCTION_MODE, temp);
return temp;
}
case SSA_NAME:
return expand_expr_real_1 (SSA_NAME_VAR (exp), target, tmode, modifier,
NULL);
case PARM_DECL:
case VAR_DECL:
if (DECL_SIZE (exp) == 0
&& COMPLETE_OR_UNBOUND_ARRAY_TYPE_P (TREE_TYPE (exp))
&& (TREE_STATIC (exp) || DECL_EXTERNAL (exp)))
layout_decl (exp, 0);
case FUNCTION_DECL:
case RESULT_DECL:
decl_rtl = DECL_RTL (exp);
gcc_assert (decl_rtl);
if (! TREE_USED (exp))
{
assemble_external (exp);
TREE_USED (exp) = 1;
}
temp = 0;
context = decl_function_context (exp);
gcc_assert (!context
|| context == current_function_decl
|| TREE_STATIC (exp)
|| TREE_CODE (exp) == FUNCTION_DECL);
if (MEM_P (decl_rtl) && REG_P (XEXP (decl_rtl, 0)))
temp = validize_mem (decl_rtl);
else if (MEM_P (decl_rtl) && modifier != EXPAND_INITIALIZER)
{
if (alt_rtl)
*alt_rtl = decl_rtl;
decl_rtl = use_anchored_address (decl_rtl);
if (modifier != EXPAND_CONST_ADDRESS
&& modifier != EXPAND_SUM
&& (!memory_address_p (DECL_MODE (exp), XEXP (decl_rtl, 0))
|| (flag_force_addr && !REG_P (XEXP (decl_rtl, 0)))))
temp = replace_equiv_address (decl_rtl,
copy_rtx (XEXP (decl_rtl, 0)));
}
if (temp != 0)
{
if (MEM_P (temp) && REG_P (XEXP (temp, 0)))
mark_reg_pointer (XEXP (temp, 0), DECL_ALIGN (exp));
return temp;
}
if (REG_P (decl_rtl)
&& GET_MODE (decl_rtl) != DECL_MODE (exp))
{
enum machine_mode pmode;
pmode = promote_mode (type, DECL_MODE (exp), &unsignedp,
(TREE_CODE (exp) == RESULT_DECL
|| TREE_CODE (exp) == PARM_DECL) ? 1 : 0);
gcc_assert (GET_MODE (decl_rtl) == pmode);
temp = gen_lowpart_SUBREG (mode, decl_rtl);
SUBREG_PROMOTED_VAR_P (temp) = 1;
SUBREG_PROMOTED_UNSIGNED_SET (temp, unsignedp);
return temp;
}
return decl_rtl;
case INTEGER_CST:
temp = immed_double_const (TREE_INT_CST_LOW (exp),
TREE_INT_CST_HIGH (exp), mode);
if (TREE_CONSTANT_OVERFLOW (exp)
&& modifier != EXPAND_INITIALIZER)
temp = force_reg (mode, temp);
return temp;
case VECTOR_CST:
{
tree tmp = NULL_TREE;
if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT
|| GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
return const_vector_from_tree (exp);
if (GET_MODE_CLASS (mode) == MODE_INT)
{
tree type_for_mode = lang_hooks.types.type_for_mode (mode, 1);
if (type_for_mode)
tmp = fold_unary (VIEW_CONVERT_EXPR, type_for_mode, exp);
}
if (!tmp)
tmp = build_constructor_from_list (type,
TREE_VECTOR_CST_ELTS (exp));
return expand_expr (tmp, ignore ? const0_rtx : target,
tmode, modifier);
}
case CONST_DECL:
return expand_expr (DECL_INITIAL (exp), target, VOIDmode, modifier);
case REAL_CST:
return CONST_DOUBLE_FROM_REAL_VALUE (TREE_REAL_CST (exp),
TYPE_MODE (TREE_TYPE (exp)));
case COMPLEX_CST:
if (original_target && GET_CODE (original_target) == CONCAT)
{
enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
rtx rtarg, itarg;
rtarg = XEXP (original_target, 0);
itarg = XEXP (original_target, 1);
op0 = expand_expr (TREE_REALPART (exp), rtarg, mode, 0);
op1 = expand_expr (TREE_IMAGPART (exp), itarg, mode, 0);
if (op0 != rtarg)
emit_move_insn (rtarg, op0);
if (op1 != itarg)
emit_move_insn (itarg, op1);
return original_target;
}
case STRING_CST:
temp = expand_expr_constant (exp, 1, modifier);
if (modifier != EXPAND_CONST_ADDRESS
&& modifier != EXPAND_INITIALIZER
&& modifier != EXPAND_SUM
&& (! memory_address_p (mode, XEXP (temp, 0))
|| flag_force_addr))
return replace_equiv_address (temp,
copy_rtx (XEXP (temp, 0)));
return temp;
case SAVE_EXPR:
{
tree val = TREE_OPERAND (exp, 0);
rtx ret = expand_expr_real_1 (val, target, tmode, modifier, alt_rtl);
if (!SAVE_EXPR_RESOLVED_P (exp))
{
gcc_assert (GET_MODE (ret) != BLKmode);
val = build_decl (VAR_DECL, NULL, TREE_TYPE (exp));
DECL_ARTIFICIAL (val) = 1;
DECL_IGNORED_P (val) = 1;
TREE_OPERAND (exp, 0) = val;
SAVE_EXPR_RESOLVED_P (exp) = 1;
if (!CONSTANT_P (ret))
ret = copy_to_reg (ret);
SET_DECL_RTL (val, ret);
}
return ret;
}
case GOTO_EXPR:
if (TREE_CODE (TREE_OPERAND (exp, 0)) == LABEL_DECL)
expand_goto (TREE_OPERAND (exp, 0));
else
expand_computed_goto (TREE_OPERAND (exp, 0));
return const0_rtx;
case CONSTRUCTOR:
if (ignore)
{
unsigned HOST_WIDE_INT idx;
tree value;
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value)
expand_expr (value, const0_rtx, VOIDmode, 0);
return const0_rtx;
}
else if (TREE_STATIC (exp)
&& !TREE_ADDRESSABLE (exp)
&& target != 0 && mode == BLKmode
&& all_zeros_p (exp))
{
clear_storage (target, expr_size (exp), BLOCK_OP_NORMAL);
return target;
}
else if ((TREE_STATIC (exp)
&& ((mode == BLKmode
&& ! (target != 0 && safe_from_p (target, exp, 1)))
|| TREE_ADDRESSABLE (exp)
|| (host_integerp (TYPE_SIZE_UNIT (type), 1)
&& (! MOVE_BY_PIECES_P
(tree_low_cst (TYPE_SIZE_UNIT (type), 1),
TYPE_ALIGN (type)))
&& ! mostly_zeros_p (exp))))
|| ((modifier == EXPAND_INITIALIZER
|| modifier == EXPAND_CONST_ADDRESS)
&& TREE_CONSTANT (exp)))
{
rtx constructor = expand_expr_constant (exp, 1, modifier);
if (modifier != EXPAND_CONST_ADDRESS
&& modifier != EXPAND_INITIALIZER
&& modifier != EXPAND_SUM)
constructor = validize_mem (constructor);
return constructor;
}
else
{
if (target == 0 || ! safe_from_p (target, exp, 1)
|| GET_CODE (target) == PARALLEL
|| modifier == EXPAND_STACK_PARM)
target
= assign_temp (build_qualified_type (type,
(TYPE_QUALS (type)
| (TREE_READONLY (exp)
* TYPE_QUAL_CONST))),
0, TREE_ADDRESSABLE (exp), 1);
store_constructor (exp, target, 0, int_expr_size (exp));
return target;
}
case MISALIGNED_INDIRECT_REF:
case ALIGN_INDIRECT_REF:
case INDIRECT_REF:
{
tree exp1 = TREE_OPERAND (exp, 0);
if (modifier != EXPAND_WRITE)
{
tree t;
t = fold_read_from_constant_string (exp);
if (t)
return expand_expr (t, target, tmode, modifier);
}
op0 = expand_expr (exp1, NULL_RTX, VOIDmode, EXPAND_SUM);
op0 = memory_address (mode, op0);
if (code == ALIGN_INDIRECT_REF)
{
int align = TYPE_ALIGN_UNIT (type);
op0 = gen_rtx_AND (Pmode, op0, GEN_INT (-align));
op0 = memory_address (mode, op0);
}
temp = gen_rtx_MEM (mode, op0);
set_mem_attributes (temp, exp, 0);
if (code == MISALIGNED_INDIRECT_REF)
{
int icode;
rtx reg, insn;
gcc_assert (modifier == EXPAND_NORMAL
|| modifier == EXPAND_STACK_PARM);
icode = movmisalign_optab->handlers[mode].insn_code;
gcc_assert (icode != CODE_FOR_nothing);
reg = gen_reg_rtx (mode);
insn = GEN_FCN (icode) (reg, temp);
emit_insn (insn);
return reg;
}
return temp;
}
case TARGET_MEM_REF:
{
struct mem_address addr;
get_address_description (exp, &addr);
op0 = addr_for_mem_ref (&addr, true);
op0 = memory_address (mode, op0);
temp = gen_rtx_MEM (mode, op0);
set_mem_attributes (temp, TMR_ORIGINAL (exp), 0);
}
return temp;
case ARRAY_REF:
{
tree array = TREE_OPERAND (exp, 0);
tree index = TREE_OPERAND (exp, 1);
if (modifier != EXPAND_CONST_ADDRESS
&& modifier != EXPAND_INITIALIZER
&& modifier != EXPAND_MEMORY)
{
tree t = fold_read_from_constant_string (exp);
if (t)
return expand_expr (t, target, tmode, modifier);
}
if (modifier != EXPAND_CONST_ADDRESS
&& modifier != EXPAND_INITIALIZER
&& modifier != EXPAND_MEMORY
&& TREE_CODE (array) == CONSTRUCTOR
&& ! TREE_SIDE_EFFECTS (array)
&& TREE_CODE (index) == INTEGER_CST)
{
unsigned HOST_WIDE_INT ix;
tree field, value;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (array), ix,
field, value)
if (tree_int_cst_equal (field, index))
{
if (!TREE_SIDE_EFFECTS (value))
return expand_expr (fold (value), target, tmode, modifier);
break;
}
}
else if (optimize >= 1
&& modifier != EXPAND_CONST_ADDRESS
&& modifier != EXPAND_INITIALIZER
&& modifier != EXPAND_MEMORY
&& TREE_READONLY (array) && ! TREE_SIDE_EFFECTS (array)
&& TREE_CODE (array) == VAR_DECL && DECL_INITIAL (array)
&& TREE_CODE (DECL_INITIAL (array)) != ERROR_MARK
&& targetm.binds_local_p (array))
{
if (TREE_CODE (index) == INTEGER_CST)
{
tree init = DECL_INITIAL (array);
if (TREE_CODE (init) == CONSTRUCTOR)
{
unsigned HOST_WIDE_INT ix;
tree field, value;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix,
field, value)
if (tree_int_cst_equal (field, index))
{
if (!TREE_SIDE_EFFECTS (value))
return expand_expr (fold (value), target, tmode,
modifier);
break;
}
}
else if(TREE_CODE (init) == STRING_CST)
{
tree index1 = index;
tree low_bound = array_ref_low_bound (exp);
index1 = fold_convert (sizetype, TREE_OPERAND (exp, 1));
if (! integer_zerop (low_bound))
index1 = size_diffop (index1, fold_convert (sizetype,
low_bound));
if (0 > compare_tree_int (index1,
TREE_STRING_LENGTH (init)))
{
tree type = TREE_TYPE (TREE_TYPE (init));
enum machine_mode mode = TYPE_MODE (type);
if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) == 1)
return gen_int_mode (TREE_STRING_POINTER (init)
[TREE_INT_CST_LOW (index1)],
mode);
}
}
}
}
}
goto normal_inner_ref;
case COMPONENT_REF:
if (TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR)
{
unsigned HOST_WIDE_INT idx;
tree field, value;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)),
idx, field, value)
if (field == TREE_OPERAND (exp, 1)
&& (! DECL_BIT_FIELD (field)
|| ((GET_MODE_CLASS (DECL_MODE (field)) == MODE_INT)
&& (GET_MODE_BITSIZE (DECL_MODE (field))
<= HOST_BITS_PER_WIDE_INT))))
{
if (DECL_BIT_FIELD (field)
&& modifier == EXPAND_STACK_PARM)
target = 0;
op0 = expand_expr (value, target, tmode, modifier);
if (DECL_BIT_FIELD (field))
{
HOST_WIDE_INT bitsize = TREE_INT_CST_LOW (DECL_SIZE (field));
enum machine_mode imode = TYPE_MODE (TREE_TYPE (field));
if (TYPE_UNSIGNED (TREE_TYPE (field)))
{
op1 = GEN_INT (((HOST_WIDE_INT) 1 << bitsize) - 1);
op0 = expand_and (imode, op0, op1, target);
}
else
{
tree count
= build_int_cst (NULL_TREE,
GET_MODE_BITSIZE (imode) - bitsize);
op0 = expand_shift (LSHIFT_EXPR, imode, op0, count,
target, 0);
op0 = expand_shift (RSHIFT_EXPR, imode, op0, count,
target, 0);
}
}
return op0;
}
}
goto normal_inner_ref;
case BIT_FIELD_REF:
case ARRAY_RANGE_REF:
normal_inner_ref:
{
enum machine_mode mode1;
HOST_WIDE_INT bitsize, bitpos;
tree offset;
int volatilep = 0;
tree tem = get_inner_reference (exp, &bitsize, &bitpos, &offset,
&mode1, &unsignedp, &volatilep, true);
rtx orig_op0;
gcc_assert (tem != exp);
orig_op0 = op0
= expand_expr (tem,
(TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE
&& (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem)))
!= INTEGER_CST)
&& modifier != EXPAND_STACK_PARM
? target : NULL_RTX),
VOIDmode,
(modifier == EXPAND_INITIALIZER
|| modifier == EXPAND_CONST_ADDRESS
|| modifier == EXPAND_STACK_PARM)
? modifier : EXPAND_NORMAL);
if (CONSTANT_P (op0))
{
enum machine_mode mode = TYPE_MODE (TREE_TYPE (tem));
if (mode != BLKmode && LEGITIMATE_CONSTANT_P (op0)
&& offset == 0
&& bitpos + bitsize <= GET_MODE_BITSIZE (mode))
op0 = force_reg (mode, op0);
else
op0 = validize_mem (force_const_mem (mode, op0));
}
else if (!MEM_P (op0)
&& (offset != 0
|| (bitpos + bitsize > GET_MODE_BITSIZE (GET_MODE (op0)))
|| (code == ARRAY_RANGE_REF && mode == BLKmode)))
{
tree nt = build_qualified_type (TREE_TYPE (tem),
(TYPE_QUALS (TREE_TYPE (tem))
| TYPE_QUAL_CONST));
rtx memloc = assign_temp (nt, 1, 1, 1);
emit_move_insn (memloc, op0);
op0 = memloc;
}
if (offset != 0)
{
rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode,
EXPAND_SUM);
gcc_assert (MEM_P (op0));
#ifdef POINTERS_EXTEND_UNSIGNED
if (GET_MODE (offset_rtx) != Pmode)
offset_rtx = convert_to_mode (Pmode, offset_rtx, 0);
#else
if (GET_MODE (offset_rtx) != ptr_mode)
offset_rtx = convert_to_mode (ptr_mode, offset_rtx, 0);
#endif
if (GET_MODE (op0) == BLKmode
&& GET_MODE (XEXP (op0, 0)) != VOIDmode
&& bitsize != 0
&& (bitpos % bitsize) == 0
&& (bitsize % GET_MODE_ALIGNMENT (mode1)) == 0
&& MEM_ALIGN (op0) == GET_MODE_ALIGNMENT (mode1))
{
op0 = adjust_address (op0, mode1, bitpos / BITS_PER_UNIT);
bitpos = 0;
}
op0 = offset_address (op0, offset_rtx,
highest_pow2_factor (offset));
}
if (MEM_P (op0) && bitpos == 0 && offset != 0
&& is_aligning_offset (offset, tem))
set_mem_align (op0, BIGGEST_ALIGNMENT);
if (MEM_P (op0) && volatilep && ! MEM_VOLATILE_P (op0))
{
if (op0 == orig_op0)
op0 = copy_rtx (op0);
MEM_VOLATILE_P (op0) = 1;
}
if (GET_CODE (op0) == CONCAT)
{
gcc_assert (bitpos == 0
&& bitsize == GET_MODE_BITSIZE (GET_MODE (op0)));
return op0;
}
if (mode1 == VOIDmode
|| REG_P (op0) || GET_CODE (op0) == SUBREG
|| (mode1 != BLKmode && ! direct_load[(int) mode1]
&& GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
&& GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT
&& modifier != EXPAND_CONST_ADDRESS
&& modifier != EXPAND_INITIALIZER)
|| (mode1 != BLKmode
&& (((TYPE_ALIGN (TREE_TYPE (tem)) < GET_MODE_ALIGNMENT (mode)
|| (bitpos % GET_MODE_ALIGNMENT (mode) != 0)
|| (MEM_P (op0)
&& (MEM_ALIGN (op0) < GET_MODE_ALIGNMENT (mode1)
|| (bitpos % GET_MODE_ALIGNMENT (mode1) != 0))))
&& ((modifier == EXPAND_CONST_ADDRESS
|| modifier == EXPAND_INITIALIZER)
? STRICT_ALIGNMENT
: SLOW_UNALIGNED_ACCESS (mode1, MEM_ALIGN (op0))))
|| (bitpos % BITS_PER_UNIT != 0)))
|| (bitsize >= 0
&& TYPE_SIZE (TREE_TYPE (exp))
&& TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) == INTEGER_CST
&& 0 != compare_tree_int (TYPE_SIZE (TREE_TYPE (exp)),
bitsize)))
{
enum machine_mode ext_mode = mode;
if (ext_mode == BLKmode
&& ! (target != 0 && MEM_P (op0)
&& MEM_P (target)
&& bitpos % BITS_PER_UNIT == 0))
ext_mode = mode_for_size (bitsize, MODE_INT, 1);
if (ext_mode == BLKmode)
{
if (target == 0)
target = assign_temp (type, 0, 1, 1);
if (bitsize == 0)
return target;
gcc_assert (MEM_P (op0)
&& (!target || MEM_P (target))
&& !(bitpos % BITS_PER_UNIT));
emit_block_move (target,
adjust_address (op0, VOIDmode,
bitpos / BITS_PER_UNIT),
GEN_INT ((bitsize + BITS_PER_UNIT - 1)
/ BITS_PER_UNIT),
(modifier == EXPAND_STACK_PARM
? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
return target;
}
op0 = validize_mem (op0);
if (MEM_P (op0) && REG_P (XEXP (op0, 0)))
mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0));
op0 = extract_bit_field (op0, bitsize, bitpos, unsignedp,
(modifier == EXPAND_STACK_PARM
? NULL_RTX : target),
ext_mode, ext_mode);
if (TREE_CODE (type) == RECORD_TYPE && BYTES_BIG_ENDIAN
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
&& bitsize < (HOST_WIDE_INT) GET_MODE_BITSIZE (GET_MODE (op0)))
op0 = expand_shift (LSHIFT_EXPR, GET_MODE (op0), op0,
size_int (GET_MODE_BITSIZE (GET_MODE (op0))
- bitsize),
op0, 1);
if (mode == BLKmode)
{
rtx new
= assign_stack_temp_for_type
(ext_mode, GET_MODE_BITSIZE (ext_mode), 0, type);
emit_move_insn (new, op0);
op0 = copy_rtx (new);
PUT_MODE (op0, BLKmode);
set_mem_attributes (op0, exp, 1);
}
return op0;
}
if (mode == BLKmode)
mode1 = BLKmode;
if (modifier == EXPAND_CONST_ADDRESS
|| modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
op0 = adjust_address_nv (op0, mode1, bitpos / BITS_PER_UNIT);
else
op0 = adjust_address (op0, mode1, bitpos / BITS_PER_UNIT);
if (op0 == orig_op0)
op0 = copy_rtx (op0);
set_mem_attributes (op0, exp, 0);
if (REG_P (XEXP (op0, 0)))
mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0));
MEM_VOLATILE_P (op0) |= volatilep;
if (mode == mode1 || mode1 == BLKmode || mode1 == tmode
|| modifier == EXPAND_CONST_ADDRESS
|| modifier == EXPAND_INITIALIZER)
return op0;
else if (target == 0)
target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
convert_move (target, op0, unsignedp);
return target;
}
case OBJ_TYPE_REF:
return expand_expr (OBJ_TYPE_REF_EXPR (exp), target, tmode, modifier);
case CALL_EXPR:
if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
== FUNCTION_DECL)
&& DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
{
if (DECL_BUILT_IN_CLASS (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
== BUILT_IN_FRONTEND)
return lang_hooks.expand_expr (exp, original_target,
tmode, modifier,
alt_rtl);
else
return expand_builtin (exp, target, subtarget, tmode, ignore);
}
return expand_call (exp, target, ignore);
case NON_LVALUE_EXPR:
case NOP_EXPR:
case CONVERT_EXPR:
if (TREE_OPERAND (exp, 0) == error_mark_node)
return const0_rtx;
if (TREE_CODE (type) == UNION_TYPE)
{
tree valtype = TREE_TYPE (TREE_OPERAND (exp, 0));
if (mode == BLKmode && TYPE_MODE (valtype) == BLKmode)
{
rtx result = expand_expr (TREE_OPERAND (exp, 0), target, tmode,
modifier);
result = copy_rtx (result);
set_mem_attributes (result, exp, 0);
return result;
}
if (target == 0)
{
if (TYPE_MODE (type) != BLKmode)
target = gen_reg_rtx (TYPE_MODE (type));
else
target = assign_temp (type, 0, 1, 1);
}
if (MEM_P (target))
store_expr (TREE_OPERAND (exp, 0),
adjust_address (target, TYPE_MODE (valtype), 0),
modifier == EXPAND_STACK_PARM);
else
{
gcc_assert (REG_P (target));
store_field (target,
MIN ((int_size_in_bytes (TREE_TYPE
(TREE_OPERAND (exp, 0)))
* BITS_PER_UNIT),
(HOST_WIDE_INT) GET_MODE_BITSIZE (mode)),
0, TYPE_MODE (valtype), TREE_OPERAND (exp, 0),
type, 0);
}
return target;
}
if (mode == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
{
op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode,
modifier);
if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) != unsignedp
&& GET_CODE (op0) == SUBREG)
SUBREG_PROMOTED_VAR_P (op0) = 0;
return REDUCE_BIT_FIELD (op0);
}
#ifdef TARGET_ARM
if (mode == SImode
&& TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == DImode
&& TREE_CODE (TREE_OPERAND (exp, 0)) == RSHIFT_EXPR
&& TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) == INTEGER_CST
&& TREE_INT_CST_HIGH (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) == 0
&& TREE_INT_CST_LOW (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) == 32)
{
op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0), NULL_RTX,
DImode, 0);
op0 = simplify_gen_subreg (SImode, op0, DImode, 4);
}
else
#endif
op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode,
modifier == EXPAND_SUM ? EXPAND_NORMAL : modifier);
if (GET_MODE (op0) == mode)
;
else if (CONSTANT_P (op0))
{
tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
enum machine_mode inner_mode = TYPE_MODE (inner_type);
if (modifier == EXPAND_INITIALIZER)
op0 = simplify_gen_subreg (mode, op0, inner_mode,
subreg_lowpart_offset (mode,
inner_mode));
else
op0= convert_modes (mode, inner_mode, op0,
TYPE_UNSIGNED (inner_type));
}
else if (modifier == EXPAND_INITIALIZER)
op0 = gen_rtx_fmt_e (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, mode, op0);
else if (target == 0)
op0 = convert_to_mode (mode, op0,
TYPE_UNSIGNED (TREE_TYPE
(TREE_OPERAND (exp, 0))));
else
{
convert_move (target, op0,
TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
op0 = target;
}
return REDUCE_BIT_FIELD (op0);
case VIEW_CONVERT_EXPR:
op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, modifier);
if (TYPE_MODE (type) == GET_MODE (op0))
;
else if (TYPE_MODE (type) != BLKmode && GET_MODE (op0) != BLKmode
&& GET_MODE_SIZE (TYPE_MODE (type))
== GET_MODE_SIZE (GET_MODE (op0)))
{
if (GET_CODE (op0) == SUBREG)
op0 = force_reg (GET_MODE (op0), op0);
op0 = gen_lowpart (TYPE_MODE (type), op0);
}
else if (SCALAR_INT_MODE_P (GET_MODE (op0))
&& SCALAR_INT_MODE_P (TYPE_MODE (type)))
op0 = convert_modes (TYPE_MODE (type), GET_MODE (op0), op0,
TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
else if (!MEM_P (op0))
{
tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
gcc_assert (!TREE_ADDRESSABLE (exp));
if (target == 0 || GET_MODE (target) != TYPE_MODE (inner_type))
target
= assign_stack_temp_for_type
(TYPE_MODE (inner_type),
GET_MODE_SIZE (TYPE_MODE (inner_type)), 0, inner_type);
emit_move_insn (target, op0);
op0 = target;
}
if (MEM_P (op0))
{
op0 = copy_rtx (op0);
if (TYPE_ALIGN_OK (type))
set_mem_align (op0, MAX (MEM_ALIGN (op0), TYPE_ALIGN (type)));
else if (TYPE_MODE (type) != BLKmode && STRICT_ALIGNMENT
&& MEM_ALIGN (op0) < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
{
tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
HOST_WIDE_INT temp_size
= MAX (int_size_in_bytes (inner_type),
(HOST_WIDE_INT) GET_MODE_SIZE (TYPE_MODE (type)));
rtx new = assign_stack_temp_for_type (TYPE_MODE (type),
temp_size, 0, type);
rtx new_with_op0_mode = adjust_address (new, GET_MODE (op0), 0);
gcc_assert (!TREE_ADDRESSABLE (exp));
if (GET_MODE (op0) == BLKmode)
emit_block_move (new_with_op0_mode, op0,
GEN_INT (GET_MODE_SIZE (TYPE_MODE (type))),
(modifier == EXPAND_STACK_PARM
? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
else
emit_move_insn (new_with_op0_mode, op0);
op0 = new;
}
op0 = adjust_address (op0, TYPE_MODE (type), 0);
}
return op0;
case PLUS_EXPR:
if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR
&& TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) == INTEGER_CST
&& TREE_CODE (TREE_OPERAND (exp, 1)) == VAR_DECL
&& (DECL_RTL (TREE_OPERAND (exp, 1)) == frame_pointer_rtx
|| DECL_RTL (TREE_OPERAND (exp, 1)) == stack_pointer_rtx
|| DECL_RTL (TREE_OPERAND (exp, 1)) == arg_pointer_rtx))
{
tree t = TREE_OPERAND (exp, 1);
TREE_OPERAND (exp, 1) = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
TREE_OPERAND (TREE_OPERAND (exp, 0), 0) = t;
}
if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER
|| (mode == ptr_mode && (unsignedp || ! flag_trapv)))
{
if (modifier == EXPAND_STACK_PARM)
target = 0;
if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& TREE_CONSTANT (TREE_OPERAND (exp, 1)))
{
rtx constant_part;
op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode,
EXPAND_SUM);
constant_part
= immed_double_const (TREE_INT_CST_LOW (TREE_OPERAND (exp, 0)),
(HOST_WIDE_INT) 0,
TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1))));
op1 = plus_constant (op1, INTVAL (constant_part));
if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
op1 = force_operand (op1, target);
return REDUCE_BIT_FIELD (op1);
}
else if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& TREE_CONSTANT (TREE_OPERAND (exp, 0)))
{
rtx constant_part;
op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode,
(modifier == EXPAND_INITIALIZER
? EXPAND_INITIALIZER : EXPAND_SUM));
if (! CONSTANT_P (op0))
{
op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
VOIDmode, modifier);
if (modifier == EXPAND_SUM
|| modifier == EXPAND_INITIALIZER)
return simplify_gen_binary (PLUS, mode, op0, op1);
goto binop2;
}
constant_part
= immed_double_const (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)),
(HOST_WIDE_INT) 0,
TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))));
op0 = plus_constant (op0, INTVAL (constant_part));
if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
op0 = force_operand (op0, target);
return REDUCE_BIT_FIELD (op0);
}
}
if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
|| mode != ptr_mode)
{
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, 0);
if (op0 == const0_rtx)
return op1;
if (op1 == const0_rtx)
return op0;
goto binop2;
}
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, modifier);
return REDUCE_BIT_FIELD (simplify_gen_binary (PLUS, mode, op0, op1));
case MINUS_EXPR:
if ((modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
&& really_constant_p (TREE_OPERAND (exp, 0))
&& really_constant_p (TREE_OPERAND (exp, 1)))
{
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
NULL_RTX, &op0, &op1, modifier);
if (GET_CODE (op1) == CONST_INT)
return REDUCE_BIT_FIELD (plus_constant (op0, - INTVAL (op1)));
else
return REDUCE_BIT_FIELD (gen_rtx_MINUS (mode, op0, op1));
}
if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
|| mode != ptr_mode)
goto binop;
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, modifier);
if (GET_CODE (op1) == CONST_INT)
{
op1 = negate_rtx (mode, op1);
return REDUCE_BIT_FIELD (simplify_gen_binary (PLUS, mode, op0, op1));
}
goto binop2;
case MULT_EXPR:
if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST)
{
tree t1 = TREE_OPERAND (exp, 0);
TREE_OPERAND (exp, 0) = TREE_OPERAND (exp, 1);
TREE_OPERAND (exp, 1) = t1;
}
if (modifier == EXPAND_SUM && mode == ptr_mode
&& host_integerp (TREE_OPERAND (exp, 1), 0))
{
tree exp1 = TREE_OPERAND (exp, 1);
op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode,
EXPAND_SUM);
if (!REG_P (op0))
op0 = force_operand (op0, NULL_RTX);
if (!REG_P (op0))
op0 = copy_to_mode_reg (mode, op0);
return REDUCE_BIT_FIELD (gen_rtx_MULT (mode, op0,
gen_int_mode (tree_low_cst (exp1, 0),
TYPE_MODE (TREE_TYPE (exp1)))));
}
if (modifier == EXPAND_STACK_PARM)
target = 0;
subexp0 = TREE_OPERAND (exp, 0);
subexp1 = TREE_OPERAND (exp, 1);
if (TREE_CODE (subexp0) == NOP_EXPR
&& TREE_CODE (subexp1) == NOP_EXPR
&& TREE_CODE (type) == INTEGER_TYPE
&& (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (subexp0, 0)))
< TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))
&& (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (subexp0, 0)))
== TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (subexp1, 0))))
&& (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (subexp0, 0)))
!= TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (subexp1, 0)))))
{
enum machine_mode innermode
= TYPE_MODE (TREE_TYPE (TREE_OPERAND (subexp0, 0)));
this_optab = usmul_widen_optab;
if (mode == GET_MODE_WIDER_MODE (innermode))
{
if (this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
{
if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (subexp0, 0))))
expand_operands (TREE_OPERAND (subexp0, 0),
TREE_OPERAND (subexp1, 0),
NULL_RTX, &op0, &op1, 0);
else
expand_operands (TREE_OPERAND (subexp0, 0),
TREE_OPERAND (subexp1, 0),
NULL_RTX, &op1, &op0, 0);
goto binop3;
}
}
}
else if (TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR
&& TREE_CODE (type) == INTEGER_TYPE
&& (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
< TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))
&& ((TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
&& int_fits_type_p (TREE_OPERAND (exp, 1),
TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
&& ((GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1))))
> HOST_BITS_PER_WIDE_INT)
|| exact_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))) < 0))
||
(TREE_CODE (TREE_OPERAND (exp, 1)) == NOP_EXPR
&& (TYPE_PRECISION (TREE_TYPE
(TREE_OPERAND (TREE_OPERAND (exp, 1), 0)))
== TYPE_PRECISION (TREE_TYPE
(TREE_OPERAND
(TREE_OPERAND (exp, 0), 0))))
&& (TYPE_UNSIGNED (TREE_TYPE
(TREE_OPERAND (TREE_OPERAND (exp, 1), 0)))
== TYPE_UNSIGNED (TREE_TYPE
(TREE_OPERAND
(TREE_OPERAND (exp, 0), 0)))))))
{
tree op0type = TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0));
enum machine_mode innermode = TYPE_MODE (op0type);
bool zextend_p = TYPE_UNSIGNED (op0type);
optab other_optab = zextend_p ? smul_widen_optab : umul_widen_optab;
this_optab = zextend_p ? umul_widen_optab : smul_widen_optab;
if (mode == GET_MODE_2XWIDER_MODE (innermode))
{
if (this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
{
if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
expand_operands (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
TREE_OPERAND (exp, 1),
NULL_RTX, &op0, &op1, EXPAND_NORMAL);
else
expand_operands (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
TREE_OPERAND (TREE_OPERAND (exp, 1), 0),
NULL_RTX, &op0, &op1, EXPAND_NORMAL);
goto binop3;
}
else if (other_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
&& innermode == word_mode)
{
rtx htem, hipart;
op0 = expand_normal (TREE_OPERAND (TREE_OPERAND (exp, 0), 0));
if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
op1 = convert_modes (innermode, mode,
expand_normal (TREE_OPERAND (exp, 1)),
unsignedp);
else
op1 = expand_normal (TREE_OPERAND (TREE_OPERAND (exp, 1), 0));
temp = expand_binop (mode, other_optab, op0, op1, target,
unsignedp, OPTAB_LIB_WIDEN);
hipart = gen_highpart (innermode, temp);
htem = expand_mult_highpart_adjust (innermode, hipart,
op0, op1, hipart,
zextend_p);
if (htem != hipart)
emit_move_insn (hipart, htem);
return REDUCE_BIT_FIELD (temp);
}
}
}
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, 0);
return REDUCE_BIT_FIELD (expand_mult (mode, op0, op1, target, unsignedp));
case TRUNC_DIV_EXPR:
case FLOOR_DIV_EXPR:
case CEIL_DIV_EXPR:
case ROUND_DIV_EXPR:
case EXACT_DIV_EXPR:
if (modifier == EXPAND_STACK_PARM)
target = 0;
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, 0);
return expand_divmod (0, code, mode, op0, op1, target, unsignedp);
case RDIV_EXPR:
goto binop;
case TRUNC_MOD_EXPR:
case FLOOR_MOD_EXPR:
case CEIL_MOD_EXPR:
case ROUND_MOD_EXPR:
if (modifier == EXPAND_STACK_PARM)
target = 0;
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, 0);
return expand_divmod (1, code, mode, op0, op1, target, unsignedp);
case FIX_ROUND_EXPR:
case FIX_FLOOR_EXPR:
case FIX_CEIL_EXPR:
gcc_unreachable ();
case FIX_TRUNC_EXPR:
op0 = expand_normal (TREE_OPERAND (exp, 0));
if (target == 0 || modifier == EXPAND_STACK_PARM)
target = gen_reg_rtx (mode);
expand_fix (target, op0, unsignedp);
return target;
case FLOAT_EXPR:
op0 = expand_normal (TREE_OPERAND (exp, 0));
if (target == 0 || modifier == EXPAND_STACK_PARM)
target = gen_reg_rtx (mode);
if (GET_MODE (op0) == VOIDmode)
op0 = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))),
op0);
expand_float (target, op0,
TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
return target;
case NEGATE_EXPR:
op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
if (modifier == EXPAND_STACK_PARM)
target = 0;
temp = expand_unop (mode,
optab_for_tree_code (NEGATE_EXPR, type),
op0, target, 0);
gcc_assert (temp);
return REDUCE_BIT_FIELD (temp);
case ABS_EXPR:
op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
if (modifier == EXPAND_STACK_PARM)
target = 0;
gcc_assert (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
&& GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT);
if (TYPE_UNSIGNED (type))
return op0;
return expand_abs (mode, op0, target, unsignedp,
safe_from_p (target, TREE_OPERAND (exp, 0), 1));
case MAX_EXPR:
case MIN_EXPR:
target = original_target;
if (target == 0
|| modifier == EXPAND_STACK_PARM
|| (MEM_P (target) && MEM_VOLATILE_P (target))
|| GET_MODE (target) != mode
|| (REG_P (target)
&& REGNO (target) < FIRST_PSEUDO_REGISTER))
target = gen_reg_rtx (mode);
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
target, &op0, &op1, 0);
this_optab = optab_for_tree_code (code, type);
temp = expand_binop (mode, this_optab, op0, op1, target, unsignedp,
OPTAB_WIDEN);
if (temp != 0)
return temp;
if (! REG_P (target))
target = gen_reg_rtx (mode);
if (target != op0 && target == op1)
{
temp = op0;
op0 = op1;
op1 = temp;
}
if (! CONSTANT_P (op1))
op1 = force_reg (mode, op1);
{
enum rtx_code comparison_code;
rtx cmpop1 = op1;
if (code == MAX_EXPR)
comparison_code = unsignedp ? GEU : GE;
else
comparison_code = unsignedp ? LEU : LE;
if (op1 == const1_rtx)
{
cmpop1 = const0_rtx;
if (code == MAX_EXPR)
comparison_code = unsignedp ? NE : GT;
}
if (op1 == constm1_rtx && !unsignedp)
{
cmpop1 = const0_rtx;
if (code == MIN_EXPR)
comparison_code = LT;
}
#ifdef HAVE_conditional_move
if (can_conditionally_move_p (mode))
{
rtx insn;
do_pending_stack_adjust ();
start_sequence ();
insn = emit_conditional_move (target, comparison_code,
op0, cmpop1, mode,
op0, op1, mode,
unsignedp);
if (insn)
{
rtx seq = get_insns ();
end_sequence ();
emit_insn (seq);
return target;
}
end_sequence ();
}
#endif
if (target != op0)
emit_move_insn (target, op0);
temp = gen_label_rtx ();
do_compare_rtx_and_jump (target, cmpop1, comparison_code,
unsignedp, mode, NULL_RTX, NULL_RTX, temp);
}
emit_move_insn (target, op1);
emit_label (temp);
return target;
case BIT_NOT_EXPR:
op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
if (modifier == EXPAND_STACK_PARM)
target = 0;
temp = expand_unop (mode, one_cmpl_optab, op0, target, 1);
gcc_assert (temp);
return temp;
case TRUTH_AND_EXPR:
code = BIT_AND_EXPR;
goto binop;
case BIT_AND_EXPR:
temp = look_for_bytemanip (exp, subtarget);
if (temp)
return REDUCE_BIT_FIELD (temp);
goto binop;
case TRUTH_OR_EXPR:
code = BIT_IOR_EXPR;
goto binop;
case BIT_IOR_EXPR:
temp = look_for_bytemanip (exp, subtarget);
if (temp)
return REDUCE_BIT_FIELD (temp);
goto binop;
case TRUTH_XOR_EXPR:
code = BIT_XOR_EXPR;
case BIT_XOR_EXPR:
goto binop;
case LSHIFT_EXPR:
case RSHIFT_EXPR:
temp = look_for_bytemanip (exp, subtarget);
if (temp)
return REDUCE_BIT_FIELD (temp);
case LROTATE_EXPR:
case RROTATE_EXPR:
if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1), 1))
subtarget = 0;
if (modifier == EXPAND_STACK_PARM)
target = 0;
op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
return expand_shift (code, mode, op0, TREE_OPERAND (exp, 1), target,
unsignedp);
case LT_EXPR:
case LE_EXPR:
case GT_EXPR:
case GE_EXPR:
case EQ_EXPR:
case NE_EXPR:
case UNORDERED_EXPR:
case ORDERED_EXPR:
case UNLT_EXPR:
case UNLE_EXPR:
case UNGT_EXPR:
case UNGE_EXPR:
case UNEQ_EXPR:
case LTGT_EXPR:
temp = do_store_flag (exp,
modifier != EXPAND_STACK_PARM ? target : NULL_RTX,
tmode != VOIDmode ? tmode : mode, 0);
if (temp != 0)
return temp;
if (code == NE_EXPR && integer_zerop (TREE_OPERAND (exp, 1))
&& original_target
&& REG_P (original_target)
&& (GET_MODE (original_target)
== TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
{
temp = expand_expr (TREE_OPERAND (exp, 0), original_target,
VOIDmode, 0);
if (GET_CODE (temp) == CONST_INT)
{
if (INTVAL (temp) != 0)
emit_move_insn (target, const1_rtx);
else
emit_move_insn (target, const0_rtx);
return target;
}
if (temp != original_target)
{
enum machine_mode mode1 = GET_MODE (temp);
if (mode1 == VOIDmode)
mode1 = tmode != VOIDmode ? tmode : mode;
temp = copy_to_mode_reg (mode1, temp);
}
op1 = gen_label_rtx ();
emit_cmp_and_jump_insns (temp, const0_rtx, EQ, NULL_RTX,
GET_MODE (temp), unsignedp, op1);
emit_move_insn (temp, const1_rtx);
emit_label (op1);
return temp;
}
if (! ignore
&& (target == 0
|| modifier == EXPAND_STACK_PARM
|| ! safe_from_p (target, exp, 1)
|| (!optimize && REG_P (target)
&& REGNO (target) < FIRST_PSEUDO_REGISTER)))
target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
if (target)
emit_move_insn (target, const0_rtx);
op1 = gen_label_rtx ();
jumpifnot (exp, op1);
if (target)
emit_move_insn (target, const1_rtx);
emit_label (op1);
return ignore ? const0_rtx : target;
case TRUTH_NOT_EXPR:
if (modifier == EXPAND_STACK_PARM)
target = 0;
op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0);
temp = expand_binop (mode, xor_optab, op0, const1_rtx,
target, 1, OPTAB_LIB_WIDEN);
gcc_assert (temp);
return temp;
case STATEMENT_LIST:
{
tree_stmt_iterator iter;
gcc_assert (ignore);
for (iter = tsi_start (exp); !tsi_end_p (iter); tsi_next (&iter))
expand_expr (tsi_stmt (iter), const0_rtx, VOIDmode, modifier);
}
return const0_rtx;
case COND_EXPR:
gcc_assert (!VOID_TYPE_P (TREE_TYPE (exp)));
gcc_assert (!TREE_ADDRESSABLE (type)
&& !ignore
&& TREE_TYPE (TREE_OPERAND (exp, 1)) != void_type_node
&& TREE_TYPE (TREE_OPERAND (exp, 2)) != void_type_node);
if (modifier != EXPAND_STACK_PARM
&& original_target
&& safe_from_p (original_target, TREE_OPERAND (exp, 0), 1)
&& GET_MODE (original_target) == mode
#ifdef HAVE_conditional_move
&& (! can_conditionally_move_p (mode)
|| REG_P (original_target))
#endif
&& !MEM_P (original_target))
temp = original_target;
else
temp = assign_temp (type, 0, 0, 1);
do_pending_stack_adjust ();
NO_DEFER_POP;
op0 = gen_label_rtx ();
op1 = gen_label_rtx ();
jumpifnot (TREE_OPERAND (exp, 0), op0);
store_expr (TREE_OPERAND (exp, 1), temp,
modifier == EXPAND_STACK_PARM);
emit_jump_insn (gen_jump (op1));
emit_barrier ();
emit_label (op0);
store_expr (TREE_OPERAND (exp, 2), temp,
modifier == EXPAND_STACK_PARM);
emit_label (op1);
OK_DEFER_POP;
return temp;
case VEC_COND_EXPR:
target = expand_vec_cond_expr (exp, target);
return target;
case MODIFY_EXPR:
{
tree lhs = TREE_OPERAND (exp, 0);
tree rhs = TREE_OPERAND (exp, 1);
gcc_assert (ignore);
if (TREE_CODE (lhs) == COMPONENT_REF
&& (TREE_CODE (rhs) == BIT_IOR_EXPR
|| TREE_CODE (rhs) == BIT_AND_EXPR)
&& TREE_OPERAND (rhs, 0) == lhs
&& TREE_CODE (TREE_OPERAND (rhs, 1)) == COMPONENT_REF
&& integer_onep (DECL_SIZE (TREE_OPERAND (lhs, 1)))
&& integer_onep (DECL_SIZE (TREE_OPERAND (TREE_OPERAND (rhs, 1), 1))))
{
rtx label = gen_label_rtx ();
int value = TREE_CODE (rhs) == BIT_IOR_EXPR;
do_jump (TREE_OPERAND (rhs, 1),
value ? label : 0,
value ? 0 : label);
expand_assignment (lhs, build_int_cst (TREE_TYPE (rhs), value));
do_pending_stack_adjust ();
emit_label (label);
return const0_rtx;
}
expand_assignment (lhs, rhs);
return const0_rtx;
}
case RETURN_EXPR:
if (!TREE_OPERAND (exp, 0))
expand_null_return ();
else
expand_return (TREE_OPERAND (exp, 0));
return const0_rtx;
case ADDR_EXPR:
return expand_expr_addr_expr (exp, target, tmode, modifier);
case COMPLEX_EXPR:
op0 = expand_normal (TREE_OPERAND (exp, 0));
op1 = expand_normal (TREE_OPERAND (exp, 1));
if (!target)
target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
write_complex_part (target, op0, false);
write_complex_part (target, op1, true);
return target;
case REALPART_EXPR:
op0 = expand_normal (TREE_OPERAND (exp, 0));
return read_complex_part (op0, false);
case IMAGPART_EXPR:
op0 = expand_normal (TREE_OPERAND (exp, 0));
return read_complex_part (op0, true);
case RESX_EXPR:
expand_resx_expr (exp);
return const0_rtx;
case TRY_CATCH_EXPR:
case CATCH_EXPR:
case EH_FILTER_EXPR:
case TRY_FINALLY_EXPR:
gcc_unreachable ();
case WITH_CLEANUP_EXPR:
case CLEANUP_POINT_EXPR:
case TARGET_EXPR:
case CASE_LABEL_EXPR:
case VA_ARG_EXPR:
case BIND_EXPR:
case INIT_EXPR:
case CONJ_EXPR:
case COMPOUND_EXPR:
case PREINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTINCREMENT_EXPR:
case POSTDECREMENT_EXPR:
case LOOP_EXPR:
case EXIT_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
gcc_unreachable ();
case EXC_PTR_EXPR:
return get_exception_pointer (cfun);
case FILTER_EXPR:
return get_exception_filter (cfun);
case FDESC_EXPR:
gcc_unreachable ();
case SWITCH_EXPR:
expand_case (exp);
return const0_rtx;
case LABEL_EXPR:
expand_label (TREE_OPERAND (exp, 0));
return const0_rtx;
case ASM_EXPR:
expand_asm_expr (exp);
return const0_rtx;
case WITH_SIZE_EXPR:
return expand_expr_real (TREE_OPERAND (exp, 0), original_target, tmode,
modifier, alt_rtl);
case REALIGN_LOAD_EXPR:
{
tree oprnd0 = TREE_OPERAND (exp, 0);
tree oprnd1 = TREE_OPERAND (exp, 1);
tree oprnd2 = TREE_OPERAND (exp, 2);
rtx op2;
this_optab = optab_for_tree_code (code, type);
expand_operands (oprnd0, oprnd1, NULL_RTX, &op0, &op1, EXPAND_NORMAL);
op2 = expand_normal (oprnd2);
temp = expand_ternary_op (mode, this_optab, op0, op1, op2,
target, unsignedp);
gcc_assert (temp);
return temp;
}
case DOT_PROD_EXPR:
{
tree oprnd0 = TREE_OPERAND (exp, 0);
tree oprnd1 = TREE_OPERAND (exp, 1);
tree oprnd2 = TREE_OPERAND (exp, 2);
rtx op2;
expand_operands (oprnd0, oprnd1, NULL_RTX, &op0, &op1, EXPAND_NORMAL);
op2 = expand_normal (oprnd2);
target = expand_widen_pattern_expr (exp, op0, op1, op2,
target, unsignedp);
return target;
}
case WIDEN_SUM_EXPR:
{
tree oprnd0 = TREE_OPERAND (exp, 0);
tree oprnd1 = TREE_OPERAND (exp, 1);
expand_operands (oprnd0, oprnd1, NULL_RTX, &op0, &op1, 0);
target = expand_widen_pattern_expr (exp, op0, NULL_RTX, op1,
target, unsignedp);
return target;
}
case REDUC_MAX_EXPR:
case REDUC_MIN_EXPR:
case REDUC_PLUS_EXPR:
{
op0 = expand_normal (TREE_OPERAND (exp, 0));
this_optab = optab_for_tree_code (code, type);
temp = expand_unop (mode, this_optab, op0, target, unsignedp);
gcc_assert (temp);
return temp;
}
case VEC_LSHIFT_EXPR:
case VEC_RSHIFT_EXPR:
{
target = expand_vec_shift_expr (exp, target);
return target;
}
default:
return lang_hooks.expand_expr (exp, original_target, tmode,
modifier, alt_rtl);
}
binop:
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, 0);
binop2:
this_optab = optab_for_tree_code (code, type);
binop3:
if (modifier == EXPAND_STACK_PARM)
target = 0;
temp = expand_binop (mode, this_optab, op0, op1, target,
unsignedp, OPTAB_LIB_WIDEN);
gcc_assert (temp);
return REDUCE_BIT_FIELD (temp);
}
#undef REDUCE_BIT_FIELD
static rtx
reduce_to_bit_field_precision (rtx exp, rtx target, tree type)
{
HOST_WIDE_INT prec = TYPE_PRECISION (type);
if (target && GET_MODE (target) != GET_MODE (exp))
target = 0;
if (GET_CODE (exp) == CONST_INT)
{
HOST_WIDE_INT value = INTVAL (exp);
tree t = build_int_cst_type (type, value);
return expand_expr (t, target, VOIDmode, EXPAND_NORMAL);
}
else if (TYPE_UNSIGNED (type))
{
rtx mask;
if (prec < HOST_BITS_PER_WIDE_INT)
mask = immed_double_const (((unsigned HOST_WIDE_INT) 1 << prec) - 1, 0,
GET_MODE (exp));
else
mask = immed_double_const ((unsigned HOST_WIDE_INT) -1,
((unsigned HOST_WIDE_INT) 1
<< (prec - HOST_BITS_PER_WIDE_INT)) - 1,
GET_MODE (exp));
return expand_and (GET_MODE (exp), exp, mask, target);
}
else
{
tree count = build_int_cst (NULL_TREE,
GET_MODE_BITSIZE (GET_MODE (exp)) - prec);
exp = expand_shift (LSHIFT_EXPR, GET_MODE (exp), exp, count, target, 0);
return expand_shift (RSHIFT_EXPR, GET_MODE (exp), exp, count, target, 0);
}
}
static int
is_aligning_offset (tree offset, tree exp)
{
while (TREE_CODE (offset) == NON_LVALUE_EXPR
|| TREE_CODE (offset) == NOP_EXPR
|| TREE_CODE (offset) == CONVERT_EXPR)
offset = TREE_OPERAND (offset, 0);
if (TREE_CODE (offset) != BIT_AND_EXPR
|| !host_integerp (TREE_OPERAND (offset, 1), 1)
|| compare_tree_int (TREE_OPERAND (offset, 1),
BIGGEST_ALIGNMENT / BITS_PER_UNIT) <= 0
|| !exact_log2 (tree_low_cst (TREE_OPERAND (offset, 1), 1) + 1) < 0)
return 0;
offset = TREE_OPERAND (offset, 0);
while (TREE_CODE (offset) == NON_LVALUE_EXPR
|| TREE_CODE (offset) == NOP_EXPR
|| TREE_CODE (offset) == CONVERT_EXPR)
offset = TREE_OPERAND (offset, 0);
if (TREE_CODE (offset) != NEGATE_EXPR)
return 0;
offset = TREE_OPERAND (offset, 0);
while (TREE_CODE (offset) == NON_LVALUE_EXPR
|| TREE_CODE (offset) == NOP_EXPR
|| TREE_CODE (offset) == CONVERT_EXPR)
offset = TREE_OPERAND (offset, 0);
return TREE_CODE (offset) == ADDR_EXPR && TREE_OPERAND (offset, 0) == exp;
}
tree
string_constant (tree arg, tree *ptr_offset)
{
tree array, offset;
STRIP_NOPS (arg);
if (TREE_CODE (arg) == ADDR_EXPR)
{
if (TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST)
{
*ptr_offset = size_zero_node;
return TREE_OPERAND (arg, 0);
}
else if (TREE_CODE (TREE_OPERAND (arg, 0)) == VAR_DECL)
{
array = TREE_OPERAND (arg, 0);
offset = size_zero_node;
}
else if (TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
{
array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1);
if (TREE_CODE (array) != STRING_CST
&& TREE_CODE (array) != VAR_DECL)
return 0;
}
else
return 0;
}
else if (TREE_CODE (arg) == PLUS_EXPR)
{
tree arg0 = TREE_OPERAND (arg, 0);
tree arg1 = TREE_OPERAND (arg, 1);
STRIP_NOPS (arg0);
STRIP_NOPS (arg1);
if (TREE_CODE (arg0) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST
|| TREE_CODE (TREE_OPERAND (arg0, 0)) == VAR_DECL))
{
array = TREE_OPERAND (arg0, 0);
offset = arg1;
}
else if (TREE_CODE (arg1) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST
|| TREE_CODE (TREE_OPERAND (arg1, 0)) == VAR_DECL))
{
array = TREE_OPERAND (arg1, 0);
offset = arg0;
}
else
return 0;
}
else
return 0;
if (TREE_CODE (array) == STRING_CST)
{
*ptr_offset = fold_convert (sizetype, offset);
return array;
}
else if (TREE_CODE (array) == VAR_DECL)
{
int length;
if (DECL_INITIAL (array) == NULL_TREE
|| TREE_CODE (DECL_INITIAL (array)) != STRING_CST)
return 0;
if (! TREE_READONLY (array)
|| TREE_SIDE_EFFECTS (array)
|| ! targetm.binds_local_p (array))
return 0;
if (DECL_SIZE_UNIT (array) == NULL_TREE
|| TREE_CODE (DECL_SIZE_UNIT (array)) != INTEGER_CST
|| (length = TREE_STRING_LENGTH (DECL_INITIAL (array))) <= 0
|| compare_tree_int (DECL_SIZE_UNIT (array), length) < 0)
return 0;
offset = fold_convert (sizetype, offset);
if (compare_tree_int (DECL_SIZE_UNIT (array), length) > 0
&& (! host_integerp (offset, 1)
|| compare_tree_int (offset, length) >= 0))
return 0;
*ptr_offset = offset;
return DECL_INITIAL (array);
}
return 0;
}
static rtx
do_store_flag (tree exp, rtx target, enum machine_mode mode, int only_cheap)
{
enum rtx_code code;
tree arg0, arg1, type;
tree tem;
enum machine_mode operand_mode;
int invert = 0;
int unsignedp;
rtx op0, op1;
enum insn_code icode;
rtx subtarget = target;
rtx result, label;
if (TREE_CODE (exp) == TRUTH_NOT_EXPR)
invert = 1, exp = TREE_OPERAND (exp, 0);
arg0 = TREE_OPERAND (exp, 0);
arg1 = TREE_OPERAND (exp, 1);
if (arg0 == error_mark_node || arg1 == error_mark_node)
return const0_rtx;
type = TREE_TYPE (arg0);
operand_mode = TYPE_MODE (type);
unsignedp = TYPE_UNSIGNED (type);
if (operand_mode == BLKmode)
return 0;
#ifdef HAVE_canonicalize_funcptr_for_compare
if (HAVE_canonicalize_funcptr_for_compare
&& ((TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == POINTER_TYPE
&& (TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))))
== FUNCTION_TYPE))
|| (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 1))) == POINTER_TYPE
&& (TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 1))))
== FUNCTION_TYPE))))
return 0;
#endif
STRIP_NOPS (arg0);
STRIP_NOPS (arg1);
switch (TREE_CODE (exp))
{
case EQ_EXPR:
code = EQ;
break;
case NE_EXPR:
code = NE;
break;
case LT_EXPR:
if (integer_onep (arg1))
arg1 = integer_zero_node, code = unsignedp ? LEU : LE;
else
code = unsignedp ? LTU : LT;
break;
case LE_EXPR:
if (! unsignedp && integer_all_onesp (arg1))
arg1 = integer_zero_node, code = LT;
else
code = unsignedp ? LEU : LE;
break;
case GT_EXPR:
if (! unsignedp && integer_all_onesp (arg1))
arg1 = integer_zero_node, code = GE;
else
code = unsignedp ? GTU : GT;
break;
case GE_EXPR:
if (integer_onep (arg1))
arg1 = integer_zero_node, code = unsignedp ? GTU : GT;
else
code = unsignedp ? GEU : GE;
break;
case UNORDERED_EXPR:
code = UNORDERED;
break;
case ORDERED_EXPR:
code = ORDERED;
break;
case UNLT_EXPR:
code = UNLT;
break;
case UNLE_EXPR:
code = UNLE;
break;
case UNGT_EXPR:
code = UNGT;
break;
case UNGE_EXPR:
code = UNGE;
break;
case UNEQ_EXPR:
code = UNEQ;
break;
case LTGT_EXPR:
code = LTGT;
break;
default:
gcc_unreachable ();
}
if (TREE_CODE (arg0) == REAL_CST || TREE_CODE (arg0) == INTEGER_CST)
{
tem = arg0; arg0 = arg1; arg1 = tem;
code = swap_condition (code);
}
if ((code == NE || code == EQ)
&& TREE_CODE (arg0) == BIT_AND_EXPR && integer_zerop (arg1)
&& integer_pow2p (TREE_OPERAND (arg0, 1)))
{
tree type = lang_hooks.types.type_for_mode (mode, unsignedp);
return expand_expr (fold_single_bit_test (code == NE ? NE_EXPR : EQ_EXPR,
arg0, arg1, type),
target, VOIDmode, EXPAND_NORMAL);
}
if (! can_compare_p (code, operand_mode, ccp_store_flag))
return 0;
icode = setcc_gen_code[(int) code];
if (icode == CODE_FOR_nothing
|| (only_cheap && insn_data[(int) icode].operand[0].mode != mode))
{
if ((code == LT && integer_zerop (arg1))
|| (! only_cheap && code == GE && integer_zerop (arg1)))
;
else if (! only_cheap && (code == NE || code == EQ)
&& TREE_CODE (type) != REAL_TYPE
&& ((abs_optab->handlers[(int) operand_mode].insn_code
!= CODE_FOR_nothing)
|| (ffs_optab->handlers[(int) operand_mode].insn_code
!= CODE_FOR_nothing)))
;
else
return 0;
}
if (! get_subtarget (target)
|| GET_MODE (subtarget) != operand_mode)
subtarget = 0;
expand_operands (arg0, arg1, subtarget, &op0, &op1, 0);
if (target == 0)
target = gen_reg_rtx (mode);
result = emit_store_flag (target, code, op0, op1,
operand_mode, unsignedp, 1);
if (result)
{
if (invert)
result = expand_binop (mode, xor_optab, result, const1_rtx,
result, 0, OPTAB_LIB_WIDEN);
return result;
}
if (!REG_P (target)
|| reg_mentioned_p (target, op0) || reg_mentioned_p (target, op1))
target = gen_reg_rtx (GET_MODE (target));
emit_move_insn (target, invert ? const0_rtx : const1_rtx);
result = compare_from_rtx (op0, op1, code, unsignedp,
operand_mode, NULL_RTX);
if (GET_CODE (result) == CONST_INT)
return (((result == const0_rtx && ! invert)
|| (result != const0_rtx && invert))
? const0_rtx : const1_rtx);
code = GET_CODE (result);
label = gen_label_rtx ();
gcc_assert (bcc_gen_fctn[(int) code]);
emit_jump_insn ((*bcc_gen_fctn[(int) code]) (label));
emit_move_insn (target, invert ? const1_rtx : const0_rtx);
emit_label (label);
return target;
}
#ifndef HAVE_casesi
# define HAVE_casesi 0
# define gen_casesi(a, b, c, d, e) (0)
# define CODE_FOR_casesi CODE_FOR_nothing
#endif
#ifndef CASE_VALUES_THRESHOLD
#define CASE_VALUES_THRESHOLD (HAVE_casesi ? 4 : 5)
#endif
unsigned int
case_values_threshold (void)
{
return CASE_VALUES_THRESHOLD;
}
int
try_casesi (tree index_type, tree index_expr, tree minval, tree range,
rtx table_label ATTRIBUTE_UNUSED, rtx default_label)
{
enum machine_mode index_mode = SImode;
int index_bits = GET_MODE_BITSIZE (index_mode);
rtx op1, op2, index;
enum machine_mode op_mode;
if (! HAVE_casesi)
return 0;
if (GET_MODE_BITSIZE (TYPE_MODE (index_type)) > GET_MODE_BITSIZE (index_mode))
{
enum machine_mode omode = TYPE_MODE (index_type);
rtx rangertx = expand_normal (range);
index_expr = build2 (MINUS_EXPR, index_type,
index_expr, minval);
minval = integer_zero_node;
index = expand_normal (index_expr);
emit_cmp_and_jump_insns (rangertx, index, LTU, NULL_RTX,
omode, 1, default_label);
index = convert_to_mode (index_mode, index, 0);
}
else
{
if (TYPE_MODE (index_type) != index_mode)
{
index_type = lang_hooks.types.type_for_size (index_bits, 0);
index_expr = fold_convert (index_type, index_expr);
}
index = expand_normal (index_expr);
}
do_pending_stack_adjust ();
op_mode = insn_data[(int) CODE_FOR_casesi].operand[0].mode;
if (! (*insn_data[(int) CODE_FOR_casesi].operand[0].predicate)
(index, op_mode))
index = copy_to_mode_reg (op_mode, index);
op1 = expand_normal (minval);
op_mode = insn_data[(int) CODE_FOR_casesi].operand[1].mode;
op1 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (minval)),
op1, TYPE_UNSIGNED (TREE_TYPE (minval)));
if (! (*insn_data[(int) CODE_FOR_casesi].operand[1].predicate)
(op1, op_mode))
op1 = copy_to_mode_reg (op_mode, op1);
op2 = expand_normal (range);
op_mode = insn_data[(int) CODE_FOR_casesi].operand[2].mode;
op2 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (range)),
op2, TYPE_UNSIGNED (TREE_TYPE (range)));
if (! (*insn_data[(int) CODE_FOR_casesi].operand[2].predicate)
(op2, op_mode))
op2 = copy_to_mode_reg (op_mode, op2);
emit_jump_insn (gen_casesi (index, op1, op2,
table_label, default_label));
return 1;
}
#ifndef HAVE_tablejump
#define HAVE_tablejump 0
#define gen_tablejump(x, y) (0)
#endif
static void
do_tablejump (rtx index, enum machine_mode mode, rtx range, rtx table_label,
rtx default_label)
{
rtx temp, vector;
if (INTVAL (range) > cfun->max_jumptable_ents)
cfun->max_jumptable_ents = INTVAL (range);
emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, mode, 1,
default_label);
if (mode != Pmode)
index = convert_to_mode (Pmode, index, 1);
#ifdef PIC_CASE_VECTOR_ADDRESS
if (flag_pic && !REG_P (index))
index = copy_to_mode_reg (Pmode, index);
#endif
index = gen_rtx_PLUS (Pmode,
gen_rtx_MULT (Pmode, index,
GEN_INT (GET_MODE_SIZE (CASE_VECTOR_MODE))),
gen_rtx_LABEL_REF (Pmode, table_label));
#ifdef PIC_CASE_VECTOR_ADDRESS
if (flag_pic)
index = PIC_CASE_VECTOR_ADDRESS (index);
else
#endif
index = memory_address_noforce (CASE_VECTOR_MODE, index);
temp = gen_reg_rtx (CASE_VECTOR_MODE);
vector = gen_const_mem (CASE_VECTOR_MODE, index);
convert_move (temp, vector, 0);
emit_jump_insn (gen_tablejump (temp, table_label));
if (! CASE_VECTOR_PC_RELATIVE && ! flag_pic)
emit_barrier ();
}
int
try_tablejump (tree index_type, tree index_expr, tree minval, tree range,
rtx table_label, rtx default_label)
{
rtx index;
if (! HAVE_tablejump)
return 0;
index_expr = fold_build2 (MINUS_EXPR, index_type,
fold_convert (index_type, index_expr),
fold_convert (index_type, minval));
index = expand_normal (index_expr);
do_pending_stack_adjust ();
do_tablejump (index, TYPE_MODE (index_type),
convert_modes (TYPE_MODE (index_type),
TYPE_MODE (TREE_TYPE (range)),
expand_normal (range),
TYPE_UNSIGNED (TREE_TYPE (range))),
table_label, default_label);
return 1;
}
int
vector_mode_valid_p (enum machine_mode mode)
{
enum mode_class class = GET_MODE_CLASS (mode);
enum machine_mode innermode;
if (class != MODE_VECTOR_INT
&& class != MODE_VECTOR_FLOAT)
return 0;
if (targetm.vector_mode_supported_p (mode))
return 1;
innermode = GET_MODE_INNER (mode);
return targetm.scalar_mode_supported_p (innermode);
}
static rtx
const_vector_from_tree (tree exp)
{
rtvec v;
int units, i;
tree link, elt;
enum machine_mode inner, mode;
mode = TYPE_MODE (TREE_TYPE (exp));
if (initializer_zerop (exp))
return CONST0_RTX (mode);
units = GET_MODE_NUNITS (mode);
inner = GET_MODE_INNER (mode);
v = rtvec_alloc (units);
link = TREE_VECTOR_CST_ELTS (exp);
for (i = 0; link; link = TREE_CHAIN (link), ++i)
{
elt = TREE_VALUE (link);
if (TREE_CODE (elt) == REAL_CST)
RTVEC_ELT (v, i) = CONST_DOUBLE_FROM_REAL_VALUE (TREE_REAL_CST (elt),
inner);
else
RTVEC_ELT (v, i) = immed_double_const (TREE_INT_CST_LOW (elt),
TREE_INT_CST_HIGH (elt),
inner);
}
for (; i < units; ++i)
RTVEC_ELT (v, i) = CONST0_RTX (inner);
return gen_rtx_CONST_VECTOR (mode, v);
}
#if defined (HAVE_bswapsi2) || defined (HAVE_bswapdi2) || defined (HAVE_uxtb16)
struct bytemanip
{
int shiftcount;
HOST_WIDE_INT andmask_high;
HOST_WIDE_INT andmask_low;
int bitmask;
};
#endif
#ifdef HAVE_bswapsi2
static struct bytemanip bswap32_shift_first[] =
{
{ -24, 0x00000000, 0xff000000, 8 },
{ -8, 0x00000000, 0x00ff0000, 4 },
{ 8, 0x00000000, 0x0000ff00, 2 },
{ 24, 0x00000000, 0x000000ff, 1 },
{ -24, 0xffffffff, 0xffffffff, 8 },
{ 24, 0xffffffff, 0xffffffff, 1 },
{ 666, 0, 0, 0 }
};
static struct bytemanip bswap32_and_first[] =
{
{ -24, 0x00000000, 0x000000ff, 8 },
{ -8, 0x00000000, 0x0000ff00, 4 },
{ 8, 0x00000000, 0x00ff0000, 2 },
{ 24, 0x00000000, 0xff000000, 1 },
{ 666, 0, 0, 0 }
};
#endif
#ifdef HAVE_bswapdi2
static struct bytemanip bswap64_shift_first[] =
{
{ -56, 0xff000000, 0x00000000, 128 },
{ -40, 0x00ff0000, 0x00000000, 64 },
{ -24, 0x0000ff00, 0x00000000, 32 },
{ -8, 0x000000ff, 0x00000000, 16 },
{ 8, 0x00000000, 0xff000000, 8 },
{ 24, 0x00000000, 0x00ff0000, 4 },
{ 40, 0x00000000, 0x0000ff00, 2 },
{ 56, 0x00000000, 0x000000ff, 1 },
{ -56, 0xffffffff, 0xffffffff, 128 },
{ 56, 0xffffffff, 0xffffffff, 1 },
{ 666, 0, 0, 0 }
};
static struct bytemanip bswap64_and_first[] =
{
{ -56, 0x00000000, 0x000000ff, 128 },
{ -40, 0x00000000, 0x0000ff00, 64 },
{ -24, 0x00000000, 0x00ff0000, 32 },
{ -8, 0x00000000, 0xff000000, 16 },
{ 8, 0x000000ff, 0x00000000, 8 },
{ 24, 0x0000ff00, 0x00000000, 4 },
{ 40, 0x00ff0000, 0x00000000, 2 },
{ 56, 0xff000000, 0x00000000, 1 },
{ 666, 0, 0, 0 }
};
#endif
#ifdef HAVE_uxtb16
static struct bytemanip uxtb16_shift_first[] =
{
{ 0, 0x00000000, 0x00ff00ff, 3 },
{ 8, 0x00000000, 0x000000ff, 4 },
{ 8, 0x00000000, 0x00ff0000, 8 },
{ 8, 0x00000000, 0x00ff00ff, 12 },
{ 16, 0x00000000, 0x000000ff, 16 },
{ -16, 0x00000000, 0x00ff0000, 32 },
{ 24, 0x00000000, 0x000000ff, 64 },
{ -8, 0x00000000, 0x00ff0000, 128 },
{ 24, 0xffffffff, 0xffffffff, 64 },
{ 666, 0, 0, 0 }
};
static struct bytemanip uxtb16_and_first[] =
{
{ 8, 0x00000000, 0x0000FF00, 4 },
{ 8, 0x00000000, 0xFF000000, 8 },
{ 8, 0x00000000, 0xFF00FF00, 12 },
{ 16, 0x00000000, 0x00FF0000, 16 },
{ -16, 0x00000000, 0x000000FF, 32 },
{ 24, 0x00000000, 0xFF000000, 64 },
{ -8, 0x00000000, 0x0000FF00, 128 },
{ 666, 0, 0, 0 }
};
#endif
#if defined (HAVE_bswapsi2) || defined (HAVE_bswapdi2) || defined (HAVE_uxtb16)
static bool
find_and_record_values (tree lhs, HOST_WIDE_INT shiftcount,
HOST_WIDE_INT andmask_low, HOST_WIDE_INT andmask_high,
struct bytemanip *table, tree *operand, int *bitmask)
{
int i;
if (*operand != NULL_TREE && *operand != lhs)
return false;
*operand = lhs;
for (i = 0; table[i].shiftcount != 666; i++)
{
if (shiftcount == table[i].shiftcount
&& andmask_low == table[i].andmask_low
&& andmask_high == table[i].andmask_high)
{
if ((*bitmask) & table[i].bitmask)
return false;
*bitmask |= table[i].bitmask;
return true;
}
}
return false;
}
static bool
analyze_leg (tree t, int *bitmask, tree *operand, enum machine_mode mode,
struct bytemanip *shift_first,
struct bytemanip *and_first)
{
HOST_WIDE_INT count;
bool m64_p;
gcc_assert (HOST_BITS_PER_WIDE_INT == 32 || HOST_BITS_PER_WIDE_INT == 64);
m64_p = (HOST_BITS_PER_WIDE_INT == 64);
if (!TYPE_UNSIGNED (TREE_TYPE (t)) || TYPE_MODE (TREE_TYPE (t)) != mode)
return false;
if (TREE_CODE (t) == BIT_IOR_EXPR)
{
if (!analyze_leg (TREE_OPERAND (t, 0), bitmask, operand, mode,
shift_first, and_first)
|| !analyze_leg (TREE_OPERAND (t, 1), bitmask, operand, mode,
shift_first, and_first))
return false;
}
else if (TREE_CODE (t) == BIT_AND_EXPR)
{
if (TREE_CODE (TREE_OPERAND (t, 1)) != INTEGER_CST)
return false;
if (TREE_CODE (TREE_OPERAND (t, 0)) == LSHIFT_EXPR
|| TREE_CODE (TREE_OPERAND (t, 0)) == RSHIFT_EXPR)
{
if (TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 1)) != INTEGER_CST)
return false;
if (!TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (t, 0)))
|| TYPE_MODE (TREE_TYPE (TREE_OPERAND (t, 0))) != mode)
return false;
count = TREE_INT_CST_LOW (TREE_OPERAND (TREE_OPERAND (t, 0), 1));
if (TREE_CODE (TREE_OPERAND (t, 0)) == LSHIFT_EXPR)
count = -count;
if (!find_and_record_values (
TREE_OPERAND (TREE_OPERAND (t, 0), 0),
count,
m64_p ? TREE_INT_CST_LOW (TREE_OPERAND (t, 1)) & 0xffffffff
: TREE_INT_CST_LOW (TREE_OPERAND (t, 1)),
m64_p ? TREE_INT_CST_LOW (TREE_OPERAND (t, 1)) / 0x100000000ll
: (unsigned HOST_WIDE_INT)
TREE_INT_CST_HIGH (TREE_OPERAND (t, 1)),
shift_first,
operand,
bitmask))
return false;
}
else
if (!find_and_record_values (
TREE_OPERAND (t, 0),
(HOST_WIDE_INT)0,
m64_p ? TREE_INT_CST_LOW (TREE_OPERAND (t, 1)) & 0xffffffff
: TREE_INT_CST_LOW (TREE_OPERAND (t, 1)),
m64_p ? TREE_INT_CST_LOW (TREE_OPERAND (t, 1)) / 0x100000000ll
: (unsigned HOST_WIDE_INT)
TREE_INT_CST_HIGH (TREE_OPERAND (t, 1)),
shift_first,
operand,
bitmask))
return false;
}
else if (TREE_CODE (t) == RSHIFT_EXPR
|| TREE_CODE (t) == LSHIFT_EXPR)
{
if (TREE_CODE (TREE_OPERAND (t, 1)) != INTEGER_CST)
return false;
count = TREE_INT_CST_LOW (TREE_OPERAND (t, 1));
if (TREE_CODE (t) == LSHIFT_EXPR)
count = -count;
if (TREE_CODE (TREE_OPERAND (t, 0)) == BIT_AND_EXPR
&& TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 1)) == INTEGER_CST)
{
if (!TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (t, 0)))
|| TYPE_MODE (TREE_TYPE (TREE_OPERAND (t, 0))) != mode)
return false;
if (!find_and_record_values (
TREE_OPERAND (TREE_OPERAND (t, 0), 0),
count,
m64_p ? TREE_INT_CST_LOW (TREE_OPERAND (TREE_OPERAND (t, 0), 1)) & 0xffffffff
: TREE_INT_CST_LOW (TREE_OPERAND (TREE_OPERAND (t, 0), 1)),
m64_p ? TREE_INT_CST_LOW (TREE_OPERAND (TREE_OPERAND (t, 0), 1)) / 0x100000000ll
: (unsigned HOST_WIDE_INT)
TREE_INT_CST_HIGH (TREE_OPERAND (TREE_OPERAND (t, 0), 1)),
and_first,
operand,
bitmask))
return false;
}
else
if (!find_and_record_values (
TREE_OPERAND (t, 0),
count,
(HOST_WIDE_INT) 0xffffffff,
(HOST_WIDE_INT) 0xffffffff,
shift_first,
operand,
bitmask))
return false;
}
else
return false;
return true;
}
#endif
static rtx
look_for_bytemanip (tree t, rtx subtarget ATTRIBUTE_UNUSED)
{
enum machine_mode mode;
gcc_assert (TREE_CODE (t) == BIT_IOR_EXPR
|| TREE_CODE (t) == BIT_AND_EXPR
|| TREE_CODE (t) == LSHIFT_EXPR
|| TREE_CODE (t) == RSHIFT_EXPR);
if (!TYPE_UNSIGNED (TREE_TYPE (t)))
return NULL_RTX;
mode = TYPE_MODE (TREE_TYPE (t));
if (mode != SImode && mode != DImode)
return NULL_RTX;
#ifdef HAVE_bswapsi2
if (HAVE_bswapsi2)
{
int bitmask = 0;
tree operand = NULL_TREE;
if (mode == SImode
&& analyze_leg (t, &bitmask, &operand, mode,
bswap32_shift_first, bswap32_and_first)
&& bitmask == 0xf)
{
rtx x = expand_expr (operand, subtarget, VOIDmode, 0);
return expand_simple_unop (mode, BSWAP, x, NULL_RTX, 1);
}
}
#endif
#ifdef HAVE_bswapdi2
if (HAVE_bswapdi2)
{
int bitmask = 0;
tree operand = NULL_TREE;
if (mode == DImode
&& analyze_leg (t, &bitmask, &operand, mode,
bswap64_shift_first, bswap64_and_first)
&& bitmask == 0xff)
{
rtx x = expand_expr (operand, subtarget, VOIDmode, 0);
return expand_simple_unop (mode, BSWAP, x, NULL_RTX, 1);
}
}
#endif
#ifdef HAVE_uxtb16
if (HAVE_uxtb16)
{
int bitmask = 0;
tree operand = NULL_TREE;
if (mode == SImode
&& analyze_leg (t, &bitmask, &operand, mode,
uxtb16_shift_first, uxtb16_and_first)
&& (bitmask == 0x3 || bitmask == 0xc || bitmask == 0x30 || bitmask == 0xc0))
{
rtx x = expand_expr (operand, subtarget, VOIDmode, 0);
x = force_reg (SImode, x);
x = gen_rtx_UNSPEC (SImode,
gen_rtvec (2, x, bitmask == 0x3 ? const0_rtx :
bitmask == 0xc ? gen_rtx_CONST_INT (SImode, 8) :
bitmask == 0x30 ? gen_rtx_CONST_INT (SImode, 16) :
gen_rtx_CONST_INT (SImode, 24)),
UNSPEC_UXTB16);
x = force_reg (SImode, x);
return x;
}
}
#endif
return NULL_RTX;
}
#include "gt-expr.h"