📄 rtlanal.c
字号:
/* Analyze RTL for C-Compiler Copyright (C) 1987, 88, 91, 92, 93, 94, 1995 Free Software Foundation, Inc.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 "rtl.h"void note_stores ();int reg_set_p ();/* Bit flags that specify the machine subtype we are compiling for. Bits are tested using macros TARGET_... defined in the tm.h file and set by `-m...' switches. Must be defined in rtlanal.c. */int target_flags;/* Return 1 if the value of X is unstable (would be different at a different point in the program). The frame pointer, arg pointer, etc. are considered stable (within one function) and so is anything marked `unchanging'. */intrtx_unstable_p (x) rtx x;{ register RTX_CODE code = GET_CODE (x); register int i; register char *fmt; if (code == MEM) return ! RTX_UNCHANGING_P (x); if (code == QUEUED) return 1; if (code == CONST || code == CONST_INT) return 0; if (code == REG) return ! (REGNO (x) == FRAME_POINTER_REGNUM || REGNO (x) == HARD_FRAME_POINTER_REGNUM || REGNO (x) == ARG_POINTER_REGNUM || RTX_UNCHANGING_P (x)); fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) if (fmt[i] == 'e') if (rtx_unstable_p (XEXP (x, i))) return 1; return 0;}/* Return 1 if X has a value that can vary even between two executions of the program. 0 means X can be compared reliably against certain constants or near-constants. The frame pointer and the arg pointer are considered constant. */intrtx_varies_p (x) rtx x;{ register RTX_CODE code = GET_CODE (x); register int i; register char *fmt; switch (code) { case MEM: case QUEUED: return 1; case CONST: case CONST_INT: case CONST_DOUBLE: case SYMBOL_REF: case LABEL_REF: return 0; case REG: /* Note that we have to test for the actual rtx used for the frame and arg pointers and not just the register number in case we have eliminated the frame and/or arg pointer and are using it for pseudos. */ return ! (x == frame_pointer_rtx || x == hard_frame_pointer_rtx || x == arg_pointer_rtx); case LO_SUM: /* The operand 0 of a LO_SUM is considered constant (in fact is it related specifically to operand 1). */ return rtx_varies_p (XEXP (x, 1)); } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) if (fmt[i] == 'e') if (rtx_varies_p (XEXP (x, i))) return 1; return 0;}/* Return 0 if the use of X as an address in a MEM can cause a trap. */intrtx_addr_can_trap_p (x) register rtx x;{ register enum rtx_code code = GET_CODE (x); switch (code) { case SYMBOL_REF: case LABEL_REF: /* SYMBOL_REF is problematic due to the possible presence of a #pragma weak, but to say that loads from symbols can trap is *very* costly. It's not at all clear what's best here. For now, we ignore the impact of #pragma weak. */ return 0; case REG: /* As in rtx_varies_p, we have to use the actual rtx, not reg number. */ return ! (x == frame_pointer_rtx || x == hard_frame_pointer_rtx || x == stack_pointer_rtx || x == arg_pointer_rtx); case CONST: return rtx_addr_can_trap_p (XEXP (x, 0)); case PLUS: /* An address is assumed not to trap if it is an address that can't trap plus a constant integer. */ return (rtx_addr_can_trap_p (XEXP (x, 0)) || GET_CODE (XEXP (x, 1)) != CONST_INT); case LO_SUM: return rtx_addr_can_trap_p (XEXP (x, 1)); } /* If it isn't one of the case above, it can cause a trap. */ return 1;}/* Return 1 if X refers to a memory location whose address cannot be compared reliably with constant addresses, or if X refers to a BLKmode memory object. */intrtx_addr_varies_p (x) rtx x;{ register enum rtx_code code; register int i; register char *fmt; if (x == 0) return 0; code = GET_CODE (x); if (code == MEM) return GET_MODE (x) == BLKmode || rtx_varies_p (XEXP (x, 0)); fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) if (fmt[i] == 'e') if (rtx_addr_varies_p (XEXP (x, i))) return 1; return 0;}/* Return the value of the integer term in X, if one is apparent; otherwise return 0. Only obvious integer terms are detected. This is used in cse.c with the `related_value' field.*/HOST_WIDE_INTget_integer_term (x) rtx x;{ if (GET_CODE (x) == CONST) x = XEXP (x, 0); if (GET_CODE (x) == MINUS && GET_CODE (XEXP (x, 1)) == CONST_INT) return - INTVAL (XEXP (x, 1)); if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) return INTVAL (XEXP (x, 1)); return 0;}/* If X is a constant, return the value sans apparent integer term; otherwise return 0. Only obvious integer terms are detected. */rtxget_related_value (x) rtx x;{ if (GET_CODE (x) != CONST) return 0; x = XEXP (x, 0); if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) return XEXP (x, 0); else if (GET_CODE (x) == MINUS && GET_CODE (XEXP (x, 1)) == CONST_INT) return XEXP (x, 0); return 0;}/* Nonzero if register REG appears somewhere within IN. Also works if REG is not a register; in this case it checks for a subexpression of IN that is Lisp "equal" to REG. */intreg_mentioned_p (reg, in) register rtx reg, in;{ register char *fmt; register int i; register enum rtx_code code; if (in == 0) return 0; if (reg == in) return 1; if (GET_CODE (in) == LABEL_REF) return reg == XEXP (in, 0); code = GET_CODE (in); switch (code) { /* Compare registers by number. */ case REG: return GET_CODE (reg) == REG && REGNO (in) == REGNO (reg); /* These codes have no constituent expressions and are unique. */ case SCRATCH: case CC0: case PC: return 0; case CONST_INT: return GET_CODE (reg) == CONST_INT && INTVAL (in) == INTVAL (reg); case CONST_DOUBLE: /* These are kept unique for a given value. */ return 0; } if (GET_CODE (reg) == code && rtx_equal_p (reg, in)) return 1; fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'E') { register int j; for (j = XVECLEN (in, i) - 1; j >= 0; j--) if (reg_mentioned_p (reg, XVECEXP (in, i, j))) return 1; } else if (fmt[i] == 'e' && reg_mentioned_p (reg, XEXP (in, i))) return 1; } return 0;}/* Return 1 if in between BEG and END, exclusive of BEG and END, there is no CODE_LABEL insn. */intno_labels_between_p (beg, end) rtx beg, end;{ register rtx p; for (p = NEXT_INSN (beg); p != end; p = NEXT_INSN (p)) if (GET_CODE (p) == CODE_LABEL) return 0; return 1;}/* Nonzero if register REG is used in an insn between FROM_INSN and TO_INSN (exclusive of those two). */intreg_used_between_p (reg, from_insn, to_insn) rtx reg, from_insn, to_insn;{ register rtx insn; if (from_insn == to_insn) return 0; for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn)) if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' && (reg_overlap_mentioned_p (reg, PATTERN (insn)) || (GET_CODE (insn) == CALL_INSN && (find_reg_fusage (insn, USE, reg) || find_reg_fusage (insn, CLOBBER, reg))))) return 1; return 0;}/* Nonzero if the old value of X, a register, is referenced in BODY. If X is entirely replaced by a new value and the only use is as a SET_DEST, we do not consider it a reference. */intreg_referenced_p (x, body) rtx x; rtx body;{ int i; switch (GET_CODE (body)) { case SET: if (reg_overlap_mentioned_p (x, SET_SRC (body))) return 1; /* If the destination is anything other than CC0, PC, a REG or a SUBREG of a REG that occupies all of the REG, the insn references X if it is mentioned in the destination. */ if (GET_CODE (SET_DEST (body)) != CC0 && GET_CODE (SET_DEST (body)) != PC && GET_CODE (SET_DEST (body)) != REG && ! (GET_CODE (SET_DEST (body)) == SUBREG && GET_CODE (SUBREG_REG (SET_DEST (body))) == REG && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (body)))) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) == ((GET_MODE_SIZE (GET_MODE (SET_DEST (body))) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))) && reg_overlap_mentioned_p (x, SET_DEST (body))) return 1; break; case ASM_OPERANDS: for (i = ASM_OPERANDS_INPUT_LENGTH (body) - 1; i >= 0; i--) if (reg_overlap_mentioned_p (x, ASM_OPERANDS_INPUT (body, i))) return 1; break; case CALL: case USE: return reg_overlap_mentioned_p (x, body); case TRAP_IF: return reg_overlap_mentioned_p (x, TRAP_CONDITION (body)); case UNSPEC: case UNSPEC_VOLATILE: case PARALLEL: for (i = XVECLEN (body, 0) - 1; i >= 0; i--) if (reg_referenced_p (x, XVECEXP (body, 0, i))) return 1; break; } return 0;}/* Nonzero if register REG is referenced in an insn between FROM_INSN and TO_INSN (exclusive of those two). Sets of REG do not count. */intreg_referenced_between_p (reg, from_insn, to_insn) rtx reg, from_insn, to_insn;{ register rtx insn; if (from_insn == to_insn) return 0; for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn)) if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' && (reg_referenced_p (reg, PATTERN (insn)) || (GET_CODE (insn) == CALL_INSN && find_reg_fusage (insn, USE, reg)))) return 1; return 0;}/* Nonzero if register REG is set or clobbered in an insn between FROM_INSN and TO_INSN (exclusive of those two). */intreg_set_between_p (reg, from_insn, to_insn) rtx reg, from_insn, to_insn;{ register rtx insn; if (from_insn == to_insn) return 0; for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn)) if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' && reg_set_p (reg, insn)) return 1; return 0;}/* Internals of reg_set_between_p. */static rtx reg_set_reg;static int reg_set_flag;static voidreg_set_p_1 (x, pat) rtx x;{ /* We don't want to return 1 if X is a MEM that contains a register within REG_SET_REG. */ if ((GET_CODE (x) != MEM) && reg_overlap_mentioned_p (reg_set_reg, x)) reg_set_flag = 1;}intreg_set_p (reg, insn) rtx reg, insn;{ rtx body = insn; /* We can be passed an insn or part of one. If we are passed an insn, check if a side-effect of the insn clobbers REG. */ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') { if (FIND_REG_INC_NOTE (insn, reg) || (GET_CODE (insn) == CALL_INSN /* We'd like to test call_used_regs here, but rtlanal.c can't reference that variable due to its use in genattrtab. So we'll just be more conservative. ??? Unless we could ensure that the CALL_INSN_FUNCTION_USAGE information holds all clobbered registers. */ && ((GET_CODE (reg) == REG && REGNO (reg) < FIRST_PSEUDO_REGISTER) || GET_CODE (reg) == MEM || find_reg_fusage (insn, CLOBBER, reg)))) return 1; body = PATTERN (insn); } reg_set_reg = reg; reg_set_flag = 0; note_stores (body, reg_set_p_1); return reg_set_flag;}/* Similar to reg_set_between_p, but check all registers in X. Return 0 only if none of them are modified between START and END. Return 1 if X contains a MEM; this routine does not perform any memory aliasing. */intmodified_between_p (x, start, end) rtx x; rtx start, end;{ enum rtx_code code = GET_CODE (x); char *fmt; int i, j; switch (code) { case CONST_INT: case CONST_DOUBLE: case CONST: case SYMBOL_REF: case LABEL_REF: return 0; case PC: case CC0: return 1; case MEM: /* If the memory is not constant, assume it is modified. If it is constant, we still have to check the address. */ if (! RTX_UNCHANGING_P (x)) return 1; break; case REG: return reg_set_between_p (x, start, end); } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e' && modified_between_p (XEXP (x, i), start, end)) return 1; if (fmt[i] == 'E') for (j = XVECLEN (x, i) - 1; j >= 0; j--) if (modified_between_p (XVECEXP (x, i, j), start, end)) return 1; } return 0;}/* Similar to reg_set_p, but check all registers in X. Return 0 only if none of them are modified in INSN. Return 1 if X contains a MEM; this routine does not perform any memory aliasing. */intmodified_in_p (x, insn) rtx x; rtx insn;{ enum rtx_code code = GET_CODE (x); char *fmt; int i, j; switch (code) { case CONST_INT: case CONST_DOUBLE: case CONST: case SYMBOL_REF: case LABEL_REF: return 0; case PC: case CC0: return 1; case MEM: /* If the memory is not constant, assume it is modified. If it is constant, we still have to check the address. */ if (! RTX_UNCHANGING_P (x)) return 1; break; case REG: return reg_set_p (x, insn); } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e' && modified_in_p (XEXP (x, i), insn)) return 1; if (fmt[i] == 'E') for (j = XVECLEN (x, i) - 1; j >= 0; j--) if (modified_in_p (XVECEXP (x, i, j), insn)) return 1; } return 0;}/* Given an INSN, return a SET expression if this insn has only a single SET. It may also have CLOBBERs, USEs, or SET whose output will not be used, which we ignore. */rtxsingle_set (insn) rtx insn;{ rtx set; int i; if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') return 0; if (GET_CODE (PATTERN (insn)) == SET) return PATTERN (insn); else if (GET_CODE (PATTERN (insn)) == PARALLEL) { for (i = 0, set = 0; i < XVECLEN (PATTERN (insn), 0); i++) if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET && (! find_reg_note (insn, REG_UNUSED, SET_DEST (XVECEXP (PATTERN (insn), 0, i))) || side_effects_p (XVECEXP (PATTERN (insn), 0, i)))) { if (set) return 0; else set = XVECEXP (PATTERN (insn), 0, i); } return set; } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -