This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
i386 splits for mov patterns
- To: egcs-patches at cygnus dot com
- Subject: i386 splits for mov patterns
- From: Jan Hubicka <hubicka at atrey dot karlin dot mff dot cuni dot cz>
- Date: Wed, 7 Oct 1998 19:37:34 +0200
Hi,
here is another part of my patch. It replaces the output_move_double with
function emit_or_output_move, that is able to handle splits as well as emiting
asm. It produces quite a lot of independent insns that scheduler can use
to interlace other code, wich quite improves scheduling. Also because standard
patterns are used, xor reg,reg is used instead mov 0,reg and add instead of
lea when possible.
I've redone this function from scratch, because I wanted to make it
handle output_move_memory and output_move_double functionality, so it reduces
a bit size of i386.c. Only disadvantage I see is that output_move_memory and
output_move_pushmem was much more general, but their functionality was unused.
Also output_move_double appears in same form in other md files, and this
change makes some unsynchronizationin. At the other hand my new replacement
don't brother with various addresing modes i386 don't have so it is much shorter
and easier ot understand IMO. Also generates better code in some cases.
I might make modified versions of the old functions if you want, but it should
make some unsynchronization too.
This patch also contains splits for movsf,df,xf and di patterns as well
as their push version.
It don't seems to introduce any new regresions...
I've also succesfuly bootstraped with this patch (that include this one),
but this test is quite weak, since gcc don't use much of long long/floating
point code. But XaoS seems to work well.
Oct 7 08:58:58 1998 Jan Hubicka <hubicka@freesoft.cz>
* i386.c:
(split_reg): New function
(do_move): New function
(emit_or_output_move): New function
(output_move_double): Removed
(output_move_memory): Removed
(output_move_pushmem): Removed
* i386.h: Add/remove prototypes
* i386.md: Allow pushsi from memory after reload
(movsf_push): Use emit_or_output_move, preffer normal register for push,
handle push to memory when TARGET_PUSH_MEMORY, new split for floating
point reg case
(movdf_push): Likewise, use emit_or_output_move, new splits
for normal register and memory case
(movxf_push): Likewise, don't do push/read again in case REG_DEAD
note is not present
(movsf_push_memory): Removed
(movdf_push_memory): Removed
(movxf_push_memory): Removed
(movdf pattern): Use emit_or_output_move, new split
(movxf pattern): Likewise
(movdi pattern): Likewise
(di mode push): Do memory push when TARGET_PUSH_MEMORY, new split
*** gcc/config/i386/i386.h.orig Wed Oct 7 17:37:24 1998
--- gcc/config/i386/i386.h Wed Oct 7 17:39:56 1998
*************** extern char *output_strlen_unroll ();
*** 2685,2693 ****
extern void output_op_from_reg ();
extern void output_to_reg ();
extern char *singlemove_string ();
! extern char *output_move_double ();
! extern char *output_move_memory ();
! extern char *output_move_pushmem ();
extern int standard_80387_constant_p ();
extern char *output_move_const_single ();
extern int symbolic_operand ();
--- 2685,2691 ----
extern void output_op_from_reg ();
extern void output_to_reg ();
extern char *singlemove_string ();
! extern char *emit_or_output_move ();
extern int standard_80387_constant_p ();
extern char *output_move_const_single ();
extern int symbolic_operand ();
*** gcc/config/i386/i386.c.origos Mon Oct 5 12:21:58 1998
--- gcc/config/i386/i386.c Wed Oct 7 18:53:28 1998
*************** asm_add (n, x)
*** 1065,1593 ****
}
}
- /* Output assembler code to perform a doubleword move insn
- with operands OPERANDS. */
! char *
! output_move_double (operands)
! rtx *operands;
! {
! enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
! rtx latehalf[2];
! rtx middlehalf[2];
! rtx xops[2];
! rtx addreg0 = 0, addreg1 = 0;
! int dest_overlapped_low = 0;
! int size = GET_MODE_SIZE (GET_MODE (operands[0]));
!
! middlehalf[0] = 0;
! middlehalf[1] = 0;
!
! /* First classify both operands. */
!
! if (REG_P (operands[0]))
! optype0 = REGOP;
! else if (offsettable_memref_p (operands[0]))
! optype0 = OFFSOP;
! else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
! optype0 = POPOP;
! else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
! optype0 = PUSHOP;
! else if (GET_CODE (operands[0]) == MEM)
! optype0 = MEMOP;
! else
! optype0 = RNDOP;
!
! if (REG_P (operands[1]))
! optype1 = REGOP;
! else if (CONSTANT_P (operands[1]))
! optype1 = CNSTOP;
! else if (offsettable_memref_p (operands[1]))
! optype1 = OFFSOP;
! else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
! optype1 = POPOP;
! else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
! optype1 = PUSHOP;
! else if (GET_CODE (operands[1]) == MEM)
! optype1 = MEMOP;
! else
! optype1 = RNDOP;
! /* Check for the cases that the operand constraints are not
! supposed to allow to happen. Abort if we get one,
! because generating code for these cases is painful. */
! if (optype0 == RNDOP || optype1 == RNDOP)
abort ();
! /* If one operand is decrementing and one is incrementing
! decrement the former register explicitly
! and change that operand into ordinary indexing. */
!
! if (optype0 == PUSHOP && optype1 == POPOP)
! {
! /* ??? Can this ever happen on i386? */
! operands[0] = XEXP (XEXP (operands[0], 0), 0);
! asm_add (-size, operands[0]);
! if (GET_MODE (operands[1]) == XFmode)
! operands[0] = gen_rtx_MEM (XFmode, operands[0]);
! else if (GET_MODE (operands[0]) == DFmode)
! operands[0] = gen_rtx_MEM (DFmode, operands[0]);
! else
! operands[0] = gen_rtx_MEM (DImode, operands[0]);
! optype0 = OFFSOP;
}
!
! if (optype0 == POPOP && optype1 == PUSHOP)
{
! /* ??? Can this ever happen on i386? */
! operands[1] = XEXP (XEXP (operands[1], 0), 0);
! asm_add (-size, operands[1]);
! if (GET_MODE (operands[1]) == XFmode)
! operands[1] = gen_rtx_MEM (XFmode, operands[1]);
! else if (GET_MODE (operands[1]) == DFmode)
! operands[1] = gen_rtx_MEM (DFmode, operands[1]);
! else
! operands[1] = gen_rtx_MEM (DImode, operands[1]);
! optype1 = OFFSOP;
}
!
! /* If an operand is an unoffsettable memory ref, find a register
! we can increment temporarily to make it refer to the second word. */
!
! if (optype0 == MEMOP)
! addreg0 = find_addr_reg (XEXP (operands[0], 0));
!
! if (optype1 == MEMOP)
! addreg1 = find_addr_reg (XEXP (operands[1], 0));
!
! /* Ok, we can do one word at a time.
! Normally we do the low-numbered word first,
! but if either operand is autodecrementing then we
! do the high-numbered word first.
!
! In either case, set up in LATEHALF the operands to use
! for the high-numbered word and in some cases alter the
! operands in OPERANDS to be suitable for the low-numbered word. */
!
! if (size == 12)
{
! if (optype0 == REGOP)
! {
! middlehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
! latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2);
! }
! else if (optype0 == OFFSOP)
{
! middlehalf[0] = adj_offsettable_operand (operands[0], 4);
! latehalf[0] = adj_offsettable_operand (operands[0], 8);
}
else
{
! middlehalf[0] = operands[0];
! latehalf[0] = operands[0];
! }
!
! if (optype1 == REGOP)
! {
! middlehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
! latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2);
! }
! else if (optype1 == OFFSOP)
! {
! middlehalf[1] = adj_offsettable_operand (operands[1], 4);
! latehalf[1] = adj_offsettable_operand (operands[1], 8);
! }
! else if (optype1 == CNSTOP)
! {
! if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
! REAL_VALUE_TYPE r; long l[3];
!
! REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
! REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l);
! operands[1] = GEN_INT (l[0]);
! middlehalf[1] = GEN_INT (l[1]);
! latehalf[1] = GEN_INT (l[2]);
! }
! else if (CONSTANT_P (operands[1]))
! /* No non-CONST_DOUBLE constant should ever appear here. */
! abort ();
! }
! else
! {
! middlehalf[1] = operands[1];
! latehalf[1] = operands[1];
! }
! }
!
! else
! {
! /* Size is not 12. */
!
! if (optype0 == REGOP)
! latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
! else if (optype0 == OFFSOP)
! latehalf[0] = adj_offsettable_operand (operands[0], 4);
! else
! latehalf[0] = operands[0];
!
! if (optype1 == REGOP)
! latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
! else if (optype1 == OFFSOP)
! latehalf[1] = adj_offsettable_operand (operands[1], 4);
! else if (optype1 == CNSTOP)
! split_double (operands[1], &operands[1], &latehalf[1]);
! else
! latehalf[1] = operands[1];
! }
!
! /* If insn is effectively movd N (sp),-(sp) then we will do the
! high word first. We should use the adjusted operand 1
! (which is N+4 (sp) or N+8 (sp))
! for the low word and middle word as well,
! to compensate for the first decrement of sp. */
! if (optype0 == PUSHOP
! && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
! && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
! middlehalf[1] = operands[1] = latehalf[1];
!
! /* For (set (reg:DI N) (mem:DI ... (reg:SI N) ...)),
! if the upper part of reg N does not appear in the MEM, arrange to
! emit the move late-half first. Otherwise, compute the MEM address
! into the upper part of N and use that as a pointer to the memory
! operand. */
! if (optype0 == REGOP
! && (optype1 == OFFSOP || optype1 == MEMOP))
! {
! if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
! && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
! {
! /* If both halves of dest are used in the src memory address,
! compute the address into latehalf of dest. */
! compadr:
! xops[0] = latehalf[0];
! xops[1] = XEXP (operands[1], 0);
! output_asm_insn (AS2 (lea%L0,%a1,%0), xops);
! if (GET_MODE (operands[1]) == XFmode)
! {
! operands[1] = gen_rtx_MEM (XFmode, latehalf[0]);
! middlehalf[1] = adj_offsettable_operand (operands[1], size-8);
! latehalf[1] = adj_offsettable_operand (operands[1], size-4);
}
! else
{
! operands[1] = gen_rtx_MEM (DImode, latehalf[0]);
! latehalf[1] = adj_offsettable_operand (operands[1], size-4);
}
! }
!
! else if (size == 12
! && reg_mentioned_p (middlehalf[0], XEXP (operands[1], 0)))
! {
! /* Check for two regs used by both source and dest. */
! if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
! || reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
! goto compadr;
!
! /* JRV says this can't happen: */
! if (addreg0 || addreg1)
! abort ();
! /* Only the middle reg conflicts; simply put it last. */
! output_asm_insn (singlemove_string (operands), operands);
! output_asm_insn (singlemove_string (latehalf), latehalf);
! output_asm_insn (singlemove_string (middlehalf), middlehalf);
! return "";
}
-
- else if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)))
- /* If the low half of dest is mentioned in the source memory
- address, the arrange to emit the move late half first. */
- dest_overlapped_low = 1;
}
! /* If one or both operands autodecrementing,
! do the two words, high-numbered first. */
!
! /* Likewise, the first move would clobber the source of the second one,
! do them in the other order. This happens only for registers;
! such overlap can't happen in memory unless the user explicitly
! sets it up, and that is an undefined circumstance. */
!
! #if 0
! if (optype0 == PUSHOP || optype1 == PUSHOP
! || (optype0 == REGOP && optype1 == REGOP
! && REGNO (operands[0]) == REGNO (latehalf[1]))
! || dest_overlapped_low)
! #endif
!
! if (optype0 == PUSHOP || optype1 == PUSHOP
! || (optype0 == REGOP && optype1 == REGOP
! && ((middlehalf[1] && REGNO (operands[0]) == REGNO (middlehalf[1]))
! || REGNO (operands[0]) == REGNO (latehalf[1])))
! || dest_overlapped_low)
! {
! /* Make any unoffsettable addresses point at high-numbered word. */
! if (addreg0)
! asm_add (size-4, addreg0);
! if (addreg1)
! asm_add (size-4, addreg1);
!
! /* Do that word. */
! output_asm_insn (singlemove_string (latehalf), latehalf);
!
! /* Undo the adds we just did. */
! if (addreg0)
! asm_add (-4, addreg0);
! if (addreg1)
! asm_add (-4, addreg1);
!
! if (size == 12)
! {
! output_asm_insn (singlemove_string (middlehalf), middlehalf);
! if (addreg0)
! asm_add (-4, addreg0);
! if (addreg1)
! asm_add (-4, addreg1);
! }
! /* Do low-numbered word. */
! return singlemove_string (operands);
}
!
! /* Normal case: do the two words, low-numbered first. */
!
! output_asm_insn (singlemove_string (operands), operands);
!
! /* Do the middle one of the three words for long double */
! if (size == 12)
{
! if (addreg0)
! asm_add (4, addreg0);
! if (addreg1)
! asm_add (4, addreg1);
!
! output_asm_insn (singlemove_string (middlehalf), middlehalf);
}
-
- /* Make any unoffsettable addresses point at high-numbered word. */
- if (addreg0)
- asm_add (4, addreg0);
- if (addreg1)
- asm_add (4, addreg1);
-
- /* Do that word. */
- output_asm_insn (singlemove_string (latehalf), latehalf);
-
- /* Undo the adds we just did. */
- if (addreg0)
- asm_add (4-size, addreg0);
- if (addreg1)
- asm_add (4-size, addreg1);
-
- return "";
}
-
- #define MAX_TMPS 2 /* max temporary registers used */
! /* Output the appropriate code to move push memory on the stack */
char *
! output_move_pushmem (operands, insn, length, tmp_start, n_operands)
! rtx operands[];
! rtx insn;
! int length;
! int tmp_start;
! int n_operands;
{
! struct
! {
! char *load;
! char *push;
! rtx xops[2];
! } tmp_info[MAX_TMPS];
!
! rtx src = operands[1];
! int max_tmps = 0;
! int offset = 0;
! int stack_p = reg_overlap_mentioned_p (stack_pointer_rtx, src);
! int stack_offset = 0;
! int i, num_tmps;
! rtx xops[1];
- if (! offsettable_memref_p (src))
- fatal_insn ("Source is not offsettable", insn);
! if ((length & 3) != 0)
! fatal_insn ("Pushing non-word aligned size", insn);
! /* Figure out which temporary registers we have available */
! for (i = tmp_start; i < n_operands; i++)
{
! if (GET_CODE (operands[i]) == REG)
! {
! if (reg_overlap_mentioned_p (operands[i], src))
! continue;
!
! tmp_info[ max_tmps++ ].xops[1] = operands[i];
! if (max_tmps == MAX_TMPS)
! break;
! }
}
! if (max_tmps == 0)
! for (offset = length - 4; offset >= 0; offset -= 4)
! {
! xops[0] = adj_offsettable_operand (src, offset + stack_offset);
! output_asm_insn (AS1(push%L0,%0), xops);
! if (stack_p)
! stack_offset += 4;
! }
!
! else
! for (offset = length - 4; offset >= 0; )
! {
! for (num_tmps = 0; num_tmps < max_tmps && offset >= 0; num_tmps++)
! {
! tmp_info[num_tmps].load = AS2(mov%L0,%0,%1);
! tmp_info[num_tmps].push = AS1(push%L0,%1);
! tmp_info[num_tmps].xops[0]
! = adj_offsettable_operand (src, offset + stack_offset);
! offset -= 4;
! }
!
! for (i = 0; i < num_tmps; i++)
! output_asm_insn (tmp_info[i].load, tmp_info[i].xops);
!
! for (i = 0; i < num_tmps; i++)
! output_asm_insn (tmp_info[i].push, tmp_info[i].xops);
!
! if (stack_p)
! stack_offset += 4*num_tmps;
! }
!
! return "";
! }
!
! /* Output the appropriate code to move data between two memory locations */
!
! char *
! output_move_memory (operands, insn, length, tmp_start, n_operands)
! rtx operands[];
! rtx insn;
! int length;
! int tmp_start;
! int n_operands;
! {
! struct
! {
! char *load;
! char *store;
! rtx xops[3];
! } tmp_info[MAX_TMPS];
!
! rtx dest = operands[0];
! rtx src = operands[1];
! rtx qi_tmp = NULL_RTX;
! int max_tmps = 0;
! int offset = 0;
! int i, num_tmps;
! rtx xops[3];
!
! if (GET_CODE (dest) == MEM
! && GET_CODE (XEXP (dest, 0)) == PRE_INC
! && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx)
! return output_move_pushmem (operands, insn, length, tmp_start, n_operands);
!
! if (! offsettable_memref_p (src))
! fatal_insn ("Source is not offsettable", insn);
- if (! offsettable_memref_p (dest))
- fatal_insn ("Destination is not offsettable", insn);
! /* Figure out which temporary registers we have available */
! for (i = tmp_start; i < n_operands; i++)
{
! if (GET_CODE (operands[i]) == REG)
{
! if ((length & 1) != 0 && qi_tmp == 0 && QI_REG_P (operands[i]))
! qi_tmp = operands[i];
!
! if (reg_overlap_mentioned_p (operands[i], dest))
! fatal_insn ("Temporary register overlaps the destination", insn);
!
! if (reg_overlap_mentioned_p (operands[i], src))
! fatal_insn ("Temporary register overlaps the source", insn);
!
! tmp_info[max_tmps++].xops[2] = operands[i];
! if (max_tmps == MAX_TMPS)
! break;
}
}
! if (max_tmps == 0)
! fatal_insn ("No scratch registers were found to do memory->memory moves",
! insn);
! if ((length & 1) != 0)
{
! if (qi_tmp == 0)
! fatal_insn ("No byte register found when moving odd # of bytes.",
! insn);
}
! while (length > 1)
! {
! for (num_tmps = 0; num_tmps < max_tmps; num_tmps++)
! {
! if (length >= 4)
! {
! tmp_info[num_tmps].load = AS2(mov%L0,%1,%2);
! tmp_info[num_tmps].store = AS2(mov%L0,%2,%0);
! tmp_info[num_tmps].xops[0]
! = adj_offsettable_operand (dest, offset);
! tmp_info[num_tmps].xops[1]
! = adj_offsettable_operand (src, offset);
! offset += 4;
! length -= 4;
! }
! else if (length >= 2)
{
! tmp_info[num_tmps].load = AS2(mov%W0,%1,%2);
! tmp_info[num_tmps].store = AS2(mov%W0,%2,%0);
! tmp_info[num_tmps].xops[0]
! = adj_offsettable_operand (dest, offset);
! tmp_info[num_tmps].xops[1]
! = adj_offsettable_operand (src, offset);
!
! offset += 2;
! length -= 2;
}
! else
! break;
}
! for (i = 0; i < num_tmps; i++)
! output_asm_insn (tmp_info[i].load, tmp_info[i].xops);
! for (i = 0; i < num_tmps; i++)
! output_asm_insn (tmp_info[i].store, tmp_info[i].xops);
}
!
! if (length == 1)
{
! xops[0] = adj_offsettable_operand (dest, offset);
! xops[1] = adj_offsettable_operand (src, offset);
! xops[2] = qi_tmp;
! output_asm_insn (AS2(mov%B0,%1,%2), xops);
! output_asm_insn (AS2(mov%B0,%2,%0), xops);
}
-
return "";
}
int
standard_80387_constant_p (x)
--- 1065,1305 ----
}
}
! /* split operand to SI mode parts. Similar to split_di, but works for floating point
! parameters references too. Returns number of parts.
! For autoincrementing/decrementing it returns just pointers to first part and
! expect, that values will be saved in right order (reversed for autodecrementing).
! Maximally three parts are generated. */
! static int
! split_reg_to_parts (operand, parts, mode)
! rtx operand, *parts;
! enum machine_mode mode;
! {
! int size;
! size = GET_MODE_SIZE (mode) / 4;
! if (size < 2 || size > 3)
abort ();
! if (GET_CODE (operand) == MEM && !offsettable_memref_p (operand)
! && (GET_CODE (XEXP (operand, 0)) == POST_INC ||
! GET_CODE (XEXP (operand, 0)) == PRE_DEC))
! {
! PUT_MODE (operand, SImode);
! parts[0] = parts[1] = parts[2] = operand;
}
! else if (GET_CODE (operand) == MEM && !offsettable_memref_p (operand))
{
! abort (); /*All references at i386 ought to be offsetable*/
}
! else
{
! if (mode == DImode)
{
! split_di (&operand, 1, &parts[0], &parts[1]);
}
else
{
! if (REG_P (operand))
{
! if (!reload_completed)
! abort();
! parts[0] = gen_rtx_REG (SImode, REGNO (operand) + 0);
! parts[1] = gen_rtx_REG (SImode, REGNO (operand) + 1);
! if (size == 3)
! parts[2] = gen_rtx_REG (SImode, REGNO (operand) + 2);
}
! else if (offsettable_memref_p (operand))
{
! PUT_MODE (operand, SImode);
! parts[0] = operand;
! parts[1] = adj_offsettable_operand (operand, 4);
! parts[2] = adj_offsettable_operand (operand, 8);
}
! else if (CONSTANT_P (operand))
! {
! REAL_VALUE_TYPE r;
! long l[3];
! REAL_VALUE_FROM_CONST_DOUBLE (r, operand);
! switch (mode)
! {
! case XFmode:
! REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l);
! parts[2] = GEN_INT (l[2]);
! break;
! case DFmode:
! REAL_VALUE_TO_TARGET_DOUBLE (r, l);
! break;
! default:
! abort ();
! }
! parts[0] = GEN_INT (l[0]);
! parts[1] = GEN_INT (l[1]);
! }
! else
! abort ();
}
}
+ return size;
+ }
! /* Next emit_or_output_move should emit asm instructions as well as insns. This
! is helper function to handle this. */
! static void
! do_mov (operand1, operand2, emit)
! rtx operand1, operand2;
! int emit;
! {
! rtx xops[2];
! if (emit)
! {
! emit_move_insn (operand1, operand2);
}
! else
{
! xops[0] = operand1;
! xops[1] = operand2;
! output_asm_insn (singlemove_string (xops), xops);
}
}
! /* Emit insns or asm instructions to perform move of long long
! and floating point values. */
char *
! emit_or_output_move (operands1, emit)
! rtx *operands1;
! int emit;
{
! rtx part[2][3];
! rtx operands[2];
! int size = GET_MODE_SIZE (GET_MODE (operands1[0])) / 4;
! int push = 0;
! int colisions = 0;
! operands[0] = copy_rtx (operands1[0]);
! operands[1] = copy_rtx (operands1[1]);
! /* Handle retyped memory correctly */
! if (GET_CODE (operands[1]) == SUBREG
! && GET_CODE (XEXP (operands[1], 0)) == MEM)
{
! operands[1] = XEXP (operands[1], 0);
}
! if (size < 2 || size > 3)
! abort ();
! /* We handle push, but not pop or combined cases. */
! if (!REG_P (operands[0]) && !offsettable_memref_p (operands[0]))
{
! if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
! abort ();
! if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
{
! if (REGNO (XEXP (XEXP (operands[0], 0), 0)) != STACK_POINTER_REGNUM)
! abort ();
! push = 1;
}
}
+ if (!REG_P (operands[1]) && !offsettable_memref_p (operands[1])
+ && !CONSTANT_P (operands[1])
+ && (GET_CODE (XEXP (operands[1], 0)) == POST_INC
+ || GET_CODE (XEXP (operands[1], 0)) == PRE_DEC))
+ abort ();
! split_reg_to_parts (operands[0], part[0], GET_MODE (operands1[0]));
! split_reg_to_parts (operands[1], part[1], GET_MODE (operands1[0]));
! /* When emiting push, takce care for source operands from stack */
! if (push && GET_CODE (operands[1]) == MEM)
{
! if (!offsettable_memref_p (operands[1]))
! abort ();
! if (reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
! {
! if (size == 3)
! part[1][1] = part[1][2];
! part[1][0] = part[1][1];
! }
}
! /* We need to do copy in right order in case address register of source conflict with
! destination. Also push require reversed order of operands. */
! if (REG_P (part[0][0]) && GET_CODE (part[1][0]) == MEM)
! {
! colisions = (reg_overlap_mentioned_p (part[0][0], XEXP (part[1][0], 0)) != 0) +
! (reg_overlap_mentioned_p (part[0][1], XEXP (part[1][0], 0)) != 0) +
! (size == 3 && reg_overlap_mentioned_p (part[0][2], XEXP (part[1][0], 0)) != 0);
! /* Collision in the middle can be handled by reordering */
! /* ??? this code is untested! */
! if (colisions == 1 && size == 3 && reg_overlap_mentioned_p (part[0][1], XEXP (part[1][0], 0)))
! {
! rtx tmp;
! tmp = part[0][1];
! part[0][1] = part[0][2];
! part[0][2] = tmp;
! tmp = part[1][1];
! part[1][1] = part[1][2];
! part[1][2] = tmp;
! }
! else
! /*If there is more colisions we can't handle it
! by reorder. Do lea to lower part and use 1 coliding mode */
! if (colisions > 1)
! {
! colisions = 1;
! if (emit)
! emit_insn (gen_movsi_lea (part[0][size - 1], XEXP (part[1][0], 0)));
! else
{
! rtx xops[2];
! xops[0] = part[0][size - 1];
! xops[1] = XEXP (part[1][0], 0);
! output_asm_insn (AS2 (lea%L0,%a1,%0), xops);
}
! part[1][0] = gen_rtx_MEM (SImode, part[0][size - 1]);
! part[1][1] = adj_offsettable_operand (part[1][0], 4);
! part[1][2] = adj_offsettable_operand (part[1][0], 8);
}
+ }
! /* In case of push or collision in the first word of destination we will use
! reversed order */
! if (!push
! && !(REG_P (part[0][0]) && REG_P (part[1][1])
! && (REGNO (part[0][0]) == REGNO (part[1][1])
! || (size == 3 && REGNO (part[0][0]) == REGNO (part[1][2]))))
! && !(colisions && reg_overlap_mentioned_p (part[0][0], XEXP (part[1][0], 0))))
! {
! do_mov (part[0][0], part[1][0], emit);
! do_mov (part[0][1], part[1][1], emit);
! if (size == 3)
! {
! do_mov (part[0][2], part[1][2], emit);
! }
}
! else
{
! if (size == 3)
! {
! do_mov (part[0][2], part[1][2], emit);
! }
! do_mov (part[0][1], part[1][1], emit);
! do_mov (part[0][0], part[1][0], emit);
}
return "";
}
+
int
standard_80387_constant_p (x)
*************** standard_80387_constant_p (x)
*** 1600,1606 ****
if (setjmp (handler))
return 0;
-
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (d, x);
is0 = REAL_VALUES_EQUAL (d, dconst0) && !REAL_VALUE_MINUS_ZERO (d);
--- 1312,1317 ----
*************** x86_adjust_cost (insn, link, dep_insn, c
*** 5533,5539 ****
&& !reg_overlap_mentioned_p (SET_DEST (PATTERN (dep_insn)),
SET_SRC (PATTERN (insn))))
return 0; /* ??? */
-
switch (ix86_cpu)
{
--- 5244,5249 ----
*** gcc/config/i386/i386.md.orig Thu Oct 1 18:29:54 1998
--- gcc/config/i386/i386.md Wed Oct 7 17:53:04 1998
***************
*** 965,975 ****
[(set_attr "memory" "store")])
;; On a 386, it is faster to push MEM directly.
(define_insn ""
[(set (match_operand:SI 0 "push_operand" "=<")
(match_operand:SI 1 "memory_operand" "m"))]
! "TARGET_PUSH_MEMORY"
"* return AS1 (push%L0,%1);"
[(set_attr "type" "memory")
(set_attr "memory" "load")])
--- 965,976 ----
[(set_attr "memory" "store")])
;; On a 386, it is faster to push MEM directly.
+ ;; emit_or_output_move should rarely generate this insn too
(define_insn ""
[(set (match_operand:SI 0 "push_operand" "=<")
(match_operand:SI 1 "memory_operand" "m"))]
! "TARGET_PUSH_MEMORY || reload_completed"
"* return AS1 (push%L0,%1);"
[(set_attr "type" "memory")
(set_attr "memory" "load")])
***************
*** 1337,1345 ****
}")
(define_insn "movsf_push"
! [(set (match_operand:SF 0 "push_operand" "=<,<")
! (match_operand:SF 1 "general_operand" "*rfF,m"))]
! "GET_CODE (operands[1]) != MEM || reload_in_progress || reload_completed"
"*
{
if (STACK_REG_P (operands[1]))
--- 1338,1347 ----
}")
(define_insn "movsf_push"
! [(set (match_operand:SF 0 "push_operand" "=<,<,<")
! (match_operand:SF 1 "general_operand" "rF,*f,m"))]
! "!TARGET_PUSH_MEMORY || GET_CODE (operands[1]) != MEM
! || reload_in_progress || reload_completed"
"*
{
if (STACK_REG_P (operands[1]))
***************
*** 1366,1376 ****
return AS1 (push%L0,%1);
}")
! (define_insn "movsf_push_memory"
! [(set (match_operand:SF 0 "push_operand" "=<")
! (match_operand:SF 1 "memory_operand" "m"))]
! "TARGET_PUSH_MEMORY"
! "* return AS1 (push%L0,%1);")
(define_expand "movsf"
[(set (match_operand:SF 0 "general_operand" "")
--- 1368,1382 ----
return AS1 (push%L0,%1);
}")
! (define_split
! [(set (match_operand:SF 0 "push_operand" "")
! (match_operand:SF 1 "general_operand" ""))]
! "reload_completed && STACK_REG_P (operands[1])"
! [(set (reg:SI 7)
! (minus:SI (reg:SI 7) (const_int 4)))
! (set (match_dup 2)
! (match_dup 1))]
! "operands[2] = AT_SP(SFmode);")
(define_expand "movsf"
[(set (match_operand:SF 0 "general_operand" "")
***************
*** 1473,1481 ****
(define_insn "movdf_push"
! [(set (match_operand:DF 0 "push_operand" "=<,<")
! (match_operand:DF 1 "general_operand" "*rfF,o"))]
! "GET_CODE (operands[1]) != MEM || reload_in_progress || reload_completed"
"*
{
if (STACK_REG_P (operands[1]))
--- 1479,1487 ----
(define_insn "movdf_push"
! [(set (match_operand:DF 0 "push_operand" "=<,<,<")
! (match_operand:DF 1 "general_operand" "rF,*f,o"))]
! "TARGET_PUSH_MEMORY || GET_CODE (operands[1]) != MEM || reload_in_progress || reload_completed"
"*
{
if (STACK_REG_P (operands[1]))
***************
*** 1496,1512 ****
RET;
}
! if (which_alternative == 1)
! return output_move_pushmem (operands, insn, GET_MODE_SIZE (DFmode), 0, 0);
!
! return output_move_double (operands);
}")
! (define_insn "movdf_push_memory"
! [(set (match_operand:DF 0 "push_operand" "=<")
! (match_operand:DF 1 "memory_operand" "o"))]
! "TARGET_PUSH_MEMORY"
! "* return output_move_pushmem (operands, insn, GET_MODE_SIZE (DFmode),0,0);")
(define_expand "movdf"
[(set (match_operand:DF 0 "general_operand" "")
--- 1502,1526 ----
RET;
}
! return (emit_or_output_move (operands, 0));
}")
! (define_split
! [(set (match_operand:DF 0 "push_operand" "")
! (match_operand:DF 1 "register_operand" ""))]
! "reload_completed && STACK_REG_P (operands[1])"
! [(set (reg:SI 7)
! (minus:SI (reg:SI 7) (const_int 8)))
! (set (match_dup 2)
! (match_dup 1))]
! "operands[2] = AT_SP(DFmode);")
!
! (define_split
! [(set (match_operand:DF 0 "push_operand" "")
! (match_operand:DF 1 "general_operand" ""))]
! "reload_completed && !STACK_REG_P (operands[1])"
! [(const_int 0)]
! "emit_or_output_move (operands, 1); DONE;")
(define_expand "movdf"
[(set (match_operand:DF 0 "general_operand" "")
***************
*** 1536,1541 ****
--- 1550,1562 ----
}
}")
+ (define_split
+ [(set (match_operand:DF 0 "nonimmediate_operand" "")
+ (match_operand:DF 1 "general_operand" ""))]
+ "reload_completed && !STACK_REG_P (operands[0]) && !STACK_REG_P (operands[1])"
+ [(const_int 0)]
+ "emit_or_output_move (operands, 1); DONE;")
+
;; For the purposes of regclass, prefer FLOAT_REGS.
(define_insn ""
[(set (match_operand:DF 0 "nonimmediate_operand" "=f,fm,!*rf,!*rm")
***************
*** 1590,1601 ****
/* Handle all DFmode moves not involving the 387 */
! return output_move_double (operands);
}"
[(set_attr "type" "fld")])
-
(define_insn "swapdf"
[(set (match_operand:DF 0 "register_operand" "f")
(match_operand:DF 1 "register_operand" "f"))
--- 1611,1621 ----
/* Handle all DFmode moves not involving the 387 */
! return (emit_or_output_move (operands, 0));
}"
[(set_attr "type" "fld")])
(define_insn "swapdf"
[(set (match_operand:DF 0 "register_operand" "f")
(match_operand:DF 1 "register_operand" "f"))
***************
*** 1611,1619 ****
}")
(define_insn "movxf_push"
! [(set (match_operand:XF 0 "push_operand" "=<,<")
! (match_operand:XF 1 "general_operand" "*rfF,o"))]
! "GET_CODE (operands[1]) != MEM || reload_in_progress || reload_completed"
"*
{
if (STACK_REG_P (operands[1]))
--- 1631,1639 ----
}")
(define_insn "movxf_push"
! [(set (match_operand:XF 0 "push_operand" "=<,<,<")
! (match_operand:XF 1 "general_operand" "rF,*f,o"))]
! "TARGET_PUSH_MEMORY || GET_CODE (operands[1]) != MEM || reload_in_progress || reload_completed"
"*
{
if (STACK_REG_P (operands[1]))
***************
*** 1626,1649 ****
output_asm_insn (AS2 (sub%L2,%1,%2), xops);
output_asm_insn (AS1 (fstp%T0,%0), xops);
if (! find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
output_asm_insn (AS1 (fld%T0,%0), xops);
RET;
}
! if (which_alternative == 1)
! return output_move_pushmem (operands, insn, GET_MODE_SIZE (XFmode), 0, 0);
!
! return output_move_double (operands);
}")
! (define_insn "movxf_push_memory"
! [(set (match_operand:XF 0 "push_operand" "=<")
! (match_operand:XF 1 "memory_operand" "o"))]
! "TARGET_PUSH_MEMORY"
! "* return output_move_pushmem (operands, insn, GET_MODE_SIZE (XFmode),0,0);")
(define_expand "movxf"
[(set (match_operand:XF 0 "general_operand" "")
--- 1646,1687 ----
output_asm_insn (AS2 (sub%L2,%1,%2), xops);
+ /* ??? Old code used to do this:
+
output_asm_insn (AS1 (fstp%T0,%0), xops);
if (! find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
output_asm_insn (AS1 (fld%T0,%0), xops);
+
+ * I don't see any purpose for this slowdown, so I changed it to
+ same code as in movdf_push and movsf_push case:*/
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp%T0,%0), xops);
+ else
+ output_asm_insn (AS1 (fst%T0,%0), xops);
RET;
}
! return (emit_or_output_move (operands, 0));
}")
! (define_split
! [(set (match_operand:XF 0 "push_operand" "")
! (match_operand:XF 1 "register_operand" ""))]
! "reload_completed && STACK_REG_P (operands[1])"
! [(set (reg:SI 7)
! (minus:SI (reg:SI 7) (const_int 12)))
! (set (match_dup 2)
! (match_dup 1))]
! "operands[2] = AT_SP(XFmode);")
!
! (define_split
! [(set (match_operand:XF 0 "push_operand" "")
! (match_operand:XF 1 "general_operand" ""))]
! "reload_completed && !STACK_REG_P (operands[1])"
! [(const_int 0)]
! "emit_or_output_move (operands, 1); DONE;")
(define_expand "movxf"
[(set (match_operand:XF 0 "general_operand" "")
***************
*** 1673,1678 ****
--- 1711,1722 ----
}
}")
+ (define_split
+ [(set (match_operand:XF 0 "nonimmediate_operand" "")
+ (match_operand:XF 1 "general_operand" ""))]
+ "reload_completed && !STACK_REG_P (operands[0]) && !STACK_REG_P (operands[1])"
+ [(const_int 0)]
+ "emit_or_output_move (operands, 1); DONE;")
(define_insn ""
[(set (match_operand:XF 0 "nonimmediate_operand" "=f,fm,!*rf,!*rm")
***************
*** 1728,1735 ****
/* Handle all XFmode moves not involving the 387 */
! return output_move_double (operands);
! }")
(define_insn "swapxf"
[(set (match_operand:XF 0 "register_operand" "f")
--- 1772,1780 ----
/* Handle all XFmode moves not involving the 387 */
! return (emit_or_output_move (operands, 0));
! }"
! [(set_attr "type" "fld")])
(define_insn "swapxf"
[(set (match_operand:XF 0 "register_operand" "f")
***************
*** 1748,1761 ****
(define_insn ""
[(set (match_operand:DI 0 "push_operand" "=<")
(match_operand:DI 1 "general_operand" "riF"))]
! ""
! "* return output_move_double (operands);")
(define_insn ""
[(set (match_operand:DI 0 "push_operand" "=<")
! (match_operand:DI 1 "memory_operand" "o"))]
"TARGET_PUSH_MEMORY"
! "* return output_move_pushmem (operands, insn, GET_MODE_SIZE (DImode),0,0);")
(define_expand "movdi"
[(set (match_operand:DI 0 "general_operand" "")
--- 1793,1813 ----
(define_insn ""
[(set (match_operand:DI 0 "push_operand" "=<")
(match_operand:DI 1 "general_operand" "riF"))]
! "!TARGET_PUSH_MEMORY"
! "#")
(define_insn ""
[(set (match_operand:DI 0 "push_operand" "=<")
! (match_operand:DI 1 "general_operand" "riFo"))]
"TARGET_PUSH_MEMORY"
! "#")
!
! (define_split
! [(set (match_operand:DI 0 "push_operand" "")
! (match_operand:DI 1 "general_operand" ""))]
! "reload_completed"
! [(const_int 0)]
! "emit_or_output_move (operands, 1); DONE;")
(define_expand "movdi"
[(set (match_operand:DI 0 "general_operand" "")
***************
*** 1778,1786 ****
(match_operand:DI 1 "general_operand" "riF,m"))]
"(!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
|| (GET_CODE (operands[1]) != MEM)"
! "* return output_move_double (operands);"
[(set_attr "type" "integer,memory")
(set_attr "memory" "*,load")])
;;- conversion instructions
--- 1830,1845 ----
(match_operand:DI 1 "general_operand" "riF,m"))]
"(!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
|| (GET_CODE (operands[1]) != MEM)"
! "#"
[(set_attr "type" "integer,memory")
(set_attr "memory" "*,load")])
+
+ (define_split
+ [(set (match_operand:DI 0 "general_operand" "")
+ (match_operand:DI 1 "general_operand" ""))]
+ "reload_completed"
+ [(const_int 0)]
+ "emit_or_output_move (operands, 1); DONE;")
;;- conversion instructions