xtensa-protos.h (xtensa_copy_incoming_a7): Update.

* config/xtensa/xtensa-protos.h (xtensa_copy_incoming_a7): Update.
	(init_cumulative_args): Likewise.
	(a7_overlap_mentioned_p): Delete prototype.
	* config/xtensa/xtensa.c (struct machine_function): Replace
	incoming_a7_copied field with need_a7_copy and vararg_a7 flags.
	Add set_frame_ptr_insn field.
	(xtensa_emit_move_sequence): Update call to xtensa_copy_incoming_a7.
	(xtensa_copy_incoming_a7): Rewrite to check need_a7_copy flag and check
	if the operand is an argument in a7.  If so, copy a7 to a new pseudo
	at the function entry and replace the operand with the pseudo.
	(init_cumulative_args): Remove unused arguments.  Add new "incoming"
	argument and record this flag in CUMULATIVE_ARGS.
	(function_arg): Remove result_mode and special-case code to handle
	arguments in a7.  Instead, set need_a7_copy flag when there is an
	incoming argument in a7.
	(xtensa_expand_prologue): Remove code to search for set_frame_ptr insn
	and use the value recorded in cfun->machine->set_frame_ptr_insn.
	(xtensa_builtin_saveregs): Check for negative gp_left value.  Set
	need_a7_copy and vararg_a7 flags.  Use move_block_from_reg instead of
	special-case code.
	(a7_overlap_mentioned_p): Delete.
	* config/xtensa/xtensa.h (CUMULATIVE_ARGS): Add "incoming" flag.
	(INIT_CUMULATIVE_ARGS, INIT_CUMULATIVE_INCOMING_ARGS): Remove useless
	arguments to init_cumulative_args and pass "incoming" flag instead.
	(BLOCK_REG_PADDING): Delete.
	* config/xtensa/xtensa.md (movdi, movsf, movdf): Remove unnecessary
	checks for reload_in_progress and reload_completed.  Update calls to
	xtensa_copy_incoming_a7.
	(ashlsi3): Rename existing insn to ashlsi3_internal.  Add expander
	to call xtensa_copy_incoming_a7.

From-SVN: r77583
This commit is contained in:
Bob Wilson 2004-02-10 01:35:53 +00:00 committed by Bob Wilson
parent 99bee03ac2
commit 997b8b4d4c
5 changed files with 191 additions and 258 deletions

View File

@ -1,3 +1,36 @@
2004-02-09 Bob Wilson <bob.wilson@acm.org>
* config/xtensa/xtensa-protos.h (xtensa_copy_incoming_a7): Update.
(init_cumulative_args): Likewise.
(a7_overlap_mentioned_p): Delete prototype.
* config/xtensa/xtensa.c (struct machine_function): Replace
incoming_a7_copied field with need_a7_copy and vararg_a7 flags.
Add set_frame_ptr_insn field.
(xtensa_emit_move_sequence): Update call to xtensa_copy_incoming_a7.
(xtensa_copy_incoming_a7): Rewrite to check need_a7_copy flag and check
if the operand is an argument in a7. If so, copy a7 to a new pseudo
at the function entry and replace the operand with the pseudo.
(init_cumulative_args): Remove unused arguments. Add new "incoming"
argument and record this flag in CUMULATIVE_ARGS.
(function_arg): Remove result_mode and special-case code to handle
arguments in a7. Instead, set need_a7_copy flag when there is an
incoming argument in a7.
(xtensa_expand_prologue): Remove code to search for set_frame_ptr insn
and use the value recorded in cfun->machine->set_frame_ptr_insn.
(xtensa_builtin_saveregs): Check for negative gp_left value. Set
need_a7_copy and vararg_a7 flags. Use move_block_from_reg instead of
special-case code.
(a7_overlap_mentioned_p): Delete.
* config/xtensa/xtensa.h (CUMULATIVE_ARGS): Add "incoming" flag.
(INIT_CUMULATIVE_ARGS, INIT_CUMULATIVE_INCOMING_ARGS): Remove useless
arguments to init_cumulative_args and pass "incoming" flag instead.
(BLOCK_REG_PADDING): Delete.
* config/xtensa/xtensa.md (movdi, movsf, movdf): Remove unnecessary
checks for reload_in_progress and reload_completed. Update calls to
xtensa_copy_incoming_a7.
(ashlsi3): Rename existing insn to ashlsi3_internal. Add expander
to call xtensa_copy_incoming_a7.
2004-02-09 DJ Delorie <dj@redhat.com>
* config/i386/xm-djgpp.h (GCC_DRIVER_HOST_INITIALIZATION): No

