📄 i960.c
字号:
/* Subroutines used for code generation on intel 80960. Copyright (C) 1992, 1995, 1996, 1997, 1998, 1999, 2000, 2001 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 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 "config.h"#include "system.h"#include <math.h>#include "rtl.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "output.h"#include "insn-attr.h"#include "flags.h"#include "tree.h"#include "expr.h"#include "except.h"#include "function.h"#include "recog.h"#include "toplev.h"#include "tm_p.h"#include "target.h"#include "target-def.h"static void i960_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));static void i960_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));static void i960_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree));/* 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. */int i960_maxbitalignment;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)/* Initialize the GCC target structure. */#undef TARGET_ASM_ALIGNED_SI_OP#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"#undef TARGET_ASM_FUNCTION_PROLOGUE#define TARGET_ASM_FUNCTION_PROLOGUE i960_output_function_prologue#undef TARGET_ASM_FUNCTION_EPILOGUE#define TARGET_ASM_FUNCTION_EPILOGUE i960_output_function_epilogue#undef TARGET_ASM_OUTPUT_MI_THUNK#define TARGET_ASM_OUTPUT_MI_THUNK i960_output_mi_thunk#undef TARGET_CAN_ASM_OUTPUT_MI_THUNK#define TARGET_CAN_ASM_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcallstruct gcc_target targetm = TARGET_INITIALIZER;/* Override conflicting target switch options. Doesn't actually detect if more than one -mARCH option is given, but does handle the case of two blatantly conflicting -mARCH options. Also initialize variables before compiling any files. */voidi960_initialize (){ if (TARGET_K_SERIES && TARGET_C_SERIES) { warning ("conflicting architectures defined - using C series"); target_flags &= ~TARGET_FLAG_K_SERIES; } if (TARGET_K_SERIES && TARGET_MC) { warning ("conflicting architectures defined - using K series"); target_flags &= ~TARGET_FLAG_MC; } if (TARGET_C_SERIES && TARGET_MC) { warning ("conflicting architectures defined - using C series"); target_flags &= ~TARGET_FLAG_MC; } if (TARGET_IC_COMPAT3_0) { flag_short_enums = 1; flag_signed_char = 1; target_flags |= TARGET_FLAG_CLEAN_LINKAGE; if (TARGET_IC_COMPAT2_0) { warning ("iC2.0 and iC3.0 are incompatible - using iC3.0"); target_flags &= ~TARGET_FLAG_IC_COMPAT2_0; } } if (TARGET_IC_COMPAT2_0) { flag_signed_char = 1; target_flags |= TARGET_FLAG_CLEAN_LINKAGE; } if (TARGET_IC_COMPAT2_0) { i960_maxbitalignment = 8; i960_last_maxbitalignment = 128; } else { i960_maxbitalignment = 128; i960_last_maxbitalignment = 8; } /* Tell the compiler which flavor of TFmode we're using. */ real_format_for_mode[TFmode - QFmode] = &ieee_extended_intel_128_format;}/* Return true if OP can be used as the source of an fp move insn. */intfpmove_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. */intreg_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. */intarith_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. */intlogic_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. */intfp_arith_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || fp_literal (op, mode));}/* Return true if OP is a register or a valid signed integer literal. */intsigned_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 an integer which fits the range constraining immediate operands in three-address insns. */intliteral (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return ((GET_CODE (op) == CONST_INT) && INTVAL(op) >= 0 && INTVAL(op) < 32);}/* Return true if OP is a float constant of 1. */intfp_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. */intfp_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. */intfp_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. */intsigned_literal(op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ 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. */intsymbolic_memory_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ 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. */inteq_or_neq (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return (GET_CODE (op) == EQ || GET_CODE (op) == NE);}/* OP is an integer register or a constant. */intarith32_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. */intpower2_operand (op,mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ 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. */intcmplpower2_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ 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. */intbitpos (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 nonzero if OP is a mask, i.e. all one bits are consecutive. The return value indicates how many consecutive nonzero 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. */intis_mask (val) unsigned int val;{ register int start, end = 0, 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. */intbitstr (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_modeselect_cc_mode (op, x) RTX_CODE op; rtx x ATTRIBUTE_UNUSED;{ 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. */rtxgen_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. */inti960_address_cost (x) rtx x;{#if 0 /* Handled before calling here. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -