blob: aa80ae63f46bbacaf71ff48df441a541b28c773c [file] [log] [blame]
/* Convert tree expression to rtl instructions, for GNU compiler.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#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 after insn-config.h so we get HAVE_conditional_move. */
#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 "target.h"
/* Decide whether a function's arguments should be processed
from first to last or from last to first.
They should if the stack and args grow in opposite directions, but
only if we have push insns. */
#ifdef PUSH_ROUNDING
#ifndef PUSH_ARGS_REVERSED
#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
#define PUSH_ARGS_REVERSED /* If it's last to first. */
#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
/* Assume that case vectors are not pc-relative. */
#ifndef CASE_VECTOR_PC_RELATIVE
#define CASE_VECTOR_PC_RELATIVE 0
#endif
/* Convert defined/undefined to boolean. */
#ifdef TARGET_MEM_FUNCTIONS
#undef TARGET_MEM_FUNCTIONS
#define TARGET_MEM_FUNCTIONS 1
#else
#define TARGET_MEM_FUNCTIONS 0
#endif
/* If this is nonzero, we do not bother generating VOLATILE
around volatile memory references, and we are willing to
output indirect addresses. If cse is to follow, we reject
indirect addresses so a useful potential cse is generated;
if it is used only once, instruction combination will produce
the same indirect address eventually. */
int cse_not_expected;
/* Chain of pending expressions for PLACEHOLDER_EXPR to replace. */
tree placeholder_list = 0;
/* This structure is used by move_by_pieces to describe the move to
be performed. */
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;
};
/* This structure is used by store_by_pieces to describe the clear to
be performed. */
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 rtx enqueue_insn (rtx, rtx);
static unsigned HOST_WIDE_INT move_by_pieces_ninsns (unsigned HOST_WIDE_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_movstr (rtx, rtx, rtx, unsigned);
static rtx emit_block_move_via_libcall (rtx, rtx, rtx);
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 bool clear_storage_via_clrstr (rtx, rtx, unsigned);
static rtx clear_storage_via_libcall (rtx, rtx);
static tree clear_storage_libcall_fn (int);
static rtx compress_float_constant (rtx, rtx);
static rtx get_subtarget (rtx);
static int is_zeros_p (tree);
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, enum machine_mode, int, tree, int);
static rtx var_rtx (tree);
static unsigned HOST_WIDE_INT highest_pow2_factor (tree);
static unsigned HOST_WIDE_INT highest_pow2_factor_for_target (tree, tree);
static int is_aligning_offset (tree, tree);
static rtx expand_increment (tree, int, int);
static void expand_operands (tree, tree, rtx, rtx*, rtx*,
enum expand_modifier);
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);
#endif
static void do_tablejump (rtx, enum machine_mode, rtx, rtx, rtx);
static rtx const_vector_from_tree (tree);
/* Record for each mode whether we can move a register directly to or
from an object of that mode in memory. If we can't, we won't try
to use that mode directly when accessing a field of that mode. */
static char direct_load[NUM_MACHINE_MODES];
static char direct_store[NUM_MACHINE_MODES];
/* Record for each mode whether we can float-extend from memory. */
static bool float_extend_from_mem[NUM_MACHINE_MODES][NUM_MACHINE_MODES];
/* This macro is used to determine whether move_by_pieces should be called
to perform a structure copy. */
#ifndef MOVE_BY_PIECES_P
#define MOVE_BY_PIECES_P(SIZE, ALIGN) \
(move_by_pieces_ninsns (SIZE, ALIGN) < (unsigned int) MOVE_RATIO)
#endif
/* This macro is used to determine whether clear_by_pieces should be
called to clear storage. */
#ifndef CLEAR_BY_PIECES_P
#define CLEAR_BY_PIECES_P(SIZE, ALIGN) \
(move_by_pieces_ninsns (SIZE, ALIGN) < (unsigned int) CLEAR_RATIO)
#endif
/* This macro is used to determine whether store_by_pieces should be
called to "memset" storage with byte values other than zero, or
to "memcpy" storage when the source is a constant string. */
#ifndef STORE_BY_PIECES_P
#define STORE_BY_PIECES_P(SIZE, ALIGN) MOVE_BY_PIECES_P (SIZE, ALIGN)
#endif
/* This array records the insn_code of insns to perform block moves. */
enum insn_code movstr_optab[NUM_MACHINE_MODES];
/* This array records the insn_code of insns to perform block clears. */
enum insn_code clrstr_optab[NUM_MACHINE_MODES];
/* These arrays record the insn_code of two different kinds of insns
to perform block compares. */
enum insn_code cmpstr_optab[NUM_MACHINE_MODES];
enum insn_code cmpmem_optab[NUM_MACHINE_MODES];
/* Stack of EXPR_WITH_FILE_LOCATION nested expressions. */
struct file_stack *expr_wfl_stack;
/* SLOW_UNALIGNED_ACCESS is nonzero if unaligned accesses are very slow. */
#ifndef SLOW_UNALIGNED_ACCESS
#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) STRICT_ALIGNMENT
#endif
/* This is run once per compilation to set up which modes can be used
directly in memory and to initialize the block move optab. */
void
init_expr_once (void)
{
rtx insn, pat;
enum machine_mode mode;
int num_clobbers;
rtx mem, mem1;
rtx reg;
/* Try indexing by frame ptr and try by stack ptr.
It is known that on the Convex the stack ptr isn't a valid index.
With luck, one or the other is valid on any machine. */
mem = gen_rtx_MEM (VOIDmode, stack_pointer_rtx);
mem1 = gen_rtx_MEM (VOIDmode, frame_pointer_rtx);
/* A scratch register we can modify in-place below to avoid
useless RTL allocations. */
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);
/* See if there is some register that can be used in this mode and
directly loaded or stored from memory. */
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;
}
}
}
/* This is run at the start of compiling a function. */
void
init_expr (void)
{
cfun->expr = ggc_alloc_cleared (sizeof (struct expr_status));
}
/* Small sanity check that the queue is empty at the end of a function. */
void
finish_expr_for_function (void)
{
if (pending_chain)
abort ();
}
/* Manage the queue of increment instructions to be output
for POSTINCREMENT_EXPR expressions, etc. */
/* Queue up to increment (or change) VAR later. BODY says how:
BODY should be the same thing you would pass to emit_insn
to increment right away. It will go to emit_insn later on.
The value is a QUEUED expression to be used in place of VAR
where you want to guarantee the pre-incrementation value of VAR. */
static rtx
enqueue_insn (rtx var, rtx body)
{
pending_chain = gen_rtx_QUEUED (GET_MODE (var), var, NULL_RTX, NULL_RTX,
body, pending_chain);
return pending_chain;
}
/* Use protect_from_queue to convert a QUEUED expression
into something that you can put immediately into an instruction.
If the queued incrementation has not happened yet,
protect_from_queue returns the variable itself.
If the incrementation has happened, protect_from_queue returns a temp
that contains a copy of the old value of the variable.
Any time an rtx which might possibly be a QUEUED is to be put
into an instruction, it must be passed through protect_from_queue first.
QUEUED expressions are not meaningful in instructions.
Do not pass a value through protect_from_queue and then hold
on to it for a while before putting it in an instruction!
If the queue is flushed in between, incorrect code will result. */
rtx
protect_from_queue (rtx x, int modify)
{
RTX_CODE code = GET_CODE (x);
#if 0 /* A QUEUED can hang around after the queue is forced out. */
/* Shortcut for most common case. */
if (pending_chain == 0)
return x;
#endif
if (code != QUEUED)
{
/* A special hack for read access to (MEM (QUEUED ...)) to facilitate
use of autoincrement. Make a copy of the contents of the memory
location rather than a copy of the address, but not if the value is
of mode BLKmode. Don't modify X in place since it might be
shared. */
if (code == MEM && GET_MODE (x) != BLKmode
&& GET_CODE (XEXP (x, 0)) == QUEUED && !modify)
{
rtx y = XEXP (x, 0);
rtx new = replace_equiv_address_nv (x, QUEUED_VAR (y));
if (QUEUED_INSN (y))
{
rtx temp = gen_reg_rtx (GET_MODE (x));
emit_insn_before (gen_move_insn (temp, new),
QUEUED_INSN (y));
return temp;
}
/* Copy the address into a pseudo, so that the returned value
remains correct across calls to emit_queue. */
return replace_equiv_address (new, copy_to_reg (XEXP (new, 0)));
}
/* Otherwise, recursively protect the subexpressions of all
the kinds of rtx's that can contain a QUEUED. */
if (code == MEM)
{
rtx tem = protect_from_queue (XEXP (x, 0), 0);
if (tem != XEXP (x, 0))
{
x = copy_rtx (x);
XEXP (x, 0) = tem;
}
}
else if (code == PLUS || code == MULT)
{
rtx new0 = protect_from_queue (XEXP (x, 0), 0);
rtx new1 = protect_from_queue (XEXP (x, 1), 0);
if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
{
x = copy_rtx (x);
XEXP (x, 0) = new0;
XEXP (x, 1) = new1;
}
}
return x;
}
/* If the increment has not happened, use the variable itself. Copy it
into a new pseudo so that the value remains correct across calls to
emit_queue. */
if (QUEUED_INSN (x) == 0)
return copy_to_reg (QUEUED_VAR (x));
/* If the increment has happened and a pre-increment copy exists,
use that copy. */
if (QUEUED_COPY (x) != 0)
return QUEUED_COPY (x);
/* The increment has happened but we haven't set up a pre-increment copy.
Set one up now, and use it. */
QUEUED_COPY (x) = gen_reg_rtx (GET_MODE (QUEUED_VAR (x)));
emit_insn_before (gen_move_insn (QUEUED_COPY (x), QUEUED_VAR (x)),
QUEUED_INSN (x));
return QUEUED_COPY (x);
}
/* Return nonzero if X contains a QUEUED expression:
if it contains anything that will be altered by a queued increment.
We handle only combinations of MEM, PLUS, MINUS and MULT operators
since memory addresses generally contain only those. */
int
queued_subexp_p (rtx x)
{
enum rtx_code code = GET_CODE (x);
switch (code)
{
case QUEUED:
return 1;
case MEM:
return queued_subexp_p (XEXP (x, 0));
case MULT:
case PLUS:
case MINUS:
return (queued_subexp_p (XEXP (x, 0))
|| queued_subexp_p (XEXP (x, 1)));
default:
return 0;
}
}
/* Retrieve a mark on the queue. */
static rtx
mark_queue (void)
{
return pending_chain;
}
/* Perform all the pending incrementations that have been enqueued
after MARK was retrieved. If MARK is null, perform all the
pending incrementations. */
static void
emit_insns_enqueued_after_mark (rtx mark)
{
rtx p;
/* The marked incrementation may have been emitted in the meantime
through a call to emit_queue. In this case, the mark is not valid
anymore so do nothing. */
if (mark && ! QUEUED_BODY (mark))
return;
while ((p = pending_chain) != mark)
{
rtx body = QUEUED_BODY (p);
switch (GET_CODE (body))
{
case INSN:
case JUMP_INSN:
case CALL_INSN:
case CODE_LABEL:
case BARRIER:
case NOTE:
QUEUED_INSN (p) = body;
emit_insn (body);
break;
#ifdef ENABLE_CHECKING
case SEQUENCE:
abort ();
break;
#endif
default:
QUEUED_INSN (p) = emit_insn (body);
break;
}
QUEUED_BODY (p) = 0;
pending_chain = QUEUED_NEXT (p);
}
}
/* Perform all the pending incrementations. */
void
emit_queue (void)
{
emit_insns_enqueued_after_mark (NULL_RTX);
}
/* Copy data from FROM to TO, where the machine modes are not the same.
Both modes may be integer, or both may be floating.
UNSIGNEDP should be nonzero if FROM is an unsigned type.
This causes zero-extension instead of sign-extension. */
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 = GET_MODE_CLASS (to_mode) == MODE_FLOAT;
int from_real = GET_MODE_CLASS (from_mode) == MODE_FLOAT;
enum insn_code code;
rtx libcall;
/* rtx code for making an equivalent value. */
enum rtx_code equiv_code = (unsignedp < 0 ? UNKNOWN
: (unsignedp ? ZERO_EXTEND : SIGN_EXTEND));
to = protect_from_queue (to, 1);
from = protect_from_queue (from, 0);
if (to_real != from_real)
abort ();
/* If FROM is a SUBREG that indicates that we have already done at least
the required extension, strip it. We don't handle such SUBREGs as
TO here. */
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;
if (GET_CODE (to) == SUBREG && SUBREG_PROMOTED_VAR_P (to))
abort ();
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))
{
if (GET_MODE_BITSIZE (from_mode) != GET_MODE_BITSIZE (to_mode))
abort ();
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;
if (GET_MODE_PRECISION (from_mode) < GET_MODE_PRECISION (to_mode))
tab = sext_optab;
else if (GET_MODE_PRECISION (from_mode) > GET_MODE_PRECISION (to_mode))
tab = trunc_optab;
else
abort ();
/* Try converting directly if the insn is supported. */
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;
}
/* Otherwise use a libcall. */
libcall = tab->handlers[to_mode][from_mode].libfunc;
if (!libcall)
/* This conversion is not implemented yet. */
abort ();
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;
}
/* Handle pointer conversion. */ /* SPEE 900220. */
/* Targets are expected to provide conversion insns between PxImode and
xImode for all MODE_PARTIAL_INT modes they use, but no others. */
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);
if (trunc_optab->handlers[to_mode][full_mode].insn_code
== CODE_FOR_nothing)
abort ();
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)
{
enum machine_mode full_mode
= smallest_mode_for_size (GET_MODE_BITSIZE (from_mode), MODE_INT);
if (sext_optab->handlers[full_mode][from_mode].insn_code
== CODE_FOR_nothing)
abort ();
emit_unop_insn (sext_optab->handlers[full_mode][from_mode].insn_code,
to, from, UNKNOWN);
if (to_mode == full_mode)
return;
/* else proceed to integer conversions below */
from_mode = full_mode;
}
/* Now both modes are integers. */
/* Handle expanding beyond a word. */
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);
/* Try converting directly if the insn is supported. */
if ((code = can_extend_p (to_mode, from_mode, unsignedp))
!= CODE_FOR_nothing)
{
/* If FROM is a SUBREG, put it into a register. Do this
so that we always generate the same set of insns for
better cse'ing; if an intermediate assignment occurred,
we won't be doing the operation directly on the SUBREG. */
if (optimize > 0 && GET_CODE (from) == SUBREG)
from = force_reg (from_mode, from);
emit_unop_insn (code, to, from, equiv_code);
return;
}
/* Next, try converting via full word. */
else if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD
&& ((code = can_extend_p (to_mode, word_mode, unsignedp))
!= CODE_FOR_nothing))
{
if (GET_CODE (to) == REG)
{
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;
}
/* No special multiword conversion insn; do it by hand. */
start_sequence ();
/* Since we will turn this into a no conflict block, we must ensure
that the source does not overlap the target. */
if (reg_overlap_mentioned_p (to, from))
from = force_reg (from_mode, from);
/* Get a copy of FROM widened to a word, if necessary. */
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);
/* Compute the value to put in each remaining word. */
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);
}
}
/* Fill the remaining words. */
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);
if (subword == 0)
abort ();
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;
}
/* Truncating multi-word to a word or less. */
if (GET_MODE_BITSIZE (from_mode) > BITS_PER_WORD
&& GET_MODE_BITSIZE (to_mode) <= BITS_PER_WORD)
{
if (!((GET_CODE (from) == MEM
&& ! MEM_VOLATILE_P (from)
&& direct_load[(int) to_mode]
&& ! mode_dependent_address_p (XEXP (from, 0)))
|| GET_CODE (from) == REG
|| GET_CODE (from) == SUBREG))
from = force_reg (from_mode, from);
convert_move (to, gen_lowpart (word_mode, from), 0);
return;
}
/* Now follow all the conversions between integers
no more than a word long. */
/* For truncation, usually we can just refer to FROM in a narrower mode. */
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 (!((GET_CODE (from) == MEM
&& ! MEM_VOLATILE_P (from)
&& direct_load[(int) to_mode]
&& ! mode_dependent_address_p (XEXP (from, 0)))
|| GET_CODE (from) == REG
|| GET_CODE (from) == SUBREG))
from = force_reg (from_mode, from);
if (GET_CODE (from) == REG && 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;
}
/* Handle extension. */
if (GET_MODE_BITSIZE (to_mode) > GET_MODE_BITSIZE (from_mode))
{
/* Convert directly if that works. */
if ((code = can_extend_p (to_mode, from_mode, unsignedp))
!= CODE_FOR_nothing)
{
if (flag_force_mem)
from = force_not_mem (from);
emit_unop_insn (code, to, from, equiv_code);
return;
}
else
{
enum machine_mode intermediate;
rtx tmp;
tree shift_amount;
/* Search for a mode to convert via. */
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;
}
/* No suitable intermediate mode.
Generate what we need with shifts. */
shift_amount = build_int_2 (GET_MODE_BITSIZE (to_mode)
- GET_MODE_BITSIZE (from_mode), 0);
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;
}
}
/* Support special truncate insns for certain modes. */
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;
}
/* Handle truncation of volatile memrefs, and so on;
the things that couldn't be truncated directly,
and for which there was no special instruction.
??? Code above formerly short-circuited this, for most integer
mode pairs, with a force_reg in from_mode followed by a recursive
call to this routine. Appears always to have been wrong. */
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;
}
/* Mode combination is not recognized. */
abort ();
}
/* Return an rtx for a value that would result
from converting X to mode MODE.
Both X and MODE may be floating, or both integer.
UNSIGNEDP is nonzero if X is an unsigned value.
This can be done by referring to a part of X in place
or by copying to a new temporary with conversion.
This function *must not* call protect_from_queue
except when putting X into an insn (in which case convert_move does it). */
rtx
convert_to_mode (enum machine_mode mode, rtx x, int unsignedp)
{
return convert_modes (mode, VOIDmode, x, unsignedp);
}
/* Return an rtx for a value that would result
from converting X from mode OLDMODE to mode MODE.
Both modes may be floating, or both integer.
UNSIGNEDP is nonzero if X is an unsigned value.
This can be done by referring to a part of X in place
or by copying to a new temporary with conversion.
You can give VOIDmode for OLDMODE, if you are sure X has a nonvoid mode.
This function *must not* call protect_from_queue
except when putting X into an insn (in which case convert_move does it). */
rtx
convert_modes (enum machine_mode mode, enum machine_mode oldmode, rtx x, int unsignedp)
{
rtx temp;
/* If FROM is a SUBREG that indicates that we have already done at least
the required extension, strip it. */
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;
/* There is one case that we must handle specially: If we are converting
a CONST_INT into a mode whose size is twice HOST_BITS_PER_WIDE_INT and
we are to interpret the constant as unsigned, gen_lowpart will do
the wrong if the constant appears negative. What we want to do is
make the high-order word of the constant zero, not all ones. */
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);
/* We need to zero extend VAL. */
val &= ((HOST_WIDE_INT) 1 << width) - 1;
}
return immed_double_const (val, (HOST_WIDE_INT) 0, mode);
}
/* We can do this with a gen_lowpart if both desired and current modes
are integer, and this is either a constant integer, a register, or a
non-volatile MEM. Except for the constant case where MODE is no
wider than HOST_BITS_PER_WIDE_INT, we must be narrowing the operand. */
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)
&& ((GET_CODE (x) == MEM && ! MEM_VOLATILE_P (x)
&& direct_load[(int) mode])
|| (GET_CODE (x) == REG
&& (! 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 we don't know OLDMODE, we have to assume here that
X does not need sign- or zero-extension. This may not be
the case, but it's the best we can do. */
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);
/* We must sign or zero-extend in this case. Start by
zero-extending, then sign extend if we need to. */
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);
}
/* Converting from integer constant into mode is always equivalent to an
subreg operation. */
if (VECTOR_MODE_P (mode) && GET_MODE (x) == VOIDmode)
{
if (GET_MODE_BITSIZE (mode) != GET_MODE_BITSIZE (oldmode))
abort ();
return simplify_gen_subreg (mode, x, oldmode, 0);
}
temp = gen_reg_rtx (mode);
convert_move (temp, x, unsignedp);
return temp;
}
/* STORE_MAX_PIECES is the number of bytes at a time that we can
store efficiently. Due to internal GCC limitations, this is
MOVE_MAX_PIECES limited by the number of bytes GCC can represent
for an immediate constant. */
#define STORE_MAX_PIECES MIN (MOVE_MAX_PIECES, 2 * sizeof (HOST_WIDE_INT))
/* Determine whether the LEN bytes can be moved by using several move
instructions. Return nonzero if a call to move_by_pieces should
succeed. */
int
can_move_by_pieces (unsigned HOST_WIDE_INT len,
unsigned int align ATTRIBUTE_UNUSED)
{
return MOVE_BY_PIECES_P (len, align);
}
/* Generate several move instructions to copy LEN bytes from block FROM to
block TO. (These are MEM rtx's with BLKmode). The caller must pass FROM
and TO through protect_from_queue before calling.
If PUSH_ROUNDING is defined and TO is NULL, emit_single_push_insn is
used to push FROM to the stack.
ALIGN is maximum stack alignment we can assume.
If ENDP is 0 return to, if ENDP is 1 return memory at the end ala
mempcpy, and if ENDP is 2 return memory the end minus one byte ala
stpcpy. */
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 copying requires more than two move insns,
copy addresses to registers (to make displacements shorter)
and use post-increment if available. */
if (!(data.autinc_from && data.autinc_to)
&& move_by_pieces_ninsns (len, align) > 2)
{
/* Find the mode of the largest move... */
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);
}
if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
|| align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
align = MOVE_MAX * BITS_PER_UNIT;
/* First move what we can in the largest integer mode, then go to
successively smaller modes. */
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);
}
/* The code above should have handled everything. */
if (data.len > 0)
abort ();
if (endp)
{
rtx to1;
if (data.reverse)
abort ();
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;
}
/* Return number of insns required to move L bytes by pieces.
ALIGN (in bits) is maximum alignment we can assume. */
static unsigned HOST_WIDE_INT
move_by_pieces_ninsns (unsigned HOST_WIDE_INT l, unsigned int align)
{
unsigned HOST_WIDE_INT n_insns = 0;
unsigned HOST_WIDE_INT max_size = MOVE_MAX + 1;
if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
|| align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
align = MOVE_MAX * BITS_PER_UNIT;
while (max_size > 1)
{
enum machine_mode mode = VOIDmode, tmode;
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);
}
if (l)
abort ();
return n_insns;
}
/* Subroutine of move_by_pieces. Move as many bytes as appropriate
with move instructions for mode MODE. GENFUN is the gen_... function
to make a move insn for that mode. DATA has all the other info. */
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);
#else
abort ();
#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;
}
}
/* Emit code to move a block Y to a block X. This may be done with
string-move instructions, with multiple scalar move instructions,
or with a library call.
Both X and Y must be MEM rtx's (perhaps inside VOLATILE) with mode BLKmode.
SIZE is an rtx that says how long they are.
ALIGN is the maximum alignment we can assume they have.
METHOD describes what kind of copy this is, and what mechanisms may be used.
Return the address of the new block, if memcpy is called and returns it,
0 otherwise. */
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:
may_use_call = true;
break;
case BLOCK_OP_CALL_PARM:
may_use_call = block_move_libcall_safe_for_call_parm ();
/* Make inhibit_defer_pop nonzero around the library call
to force it to pop the arguments right away. */
NO_DEFER_POP;
break;
case BLOCK_OP_NO_LIBCALL:
may_use_call = false;
break;
default:
abort ();
}
align = MIN (MEM_ALIGN (x), MEM_ALIGN (y));
if (GET_MODE (x) != BLKmode)
abort ();
if (GET_MODE (y) != BLKmode)
abort ();
x = protect_from_queue (x, 1);
y = protect_from_queue (y, 0);
size = protect_from_queue (size, 0);
if (GET_CODE (x) != MEM)
abort ();
if (GET_CODE (y) != MEM)
abort ();
if (size == 0)
abort ();
/* Set MEM_SIZE as appropriate for this block copy. The main place this
can be incorrect is coming from __builtin_memcpy. */
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_movstr (x, y, size, align))
;
else if (may_use_call)
retval = emit_block_move_via_libcall (x, y, size);
else
emit_block_move_via_loop (x, y, size, align);
if (method == BLOCK_OP_CALL_PARM)
OK_DEFER_POP;
return retval;
}
/* A subroutine of emit_block_move. Returns true if calling the
block move libcall will not clobber any parameters which may have
already been placed on the stack. */
static bool
block_move_libcall_safe_for_call_parm (void)
{
/* If arguments are pushed on the stack, then they're safe. */
if (PUSH_ARGS)
return true;
/* If registers go on the stack anyway, any argument is sure to clobber
an outgoing argument. */
#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
/* If any argument goes in memory, then it might clobber an outgoing
argument. */
{
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;
#ifdef FUNCTION_ARG_PARTIAL_NREGS
if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode,
NULL_TREE, 1))
return false;
#endif
FUNCTION_ARG_ADVANCE (args_so_far, mode, NULL_TREE, 1);
}
}
return true;
}
/* A subroutine of emit_block_move. Expand a movstr pattern;
return true if successful. */
static bool
emit_block_move_via_movstr (rtx x, rtx y, rtx size, unsigned int align)
{
rtx opalign = GEN_INT (align / BITS_PER_UNIT);
enum machine_mode mode;
/* Since this is a move insn, we don't care about volatility. */
volatile_ok = 1;
/* Try the most limited insn first, because there's no point
including more than one in the machine description unless
the more limited one has some advantage. */
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
{
enum insn_code code = movstr_optab[(int) mode];
insn_operand_predicate_fn pred;
if (code != CODE_FOR_nothing
/* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT
here because if SIZE is less than the mode mask, as it is
returned by the macro, it will definitely be less than the
actual mode mask. */
&& ((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);
/* ??? When called via emit_block_move_for_call, it'd be
nice if there were some way to inform the backend, so
that it doesn't fail the expansion because it thinks
emitting the libcall would be more efficient. */
pat = GEN_FCN ((int) code) (x, y, op2, opalign);
if (pat)
{
emit_insn (pat);
volatile_ok = 0;
return true;
}
else
delete_insns_since (last);
}
}
volatile_ok = 0;
return false;
}
/* A subroutine of emit_block_move. Expand a call to memcpy or bcopy.
Return the return value from memcpy, 0 otherwise. */
static rtx
emit_block_move_via_libcall (rtx dst, rtx src, rtx size)
{
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, SRC, or SIZE may have been passed through protect_from_queue.
It is unsafe to save the value generated by protect_from_queue and reuse
it later. Consider what happens if emit_queue is called before the
return value from protect_from_queue is used.
Expansion of the CALL_EXPR below will call emit_queue before we are
finished emitting RTL for argument setup. So if we are not careful we
could get the wrong value for an argument.
To avoid this problem we go ahead and emit code to copy the addresses of
DST and SRC and SIZE into new pseudos. We can then place those new
pseudos into an RTL_EXPR and use them later, even after a call to
emit_queue.
Note this is not strictly needed for library calls since they do not call
emit_queue before loading their arguments. However, we may need to have
library calls call emit_queue in the future since failing to do so could
cause problems for targets which define SMALL_REGISTER_CLASSES and pass
arguments in registers. */
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);
if (TARGET_MEM_FUNCTIONS)
size_mode = TYPE_MODE (sizetype);
else
size_mode = TYPE_MODE (unsigned_type_node);
size = convert_to_mode (size_mode, size, 1);
size = copy_to_mode_reg (size_mode, size);
/* It is incorrect to use the libcall calling conventions to call
memcpy in this context. This could be a user call to memcpy and
the user may wish to examine the return value from memcpy. For
targets where libcalls and normal calls have different conventions
for returning pointers, we could end up generating incorrect code.
For convenience, we generate the call to bcopy this way as well. */
if (TARGET_MEM_FUNCTIONS)
size_tree = make_tree (sizetype, size);
else
size_tree = make_tree (unsigned_type_node, size);
fn = emit_block_move_libcall_fn (true);
arg_list = tree_cons (NULL_TREE, size_tree, NULL_TREE);
if (TARGET_MEM_FUNCTIONS)
{
arg_list = tree_cons (NULL_TREE, src_tree, arg_list);
arg_list = tree_cons (NULL_TREE, dst_tree, arg_list);
}
else
{
arg_list = tree_cons (NULL_TREE, dst_tree, arg_list);
arg_list = tree_cons (NULL_TREE, src_tree, arg_list);
}
/* Now we have to build up the CALL_EXPR itself. */
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arg_list, NULL_TREE);
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
/* If we are initializing a readonly value, show the above call clobbered
it. Otherwise, a load from it may erroneously be hoisted from a loop, or
the delay slot scheduler might overlook conflicts and take nasty
decisions. */
if (RTX_UNCHANGING_P (dst))
add_function_usage_to
(last_call_insn (), gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_CLOBBER (VOIDmode, dst),
NULL_RTX));
return TARGET_MEM_FUNCTIONS ? retval : NULL_RTX;
}
/* A subroutine of emit_block_move_via_libcall. Create the tree node
for the function we use for block copies. The first time FOR_CALL
is true, we call assemble_external. */
static GTY(()) tree block_move_fn;
void
init_block_move_fn (const char *asmspec)
{
if (!block_move_fn)
{
tree args, fn;
if (TARGET_MEM_FUNCTIONS)
{
fn = get_identifier ("memcpy");
args = build_function_type_list (ptr_type_node, ptr_type_node,
const_ptr_type_node, sizetype,
NULL_TREE);
}
else
{
fn = get_identifier ("bcopy");
args = build_function_type_list (void_type_node, const_ptr_type_node,
ptr_type_node, unsigned_type_node,
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;
block_move_fn = fn;
}
if (asmspec)
{
SET_DECL_RTL (block_move_fn, NULL_RTX);
SET_DECL_ASSEMBLER_NAME (block_move_fn, get_identifier (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, NULL);
assemble_external (block_move_fn);
}
return block_move_fn;
}
/* A subroutine of emit_block_move. Copy the data via an explicit
loop. This is used only when libcalls are forbidden. */
/* ??? It'd be nice to copy in hunks larger than QImode. */
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_note (NOTE_INSN_LOOP_BEG);
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_note (NOTE_INSN_LOOP_CONT);
emit_label (cmp_label);
emit_cmp_and_jump_insns (iter, size, LT, NULL_RTX, iter_mode,
true, top_label);
emit_note (NOTE_INSN_LOOP_END);
}
/* Copy all or part of a value X into registers starting at REGNO.
The number of registers to be filled is NREGS. */
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));
/* See if the machine can do this with a load multiple insn. */
#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));
}
/* Copy all or part of a BLKmode value X out of registers starting at REGNO.
The number of registers to be filled is NREGS. */
void
move_block_from_reg (int regno, rtx x, int nregs)
{
int i;
if (nregs == 0)
return;
/* See if the machine can do this with a store multiple insn. */
#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);
if (tem == 0)
abort ();
emit_move_insn (tem, gen_rtx_REG (word_mode, regno + i));
}
}
/* Generate a PARALLEL rtx for a new non-consecutive group of registers from
ORIG, where ORIG is a non-consecutive group of registers represented by
a PARALLEL. The clone is identical to the original except in that the
original set of registers is replaced by a new set of pseudo registers.
The new set has the same modes as the original set. */
rtx
gen_group_rtx (rtx orig)
{
int i, length;
rtx *tmps;
if (GET_CODE (orig) != PARALLEL)
abort ();
length = XVECLEN (orig, 0);
tmps = alloca (sizeof (rtx) * length);
/* Skip a NULL entry in first slot. */
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));
}
/* Emit code to move a block ORIG_SRC of type TYPE to a block DST,
where DST is non-consecutive registers represented by a PARALLEL.
SSIZE represents the total size of block ORIG_SRC in bytes, or -1
if not known. */
void
emit_group_load (rtx dst, rtx orig_src, tree type ATTRIBUTE_UNUSED, int ssize)
{
rtx *tmps, src;
int start, i;
if (GET_CODE (dst) != PARALLEL)
abort ();
/* Check for a NULL entry, used to indicate that the parameter goes
both on the stack and in registers. */
if (XEXP (XVECEXP (dst, 0, 0), 0))
start = 0;
else
start = 1;
tmps = alloca (sizeof (rtx) * XVECLEN (dst, 0));
/* Process the pieces. */
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;
/* Handle trailing fragments that run over the size of the struct. */
if (ssize >= 0 && bytepos + (HOST_WIDE_INT) bytelen > ssize)
{
/* Arrange to shift the fragment to where it belongs.
extract_bit_field loads to the lsb of the reg. */
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;
if (bytelen <= 0)
abort ();
}
/* If we won't be loading directly from memory, protect the real source
from strange tricks we might play; but make sure that the source can
be loaded directly into the destination. */
src = orig_src;
if (GET_CODE (orig_src) != MEM
&& (!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);
}
/* Optimize the access just a bit. */
if (GET_CODE (src) == MEM
&& (! 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 (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))
{
/* The following assumes that the concatenated objects all
have the same size. In this case, a simple calculation
can be used to determine the object and the bit field
to be extracted. */
tmps[i] = XEXP (src, bytepos / slen0);
if (! CONSTANT_P (tmps[i])
&& (GET_CODE (tmps[i]) != REG || 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, ssize);
}
else if (bytepos == 0)
{
rtx mem = assign_stack_temp (GET_MODE (src), slen, 0);
emit_move_insn (mem, src);
tmps[i] = adjust_address (mem, mode, 0);
}
else
abort ();
}
/* FIXME: A SIMD parallel will eventually lead to a subreg of a
SIMD register, which is currently broken. While we get GCC
to emit proper RTL for these cases, let's dump to memory. */
else if (VECTOR_MODE_P (GET_MODE (dst))
&& GET_CODE (src) == REG)
{
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)
|| (GET_CODE (src) == REG && 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, ssize);
if (shift)
expand_binop (mode, ashl_optab, tmps[i], GEN_INT (shift),
tmps[i], 0, OPTAB_WIDEN);
}
emit_queue ();
/* Copy the extracted pieces into the proper (probable) hard regs. */
for (i = start; i < XVECLEN (dst, 0); i++)
emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0), tmps[i]);
}
/* Emit code to move a block SRC to block DST, where SRC and DST are
non-consecutive groups of registers, each represented by a PARALLEL. */
void
emit_group_move (rtx dst, rtx src)
{
int i;
if (GET_CODE (src) != PARALLEL
|| GET_CODE (dst) != PARALLEL
|| XVECLEN (src, 0) != XVECLEN (dst, 0))
abort ();
/* Skip first entry if NULL. */
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));
}
/* Emit code to move a block SRC to a block ORIG_DST of type TYPE,
where SRC is non-consecutive registers represented by a PARALLEL.
SSIZE represents the total size of block ORIG_DST, or -1 if not
known. */
void
emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED, int ssize)
{
rtx *tmps, dst;
int start, i;
if (GET_CODE (src) != PARALLEL)
abort ();
/* Check for a NULL entry, used to indicate that the parameter goes
both on the stack and in registers. */
if (XEXP (XVECEXP (src, 0, 0), 0))
start = 0;
else
start = 1;
tmps = alloca (sizeof (rtx) * XVECLEN (src, 0));
/* Copy the (probable) hard regs into pseudos. */
for (i = start; i < XVECLEN (src, 0); i++)
{
rtx reg = XEXP (XVECEXP (src, 0, i), 0);
tmps[i] = gen_reg_rtx (GET_MODE (reg));
emit_move_insn (tmps[i], reg);
}
emit_queue ();
/* If we won't be storing directly into memory, protect the real destination
from strange tricks we might play. */
dst = orig_dst;
if (GET_CODE (dst) == PARALLEL)
{
rtx temp;
/* We can get a PARALLEL dst if there is a conditional expression in
a return statement. In that case, the dst and src are the same,
so no action is necessary. */
if (rtx_equal_p (dst, src))
return;
/* It is unclear if we can ever reach here, but we may as well handle
it. Allocate a temporary, and split this into a store/load to/from
the temporary. */
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 (GET_CODE (dst) != MEM && GET_CODE (dst) != CONCAT)
{
dst = gen_reg_rtx (GET_MODE (orig_dst));
/* Make life a bit easier for combine. */
emit_move_insn (dst, CONST0_RTX (GET_MODE (orig_dst)));
}
/* Process the pieces. */
for (i = start; i < XVECLEN (src, 0); 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;
/* Handle trailing fragments that run over the size of the struct. */
if (ssize >= 0 && bytepos + (HOST_WIDE_INT) bytelen > ssize)
{
/* store_bit_field always takes its value from the lsb.
Move the fragment to the lsb if it's not already there. */
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;
expand_binop (mode, ashr_optab, tmps[i], GEN_INT (shift),
tmps[i], 0, OPTAB_WIDEN);
}
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 if (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;
}
else
abort ();
}
/* Optimize the access just a bit. */
if (GET_CODE (dest) == MEM
&& (! 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], ssize);
}
emit_queue ();
/* Copy from the pseudo into the (probable) hard reg. */
if (orig_dst != dst)
emit_move_insn (orig_dst, dst);
}
/* Generate code to copy a BLKmode object of TYPE out of a
set of registers starting with SRCREG into TGTBLK. If TGTBLK
is null, a stack temporary is created. TGTBLK is returned.
The purpose of this routine is to handle functions that return
BLKmode structures in registers. Some machines (the PA for example)
want to return all small structures in registers regardless of the
structure's alignment. */
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);
}
/* This code assumes srcreg is at least a full word. If it isn't, copy it
into a new pseudo which is a full word. */
if (GET_MODE (srcreg) != BLKmode
&& GET_MODE_SIZE (GET_MODE (srcreg)) < UNITS_PER_WORD)
srcreg = convert_to_mode (word_mode, srcreg, TREE_UNSIGNED (type));
/* If the structure doesn't take up a whole number of words, see whether
SRCREG is padded on the left or on the right. If it's on the left,
set PADDING_CORRECTION to the number of bits to skip.
In most ABIs, the structure will be returned at the least end of
the register, which translates to right padding on little-endian
targets and left padding on big-endian targets. The opposite
holds if the structure is returned at the most significant
end of the register. */
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));
/* Copy the structure BITSIZE bites at a time.
We could probably emit more efficient code for machines which do not use
strict alignment, but it doesn't seem worth the effort at the current
time. */
for (bitpos = 0, xbitpos = padding_correction;
bitpos < bytes * BITS_PER_UNIT;
bitpos += bitsize, xbitpos += bitsize)
{
/* We need a new source operand each time xbitpos is on a
word boundary and when xbitpos == padding_correction
(the first time through). */
if (xbitpos % BITS_PER_WORD == 0
|| xbitpos == padding_correction)
src = operand_subword_force (srcreg, xbitpos / BITS_PER_WORD,
GET_MODE (srcreg));
/* We need a new destination operand each time bitpos is on
a word boundary. */
if (bitpos % BITS_PER_WORD == 0)
dst = operand_subword (tgtblk, bitpos / BITS_PER_WORD, 1, BLKmode);
/* Use xbitpos for the source extraction (right justified) and
xbitpos for the destination store (left justified). */
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,
BITS_PER_WORD),
BITS_PER_WORD);
}
return tgtblk;
}
/* Add a USE expression for REG to the (possibly empty) list pointed
to by CALL_FUSAGE. REG must denote a hard register. */
void
use_reg (rtx *call_fusage, rtx reg)
{
if (GET_CODE (reg) != REG
|| REGNO (reg) >= FIRST_PSEUDO_REGISTER)
abort ();
*call_fusage
= gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_USE (VOIDmode, reg), *call_fusage);
}
/* Add USE expressions to *CALL_FUSAGE for each of NREGS consecutive regs,
starting at REGNO. All of these registers must be hard registers. */
void
use_regs (rtx *call_fusage, int regno, int nregs)
{
int i;
if (regno + nregs > FIRST_PSEUDO_REGISTER)
abort ();
for (i = 0; i < nregs; i++)
use_reg (call_fusage, regno_reg_rtx[regno + i]);
}
/* Add USE expressions to *CALL_FUSAGE for each REG contained in the
PARALLEL REGS. This is for calls that pass values in multiple
non-contiguous locations. The Irix 6 ABI has examples of this. */
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);
/* A NULL entry means the parameter goes both on the stack and in
registers. This can also be a MEM for targets that pass values
partially on the stack and partially in registers. */
if (reg != 0 && GET_CODE (reg) == REG)
use_reg (call_fusage, reg);
}
}
/* Determine whether the LEN bytes generated by CONSTFUN can be
stored to memory using several move instructions. CONSTFUNDATA is
a pointer which will be passed as argument in every CONSTFUN call.
ALIGN is maximum alignment we can assume. Return nonzero if a
call to store_by_pieces should succeed. */
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 max_size, l;
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;
if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
|| align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
align = MOVE_MAX * BITS_PER_UNIT;
/* We would first store what we can in the largest integer mode, then go to
successively smaller modes. */
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);
}
/* The code above should have handled everything. */
if (l != 0)
abort ();
}
return 1;
}
/* Generate several move instructions to store LEN bytes generated by
CONSTFUN to block TO. (A MEM rtx with BLKmode). CONSTFUNDATA is a
pointer which will be passed as argument in every CONSTFUN call.
ALIGN is maximum alignment we can assume.
If ENDP is 0 return to, if ENDP is 1 return memory at the end ala
mempcpy, and if ENDP is 2 return memory the end minus one byte ala
stpcpy. */
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)
{
if (endp == 2)
abort ();
return to;
}
if (! STORE_BY_PIECES_P (len, align))
abort ();
to = protect_from_queue (to, 1);
data.constfun = constfun;
data.constfundata = constfundata;
data.len = len;
data.to = to;
store_by_pieces_1 (&data, align);
if (endp)
{
rtx to1;
if (data.reverse)
abort ();
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;
}
/* Generate several move instructions to clear LEN bytes of block TO. (A MEM
rtx with BLKmode). The caller must pass TO through protect_from_queue
before calling. ALIGN is maximum alignment we can assume. */
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);
}
/* Callback routine for clear_by_pieces.
Return const0_rtx unconditionally. */
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;
}
/* Subroutine of clear_by_pieces and store_by_pieces.
Generate several move instructions to store LEN bytes of block TO. (A MEM
rtx with BLKmode). The caller must pass TO through protect_from_queue
before calling. ALIGN is maximum alignment we can assume. */
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 HOST_WIDE_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 storing requires more than two move insns,
copy addresses to registers (to make displacements shorter)
and use post-increment if available. */
if (!data->autinc_to
&& move_by_pieces_ninsns (data->len, align) > 2)
{
/* Determine the main mode we'll be using. */
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);
}
if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
|| align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
align = MOVE_MAX * BITS_PER_UNIT;
/* First store what we can in the largest integer mode, then go to
successively smaller modes. */
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);
}
/* The code above should have handled everything. */
if (data->len != 0)
abort ();
}
/* Subroutine of store_by_pieces_1. Store as many bytes as appropriate
with move instructions for mode MODE. GENFUN is the gen_... function
to make a move insn for that mode. DATA has all the other info. */
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;
}
}
/* Write zeros through the storage of OBJECT. If OBJECT has BLKmode, SIZE is
its length in bytes. */
rtx
clear_storage (rtx object, rtx size)
{
rtx retval = 0;
unsigned int align = (GET_CODE (object) == MEM ? MEM_ALIGN (object)
: GET_MODE_ALIGNMENT (GET_MODE (object)));
/* If OBJECT is not BLKmode and SIZE is the same size as its mode,
just move a zero. Otherwise, do this a piece at a time. */
if (GET_MODE (object) != BLKmode
&& GET_CODE (size) == CONST_INT
&& INTVAL (size) == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (object)))
emit_move_insn (object, CONST0_RTX (GET_MODE (object)));
else
{
object = protect_from_queue (object, 1);
size = protect_from_queue (size, 0);
if (size == const0_rtx)
;
else if (GET_CODE (size) == CONST_INT
&& CLEAR_BY_PIECES_P (INTVAL (size), align))
clear_by_pieces (object, INTVAL (size), align);
else if (clear_storage_via_clrstr (object, size, align))
;
else
retval = clear_storage_via_libcall (object, size);
}
return retval;
}
/* A subroutine of clear_storage. Expand a clrstr pattern;
return true if successful. */
static bool
clear_storage_via_clrstr (rtx object, rtx size, unsigned int align)
{
/* Try the most limited insn first, because there's no point
including more than one in the machine description unless
the more limited one has some advantage. */
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 = clrstr_optab[(int) mode];
insn_operand_predicate_fn pred;
if (code != CODE_FOR_nothing
/* We don't need MODE to be narrower than
BITS_PER_HOST_WIDE_INT here because if SIZE is less than
the mode mask, as it is returned by the macro, it will
definitely be less than the actual mode mask. */
&& ((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[2].predicate) == 0
|| (*pred) (opalign, VOIDmode)))
{
rtx op1;
rtx last = get_last_insn ();
rtx pat;
op1 = convert_to_mode (mode, size, 1);
pred = insn_data[(int) code].operand[1].predicate;
if (pred != 0 && ! (*pred) (op1, mode))
op1 = copy_to_mode_reg (mode, op1);
pat = GEN_FCN ((int) code) (object, op1, opalign);
if (pat)
{
emit_insn (pat);
return true;
}
else
delete_insns_since (last);
}
}
return false;
}
/* A subroutine of clear_storage. Expand a call to memset or bzero.
Return the return value of memset, 0 otherwise. */
static rtx
clear_storage_via_libcall (rtx object, rtx size)
{
tree call_expr, arg_list, fn, object_tree, size_tree;
enum machine_mode size_mode;
rtx retval;
/* OBJECT or SIZE may have been passed through protect_from_queue.
It is unsafe to save the value generated by protect_from_queue
and reuse it later. Consider what happens if emit_queue is
called before the return value from protect_from_queue is used.
Expansion of the CALL_EXPR below will call emit_queue before
we are finished emitting RTL for argument setup. So if we are
not careful we could get the wrong value for an argument.
To avoid this problem we go ahead and emit code to copy OBJECT
and SIZE into new pseudos. We can then place those new pseudos
into an RTL_EXPR and use them later, even after a call to
emit_queue.
Note this is not strictly needed for library calls since they
do not call emit_queue before loading their arguments. However,
we may need to have library calls call emit_queue in the future
since failing to do so could cause problems for targets which
define SMALL_REGISTER_CLASSES and pass arguments in registers. */
object = copy_to_mode_reg (Pmode, XEXP (object, 0));
if (TARGET_MEM_FUNCTIONS)
size_mode = TYPE_MODE (sizetype);
else
size_mode = TYPE_MODE (unsigned_type_node);
size = convert_to_mode (size_mode, size, 1);
size = copy_to_mode_reg (size_mode, size);
/* It is incorrect to use the libcall calling conventions to call
memset in this context. This could be a user call to memset and
the user may wish to examine the return value from memset. For
targets where libcalls and normal calls have different conventions
for returning pointers, we could end up generating incorrect code.
For convenience, we generate the call to bzero this way as well. */
object_tree = make_tree (ptr_type_node, object);
if (TARGET_MEM_FUNCTIONS)
size_tree = make_tree (sizetype, size);
else
size_tree = make_tree (unsigned_type_node, size);
fn = clear_storage_libcall_fn (true);
arg_list = tree_cons (NULL_TREE, size_tree, NULL_TREE);
if (TARGET_MEM_FUNCTIONS)
arg_list = tree_cons (NULL_TREE, integer_zero_node, arg_list);
arg_list = tree_cons (NULL_TREE, object_tree, arg_list);
/* Now we have to build up the CALL_EXPR itself. */
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arg_list, NULL_TREE);
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
/* If we are initializing a readonly value, show the above call
clobbered it. Otherwise, a load from it may erroneously be
hoisted from a loop. */
if (RTX_UNCHANGING_P (object))
emit_insn (gen_rtx_CLOBBER (VOIDmode, object));
return (TARGET_MEM_FUNCTIONS ? retval : NULL_RTX);
}
/* A subroutine of clear_storage_via_libcall. Create the tree node
for the function we use for block clears. The first time FOR_CALL
is true, we call assemble_external. */
static GTY(()) tree block_clear_fn;
void
init_block_clear_fn (const char *asmspec)
{
if (!block_clear_fn)
{
tree fn, args;
if (TARGET_MEM_FUNCTIONS)
{
fn = get_identifier ("memset");
args = build_function_type_list (ptr_type_node, ptr_type_node,
integer_type_node, sizetype,
NULL_TREE);
}
else
{
fn = get_identifier ("bzero");
args = build_function_type_list (void_type_node, ptr_type_node,
unsigned_type_node, 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;
block_clear_fn = fn;
}
if (asmspec)
{
SET_DECL_RTL (block_clear_fn, NULL_RTX);
SET_DECL_ASSEMBLER_NAME (block_clear_fn, get_identifier (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, NULL);
assemble_external (block_clear_fn);
}
return block_clear_fn;
}
/* 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.
This mode cannot be BLKmode; use emit_block_move for that.
Return the last instruction emitted. */
rtx
emit_move_insn (rtx x, rtx y)
{
enum machine_mode mode = GET_MODE (x);
rtx y_cst = NULL_RTX;
rtx last_insn, set;
x = protect_from_queue (x, 1);
y = protect_from_queue (y, 0);
if (mode == BLKmode || (GET_MODE (y) != mode && GET_MODE (y) != VOIDmode))
abort ();
/* Never force constant_p_rtx to memory. */
if (GET_CODE (y) == CONSTANT_P_RTX)
;
else 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 the target's cannot_force_const_mem prevented the spill,
assume that the target's move expanders will also take care
of the non-legitimate constant. */
if (!y)
y = y_cst;
}
}
/* If X or Y are memory references, verify that their addresses are valid
for the machine. */
if (GET_CODE (x) == MEM
&& ((! 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 (GET_CODE (y) == MEM
&& (! memory_address_p (GET_MODE (y), XEXP (y, 0))
|| (flag_force_addr
&& CONSTANT_ADDRESS_P (XEXP (y, 0)))))
y = validize_mem (y);
if (mode == BLKmode)
abort ();
last_insn = emit_move_insn_1 (x, y);
if (y_cst && GET_CODE (x) == REG
&& (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;
}
/* 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;
enum mode_class class = GET_MODE_CLASS (mode);
if ((unsigned int) mode >= (unsigned int) MAX_MACHINE_MODE)
abort ();
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 ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
&& BLKmode != (submode = GET_MODE_INNER (mode))
&& (mov_optab->handlers[(int) submode].insn_code
!= CODE_FOR_nothing))
{
/* Don't split destination if it is a stack push. */
int stack = push_operand (x, GET_MODE (x));
#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 (GET_MODE_SIZE (submode))
!= GET_MODE_SIZE (submode)))
{
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
(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);
#ifdef STACK_GROWS_DOWNWARD
offset1 = 0;
offset2 = GET_MODE_SIZE (submode);
#else
offset1 = -PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)));
offset2 = (-PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)))
+ GET_MODE_SIZE (submode));
#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 (GET_MODE_BITSIZE (mode) < 2 * BITS_PER_WORD
&& (reload_in_progress | reload_completed) == 0)
{
int packed_dest_p
= (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER);
int packed_src_p
= (REG_P (y) && REGNO (y) < FIRST_PSEUDO_REGISTER);
if (packed_dest_p || packed_src_p)
{
enum mode_class reg_class = ((class == MODE_COMPLEX_FLOAT)
? MODE_FLOAT : MODE_INT);
enum machine_mode reg_mode
= mode_for_size (GET_MODE_BITSIZE (mode), reg_class, 1);
if (reg_mode != BLKmode)
{
rtx mem = assign_stack_temp (reg_mode,
GET_MODE_SIZE (mode), 0);
rtx cmem = adjust_address (mem, mode, 0);
cfun->cannot_inline
= N_("function using short complex types cannot be inline");
if (packed_dest_p)
{
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;
if (tmode == VOIDmode)
abort ();
/* 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 && GET_CODE (x1) == MEM)
{
x = adjust_address_nv (x1, tmode, 0);
copy_replacements (x1, x);
}
y = gen_lowpart_common (tmode, y1);
if (y == 0 && GET_CODE (y1) == MEM)
{
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 if (GET_MODE_SIZE (mode) >= UNITS_PER_WORD)
{
rtx last_insn </