/* Subroutines for insn-output.c for Alliant FX computers.
Copyright (C) 1989 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC 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 1, or (at your option)
GNU CC 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 GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Some output-actions in alliant.md need these. */
extern FILE *asm_out_file
;
/* Index into this array by (register number >> 3) to find the
smallest class which contains that register. */
enum reg_class regno_reg_class
[]
= { DATA_REGS
, ADDR_REGS
, FP_REGS
};
static rtx
find_addr_reg ();
output_btst (operands
, countop
, dataop
, insn
, signpos
)
if (GET_CODE (countop
) == CONST_INT
)
register int count
= INTVAL (countop
);
/* If COUNT is bigger than size of storage unit in use,
advance to the containing unit of same size. */
int offset
= (count
& ~signpos
) / 8;
operands
[1] = dataop
= adj_offsettable_operand (dataop
, offset
);
cc_status
.flags
= CC_NOT_POSITIVE
| CC_Z_IN_NOT_N
;
cc_status
.flags
= CC_NOT_NEGATIVE
| CC_Z_IN_NOT_N
;
&& next_insns_test_no_inequality (insn
))
&& next_insns_test_no_inequality (insn
))
&& next_insns_test_no_inequality (insn
))
cc_status
.flags
= CC_NOT_NEGATIVE
;
/* Return the best assembler insn template
for moving operands[1] into operands[0] as a fullword. */
singlemove_string (operands
)
if (operands
[1] != const0_rtx
)
if (! ADDRESS_REG_P (operands
[0]))
/* Output assembler code to perform a doubleword move insn
with operands OPERANDS. */
output_move_double (operands
)
enum { REGOP
, OFFSOP
, MEMOP
, PUSHOP
, POPOP
, CNSTOP
, RNDOP
} optype0
, optype1
;
rtx addreg0
= 0, addreg1
= 0;
/* First classify both operands. */
else if (offsettable_memref_p (operands
[0]))
else if (GET_CODE (XEXP (operands
[0], 0)) == POST_INC
)
else if (GET_CODE (XEXP (operands
[0], 0)) == PRE_DEC
)
else if (GET_CODE (operands
[0]) == MEM
)
else if (CONSTANT_P (operands
[1])
|| GET_CODE (operands
[1]) == CONST_DOUBLE
)
else if (offsettable_memref_p (operands
[1]))
else if (GET_CODE (XEXP (operands
[1], 0)) == POST_INC
)
else if (GET_CODE (XEXP (operands
[1], 0)) == PRE_DEC
)
else if (GET_CODE (operands
[1]) == MEM
)
/* 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
)
/* 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
)
operands
[0] = XEXP (XEXP (operands
[0], 0), 0);
output_asm_insn ("subq%.l %#8,%0", operands
);
operands
[0] = gen_rtx (MEM
, DImode
, operands
[0]);
if (optype0
== POPOP
&& optype1
== PUSHOP
)
operands
[1] = XEXP (XEXP (operands
[1], 0), 0);
output_asm_insn ("subq%.l %#8,%1", operands
);
operands
[1] = gen_rtx (MEM
, DImode
, operands
[1]);
/* If an operand is an unoffsettable memory ref, find a register
we can increment temporarily to make it refer to the second word. */
addreg0
= find_addr_reg (XEXP (operands
[0], 0));
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. */
latehalf
[0] = gen_rtx (REG
, SImode
, REGNO (operands
[0]) + 1);
else if (optype0
== OFFSOP
)
latehalf
[0] = adj_offsettable_operand (operands
[0], 4);
latehalf
[0] = operands
[0];
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
)
if (CONSTANT_P (operands
[1]))
latehalf
[1] = const0_rtx
;
else if (GET_CODE (operands
[1]) == CONST_DOUBLE
)
latehalf
[1] = gen_rtx (CONST_INT
, VOIDmode
,
CONST_DOUBLE_HIGH (operands
[1]));
operands
[1] = gen_rtx (CONST_INT
, VOIDmode
,
CONST_DOUBLE_LOW (operands
[1]));
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))
for the low word as well, to compensate for the first decrement of sp. */
&& REGNO (XEXP (XEXP (operands
[0], 0), 0)) == STACK_POINTER_REGNUM
&& reg_overlap_mentioned_p (stack_pointer_rtx
, operands
[1]))
operands
[1] = latehalf
[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 (optype0
== PUSHOP
|| optype1
== PUSHOP
|| (optype0
== REGOP
&& optype1
== REGOP
&& REGNO (operands
[0]) == REGNO (latehalf
[1])))
/* Make any unoffsettable addresses point at high-numbered word. */
output_asm_insn ("addql %#4,%0", &addreg0
);
output_asm_insn ("addql %#4,%0", &addreg1
);
output_asm_insn (singlemove_string (latehalf
), latehalf
);
/* Undo the adds we just did. */
output_asm_insn ("subql %#4,%0", &addreg0
);
output_asm_insn ("subql %#4,%0", &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
);
/* Make any unoffsettable addresses point at high-numbered word. */
output_asm_insn ("addql %#4,%0", &addreg0
);
output_asm_insn ("addql %#4,%0", &addreg1
);
output_asm_insn (singlemove_string (latehalf
), latehalf
);
/* Undo the adds we just did. */
output_asm_insn ("subql %#4,%0", &addreg0
);
output_asm_insn ("subql %#4,%0", &addreg1
);
/* Return a REG that occurs in ADDR with coefficient 1.
ADDR can be effectively incremented by incrementing REG. */
while (GET_CODE (addr
) == PLUS
)
if (GET_CODE (XEXP (addr
, 0)) == REG
)
else if (GET_CODE (XEXP (addr
, 1)) == REG
)
else if (CONSTANT_P (XEXP (addr
, 0)))
else if (CONSTANT_P (XEXP (addr
, 1)))
if (GET_CODE (addr
) == REG
)
standard_SunFPA_constant_p (x
)