From 581cc66cd59aca5b7a07ce614206574d8e720bd0 Mon Sep 17 00:00:00 2001 From: Charles Hannum Date: Fri, 31 May 1991 19:51:09 +0000 Subject: [PATCH] entered into RCS From-SVN: r24 --- gcc/config/spur/spur.c | 326 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 gcc/config/spur/spur.c diff --git a/gcc/config/spur/spur.c b/gcc/config/spur/spur.c new file mode 100644 index 00000000000..48c65a2866e --- /dev/null +++ b/gcc/config/spur/spur.c @@ -0,0 +1,326 @@ +/* Subroutines for insn-output.c for SPUR. Adapted from routines for + the Motorola 68000 family. + Copyright (C) 1988, 1991 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. */ + +#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" + +static rtx find_addr_reg (); + +char * +output_compare (operands, opcode, exchange_opcode, + neg_opcode, neg_exchange_opcode) + rtx *operands; + char *opcode; + char *exchange_opcode; + char *neg_opcode; + char *neg_exchange_opcode; +{ + static char buf[100]; + operands[2] = operands[0]; + if (GET_CODE (cc_prev_status.value1) == CONST_INT) + { + operands[1] = cc_prev_status.value1; + operands[0] = cc_prev_status.value2; + opcode = exchange_opcode, neg_opcode = neg_exchange_opcode; + } + else + { + operands[0] = cc_prev_status.value1; + operands[1] = cc_prev_status.value2; + } + if (TARGET_LONG_JUMPS) + sprintf (buf, + "cmp_br_delayed %s,%%0,%%1,1f\n\tnop\n\tjump %%l2\n\tnop\n1:", + neg_opcode); + else + sprintf (buf, "cmp_br_delayed %s,%%0,%%1,%%l2\n\tnop", opcode); + return buf; +} + +/* 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_32 %r1,%0"; + if (GET_CODE (operands[1]) == MEM) + return "ld_32 %0,%1\n\tnop"; + if (GET_CODE (operands[1]) == REG) + return "add_nt %0,%1,$0"; + return "add_nt %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])) + 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 (XEXP (operands[0], 0)); + + if (optype1 == MEMOP) + addreg1 = find_addr_reg (XEXP (operands[1], 0)); + + /* 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 (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 if (CONSTANT_P (operands[1])) + latehalf[1] = const0_rtx; + } + 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 ("add_nt %0,%0,$4", &addreg0); + if (addreg1) + output_asm_insn ("add_nt %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 ("add_nt %0,%0,$-4", &addreg0); + if (addreg1) + output_asm_insn ("add_nt %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 ("add_nt %0,%0,$4", &addreg0); + if (addreg1) + output_asm_insn ("add_nt %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 ("add_nt %0,%0,$-4", &addreg0); + if (addreg1) + output_asm_insn ("add_nt %0,%0,$-4", &addreg1); + + return ""; +} + +static char * +output_fp_move_double (operands) + rtx *operands; +{ + if (FP_REG_P (operands[0])) + { + if (FP_REG_P (operands[1])) + return "fmov %0,%1"; + if (GET_CODE (operands[1]) == REG) + { + rtx xoperands[2]; + int offset = - get_frame_size () - 8; + xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); + xoperands[0] = gen_rtx (CONST_INT, VOIDmode, offset + 4); + output_asm_insn ("st_32 %1,r25,%0", xoperands); + xoperands[1] = operands[1]; + xoperands[0] = gen_rtx (CONST_INT, VOIDmode, offset); + output_asm_insn ("st_32 %1,r25,%0", xoperands); + xoperands[1] = operands[0]; + output_asm_insn ("ld_dbl %1,r25,%0\n\tnop", xoperands); + return ""; + } + return "ld_dbl %0,%1\n\tnop"; + } + else if (FP_REG_P (operands[1])) + { + if (GET_CODE (operands[0]) == REG) + { + rtx xoperands[2]; + int offset = - get_frame_size () - 8; + xoperands[0] = gen_rtx (CONST_INT, VOIDmode, offset); + xoperands[1] = operands[1]; + output_asm_insn ("st_dbl %1,r25,%0", xoperands); + xoperands[1] = operands[0]; + output_asm_insn ("ld_32 %1,r25,%0\n\tnop", xoperands); + xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); + xoperands[0] = gen_rtx (CONST_INT, VOIDmode, offset + 4); + output_asm_insn ("ld_32 %1,r25,%0\n\tnop", xoperands); + return ""; + } + return "st_dbl %1,%0"; + } +} + +/* 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); + else if (GET_CODE (XEXP (addr, 1)) == REG) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 0))) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 1))) + addr = XEXP (addr, 0); + else + abort (); + } + if (GET_CODE (addr) == REG) + return addr; + abort (); +} + +/* Generate code to add a large integer constant to register, reg, storing + * the result in a register, target. Offset must be 27-bit signed quantity */ + +static char * +output_add_large_offset (target, reg, offset) + rtx target, reg; + int offset; +{ + rtx operands[3]; + int high, n, i; + operands[0] = target, operands[1] = reg; + + for (high = offset, n = 0; + (unsigned) (high + 0x2000) >= 0x4000; + high >>= 1, n += 1) + ; + operands[2] = gen_rtx (CONST_INT, VOIDmode, high); + output_asm_insn ("add_nt r2,r0,%2", operands); + i = n; + while (i >= 3) + output_asm_insn ("sll r2,r2,$3", operands), i -= 3; + if (i == 2) + output_asm_insn ("sll r2,r2,$2", operands); + else if (i == 1) + output_asm_insn ("sll r2,r2,$1", operands); + output_asm_insn ("add_nt %0,r2,%1", operands); + if (offset - (high << n) != 0) + { + operands[2] = gen_rtx (CONST_INT, VOIDmode, offset - (high << n)); + output_asm_insn ("add_nt %0,%0,%2", operands); + } + return ""; +} + +/* Additional TESTFN for matching. Like immediate_operand, but matches big + * constants */ + +int +big_immediate_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == CONST_INT); +} -- 2.30.2