mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-24 21:01:27 +08:00
re PR rtl-optimization/15289 (reload error with non-lowpart subregs)
PR rtl-opt/15289 * emit-rtl.c (gen_complex_constant_part): Remove. (gen_realpart, gen_imagpart): Remove. * rtl.h (gen_realpart, gen_imagpart): Remove. * expmed.c (extract_bit_field): Remove CONCAT hack catering to gen_realpart/gen_imagpart. * expr.c (write_complex_part, read_complex_part): New. (emit_move_via_alt_mode, emit_move_via_integer, emit_move_resolve_push, emit_move_complex_push, emit_move_complex, emit_move_ccmode, emit_move_multi_word): Split out from ... (emit_move_insn_1): ... here. (expand_expr_real_1) <COMPLEX_EXPR>: Use write_complex_part. <REALPART_EXPR, IMAGPART_EXPR>: Use read_complex_part. * function.c (assign_parm_setup_reg): Hard-code transformations instead of using gen_realpart/gen_imagpart. From-SVN: r91571
This commit is contained in:
parent
8c1cfd5aa4
commit
1466e38701
@ -1,5 +1,21 @@
|
||||
2004-12-01 Richard Henderson <rth@redhat.com>
|
||||
|
||||
PR rtl-opt/15289
|
||||
* emit-rtl.c (gen_complex_constant_part): Remove.
|
||||
(gen_realpart, gen_imagpart): Remove.
|
||||
* rtl.h (gen_realpart, gen_imagpart): Remove.
|
||||
* expmed.c (extract_bit_field): Remove CONCAT hack catering to
|
||||
gen_realpart/gen_imagpart.
|
||||
* expr.c (write_complex_part, read_complex_part): New.
|
||||
(emit_move_via_alt_mode, emit_move_via_integer, emit_move_resolve_push,
|
||||
emit_move_complex_push, emit_move_complex, emit_move_ccmode,
|
||||
emit_move_multi_word): Split out from ...
|
||||
(emit_move_insn_1): ... here.
|
||||
(expand_expr_real_1) <COMPLEX_EXPR>: Use write_complex_part.
|
||||
<REALPART_EXPR, IMAGPART_EXPR>: Use read_complex_part.
|
||||
* function.c (assign_parm_setup_reg): Hard-code transformations
|
||||
instead of using gen_realpart/gen_imagpart.
|
||||
|
||||
* expr.c (optimize_bitfield_assignment_op): Split out from ...
|
||||
(expand_assignment): ... here. Use handled_component_p to gate
|
||||
get_inner_reference code. Simplify MEM handling. Special case
|
||||
|
@ -184,7 +184,6 @@ static int reg_attrs_htab_eq (const void *, const void *);
|
||||
static reg_attrs *get_reg_attrs (tree, int);
|
||||
static tree component_ref_for_mem_expr (tree);
|
||||
static rtx gen_const_vector (enum machine_mode, int);
|
||||
static rtx gen_complex_constant_part (enum machine_mode, rtx, int);
|
||||
static void copy_rtx_if_shared_1 (rtx *orig);
|
||||
|
||||
/* Probability of the conditional branch currently proceeded by try_split.
|
||||
@ -1169,81 +1168,6 @@ gen_lowpart_common (enum machine_mode mode, rtx x)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the constant real or imaginary part (which has mode MODE)
|
||||
of a complex value X. The IMAGPART_P argument determines whether
|
||||
the real or complex component should be returned. This function
|
||||
returns NULL_RTX if the component isn't a constant. */
|
||||
|
||||
static rtx
|
||||
gen_complex_constant_part (enum machine_mode mode, rtx x, int imagpart_p)
|
||||
{
|
||||
tree decl, part;
|
||||
|
||||
if (MEM_P (x)
|
||||
&& GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
|
||||
{
|
||||
decl = SYMBOL_REF_DECL (XEXP (x, 0));
|
||||
if (decl != NULL_TREE && TREE_CODE (decl) == COMPLEX_CST)
|
||||
{
|
||||
part = imagpart_p ? TREE_IMAGPART (decl) : TREE_REALPART (decl);
|
||||
if (TREE_CODE (part) == REAL_CST
|
||||
|| TREE_CODE (part) == INTEGER_CST)
|
||||
return expand_expr (part, NULL_RTX, mode, 0);
|
||||
}
|
||||
}
|
||||
return NULL_RTX;
|
||||
}
|
||||
|
||||
/* Return the real part (which has mode MODE) of a complex value X.
|
||||
This always comes at the low address in memory. */
|
||||
|
||||
rtx
|
||||
gen_realpart (enum machine_mode mode, rtx x)
|
||||
{
|
||||
rtx part;
|
||||
|
||||
/* Handle complex constants. */
|
||||
part = gen_complex_constant_part (mode, x, 0);
|
||||
if (part != NULL_RTX)
|
||||
return part;
|
||||
|
||||
if (WORDS_BIG_ENDIAN
|
||||
&& GET_MODE_BITSIZE (mode) < BITS_PER_WORD
|
||||
&& REG_P (x)
|
||||
&& REGNO (x) < FIRST_PSEUDO_REGISTER)
|
||||
internal_error
|
||||
("can't access real part of complex value in hard register");
|
||||
else if (WORDS_BIG_ENDIAN)
|
||||
return gen_highpart (mode, x);
|
||||
else
|
||||
return gen_lowpart (mode, x);
|
||||
}
|
||||
|
||||
/* Return the imaginary part (which has mode MODE) of a complex value X.
|
||||
This always comes at the high address in memory. */
|
||||
|
||||
rtx
|
||||
gen_imagpart (enum machine_mode mode, rtx x)
|
||||
{
|
||||
rtx part;
|
||||
|
||||
/* Handle complex constants. */
|
||||
part = gen_complex_constant_part (mode, x, 1);
|
||||
if (part != NULL_RTX)
|
||||
return part;
|
||||
|
||||
if (WORDS_BIG_ENDIAN)
|
||||
return gen_lowpart (mode, x);
|
||||
else if (! WORDS_BIG_ENDIAN
|
||||
&& GET_MODE_BITSIZE (mode) < BITS_PER_WORD
|
||||
&& REG_P (x)
|
||||
&& REGNO (x) < FIRST_PSEUDO_REGISTER)
|
||||
internal_error
|
||||
("can't access imaginary part of complex value in hard register");
|
||||
else
|
||||
return gen_highpart (mode, x);
|
||||
}
|
||||
|
||||
rtx
|
||||
gen_highpart (enum machine_mode mode, rtx x)
|
||||
{
|
||||
|
22
gcc/expmed.c
22
gcc/expmed.c
@ -1611,28 +1611,6 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
|
||||
return spec_target;
|
||||
if (GET_MODE (target) != tmode && GET_MODE (target) != mode)
|
||||
{
|
||||
/* If the target mode is complex, then extract the two scalar elements
|
||||
from the value now. Creating (subreg:SC (reg:DI) 0), as we would do
|
||||
with the clause below, will cause gen_realpart or gen_imagpart to
|
||||
fail, since those functions must return lvalues. */
|
||||
if (COMPLEX_MODE_P (tmode))
|
||||
{
|
||||
rtx realpart, imagpart;
|
||||
enum machine_mode itmode = GET_MODE_INNER (tmode);
|
||||
|
||||
target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode),
|
||||
MODE_INT, 0),
|
||||
target, unsignedp);
|
||||
|
||||
realpart = extract_bit_field (target, GET_MODE_BITSIZE (itmode), 0,
|
||||
unsignedp, NULL, itmode, itmode);
|
||||
imagpart = extract_bit_field (target, GET_MODE_BITSIZE (itmode),
|
||||
GET_MODE_BITSIZE (itmode), unsignedp,
|
||||
NULL, itmode, itmode);
|
||||
|
||||
return gen_rtx_CONCAT (tmode, realpart, imagpart);
|
||||
}
|
||||
|
||||
/* If the target mode is not a scalar integral, first convert to the
|
||||
integer mode of that size and then access it as a floating-point
|
||||
value via a SUBREG. */
|
||||
|
773
gcc/expr.c
773
gcc/expr.c
@ -2574,6 +2574,403 @@ clear_storage_libcall_fn (int for_call)
|
||||
return block_clear_fn;
|
||||
}
|
||||
|
||||
/* Write to one of the components of the complex value CPLX. Write VAL to
|
||||
the real part if IMAG_P is false, and the imaginary part if its true. */
|
||||
|
||||
static void
|
||||
write_complex_part (rtx cplx, rtx val, bool imag_p)
|
||||
{
|
||||
if (GET_CODE (cplx) == CONCAT)
|
||||
emit_move_insn (XEXP (cplx, imag_p), val);
|
||||
else
|
||||
{
|
||||
enum machine_mode cmode = GET_MODE (cplx);
|
||||
enum machine_mode imode = GET_MODE_INNER (cmode);
|
||||
unsigned ibitsize = GET_MODE_BITSIZE (imode);
|
||||
|
||||
store_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0, imode, val);
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract one of the components of the complex value CPLX. Extract the
|
||||
real part if IMAG_P is false, and the imaginary part if it's true. */
|
||||
|
||||
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);
|
||||
|
||||
/* Special case reads from complex constants that got spilled to memory. */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return extract_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0,
|
||||
true, NULL_RTX, imode, imode);
|
||||
}
|
||||
|
||||
/* A subroutine of emit_move_insn_1. Generate a move from Y into X using
|
||||
ALT_MODE instead of the operand's natural mode, MODE. CODE is the insn
|
||||
code for the move in ALT_MODE, and is known to be valid. Returns the
|
||||
instruction emitted. */
|
||||
|
||||
static rtx
|
||||
emit_move_via_alt_mode (enum machine_mode alt_mode, enum machine_mode mode,
|
||||
enum insn_code code, rtx x, rtx y)
|
||||
{
|
||||
/* Get X and Y in ALT_MODE. We can't use gen_lowpart here because it
|
||||
may call change_address which is not appropriate if we were
|
||||
called when a reload was in progress. We don't have to worry
|
||||
about changing the address since the size in bytes is supposed to
|
||||
be the same. Copy the MEM to change the mode and move any
|
||||
substitutions from the old MEM to the new one. */
|
||||
|
||||
if (reload_in_progress)
|
||||
{
|
||||
rtx x1 = x, y1 = y;
|
||||
|
||||
x = gen_lowpart_common (alt_mode, x1);
|
||||
if (x == 0 && MEM_P (x1))
|
||||
{
|
||||
x = adjust_address_nv (x1, alt_mode, 0);
|
||||
copy_replacements (x1, x);
|
||||
}
|
||||
|
||||
y = gen_lowpart_common (alt_mode, y1);
|
||||
if (y == 0 && MEM_P (y1))
|
||||
{
|
||||
y = adjust_address_nv (y1, alt_mode, 0);
|
||||
copy_replacements (y1, y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
x = simplify_gen_subreg (alt_mode, x, mode, 0);
|
||||
y = simplify_gen_subreg (alt_mode, y, mode, 0);
|
||||
}
|
||||
|
||||
return emit_insn (GEN_FCN (code) (x, y));
|
||||
}
|
||||
|
||||
/* A subroutine of emit_move_insn_1. Generate a move from Y into X using
|
||||
an integer mode of the same size as MODE. Returns the instruction
|
||||
emitted, or NULL if such a move could not be generated. */
|
||||
|
||||
static rtx
|
||||
emit_move_via_integer (enum machine_mode mode, rtx x, rtx y)
|
||||
{
|
||||
enum machine_mode imode;
|
||||
enum insn_code code;
|
||||
|
||||
/* There must exist a mode of the exact size we require. */
|
||||
imode = int_mode_for_mode (mode);
|
||||
if (imode == BLKmode)
|
||||
return NULL_RTX;
|
||||
|
||||
/* The target must support moves in this mode. */
|
||||
code = mov_optab->handlers[imode].insn_code;
|
||||
if (code == CODE_FOR_nothing)
|
||||
return NULL_RTX;
|
||||
|
||||
return emit_move_via_alt_mode (imode, mode, code, x, y);
|
||||
}
|
||||
|
||||
/* A subroutine of emit_move_insn_1. X is a push_operand in MODE.
|
||||
Return an equivalent MEM that does not use an auto-increment. */
|
||||
|
||||
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;
|
||||
|
||||
/* Do not use anti_adjust_stack, since we don't want to update
|
||||
stack_pointer_delta. */
|
||||
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:
|
||||
temp = stack_pointer_rtx;
|
||||
break;
|
||||
case POST_INC:
|
||||
temp = plus_constant (stack_pointer_rtx, -GET_MODE_SIZE (mode));
|
||||
break;
|
||||
case POST_DEC:
|
||||
temp = plus_constant (stack_pointer_rtx, GET_MODE_SIZE (mode));
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
return replace_equiv_address (x, temp);
|
||||
}
|
||||
|
||||
/* A subroutine of emit_move_complex. Generate a move from Y into X.
|
||||
X is known to satisfy push_operand, and MODE is known to be complex.
|
||||
Returns the last instruction emitted. */
|
||||
|
||||
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);
|
||||
|
||||
/* In case we output to the stack, but the size is smaller than the
|
||||
machine can push exactly, we need to use move instructions. */
|
||||
if (PUSH_ROUNDING (submodesize) != submodesize)
|
||||
{
|
||||
x = emit_move_resolve_push (mode, x);
|
||||
return emit_move_insn (x, y);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Note that the real part always precedes the imag part in memory
|
||||
regardless of machine's endianness. */
|
||||
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));
|
||||
}
|
||||
|
||||
/* A subroutine of emit_move_insn_1. Generate a move from Y into X.
|
||||
MODE is known to be complex. Returns the last instruction emitted. */
|
||||
|
||||
static rtx
|
||||
emit_move_complex (enum machine_mode mode, rtx x, rtx y)
|
||||
{
|
||||
bool try_int;
|
||||
|
||||
/* Need to take special care for pushes, to maintain proper ordering
|
||||
of the data, and possibly extra padding. */
|
||||
if (push_operand (x, mode))
|
||||
return emit_move_complex_push (mode, x, y);
|
||||
|
||||
/* For memory to memory moves, optimial behaviour can be had with the
|
||||
existing block move logic. */
|
||||
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 ();
|
||||
}
|
||||
|
||||
/* See if we can coerce the target into moving both values at once. */
|
||||
|
||||
/* Not possible if the values are inherently not adjacent. */
|
||||
if (GET_CODE (x) == CONCAT || GET_CODE (y) == CONCAT)
|
||||
try_int = false;
|
||||
/* Is possible if both are registers (or subregs of registers). */
|
||||
else if (register_operand (x, mode) && register_operand (y, mode))
|
||||
try_int = true;
|
||||
/* If one of the operands is a memory, and alignment constraints
|
||||
are friendly enough, we may be able to do combined memory operations.
|
||||
We do not attempt this if Y is a constant because that combination is
|
||||
usually better with the by-parts thing below. */
|
||||
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 = emit_move_via_integer (mode, x, y);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Show the output dies here. This is necessary for SUBREGs
|
||||
of pseudos since we cannot track their lifetimes correctly;
|
||||
hard regs shouldn't appear here except as return values. */
|
||||
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 ();
|
||||
}
|
||||
|
||||
/* A subroutine of emit_move_insn_1. Generate a move from Y into X.
|
||||
MODE is known to be MODE_CC. Returns the last instruction emitted. */
|
||||
|
||||
static rtx
|
||||
emit_move_ccmode (enum machine_mode mode, rtx x, rtx y)
|
||||
{
|
||||
rtx ret;
|
||||
|
||||
/* Assume all MODE_CC modes are equivalent; if we have movcc, use it. */
|
||||
if (mode != CCmode)
|
||||
{
|
||||
enum insn_code code = mov_optab->handlers[CCmode].insn_code;
|
||||
if (code != CODE_FOR_nothing)
|
||||
return emit_move_via_alt_mode (CCmode, mode, code, x, y);
|
||||
}
|
||||
|
||||
/* Otherwise, find the MODE_INT mode of the same width. */
|
||||
ret = emit_move_via_integer (mode, x, y);
|
||||
gcc_assert (ret != NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* A subroutine of emit_move_insn_1. Generate a move from Y into X.
|
||||
MODE is any multi-word or full-word mode that lacks a move_insn
|
||||
pattern. Note that you will get better code if you define such
|
||||
patterns, even if they must turn into multiple assembler instructions. */
|
||||
|
||||
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 X is a push on the stack, do the push now and replace
|
||||
X with a reference to the stack pointer. */
|
||||
if (push_operand (x, mode))
|
||||
x = emit_move_resolve_push (mode, x);
|
||||
|
||||
/* If we are in reload, see if either operand is a MEM whose address
|
||||
is scheduled for replacement. */
|
||||
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 = operand_subword (y, i, 1, mode);
|
||||
|
||||
/* If we can't get a part of Y, put Y into memory if it is a
|
||||
constant. Otherwise, force it into a register. If we still
|
||||
can't get a part of Y, abort. */
|
||||
if (ypart == 0 && CONSTANT_P (y))
|
||||
{
|
||||
y = 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 ();
|
||||
|
||||
/* Show the output dies here. This is necessary for SUBREGs
|
||||
of pseudos since we cannot track their lifetimes correctly;
|
||||
hard regs shouldn't appear here except as return values.
|
||||
We never want to emit such a clobber after reload. */
|
||||
if (x != y
|
||||
&& ! (reload_in_progress || reload_completed)
|
||||
&& need_clobber != 0)
|
||||
emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
|
||||
|
||||
emit_insn (seq);
|
||||
|
||||
return last_insn;
|
||||
}
|
||||
|
||||
/* Low level part of emit_move_insn.
|
||||
Called just like emit_move_insn, but assumes X and Y
|
||||
are basically valid. */
|
||||
|
||||
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));
|
||||
|
||||
/* Expand complex moves by moving real part and imag part. */
|
||||
if (COMPLEX_MODE_P (mode))
|
||||
return emit_move_complex (mode, x, y);
|
||||
|
||||
if (GET_MODE_CLASS (mode) == MODE_CC)
|
||||
return emit_move_ccmode (mode, x, y);
|
||||
|
||||
/* Try using a move pattern for the corresponding integer mode. This is
|
||||
only safe when simplify_subreg can convert MODE constants into integer
|
||||
constants. At present, it can only do this reliably if the value
|
||||
fits within a HOST_WIDE_INT. */
|
||||
if (!CONSTANT_P (y) || GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
|
||||
{
|
||||
rtx ret = emit_move_via_integer (mode, x, y);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return emit_move_multi_word (mode, x, y);
|
||||
}
|
||||
|
||||
/* Generate code to copy Y into X.
|
||||
Both Y and X must have the same mode, except that
|
||||
Y can be a constant with VOIDmode.
|
||||
@ -2640,340 +3037,6 @@ emit_move_insn (rtx x, rtx y)
|
||||
return last_insn;
|
||||
}
|
||||
|
||||
/* Low level part of emit_move_insn.
|
||||
Called just like emit_move_insn, but assumes X and Y
|
||||
are basically valid. */
|
||||
|
||||
rtx
|
||||
emit_move_insn_1 (rtx x, rtx y)
|
||||
{
|
||||
enum machine_mode mode = GET_MODE (x);
|
||||
enum machine_mode submode;
|
||||
|
||||
gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
|
||||
|
||||
if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
|
||||
return
|
||||
emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y));
|
||||
|
||||
/* Expand complex moves by moving real part and imag part, if possible. */
|
||||
else if (COMPLEX_MODE_P (mode)
|
||||
&& BLKmode != (submode = GET_MODE_INNER (mode))
|
||||
&& (mov_optab->handlers[(int) submode].insn_code
|
||||
!= CODE_FOR_nothing))
|
||||
{
|
||||
unsigned int modesize = GET_MODE_SIZE (mode);
|
||||
unsigned int submodesize = GET_MODE_SIZE (submode);
|
||||
|
||||
/* Don't split destination if it is a stack push. */
|
||||
int stack = push_operand (x, mode);
|
||||
|
||||
#ifdef PUSH_ROUNDING
|
||||
/* In case we output to the stack, but the size is smaller than the
|
||||
machine can push exactly, we need to use move instructions. */
|
||||
if (stack && PUSH_ROUNDING (submodesize) != submodesize)
|
||||
{
|
||||
rtx temp;
|
||||
HOST_WIDE_INT offset1, offset2;
|
||||
|
||||
/* Do not use anti_adjust_stack, since we don't want to update
|
||||
stack_pointer_delta. */
|
||||
temp = expand_binop (Pmode,
|
||||
#ifdef STACK_GROWS_DOWNWARD
|
||||
sub_optab,
|
||||
#else
|
||||
add_optab,
|
||||
#endif
|
||||
stack_pointer_rtx,
|
||||
GEN_INT (PUSH_ROUNDING (modesize)),
|
||||
stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
|
||||
|
||||
if (temp != stack_pointer_rtx)
|
||||
emit_move_insn (stack_pointer_rtx, temp);
|
||||
|
||||
#ifdef STACK_GROWS_DOWNWARD
|
||||
offset1 = 0;
|
||||
offset2 = submodesize;
|
||||
#else
|
||||
offset1 = -PUSH_ROUNDING (modesize);
|
||||
offset2 = -PUSH_ROUNDING (modesize) + submodesize;
|
||||
#endif
|
||||
|
||||
emit_move_insn (change_address (x, submode,
|
||||
gen_rtx_PLUS (Pmode,
|
||||
stack_pointer_rtx,
|
||||
GEN_INT (offset1))),
|
||||
gen_realpart (submode, y));
|
||||
emit_move_insn (change_address (x, submode,
|
||||
gen_rtx_PLUS (Pmode,
|
||||
stack_pointer_rtx,
|
||||
GEN_INT (offset2))),
|
||||
gen_imagpart (submode, y));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
/* If this is a stack, push the highpart first, so it
|
||||
will be in the argument order.
|
||||
|
||||
In that case, change_address is used only to convert
|
||||
the mode, not to change the address. */
|
||||
if (stack)
|
||||
{
|
||||
/* Note that the real part always precedes the imag part in memory
|
||||
regardless of machine's endianness. */
|
||||
#ifdef STACK_GROWS_DOWNWARD
|
||||
emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
|
||||
gen_imagpart (submode, y));
|
||||
emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
|
||||
gen_realpart (submode, y));
|
||||
#else
|
||||
emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
|
||||
gen_realpart (submode, y));
|
||||
emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
|
||||
gen_imagpart (submode, y));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
rtx realpart_x, realpart_y;
|
||||
rtx imagpart_x, imagpart_y;
|
||||
|
||||
/* If this is a complex value with each part being smaller than a
|
||||
word, the usual calling sequence will likely pack the pieces into
|
||||
a single register. Unfortunately, SUBREG of hard registers only
|
||||
deals in terms of words, so we have a problem converting input
|
||||
arguments to the CONCAT of two registers that is used elsewhere
|
||||
for complex values. If this is before reload, we can copy it into
|
||||
memory and reload. FIXME, we should see about using extract and
|
||||
insert on integer registers, but complex short and complex char
|
||||
variables should be rarely used. */
|
||||
if ((reload_in_progress | reload_completed) == 0
|
||||
&& (!validate_subreg (submode, mode, NULL, submodesize)
|
||||
|| !validate_subreg (submode, mode, NULL, 0)))
|
||||
{
|
||||
if (REG_P (x) || REG_P (y))
|
||||
{
|
||||
rtx mem, cmem;
|
||||
enum machine_mode reg_mode
|
||||
= mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 1);
|
||||
|
||||
gcc_assert (reg_mode != BLKmode);
|
||||
|
||||
mem = assign_stack_temp (reg_mode, modesize, 0);
|
||||
cmem = adjust_address (mem, mode, 0);
|
||||
|
||||
if (REG_P (x))
|
||||
{
|
||||
rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
|
||||
emit_move_insn_1 (cmem, y);
|
||||
return emit_move_insn_1 (sreg, mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
|
||||
emit_move_insn_1 (mem, sreg);
|
||||
return emit_move_insn_1 (x, cmem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
realpart_x = gen_realpart (submode, x);
|
||||
realpart_y = gen_realpart (submode, y);
|
||||
imagpart_x = gen_imagpart (submode, x);
|
||||
imagpart_y = gen_imagpart (submode, y);
|
||||
|
||||
/* Show the output dies here. This is necessary for SUBREGs
|
||||
of pseudos since we cannot track their lifetimes correctly;
|
||||
hard regs shouldn't appear here except as return values.
|
||||
We never want to emit such a clobber after reload. */
|
||||
if (x != y
|
||||
&& ! (reload_in_progress || reload_completed)
|
||||
&& (GET_CODE (realpart_x) == SUBREG
|
||||
|| GET_CODE (imagpart_x) == SUBREG))
|
||||
emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
|
||||
|
||||
emit_move_insn (realpart_x, realpart_y);
|
||||
emit_move_insn (imagpart_x, imagpart_y);
|
||||
}
|
||||
|
||||
return get_last_insn ();
|
||||
}
|
||||
|
||||
/* Handle MODE_CC modes: If we don't have a special move insn for this mode,
|
||||
find a mode to do it in. If we have a movcc, use it. Otherwise,
|
||||
find the MODE_INT mode of the same width. */
|
||||
else if (GET_MODE_CLASS (mode) == MODE_CC
|
||||
&& mov_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
|
||||
{
|
||||
enum insn_code insn_code;
|
||||
enum machine_mode tmode = VOIDmode;
|
||||
rtx x1 = x, y1 = y;
|
||||
|
||||
if (mode != CCmode
|
||||
&& mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing)
|
||||
tmode = CCmode;
|
||||
else
|
||||
for (tmode = QImode; tmode != VOIDmode;
|
||||
tmode = GET_MODE_WIDER_MODE (tmode))
|
||||
if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode))
|
||||
break;
|
||||
|
||||
gcc_assert (tmode != VOIDmode);
|
||||
|
||||
/* Get X and Y in TMODE. We can't use gen_lowpart here because it
|
||||
may call change_address which is not appropriate if we were
|
||||
called when a reload was in progress. We don't have to worry
|
||||
about changing the address since the size in bytes is supposed to
|
||||
be the same. Copy the MEM to change the mode and move any
|
||||
substitutions from the old MEM to the new one. */
|
||||
|
||||
if (reload_in_progress)
|
||||
{
|
||||
x = gen_lowpart_common (tmode, x1);
|
||||
if (x == 0 && MEM_P (x1))
|
||||
{
|
||||
x = adjust_address_nv (x1, tmode, 0);
|
||||
copy_replacements (x1, x);
|
||||
}
|
||||
|
||||
y = gen_lowpart_common (tmode, y1);
|
||||
if (y == 0 && MEM_P (y1))
|
||||
{
|
||||
y = adjust_address_nv (y1, tmode, 0);
|
||||
copy_replacements (y1, y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
x = gen_lowpart (tmode, x);
|
||||
y = gen_lowpart (tmode, y);
|
||||
}
|
||||
|
||||
insn_code = mov_optab->handlers[(int) tmode].insn_code;
|
||||
return emit_insn (GEN_FCN (insn_code) (x, y));
|
||||
}
|
||||
|
||||
/* Try using a move pattern for the corresponding integer mode. This is
|
||||
only safe when simplify_subreg can convert MODE constants into integer
|
||||
constants. At present, it can only do this reliably if the value
|
||||
fits within a HOST_WIDE_INT. */
|
||||
else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
|
||||
&& (submode = int_mode_for_mode (mode)) != BLKmode
|
||||
&& mov_optab->handlers[submode].insn_code != CODE_FOR_nothing)
|
||||
return emit_insn (GEN_FCN (mov_optab->handlers[submode].insn_code)
|
||||
(simplify_gen_subreg (submode, x, mode, 0),
|
||||
simplify_gen_subreg (submode, y, mode, 0)));
|
||||
|
||||
/* This will handle any multi-word or full-word mode that lacks a move_insn
|
||||
pattern. However, you will get better code if you define such patterns,
|
||||
even if they must turn into multiple assembler instructions. */
|
||||
else
|
||||
{
|
||||
rtx last_insn = 0;
|
||||
rtx seq, inner;
|
||||
int need_clobber;
|
||||
int i;
|
||||
|
||||
gcc_assert (GET_MODE_SIZE (mode) >= UNITS_PER_WORD);
|
||||
|
||||
#ifdef PUSH_ROUNDING
|
||||
|
||||
/* If X is a push on the stack, do the push now and replace
|
||||
X with a reference to the stack pointer. */
|
||||
if (push_operand (x, GET_MODE (x)))
|
||||
{
|
||||
rtx temp;
|
||||
enum rtx_code code;
|
||||
|
||||
/* Do not use anti_adjust_stack, since we don't want to update
|
||||
stack_pointer_delta. */
|
||||
temp = expand_binop (Pmode,
|
||||
#ifdef STACK_GROWS_DOWNWARD
|
||||
sub_optab,
|
||||
#else
|
||||
add_optab,
|
||||
#endif
|
||||
stack_pointer_rtx,
|
||||
GEN_INT
|
||||
(PUSH_ROUNDING
|
||||
(GET_MODE_SIZE (GET_MODE (x)))),
|
||||
stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
|
||||
|
||||
if (temp != stack_pointer_rtx)
|
||||
emit_move_insn (stack_pointer_rtx, temp);
|
||||
|
||||
code = GET_CODE (XEXP (x, 0));
|
||||
|
||||
/* Just hope that small offsets off SP are OK. */
|
||||
if (code == POST_INC)
|
||||
temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
|
||||
GEN_INT (-((HOST_WIDE_INT)
|
||||
GET_MODE_SIZE (GET_MODE (x)))));
|
||||
else if (code == POST_DEC)
|
||||
temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
|
||||
GEN_INT (GET_MODE_SIZE (GET_MODE (x))));
|
||||
else
|
||||
temp = stack_pointer_rtx;
|
||||
|
||||
x = change_address (x, VOIDmode, temp);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If we are in reload, see if either operand is a MEM whose address
|
||||
is scheduled for replacement. */
|
||||
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 = 0;
|
||||
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 = operand_subword (y, i, 1, mode);
|
||||
|
||||
/* If we can't get a part of Y, put Y into memory if it is a
|
||||
constant. Otherwise, force it into a register. If we still
|
||||
can't get a part of Y, abort. */
|
||||
if (ypart == 0 && CONSTANT_P (y))
|
||||
{
|
||||
y = 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 ();
|
||||
|
||||
/* Show the output dies here. This is necessary for SUBREGs
|
||||
of pseudos since we cannot track their lifetimes correctly;
|
||||
hard regs shouldn't appear here except as return values.
|
||||
We never want to emit such a clobber after reload. */
|
||||
if (x != y
|
||||
&& ! (reload_in_progress || reload_completed)
|
||||
&& need_clobber != 0)
|
||||
emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
|
||||
|
||||
emit_insn (seq);
|
||||
|
||||
return last_insn;
|
||||
}
|
||||
}
|
||||
|
||||
/* If Y is representable exactly in a narrower mode, and the target can
|
||||
perform the extension directly from constant or memory, then emit the
|
||||
move as an extension. */
|
||||
@ -8083,47 +8146,27 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
|
||||
case ADDR_EXPR:
|
||||
return expand_expr_addr_expr (exp, target, tmode, modifier);
|
||||
|
||||
/* COMPLEX type for Extended Pascal & Fortran */
|
||||
case COMPLEX_EXPR:
|
||||
{
|
||||
enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
|
||||
rtx insns;
|
||||
/* Get the rtx code of the operands. */
|
||||
op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
|
||||
op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
|
||||
|
||||
/* Get the rtx code of the operands. */
|
||||
op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
|
||||
op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
|
||||
if (!target)
|
||||
target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
|
||||
|
||||
if (! target)
|
||||
target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
|
||||
/* Move the real (op0) and imaginary (op1) parts to their location. */
|
||||
write_complex_part (target, op0, false);
|
||||
write_complex_part (target, op1, true);
|
||||
|
||||
start_sequence ();
|
||||
|
||||
/* Move the real (op0) and imaginary (op1) parts to their location. */
|
||||
emit_move_insn (gen_realpart (mode, target), op0);
|
||||
emit_move_insn (gen_imagpart (mode, target), op1);
|
||||
|
||||
insns = get_insns ();
|
||||
end_sequence ();
|
||||
|
||||
/* Complex construction should appear as a single unit. */
|
||||
/* If TARGET is a CONCAT, we got insns like RD = RS, ID = IS,
|
||||
each with a separate pseudo as destination.
|
||||
It's not correct for flow to treat them as a unit. */
|
||||
if (GET_CODE (target) != CONCAT)
|
||||
emit_no_conflict_block (insns, target, op0, op1, NULL_RTX);
|
||||
else
|
||||
emit_insn (insns);
|
||||
|
||||
return target;
|
||||
}
|
||||
return target;
|
||||
|
||||
case REALPART_EXPR:
|
||||
op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
|
||||
return gen_realpart (mode, op0);
|
||||
return read_complex_part (op0, false);
|
||||
|
||||
case IMAGPART_EXPR:
|
||||
op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
|
||||
return gen_imagpart (mode, op0);
|
||||
return read_complex_part (op0, true);
|
||||
|
||||
case RESX_EXPR:
|
||||
expand_resx_expr (exp);
|
||||
|
@ -2869,10 +2869,11 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
|
||||
{
|
||||
enum machine_mode submode
|
||||
= GET_MODE_INNER (GET_MODE (parmreg));
|
||||
int regnor = REGNO (gen_realpart (submode, parmreg));
|
||||
int regnoi = REGNO (gen_imagpart (submode, parmreg));
|
||||
rtx stackr = gen_realpart (submode, data->stack_parm);
|
||||
rtx stacki = gen_imagpart (submode, data->stack_parm);
|
||||
int regnor = REGNO (XEXP (parmreg, 0));
|
||||
int regnoi = REGNO (XEXP (parmreg, 1));
|
||||
rtx stackr = adjust_address_nv (data->stack_parm, submode, 0);
|
||||
rtx stacki = adjust_address_nv (data->stack_parm, submode,
|
||||
GET_MODE_SIZE (submode));
|
||||
|
||||
/* Scan backwards for the set of the real and
|
||||
imaginary parts. */
|
||||
|
@ -1383,8 +1383,6 @@ extern rtx gen_lowpart_if_possible (enum machine_mode, rtx);
|
||||
/* In emit-rtl.c */
|
||||
extern rtx gen_highpart (enum machine_mode, rtx);
|
||||
extern rtx gen_highpart_mode (enum machine_mode, enum machine_mode, rtx);
|
||||
extern rtx gen_realpart (enum machine_mode, rtx);
|
||||
extern rtx gen_imagpart (enum machine_mode, rtx);
|
||||
extern rtx operand_subword (rtx, unsigned int, int, enum machine_mode);
|
||||
|
||||
/* In emit-rtl.c */
|
||||
|
Loading…
x
Reference in New Issue
Block a user