summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorrms <rms@138bc75d-0d04-0410-961f-82ee72b054a4>1991-12-24 15:38:27 +0000
committerrms <rms@138bc75d-0d04-0410-961f-82ee72b054a4>1991-12-24 15:38:27 +0000
commit5d812e6ce26ddf3386e53df4e068a1cbd0d57b6d (patch)
tree4a0e8d38cb821313ec5672313ed375a13e5ca1df /gcc
parentb5466b7f43c173e7602dd05ca9f6d582e8ddf291 (diff)
downloadgcc-5d812e6ce26ddf3386e53df4e068a1cbd0d57b6d.tar.gz
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@140 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/config/ns32k/ns32k.c667
1 files changed, 667 insertions, 0 deletions
diff --git a/gcc/config/ns32k/ns32k.c b/gcc/config/ns32k/ns32k.c
new file mode 100644
index 00000000000..f63f05a6891
--- /dev/null
+++ b/gcc/config/ns32k/ns32k.c
@@ -0,0 +1,667 @@
+/* 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 2, 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>
+#include "config.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+
+#ifdef OSF_OS
+int ns32k_num_files = 0;
+#endif
+
+void
+trace (s, s1, s2)
+ char *s, *s1, *s2;
+{
+ fprintf (stderr, s, s1, s2);
+}
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */
+
+int
+hard_regno_mode_ok( regno, mode )
+int regno;
+int mode;
+{
+ switch( mode ) {
+ case QImode:
+ case HImode:
+ case PSImode:
+ case SImode:
+ case PDImode:
+ case VOIDmode:
+ case BLKmode:
+ if( (regno < 8) || (regno == 16) || (regno == 17) ) {
+ return( 1 );
+ }
+ else {
+ return( 0 );
+ }
+
+ case DImode:
+ if( (regno < 8) && ((regno & 1) == 0) ) {
+ return( 1 );
+ }
+ else {
+ return( 0 );
+ }
+
+
+ case SFmode:
+ case SCmode:
+ if( TARGET_32081 ) {
+ if( regno < 16 ) {
+ return( 1 );
+ }
+ else {
+ return( 0 );
+ }
+ }
+ else {
+ if( regno < 8 ) {
+ return( 1 );
+ }
+ else {
+ return( 0 );
+ }
+ }
+
+ case DFmode:
+ case DCmode:
+ if( (regno & 1) == 0 ) {
+ if( TARGET_32081 ) {
+ if( regno < 16 ) {
+ return( 1 );
+ }
+ else {
+ return( 0 );
+ }
+ }
+ else {
+ if( regno < 8 ) {
+ return( 1 );
+ }
+ else {
+ return( 0 );
+ }
+ }
+ }
+ else {
+ return( 0 );
+ }
+
+ case XFmode:
+ abort( 0 );
+ case CCmode:
+ abort( 0 );
+ case TImode:
+ abort( 0 );
+ case XCmode:
+ abort( 0 );
+ case TFmode:
+ abort( 0 );
+ case TCmode:
+ abort( 0 );
+
+
+ default:
+ fprintf( stderr, "cant match mode %d\n", mode );
+ abort( 0 );
+ }
+ abort(0);
+}
+
+/* ADDRESS_COST calls this. This function is not optimal
+ for the 32032 & 32332, but it probably is better than
+ the default. */
+
+int
+calc_address_cost (operand)
+ rtx operand;
+{
+ int i;
+ int cost = 0;
+
+ if (GET_CODE (operand) == MEM)
+ cost += 3;
+ if (GET_CODE (operand) == MULT)
+ cost += 2;
+#if 0
+ if (GET_CODE (operand) == REG)
+ cost += 1; /* not really, but the documentation
+ says different amount of registers
+ shouldn't return the same costs */
+#endif
+ switch (GET_CODE (operand))
+ {
+ case REG:
+ case CONST:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case POST_DEC:
+ case PRE_DEC:
+ break;
+ case MULT:
+ case MEM:
+ case PLUS:
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++)
+ {
+ cost += calc_address_cost (XEXP (operand, i));
+ }
+ default:
+ break;
+ }
+ return cost;
+}
+
+/* Return the register class of a scratch register needed to copy IN into
+ or out of a register in CLASS in MODE. If it can be done directly,
+ NO_REGS is returned. */
+
+enum reg_class
+secondary_reload_class (class, mode, in)
+ enum reg_class class;
+ enum machine_mode mode;
+ rtx in;
+{
+ int regno = true_regnum (in);
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ regno = -1;
+
+ /* We can place anything into GENERAL_REGS and can put GENERAL_REGS
+ into anything. */
+ if (class == GENERAL_REGS || (regno >= 0 && regno < 8))
+ return NO_REGS;
+
+ /* Constants, memory, and FP registers can go into FP registers */
+ if ((regno == -1 || (regno >= 8 && regno < 16)) && (class == FLOAT_REGS))
+ return NO_REGS;
+
+ /* Otherwise, we need GENERAL_REGS. */
+ return GENERAL_REGS;
+}
+/* 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 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)
+ split_double (operands[1], &operands[1], &latehalf[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 is defined to call this function,
+ which is easier to debug than putting all the code in
+ a macro definition in ns32k.h. */
+
+void
+print_operand (file, x, code)
+ FILE *file;
+ rtx x;
+ char code;
+{
+ if (code == '$')
+ PUT_IMMEDIATE_PREFIX(file);
+ else if (code == '?')
+ PUT_EXTERNAL_PREFIX(file);
+ else if (GET_CODE (x) == REG)
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ else if (GET_CODE (x) == MEM)
+ output_address (XEXP (x, 0));
+ else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != DImode)
+ if (GET_MODE (x) == DFmode)
+ {
+ union { double d; int i[2]; } u;
+ u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x);
+ PUT_IMMEDIATE_PREFIX(file);
+ fprintf (file, "0d%.20e", u.d);
+ }
+ else
+ {
+ union { double d; int i[2]; } u;
+ u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x);
+ PUT_IMMEDIATE_PREFIX(file);
+ fprintf (file, "0f%.20e", u.d);
+ }
+ else
+ {
+ PUT_IMMEDIATE_PREFIX(file);
+ output_addr_const (file, x);
+ }
+}
+
+/* PRINT_OPERAND_ADDRESS is defined to call this function,
+ which is easier to debug than putting all the code in
+ a macro definition in ns32k.h . */
+
+/* Completely rewritten to get this to work with Gas for PC532 Mach.
+ This function didn't work and I just wasn't able (nor very willing) to
+ figure out how it worked.
+ 90-11-25 Tatu Yl|nen <ylo@cs.hut.fi> */
+
+print_operand_address (file, addr)
+ register FILE *file;
+ register rtx addr;
+{
+ static char scales[] = { 'b', 'w', 'd', 0, 'q', };
+ rtx offset, base, indexexp, tmp;
+ int scale;
+
+ if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == POST_DEC)
+ {
+ fprintf (file, "tos");
+ return;
+ }
+
+ offset = NULL;
+ base = NULL;
+ indexexp = NULL;
+ while (addr != NULL)
+ {
+ if (GET_CODE (addr) == PLUS)
+ {
+ if (GET_CODE (XEXP (addr, 0)) == PLUS)
+ {
+ tmp = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else
+ {
+ tmp = XEXP (addr,0);
+ addr = XEXP (addr,1);
+ }
+ }
+ else
+ {
+ tmp = addr;
+ addr = NULL;
+ }
+ switch (GET_CODE (tmp))
+ {
+ case PLUS:
+ abort ();
+ case MEM:
+ if (base)
+ {
+ indexexp = base;
+ base = tmp;
+ }
+ else
+ base = tmp;
+ break;
+ case REG:
+ if (REGNO (tmp) < 8)
+ if (base)
+ {
+ indexexp = tmp;
+ }
+ else
+ base = tmp;
+ else
+ if (base)
+ {
+ indexexp = base;
+ base = tmp;
+ }
+ else
+ base = tmp;
+ break;
+ case MULT:
+ indexexp = tmp;
+ break;
+ case CONST:
+ case CONST_INT:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ if (offset)
+ offset = gen_rtx (PLUS, SImode, tmp, offset);
+ else
+ offset = tmp;
+ break;
+ default:
+ abort ();
+ }
+ }
+ if (! offset)
+ offset = const0_rtx;
+ /* now, offset, base and indexexp are set */
+ if (! base)
+#ifdef PC_RELATIVE
+ {
+ if (GET_CODE (offset) == LABEL_REF || GET_CODE (offset) == SYMBOL_REF)
+ ;
+ else
+#endif
+ PUT_ABSOLUTE_PREFIX (file);
+#ifdef PC_RELATIVE
+ }
+#endif
+
+ output_addr_const (file,offset);
+ if (base) /* base can be (REG ...) or (MEM ...) */
+ switch (GET_CODE (base))
+ {
+ /* now we must output base. Possible alternatives are:
+ (rN) (REG ...)
+ (sp) (REG ...)
+ (fp) (REG ...)
+ (pc) (REG ...) used for SYMBOL_REF and LABEL_REF, output
+ (disp(fp)) (MEM ...) just before possible [rX:y]
+ (disp(sp)) (MEM ...)
+ (disp(sb)) (MEM ...)
+ */
+ case REG:
+ fprintf (file, "(%s)", reg_names[REGNO (base)]);
+ break;
+ case MEM:
+ addr = XEXP(base,0);
+ base = NULL;
+ offset = NULL;
+ while (addr != NULL)
+ {
+ if (GET_CODE (addr) == PLUS)
+ {
+ if (GET_CODE (XEXP (addr, 0)) == PLUS)
+ {
+ tmp = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else
+ {
+ tmp = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ }
+ else
+ {
+ tmp = addr;
+ addr = NULL;
+ }
+ switch (GET_CODE (tmp))
+ {
+ case REG:
+ base = tmp;
+ break;
+ case CONST:
+ case CONST_INT:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ if (offset)
+ offset = gen_rtx (PLUS, SImode, tmp, offset);
+ else
+ offset = tmp;
+ break;
+ default:
+ abort ();
+ }
+ }
+ if (! offset)
+ offset = const0_rtx;
+ fprintf (file, "(");
+ output_addr_const (file, offset);
+ if (base)
+ fprintf (file, "(%s)", reg_names[REGNO (base)]);
+ else if (TARGET_SB)
+ fprintf (file, "(sb)");
+ else
+ abort ();
+ fprintf (file, ")");
+ break;
+
+ default:
+ abort ();
+ }
+#ifdef PC_RELATIVE
+ else /* no base */
+ if (GET_CODE (offset) == LABEL_REF || GET_CODE (offset) == SYMBOL_REF)
+ fprintf (file, "(pc)");
+#endif
+#ifdef BASE_REG_NEEDED /* this is defined if the assembler always
+ needs a base register */
+ else if (TARGET_SB)
+ fprintf (file, "(sb)");
+ else
+ abort ();
+#endif
+ /* now print index if we have one */
+ if (indexexp)
+ {
+ if (GET_CODE (indexexp) == MULT)
+ {
+ scale = INTVAL (XEXP (indexexp, 1)) >> 1;
+ indexexp = XEXP (indexexp, 0);
+ }
+ else
+ scale = 0;
+ if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= 8)
+ abort ();
+
+ fprintf (file, "[%s:%c]",
+ reg_names[REGNO (indexexp)],
+ scales[scale]);
+ }
+}
+
+/* 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";
+}