View File

@ -68,14 +68,14 @@ extern int xtensa_expand_scc (rtx *);
extern int xtensa_expand_block_move (rtx *);
extern void xtensa_split_operand_pair (rtx *, enum machine_mode);
extern int xtensa_emit_move_sequence (rtx *, enum machine_mode);
extern bool xtensa_copy_incoming_a7 (rtx *, enum machine_mode);
extern rtx xtensa_copy_incoming_a7 (rtx);
extern void xtensa_emit_block_move (rtx *, rtx *, int);
extern void xtensa_expand_nonlocal_goto (rtx *);
extern void xtensa_emit_loop_end (rtx, rtx *);
extern char *xtensa_emit_call (int, rtx *);
#ifdef TREE_CODE
extern void init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx);
extern void init_cumulative_args (CUMULATIVE_ARGS *, int);
extern void xtensa_va_start (tree, rtx);
extern rtx xtensa_va_arg (tree, tree);
#endif /* TREE_CODE */
@ -88,7 +88,6 @@ extern enum reg_class xtensa_preferred_reload_class (rtx, enum reg_class, int);
extern enum reg_class xtensa_secondary_reload_class (enum reg_class,
enum machine_mode, rtx,
int);
extern int a7_overlap_mentioned_p (rtx);
#endif /* RTX_CODE */
#ifdef TREE_CODE

View File

