add directory gnu
This commit is contained in:
564
gnu/gcc/gcc-2.2.2/config/tahoe.c
Normal file
564
gnu/gcc/gcc-2.2.2/config/tahoe.c
Normal file
@@ -0,0 +1,564 @@
|
||||
/* 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, 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"
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user