aboutsummaryrefslogtreecommitdiff
path: root/gcc-1.40/config/out-ns32k.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-1.40/config/out-ns32k.c')
-rw-r--r--gcc-1.40/config/out-ns32k.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/gcc-1.40/config/out-ns32k.c b/gcc-1.40/config/out-ns32k.c
new file mode 100644
index 0000000..e3df4fe
--- /dev/null
+++ b/gcc-1.40/config/out-ns32k.c
@@ -0,0 +1,575 @@
+/* Subroutines for assembler code output on the NS32000.
+ Copyright (C) 1988 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. */
+
+/* Some output-actions in ns32k.md need these. */
+#include <stdio.h>
+extern FILE *asm_out_file;
+
+#define FP_REG_P(X) (GET_CODE (X) == REG && REGNO (X) > 7 && REGNO (X) < 16)
+
+/* Generate the rtx that comes from an address expression in the md file */
+/* The expression to be build is BASE[INDEX:SCALE]. To recognize this,
+ scale must be converted from an exponent (from ASHIFT) to a
+ muliplier (for MULT). */
+rtx
+gen_indexed_expr (base, index, scale)
+ rtx base, index, scale;
+{
+ rtx addr;
+
+ /* This generates an illegal addressing mode, if BASE is
+ fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */
+ if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT)
+ base = gen_rtx (MEM, SImode, base);
+ addr = gen_rtx (MULT, SImode, index,
+ gen_rtx (CONST_INT, VOIDmode, 1 << INTVAL (scale)));
+ addr = gen_rtx (PLUS, SImode, base, addr);
+ return addr;
+}
+
+/* Return 1 if OP is a valid constant int. These can be modeless
+ (void mode), so we do not mess with their modes.
+
+ The main use of this function is as a predicate in match_operand
+ expressions in the machine description. */
+
+int
+const_int (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return (GET_CODE (op) == CONST_INT);
+}
+
+/* Return 1 if OP is a valid operand of mode MODE. This
+ predicate rejects operands which do not have a mode
+ (such as CONST_INT which are VOIDmode). */
+int
+reg_or_mem_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return (GET_MODE (op) == mode
+ && (GET_CODE (op) == REG
+ || GET_CODE (op) == SUBREG
+ || GET_CODE (op) == MEM));
+}
+
+/* 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[1]) == CONST_INT
+ && INTVAL (operands[1]) <= 7
+ && INTVAL (operands[1]) >= -8)
+ return "movqd %1,%0";
+ return "movd %1,%0";
+}
+
+char *
+output_move_double (operands)
+ rtx *operands;
+{
+ enum anon1 { REGOP, OFFSOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
+ rtx latehalf[2];
+
+ /* 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)) == PRE_DEC)
+ optype0 = POPOP;
+ else
+ optype0 = RNDOP;
+
+ if (REG_P (operands[1]))
+ optype1 = REGOP;
+ else if (CONSTANT_ADDRESS_P (operands[1])
+ || GET_CODE (operands[1]) == CONST_DOUBLE)
+ optype1 = CNSTOP;
+ else if (offsettable_memref_p (operands[1]))
+ optype1 = OFFSOP;
+ else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
+ optype1 = POPOP;
+ 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 ();
+
+ /* 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_ADDRESS_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 one or both operands autodecrementing,
+ do the two words, high-numbered first. */
+
+ if (optype0 == POPOP || optype1 == POPOP)
+ {
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+ return singlemove_string (operands);
+ }
+
+ /* Not autodecrementing. Do the two words, low-numbered first. */
+
+ output_asm_insn (singlemove_string (operands), operands);
+
+ operands[0] = latehalf[0];
+ operands[1] = latehalf[1];
+ return singlemove_string (operands);
+}
+
+int
+check_reg (oper, reg)
+ rtx oper;
+ int reg;
+{
+ register int i;
+
+ if (oper == 0)
+ return 0;
+ switch (GET_CODE(oper))
+ {
+ case REG:
+ return (REGNO(oper) == reg) ? 1 : 0;
+ case MEM:
+ return check_reg(XEXP(oper, 0), reg);
+ case PLUS:
+ case MULT:
+ return check_reg(XEXP(oper, 0), reg) || check_reg(XEXP(oper, 1), reg);
+ }
+ return 0;
+}
+
+/* PRINT_OPERAND_ADDRESS is defined to call this function,
+ which is easier to debug than putting all the code in
+ a macro definition in tm-ns32k.h . */
+
+/* Nonzero if we have printed a base register.
+ If zero, on some systems, it means `(sb)' must be printed. */
+int paren_base_reg_printed = 0;
+
+print_operand_address (file, addr)
+ register FILE *file;
+ register rtx addr;
+{
+ register rtx reg1, reg2, breg, ireg;
+ rtx offset;
+ static char scales[] = { 'b', 'w', 'd', 0, 'q', };
+
+ retry:
+ switch (GET_CODE (addr))
+ {
+ case MEM:
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) == REG)
+ if (REGNO (addr) == STACK_POINTER_REGNUM)
+ { fprintf (file, "tos"); break; }
+ else
+ { fprintf (file, "%s", reg_names[REGNO (addr)]); break; }
+ else if (CONSTANT_P (addr))
+ { output_addr_const (file, addr); break; }
+ else if (GET_CODE (addr) == MULT)
+ { fprintf (file, "@0"); ireg = addr; goto print_index; }
+ else if (GET_CODE (addr) == MEM)
+ {
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) == PLUS)
+ {
+ offset = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else
+ {
+ offset = const0_rtx;
+ }
+ output_addr_const (file, offset);
+ fprintf (file, "(%s)", reg_names[REGNO (addr)]);
+ break;
+ }
+
+ if (GET_CODE (addr) != PLUS)
+ abort ();
+
+ goto retry;
+
+ case REG:
+ if (REGNO (addr) == STACK_POINTER_REGNUM)
+ fprintf (file, "tos");
+ else
+ fprintf (file, "0(%s)", reg_names[REGNO (addr)]);
+ break;
+
+ case PRE_DEC:
+ case POST_INC:
+ fprintf (file, "tos");
+ break;
+
+ case MULT:
+ fprintf (file, "@0");
+ ireg = addr; /* [rX:Y] */
+ goto print_index;
+ break;
+
+ case PLUS:
+ reg1 = 0; reg2 = 0;
+ ireg = 0; breg = 0;
+ offset = const0_rtx;
+ if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
+ {
+ offset = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
+ {
+ 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);
+ }
+ /* The case for memory is somewhat tricky: to get
+ a MEM here, the only RTX formats that could
+ get here are either (modulo commutativity)
+ (PLUS (PLUS (REG *MEM)) CONST) -or-
+ (PLUS (PLUS (CONST REG/MULT)) *MEM)
+ We take advantage of that knowledge here. */
+ else if (GET_CODE (XEXP (addr, 0)) == MEM
+ || GET_CODE (XEXP (addr, 1)) == MEM)
+ {
+ rtx temp;
+
+ if (GET_CODE (XEXP (addr, 0)) == MEM)
+ {
+ temp = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else
+ {
+ temp = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+
+ if (GET_CODE (temp) == REG)
+ {
+ reg1 = temp;
+ }
+ else
+ {
+ if (GET_CODE (temp) != PLUS)
+ abort ();
+
+ if (GET_CODE (XEXP (temp, 0)) == MULT)
+ {
+ reg1 = XEXP (temp, 0);
+ offset = XEXP (temp, 1);
+ }
+ if (GET_CODE (XEXP (temp, 1)) == MULT)
+ {
+ reg1 = XEXP (temp, 1);
+ offset = XEXP (temp, 0);
+ }
+ else
+ abort ();
+ }
+ }
+ else if (GET_CODE (XEXP (addr, 0)) == REG
+ || GET_CODE (XEXP (addr, 1)) == REG)
+ {
+ rtx temp;
+
+ if (GET_CODE (XEXP (addr, 0)) == REG)
+ {
+ temp = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else
+ {
+ temp = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+
+ if (GET_CODE (addr) == REG)
+ {
+ if (REGNO (temp) >= FRAME_POINTER_REGNUM)
+ { reg1 = addr; addr = temp; }
+ else
+ { reg1 = temp; }
+ }
+ else if (CONSTANT_P (addr))
+ {
+ if (GET_CODE (offset) == CONST_INT
+ && INTVAL (offset))
+ offset = plus_constant (addr, INTVAL (offset));
+ addr = temp;
+ }
+ else if (GET_CODE (addr) != PLUS)
+ abort ();
+ else
+ {
+ if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
+ {
+ offset = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
+ {
+ offset = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else abort ();
+
+ if (GET_CODE (addr) == REG)
+ {
+ if (REGNO (temp) >= FRAME_POINTER_REGNUM)
+ { reg1 = addr; addr = temp; }
+ else
+ { reg1 = temp; }
+ }
+ else
+ reg1 = temp;
+ }
+ }
+
+ if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
+ { if (reg1 == 0) reg1 = addr; else reg2 = addr; addr = 0; }
+ if (addr != 0)
+ {
+ if (CONSTANT_P (addr) && reg1)
+ {
+ /* OFFSET comes second, to prevent outputting
+ operands of the form INT+SYMBOL+INT.
+ The Genix assembler dies on them. */
+ output_addr_const (file, addr);
+ if (offset != const0_rtx)
+ {
+ putc ('+', file);
+ output_addr_const (file, offset);
+ }
+ ireg = reg1;
+ goto print_index;
+ }
+ else if (GET_CODE (addr) != MEM)
+ abort ();
+
+ output_addr_const (file, offset);
+#ifndef SEQUENT_ADDRESS_BUG
+ putc ('(', file);
+ paren_base_reg_printed = 0;
+ output_address (addr);
+#ifdef SEQUENT_BASE_REGS
+ if (!paren_base_reg_printed)
+ fprintf (file, "(sb)");
+#endif
+ putc (')', file);
+#else /* SEQUENT_ADDRESS_BUG */
+ if ((GET_CODE (offset) == SYMBOL_REF
+ || GET_CODE (offset) == CONST)
+ && GET_CODE (addr) == REG)
+ {
+ if (reg1) abort ();
+ fprintf (file, "[%s:b]", reg_names[REGNO (addr)]);
+ }
+ else
+ {
+ putc ('(', file);
+ paren_base_reg_printed = 0;
+ output_address (addr);
+#ifdef SEQUENT_BASE_REGS
+ if (!paren_base_reg_printed)
+ fprintf (file, "(sb)");
+#endif
+ putc (')', file);
+ }
+#endif /* SEQUENT_ADDRESS_BUG */
+ ireg = reg1;
+ goto print_index;
+ }
+ else addr = offset;
+ if (reg1 && GET_CODE (reg1) == MULT)
+ { breg = reg2; ireg = reg1; }
+ else if (reg2 && GET_CODE (reg2) == MULT)
+ { breg = reg1; ireg = reg2; }
+ else if (reg2 || GET_CODE (addr) == MEM)
+ { breg = reg2; ireg = reg1; }
+ else
+ { breg = reg1; ireg = reg2; }
+ if (ireg != 0 && breg == 0 && GET_CODE (addr) == LABEL_REF)
+ {
+ int scale;
+ if (GET_CODE (ireg) == MULT)
+ {
+ scale = INTVAL (XEXP (ireg, 1)) >> 1;
+ ireg = XEXP (ireg, 0);
+ }
+ else scale = 0;
+ output_asm_label (addr);
+ fprintf (file, "[%s:%c]",
+ reg_names[REGNO (ireg)], scales[scale]);
+ break;
+ }
+ if (ireg && breg && offset == const0_rtx)
+ fprintf (file, "0(%s)", reg_names[REGNO (breg)]);
+ else
+ {
+ if (addr != 0)
+ {
+ if (ireg != 0 && breg == 0
+ && GET_CODE (offset) == CONST_INT) putc('@', file);
+ output_addr_const (file, offset);
+ }
+ if (breg != 0)
+ {
+ if (GET_CODE (breg) != REG) abort ();
+#ifndef SEQUENT_ADDRESS_BUG
+ fprintf (file, "(%s)", reg_names[REGNO (breg)]);
+ paren_base_reg_printed = -1;
+#else
+ if (GET_CODE (offset) == SYMBOL_REF || GET_CODE (offset) == CONST)
+ {
+ if (ireg) abort ();
+ fprintf (file, "[%s:b]", reg_names[REGNO (breg)]);
+ }
+ else
+ {
+ fprintf (file, "(%s)", reg_names[REGNO (breg)]);
+ paren_base_reg_printed = -1;
+ }
+#endif
+ }
+ }
+ print_index:
+ if (ireg != 0)
+ {
+ int scale;
+ if (GET_CODE (ireg) == MULT)
+ {
+ scale = INTVAL (XEXP (ireg, 1)) >> 1;
+ ireg = XEXP (ireg, 0);
+ }
+ else scale = 0;
+ if (GET_CODE (ireg) != REG) abort ();
+ fprintf (file, "[%s:%c]",
+ reg_names[REGNO (ireg)],
+ scales[scale]);
+ }
+ break;
+ default:
+ output_addr_const (file, addr);
+ }
+}
+
+/* National 32032 shifting is so bad that we can get
+ better performance in many common cases by using other
+ techniques. */
+char *
+output_shift_insn (operands)
+ rtx *operands;
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 0
+ && INTVAL (operands[2]) <= 3)
+ if (GET_CODE (operands[0]) == REG)
+ {
+ if (GET_CODE (operands[1]) == REG)
+ {
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ if (operands[2] == const1_rtx)
+ return "addd %0,%0";
+ else if (INTVAL (operands[2]) == 2)
+ return "addd %0,%0\n\taddd %0,%0";
+ }
+ if (operands[2] == const1_rtx)
+ return "movd %1,%0\n\taddd %0,%0";
+
+ operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
+ return "addr %a1,%0";
+ }
+ if (operands[2] == const1_rtx)
+ return "movd %1,%0\n\taddd %0,%0";
+ }
+ else if (GET_CODE (operands[1]) == REG)
+ {
+ operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
+ return "addr %a1,%0";
+ }
+ else if (INTVAL (operands[2]) == 1
+ && GET_CODE (operands[1]) == MEM
+ && rtx_equal_p (operands [0], operands[1]))
+ {
+ rtx temp = XEXP (operands[1], 0);
+
+ if (GET_CODE (temp) == REG
+ || (GET_CODE (temp) == PLUS
+ && GET_CODE (XEXP (temp, 0)) == REG
+ && GET_CODE (XEXP (temp, 1)) == CONST_INT))
+ return "addd %0,%0";
+ }
+ else return "ashd %2,%0";
+ return "ashd %2,%0";
+}