diff options
author | kenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4> | 1994-10-26 18:26:41 +0000 |
---|---|---|
committer | kenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4> | 1994-10-26 18:26:41 +0000 |
commit | ed36ec654ee8c5e82a73ba3b1bbd4824baa02c22 (patch) | |
tree | 7d71223190a50413ab7d6ecaae5c71af2d3e0e42 /gcc/config/pdp11/pdp11.c | |
parent | 28598d9132ae5af97456b4ee7af6674154a74314 (diff) | |
download | gcc-ed36ec654ee8c5e82a73ba3b1bbd4824baa02c22.tar.gz |
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@8342 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config/pdp11/pdp11.c')
-rw-r--r-- | gcc/config/pdp11/pdp11.c | 1416 |
1 files changed, 1416 insertions, 0 deletions
diff --git a/gcc/config/pdp11/pdp11.c b/gcc/config/pdp11/pdp11.c new file mode 100644 index 00000000000..0d7da7e4507 --- /dev/null +++ b/gcc/config/pdp11/pdp11.c @@ -0,0 +1,1416 @@ +/* Subroutines for gcc2 for pdp11. + Copyright (C) 1994 Free Software Foundation, Inc. + Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at). + +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 1, 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. */ + +#ifndef FILE +#include <stdio.h> +#endif +#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" + +/* +#define FPU_REG_P(X) ((X)>=8 && (X)<14) +#define CPU_REG_P(X) ((X)>=0 && (X)<8) +*/ + +/* this is the current value returned by the macro FIRST_PARM_OFFSET + defined in tm.h */ +int current_first_parm_offset; + +/* This is where the condition code register lives. */ +/* rtx cc0_reg_rtx; - no longer needed? */ + +static rtx find_addr_reg (); + +/* Nonzero if OP is a valid second operand for an arithmetic insn. */ + +int +arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) || GET_CODE (op) == CONST_INT); +} + +int +const_immediate_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == CONST_INT); +} + +int +immediate15_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == CONST_INT && ((INTVAL (op) & 0x8000) == 0x0000)); +} + +int +expand_shift_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == CONST_INT + && abs (INTVAL(op)) > 1 + && abs (INTVAL(op)) <= 4); +} + +/* + stream is a stdio stream to output the code to. + size is an int: how many units of temporary storage to allocate. + Refer to the array `regs_ever_live' to determine which registers + to save; `regs_ever_live[I]' is nonzero if register number I + is ever used in the function. This macro is responsible for + knowing which registers should not be saved even if used. +*/ + +void +output_function_prologue(stream, size) + FILE *stream; + int size; +{ + extern char call_used_regs[]; + extern int frame_pointer_needed; + + int fsize = ((size) + 1) & ~1; + int regno, nregs, i; + int offset = 0; + + int via_ac = -1; + + fprintf (stream, "\n\t; /* function prologue %s*/\n", current_function_name); + + /* if we are outputting code for main, + the switch FPU to right mode if TARGET_FPU */ + if ( (strcmp ("main", current_function_name) == 0) + && TARGET_FPU) + { + fprintf(stream, "\t;/* switch cpu to double float, single integer */\n"); + fprintf(stream, "\tsetd\n"); + fprintf(stream, "\tseti\n\n"); + } + + if (frame_pointer_needed) + { + fprintf(stream, "\tmov fp, -(sp)\n"); + fprintf(stream, "\tmov sp, fp\n"); + } + else + { + /* DON'T SAVE FP */ + } + + /* make frame */ + if (fsize) + fprintf (stream, "\tsub $%d, sp\n", fsize); + + /* save CPU registers */ + for (regno = 0; regno < 8; regno++) + if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (! ((regno == FRAME_POINTER_REGNUM) + && frame_pointer_needed)) + fprintf (stream, "\tmov %s, -(sp)\n", reg_names[regno]); + /* fpu regs saving */ + + /* via_ac specifies the ac to use for saving ac4, ac5 */ + via_ac = -1; + + for (regno = 8; regno < FIRST_PSEUDO_REGISTER ; regno++) + { + /* ac0 - ac3 */ + if (LOAD_FPU_REG_P(regno) + && regs_ever_live[regno] + && ! call_used_regs[regno]) + { + fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[regno]); + via_ac = regno; + } + + /* maybe make ac4, ac5 call used regs?? */ + /* ac4 - ac5 */ + if (NO_LOAD_FPU_REG_P(regno) + && regs_ever_live[regno] + && ! call_used_regs[regno]) + { + if (via_ac == -1) + abort(); + + fprintf (stream, "\tfldd %s, %s\n", reg_names[regno], reg_names[via_ac]); + fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[via_ac]); + } + } + + fprintf (stream, "\t;/* end of prologue */\n\n"); +} + +/* + The function epilogue should not depend on the current stack pointer! + It should use the frame pointer only. This is mandatory because + of alloca; we also take advantage of it to omit stack adjustments + before returning. */ + +/* maybe we can make leaf functions faster by switching to the + second register file - this way we don't have to save regs! + leaf functions are ~ 50% of all functions (dynamically!) + + set/clear bit 11 (dec. 2048) to status word for switching - + but how can we do this? pdp11/45 says bit may only be set (p.24) + switching to kernel is probably more expensive, so we'll leave it + like this + + maybe as option if you want to generate code for kernel mode? */ + + +void +output_function_epilogue(stream, size) + FILE *stream; + int size; +{ + extern char call_used_regs[]; + extern int may_call_alloca; + + int fsize = ((size) + 1) & ~1; + int nregs, regno, i, j, k, adjust_fp; + + int via_ac; + + fprintf (stream, "\n\t; /*function epilogue */\n"); + + if (frame_pointer_needed) + { + /* hope this is safe - m68k does it also .... */ + regs_ever_live[FRAME_POINTER_REGNUM] = 0; + + for (i =7, j = 0 ; i >= 0 ; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) + j++; + + /* remember # of pushed bytes for CPU regs */ + k = 2*j; + + for (i =7 ; i >= 0 ; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf(stream, "\tmov %d(fp), %s\n",-fsize-2*j--, reg_names[i]); + + /* get ACs */ + via_ac = FIRST_PSEUDO_REGISTER -1; + + for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) + { + via_ac = i; + k += 8; + } + + for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) + { + if (LOAD_FPU_REG_P(i) + && regs_ever_live[i] + && ! call_used_regs[i]) + { + fprintf(stream, "\tfldd %d(fp), %s\n", -fsize-k, reg_names[i]); + k -= 8; + } + + if (NO_LOAD_FPU_REG_P(i) + && regs_ever_live[i] + && ! call_used_regs[i]) + { + if (! LOAD_FPU_REG_P(via_ac)) + abort(); + + fprintf(stream, "\tfldd %d(fp), %s\n", -fsize-k, reg_names[via_ac]); + fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]); + k -= 8; + } + } + + fprintf(stream, "\tmov fp, sp\n"); + fprintf (stream, "\tmov (sp)+, fp\n"); + } + else + { + via_ac = FIRST_PSEUDO_REGISTER -1; + + /* get ACs */ + for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) + if (regs_ever_live[i] && call_used_regs[i]) + via_ac = i; + + for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) + { + if (LOAD_FPU_REG_P(i) + && regs_ever_live[i] + && ! call_used_regs[i]) + fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[i]); + + if (NO_LOAD_FPU_REG_P(i) + && regs_ever_live[i] + && ! call_used_regs[i]) + { + if (! LOAD_FPU_REG_P(via_ac)) + abort(); + + fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[via_ac]); + fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]); + } + } + + for (i=7; i >= 0; i--) + if (regs_ever_live[i] && !call_used_regs[i]) + fprintf(stream, "\tmov (sp)+, %s\n", reg_names[i]); + + if (fsize) + fprintf((stream), "\tadd $%d, sp\n", fsize); + } + + fprintf (stream, "\trts pc\n"); + fprintf (stream, "\t;/* end of epilogue*/\n\n\n"); +} + +/* Return the best assembler insn template + for moving operands[1] into operands[0] as a fullword. */ +static char * +singlemove_string (operands) + rtx *operands; +{ + if (operands[1] != const0_rtx) + return "mov %1,%0"; + + return "clr %0"; +} + + +/* 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 (XEXP (operands[0], 0)) == POST_INC) + optype0 = POPOP; + else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) + optype0 = PUSHOP; + 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])) +#if 0 + || GET_CODE (operands[1]) == CONST_DOUBLE) +#endif + optype1 = CNSTOP; + 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 (XEXP (operands[1], 0)) == PRE_DEC) + optype1 = PUSHOP; + 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 one operand is decrementing and one is incrementing + decrement the former register explicitly + and change that operand into ordinary indexing. */ + + if (optype0 == PUSHOP && optype1 == POPOP) + { + operands[0] = XEXP (XEXP (operands[0], 0), 0); + output_asm_insn ("sub $4,%0", operands); + operands[0] = gen_rtx (MEM, SImode, operands[0]); + optype0 = OFFSOP; + } + if (optype0 == POPOP && optype1 == PUSHOP) + { + operands[1] = XEXP (XEXP (operands[1], 0), 0); + output_asm_insn ("sub $4,%1", operands); + operands[1] = gen_rtx (MEM, SImode, operands[1]); + optype1 = OFFSOP; + } + + /* 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, HImode, REGNO (operands[0]) + 1); + else if (optype0 == OFFSOP) + latehalf[0] = adj_offsettable_operand (operands[0], 2); + else + latehalf[0] = operands[0]; + + if (optype1 == REGOP) + latehalf[1] = gen_rtx (REG, HImode, REGNO (operands[1]) + 1); + else if (optype1 == OFFSOP) + latehalf[1] = adj_offsettable_operand (operands[1], 2); + else if (optype1 == CNSTOP) + { + if (CONSTANT_P (operands[1])) + { + /* now the mess begins, high word is in lower word??? + + that's what ashc makes me think, but I don't remember :-( */ + latehalf[1] = gen_rtx(CONST_INT, VOIDmode, + INTVAL(operands[1])>>16); + operands[1] = gen_rtx(CONST_INT, VOIDmode, + INTVAL(operands[1])&0xff); + } + else if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + /* immediate 32 bit values not allowed */ + abort(); + } + } + else + latehalf[1] = operands[1]; + + /* If insn is effectively movd N(sp),-(sp) then we will do the + high word first. We should use the adjusted operand 1 (which is N+4(sp)) + for the low word as well, to compensate for the first decrement of sp. */ + if (optype0 == PUSHOP + && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM + && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) + operands[1] = latehalf[1]; + + /* If one or both operands autodecrementing, + do the two words, high-numbered first. */ + + /* Likewise, 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 == PUSHOP || optype1 == PUSHOP + || (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 $2,%0", &addreg0); + if (addreg1) + output_asm_insn ("add $2,%0", &addreg1); + + /* Do that word. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + output_asm_insn ("sub $2,%0", &addreg0); + if (addreg1) + output_asm_insn ("sub $2,%0", &addreg1); + + /* 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 $2,%0", &addreg0); + if (addreg1) + output_asm_insn ("add $2,%0", &addreg1); + + /* Do that word. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + output_asm_insn ("sub $2,%0", &addreg0); + if (addreg1) + output_asm_insn ("sub $2,%0", &addreg1); + + return ""; +} +/* Output assembler code to perform a quadword move insn + with operands OPERANDS. */ + +char * +output_move_quad (operands) + rtx *operands; +{ + enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; + rtx latehalf[2]; + rtx addreg0 = 0, addreg1 = 0; + + output_asm_insn(";; movdi/df: %1 -> %0", 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)) == POST_INC) + optype0 = POPOP; + else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) + optype0 = PUSHOP; + 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]) + || 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)) == POST_INC) + optype1 = POPOP; + else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) + optype1 = PUSHOP; + 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 (); + + /* check if we move a CPU reg to an FPU reg, or vice versa! */ + if (optype0 == REGOP && optype1 == REGOP) + /* bogus - 64 bit cannot reside in CPU! */ + if (CPU_REG_P(REGNO(operands[0])) + || CPU_REG_P (REGNO(operands[1]))) + abort(); + + if (optype0 == REGOP || optype1 == REGOP) + { + /* check for use of clrd???? + if you ever allow ac4 and ac5 (now we require secondary load) + you must check whether + you want to load into them or store from them - + then dump ac0 into $help$ movce ac4/5 to ac0, do the + store from ac0, and restore ac0 - if you can find + an unused ac[0-3], use that and you save a store and a load!*/ + + if (FPU_REG_P(REGNO(operands[0]))) + { + if (GET_CODE(operands[1]) == CONST_DOUBLE) + { + union { double d; int i[2]; } u; + u.i[0] = CONST_DOUBLE_LOW (operands[1]); + u.i[1] = CONST_DOUBLE_HIGH (operands[1]); + + if (u.d == 0.0) + return "clrd %0"; + } + + return "ldd %1, %0"; + } + + if (FPU_REG_P(REGNO(operands[1]))) + return "std %1, %0"; + } + + /* If one operand is decrementing and one is incrementing + decrement the former register explicitly + and change that operand into ordinary indexing. */ + + if (optype0 == PUSHOP && optype1 == POPOP) + { + operands[0] = XEXP (XEXP (operands[0], 0), 0); + output_asm_insn ("sub $8,%0", operands); + operands[0] = gen_rtx (MEM, DImode, operands[0]); + optype0 = OFFSOP; + } + if (optype0 == POPOP && optype1 == PUSHOP) + { + operands[1] = XEXP (XEXP (operands[1], 0), 0); + output_asm_insn ("sub $8,%1", operands); + operands[1] = gen_rtx (MEM, SImode, operands[1]); + optype1 = OFFSOP; + } + + /* 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]) + 2); + 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]) + 2); + else if (optype1 == OFFSOP) + latehalf[1] = adj_offsettable_operand (operands[1], 4); + else if (optype1 == CNSTOP) + { + if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + /* floats only. not yet supported! + + -- compute it into PDP float format, - internally, + just use IEEE and ignore possible problems ;-) + + we might get away with it !!!! */ + + abort(); + +#ifndef HOST_WORDS_BIG_ENDIAN + latehalf[1] = gen_rtx (CONST_INT, VOIDmode, + CONST_DOUBLE_LOW (operands[1])); + operands[1] = gen_rtx (CONST_INT, VOIDmode, + CONST_DOUBLE_HIGH (operands[1])); +#else /* HOST_WORDS_BIG_ENDIAN */ + latehalf[1] = gen_rtx (CONST_INT, VOIDmode, + CONST_DOUBLE_HIGH (operands[1])); + operands[1] = gen_rtx (CONST_INT, VOIDmode, + CONST_DOUBLE_LOW (operands[1])); +#endif /* HOST_WORDS_BIG_ENDIAN */ + } + } + else + latehalf[1] = operands[1]; + + /* If insn is effectively movd N(sp),-(sp) then we will do the + high word first. We should use the adjusted operand 1 (which is N+4(sp)) + for the low word as well, to compensate for the first decrement of sp. */ + if (optype0 == PUSHOP + && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM + && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) + operands[1] = latehalf[1]; + + /* If one or both operands autodecrementing, + do the two words, high-numbered first. */ + + /* Likewise, 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 == PUSHOP || optype1 == PUSHOP + || (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 $4,%0", &addreg0); + if (addreg1) + output_asm_insn ("add $4,%0", &addreg1); + + /* Do that word. */ + output_asm_insn(output_move_double(latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + output_asm_insn ("sub $4,%0", &addreg0); + if (addreg1) + output_asm_insn ("sub $4,%0", &addreg1); + + /* Do low-numbered word. */ + return output_move_double (operands); + } + + /* Normal case: do the two words, low-numbered first. */ + + output_asm_insn (output_move_double (operands), operands); + + /* Make any unoffsettable addresses point at high-numbered word. */ + if (addreg0) + output_asm_insn ("add $4,%0", &addreg0); + if (addreg1) + output_asm_insn ("add $4,%0", &addreg1); + + /* Do that word. */ + output_asm_insn (output_move_double (latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + output_asm_insn ("sub $4,%0", &addreg0); + if (addreg1) + output_asm_insn ("sub $4,%0", &addreg1); + + return ""; +} + + +/* 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); + if (GET_CODE (XEXP (addr, 1)) == REG) + addr = XEXP (addr, 1); + if (CONSTANT_P (XEXP (addr, 0))) + addr = XEXP (addr, 1); + if (CONSTANT_P (XEXP (addr, 1))) + addr = XEXP (addr, 0); + } + if (GET_CODE (addr) == REG) + return addr; + return 0; +} + +/* Output an ascii string. */ +output_ascii (file, p, size) + FILE *file; + char *p; + int size; +{ + int i; + + fprintf (file, "\t.byte \""); + + for (i = 0; i < size; i++) + { + register int c = p[i]; + if (c == '\"' || c == '\\') + putc ('\\', file); + if (c >= ' ' && c < 0177) + putc (c, file); + else + { + fprintf (file, "\\%03o", c); + /* After an octal-escape, if a digit follows, + terminate one string constant and start another. + The Vax assembler fails to stop reading the escape + after three digits, so this is the only way we + can get it to parse the data properly. */ + if (i < size - 1 && p[i + 1] >= '0' && p[i + 1] <= '9') + fprintf (file, "\"\n\tstring \""); + } + } + fprintf (file, "\"\n"); +} + + +/* --- stole from out-vax, needs changes */ + +print_operand_address (file, addr) + FILE *file; + register rtx addr; +{ + register rtx reg1, reg2, breg, ireg; + rtx offset; + + retry: + + switch (GET_CODE (addr)) + { + case MEM: + fprintf (file, "@"); + addr = XEXP (addr, 0); + goto retry; + + case REG: + fprintf (file, "(%s)", reg_names[REGNO (addr)]); + break; + + case PRE_DEC: + fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]); + break; + + case POST_INC: + fprintf (file, "(%s)+", reg_names[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, 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 (addr); + if (breg != 0) + { + if (GET_CODE (breg) != REG) + abort (); + fprintf (file, "(%s)", reg_names[REGNO (breg)]); + } + if (ireg != 0) + { + if (GET_CODE (ireg) == MULT) + ireg = XEXP (ireg, 0); + if (GET_CODE (ireg) != REG) + abort (); + abort(); + fprintf (file, "[%s]", reg_names[REGNO (ireg)]); + } + break; + + default: + output_addr_const (file, addr); + } +} + +/* register move costs, indexed by regs */ + +static int move_costs[N_REG_CLASSES][N_REG_CLASSES] = +{ + /* NO MUL GEN LFPU NLFPU FPU ALL */ + +/* NO */ { 0, 0, 0, 0, 0, 0, 0}, +/* MUL */ { 0, 2, 2, 10, 22, 22, 22}, +/* GEN */ { 0, 2, 2, 10, 22, 22, 22}, +/* LFPU */ { 0, 10, 10, 2, 2, 2, 10}, +/* NLFPU */ { 0, 22, 22, 2, 2, 2, 22}, +/* FPU */ { 0, 22, 22, 2, 2, 2, 22}, +/* ALL */ { 0, 22, 22, 10, 22, 22, 22} +} ; + + +/* -- note that some moves are tremendously expensive, + because they require lots of tricks? do we have to + charge the costs incurred by secondary reload class + -- as we do here with 22 -- or not ? */ + +int +register_move_cost(c1, c2) + enum reg_class c1, c2; +{ + return move_costs[(int)c1][(int)c2]; +} + +char * +output_jump(pos, neg, length) + int length; + char *pos, *neg; +{ + static int x = 0; + + static char buf[1000]; + +#if 0 +/* currently we don't need this, because the tstdf and cmpdf + copy the condition code immediately, and other float operations are not + yet recognized as changing the FCC - if so, then the length-cost of all + jump insns increases by one, because we have to potentially copy the + FCC! */ + if (cc_status.flags & CC_IN_FPU) + output_asm_insn("cfcc", NULL); +#endif + + switch (length) + { + case 1: + + strcpy(buf, pos); + strcat(buf, " %l0"); + + return buf; + + case 3: + + sprintf(buf, "%s JMP_%d\n\tjmp %%l0\nJMP_%d:", neg, x, x); + + x++; + + return buf; + + default: + + abort(); + } + +} + +void +notice_update_cc_on_set(exp, insn) + rtx exp; + rtx insn; +{ + if (GET_CODE (SET_DEST (exp)) == CC0) + { + cc_status.flags = 0; + cc_status.value1 = SET_DEST (exp); + cc_status.value2 = SET_SRC (exp); + +/* + if (GET_MODE(SET_SRC(exp)) == DFmode) + cc_status.flags |= CC_IN_FPU; +*/ + } + else if ((GET_CODE (SET_DEST (exp)) == REG + || GET_CODE (SET_DEST (exp)) == MEM) + && GET_CODE (SET_SRC (exp)) != PC + && (GET_MODE (SET_DEST(exp)) == HImode + || GET_MODE (SET_DEST(exp)) == QImode) + && (GET_CODE (SET_SRC(exp)) == PLUS + || GET_CODE (SET_SRC(exp)) == MINUS + || GET_CODE (SET_SRC(exp)) == AND + || GET_CODE (SET_SRC(exp)) == IOR + || GET_CODE (SET_SRC(exp)) == XOR + || GET_CODE (SET_SRC(exp)) == NOT + || GET_CODE (SET_SRC(exp)) == NEG + || GET_CODE (SET_SRC(exp)) == REG + || GET_CODE (SET_SRC(exp)) == MEM)) + { + cc_status.flags = 0; + cc_status.value1 = SET_SRC (exp); + cc_status.value2 = SET_DEST (exp); + + if (cc_status.value1 && GET_CODE (cc_status.value1) == REG + && cc_status.value2 + && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2)) + cc_status.value2 = 0; + if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM + && cc_status.value2 + && GET_CODE (cc_status.value2) == MEM) + cc_status.value2 = 0; + } + else if (GET_CODE (SET_SRC (exp)) == CALL) + { + CC_STATUS_INIT; + } + else if (GET_CODE (SET_DEST (exp)) == REG) + /* what's this ? */ + { + if ((cc_status.value1 + && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))) + cc_status.value1 = 0; + if ((cc_status.value2 + && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))) + cc_status.value2 = 0; + } + else if (SET_DEST(exp) == pc_rtx) + { + /* jump */ + } + else /* if (GET_CODE (SET_DEST (exp)) == MEM) */ + { + /* the last else is a bit paranoic, but since nearly all instructions + play with condition codes, it's reasonable! */ + + CC_STATUS_INIT; /* paranoia*/ + } +} + + +int simple_memory_operand(op, mode) + rtx op; + enum machine_mode mode; +{ + rtx addr, plus0, plus1; + int offset = 0; + + /* Eliminate non-memory operations */ + if (GET_CODE (op) != MEM) + return FALSE; + +#if 0 + /* dword operations really put out 2 instructions, so eliminate them. */ + if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4)) + return FALSE; +#endif + + /* Decode the address now. */ + + indirection: + + addr = XEXP (op, 0); + + switch (GET_CODE (addr)) + { + case REG: + /* (R0) - no extra cost */ + return 1; + + case PRE_DEC: + case POST_INC: + /* -(R0), (R0)+ - cheap! */ + return 0; + + case MEM: + /* cheap - is encoded in addressing mode info! + + -- except for @(R0), which has to be @0(R0) !!! */ + + if (GET_CODE (XEXP (addr, 0)) == REG) + return 0; + + op=addr; + goto indirection; + + case CONST_INT: + case LABEL_REF: + case CONST: + case SYMBOL_REF: + /* @#address - extra cost */ + return 0; + + case PLUS: + /* X(R0) - extra cost */ + return 0; + } + + return FALSE; +} + + +/* + * output a block move: + * + * operands[0] ... to + * operands[1] ... from + * operands[2] ... length + * operands[3] ... alignment + * operands[4] ... scratch register + */ + + +char * +output_block_move(operands) + rtx *operands; +{ + static int count = 0; + char buf[200]; + + if (GET_CODE(operands[2]) == CONST_INT + && TARGET_TIME) + { + if (INTVAL(operands[2]) < 16 + && INTVAL(operands[3]) == 1) + { + register int i; + + for (i = 1; i <= INTVAL(operands[2]); i++) + output_asm_insn("movb (%1)+, (%0)+", operands); + + return ""; + } + else if (INTVAL(operands[2]) < 32) + { + register int i; + + for (i = 1; i <= INTVAL(operands[2])/2; i++) + output_asm_insn("mov (%1)+, (%0)+", operands); + + /* may I assume that moved quantity is + multiple of alignment ??? + + I HOPE SO ! + */ + + return ""; + } + + + /* can do other clever things, maybe... */ + } + + if (CONSTANT_P(operands[2]) ) + { + /* just move count to scratch */ + output_asm_insn("mov %2, %4", operands); + } + else + { + /* just clobber the register */ + operands[4] = operands[2]; + } + + + /* switch over alignment */ + switch (INTVAL(operands[3])) + { + case 1: + + /* + x: + movb (%1)+, (%0)+ + + if (TARGET_45) + sob %4,x + else + dec %4 + bgt x + + */ + + sprintf(buf, "\nmovestrhi%d:", count); + output_asm_insn(buf, NULL); + + output_asm_insn("movb (%1)+, (%0)+", operands); + + if (TARGET_45) + { + sprintf(buf, "sob %%4, movestrhi%d", count); + output_asm_insn(buf, operands); + } + else + { + output_asm_insn("dec %4", operands); + + sprintf(buf, "bgt movestrhi%d", count); + output_asm_insn(buf, NULL); + } + + count ++; + break; + + case 2: + + /* + asr %4 + + x: + + mov (%1)+, (%0)+ + + if (TARGET_45) + sob %4, x + else + dec %4 + bgt x + */ + + generate_compact_code: + + output_asm_insn("asr %4", operands); + + sprintf(buf, "\nmovestrhi%d:", count); + output_asm_insn(buf, NULL); + + output_asm_insn("mov (%1)+, (%0)+", operands); + + if (TARGET_45) + { + sprintf(buf, "sob %%4, movestrhi%d", count); + output_asm_insn(buf, operands); + } + else + { + output_asm_insn("dec %4", operands); + + sprintf(buf, "bgt movestrhi%d", count); + output_asm_insn(buf, NULL); + } + + count ++; + break; + + case 4: + + /* + + asr %4 + asr %4 + + x: + + mov (%1)+, (%0)+ + mov (%1)+, (%0)+ + + if (TARGET_45) + sob %4, x + else + dec %4 + bgt x + */ + + if (TARGET_SPACE) + goto generate_compact_code; + + output_asm_insn("asr %4", operands); + output_asm_insn("asr %4", operands); + + sprintf(buf, "\nmovestrhi%d:", count); + output_asm_insn(buf, NULL); + + output_asm_insn("mov (%1)+, (%0)+", operands); + output_asm_insn("mov (%1)+, (%0)+", operands); + + if (TARGET_45) + { + sprintf(buf, "sob %%4, movestrhi%d", count); + output_asm_insn(buf, operands); + } + else + { + output_asm_insn("dec %4", operands); + + sprintf(buf, "bgt movestrhi%d", count); + output_asm_insn(buf, NULL); + } + + count ++; + break; + + default: + + /* + + asr %4 + asr %4 + asr %4 + + x: + + mov (%1)+, (%0)+ + mov (%1)+, (%0)+ + mov (%1)+, (%0)+ + mov (%1)+, (%0)+ + + if (TARGET_45) + sob %4, x + else + dec %4 + bgt x + */ + + + if (TARGET_SPACE) + goto generate_compact_code; + + output_asm_insn("asr %4", operands); + output_asm_insn("asr %4", operands); + output_asm_insn("asr %4", operands); + + sprintf(buf, "\nmovestrhi%d:", count); + output_asm_insn(buf, NULL); + + output_asm_insn("mov (%1)+, (%0)+", operands); + output_asm_insn("mov (%1)+, (%0)+", operands); + output_asm_insn("mov (%1)+, (%0)+", operands); + output_asm_insn("mov (%1)+, (%0)+", operands); + + if (TARGET_45) + { + sprintf(buf, "sob %%4, movestrhi%d", count); + output_asm_insn(buf, operands); + } + else + { + output_asm_insn("dec %4", operands); + + sprintf(buf, "bgt movestrhi%d", count); + output_asm_insn(buf, NULL); + } + + count ++; + break; + + ; + + } + + return ""; +} + +/* for future use */ +int +comparison_operator_index(op) + rtx op; +{ + switch (GET_CODE(op)) + { + case NE: + return 0; + + case EQ: + return 1; + + case GE: + return 2; + + case GT: + return 3; + + case LE: + return 4; + + case LT: + return 5; + + case GEU: + return 6; + + case GTU: + return 7; + + case LEU: + return 8; + + case LTU: + return 9; + + default: + return -1; + } +} + +/* tests whether the rtx is a comparison operator */ +int +comp_operator (op, mode) + rtx op; + enum machine_mode mode; +{ + return comparison_operator_index(op) >= 0; +} + + +int +legitimate_address_p (mode, address) + enum machine_mode mode; + rtx address; +{ +/* #define REG_OK_STRICT */ + GO_IF_LEGITIMATE_ADDRESS(mode, address, win); + + return 0; + + win: + return 1; + +/* #undef REG_OK_STRICT */ +} |