📄 rs6000.c
字号:
/* Subroutines used for code generation on IBM RS/6000. Copyright (C) 1991 Free Software Foundation, Inc. Contributed by Richard Kenner (kenner@nyu.edu)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, 675 Mass Ave, Cambridge, MA 02139, USA. */#include <stdio.h>#include "config.h"#include "rtl.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "insn-flags.h"#include "output.h"#include "insn-attr.h"#include "flags.h"#include "recog.h"#include "expr.h"#include "obstack.h"#include "tree.h"extern char *language_string;#define min(A,B) ((A) < (B) ? (A) : (B))#define max(A,B) ((A) > (B) ? (A) : (B))/* Set to non-zero by "fix" operation to indicate that itrunc and uitrunc must be defined. */int rs6000_trunc_used;/* Set to non-zero once they have been defined. */static int trunc_defined;/* Save information from a "cmpxx" operation until the branch or scc is emitted. */rtx rs6000_compare_op0, rs6000_compare_op1;int rs6000_compare_fp_p;/* Return non-zero if this function is known to have a null epilogue. */intdirect_return (){ return (reload_completed && first_reg_to_save () == 32 && first_fp_reg_to_save () == 64 && ! regs_ever_live[65] && ! rs6000_pushes_stack ());}/* Returns 1 always. */intany_operand (op, mode) register rtx op; enum machine_mode mode;{ return 1;}/* Return 1 if OP is a constant that can fit in a D field. */intshort_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return (GET_CODE (op) == CONST_INT && (unsigned) (INTVAL (op) + 0x8000) < 0x10000);}/* Similar for a unsigned D field. */intu_short_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) == 0);}/* Return 1 if OP is a CONST_INT that cannot fit in a signed D field. */intnon_short_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return (GET_CODE (op) == CONST_INT && (unsigned) (INTVAL (op) + 0x8000) >= 0x10000);}/* Returns 1 if OP is a register that is not special (i.e., not MQ, ctr, or lr). */intgpc_reg_operand (op, mode) register rtx op; enum machine_mode mode;{ return (register_operand (op, mode) && (GET_CODE (op) != REG || REGNO (op) >= 67 || REGNO (op) < 64));}/* Returns 1 if OP is either a pseudo-register or a register denoting a CR field. */intcc_reg_operand (op, mode) register rtx op; enum machine_mode mode;{ return (register_operand (op, mode) && (GET_CODE (op) != REG || REGNO (op) >= FIRST_PSEUDO_REGISTER || CR_REGNO_P (REGNO (op))));}/* Returns 1 if OP is either a constant integer valid for a D-field or a non-special register. If a register, it must be in the proper mode unless MODE is VOIDmode. */intreg_or_short_operand (op, mode) register rtx op; enum machine_mode mode;{ if (GET_CODE (op) == CONST_INT) return short_cint_operand (op, mode); return gpc_reg_operand (op, mode);}/* Similar, except check if the negation of the constant would be valid for a D-field. */intreg_or_neg_short_operand (op, mode) register rtx op; enum machine_mode mode;{ if (GET_CODE (op) == CONST_INT) return CONST_OK_FOR_LETTER_P (INTVAL (op), 'P'); return gpc_reg_operand (op, mode);}/* Return 1 if the operand is either a register or an integer whose high-order 16 bits are zero. */intreg_or_u_short_operand (op, mode) register rtx op; enum machine_mode mode;{ if (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) == 0) return 1; return gpc_reg_operand (op, mode);}/* Return 1 is the operand is either a non-special register or ANY constant integer. */intreg_or_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return GET_CODE (op) == CONST_INT || gpc_reg_operand (op, mode);}/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a register with one instruction per word. For SFmode, this means that the low 16-bits are zero. For DFmode, it means the low 16-bits of the first word are zero and the high 16 bits of the second word are zero (usually all bits in the low-order word will be zero). We only do this if we can safely read CONST_DOUBLE_{LOW,HIGH}. */inteasy_fp_constant (op, mode) register rtx op; register enum machine_mode mode;{ rtx low, high; if (GET_CODE (op) != CONST_DOUBLE || GET_MODE (op) != mode || GET_MODE_CLASS (mode) != MODE_FLOAT) return 0; high = operand_subword (op, 0, 0, mode); low = operand_subword (op, 1, 0, mode); if (high == 0 || GET_CODE (high) != CONST_INT || (INTVAL (high) & 0xffff)) return 0; return (mode == SFmode || (low != 0 && GET_CODE (low) == CONST_INT && (INTVAL (low) & 0xffff0000) == 0));} /* Return 1 if the operand is either a floating-point register, a pseudo register, or memory. */intfp_reg_or_mem_operand (op, mode) register rtx op; enum machine_mode mode;{ return (memory_operand (op, mode) || (register_operand (op, mode) && (GET_CODE (op) != REG || REGNO (op) >= FIRST_PSEUDO_REGISTER || FP_REGNO_P (REGNO (op)))));}/* Return 1 if the operand is either an easy FP constant (see above) or memory. */intmem_or_easy_const_operand (op, mode) register rtx op; enum machine_mode mode;{ return memory_operand (op, mode) || easy_fp_constant (op, mode);}/* Return 1 if the operand is either a non-special register or an item that can be used as the operand of an SI add insn. */intadd_operand (op, mode) register rtx op; enum machine_mode mode;{ return (reg_or_short_operand (op, mode) || (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff) == 0));}/* Return 1 if OP is a constant but not a valid add_operand. */intnon_add_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return (GET_CODE (op) == CONST_INT && (unsigned) (INTVAL (op) + 0x8000) >= 0x10000 && (INTVAL (op) & 0xffff) != 0);}/* Return 1 if the operand is a non-special register or a constant that can be used as the operand of an OR or XOR insn on the RS/6000. */intlogical_operand (op, mode) register rtx op; enum machine_mode mode;{ return (gpc_reg_operand (op, mode) || (GET_CODE (op) == CONST_INT && ((INTVAL (op) & 0xffff0000) == 0 || (INTVAL (op) & 0xffff) == 0)));}/* Return 1 if C is a constant that is not a logical operand (as above). */intnon_logical_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) != 0 && (INTVAL (op) & 0xffff) != 0);}/* Return 1 if C is a constant that can be encoded in a mask on the RS/6000. It is if there are no more than two 1->0 or 0->1 transitions. Reject all ones and all zeros, since these should have been optimized away and confuse the making of MB and ME. */intmask_constant (c) register int c;{ int i; int last_bit_value; int transitions = 0; if (c == 0 || c == ~0) return 0; last_bit_value = c & 1; for (i = 1; i < 32; i++) if (((c >>= 1) & 1) != last_bit_value) last_bit_value ^= 1, transitions++; return transitions <= 2;}/* Return 1 if the operand is a constant that is a mask on the RS/6000. */intmask_operand (op, mode) register rtx op; enum machine_mode mode;{ return GET_CODE (op) == CONST_INT && mask_constant (INTVAL (op));}/* Return 1 if the operand is either a non-special register or a constant that can be used as the operand of an RS/6000 logical AND insn. */intand_operand (op, mode) register rtx op; enum machine_mode mode;{ return (reg_or_short_operand (op, mode) || logical_operand (op, mode) || mask_operand (op, mode));}/* Return 1 if the operand is a constant but not a valid operand for an AND insn. */intnon_and_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return GET_CODE (op) == CONST_INT && ! and_operand (op, mode);}/* Return 1 if the operand is a general register or memory operand. */intreg_or_mem_operand (op, mode) register rtx op; register enum machine_mode mode;{ return gpc_reg_operand (op, mode) || memory_operand (op, mode);}/* Return 1 if the operand, used inside a MEM, is a valid first argument to CALL. This is a SYMBOL_REF or a pseudo-register, which will be forced to lr. */intcall_operand (op, mode) register rtx op; enum machine_mode mode;{ if (mode != VOIDmode && GET_MODE (op) != mode) return 0; return (GET_CODE (op) == SYMBOL_REF || (GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER));}/* Return 1 if this operand is a valid input for a move insn. */intinput_operand (op, mode) register rtx op; enum machine_mode mode;{ if (memory_operand (op, mode)) return 1; /* For floating-point or multi-word mode, only register or memory is valid. */ if (GET_MODE_CLASS (mode) == MODE_FLOAT || GET_MODE_SIZE (mode) > UNITS_PER_WORD) return gpc_reg_operand (op, mode); /* The only cases left are integral modes one word or smaller (we do not get called for MODE_CC values). These can be in any register. */ if (register_operand (op, mode)) return; /* For HImode and QImode, any constant is valid. */ if ((mode == HImode || mode == QImode) && GET_CODE (op) == CONST_INT) return 1; /* Otherwise, we will be doing this SET with an add, so anything valid for an add will be valid. */ return add_operand (op, mode);}/* Return 1 if OP is a load multiple operation. It is known to be a PARALLEL and the first section will be tested. */intload_multiple_operation (op, mode) rtx op; enum machine_mode mode;{ int count = XVECLEN (op, 0); int dest_regno; rtx src_addr; int i; /* Perform a quick check so we don't blow up below. */ if (count <= 1 || GET_CODE (XVECEXP (op, 0, 0)) != SET || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM) return 0; dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); for (i = 1; i < count; i++) { rtx elt = XVECEXP (op, 0, i); if (GET_CODE (elt) != SET || GET_CODE (SET_DEST (elt)) != REG || GET_MODE (SET_DEST (elt)) != SImode || REGNO (SET_DEST (elt)) != dest_regno + i || GET_CODE (SET_SRC (elt)) != MEM || GET_MODE (SET_SRC (elt)) != SImode || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -