diff options
Diffstat (limited to 'gnu/usr.bin/gcc/config/tahoe/tahoe.c')
-rw-r--r-- | gnu/usr.bin/gcc/config/tahoe/tahoe.c | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/gnu/usr.bin/gcc/config/tahoe/tahoe.c b/gnu/usr.bin/gcc/config/tahoe/tahoe.c new file mode 100644 index 00000000000..6fec44418f4 --- /dev/null +++ b/gnu/usr.bin/gcc/config/tahoe/tahoe.c @@ -0,0 +1,565 @@ +/* Subroutines for insn-output.c for Tahoe. + Copyright (C) 1989, 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, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, 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" + +/* + * File: output-tahoe.c + * + * Original port made at the University of Buffalo by Devon Bowen, + * Dale Wiles and Kevin Zachmann. + * + * Changes for HCX by Piet van Oostrum, + * University of Utrecht, The Netherlands (piet@cs.ruu.nl) + * + * Speed tweaks by Michael Tiemann (tiemann@lurch.stanford.edu). + * + * Mail bugs reports or fixes to: gcc@cs.buffalo.edu + */ + + +/* On tahoe, you have to go to memory to convert a register + from sub-word to word. */ + +rtx tahoe_reg_conversion_loc; + +int +extendable_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if ((GET_CODE (op) == REG + || (GET_CODE (op) == SUBREG + && GET_CODE (SUBREG_REG (op)) == REG)) + && tahoe_reg_conversion_loc == 0) + tahoe_reg_conversion_loc = assign_stack_local (SImode, GET_MODE_SIZE (SImode)); + return general_operand (op, mode); +} + +/* 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 (operands[1] == const0_rtx) + return "clrl %0"; + if (push_operand (operands[0], SImode)) + 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. */ + +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. */ + +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 (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 multiplication. 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 : + if (GET_CODE (operands[1]) == CONST_DOUBLE) + split_double (operands[1], &operands[1], &latehalf[1]); + else if (CONSTANT_P (operands[1])) + latehalf[1] = const0_rtx; + else abort (); + 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 ""; +} + + +/* This checks if a zero_extended cmp[bw] can be replaced by a sign_extended + cmp[bw]. This can be done if the operand is a constant that fits in a + byte/word or a memory operand. Besides that the next instruction must be an + unsigned compare. Some of these tests are done by the machine description */ + +int +tahoe_cmp_check (insn, op, max) +rtx insn, op; int max; +{ + if (GET_CODE (op) == CONST_INT + && ( INTVAL (op) < 0 || INTVAL (op) > max )) + return 0; + { + register rtx next = NEXT_INSN (insn); + + if ((GET_CODE (next) == JUMP_INSN + || GET_CODE (next) == INSN + || GET_CODE (next) == CALL_INSN)) + { + next = PATTERN (next); + if (GET_CODE (next) == SET + && SET_DEST (next) == pc_rtx + && GET_CODE (SET_SRC (next)) == IF_THEN_ELSE) + switch (GET_CODE (XEXP (SET_SRC (next), 0))) + { + case EQ: + case NE: + case LTU: + case GTU: + case LEU: + case GEU: + return 1; + } + } + } + return 0; +} |