📄 sparc.c
字号:
/* Subroutines for insn-output.c for Sun SPARC. Copyright (C) 1987, 88, 89, 92, 93, 94, 1995 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) 64 bit SPARC V9 support by Michael Tiemann, Jim Wilson, and Doug Evans, at Cygnus Support.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include <stdio.h>#include "config.h"#include "tree.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 "expr.h"#include "recog.h"/* 1 if the caller has placed an "unimp" insn immediately after the call. This is used in v8 code when calling a function that returns a structure. v9 doesn't have this. */#define SKIP_CALLERS_UNIMP_P (!TARGET_V9 && current_function_returns_struct)/* Global variables for machine-dependent things. *//* Says what architecture we're compiling for. */enum arch_type sparc_arch_type;/* Size of frame. Need to know this to emit return insns from leaf procedures. ACTUAL_FSIZE is set by compute_frame_size() which is called during the reload pass. This is important as the value is later used in insn scheduling (to see what can go in a delay slot). APPARENT_FSIZE is the size of the stack less the register save area and less the outgoing argument area. It is used when saving call preserved regs. */static int apparent_fsize;static int actual_fsize;/* Save the operands last given to a compare for use when we generate a scc or bcc insn. */rtx sparc_compare_op0, sparc_compare_op1;/* Count of named arguments (v9 only). ??? INIT_CUMULATIVE_ARGS initializes these, and FUNCTION_ARG_ADVANCE increments SPARC_ARG_COUNT. They are then used by FUNCTION_ARG_CALLEE_COPIES to determine if the argument is really a named argument or not. This hack is necessary because the NAMED argument to the FUNCTION_ARG_XXX macros is not what it says it is: it does not include the last named argument. */int sparc_arg_count;int sparc_n_named_args;/* We may need an epilogue if we spill too many registers. If this is non-zero, then we branch here for the epilogue. */static rtx leaf_label;#ifdef LEAF_REGISTERS/* Vector to say how input registers are mapped to output registers. FRAME_POINTER_REGNUM cannot be remapped by this function to eliminate it. You must use -fomit-frame-pointer to get that. */char leaf_reg_remap[] ={ 0, 1, 2, 3, 4, 5, 6, 7, -1, -1, -1, -1, -1, -1, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 9, 10, 11, 12, 13, -1, 15, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};#endif/* Name of where we pretend to think the frame pointer points. Normally, this is "%fp", but if we are in a leaf procedure, this is "%sp+something". We record "something" separately as it may be too big for reg+constant addressing. */static char *frame_base_name;static int frame_base_offset;static rtx find_addr_reg ();static void sparc_init_modes ();/* Option handling. *//* Validate and override various options, and do some machine dependent initialization. */voidsparc_override_options (){ /* Check for any conflicts in the choice of options. */ /* ??? This stuff isn't really usable yet. */ if (! TARGET_V9) { if (target_flags & MASK_CODE_MODEL) error ("code model support is only available with -mv9"); if (TARGET_INT64) error ("-mint64 is only available with -mv9"); if (TARGET_LONG64) error ("-mlong64 is only available with -mv9"); if (TARGET_PTR64) error ("-mptr64 is only available with -mv9"); if (TARGET_ENV32) error ("-menv32 is only available with -mv9"); if (TARGET_STACK_BIAS) error ("-mstack-bias is only available with -mv9"); } else { /* ??? Are there any options that aren't usable with v9. -munaligned-doubles? */ } /* Check for conflicts in cpu specification. If we use -mcpu=xxx, this can be removed. */ if ((TARGET_V8 != 0) + (TARGET_SPARCLITE != 0) + (TARGET_V9 != 0) > 1) error ("conflicting architectures defined"); /* Do various machine dependent initializations. */ sparc_init_modes ();}/* Float conversions (v9 only). The floating point registers cannot hold DImode values because SUBREG's on them get the wrong register. "(subreg:SI (reg:DI M int-reg) 0)" is the same as "(subreg:SI (reg:DI N float-reg) 1)", but gcc doesn't know how to turn the "0" to a "1". Therefore, we must explicitly do the conversions to/from int/fp regs. `sparc64_fpconv_stack_slot' is the address of an 8 byte stack slot used during the transfer. ??? I could have used [%fp-16] but I didn't want to add yet another dependence on this. *//* ??? Can we use assign_stack_temp here? */static rtx fpconv_stack_temp;/* Called once for each function. */voidsparc64_init_expanders (){ fpconv_stack_temp = NULL_RTX;}/* Assign a stack temp for fp/int DImode conversions. */rtxsparc64_fpconv_stack_temp (){ if (fpconv_stack_temp == NULL_RTX) fpconv_stack_temp = assign_stack_local (DImode, GET_MODE_SIZE (DImode), 0); return fpconv_stack_temp;}/* Miscellaneous utilities. *//* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move or branch on register contents instructions. */intv9_regcmp_p (code) enum rtx_code code;{ return (code == EQ || code == NE || code == GE || code == LT || code == LE || code == GT);}/* Operand constraints. *//* Return non-zero only if OP is a register of mode MODE, or const0_rtx. */intreg_or_0_operand (op, mode) rtx op; enum machine_mode mode;{ if (op == const0_rtx || register_operand (op, mode)) return 1; if (GET_MODE (op) == VOIDmode && GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0) return 1; if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT && GET_CODE (op) == CONST_DOUBLE && fp_zero_operand (op)) return 1; return 0;}/* Nonzero if OP is a floating point value with value 0.0. */intfp_zero_operand (op) rtx op;{ REAL_VALUE_TYPE r; REAL_VALUE_FROM_CONST_DOUBLE (r, op); return REAL_VALUES_EQUAL (r, dconst0);}/* Nonzero if OP is an integer register. */intintreg_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, SImode) || (TARGET_V9 && register_operand (op, DImode)));}/* Nonzero if OP is a floating point condition code register. */intccfp_reg_operand (op, mode) rtx op; enum machine_mode mode;{ /* This can happen when recog is called from combine. Op may be a MEM. Fail instead of calling abort in this case. */ if (GET_CODE (op) != REG || REGNO (op) == 0) return 0; if (GET_MODE (op) != mode) return 0;#if 0 /* ??? ==> 1 when %fcc1-3 are pseudos first. See gen_compare_reg(). */ if (reg_renumber == 0) return REGNO (op) >= FIRST_PSEUDO_REGISTER; return REGNO_OK_FOR_CCFP_P (REGNO (op));#else return (unsigned) REGNO (op) - 96 < 4;#endif}/* Nonzero if OP can appear as the dest of a RESTORE insn. */intrestore_operand (op, mode) rtx op; enum machine_mode mode;{ return (GET_CODE (op) == REG && GET_MODE (op) == mode && (REGNO (op) < 8 || (REGNO (op) >= 24 && REGNO (op) < 32)));}/* Call insn on SPARC can take a PC-relative constant address, or any regular memory address. */intcall_operand (op, mode) rtx op; enum machine_mode mode;{ if (GET_CODE (op) != MEM) abort (); op = XEXP (op, 0); return (symbolic_operand (op, mode) || memory_address_p (Pmode, op));}intcall_operand_address (op, mode) rtx op; enum machine_mode mode;{ return (symbolic_operand (op, mode) || memory_address_p (Pmode, op));}/* Returns 1 if OP is either a symbol reference or a sum of a symbol reference and a constant. */intsymbolic_operand (op, mode) register rtx op; enum machine_mode mode;{ switch (GET_CODE (op)) { case SYMBOL_REF: case LABEL_REF: return 1; case CONST: op = XEXP (op, 0); return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF || GET_CODE (XEXP (op, 0)) == LABEL_REF) && GET_CODE (XEXP (op, 1)) == CONST_INT); /* ??? This clause seems to be irrelevant. */ case CONST_DOUBLE: return GET_MODE (op) == mode; default: return 0; }}/* Return truth value of statement that OP is a symbolic memory operand of mode MODE. */intsymbolic_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 1 if the operand is a data segment reference. This includes the readonly data segment, or in other words anything but the text segment. This is needed in the medium/anywhere code model on v9. These values are accessed with MEDANY_BASE_REG. */intdata_segment_operand (op, mode) rtx op; enum machine_mode mode;{ switch (GET_CODE (op)) { case SYMBOL_REF : return ! SYMBOL_REF_FLAG (op); case PLUS : /* Assume canonical format of symbol + constant. */ case CONST : return data_segment_operand (XEXP (op, 0)); default : return 0; }}/* Return 1 if the operand is a text segment reference. This is needed in the medium/anywhere code model on v9. */inttext_segment_operand (op, mode) rtx op; enum machine_mode mode;{ switch (GET_CODE (op)) { case LABEL_REF : return 1; case SYMBOL_REF : return SYMBOL_REF_FLAG (op); case PLUS : /* Assume canonical format of symbol + constant. */ case CONST : return text_segment_operand (XEXP (op, 0)); default : return 0; }}/* Return 1 if the operand is either a register or a memory operand that is not symbolic. */intreg_or_nonsymb_mem_operand (op, mode) register rtx op; enum machine_mode mode;{ if (register_operand (op, mode)) return 1; if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode)) return 1; return 0;}intsparc_operand (op, mode) rtx op; enum machine_mode mode;{ if (register_operand (op, mode)) return 1; if (GET_CODE (op) == CONST_INT) return SMALL_INT (op); if (GET_MODE (op) != mode) return 0; if (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); if (GET_CODE (op) != MEM) return 0; op = XEXP (op, 0); if (GET_CODE (op) == LO_SUM) return (GET_CODE (XEXP (op, 0)) == REG && symbolic_operand (XEXP (op, 1), Pmode)); return memory_address_p (mode, op);}intmove_operand (op, mode) rtx op; enum machine_mode mode;{ if (mode == DImode && arith_double_operand (op, mode)) return 1; if (register_operand (op, mode)) return 1; if (GET_CODE (op) == CONST_INT) return (SMALL_INT (op) || (INTVAL (op) & 0x3ff) == 0); if (GET_MODE (op) != mode) return 0; if (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); if (GET_CODE (op) != MEM) return 0; op = XEXP (op, 0); if (GET_CODE (op) == LO_SUM) return (register_operand (XEXP (op, 0), Pmode) && CONSTANT_P (XEXP (op, 1))); return memory_address_p (mode, op);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -