From 2861fe6d2eac35caaf3d5a630e2a243289b2447d Mon Sep 17 00:00:00 2001 From: Richard Stallman Date: Tue, 24 Dec 1991 15:38:27 +0000 Subject: [PATCH] Initial revision From-SVN: r140 --- gcc/config/ns32k/ns32k.c | 667 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 667 insertions(+) create mode 100644 gcc/config/ns32k/ns32k.c 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 +#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 */ + +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"; +}