diff options
Diffstat (limited to 'gcc/config/i960/i960.c')
-rwxr-xr-x | gcc/config/i960/i960.c | 3000 |
1 files changed, 0 insertions, 3000 deletions
diff --git a/gcc/config/i960/i960.c b/gcc/config/i960/i960.c deleted file mode 100755 index 970cef0..0000000 --- a/gcc/config/i960/i960.c +++ /dev/null @@ -1,3000 +0,0 @@ -/* Subroutines used for code generation on intel 80960. - Copyright (C) 1992, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. - Contributed by Steven McGeady, Intel Corp. - Additional Work by Glenn Colon-Bonet, Jonathan Shapiro, Andy Wilson - Converted to GCC 2.0 by Jim Wilson and Michael Tiemann, Cygnus Support. - -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 <stdio.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" -#include "flags.h" -#include "tree.h" -#include "insn-codes.h" -#include "expr.h" -#include "except.h" -#include "function.h" -#include "recog.h" -#include <math.h> - -/* Save the operands last given to a compare for use when we - generate a scc or bcc insn. */ - -rtx i960_compare_op0, i960_compare_op1; - -/* Used to implement #pragma align/noalign. Initialized by OVERRIDE_OPTIONS - macro in i960.h. */ - -static int i960_maxbitalignment; -static int i960_last_maxbitalignment; - -/* Used to implement switching between MEM and ALU insn types, for better - C series performance. */ - -enum insn_types i960_last_insn_type; - -/* The leaf-procedure return register. Set only if this is a leaf routine. */ - -static int i960_leaf_ret_reg; - -/* True if replacing tail calls with jumps is OK. */ - -static int tail_call_ok; - -/* A string containing a list of insns to emit in the epilogue so as to - restore all registers saved by the prologue. Created by the prologue - code as it saves registers away. */ - -char epilogue_string[1000]; - -/* A unique number (per function) for return labels. */ - -static int ret_label = 0; - -/* This is true if FNDECL is either a varargs or a stdarg function. - This is used to help identify functions that use an argument block. */ - -#define VARARGS_STDARG_FUNCTION(FNDECL) \ -((TYPE_ARG_TYPES (TREE_TYPE (FNDECL)) != 0 \ - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (TREE_TYPE (FNDECL)))) != void_type_node)) \ - || current_function_varargs) - -/* Handle pragmas for compatibility with Intel's compilers. */ - -/* ??? This is incomplete, since it does not handle all pragmas that the - intel compilers understand. */ - -int -process_pragma (p_getc, p_ungetc, pname) - int (* p_getc) PROTO ((void)); - void (* p_ungetc) PROTO ((int)); - char * pname; -{ - int i; - register int c; - char buf[20]; - char *s = buf; - int align; - - /* Should be pragma 'far' or equivalent for callx/balx here. */ - if (strcmp (pname, "align") != 0) - return 0; - - do - { - c = p_getc (); - } - while (c == ' ' || c == '\t'); - - if (c == '(') - c = p_getc (); - - while (c >= '0' && c <= '9') - { - if (s < buf + sizeof buf - 1) - *s++ = c; - c = p_getc (); - } - - *s = '\0'; - - /* We had to read a non-numerical character to get out of the - while loop---often a newline. So, we have to put it back to - make sure we continue to parse everything properly. */ - - p_ungetc (c); - - align = atoi (buf); - - switch (align) - { - case 0: - /* Return to last alignment. */ - align = i960_last_maxbitalignment / 8; - /* Fall through. */ - case 16: - case 8: - case 4: - case 2: - case 1: - i960_last_maxbitalignment = i960_maxbitalignment; - i960_maxbitalignment = align * 8; - break; - - default: - /* Silently ignore bad values. */ - break; - } - - /* NOTE: ic960 R3.0 pragma align definition: - - #pragma align [(size)] | (identifier=size[,...]) - #pragma noalign [(identifier)[,...]] - - (all parens are optional) - - - size is [1,2,4,8,16] - - noalign means size==1 - - applies only to component elements of a struct (and union?) - - identifier applies to structure tag (only) - - missing identifier means next struct - - - alignment rules for bitfields need more investigation */ - - return 1; -} - -/* Initialize variables before compiling any files. */ - -void -i960_initialize () -{ - if (TARGET_IC_COMPAT2_0) - { - i960_maxbitalignment = 8; - i960_last_maxbitalignment = 128; - } - else - { - i960_maxbitalignment = 128; - i960_last_maxbitalignment = 8; - } -} - -/* Return true if OP can be used as the source of an fp move insn. */ - -int -fpmove_src_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return (GET_CODE (op) == CONST_DOUBLE || general_operand (op, mode)); -} - -#if 0 -/* Return true if OP is a register or zero. */ - -int -reg_or_zero_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return register_operand (op, mode) || op == const0_rtx; -} -#endif - -/* Return truth value of whether OP can be used as an operands in a three - address arithmetic insn (such as add %o1,7,%l2) of mode MODE. */ - -int -arith_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return (register_operand (op, mode) || literal (op, mode)); -} - -/* Return truth value of whether OP can be used as an operands in a three - address logic insn, possibly complementing OP, of mode MODE. */ - -int -logic_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return (register_operand (op, mode) - || (GET_CODE (op) == CONST_INT - && INTVAL(op) >= -32 && INTVAL(op) < 32)); -} - -/* Return true if OP is a register or a valid floating point literal. */ - -int -fp_arith_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return (register_operand (op, mode) || fp_literal (op, mode)); -} - -/* Return true is OP is a register or a valid signed integer literal. */ - -int -signed_arith_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return (register_operand (op, mode) || signed_literal (op, mode)); -} - -/* Return truth value of whether OP is a integer which fits the - range constraining immediate operands in three-address insns. */ - -int -literal (op, mode) - rtx op; - enum machine_mode mode; -{ - return ((GET_CODE (op) == CONST_INT) && INTVAL(op) >= 0 && INTVAL(op) < 32); -} - -/* Return true if OP is a float constant of 1. */ - -int -fp_literal_one (op, mode) - rtx op; - enum machine_mode mode; -{ - return (TARGET_NUMERICS && mode == GET_MODE (op) && op == CONST1_RTX (mode)); -} - -/* Return true if OP is a float constant of 0. */ - -int -fp_literal_zero (op, mode) - rtx op; - enum machine_mode mode; -{ - return (TARGET_NUMERICS && mode == GET_MODE (op) && op == CONST0_RTX (mode)); -} - -/* Return true if OP is a valid floating point literal. */ - -int -fp_literal(op, mode) - rtx op; - enum machine_mode mode; -{ - return fp_literal_zero (op, mode) || fp_literal_one (op, mode); -} - -/* Return true if OP is a valid signed immediate constant. */ - -int -signed_literal(op, mode) - rtx op; - enum machine_mode mode; -{ - return ((GET_CODE (op) == CONST_INT) && INTVAL(op) > -32 && INTVAL(op) < 32); -} - -/* Return truth value of statement that OP is a symbolic memory - operand of mode MODE. */ - -int -symbolic_memory_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - if (GET_CODE (op) != MEM) - return 0; - op = XEXP (op, 0); - return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST - || GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF); -} - -/* Return truth value of whether OP is EQ or NE. */ - -int -eq_or_neq (op, mode) - rtx op; - enum machine_mode mode; -{ - return (GET_CODE (op) == EQ || GET_CODE (op) == NE); -} - -/* OP is an integer register or a constant. */ - -int -arith32_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (register_operand (op, mode)) - return 1; - return (CONSTANT_P (op)); -} - -/* Return true if OP is an integer constant which is a power of 2. */ - -int -power2_operand (op,mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != CONST_INT) - return 0; - - return exact_log2 (INTVAL (op)) >= 0; -} - -/* Return true if OP is an integer constant which is the complement of a - power of 2. */ - -int -cmplpower2_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != CONST_INT) - return 0; - - return exact_log2 (~ INTVAL (op)) >= 0; -} - -/* If VAL has only one bit set, return the index of that bit. Otherwise - return -1. */ - -int -bitpos (val) - unsigned int val; -{ - register int i; - - for (i = 0; val != 0; i++, val >>= 1) - { - if (val & 1) - { - if (val != 1) - return -1; - return i; - } - } - return -1; -} - -/* Return non-zero if OP is a mask, i.e. all one bits are consecutive. - The return value indicates how many consecutive non-zero bits exist - if this is a mask. This is the same as the next function, except that - it does not indicate what the start and stop bit positions are. */ - -int -is_mask (val) - unsigned int val; -{ - register int start, end, i; - - start = -1; - for (i = 0; val != 0; val >>= 1, i++) - { - if (val & 1) - { - if (start < 0) - start = i; - - end = i; - continue; - } - /* Still looking for the first bit. */ - if (start < 0) - continue; - - /* We've seen the start of a bit sequence, and now a zero. There - must be more one bits, otherwise we would have exited the loop. - Therefore, it is not a mask. */ - if (val) - return 0; - } - - /* The bit string has ones from START to END bit positions only. */ - return end - start + 1; -} - -/* If VAL is a mask, then return nonzero, with S set to the starting bit - position and E set to the ending bit position of the mask. The return - value indicates how many consecutive bits exist in the mask. This is - the same as the previous function, except that it also indicates the - start and end bit positions of the mask. */ - -int -bitstr (val, s, e) - unsigned int val; - int *s, *e; -{ - register int start, end, i; - - start = -1; - end = -1; - for (i = 0; val != 0; val >>= 1, i++) - { - if (val & 1) - { - if (start < 0) - start = i; - - end = i; - continue; - } - - /* Still looking for the first bit. */ - if (start < 0) - continue; - - /* We've seen the start of a bit sequence, and now a zero. There - must be more one bits, otherwise we would have exited the loop. - Therefor, it is not a mask. */ - if (val) - { - start = -1; - end = -1; - break; - } - } - - /* The bit string has ones from START to END bit positions only. */ - *s = start; - *e = end; - return ((start < 0) ? 0 : end - start + 1); -} - -/* Return the machine mode to use for a comparison. */ - -enum machine_mode -select_cc_mode (op, x) - RTX_CODE op; - rtx x; -{ - if (op == GTU || op == LTU || op == GEU || op == LEU) - return CC_UNSmode; - return CCmode; -} - -/* X and Y are two things to compare using CODE. Emit the compare insn and - return the rtx for register 36 in the proper mode. */ - -rtx -gen_compare_reg (code, x, y) - enum rtx_code code; - rtx x, y; -{ - rtx cc_reg; - enum machine_mode ccmode = SELECT_CC_MODE (code, x, y); - enum machine_mode mode - = GET_MODE (x) == VOIDmode ? GET_MODE (y) : GET_MODE (x); - - if (mode == SImode) - { - if (! arith_operand (x, mode)) - x = force_reg (SImode, x); - if (! arith_operand (y, mode)) - y = force_reg (SImode, y); - } - - cc_reg = gen_rtx (REG, ccmode, 36); - emit_insn (gen_rtx (SET, VOIDmode, cc_reg, - gen_rtx (COMPARE, ccmode, x, y))); - - return cc_reg; -} - -/* For the i960, REG is cost 1, REG+immed CONST is cost 2, REG+REG is cost 2, - REG+nonimmed CONST is cost 4. REG+SYMBOL_REF, SYMBOL_REF, and similar - are 4. Indexed addresses are cost 6. */ - -/* ??? Try using just RTX_COST, i.e. not defining ADDRESS_COST. */ - -int -i960_address_cost (x) - rtx x; -{ -#if 0 - /* Handled before calling here. */ - if (GET_CODE (x) == REG) - return 1; -#endif - /* This is a MEMA operand -- it's free. */ - if (GET_CODE (x) == CONST_INT - && INTVAL (x) >= 0 - && INTVAL (x) < 4096) - return 0; - - if (GET_CODE (x) == PLUS) - { - rtx base = XEXP (x, 0); - rtx offset = XEXP (x, 1); - - if (GET_CODE (base) == SUBREG) - base = SUBREG_REG (base); - if (GET_CODE (offset) == SUBREG) - offset = SUBREG_REG (offset); - - if (GET_CODE (base) == REG) - { - if (GET_CODE (offset) == REG) - return 2; - if (GET_CODE (offset) == CONST_INT) - { - if ((unsigned)INTVAL (offset) < 2047) - return 2; - return 4; - } - if (CONSTANT_P (offset)) - return 4; - } - if (GET_CODE (base) == PLUS || GET_CODE (base) == MULT) - return 6; - - /* This is an invalid address. The return value doesn't matter, but - for convenience we make this more expensive than anything else. */ - return 12; - } - if (GET_CODE (x) == MULT) - return 6; - - /* Symbol_refs and other unrecognized addresses are cost 4. */ - return 4; -} - -/* CYGNUS LOCAL -- move coalescence */ -/* Global variables that are reset for each function. */ - -static rtx i960_const0_r12r13; /* for DImode const0_rtx */ -static rtx i960_const0_r12r15; /* for TImode const0_rtx */ - -/* Define the structure for the machine field in struct function. */ -struct machine_function -{ - rtx const0_r12r13; - rtx const0_r12r15; -}; - -/* Functions to save and restore i960_fpmem_size. - These will be called, via pointer variables, - from push_function_context and pop_function_context. */ - -void -i960_save_machine_status (p) - struct function *p; -{ - struct machine_function *machine = - (struct machine_function *) xmalloc (sizeof (struct machine_function)); - - p->machine = machine; - machine->const0_r12r13 = i960_const0_r12r13; - machine->const0_r12r15 = i960_const0_r12r15; -} - -void -i960_restore_machine_status (p) - struct function *p; -{ - struct machine_function *machine = p->machine; - - i960_const0_r12r13 = machine->const0_r12r13; - i960_const0_r12r15 = machine->const0_r12r15; - - free (machine); - p->machine = (struct machine_function *)0; -} - -/* Do anything needed before RTL is emitted for each function. */ - -void -i960_init_expanders () -{ - i960_const0_r12r13 = (rtx) 0; - i960_const0_r12r15 = (rtx) 0; - - /* Arrange to save and restore machine status around nested functions. */ - save_machine_status = i960_save_machine_status; - restore_machine_status = i960_restore_machine_status; -} - - -/* END CYGNUS LOCAL -- move coalescence */ - -/* Emit insns to move operands[1] into operands[0]. - - Return 1 if we have written out everything that needs to be done to - do the move. Otherwise, return 0 and the caller will emit the move - normally. */ - -int -emit_move_sequence (operands, mode) - rtx *operands; - enum machine_mode mode; -{ - /* We can only store registers to memory. */ - - if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) != REG - && (operands[1] != const0_rtx || current_function_args_size - || current_function_varargs || current_function_stdarg - || rtx_equal_function_value_matters)) - /* Here we use the same test as movsi+1 pattern -- see i960.md. */ - operands[1] = force_reg (mode, operands[1]); - - /* Storing multi-word values in unaligned hard registers to memory may - require a scratch since we have to store them a register at a time and - adding 4 to the memory address may not yield a valid insn. */ - /* ??? We don't always need the scratch, but that would complicate things. - Maybe later. */ - /* ??? We must also handle stores to pseudos here, because the pseudo may be - replaced with a MEM later. This would be cleaner if we didn't have - a separate pattern for unaligned DImode/TImode stores. */ - if (GET_MODE_SIZE (mode) > UNITS_PER_WORD - && (GET_CODE (operands[0]) == MEM - || (GET_CODE (operands[0]) == REG - && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER)) - && GET_CODE (operands[1]) == REG - && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER - && ! HARD_REGNO_MODE_OK (REGNO (operands[1]), mode)) - { - emit_insn (gen_rtx (PARALLEL, VOIDmode, - gen_rtvec (2, - gen_rtx (SET, VOIDmode, - operands[0], operands[1]), - gen_rtx (CLOBBER, VOIDmode, - gen_rtx (SCRATCH, Pmode))))); - return 1; - } - -/* CYGNUS LOCAL -- move coalescence */ - /* See if we can coalesce this move with a previous move. - ??? Actually coalesing is machine-independent optimization. If - the optimization is implemented in machine independent part of - the compiler we will need remove the code. */ - if (! TARGET_STRICT_ALIGN - && (operands[1] == const0_rtx || operands[1] == i960_const0_r12r13) - && GET_CODE (operands[0]) == MEM) - { - /* ??? This is bogus because we can get insn from a previous - sequence. But we need it, otherwise the optimization will - never work because move pattern themselves always start an - new sequence. */ - rtx last_insn = (TARGET_MOVE_COALESCENCE - ? get_last_insn_anywhere () : (rtx) 0); - rtx last_dest; - - if (last_insn && GET_CODE (last_insn) == NOTE) - last_insn = prev_nonnote_insn (last_insn); - - if (last_insn && GET_CODE (last_insn) == INSN - && GET_CODE (PATTERN (last_insn)) == SET - && (SET_SRC (PATTERN (last_insn)) == const0_rtx - || SET_SRC (PATTERN (last_insn)) == i960_const0_r12r13) - && GET_CODE (last_dest = SET_DEST (PATTERN (last_insn))) == MEM) - { - rtx last_addr = XEXP (last_dest, 0); - rtx this_addr = XEXP (operands[0], 0); - enum machine_mode last_mode = GET_MODE (last_dest); - - if (last_mode == GET_MODE (operands[0]) - && GET_MODE_SIZE (last_mode) <= 8) - { - rtx last_base = last_addr, this_base = this_addr; - rtx last_offset = const0_rtx, this_offset = const0_rtx; - - if (GET_CODE (last_addr) == PLUS) - { - last_offset = XEXP (last_addr, 1); - last_base = XEXP (last_addr, 0); - } - if (GET_CODE (this_addr) == PLUS) - { - this_offset = XEXP (this_addr, 1); - this_base = XEXP (this_addr, 0); - } - - /* Now comes the time for the payoff: we check whether - the two store instructions are to adjacent memory - locations, and if so, we merge them. We recurse - here to pick up merges across 4, 8 or even 16 insns. */ - - if (rtx_equal_p (this_base, last_base)) - { - if (rtx_equal_p (last_offset, - plus_constant (this_offset, - GET_MODE_SIZE (last_mode)))) - XEXP (last_dest, 0) = this_addr; - else if (!rtx_equal_p (this_offset, - plus_constant - (last_offset, - GET_MODE_SIZE (last_mode)))) - last_dest = 0; - } - else - last_dest = 0; - - /* If it worked, we've got some housekeeping to do... */ - if (last_dest) - { - /* Change the size of our dest operand. */ - PUT_MODE (last_dest, GET_MODE_WIDER_MODE (last_mode)); - operands[0] = last_dest; - - /* Remove our last insn so that recursion will work. */ - PUT_CODE (last_insn, NOTE); - NOTE_LINE_NUMBER (last_insn) = NOTE_INSN_DELETED; - - /* Make sure we have a large enough register - to act as the source for const0_rtx. */ - - if (GET_MODE_SIZE (GET_MODE (last_dest)) > 4) - { - rtx reg_needs_init = 0; - rtx const0_equiv; - - if (GET_MODE (last_dest) == DImode) - { - if (i960_const0_r12r13 == 0) - { - i960_const0_r12r13 = gen_rtx (REG, DImode, 28); - reg_needs_init = i960_const0_r12r13; - } - const0_equiv = i960_const0_r12r13; - } - else - { - if (i960_const0_r12r15 == 0) - { - i960_const0_r12r15 = gen_rtx (REG, TImode, 28); - reg_needs_init = i960_const0_r12r15; - } - const0_equiv = i960_const0_r12r15; - } - if (reg_needs_init) - { - push_topmost_sequence (); - emit_insn_before (gen_rtx (SET, VOIDmode, - reg_needs_init, - const0_rtx), - next_real_insn (get_insns ())); - pop_topmost_sequence (); - } - operands[1] = const0_equiv; - } - return emit_move_sequence (operands, GET_MODE (last_dest)); - } - } - } - } -/* END CYGNUS LOCAL -- move coalescence */ - return 0; -} - -/* Output assembler to move a double word value. */ - -char * -i960_output_move_double (dst, src) - rtx dst, src; -{ - rtx operands[5]; - - if (GET_CODE (dst) == REG - && GET_CODE (src) == REG) - { - if ((REGNO (src) & 1) - || (REGNO (dst) & 1)) - { - /* We normally copy the low-numbered register first. However, if - the second source register is the same as the first destination - register, we must copy in the opposite order. */ - if (REGNO (src) + 1 == REGNO (dst)) - return "mov %D1,%D0\n\tmov %1,%0"; - else - return "mov %1,%0\n\tmov %D1,%D0"; - } - else - return "movl %1,%0"; - } - else if (GET_CODE (dst) == REG - && GET_CODE (src) == CONST_INT - && CONST_OK_FOR_LETTER_P (INTVAL (src), 'I')) - { - if (REGNO (dst) & 1) - return "mov %1,%0\n\tmov 0,%D0"; - else - return "movl %1,%0"; - } - else if (GET_CODE (dst) == REG - && GET_CODE (src) == MEM) - { - if (REGNO (dst) & 1) - { - /* One can optimize a few cases here, but you have to be - careful of clobbering registers used in the address and - edge conditions. */ - operands[0] = dst; - operands[1] = src; - operands[2] = gen_rtx (REG, Pmode, REGNO (dst) + 1); - operands[3] = gen_rtx (MEM, word_mode, operands[2]); - operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD); - output_asm_insn ("lda %1,%2\n\tld %3,%0\n\tld %4,%D0", operands); - return ""; - } - else - return "ldl %1,%0"; - } - else if (GET_CODE (dst) == MEM - && GET_CODE (src) == REG) - { - if (REGNO (src) & 1) - { - operands[0] = dst; - operands[1] = adj_offsettable_operand (dst, UNITS_PER_WORD); - if (! memory_address_p (word_mode, XEXP (operands[1], 0))) - abort (); - operands[2] = src; - output_asm_insn ("st %2,%0\n\tst %D2,%1", operands); - return ""; - } - return "stl %1,%0"; - } - else - abort (); -} - -/* Output assembler to move a double word zero. */ - -char * -i960_output_move_double_zero (dst) - rtx dst; -{ - rtx operands[2]; - - operands[0] = dst; -/* CYGNUS LOCAL -- move coalescence */ - if (i960_const0_r12r13) - output_asm_insn ("stl r12,%0", operands); - else -/* END CYGNUS LOCAL -- move coalescence */ - { - operands[1] = adj_offsettable_operand (dst, 4); - output_asm_insn ("st g14,%0\n\tst g14,%1", operands); - } - return ""; -} - -/* Output assembler to move a quad word value. */ - -char * -i960_output_move_quad (dst, src) - rtx dst, src; -{ - rtx operands[7]; - - if (GET_CODE (dst) == REG - && GET_CODE (src) == REG) - { - if ((REGNO (src) & 3) - || (REGNO (dst) & 3)) - { - /* We normally copy starting with the low numbered register. - However, if there is an overlap such that the first dest reg - is <= the last source reg but not < the first source reg, we - must copy in the opposite order. */ - if (REGNO (dst) <= REGNO (src) + 3 - && REGNO (dst) >= REGNO (src)) - return "mov %F1,%F0\n\tmov %E1,%E0\n\tmov %D1,%D0\n\tmov %1,%0"; - else - return "mov %1,%0\n\tmov %D1,%D0\n\tmov %E1,%E0\n\tmov %F1,%F0"; - } - else - return "movq %1,%0"; - } - else if (GET_CODE (dst) == REG - && GET_CODE (src) == CONST_INT - && CONST_OK_FOR_LETTER_P (INTVAL (src), 'I')) - { - if (REGNO (dst) & 3) - return "mov %1,%0\n\tmov 0,%D0\n\tmov 0,%E0\n\tmov 0,%F0"; - else - return "movq %1,%0"; - } - else if (GET_CODE (dst) == REG - && GET_CODE (src) == MEM) - { - if (REGNO (dst) & 3) - { - /* One can optimize a few cases here, but you have to be - careful of clobbering registers used in the address and - edge conditions. */ - operands[0] = dst; - operands[1] = src; - operands[2] = gen_rtx (REG, Pmode, REGNO (dst) + 3); - operands[3] = gen_rtx (MEM, word_mode, operands[2]); - operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD); - operands[5] = adj_offsettable_operand (operands[4], UNITS_PER_WORD); - operands[6] = adj_offsettable_operand (operands[5], UNITS_PER_WORD); - output_asm_insn ("lda %1,%2\n\tld %3,%0\n\tld %4,%D0\n\tld %5,%E0\n\tld %6,%F0", operands); - return ""; - } - else - return "ldq %1,%0"; - } - else if (GET_CODE (dst) == MEM - && GET_CODE (src) == REG) - { - if (REGNO (src) & 3) - { - operands[0] = dst; - operands[1] = adj_offsettable_operand (dst, UNITS_PER_WORD); - operands[2] = adj_offsettable_operand (dst, 2*UNITS_PER_WORD); - operands[3] = adj_offsettable_operand (dst, 3*UNITS_PER_WORD); - if (! memory_address_p (word_mode, XEXP (operands[3], 0))) - abort (); - operands[4] = src; - output_asm_insn ("st %4,%0\n\tst %D4,%1\n\tst %E4,%2\n\tst %F4,%3", operands); - return ""; - } - return "stq %1,%0"; - } - else - abort (); -} - -/* Output assembler to move a quad word zero. */ - -char * -i960_output_move_quad_zero (dst) - rtx dst; -{ - rtx operands[4]; - - operands[0] = dst; -/* CYGNUS LOCAL -- move coalescence */ - if (i960_const0_r12r15) - output_asm_insn ("stq r12,%0", operands); - else -/* END CYGNUS LOCAL -- move coalescence */ - { - operands[1] = adj_offsettable_operand (dst, 4); - operands[2] = adj_offsettable_operand (dst, 8); - operands[3] = adj_offsettable_operand (dst, 12); - output_asm_insn ("st g14,%0\n\tst g14,%1\n\tst g14,%2\n\tst g14,%3", operands); - } - return ""; -} - - -/* Emit insns to load a constant to non-floating point registers. - Uses several strategies to try to use as few insns as possible. */ - -char * -i960_output_ldconst (dst, src) - register rtx dst, src; -{ - register int rsrc1; - register unsigned rsrc2; - enum machine_mode mode = GET_MODE (dst); - rtx operands[4]; - - operands[0] = operands[2] = dst; - operands[1] = operands[3] = src; - - /* Anything that isn't a compile time constant, such as a SYMBOL_REF, - must be a ldconst insn. */ - - if (GET_CODE (src) != CONST_INT && GET_CODE (src) != CONST_DOUBLE) - { - output_asm_insn ("ldconst %1,%0", operands); - return ""; - } - else if (mode == XFmode) - { - REAL_VALUE_TYPE d; - long value_long[3]; - int i; - - if (fp_literal_zero (src, XFmode)) - return "movt 0,%0"; - - REAL_VALUE_FROM_CONST_DOUBLE (d, src); - REAL_VALUE_TO_TARGET_LONG_DOUBLE (d, value_long); - - output_asm_insn ("# ldconst %1,%0",operands); - - for (i = 0; i < 3; i++) - { - operands[0] = gen_rtx (REG, SImode, REGNO (dst) + i); - operands[1] = GEN_INT (value_long[i]); - output_asm_insn (i960_output_ldconst (operands[0], operands[1]), - operands); - } - - return ""; - } - else if (mode == DFmode) - { - rtx first, second; - - if (fp_literal_zero (src, DFmode)) - return "movl 0,%0"; - - split_double (src, &first, &second); - - output_asm_insn ("# ldconst %1,%0",operands); - - operands[0] = gen_rtx (REG, SImode, REGNO (dst)); - operands[1] = first; - output_asm_insn (i960_output_ldconst (operands[0], operands[1]), - operands); - operands[0] = gen_rtx (REG, SImode, REGNO (dst) + 1); - operands[1] = second; - output_asm_insn (i960_output_ldconst (operands[0], operands[1]), - operands); - return ""; - } - else if (mode == SFmode) - { - REAL_VALUE_TYPE d; - long value; - - REAL_VALUE_FROM_CONST_DOUBLE (d, src); - REAL_VALUE_TO_TARGET_SINGLE (d, value); - - output_asm_insn ("# ldconst %1,%0",operands); - operands[0] = gen_rtx (REG, SImode, REGNO (dst)); - operands[1] = GEN_INT (value); - output_asm_insn (i960_output_ldconst (operands[0], operands[1]), - operands); - return ""; - } - else if (mode == TImode) - { - /* ??? This is currently not handled at all. */ - abort (); - - /* Note: lowest order word goes in lowest numbered reg. */ - rsrc1 = INTVAL (src); - if (rsrc1 >= 0 && rsrc1 < 32) - return "movq %1,%0"; - else - output_asm_insn ("movq\t0,%0\t# ldconstq %1,%0",operands); - /* Go pick up the low-order word. */ - } - else if (mode == DImode) - { - rtx upperhalf, lowerhalf, xoperands[2]; - - if (GET_CODE (src) == CONST_DOUBLE || GET_CODE (src) == CONST_INT) - split_double (src, &lowerhalf, &upperhalf); - - else - abort (); - - /* Note: lowest order word goes in lowest numbered reg. */ - /* Numbers from 0 to 31 can be handled with a single insn. */ - rsrc1 = INTVAL (lowerhalf); - if (upperhalf == const0_rtx && rsrc1 >= 0 && rsrc1 < 32) - return "movl %1,%0"; - - /* Output the upper half with a recursive call. */ - xoperands[0] = gen_rtx (REG, SImode, REGNO (dst) + 1); - xoperands[1] = upperhalf; - output_asm_insn (i960_output_ldconst (xoperands[0], xoperands[1]), - xoperands); - /* The lower word is emitted as normally. */ - } - else - { - rsrc1 = INTVAL (src); - if (mode == QImode) - { - if (rsrc1 > 0xff) - rsrc1 &= 0xff; - } - else if (mode == HImode) - { - if (rsrc1 > 0xffff) - rsrc1 &= 0xffff; - } - } - - if (rsrc1 >= 0) - { - /* ldconst 0..31,X -> mov 0..31,X */ - if (rsrc1 < 32) - { - if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES) - return "lda %1,%0"; - return "mov %1,%0"; - } - - /* ldconst 32..63,X -> add 31,nn,X */ - if (rsrc1 < 63) - { - if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES) - return "lda %1,%0"; - operands[1] = GEN_INT (rsrc1 - 31); - output_asm_insn ("addo\t31,%1,%0\t# ldconst %3,%0", operands); - return ""; - } - } - else if (rsrc1 < 0) - { - /* ldconst -1..-31 -> sub 0,0..31,X */ - if (rsrc1 >= -31) - { - /* return 'sub -(%1),0,%0' */ - operands[1] = GEN_INT (- rsrc1); - output_asm_insn ("subo\t%1,0,%0\t# ldconst %3,%0", operands); - return ""; - } - - /* ldconst -32 -> not 31,X */ - if (rsrc1 == -32) - { - operands[1] = GEN_INT (~rsrc1); - output_asm_insn ("not\t%1,%0 # ldconst %3,%0", operands); - return ""; - } - } - - /* If const is a single bit. */ - if (bitpos (rsrc1) >= 0) - { - operands[1] = GEN_INT (bitpos (rsrc1)); - output_asm_insn ("setbit\t%1,0,%0\t# ldconst %3,%0", operands); - return ""; - } - - /* If const is a bit string of less than 6 bits (1..31 shifted). */ - if (is_mask (rsrc1)) - { - int s, e; - - if (bitstr (rsrc1, &s, &e) < 6) - { - rsrc2 = ((unsigned int) rsrc1) >> s; - operands[1] = GEN_INT (rsrc2); - operands[2] = GEN_INT (s); - output_asm_insn ("shlo\t%2,%1,%0\t# ldconst %3,%0", operands); - return ""; - } - } - - /* Unimplemented cases: - const is in range 0..31 but rotated around end of word: - ror 31,3,g0 -> ldconst 0xe0000003,g0 - - and any 2 instruction cases that might be worthwhile */ - - output_asm_insn ("ldconst %1,%0", operands); - return ""; -} - -/* Determine if there is an opportunity for a bypass optimization. - Bypass succeeds on the 960K* if the destination of the previous - instruction is the second operand of the current instruction. - Bypass always succeeds on the C*. - - Return 1 if the pattern should interchange the operands. - - CMPBR_FLAG is true if this is for a compare-and-branch insn. - OP1 and OP2 are the two source operands of a 3 operand insn. */ - -int -i960_bypass (insn, op1, op2, cmpbr_flag) - register rtx insn, op1, op2; - int cmpbr_flag; -{ - register rtx prev_insn, prev_dest; - - if (TARGET_C_SERIES) - return 0; - - /* Can't do this if op1 isn't a register. */ - if (! REG_P (op1)) - return 0; - - /* Can't do this for a compare-and-branch if both ops aren't regs. */ - if (cmpbr_flag && ! REG_P (op2)) - return 0; - - prev_insn = prev_real_insn (insn); - - if (prev_insn && GET_CODE (prev_insn) == INSN - && GET_CODE (PATTERN (prev_insn)) == SET) - { - prev_dest = SET_DEST (PATTERN (prev_insn)); - if ((GET_CODE (prev_dest) == REG && REGNO (prev_dest) == REGNO (op1)) - || (GET_CODE (prev_dest) == SUBREG - && GET_CODE (SUBREG_REG (prev_dest)) == REG - && REGNO (SUBREG_REG (prev_dest)) == REGNO (op1))) - return 1; - } - return 0; -} - -/* Output the code which declares the function name. This also handles - leaf routines, which have special requirements, and initializes some - global variables. */ - -void -i960_function_name_declare (file, name, fndecl) - FILE *file; - char *name; - tree fndecl; -{ - register int i, j; - int leaf_proc_ok; - rtx insn; - - /* Increment global return label. */ - - ret_label++; - - /* Compute whether tail calls and leaf routine optimizations can be performed - for this function. */ - - if (TARGET_TAILCALL) - tail_call_ok = 1; - else - tail_call_ok = 0; - - if (TARGET_LEAFPROC) - leaf_proc_ok = 1; - else - leaf_proc_ok = 0; - - /* Even if nobody uses extra parms, can't have leafproc or tail calls if - argblock, because argblock uses g14 implicitly. */ - - if (current_function_args_size != 0 || VARARGS_STDARG_FUNCTION (fndecl)) - { - tail_call_ok = 0; - leaf_proc_ok = 0; - } - - /* See if caller passes in an address to return value. */ - - if (aggregate_value_p (DECL_RESULT (fndecl))) - { - tail_call_ok = 0; - leaf_proc_ok = 0; - } - - /* Can not use tail calls or make this a leaf routine if there is a non - zero frame size. */ - - if (get_frame_size () != 0) - leaf_proc_ok = 0; - - /* I don't understand this condition, and do not think that it is correct. - Apparently this is just checking whether the frame pointer is used, and - we can't trust regs_ever_live[fp] since it is (almost?) always set. */ - - if (tail_call_ok) - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN - && reg_mentioned_p (frame_pointer_rtx, insn)) - { - tail_call_ok = 0; - break; - } - - /* Check for CALL insns. Can not be a leaf routine if there are any. */ - - if (leaf_proc_ok) - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) - { - leaf_proc_ok = 0; - break; - } - - /* Can not be a leaf routine if any non-call clobbered registers are - used in this function. */ - - if (leaf_proc_ok) - for (i = 0, j = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i] - && ((! call_used_regs[i]) || (i > 7 && i < 12))) - { - /* Global registers. */ - if (i < 16 && i > 7 && i != 13) - leaf_proc_ok = 0; - /* Local registers. */ - else if (i < 32) - leaf_proc_ok = 0; - } - - /* Now choose a leaf return register, if we can find one, and if it is - OK for this to be a leaf routine. */ - - i960_leaf_ret_reg = -1; - - if (optimize && leaf_proc_ok) - { - for (i960_leaf_ret_reg = -1, i = 0; i < 8; i++) - if (regs_ever_live[i] == 0) - { - i960_leaf_ret_reg = i; - regs_ever_live[i] = 1; - break; - } - } - - /* Do this after choosing the leaf return register, so it will be listed - if one was chosen. */ - - fprintf (file, "\t# Function '%s'\n", (name[0] == '*' ? &name[1] : name)); - fprintf (file, "\t# Registers used: "); - - for (i = 0, j = 0; i < FIRST_PSEUDO_REGISTER; i++) - { - if (regs_ever_live[i]) - { - fprintf (file, "%s%s ", reg_names[i], call_used_regs[i] ? "" : "*"); - - if (i > 15 && j == 0) - { - fprintf (file,"\n\t#\t\t "); - j++; - } - } - } - - fprintf (file, "\n"); - - if (i960_leaf_ret_reg >= 0) - { - /* Make it a leaf procedure. */ - - if (TREE_PUBLIC (fndecl)) - fprintf (file,"\t.globl\t%s.lf\n", (name[0] == '*' ? &name[1] : name)); - - fprintf (file, "\t.leafproc\t"); - assemble_name (file, name); - fprintf (file, ",%s.lf\n", (name[0] == '*' ? &name[1] : name)); - ASM_OUTPUT_LABEL (file, name); - fprintf (file, "\tlda LR%d,g14\n", ret_label); - fprintf (file, "%s.lf:\n", (name[0] == '*' ? &name[1] : name)); - fprintf (file, "\tmov g14,g%d\n", i960_leaf_ret_reg); - - if (TARGET_C_SERIES) - { - fprintf (file, "\tlda 0,g14\n"); - i960_last_insn_type = I_TYPE_MEM; - } - else - { - fprintf (file, "\tmov 0,g14\n"); - i960_last_insn_type = I_TYPE_REG; - } - } - else - { - ASM_OUTPUT_LABEL (file, name); - i960_last_insn_type = I_TYPE_CTRL; - } -} - -/* Compute and return the frame size. */ - -int -compute_frame_size (size) - int size; -{ - int actual_fsize; - int outgoing_args_size = current_function_outgoing_args_size; - - /* The STARTING_FRAME_OFFSET is totally hidden to us as far - as size is concerned. */ - actual_fsize = (size + 15) & -16; - actual_fsize += (outgoing_args_size + 15) & -16; - - return actual_fsize; -} - -/* Here register group is range of registers which can be moved by - one i960 instruction. */ - -struct reg_group -{ - char start_reg; - char length; -}; - -/* The following functions forms the biggest as possible register - groups with registers in STATE. REGS contain states of the - registers in range [start, finish_reg). The function returns the - number of groups formed. */ -static int -i960_form_reg_groups (start_reg, finish_reg, regs, state, reg_groups) - int start_reg; - int finish_reg; - int *regs; - int state; - struct reg_group *reg_groups; -{ - int i; - int nw = 0; - - for (i = start_reg; i < finish_reg; ) - { - if (regs [i] != state) - { - i++; - continue; - } - else if (i % 2 != 0 || regs [i + 1] != state) - reg_groups [nw].length = 1; - else if (i % 4 != 0 || regs [i + 2] != state) - reg_groups [nw].length = 2; - else if (regs [i + 3] != state) - reg_groups [nw].length = 3; - else - reg_groups [nw].length = 4; - reg_groups [nw].start_reg = i; - i += reg_groups [nw].length; - nw++; - } - return nw; -} - -/* We sort register winodws in descending order by length. */ -static int -i960_reg_group_compare (group1, group2) - void *group1; - void *group2; -{ - struct reg_group *w1 = group1; - struct reg_group *w2 = group2; - - if (w1->length > w2->length) - return -1; - else if (w1->length < w2->length) - return 1; - else - return 0; -} - -/* Split the first register group in REG_GROUPS on subgroups one of - which will contain SUBGROUP_LENGTH registers. The function - returns new number of winodws. */ -static int -i960_split_reg_group (reg_groups, nw, subgroup_length) - struct reg_group *reg_groups; - int nw; - int subgroup_length; -{ - if (subgroup_length < reg_groups->length - subgroup_length) - /* This guarantees correct alignments of the two subgroups for - i960 (see spliting for the group length 2, 3, 4). More - generalized algorithm would require splitting the group more - two subgroups. */ - subgroup_length = reg_groups->length - subgroup_length; - /* More generalized algorithm would require to try merging - subgroups here. But in case i960 it always results in failure - because of register group alignment. */ - reg_groups[nw].length = reg_groups->length - subgroup_length; - reg_groups[nw].start_reg = reg_groups->start_reg + subgroup_length; - nw++; - reg_groups->length = subgroup_length; - qsort (reg_groups, nw, sizeof (struct reg_group), i960_reg_group_compare); - return nw; -} - -/* Output code for the function prologue. */ - -void -i960_function_prologue (file, size) - FILE *file; - unsigned int size; -{ - register int i, j, nr; - int n_iregs = 0; - int rsize = 0; - int actual_fsize, offset; - int gnw, lnw; - struct reg_group *g, *l; - char tmpstr[1000]; - /* -1 if reg must be saved on proc entry, 0 if available, 1 if saved - somewhere. */ - int regs[FIRST_PSEUDO_REGISTER]; - /* All global registers (which must be saved) divided by groups. */ - struct reg_group global_reg_groups [16]; - /* All local registers (which are available) divided by groups. */ - struct reg_group local_reg_groups [16]; - - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (regs_ever_live[i] - && ((! call_used_regs[i]) || (i > 7 && i < 12))) - { - regs[i] = -1; - /* Count global registers that need saving. */ - if (i < 16) - n_iregs++; - } - else - regs[i] = 0; - - epilogue_string[0] = '\0'; - - if (profile_flag || profile_block_flag) - { - /* When profiling, we may use registers 20 to 27 to save arguments, so - they can't be used here for saving globals. J is the number of - argument registers the mcount call will save. */ - for (j = 7; j >= 0 && ! regs_ever_live[j]; j--) - ; - - for (i = 20; i <= j + 20; i++) - regs[i] = -1; - } - - gnw = i960_form_reg_groups (0, 16, regs, -1, global_reg_groups); - lnw = i960_form_reg_groups (19, 32, regs, 0, local_reg_groups); - qsort (global_reg_groups, gnw, sizeof (struct reg_group), - i960_reg_group_compare); - qsort (local_reg_groups, lnw, sizeof (struct reg_group), - i960_reg_group_compare); - for (g = global_reg_groups, l = local_reg_groups; lnw != 0 && gnw != 0;) - { - if (g->length == l->length) - { - fprintf (file, "\tmov%s %s,%s\n", - ((g->length == 4) ? "q" : - (g->length == 3) ? "t" : - (g->length == 2) ? "l" : ""), - reg_names[g->start_reg], reg_names[l->start_reg]); - sprintf (tmpstr, "\tmov%s %s,%s\n", - ((g->length == 4) ? "q" : - (g->length == 3) ? "t" : - (g->length == 2) ? "l" : ""), - reg_names[l->start_reg], reg_names[g->start_reg]); - strcat (epilogue_string, tmpstr); - n_iregs -= g->length; - for (i = 0; i < g->length; i++) - { - regs [i + g->start_reg] = 1; - regs [i + l->start_reg] = -1; - regs_ever_live [i + l->start_reg] = 1; - } - g++; - l++; - gnw--; - lnw--; - } - else if (g->length > l->length) - gnw = i960_split_reg_group (g, gnw, l->length); - else - lnw = i960_split_reg_group (l, lnw, g->length); - } - - /* N_iregs is now the number of global registers that haven't been saved - yet. */ - - rsize = (n_iregs * 4); - actual_fsize = compute_frame_size (size) + rsize; -#if 0 - /* ??? The 1.2.1 compiler does this also. This is meant to round the frame - size up to the nearest multiple of 16. I don't know whether this is - necessary, or even desirable. - - The frame pointer must be aligned, but the call instruction takes care of - that. If we leave the stack pointer unaligned, we may save a little on - dynamic stack allocation. And we don't lose, at least according to the - i960CA manual. */ - actual_fsize = (actual_fsize + 15) & ~0xF; -#endif - - /* Allocate space for register save and locals. */ - if (actual_fsize > 0) - { - if (actual_fsize < 32) - fprintf (file, "\taddo %d,sp,sp\n", actual_fsize); - else - fprintf (file, "\tlda\t%d(sp),sp\n", actual_fsize); - } - - /* Take hardware register save area created by the call instruction - into account, but store them before the argument block area. */ - offset = 64 + actual_fsize - compute_frame_size (0) - rsize; - /* Save registers on stack if needed. */ - /* ??? Is it worth to use the same algorithm as one for saving - global registers in local registers? */ - for (i = 0, j = n_iregs; j > 0 && i < 16; i++) - { - if (regs[i] != -1) - continue; - - nr = 1; - - if (i <= 14 && i % 2 == 0 && regs[i+1] == -1 && offset % 2 == 0) - nr = 2; - - if (nr == 2 && i <= 12 && i % 4 == 0 && regs[i+2] == -1 - && offset % 4 == 0) - nr = 3; - - if (nr == 3 && regs[i+3] == -1) - nr = 4; - - fprintf (file,"\tst%s %s,%d(fp)\n", - ((nr == 4) ? "q" : - (nr == 3) ? "t" : - (nr == 2) ? "l" : ""), - reg_names[i], offset); - sprintf (tmpstr,"\tld%s %d(fp),%s\n", - ((nr == 4) ? "q" : - (nr == 3) ? "t" : - (nr == 2) ? "l" : ""), - offset, reg_names[i]); - strcat (epilogue_string, tmpstr); - i += nr-1; - j -= nr; - offset += nr * 4; - } - - if (actual_fsize == 0 && size == 0 && rsize == 0) - return; - - fprintf (file, "\t#Prologue stats:\n"); - fprintf (file, "\t# Total Frame Size: %d bytes\n", actual_fsize); - - if (size) - fprintf (file, "\t# Local Variable Size: %d bytes\n", size); - if (rsize) - fprintf (file, "\t# Register Save Size: %d regs, %d bytes\n", - n_iregs, rsize); - fprintf (file, "\t#End Prologue#\n"); -} - -/* Output code for the function profiler. */ - -void -output_function_profiler (file, labelno) - FILE *file; - int labelno; -{ - /* The last used parameter register. */ - int last_parm_reg; - int i, j, increment; - int varargs_stdarg_function - = VARARGS_STDARG_FUNCTION (current_function_decl); - - /* Figure out the last used parameter register. The proper thing to do - is to walk incoming args of the function. A function might have live - parameter registers even if it has no incoming args. Note that we - don't have to save parameter registers g8 to g11 because they are - call preserved. */ - - /* See also output_function_prologue, which tries to use local registers - for preserved call-saved global registers. */ - - for (last_parm_reg = 7; - last_parm_reg >= 0 && ! regs_ever_live[last_parm_reg]; - last_parm_reg--) - ; - - /* Save parameter registers in regs r4 (20) to r11 (27). */ - - for (i = 0, j = 4; i <= last_parm_reg; i += increment, j += increment) - { - if (i % 4 == 0 && (last_parm_reg - i) >= 3) - increment = 4; - else if (i % 4 == 0 && (last_parm_reg - i) >= 2) - increment = 3; - else if (i % 2 == 0 && (last_parm_reg - i) >= 1) - increment = 2; - else - increment = 1; - - fprintf (file, "\tmov%s g%d,r%d\n", - (increment == 4 ? "q" : increment == 3 ? "t" - : increment == 2 ? "l": ""), i, j); - } - - /* If this function uses the arg pointer, then save it in r3 and then - set it to zero. */ - - if (current_function_args_size != 0 || varargs_stdarg_function) - fprintf (file, "\tmov g14,r3\n\tmov 0,g14\n"); - - /* Load location address into g0 and call mcount. */ - - fprintf (file, "\tlda\tLP%d,g0\n\tcallx\tmcount\n", labelno); - - /* If this function uses the arg pointer, restore it. */ - - if (current_function_args_size != 0 || varargs_stdarg_function) - fprintf (file, "\tmov r3,g14\n"); - - /* Restore parameter registers. */ - - for (i = 0, j = 4; i <= last_parm_reg; i += increment, j += increment) - { - if (i % 4 == 0 && (last_parm_reg - i) >= 3) - increment = 4; - else if (i % 4 == 0 && (last_parm_reg - i) >= 2) - increment = 3; - else if (i % 2 == 0 && (last_parm_reg - i) >= 1) - increment = 2; - else - increment = 1; - - fprintf (file, "\tmov%s r%d,g%d\n", - (increment == 4 ? "q" : increment == 3 ? "t" - : increment == 2 ? "l": ""), j, i); - } -} - -/* Output code for the function epilogue. */ - -void -i960_function_epilogue (file, size) - FILE *file; - unsigned int size; -{ - if (i960_leaf_ret_reg >= 0) - { - fprintf (file, "LR%d: ret\n", ret_label); - return; - } - - if (*epilogue_string == 0) - { - register rtx tmp; - - /* Emit a return insn, but only if control can fall through to here. */ - - tmp = get_last_insn (); - while (tmp) - { - if (GET_CODE (tmp) == BARRIER) - return; - if (GET_CODE (tmp) == CODE_LABEL) - break; - if (GET_CODE (tmp) == JUMP_INSN) - { - if (GET_CODE (PATTERN (tmp)) == RETURN) - return; - break; - } - if (GET_CODE (tmp) == NOTE) - { - tmp = PREV_INSN (tmp); - continue; - } - break; - } - fprintf (file, "LR%d: ret\n", ret_label); - return; - } - - fprintf (file, "LR%d:\n", ret_label); - - fprintf (file, "\t#EPILOGUE#\n"); - - /* Output the string created by the prologue which will restore all - registers saved by the prologue. */ - - if (epilogue_string[0] != '\0') - fprintf (file, "%s", epilogue_string); - - /* Must clear g14 on return if this function set it. - Only varargs/stdarg functions modify g14. */ - - if (VARARGS_STDARG_FUNCTION (current_function_decl)) - fprintf (file, "\tmov 0,g14\n"); - - fprintf (file, "\tret\n"); - fprintf (file, "\t#End Epilogue#\n"); -} - -/* Output code for a call insn. */ - -char * -i960_output_call_insn (target, argsize_rtx, arg_pointer, insn) - register rtx target, argsize_rtx, arg_pointer, insn; -{ - int argsize = INTVAL (argsize_rtx); - rtx nexti = next_real_insn (insn); - rtx operands[2]; - int varargs_stdarg_function - = VARARGS_STDARG_FUNCTION (current_function_decl); - - operands[0] = target; - operands[1] = arg_pointer; - - if (current_function_args_size != 0 || varargs_stdarg_function) - output_asm_insn ("mov g14,r3", operands); - - if (argsize > 48) - output_asm_insn ("lda %a1,g14", operands); - else if (current_function_args_size != 0 || varargs_stdarg_function) - output_asm_insn ("mov 0,g14", operands); - - /* The code used to assume that calls to SYMBOL_REFs could not be more - than 24 bits away (b vs bx, callj vs callx). This is not true. This - feature is now implemented by relaxing in the GNU linker. It can convert - bx to b if in range, and callx to calls/call/balx/bal as appropriate. */ - - /* Nexti could be zero if the called routine is volatile. */ - if (optimize && (*epilogue_string == 0) && argsize == 0 && tail_call_ok - && (nexti == 0 || GET_CODE (PATTERN (nexti)) == RETURN)) - { - /* Delete following return insn. */ - if (nexti && no_labels_between_p (insn, nexti)) - delete_insn (nexti); - output_asm_insn ("bx %0", operands); - return "# notreached"; - } - - output_asm_insn ("callx %0", operands); - - /* If the caller sets g14 to the address of the argblock, then the caller - must clear it after the return. */ - - if (current_function_args_size != 0 || varargs_stdarg_function) - output_asm_insn ("mov r3,g14", operands); - else if (argsize > 48) - output_asm_insn ("mov 0,g14", operands); - - return ""; -} - -/* Output code for a return insn. */ - -char * -i960_output_ret_insn (insn) - register rtx insn; -{ - static char lbuf[20]; - - if (*epilogue_string != 0) - { - if (! TARGET_CODE_ALIGN && next_real_insn (insn) == 0) - return ""; - - sprintf (lbuf, "b LR%d", ret_label); - return lbuf; - } - - /* Must clear g14 on return if this function set it. - Only varargs/stdarg functions modify g14. */ - - if (VARARGS_STDARG_FUNCTION (current_function_decl)) - output_asm_insn ("mov 0,g14", 0); - - if (i960_leaf_ret_reg >= 0) - { - sprintf (lbuf, "bx (%s)", reg_names[i960_leaf_ret_reg]); - return lbuf; - } - return "ret"; -} - -#if 0 -/* Return a character string representing the branch prediction - opcode to be tacked on an instruction. This must at least - return a null string. */ - -char * -i960_br_predict_opcode (lab_ref, insn) - rtx lab_ref, insn; -{ - if (TARGET_BRANCH_PREDICT) - { - unsigned long label_uid; - - if (GET_CODE (lab_ref) == CODE_LABEL) - label_uid = INSN_UID (lab_ref); - else if (GET_CODE (lab_ref) == LABEL_REF) - label_uid = INSN_UID (XEXP (lab_ref, 0)); - else - return ".f"; - - /* If not optimizing, then the insn_addresses array will not be - valid. In this case, always return ".t" since most branches - are taken. If optimizing, return .t for backward branches - and .f for forward branches. */ - if (! optimize - || insn_addresses[label_uid] < insn_addresses[INSN_UID (insn)]) - return ".t"; - return ".f"; - } - - return ""; -} -#endif - -/* Print the operand represented by rtx X formatted by code CODE. */ - -void -i960_print_operand (file, x, code) - FILE *file; - rtx x; - char code; -{ - enum rtx_code rtxcode = GET_CODE (x); - - if (rtxcode == REG) - { - switch (code) - { - case 'D': - /* Second reg of a double or quad. */ - fprintf (file, "%s", reg_names[REGNO (x)+1]); - break; - - case 'E': - /* Third reg of a quad. */ - fprintf (file, "%s", reg_names[REGNO (x)+2]); - break; - - case 'F': - /* Fourth reg of a quad. */ - fprintf (file, "%s", reg_names[REGNO (x)+3]); - break; - - case 0: - fprintf (file, "%s", reg_names[REGNO (x)]); - break; - - default: - abort (); - } - return; - } - else if (rtxcode == MEM) - { - output_address (XEXP (x, 0)); - return; - } - else if (rtxcode == CONST_INT) - { - HOST_WIDE_INT val = INTVAL (x); - if (code == 'C') - val = ~val; - if (val > 9999 || val < -999) - fprintf (file, "0x%x", val); - else - fprintf (file, "%d", val); - return; - } - else if (rtxcode == CONST_DOUBLE) - { - REAL_VALUE_TYPE d; - char dstr[30]; - - if (x == CONST0_RTX (GET_MODE (x))) - { - fprintf (file, "0f0.0"); - return; - } - else if (x == CONST1_RTX (GET_MODE (x))) - { - fprintf (file, "0f1.0"); - return; - } - - REAL_VALUE_FROM_CONST_DOUBLE (d, x); - REAL_VALUE_TO_DECIMAL (d, "%#g", dstr); - fprintf (file, "0f%s", dstr); - return; - } - - switch(code) - { - case 'B': - /* Branch or jump, depending on assembler. */ - if (TARGET_ASM_COMPAT) - fputs ("j", file); - else - fputs ("b", file); - break; - - case 'S': - /* Sign of condition. */ - if ((rtxcode == EQ) || (rtxcode == NE) || (rtxcode == GTU) - || (rtxcode == LTU) || (rtxcode == GEU) || (rtxcode == LEU)) - fputs ("o", file); - else if ((rtxcode == GT) || (rtxcode == LT) - || (rtxcode == GE) || (rtxcode == LE)) - fputs ("i", file); - else - abort(); - break; - - case 'I': - /* Inverted condition. */ - rtxcode = reverse_condition (rtxcode); - goto normal; - - case 'X': - /* Inverted condition w/ reversed operands. */ - rtxcode = reverse_condition (rtxcode); - /* Fallthrough. */ - - case 'R': - /* Reversed operand condition. */ - rtxcode = swap_condition (rtxcode); - /* Fallthrough. */ - - case 'C': - /* Normal condition. */ - normal: - if (rtxcode == EQ) { fputs ("e", file); return; } - else if (rtxcode == NE) { fputs ("ne", file); return; } - else if (rtxcode == GT) { fputs ("g", file); return; } - else if (rtxcode == GTU) { fputs ("g", file); return; } - else if (rtxcode == LT) { fputs ("l", file); return; } - else if (rtxcode == LTU) { fputs ("l", file); return; } - else if (rtxcode == GE) { fputs ("ge", file); return; } - else if (rtxcode == GEU) { fputs ("ge", file); return; } - else if (rtxcode == LE) { fputs ("le", file); return; } - else if (rtxcode == LEU) { fputs ("le", file); return; } - else abort (); - break; - - case 0: - output_addr_const (file, x); - break; - - default: - abort (); - } - - return; -} - -/* Print a memory address as an operand to reference that memory location. - - This is exactly the same as legitimate_address_p, except that it the prints - addresses instead of recognizing them. */ - -void -i960_print_operand_addr (file, addr) - FILE *file; - register rtx addr; -{ - rtx breg, ireg; - rtx scale, offset; - - ireg = 0; - breg = 0; - offset = 0; - scale = const1_rtx; - - if (GET_CODE (addr) == REG) - breg = addr; - else if (CONSTANT_P (addr)) - offset = addr; - else if (GET_CODE (addr) == PLUS) - { - rtx op0, op1; - - op0 = XEXP (addr, 0); - op1 = XEXP (addr, 1); - - if (GET_CODE (op0) == REG) - { - breg = op0; - if (GET_CODE (op1) == REG) - ireg = op1; - else if (CONSTANT_P (op1)) - offset = op1; - else - abort (); - } - else if (GET_CODE (op0) == PLUS) - { - if (GET_CODE (XEXP (op0, 0)) == MULT) - { - ireg = XEXP (XEXP (op0, 0), 0); - scale = XEXP (XEXP (op0, 0), 1); - if (GET_CODE (XEXP (op0, 1)) == REG) - { - breg = XEXP (op0, 1); - offset = op1; - } - else - abort (); - } - else if (GET_CODE (XEXP (op0, 0)) == REG) - { - breg = XEXP (op0, 0); - if (GET_CODE (XEXP (op0, 1)) == REG) - { - ireg = XEXP (op0, 1); - offset = op1; - } - else - abort (); - } - else - abort (); - } - else if (GET_CODE (op0) == MULT) - { - ireg = XEXP (op0, 0); - scale = XEXP (op0, 1); - if (GET_CODE (op1) == REG) - breg = op1; - else if (CONSTANT_P (op1)) - offset = op1; - else - abort (); - } - else - abort (); - } - else if (GET_CODE (addr) == MULT) - { - ireg = XEXP (addr, 0); - scale = XEXP (addr, 1); - } - else - abort (); - - if (offset) - output_addr_const (file, offset); - if (breg) - fprintf (file, "(%s)", reg_names[REGNO (breg)]); - if (ireg) - fprintf (file, "[%s*%d]", reg_names[REGNO (ireg)], INTVAL (scale)); -} - -/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression - that is a valid memory address for an instruction. - The MODE argument is the machine mode for the MEM expression - that wants to use this address. - - On 80960, legitimate addresses are: - base ld (g0),r0 - disp (12 or 32 bit) ld foo,r0 - base + index ld (g0)[g1*1],r0 - base + displ ld 0xf00(g0),r0 - base + index*scale + displ ld 0xf00(g0)[g1*4],r0 - index*scale + base ld (g0)[g1*4],r0 - index*scale + displ ld 0xf00[g1*4],r0 - index*scale ld [g1*4],r0 - index + base + displ ld 0xf00(g0)[g1*1],r0 - - In each case, scale can be 1, 2, 4, 8, or 16. */ - -/* This is exactly the same as i960_print_operand_addr, except that - it recognizes addresses instead of printing them. - - It only recognizes address in canonical form. LEGITIMIZE_ADDRESS should - convert common non-canonical forms to canonical form so that they will - be recognized. */ - -/* These two macros allow us to accept either a REG or a SUBREG anyplace - where a register is valid. */ - -#define RTX_OK_FOR_BASE_P(X, STRICT) \ - ((GET_CODE (X) == REG \ - && (STRICT ? REG_OK_FOR_BASE_P_STRICT (X) : REG_OK_FOR_BASE_P (X))) \ - || (GET_CODE (X) == SUBREG \ - && GET_CODE (SUBREG_REG (X)) == REG \ - && (STRICT ? REG_OK_FOR_BASE_P_STRICT (SUBREG_REG (X)) \ - : REG_OK_FOR_BASE_P (SUBREG_REG (X))))) - -#define RTX_OK_FOR_INDEX_P(X, STRICT) \ - ((GET_CODE (X) == REG \ - && (STRICT ? REG_OK_FOR_INDEX_P_STRICT (X) : REG_OK_FOR_INDEX_P (X)))\ - || (GET_CODE (X) == SUBREG \ - && GET_CODE (SUBREG_REG (X)) == REG \ - && (STRICT ? REG_OK_FOR_INDEX_P_STRICT (SUBREG_REG (X)) \ - : REG_OK_FOR_INDEX_P (SUBREG_REG (X))))) - -int -legitimate_address_p (mode, addr, strict) - enum machine_mode mode; - register rtx addr; - int strict; -{ - if (RTX_OK_FOR_BASE_P (addr, strict)) - return 1; - else if (CONSTANT_P (addr)) - return 1; - else if (GET_CODE (addr) == PLUS) - { - rtx op0, op1; - - if (! TARGET_COMPLEX_ADDR && ! reload_completed) - return 0; - - op0 = XEXP (addr, 0); - op1 = XEXP (addr, 1); - - if (RTX_OK_FOR_BASE_P (op0, strict)) - { - if (RTX_OK_FOR_INDEX_P (op1, strict)) - return 1; - else if (CONSTANT_P (op1)) - return 1; - else - return 0; - } - else if (GET_CODE (op0) == PLUS) - { - if (GET_CODE (XEXP (op0, 0)) == MULT) - { - if (! (RTX_OK_FOR_INDEX_P (XEXP (XEXP (op0, 0), 0), strict) - && SCALE_TERM_P (XEXP (XEXP (op0, 0), 1)))) - return 0; - - if (RTX_OK_FOR_BASE_P (XEXP (op0, 1), strict) - && CONSTANT_P (op1)) - return 1; - else - return 0; - } - else if (RTX_OK_FOR_BASE_P (XEXP (op0, 0), strict)) - { - if (RTX_OK_FOR_INDEX_P (XEXP (op0, 1), strict) - && CONSTANT_P (op1)) - return 1; - else - return 0; - } - else - return 0; - } - else if (GET_CODE (op0) == MULT) - { - if (! (RTX_OK_FOR_INDEX_P (XEXP (op0, 0), strict) - && SCALE_TERM_P (XEXP (op0, 1)))) - return 0; - - if (RTX_OK_FOR_BASE_P (op1, strict)) - return 1; - else if (CONSTANT_P (op1)) - return 1; - else - return 0; - } - else - return 0; - } - else if (GET_CODE (addr) == MULT) - { - if (! TARGET_COMPLEX_ADDR && ! reload_completed) - return 0; - - return (RTX_OK_FOR_INDEX_P (XEXP (addr, 0), strict) - && SCALE_TERM_P (XEXP (addr, 1))); - } - else - return 0; -} - -/* Try machine-dependent ways of modifying an illegitimate address - to be legitimate. If we find one, return the new, valid address. - This macro is used in only one place: `memory_address' in explow.c. - - This converts some non-canonical addresses to canonical form so they - can be recognized. */ - -rtx -legitimize_address (x, oldx, mode) - register rtx x; - register rtx oldx; - enum machine_mode mode; -{ - if (GET_CODE (x) == SYMBOL_REF) - { - abort (); - x = copy_to_reg (x); - } - - if (! TARGET_COMPLEX_ADDR && ! reload_completed) - return x; - - /* Canonicalize (plus (mult (reg) (const)) (plus (reg) (const))) - into (plus (plus (mult (reg) (const)) (reg)) (const)). This can be - created by virtual register instantiation, register elimination, and - similar optimizations. */ - if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT - && GET_CODE (XEXP (x, 1)) == PLUS) - x = gen_rtx (PLUS, Pmode, - gen_rtx (PLUS, Pmode, XEXP (x, 0), XEXP (XEXP (x, 1), 0)), - XEXP (XEXP (x, 1), 1)); - - /* Canonicalize (plus (plus (mult (reg) (const)) (plus (reg) (const))) const) - into (plus (plus (mult (reg) (const)) (reg)) (const)). */ - else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT - && GET_CODE (XEXP (XEXP (x, 0), 1)) == PLUS - && CONSTANT_P (XEXP (x, 1))) - { - rtx constant, other; - - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - { - constant = XEXP (x, 1); - other = XEXP (XEXP (XEXP (x, 0), 1), 1); - } - else if (GET_CODE (XEXP (XEXP (XEXP (x, 0), 1), 1)) == CONST_INT) - { - constant = XEXP (XEXP (XEXP (x, 0), 1), 1); - other = XEXP (x, 1); - } - else - constant = 0; - - if (constant) - x = gen_rtx (PLUS, Pmode, - gen_rtx (PLUS, Pmode, XEXP (XEXP (x, 0), 0), - XEXP (XEXP (XEXP (x, 0), 1), 0)), - plus_constant (other, INTVAL (constant))); - } - - return x; -} - -#if 0 -/* Return the most stringent alignment that we are willing to consider - objects of size SIZE and known alignment ALIGN as having. */ - -int -i960_alignment (size, align) - int size; - int align; -{ - int i; - - if (! TARGET_STRICT_ALIGN) - if (TARGET_IC_COMPAT2_0 || align >= 4) - { - i = i960_object_bytes_bitalign (size) / BITS_PER_UNIT; - if (i > align) - align = i; - } - - return align; -} -#endif - - -int -hard_regno_mode_ok (regno, mode) - int regno; - enum machine_mode mode; -{ - if (regno < 32) - { - switch (mode) - { - case CCmode: case CC_UNSmode: case CC_CHKmode: - return 0; - - case DImode: case DFmode: - return (regno & 1) == 0; - - case TImode: case XFmode: - return (regno & 3) == 0; - - default: - return 1; - } - } - else if (regno >= 32 && regno < 36) - { - switch (mode) - { - case SFmode: case DFmode: case XFmode: - case SCmode: case DCmode: - return 1; - - default: - return 0; - } - } - else if (regno == 36) - { - switch (mode) - { - case CCmode: case CC_UNSmode: case CC_CHKmode: - return 1; - - default: - return 0; - } - } - else if (regno == 37) - return 0; - - abort (); -} - - -/* Return the minimum alignment of an expression rtx X in bytes. This takes - advantage of machine specific facts, such as knowing that the frame pointer - is always 16 byte aligned. */ - -int -i960_expr_alignment (x, size) - rtx x; - int size; -{ - int align = 1; - - if (x == 0) - return 1; - - switch (GET_CODE(x)) - { - case CONST_INT: - align = INTVAL(x); - - if ((align & 0xf) == 0) - align = 16; - else if ((align & 0x7) == 0) - align = 8; - else if ((align & 0x3) == 0) - align = 4; - else if ((align & 0x1) == 0) - align = 2; - else - align = 1; - break; - - case PLUS: - align = MIN (i960_expr_alignment (XEXP (x, 0), size), - i960_expr_alignment (XEXP (x, 1), size)); - break; - - case SYMBOL_REF: - /* If this is a valid program, objects are guaranteed to be - correctly aligned for whatever size the reference actually is. */ - align = i960_object_bytes_bitalign (size) / BITS_PER_UNIT; - break; - - case REG: - if (REGNO (x) == FRAME_POINTER_REGNUM) - align = 16; - break; - - case ASHIFT: - align = i960_expr_alignment (XEXP (x, 0)); - - if (GET_CODE (XEXP (x, 1)) == CONST_INT) - { - align = align << INTVAL (XEXP (x, 1)); - align = MIN (align, 16); - } - break; - - case MULT: - align = (i960_expr_alignment (XEXP (x, 0), size) * - i960_expr_alignment (XEXP (x, 1), size)); - - align = MIN (align, 16); - break; - } - - return align; -} - -/* Return true if it is possible to reference both BASE and OFFSET, which - have alignment at least as great as 4 byte, as if they had alignment valid - for an object of size SIZE. */ - -int -i960_improve_align (base, offset, size) - rtx base; - rtx offset; - int size; -{ - int i, j; - - /* We have at least a word reference to the object, so we know it has to - be aligned at least to 4 bytes. */ - - i = MIN (i960_expr_alignment (base, 4), - i960_expr_alignment (offset, 4)); - - i = MAX (i, 4); - - /* We know the size of the request. If strict align is not enabled, we - can guess that the alignment is OK for the requested size. */ - - if (! TARGET_STRICT_ALIGN) - if ((j = (i960_object_bytes_bitalign (size) / BITS_PER_UNIT)) > i) - i = j; - - return (i >= size); -} - -/* Return true if it is possible to access BASE and OFFSET, which have 4 byte - (SImode) alignment as if they had 16 byte (TImode) alignment. */ - -int -i960_si_ti (base, offset) - rtx base; - rtx offset; -{ - return i960_improve_align (base, offset, 16); -} - -/* Return true if it is possible to access BASE and OFFSET, which have 4 byte - (SImode) alignment as if they had 8 byte (DImode) alignment. */ - -int -i960_si_di (base, offset) - rtx base; - rtx offset; -{ - return i960_improve_align (base, offset, 8); -} - -/* Return raw values of size and alignment (in words) for the data - type being accessed. These values will be rounded by the caller. */ - -static void -i960_arg_size_and_align (mode, type, size_out, align_out) - enum machine_mode mode; - tree type; - int *size_out; - int *align_out; -{ - int size, align; - - /* Use formal alignment requirements of type being passed, except make - it at least a word. If we don't have a type, this is a library call, - and the parm has to be of scalar type. In this case, consider its - formal alignment requirement to be its size in words. */ - - if (mode == BLKmode) - size = (int_size_in_bytes (type) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; - else if (mode == VOIDmode) - { - /* End of parm list. */ - if (type == 0 || TYPE_MODE (type) != VOIDmode) - abort (); - size = 1; - } - else - size = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; - - if (type == 0) - { - /* ??? This is a hack to properly correct the alignment of XFmode - values without affecting anything else. */ - if (size == 3) - align = 4; - else - align = size; - } - else if (TYPE_ALIGN (type) >= BITS_PER_WORD) - align = TYPE_ALIGN (type) / BITS_PER_WORD; - else - align = 1; - - *size_out = size; - *align_out = align; -} - -/* On the 80960 the first 12 args are in registers and the rest are pushed. - Any arg that is bigger than 4 words is placed on the stack and all - subsequent arguments are placed on the stack. - - Additionally, parameters with an alignment requirement stronger than - a word must be aligned appropriately. Note that this means that a - 64 bit object with a 32 bit alignment is not 64 bit aligned and may be - passed in an odd/even register pair. */ - -/* Update CUM to advance past an argument described by MODE and TYPE. */ - -void -i960_function_arg_advance (cum, mode, type, named) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int named; -{ - int size, align; - - i960_arg_size_and_align (mode, type, &size, &align); - - if (size > 4 || cum->ca_nstackparms != 0 - || (size + ROUND_PARM (cum->ca_nregparms, align)) > NPARM_REGS - || MUST_PASS_IN_STACK (mode, type)) - { - /* Indicate that all the registers are in use, even if all are not, - so va_start will compute the right value. */ - cum->ca_nregparms = NPARM_REGS; - cum->ca_nstackparms = ROUND_PARM (cum->ca_nstackparms, align) + size; - } - else - cum->ca_nregparms = ROUND_PARM (cum->ca_nregparms, align) + size; -} - -/* Return the register that the argument described by MODE and TYPE is - passed in, or else return 0 if it is passed on the stack. */ - -rtx -i960_function_arg (cum, mode, type, named) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int named; -{ - rtx ret; - int size, align; - - i960_arg_size_and_align (mode, type, &size, &align); - - if (size > 4 || cum->ca_nstackparms != 0 - || (size + ROUND_PARM (cum->ca_nregparms, align)) > NPARM_REGS - || MUST_PASS_IN_STACK (mode, type)) - { - cum->ca_nstackparms = ROUND_PARM (cum->ca_nstackparms, align); - ret = 0; - } - else - { - cum->ca_nregparms = ROUND_PARM (cum->ca_nregparms, align); - ret = gen_rtx (REG, mode, cum->ca_nregparms); - } - - return ret; -} - -/* Floating-point support. */ - -void -i960_output_long_double (file, value) - FILE *file; - REAL_VALUE_TYPE value; -{ - long value_long[3]; - char dstr[30]; - - REAL_VALUE_TO_TARGET_LONG_DOUBLE (value, value_long); - REAL_VALUE_TO_DECIMAL (value, "%.20g", dstr); - - fprintf (file, - "\t.word\t0x%08lx\t\t# %s\n\t.word\t0x%08lx\n\t.word\t0x%08lx\n", - value_long[0], dstr, value_long[1], value_long[2]); - fprintf (file, "\t.word\t0x0\n"); -} - -void -i960_output_double (file, value) - FILE *file; - REAL_VALUE_TYPE value; -{ - long value_long[2]; - char dstr[30]; - - REAL_VALUE_TO_TARGET_DOUBLE (value, value_long); - REAL_VALUE_TO_DECIMAL (value, "%.20g", dstr); - - fprintf (file, "\t.word\t0x%08lx\t\t# %s\n\t.word\t0x%08lx\n", - value_long[0], dstr, value_long[1]); -} - -void -i960_output_float (file, value) - FILE *file; - REAL_VALUE_TYPE value; -{ - long value_long; - char dstr[30]; - - REAL_VALUE_TO_TARGET_SINGLE (value, value_long); - REAL_VALUE_TO_DECIMAL (value, "%.12g", dstr); - - fprintf (file, "\t.word\t0x%08lx\t\t# %s (float)\n", value_long, dstr); -} - -/* Return the number of bits that an object of size N bytes is aligned to. */ - -int -i960_object_bytes_bitalign (n) - int n; -{ - if (n > 8) n = 128; - else if (n > 4) n = 64; - else if (n > 2) n = 32; - else if (n > 1) n = 16; - else n = 8; - - return n; -} - -/* Compute the alignment for an aggregate type TSIZE. - Alignment is MAX (greatest member alignment, - MIN (pragma align, structure size alignment)). */ - -int -i960_round_align (align, tsize) - int align; - tree tsize; -{ - int new_align; - - if (TREE_CODE (tsize) != INTEGER_CST) - return align; - - new_align = i960_object_bytes_bitalign (TREE_INT_CST_LOW (tsize) - / BITS_PER_UNIT); - /* Handle #pragma align. */ - if (new_align > i960_maxbitalignment) - new_align = i960_maxbitalignment; - - if (align < new_align) - align = new_align; - - return align; -} - -/* Do any needed setup for a varargs function. For the i960, we must - create a register parameter block if one doesn't exist, and then copy - all register parameters to memory. */ - -void -i960_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int *pretend_size; - int no_rtl; -{ - /* Note: for a varargs fn with only a va_alist argument, this is 0. */ - int first_reg = cum->ca_nregparms; - - /* Copy only unnamed register arguments to memory. If there are - any stack parms, there are no unnamed arguments in registers, and - an argument block was already allocated by the caller. - Remember that any arg bigger than 4 words is passed on the stack as - are all subsequent args. - - If there are no stack arguments but there are exactly NPARM_REGS - registers, either there were no extra arguments or the caller - allocated an argument block. */ - - if (cum->ca_nstackparms == 0 && first_reg < NPARM_REGS && !no_rtl) - { - rtx label = gen_label_rtx (); - rtx regblock; - - /* If arg_pointer_rtx == 0, no arguments were passed on the stack - and we need to allocate a chunk to save the registers (if any - arguments were passed on the stack the caller would allocate the - 48 bytes as well). We must allocate all 48 bytes (12*4) because - va_start assumes it. */ - emit_insn (gen_cmpsi (arg_pointer_rtx, const0_rtx)); - emit_jump_insn (gen_bne (label)); - emit_insn (gen_rtx (SET, VOIDmode, arg_pointer_rtx, - stack_pointer_rtx)); - emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx, - memory_address (SImode, - plus_constant (stack_pointer_rtx, - 48)))); - emit_label (label); - - /* ??? Note that we unnecessarily store one extra register for stdarg - fns. We could optimize this, but it's kept as for now. */ - regblock = gen_rtx (MEM, BLKmode, - plus_constant (arg_pointer_rtx, - first_reg * 4)); - move_block_from_reg (first_reg, regblock, - NPARM_REGS - first_reg, - (NPARM_REGS - first_reg) * UNITS_PER_WORD); - } -} - -/* Calculate the final size of the reg parm stack space for the current - function, based on how many bytes would be allocated on the stack. */ - -int -i960_final_reg_parm_stack_space (const_size, var_size) - int const_size; - tree var_size; -{ - if (var_size || const_size > 48) - return 48; - else - return 0; -} - -/* Calculate the size of the reg parm stack space. This is a bit complicated - on the i960. */ - -int -i960_reg_parm_stack_space (fndecl) - tree fndecl; -{ - /* In this case, we are called from emit_library_call, and we don't need - to pretend we have more space for parameters than what's apparent. */ - if (fndecl == 0) - return 0; - - /* In this case, we are called from locate_and_pad_parms when we're - not IN_REGS, so we have an arg block. */ - if (fndecl != current_function_decl) - return 48; - - /* Otherwise, we have an arg block if the current function has more than - 48 bytes of parameters. */ - if (current_function_args_size != 0 || VARARGS_STDARG_FUNCTION (fndecl)) - return 48; - else - return 0; -} - -/* 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 = -1; - - if (GET_CODE (in) == REG || GET_CODE (in) == SUBREG) - regno = true_regnum (in); - - /* We can place anything into LOCAL_OR_GLOBAL_REGS and can put - LOCAL_OR_GLOBAL_REGS into anything. */ - if (class == LOCAL_OR_GLOBAL_REGS || class == LOCAL_REGS - || class == GLOBAL_REGS || (regno >= 0 && regno < 32)) - return NO_REGS; - - /* We can place any hard register, 0.0, and 1.0 into FP_REGS. */ - if (class == FP_REGS - && ((regno >= 0 && regno < FIRST_PSEUDO_REGISTER) - || in == CONST0_RTX (mode) || in == CONST1_RTX (mode))) - return NO_REGS; - - return LOCAL_OR_GLOBAL_REGS; -} - -/* Look at the opcode P, and set i96_last_insn_type to indicate which - function unit it executed on. */ - -/* ??? This would make more sense as an attribute. */ - -void -i960_scan_opcode (p) - char *p; -{ - switch (*p) - { - case 'a': - case 'd': - case 'e': - case 'm': - case 'n': - case 'o': - case 'r': - /* Ret is not actually of type REG, but it won't matter, because no - insn will ever follow it. */ - case 'u': - case 'x': - i960_last_insn_type = I_TYPE_REG; - break; - - case 'b': - if (p[1] == 'x' || p[3] == 'x') - i960_last_insn_type = I_TYPE_MEM; - i960_last_insn_type = I_TYPE_CTRL; - break; - - case 'f': - case 't': - i960_last_insn_type = I_TYPE_CTRL; - break; - - case 'c': - if (p[1] == 'a') - { - if (p[4] == 'x') - i960_last_insn_type = I_TYPE_MEM; - else - i960_last_insn_type = I_TYPE_CTRL; - } - else if (p[1] == 'm') - { - if (p[3] == 'd') - i960_last_insn_type = I_TYPE_REG; - else if (p[4] == 'b' || p[4] == 'j') - i960_last_insn_type = I_TYPE_CTRL; - else - i960_last_insn_type = I_TYPE_REG; - } - else - i960_last_insn_type = I_TYPE_REG; - break; - - case 'l': - i960_last_insn_type = I_TYPE_MEM; - break; - - case 's': - if (p[1] == 't') - i960_last_insn_type = I_TYPE_MEM; - else - i960_last_insn_type = I_TYPE_REG; - break; - } -} - - -/* CYGNUS LOCAL -- branch prediction */ -int -i960_branch_predict_p (reverse, comparison, expected, value) - int reverse; - rtx comparison; - rtx expected; - rtx value; -{ - HOST_WIDE_INT s_exp; - HOST_WIDE_INT s_val; - unsigned HOST_WIDE_INT u_exp; - unsigned HOST_WIDE_INT u_val; - int ret = 0; - - if (GET_CODE (expected) != CONST_INT || GET_CODE (value) != CONST_INT) - abort (); - - u_exp = s_exp = INTVAL (expected); - u_val = s_val = INTVAL (value); - switch (GET_CODE (comparison)) { - default: - abort (); - - case EQ: ret = (s_exp == s_val); break; - case NE: ret = (s_exp != s_val); break; - case LT: ret = (s_exp < s_val); break; - case LE: ret = (s_exp <= s_val); break; - case GT: ret = (s_exp > s_val); break; - case GE: ret = (s_exp >= s_val); break; - case LTU: ret = (u_exp < u_val); break; - case LEU: ret = (u_exp <= u_val); break; - case GTU: ret = (u_exp > u_val); break; - case GEU: ret = (u_exp >= u_val); break; - } - - return ret; -} -/* END CYGNUS LOCAL -- branch prediction */ |