aboutsummaryrefslogtreecommitdiff
path: root/gcc-1.40/config/out-m88k.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-1.40/config/out-m88k.c')
-rw-r--r--gcc-1.40/config/out-m88k.c605
1 files changed, 605 insertions, 0 deletions
diff --git a/gcc-1.40/config/out-m88k.c b/gcc-1.40/config/out-m88k.c
new file mode 100644
index 0000000..d02ac86
--- /dev/null
+++ b/gcc-1.40/config/out-m88k.c
@@ -0,0 +1,605 @@
+/* Subroutines for insn-output.c for Motorola 88000.
+ Copyright (C) 1987 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@mcc.com)
+
+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)
+any later version.
+
+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. */
+
+#ifndef FILE
+#include <stdio.h>
+#endif
+
+/* This is where the condition code register lives. */
+rtx cc0_reg_rtx;
+
+static rtx find_addr_reg ();
+
+#if 0
+char *
+output_compare (operands, opcode, exchange_opcode)
+ rtx *operands;
+ char *opcode;
+ char *exchange_opcode;
+{
+ static char buf[40];
+ rtx op1, op2;
+
+ if (GET_CODE (cc_prev_status.value2) == COMPARE)
+ {
+ op1 = XEXP (cc_prev_status.value2, 0);
+ op2 = XEXP (cc_prev_status.value2, 1);
+ }
+ else
+ {
+ op1 = cc_prev_status.value2;
+ op2 = const0_rtx;
+ }
+ if (GET_CODE (op1) == CONST_INT)
+ {
+ operands[2] = op1;
+ operands[1] = op2;
+ opcode = exchange_opcode;
+ }
+ else
+ {
+ operands[1] = op1;
+ operands[2] = op2;
+ }
+ sprintf (buf, "cmp r25,%%1,%%2\n\tbcnd %s,r25,%%l0", opcode);
+ return buf;
+}
+
+char *
+output_fcompare (operands, opcode, exchange_opcode)
+ rtx *operands;
+ char *opcode;
+ char *exchange_opcode;
+{
+ static char buf[40];
+
+ rtx op1, op2;
+
+ if (GET_CODE (cc_prev_status.value2) == COMPARE)
+ {
+ op1 = XEXP (cc_prev_status.value2, 0);
+ op2 = XEXP (cc_prev_status.value2, 1);
+ }
+ else
+ {
+ op1 = cc_prev_status.value2;
+ op2 = const0_rtx;
+ }
+ if (GET_CODE (op1) == CONST_DOUBLE)
+ {
+ operands[2] = op1;
+ operands[1] = op2;
+ opcode = exchange_opcode;
+ }
+ else
+ {
+ operands[1] = op1;
+ operands[2] = op2;
+ }
+ sprintf (buf, "cmp r25,%%1,%%2\n\tbcnd %s,r25,%%l0", opcode);
+ return buf;
+}
+
+char *
+output_store (operands, opcode, exchange_opcode)
+ rtx *operands;
+ char *opcode;
+ char *exchange_opcode;
+{
+ static char buf[40];
+ rtx op1, op2;
+
+ if (GET_CODE (cc_prev_status.value2) == COMPARE)
+ {
+ op1 = XEXP (cc_prev_status.value2, 0);
+ op2 = XEXP (cc_prev_status.value2, 1);
+ }
+ else
+ {
+ op1 = cc_prev_status.value2;
+ op2 = const0_rtx;
+ }
+
+ if (GET_CODE (op1) == CONST_INT)
+ {
+ operands[2] = op1;
+ operands[1] = op2;
+ opcode = exchange_opcode;
+ }
+ else
+ {
+ operands[1] = op1;
+ operands[2] = op2;
+ }
+
+ sprintf (buf, "cmp r25,%%1,%%2\n\textu %%0,r25,1<%s>", opcode);
+ return buf;
+}
+#endif
+
+/* Nonzero if OP is a valid second operand for an arithmetic insn. */
+
+int
+arith_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (register_operand (op, mode)
+ || (GET_CODE (op) == CONST_INT
+ && (unsigned) INTVAL (op) < 0x10000));
+}
+
+int
+arith32_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (register_operand (op, mode) || GET_CODE (op) == CONST_INT);
+}
+
+int
+int5_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 0x20);
+}
+
+/* Return the best assembler insn template
+ for moving operands[1] into operands[0] as a fullword. */
+
+static char *
+singlemove_string (operands)
+ rtx *operands;
+{
+ if (GET_CODE (operands[0]) == MEM)
+ return "st %r1,%0";
+ if (GET_CODE (operands[1]) == MEM)
+ return "ld %0,%1";
+ return "or %0,r0,%1";
+}
+
+/* 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 addreg0 = 0, addreg1 = 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 (operands[0]) == MEM)
+ optype0 = MEMOP;
+ else
+ optype0 = RNDOP;
+
+ if (REG_P (operands[1]))
+ optype1 = REGOP;
+ else if (CONSTANT_P (operands[1])
+ || GET_CODE (operands[1]) == CONST_DOUBLE)
+ optype1 = CNSTOP;
+ else if (offsettable_memref_p (operands[1]))
+ optype1 = OFFSOP;
+ 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 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 (operands[0]);
+
+ if (optype1 == MEMOP)
+ addreg1 = find_addr_reg (operands[1]);
+
+ /* 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 (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)
+ {
+ 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]));
+ }
+ }
+ else
+ latehalf[1] = operands[1];
+
+ /* If 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 == REGOP && optype1 == REGOP
+ && REGNO (operands[0]) == REGNO (latehalf[1]))
+ {
+ /* Make any unoffsettable addresses point at high-numbered word. */
+ if (addreg0)
+ output_asm_insn ("addu %0,%0,4", &addreg0);
+ if (addreg1)
+ output_asm_insn ("addu %0,%0,4", &addreg1);
+
+ /* Do that word. */
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+
+ /* Undo the adds we just did. */
+ if (addreg0)
+ output_asm_insn ("subu %0,%0,4", &addreg0);
+ if (addreg1)
+ output_asm_insn ("subu %0,%0,4", &addreg0);
+
+ /* 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. */
+ if (addreg0)
+ output_asm_insn ("addu %0,%0,4", &addreg0);
+ if (addreg1)
+ output_asm_insn ("addu %0,%0,4", &addreg1);
+
+ /* Do that word. */
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+
+ /* Undo the adds we just did. */
+ if (addreg0)
+ output_asm_insn ("subu %0,%0,4", &addreg0);
+ if (addreg1)
+ output_asm_insn ("subu %0,%0,4", &addreg1);
+
+ return "";
+}
+
+/* Return a REG that occurs in ADDR with coefficient 1.
+ ADDR can be effectively incremented by incrementing REG. */
+
+static rtx
+find_addr_reg (addr)
+ rtx addr;
+{
+ while (GET_CODE (addr) == PLUS)
+ {
+ if (GET_CODE (XEXP (addr, 0)) == REG)
+ addr = XEXP (addr, 0);
+ if (GET_CODE (XEXP (addr, 1)) == REG)
+ addr = XEXP (addr, 1);
+ if (CONSTANT_P (XEXP (addr, 0)))
+ addr = XEXP (addr, 1);
+ if (CONSTANT_P (XEXP (addr, 1)))
+ addr = XEXP (addr, 0);
+ }
+ if (GET_CODE (addr) == REG)
+ return addr;
+ return 0;
+}
+
+/* Output an ascii string. */
+output_ascii (file, p, size)
+ FILE *file;
+ char *p;
+ int size;
+{
+ int i;
+
+ fprintf (file, "\tstring \"");
+
+ for (i = 0; i < size; i++)
+ {
+ register int c = p[i];
+ if (c == '\"' || c == '\\')
+ putc ('\\', file);
+ if (c >= ' ' && c < 0177)
+ putc (c, file);
+ else
+ {
+ fprintf (file, "\\%03o", c);
+ /* After an octal-escape, if a digit follows,
+ terminate one string constant and start another.
+ The Vax assembler fails to stop reading the escape
+ after three digits, so this is the only way we
+ can get it to parse the data properly. */
+ if (i < size - 1 && p[i + 1] >= '0' && p[i + 1] <= '9')
+ fprintf (file, "\"\n\tstring \"");
+ }
+ }
+ fprintf (file, "\"\n");
+}
+
+void
+output_load_address (operands)
+ rtx *operands;
+{
+ rtx base, offset;
+
+ if (CONSTANT_P (operands[3]))
+ {
+ output_asm_insn ("lda %0,%3", operands);
+ return;
+ }
+
+ if (REG_P (operands[3]))
+ {
+ if (REGNO (operands[0]) != REGNO (operands[3]))
+ output_asm_insn ("or %0,r0,%3", operands);
+ return;
+ }
+
+ base = XEXP (operands[3], 0);
+ offset = XEXP (operands[3], 1);
+
+ if (GET_CODE (base) == CONST_INT)
+ {
+ rtx tmp = base;
+ base = offset;
+ offset = tmp;
+ }
+
+ if (GET_CODE (offset) != CONST_INT)
+ abort ();
+
+ operands[6] = base;
+ operands[7] = offset;
+
+ if (REG_P (base))
+ if (FITS_16_BITS (offset))
+ output_asm_insn ("addu %0,%6,%7", operands);
+ else if (INT_FITS_16_BITS (- INTVAL (offset)))
+ output_asm_insn ("subu %0,%6,%7", operands);
+ else
+ output_asm_insn ("or.h %0,r0,hi16(%7)\n\tor %0,%0,lo16(%7)\n\tadd %0,%6,%0", operands);
+ else
+ {
+ if (GET_CODE (base) == MULT)
+ if (GET_MODE (base) == QImode)
+ output_asm_insn ("lda.b %0,%6");
+ else if (GET_MODE (base) == HImode)
+ output_asm_insn ("lda.h %0,%6");
+ else if (GET_MODE (base) == SImode)
+ output_asm_insn ("lda %0,%6");
+ else
+ output_asm_insn ("lda.d %0,%6");
+ else
+ output_asm_insn ("lda %0,%6");
+
+ if (FITS_16_BITS (offset))
+ output_asm_insn ("addu %0,%7,%0", operands);
+ else if (INT_FITS_16_BITS (- INTVAL (offset)))
+ output_asm_insn ("subu %0,%7,%0", operands);
+ else
+ output_asm_insn ("or.h r25,r0,hi16(%7)\n\tor r25,r0,lo16(%7)\n\taddu %0,%0r25", operands);
+ }
+}
+
+char *
+output_block_move (operands)
+ rtx *operands;
+{
+ static int movstrsi_label = 0;
+ int align = 4;
+
+ rtx xoperands[9];
+ int available[3];
+ int i, j;
+
+ /* Since we clobber untold things, nix the condition codes. */
+ CC_STATUS_INIT;
+
+ /* Get past the MEMs. */
+ operands[0] = XEXP (operands[0], 0);
+ operands[1] = XEXP (operands[1], 0);
+
+ xoperands[0] = 0;
+ xoperands[1] = 0;
+ xoperands[2] = 0;
+
+ available[0] = 1;
+ available[1] = 1;
+ available[2] = 1;
+#if 1
+ /* Prepare to juggle registers if necessary. */
+ if (REG_P (operands[0]) && (unsigned) (REGNO (operands[0]) - 10) < 3)
+ {
+ xoperands[0] = operands[0];
+ available[REGNO (operands[0]) - 10] = 0;
+ }
+ if (REG_P (operands[1]) && (unsigned) (REGNO (operands[1]) - 10) < 3)
+ {
+ xoperands[1] = operands[1];
+ available[REGNO (operands[1]) - 10] = 0;
+ }
+ if (REG_P (operands[2]) && (unsigned) (REGNO (operands[2]) - 10) < 3)
+ {
+ xoperands[2] = operands[2];
+ available[REGNO (operands[2]) - 10] = 0;
+ }
+ for (i = 0; i < 3; i++)
+ {
+ if (xoperands[i])
+ continue;
+ if (available[0])
+ {
+ xoperands[i] = gen_rtx (REG, SImode, 10);
+ available[0] = 0;
+ continue;
+ }
+ if (available[1])
+ {
+ xoperands[i] = gen_rtx (REG, SImode, 11);
+ available[1] = 0;
+ continue;
+ }
+ xoperands[i] = gen_rtx (REG, SImode, 12);
+ available[2] = 0;
+ }
+#endif
+
+ /* First, figure out best alignment we may assume. */
+ if (REG_P (operands[2]))
+ {
+ xoperands[5] = operands[2];
+ output_asm_insn ("sub %5,%2,1", xoperands);
+ align = 1;
+ }
+ else
+ {
+ int i = INTVAL (operands[2]);
+
+ if (i & 1)
+ align = 1;
+ else if (i & 3)
+ {
+ align = 2;
+ i >>= 1;
+ }
+ else
+ i >>= 2;
+
+ /* predecrement count. */
+ i -= 1;
+ if (i < 0) abort ();
+
+ xoperands[5] = gen_rtx (CONST_INT, VOIDmode, i);
+
+ if (INT_FITS_16_BITS (i))
+ output_asm_insn ("addu %2,r0,%5", xoperands);
+ else if (INT_FITS_16_BITS (-i))
+ {
+ xoperands[5] = gen_rtx (CONST_INT, VOIDmode, -i);
+ output_asm_insn ("subu %2,r0,%5", xoperands);
+ }
+ else
+ output_asm_insn ("or.u %2,r0,hi16(%5)\n\tor %2,%2,lo16(%5)", xoperands);
+ }
+ /* Now, set up for pipelined operation: dest must contain
+ a pre-incremented address, because its index is pre-decremented. */
+
+ xoperands[3] = plus_constant (operands[0], align);
+ output_load_address (xoperands);
+
+ xoperands[4] = operands[1];
+ output_load_address (xoperands+1);
+
+ xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++);
+
+ if (align == 4)
+ output_asm_insn ("\n@Lm%3:\n\tld r25,%1[%2]\n\tsubu %2,%2,1\n\tbcnd.n ge0,%2,@Lm%3\n\tst r25,%0[%2]", xoperands);
+ else if (align == 2)
+ output_asm_insn ("\n@Lm%3:\n\tld.h r25,%1[%2]\n\tsubu %2,%2,1\n\tbcnd.n ge0,%2,@Lm%3\n\tst.h r25,%0[%2]", xoperands);
+ else
+ output_asm_insn ("\n@Lm%3:\n\tld.b r25,%1[%2]\n\tsubu %2,%2,1\n\tbcnd.n ge0,%2,@Lm%3\n\tst.b r25,%0[%2]", xoperands);
+ return "";
+}
+
+char *
+output_store_const_int (mode, operands)
+ enum machine_mode mode;
+ rtx *operands;
+{
+ int i = INTVAL (operands[1]);
+ if (INT_FITS_16_BITS (i))
+ return "addu %0,r0,%1";
+ if (INT_FITS_16_BITS (-i))
+ {
+ operands[1] = gen_rtx (CONST_INT, VOIDmode, -i);
+ return "subu %0,r0,%1";
+ }
+ if ((i & 0xffff) == 0)
+ return "or.u %0,r0,hi16(%1)";
+ /* Could check to see if number is a contiguous field
+ of 1's. Then we could use the SET instruction. */
+ if (mode == HImode)
+ {
+ warning ("truncating constant `%d' to fit in half-word", INTVAL (operands[1]));
+ return "or %0,r0,lo16(%1)";
+ }
+ if (mode == QImode)
+ {
+ warning ("truncating constant `%d' to fit in byte");
+ operands[1] = gen_rtx (CONST_INT, VOIDmode, i & 0xff);
+ return "or %0,r0,%1";
+ }
+
+ return "or.u %0,r0,hi16(%1)\n\tor %0,%0,lo16(%1)";
+}
+
+/* This routine assumes that floating point numbers are represented
+ in a manner which is consistent between host and target machines. */
+char *
+output_store_const_float (mode, operands)
+ enum machine_mode mode;
+ rtx *operands;
+{
+ int i = INTVAL (operands[1]);
+ if (INT_FITS_16_BITS (i))
+ return "addu %0,r0,%1";
+ if (INT_FITS_16_BITS (-i))
+ {
+ operands[1] = gen_rtx (CONST_INT, VOIDmode, -i);
+ return "subu %0,r0,%1";
+ }
+ if ((i & 0xffff) == 0)
+ return "or.u %0,r0,hi16(%1)";
+ /* Could check to see if number is a contiguous field
+ of 1's. Then we could use the SET instruction. */
+ return "or.u %0,r0,hi16(%1)\n\tor %0,%0,lo16(%1)";
+}