@ -91,7 +91,9 @@ const char *xtensa_st_opcodes[(int) MAX_MACHINE_MODE];
struct machine_function GTY(())
{
int accesses_prev_frame;
bool incoming_a7_copied;
bool need_a7_copy;
bool vararg_a7;
rtx set_frame_ptr_insn;
};
/* Vector, indexed by hard register number, which contains 1 for a
@ -1271,14 +1273,11 @@ xtensa_emit_move_sequence (rtx *operands, enum machine_mode mode)
}
}
if (!(reload_in_progress | reload_completed))
{
if (!xtensa_valid_move (mode, operands))
operands[1] = force_reg (mode, operands[1]);
if (!(reload_in_progress | reload_completed)
&& !xtensa_valid_move (mode, operands))
operands[1] = force_reg (mode, operands[1]);
if (xtensa_copy_incoming_a7 (operands, mode))
return 1;
}
operands[1] = xtensa_copy_incoming_a7 (operands[1]);
/* During reload we don't want to emit (subreg:X (mem:Y)) since that
instruction won't be recognized after reload, so we remove the
@ -1309,89 +1308,114 @@ fixup_subreg_mem (rtx x)
}
/* Check if this move is copying an incoming argument in a7. If so,
emit the move, followed by the special "set_frame_ptr"
unspec_volatile insn, at the very beginning of the function. This
is necessary because the register allocator will ignore conflicts
with a7 and may assign some other pseudo to a7. If that pseudo was
assigned prior to this move, it would clobber the incoming argument
in a7. By copying the argument out of a7 as the very first thing,
and then immediately following that with an unspec_volatile to keep
the scheduler away, we should avoid any problems. */
/* Check if an incoming argument in a7 is expected to be used soon and
if OPND is a register or register pair that includes a7. If so,
create a new pseudo and copy a7 into that pseudo at the very
beginning of the function, followed by the special "set_frame_ptr"
unspec_volatile insn. The return value is either the original
operand, if it is not a7, or the new pseudo containing a copy of
the incoming argument. This is necessary because the register
allocator will ignore conflicts with a7 and may either assign some
other pseudo to a7 or use a7 as the hard_frame_pointer, clobbering
the incoming argument in a7. By copying the argument out of a7 as
the very first thing, and then immediately following that with an
unspec_volatile to keep the scheduler away, we should avoid any
problems. Putting the set_frame_ptr insn at the beginning, with
only the a7 copy before it, also makes it easier for the prologue
expander to initialize the frame pointer after the a7 copy and to
fix up the a7 copy to use the stack pointer instead of the frame
pointer. */
bool
xtensa_copy_incoming_a7 (rtx *operands, enum machine_mode mode)
rtx
xtensa_copy_incoming_a7 (rtx opnd)
{
if (a7_overlap_mentioned_p (operands[1])
&& !cfun->machine->incoming_a7_copied)
rtx entry_insns = 0;
rtx reg, tmp;
enum machine_mode mode;
if (!cfun->machine->need_a7_copy)
return opnd;
/* This function should never be called again once a7 has been copied. */
if (cfun->machine->set_frame_ptr_insn)
abort ();
mode = GET_MODE (opnd);
/* The operand using a7 may come in a later instruction, so just return
the original operand if it doesn't use a7. */
reg = opnd;
if (GET_CODE (reg) == SUBREG)
{
rtx mov, src;
if (SUBREG_BYTE (reg) != 0)
abort ();
reg = SUBREG_REG (reg);
}
if (GET_CODE (reg) != REG
|| REGNO (reg) > A7_REG
|| REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) <= A7_REG)
return opnd;
/* Despite defining SPLIT_COMPLEX_ARGS, complex function
arguments may still appear if they are wrapped in a struct.
For CQImode and CHImode arguments, this results in a move
with a source operand of the form: "(subreg:SI (reg:CHI a7)
0)". The subreg is later removed by the reload pass,
resulting in the RTL for a7 being regenerated using
hard_frame_pointer_rtx, and making it impossible for us to
distinguish the function argument. Detect this here when
generating the RTL and remove the subreg immediately so that
reload won't mess it up. */
src = operands[1];
if (GET_CODE (src) == SUBREG
&& GET_CODE (SUBREG_REG (src)) == REG
&& REGNO (SUBREG_REG (src)) == A7_REG
&& SUBREG_BYTE (src) == 0
&& (GET_MODE (SUBREG_REG (src)) == CHImode
|| GET_MODE (SUBREG_REG (src)) == CQImode))
operands[1] = gen_raw_REG (mode, A7_REG);
/* 1-word args will always be in a7; 2-word args in a6/a7. */
if (REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) - 1 != A7_REG)
abort ();
switch (mode)
{
case DFmode:
mov = gen_movdf_internal (operands[0], operands[1]);
break;
case SFmode:
mov = gen_movsf_internal (operands[0], operands[1]);
break;
case DImode:
mov = gen_movdi_internal (operands[0], operands[1]);
break;
case SImode:
mov = gen_movsi_internal (operands[0], operands[1]);
break;
case HImode:
mov = gen_movhi_internal (operands[0], operands[1]);
break;
case QImode:
mov = gen_movqi_internal (operands[0], operands[1]);
break;
default:
abort ();
}
cfun->machine->need_a7_copy = false;
/* Insert the instructions before any other argument copies.
(The set_frame_ptr insn comes _after_ the move, so push it
out first.) */
push_topmost_sequence ();
emit_insn_after (gen_set_frame_ptr (), get_insns ());
emit_insn_after (mov, get_insns ());
pop_topmost_sequence ();
/* Copy a7 to a new pseudo at the function entry. Use gen_raw_REG to
create the REG for a7 so that hard_frame_pointer_rtx is not used. */
/* Ideally the incoming argument in a7 would only be copied
once, since propagating a7 into the body of a function
will almost certainly lead to errors. However, there is
at least one harmless case (in GCSE) where the original
copy from a7 is changed to copy into a new pseudo. Thus,
we use a flag to only do this special treatment for the
first copy of a7. */
push_to_sequence (entry_insns);
tmp = gen_reg_rtx (mode);
cfun->machine->incoming_a7_copied = true;
return 1;
switch (mode)
{
case DFmode:
case DImode:
emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 0),
gen_rtx_REG (SImode, A7_REG - 1)));
emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 4),
gen_raw_REG (SImode, A7_REG)));
break;
case SFmode:
emit_insn (gen_movsf_internal (tmp, gen_raw_REG (mode, A7_REG)));
break;
case SImode:
emit_insn (gen_movsi_internal (tmp, gen_raw_REG (mode, A7_REG)));
break;
case HImode:
emit_insn (gen_movhi_internal (tmp, gen_raw_REG (mode, A7_REG)));
break;
case QImode:
emit_insn (gen_movqi_internal (tmp, gen_raw_REG (mode, A7_REG)));
break;
default:
abort ();
}
return 0;
cfun->machine->set_frame_ptr_insn = emit_insn (gen_set_frame_ptr ());
entry_insns = get_insns ();
end_sequence ();
if (cfun->machine->vararg_a7)
{
/* This is called from within builtin_savereg, so we're already
inside a start_sequence that will be placed at the start of
the function. */
emit_insn (entry_insns);
}
else
{
/* Put entry_insns after the NOTE that starts the function. If
this is inside a start_sequence, make the outer-level insn
chain current, so the code is placed at the start of the
function. */
push_topmost_sequence ();
emit_insn_after (entry_insns, get_insns ());
pop_topmost_sequence ();
}
return tmp;
}
@ -1727,11 +1751,10 @@ xtensa_dbx_register_number (int regno)
/* Initialize CUMULATIVE_ARGS for a function. */
void
init_cumulative_args (CUMULATIVE_ARGS *cum,
tree fntype ATTRIBUTE_UNUSED,
rtx libname ATTRIBUTE_UNUSED)
init_cumulative_args (CUMULATIVE_ARGS *cum, int incoming)
{
cum->arg_words = 0;
cum->incoming = incoming;
}
@ -1768,7 +1791,6 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
int regbase, words, max;
int *arg_words;
int regno;
enum machine_mode result_mode;
arg_words = &cum->arg_words;
regbase = (incoming_p ? GP_ARG_FIRST : GP_OUTGOING_ARG_FIRST);
@ -1785,37 +1807,11 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
return (rtx)0;
regno = regbase + *arg_words;
result_mode = (mode == BLKmode ? TYPE_MODE (type) : mode);
/* We need to make sure that references to a7 are represented with
rtx that is not equal to hard_frame_pointer_rtx. For multi-word
modes for which we don't define move patterns, we can't control
the expansion unless we explicitly list the individual registers
in a PARALLEL. Likewise, a single-word BLKmode argument passed
in a7 must be wrapped in a PARALLEL to avoid code that takes the
register number and builds a new REG. This is extremely fragile
but seems to be the best solution for now. */
if (cum->incoming && regno <= A7_REG && regno + words > A7_REG)
cfun->machine->need_a7_copy = true;
if ((mode != DImode && mode != DFmode
&& regno < A7_REG
&& regno + words > A7_REG)
|| (mode == BLKmode && regno == A7_REG))
{
rtx result;
int n;
result = gen_rtx_PARALLEL (result_mode, rtvec_alloc (words));
for (n = 0; n < words; n++)
{
XVECEXP (result, 0, n) =
gen_rtx_EXPR_LIST (VOIDmode,
gen_raw_REG (SImode, regno + n),
GEN_INT (n * UNITS_PER_WORD));
}
return result;
}
return gen_raw_REG (result_mode, regno);
return gen_rtx_REG (mode, regno);
}
@ -2264,41 +2260,18 @@ xtensa_expand_prologue (void)
if (frame_pointer_needed)
{
rtx first, insn, set_frame_ptr_insn = 0;
push_topmost_sequence ();
first = get_insns ();
pop_topmost_sequence ();
/* Search all instructions, looking for the insn that sets up the
frame pointer. This search will fail if the function does not
have an incoming argument in $a7, but in that case, we can just
set up the frame pointer at the very beginning of the
function. */
for (insn = first; insn; insn = NEXT_INSN (insn))
if (cfun->machine->set_frame_ptr_insn)
{
rtx pat;
rtx first, insn;
if (!INSN_P (insn))
continue;
push_topmost_sequence ();
first = get_insns ();
pop_topmost_sequence ();
pat = PATTERN (insn);
if (GET_CODE (pat) == SET
&& GET_CODE (SET_SRC (pat)) == UNSPEC_VOLATILE
&& (XINT (SET_SRC (pat), 1) == UNSPECV_SET_FP))
{
set_frame_ptr_insn = insn;
break;
}
}
if (set_frame_ptr_insn)
{
/* For all instructions prior to set_frame_ptr_insn, replace
hard_frame_pointer references with stack_pointer. */
for (insn = first;
insn != set_frame_ptr_insn;
insn != cfun->machine->set_frame_ptr_insn;
insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
@ -2400,9 +2373,8 @@ xtensa_builtin_saveregs (void)
rtx gp_regs, dest;
int arg_words = current_function_arg_words;
int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
int i;
if (gp_left == 0)
if (gp_left <= 0)
return const0_rtx;
/* Allocate the general-purpose register space. */
@ -2414,16 +2386,9 @@ xtensa_builtin_saveregs (void)
dest = change_address (gp_regs, SImode,
plus_constant (XEXP (gp_regs, 0),
arg_words * UNITS_PER_WORD));
/* Note: Don't use move_block_from_reg() here because the incoming
argument in a7 cannot be represented by hard_frame_pointer_rtx.
Instead, call gen_raw_REG() directly so that we get a distinct
instance of (REG:SI 7). */
for (i = 0; i < gp_left; i++)
{
emit_move_insn (operand_subword (dest, i, 1, BLKmode),
gen_raw_REG (SImode, GP_ARG_FIRST + arg_words + i));
}
cfun->machine->need_a7_copy = true;
cfun->machine->vararg_a7 = true;
move_block_from_reg (GP_ARG_FIRST + arg_words, dest, gp_left);
return XEXP (gp_regs, 0);
}
@ -2749,55 +2714,6 @@ order_regs_for_local_alloc (void)
}
/* A customized version of reg_overlap_mentioned_p that only looks for
references to a7 (as opposed to hard_frame_pointer_rtx). */
int
a7_overlap_mentioned_p (rtx x)
{
int i, j;
unsigned int x_regno;
const char *fmt;
if (GET_CODE (x) == REG)
{
x_regno = REGNO (x);
return (x != hard_frame_pointer_rtx
&& x_regno < A7_REG + 1
&& x_regno + HARD_REGNO_NREGS (A7_REG, GET_MODE (x)) > A7_REG);
}
if (GET_CODE (x) == SUBREG
&& GET_CODE (SUBREG_REG (x)) == REG
&& REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER)
{
x_regno = subreg_regno (x);
return (SUBREG_REG (x) != hard_frame_pointer_rtx
&& x_regno < A7_REG + 1
&& x_regno + HARD_REGNO_NREGS (A7_REG, GET_MODE (x)) > A7_REG);
}
/* X does not match, so try its subexpressions. */
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (a7_overlap_mentioned_p (XEXP (x, i)))
return 1;
}
else if (fmt[i] == 'E')
{
for (j = XVECLEN (x, i) - 1; j >=0; j--)
if (a7_overlap_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
}
return 0;
}
/* Some Xtensa targets support multiple bss sections. If the section
name ends with ".bss", add SECTION_BSS to the flags. */

View File

@ -736,23 +736,21 @@ extern enum reg_class xtensa_char_to_class[256];
#define FUNCTION_ARG_REGNO_P(N) \
((N) >= GP_OUTGOING_ARG_FIRST && (N) <= GP_OUTGOING_ARG_LAST)
/* Define a data type for recording info about an argument list
during the scan of that argument list. This data type should
hold all necessary information about the function itself
and about the args processed so far, enough to enable macros
such as FUNCTION_ARG to determine where the next arg should go. */
typedef struct xtensa_args {
int arg_words; /* # total words the arguments take */
/* Record the number of argument words seen so far, along with a flag to
indicate whether these are incoming arguments. (FUNCTION_INCOMING_ARG
is used for both incoming and outgoing args, so a separate flag is
needed. */
typedef struct xtensa_args
{
int arg_words;
int incoming;
} CUMULATIVE_ARGS;
/* Initialize a variable CUM of type CUMULATIVE_ARGS
for a call to a function whose data type is FNTYPE.
For a library call, FNTYPE is 0. */
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
init_cumulative_args (&CUM, FNTYPE, LIBNAME)
init_cumulative_args (&CUM, 0)
#define INIT_CUMULATIVE_INCOMING_ARGS(CUM, FNTYPE, LIBNAME) \
init_cumulative_args (&CUM, FNTYPE, LIBNAME)
init_cumulative_args (&CUM, 1)
/* Update the data in CUM to advance over an argument
of mode MODE and data type TYPE.
@ -798,12 +796,6 @@ typedef struct xtensa_args {
/* Pass complex arguments independently. */
#define SPLIT_COMPLEX_ARGS 1
/* Because Xtensa's function_arg() wraps BLKmode arguments passed in
a7 inside a PARALLEL, BLOCK_REG_PADDING needs to be defined
to get emit_group_store to do the right thing. */
#define BLOCK_REG_PADDING(MODE, TYPE, FIRST) \
FUNCTION_ARG_PADDING (MODE, TYPE)
/* Profiling Xtensa code is typically done with the built-in profiling
feature of Tensilica's instruction set simulator, which does not
require any compiler support. Profiling code on a real (i.e.,
@ -1142,11 +1134,6 @@ typedef struct xtensa_args {
/* Prefer word-sized loads. */
#define SLOW_BYTE_ACCESS 1
/* ??? Xtensa doesn't have any instructions that set integer values
based on the results of comparisons, but the simplification code in
the combiner also uses STORE_FLAG_VALUE. The default value (1) is
fine for us, but (-1) might be better. */
/* Shift instructions ignore all but the low-order few bits. */
#define SHIFT_COUNT_TRUNCATED 1

View File

@ -803,15 +803,11 @@
if (CONSTANT_P (operands[1]) && !TARGET_CONST16)
operands[1] = force_const_mem (DImode, operands[1]);
if (!(reload_in_progress | reload_completed))
{
if (!register_operand (operands[0], DImode)
&& !register_operand (operands[1], DImode))
operands[1] = force_reg (DImode, operands[1]);
if (!register_operand (operands[0], DImode)
&& !register_operand (operands[1], DImode))
operands[1] = force_reg (DImode, operands[1]);
if (xtensa_copy_incoming_a7 (operands, DImode))
DONE;
}
operands[1] = xtensa_copy_incoming_a7 (operands[1]);
})
(define_insn_and_split "movdi_internal"
@ -934,18 +930,15 @@
if (!TARGET_CONST16 && CONSTANT_P (operands[1]))
operands[1] = force_const_mem (SFmode, operands[1]);
if (!(reload_in_progress | reload_completed))
{
if ((!register_operand (operands[0], SFmode)
&& !register_operand (operands[1], SFmode))
|| (FP_REG_P (xt_true_regnum (operands[0]))
&& (constantpool_mem_p (operands[1])
|| CONSTANT_P (operands[1]))))
operands[1] = force_reg (SFmode, operands[1]);
if ((!register_operand (operands[0], SFmode)
&& !register_operand (operands[1], SFmode))
|| (FP_REG_P (xt_true_regnum (operands[0]))
&& !(reload_in_progress | reload_completed)
&& (constantpool_mem_p (operands[1])
|| CONSTANT_P (operands[1]))))
operands[1] = force_reg (SFmode, operands[1]);
if (xtensa_copy_incoming_a7 (operands, SFmode))
DONE;
}
operands[1] = xtensa_copy_incoming_a7 (operands[1]);
})
(define_insn "movsf_internal"
@ -1015,15 +1008,11 @@
if (CONSTANT_P (operands[1]) && !TARGET_CONST16)
operands[1] = force_const_mem (DFmode, operands[1]);
if (!(reload_in_progress | reload_completed))
{
if (!register_operand (operands[0], DFmode)
&& !register_operand (operands[1], DFmode))
operands[1] = force_reg (DFmode, operands[1]);
if (!register_operand (operands[0], DFmode)
&& !register_operand (operands[1], DFmode))
operands[1] = force_reg (DFmode, operands[1]);
if (xtensa_copy_incoming_a7 (operands, DFmode))
DONE;
}
operands[1] = xtensa_copy_incoming_a7 (operands[1]);
})
(define_insn_and_split "movdf_internal"
@ -1081,7 +1070,16 @@
;; Shift instructions.
(define_insn "ashlsi3"
(define_expand "ashlsi3"
[(set (match_operand:SI 0 "register_operand" "")
(ashift:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "arith_operand" "")))]
""
{
operands[1] = xtensa_copy_incoming_a7 (operands[1]);
})
(define_insn "ashlsi3_internal"
[(set (match_operand:SI 0 "register_operand" "=a,a")
(ashift:SI (match_operand:SI 1 "register_operand" "r,r")
(match_operand:SI 2 "arith_operand" "J,r")))]