aboutsummaryrefslogtreecommitdiff
path: root/gcc-1.40/config/out-tahoe.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-1.40/config/out-tahoe.c')
-rw-r--r--gcc-1.40/config/out-tahoe.c550
1 files changed, 550 insertions, 0 deletions
diff --git a/gcc-1.40/config/out-tahoe.c b/gcc-1.40/config/out-tahoe.c
new file mode 100644
index 0000000..47f9591
--- /dev/null
+++ b/gcc-1.40/config/out-tahoe.c
@@ -0,0 +1,550 @@
+/* Subroutines for insn-output.c for Tahoe.
+ 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)
+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. */
+
+
+/*
+ * File: output-tahoe.c
+ *
+ * This port made at the University of Buffalo by Devon Bowen,
+ * Dale Wiles and Kevin Zachmann.
+ *
+ * Mail bugs reports or fixes to: gcc@cs.buffalo.edu
+ */
+
+
+/* most of the print_operand_address function was taken from the vax */
+/* since the modes are basically the same. I had to add a special case, */
+/* though, for symbol references with offsets. */
+
+#include <stdio.h>
+
+print_operand_address (file, addr)
+ FILE *file;
+ register rtx addr;
+{
+ register rtx reg1, reg2, breg, ireg;
+ rtx offset;
+ static char *reg_name[] = REGISTER_NAMES;
+
+ retry:
+ switch (GET_CODE (addr))
+ {
+ case MEM:
+ fprintf (file, "*");
+ addr = XEXP (addr, 0);
+ goto retry;
+
+ case REG:
+ fprintf (file, "(%s)", reg_name [REGNO (addr)]);
+ break;
+
+ case PRE_DEC:
+ fprintf (file, "-(%s)", reg_name [REGNO (XEXP (addr, 0))]);
+ break;
+
+ case POST_INC:
+ fprintf (file, "(%s)+", reg_name [REGNO (XEXP (addr, 0))]);
+ break;
+
+ case PLUS:
+ reg1 = 0; reg2 = 0;
+ ireg = 0; breg = 0;
+ offset = 0;
+
+ if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ output_addr_const (file, addr);
+
+ if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
+ && GET_CODE (XEXP (addr, 0)) == CONST_INT)
+ output_addr_const (file, addr);
+
+ if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
+ || GET_CODE (XEXP (addr, 0)) == MEM)
+ {
+ offset = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
+ || GET_CODE (XEXP (addr, 1)) == MEM)
+ {
+ offset = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ if (GET_CODE (addr) != PLUS)
+ ;
+ else if (GET_CODE (XEXP (addr, 0)) == MULT)
+ {
+ reg1 = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (GET_CODE (XEXP (addr, 1)) == MULT)
+ {
+ reg1 = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else if (GET_CODE (XEXP (addr, 0)) == REG)
+ {
+ reg1 = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (GET_CODE (XEXP (addr, 1)) == REG)
+ {
+ reg1 = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
+ {
+ if (reg1 == 0)
+ reg1 = addr;
+ else
+ reg2 = addr;
+ addr = 0;
+ }
+ if (offset != 0)
+ {
+ if (addr != 0) abort ();
+ addr = offset;
+ }
+ if (reg1 != 0 && GET_CODE (reg1) == MULT)
+ {
+ breg = reg2;
+ ireg = reg1;
+ }
+ else if (reg2 != 0 && GET_CODE (reg2) == MULT)
+ {
+ breg = reg1;
+ ireg = reg2;
+ }
+ else if (reg2 != 0 || GET_CODE (addr) == MEM)
+ {
+ breg = reg2;
+ ireg = reg1;
+ }
+ else
+ {
+ breg = reg1;
+ ireg = reg2;
+ }
+ if (addr != 0)
+ output_address (offset);
+ if (breg != 0)
+ {
+ if (GET_CODE (breg) != REG)
+ abort ();
+ fprintf (file, "(%s)", reg_name[REGNO (breg)]);
+ }
+ if (ireg != 0)
+ {
+ if (GET_CODE (ireg) == MULT)
+ ireg = XEXP (ireg, 0);
+ if (GET_CODE (ireg) != REG)
+ abort ();
+ fprintf (file, "[%s]", reg_name[REGNO (ireg)]);
+ }
+ break;
+
+ default:
+ output_addr_const (file, addr);
+ }
+}
+
+
+/* Do a quick check and find out what the best way to do the */
+/* mini-move is. Could be a push or a move..... */
+
+static char *
+singlemove_string (operands)
+ rtx *operands;
+{
+ if (GET_CODE (operands[0]) == MEM
+ && GET_CODE (XEXP (operands[0],0)) == PRE_DEC)
+ return "pushl %1";
+ return "movl %1,%0";
+}
+
+
+/* given the rtx for an address, return true if the given */
+/* register number is used in the address somewhere. */
+
+int
+regisused (addr,regnum)
+ rtx addr;
+ int regnum;
+{
+ if (GET_CODE (addr) == REG)
+ {
+ if (REGNO (addr) == regnum)
+ return (1);
+ else
+ return (0);
+ }
+
+ if (GET_CODE (addr) == MEM)
+ return regisused (XEXP (addr,0),regnum);
+
+ if (GET_CODE (addr) == MULT || GET_CODE (addr) == PLUS)
+ return (regisused (XEXP (addr,0),regnum)
+ || regisused (XEXP (addr,1),regnum));
+
+ return 0;
+}
+
+
+/* Given some rtx, traverse it and return the register used in a */
+/* index. If no index is found, return 0. */
+
+rtx
+index_reg (addr)
+ rtx addr;
+{
+ rtx temp;
+
+ if (GET_CODE (addr) == MEM)
+ return index_reg (XEXP (addr,0));
+
+ if (GET_CODE (addr) == MULT)
+ {
+ if (GET_CODE (XEXP (addr,0)) == REG)
+ return XEXP (addr,0);
+ else
+ return XEXP (addr,1);
+ }
+
+ if (GET_CODE (addr) == PLUS)
+ {
+ if (temp = index_reg (XEXP (addr,0)))
+ return temp;
+ else
+ return index_reg (XEXP (addr,1));
+ }
+
+ return 0;
+}
+
+
+/* simulate the move double by generating two movl's. You have */
+/* to be careful about mixing modes here. A future improvement */
+/* would be to allow immediate doubles. */
+
+char *
+output_move_double (operands)
+ rtx *operands;
+{
+ enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, INDOP, CNSTOP, RNDOP } optype0, optype1;
+ rtx latehalf[2];
+ rtx shftreg0 = 0, shftreg1 = 0;
+ rtx temp0 = 0, temp1 = 0;
+ rtx addreg0 = 0, addreg1 = 0;
+ int dohighfirst = 0;
+
+ /* First classify both operands. */
+
+ if (REG_P (operands[0]))
+ optype0 = REGOP;
+ else if ((GET_CODE (operands[0])==MEM) && (shftreg0=index_reg (operands[0])))
+ optype0 = INDOP;
+ else if (offsettable_memref_p (operands[0]))
+ optype0 = OFFSOP;
+ else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
+ {
+ optype0 = PUSHOP;
+ dohighfirst++;
+ }
+ else if (GET_CODE (operands[0]) == MEM)
+ optype0 = MEMOP;
+ else
+ optype0 = RNDOP;
+
+ if (REG_P (operands[1]))
+ optype1 = REGOP;
+ else if ((GET_CODE (operands[1])==MEM) && (shftreg1=index_reg (operands[1])))
+ optype1 = INDOP;
+ 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 (operands[1]) == MEM)
+ optype1 = MEMOP;
+ else if (GET_CODE (operands[1]) == CONST_DOUBLE || CONSTANT_P (operands[1]))
+ optype1 = CNSTOP;
+ else
+ optype1 = RNDOP;
+
+ /* set up for the high byte move for operand zero */
+
+ switch (optype0)
+ {
+
+ /* if it's a register, just use the next highest in the */
+ /* high address move. */
+
+ case REGOP:
+ latehalf[0] = gen_rtx (REG,SImode,REGNO (operands[0])+1);
+ break;
+
+ /* for an offsettable address, use the gcc function to */
+ /* modify the operand to get an offset of 4 higher for */
+ /* the second move. */
+
+ case OFFSOP:
+ latehalf[0] = adj_offsettable_operand (operands[0], 4);
+ break;
+
+ /* if the operand is MEMOP type, it must be a pointer */
+ /* to a pointer. So just remember to increase the mem */
+ /* location and use the same operand. */
+
+ case MEMOP:
+ latehalf[0] = operands[0];
+ addreg0 = XEXP (operands[0],0);
+ break;
+
+ /* if we're dealing with a push instruction, just leave */
+ /* the operand alone since it auto-increments. */
+
+ case PUSHOP:
+ latehalf[0] = operands[0];
+ break;
+
+ /* YUCK! Indexed addressing!! If the address is considered */
+ /* offsettable, go use the offset in the high part. Otherwise */
+ /* find what exactly is being added to the mutiplication. If */
+ /* it's a mem reference, increment that with the high part */
+ /* being unchanged to cause the shift. If it's a reg, do the */
+ /* same. If you can't identify it, abort. Remember that the */
+ /* shift register was already set during identification. */
+
+ case INDOP:
+ if (offsettable_memref_p (operands[0]))
+ {
+ latehalf[0] = adj_offsettable_operand (operands[0],4);
+ break;
+ }
+
+ latehalf[0] = operands[0];
+
+ temp0 = XEXP (XEXP (operands[0],0),0);
+ if (GET_CODE (temp0) == MULT)
+ {
+ temp1 = temp0;
+ temp0 = XEXP (XEXP (operands[0],0),1);
+ }
+ else
+ {
+ temp1 = XEXP (XEXP (operands[0],0),1);
+ if (GET_CODE (temp1) != MULT)
+ abort ();
+ }
+
+ if (GET_CODE (temp0) == MEM)
+ addreg0 = temp0;
+ else if (GET_CODE (temp0) == REG)
+ addreg0 = temp0;
+ else
+ abort ();
+
+ break;
+
+ /* if we don't know the operand type, print a friendly */
+ /* little error message... 8-) */
+
+ case RNDOP:
+ default:
+ abort ();
+ }
+
+ /* do the same setup for operand one */
+
+ switch (optype1)
+ {
+
+ case REGOP:
+ latehalf[1] = gen_rtx (REG,SImode,REGNO (operands[1])+1);
+ break;
+
+ case OFFSOP:
+ latehalf[1] = adj_offsettable_operand (operands[1], 4);
+ break;
+
+ case MEMOP:
+ latehalf[1] = operands[1];
+ addreg1 = XEXP (operands[1],0);
+ break;
+
+ case POPOP:
+ latehalf[1] = operands[1];
+ break;
+
+ case INDOP:
+ if (offsettable_memref_p (operands[1]))
+ {
+ latehalf[1] = adj_offsettable_operand (operands[1],4);
+ break;
+ }
+
+ latehalf[1] = operands[1];
+
+ temp0 = XEXP (XEXP (operands[1],0),0);
+ if (GET_CODE (temp0) == MULT)
+ {
+ temp1 = temp0;
+ temp0 = XEXP (XEXP (operands[1],0),1);
+ }
+ else
+ {
+ temp1 = XEXP (XEXP (operands[1],0),1);
+ if (GET_CODE (temp1) != MULT)
+ abort ();
+ }
+
+ if (GET_CODE (temp0) == MEM)
+ addreg1 = temp0;
+ else if (GET_CODE (temp0) == REG)
+ addreg1 = temp0;
+ else
+ abort ();
+
+ break;
+
+ case CNSTOP:
+ /* Since this machine is big-endian,
+ the late half must be the low-order word for an integer,
+ or the latter word for a float. */
+ if (GET_CODE (operands[1]) == CONST_DOUBLE)
+ {
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT)
+ {
+ 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] = gen_rtx (CONST_INT, VOIDmode,
+ CONST_DOUBLE_LOW (operands[1]));
+ operands[1] = gen_rtx (CONST_INT, VOIDmode,
+ CONST_DOUBLE_HIGH (operands[1]));
+ }
+ }
+ else
+ {
+ latehalf[1] = operands[1];
+ operands[1] = const0_rtx;
+ }
+ break;
+
+ case RNDOP:
+ default:
+ abort ();
+ }
+
+
+ /* double the register used for shifting in both of the operands */
+ /* but make sure the same register isn't doubled twice! */
+
+ if (shftreg0 && shftreg1 && rtx_equal_p (shftreg0, shftreg1))
+ output_asm_insn ("addl2 %0,%0", &shftreg0);
+ else
+ {
+ if (shftreg0)
+ output_asm_insn ("addl2 %0,%0", &shftreg0);
+ if (shftreg1)
+ output_asm_insn ("addl2 %0,%0", &shftreg1);
+ }
+
+ /* if the destination is a register and that register is needed in */
+ /* the source addressing mode, swap the order of the moves since we */
+ /* don't want this destroyed til last. If both regs are used, not */
+ /* much we can do, so abort. If these becomes a problem, maybe we */
+ /* can do it on the stack? */
+
+ if (GET_CODE (operands[0])==REG && regisused (operands[1],REGNO (operands[0])))
+ if (regisused (latehalf[1],REGNO (latehalf[0])))
+ 8;
+ else
+ dohighfirst++;
+
+ /* if we're pushing, do the high address part first. */
+
+ if (dohighfirst)
+ {
+
+ if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
+ output_asm_insn ("addl2 $4,%0", &addreg0);
+ else
+ {
+ if (addreg0)
+ output_asm_insn ("addl2 $4,%0", &addreg0);
+ if (addreg1)
+ output_asm_insn ("addl2 $4,%0", &addreg1);
+ }
+
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+
+ if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
+ output_asm_insn ("subl2 $4,%0", &addreg0);
+ else
+ {
+ if (addreg0)
+ output_asm_insn ("subl2 $4,%0", &addreg0);
+ if (addreg1)
+ output_asm_insn ("subl2 $4,%0", &addreg1);
+ }
+
+ return singlemove_string (operands);
+ }
+
+ output_asm_insn (singlemove_string (operands), operands);
+
+ if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
+ output_asm_insn ("addl2 $4,%0", &addreg0);
+ else
+ {
+ if (addreg0)
+ output_asm_insn ("addl2 $4,%0", &addreg0);
+ if (addreg1)
+ output_asm_insn ("addl2 $4,%0", &addreg1);
+ }
+
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+
+ if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
+ output_asm_insn ("subl2 $4,%0", &addreg0);
+ else
+ {
+ if (addreg0)
+ output_asm_insn ("subl2 $4,%0", &addreg0);
+ if (addreg1)
+ output_asm_insn ("subl2 $4,%0", &addreg1);
+ }
+
+ if (shftreg0 && shftreg1 && (rtx_equal_p (shftreg0,shftreg1)))
+ output_asm_insn ("shar $1,%0,%0", &shftreg0);
+ else
+ {
+ if (shftreg0)
+ output_asm_insn ("shar $1,%0,%0", &shftreg0);
+ if (shftreg1)
+ output_asm_insn ("shar $1,%0,%0", &shftreg1);
+ }
+
+ return "";
+}