📄 recog.c
字号:
/* Subroutines used by or related to instruction recognition. Copyright (C) 1987, 1988, 1991, 1992 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, 675 Mass Ave, Cambridge, MA 02139, USA. */#include "config.h"#include "rtl.h"#include <stdio.h>#include "insn-config.h"#include "insn-attr.h"#include "insn-flags.h"#include "insn-codes.h"#include "recog.h"#include "regs.h"#include "hard-reg-set.h"#include "flags.h"#include "real.h"#ifndef STACK_PUSH_CODE#ifdef STACK_GROWS_DOWNWARD#define STACK_PUSH_CODE PRE_DEC#else#define STACK_PUSH_CODE PRE_INC#endif#endif/* Import from final.c: */extern rtx alter_subreg ();int strict_memory_address_p ();int memory_address_p ();/* Nonzero means allow operands to be volatile. This should be 0 if you are generating rtl, such as if you are calling the functions in optabs.c and expmed.c (most of the time). This should be 1 if all valid insns need to be recognized, such as in regclass.c and final.c and reload.c. init_recog and init_recog_no_volatile are responsible for setting this. */int volatile_ok;/* On return from `constrain_operands', indicate which alternative was satisfied. */int which_alternative;/* Nonzero after end of reload pass. Set to 1 or 0 by toplev.c. Controls the significance of (SUBREG (MEM)). */int reload_completed;/* Initialize data used by the function `recog'. This must be called once in the compilation of a function before any insn recognition may be done in the function. */voidinit_recog_no_volatile (){ volatile_ok = 0;}voidinit_recog (){ volatile_ok = 1;}/* Try recognizing the instruction INSN, and return the code number that results. Remeber the code so that repeated calls do not need to spend the time for actual rerecognition. This function is the normal interface to instruction recognition. The automatically-generated function `recog' is normally called through this one. (The only exception is in combine.c.) */intrecog_memoized (insn) rtx insn;{ if (INSN_CODE (insn) < 0) INSN_CODE (insn) = recog (PATTERN (insn), insn, NULL_PTR); return INSN_CODE (insn);}/* Check that X is an insn-body for an `asm' with operands and that the operands mentioned in it are legitimate. */intcheck_asm_operands (x) rtx x;{ int noperands = asm_noperands (x); rtx *operands; int i; if (noperands < 0) return 0; if (noperands == 0) return 1; operands = (rtx *) alloca (noperands * sizeof (rtx)); decode_asm_operands (x, operands, NULL_PTR, NULL_PTR, NULL_PTR); for (i = 0; i < noperands; i++) if (!general_operand (operands[i], VOIDmode)) return 0; return 1;}/* Static data for the next two routines. The maximum number of changes supported is defined as the maximum number of operands times 5. This allows for repeated substitutions inside complex indexed address, or, alternatively, changes in up to 5 insns. */#define MAX_CHANGE_LOCS (MAX_RECOG_OPERANDS * 5)static rtx change_objects[MAX_CHANGE_LOCS];static int change_old_codes[MAX_CHANGE_LOCS];static rtx *change_locs[MAX_CHANGE_LOCS];static rtx change_olds[MAX_CHANGE_LOCS];static int num_changes = 0;/* Validate a proposed change to OBJECT. LOC is the location in the rtl for at which NEW will be placed. If OBJECT is zero, no validation is done, the change is simply made. Two types of objects are supported: If OBJECT is a MEM, memory_address_p will be called with the address and mode as parameters. If OBJECT is an INSN, CALL_INSN, or JUMP_INSN, the insn will be re-recognized with the change in place. IN_GROUP is non-zero if this is part of a group of changes that must be performed as a group. In that case, the changes will be stored. The function `apply_change_group' will validate and apply the changes. If IN_GROUP is zero, this is a single change. Try to recognize the insn or validate the memory reference with the change applied. If the result is not valid for the machine, suppress the change and return zero. Otherwise, perform the change and return 1. */intvalidate_change (object, loc, new, in_group) rtx object; rtx *loc; rtx new; int in_group;{ rtx old = *loc; if (old == new || rtx_equal_p (old, new)) return 1; if (num_changes >= MAX_CHANGE_LOCS || (in_group == 0 && num_changes != 0)) abort (); *loc = new; /* Save the information describing this change. */ change_objects[num_changes] = object; change_locs[num_changes] = loc; change_olds[num_changes] = old; if (object && GET_CODE (object) != MEM) { /* Set INSN_CODE to force rerecognition of insn. Save old code in case invalid. */ change_old_codes[num_changes] = INSN_CODE (object); INSN_CODE (object) = -1; } num_changes++; /* If we are making a group of changes, return 1. Otherwise, validate the change group we made. */ if (in_group) return 1; else return apply_change_group ();}/* Apply a group of changes previously issued with `validate_change'. Return 1 if all changes are valid, zero otherwise. */intapply_change_group (){ int i; /* The changes have been applied and all INSN_CODEs have been reset to force rerecognition. The changes are valid if we aren't given an object, or if we are given a MEM and it still is a valid address, or if this is in insn and it is recognized. In the latter case, if reload has completed, we also require that the operands meet the constraints for the insn. We do not allow modifying an ASM_OPERANDS after reload has completed because verifying the constraints is too difficult. */ for (i = 0; i < num_changes; i++) { rtx object = change_objects[i]; if (object == 0) continue; if (GET_CODE (object) == MEM) { if (! memory_address_p (GET_MODE (object), XEXP (object, 0))) break; } else if ((recog_memoized (object) < 0 && (asm_noperands (PATTERN (object)) < 0 || ! check_asm_operands (PATTERN (object)) || reload_completed)) || (reload_completed && (insn_extract (object), ! constrain_operands (INSN_CODE (object), 1)))) { rtx pat = PATTERN (object); /* Perhaps we couldn't recognize the insn because there were extra CLOBBERs at the end. If so, try to re-recognize without the last CLOBBER (later iterations will cause each of them to be eliminated, in turn). But don't do this if we have an ASM_OPERAND. */ if (GET_CODE (pat) == PARALLEL && GET_CODE (XVECEXP (pat, 0, XVECLEN (pat, 0) - 1)) == CLOBBER && asm_noperands (PATTERN (object)) < 0) { rtx newpat; if (XVECLEN (pat, 0) == 2) newpat = XVECEXP (pat, 0, 0); else { int j; newpat = gen_rtx (PARALLEL, VOIDmode, gen_rtvec (XVECLEN (pat, 0) - 1)); for (j = 0; j < XVECLEN (newpat, 0); j++) XVECEXP (newpat, 0, j) = XVECEXP (pat, 0, j); } /* Add a new change to this group to replace the pattern with this new pattern. Then consider this change as having succeeded. The change we added will cause the entire call to fail if things remain invalid. Note that this can lose if a later change than the one we are processing specified &XVECEXP (PATTERN (object), 0, X) but this shouldn't occur. */ validate_change (object, &PATTERN (object), newpat, 1); } else if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER) /* If this insn is a CLOBBER or USE, it is always valid, but is never recognized. */ continue; else break; } } if (i == num_changes) { num_changes = 0; return 1; } else { cancel_changes (0); return 0; }}/* Return the number of changes so far in the current group. */intnum_validated_changes (){ return num_changes;}/* Retract the changes numbered NUM and up. */voidcancel_changes (num) int num;{ int i; /* Back out all the changes. Do this in the opposite order in which they were made. */ for (i = num_changes - 1; i >= num; i--) { *change_locs[i] = change_olds[i]; if (change_objects[i] && GET_CODE (change_objects[i]) != MEM) INSN_CODE (change_objects[i]) = change_old_codes[i]; } num_changes = num;}/* Replace every occurrence of FROM in X with TO. Mark each change with validate_change passing OBJECT. */static voidvalidate_replace_rtx_1 (loc, from, to, object) rtx *loc; rtx from, to, object;{ register int i, j; register char *fmt; register rtx x = *loc; enum rtx_code code = GET_CODE (x); /* X matches FROM if it is the same rtx or they are both referring to the same register in the same mode. Avoid calling rtx_equal_p unless the operands look similar. */ if (x == from || (GET_CODE (x) == REG && GET_CODE (from) == REG && GET_MODE (x) == GET_MODE (from) && REGNO (x) == REGNO (from)) || (GET_CODE (x) == GET_CODE (from) && GET_MODE (x) == GET_MODE (from) && rtx_equal_p (x, from))) { validate_change (object, loc, to, 1); return; } /* For commutative or comparison operations, try replacing each argument separately and seeing if we made any changes. If so, put a constant argument last.*/ if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c') { int prev_changes = num_changes; validate_replace_rtx_1 (&XEXP (x, 0), from, to, object); validate_replace_rtx_1 (&XEXP (x, 1), from, to, object); if (prev_changes != num_changes && CONSTANT_P (XEXP (x, 0))) { validate_change (object, loc, gen_rtx (GET_RTX_CLASS (code) == 'c' ? code : swap_condition (code), GET_MODE (x), XEXP (x, 1), XEXP (x, 0)), 1); x = *loc; code = GET_CODE (x); } } switch (code) { case PLUS: /* If we have have a PLUS whose second operand is now a CONST_INT, use plus_constant to try to simplify it. */ if (GET_CODE (XEXP (x, 1)) == CONST_INT && XEXP (x, 1) == to) validate_change (object, loc, plus_constant (XEXP (x, 0), INTVAL (XEXP (x, 1))), 1); return; case ZERO_EXTEND: case SIGN_EXTEND: /* In these cases, the operation to be performed depends on the mode of the operand. If we are replacing the operand with a VOIDmode constant, we lose the information. So try to simplify the operation in that case. If it fails, substitute in something that we know won't be recognized. */ if (GET_MODE (to) == VOIDmode && (XEXP (x, 0) == from || (GET_CODE (XEXP (x, 0)) == REG && GET_CODE (from) == REG && GET_MODE (XEXP (x, 0)) == GET_MODE (from) && REGNO (XEXP (x, 0)) == REGNO (from)))) { rtx new = simplify_unary_operation (code, GET_MODE (x), to, GET_MODE (from)); if (new == 0) new = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx); validate_change (object, loc, new, 1); return; } break; case SUBREG: /* If we have a SUBREG of a register that we are replacing and we are replacing it with a MEM, make a new MEM and try replacing the SUBREG with it. Don't do this if the MEM has a mode-dependent address or if we would be widening it. */ if (SUBREG_REG (x) == from && GET_CODE (from) == REG && GET_CODE (to) == MEM && ! mode_dependent_address_p (XEXP (to, 0)) && ! MEM_VOLATILE_P (to) && GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (to))) { int offset = SUBREG_WORD (x) * UNITS_PER_WORD; enum machine_mode mode = GET_MODE (x); rtx new;#if BYTES_BIG_ENDIAN offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));#endif new = gen_rtx (MEM, mode, plus_constant (XEXP (to, 0), offset)); MEM_VOLATILE_P (new) = MEM_VOLATILE_P (to); RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (to); MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (to); validate_change (object, loc, new, 1); return; } break; case ZERO_EXTRACT: case SIGN_EXTRACT: /* If we are replacing a register with memory, try to change the memory to be the mode required for memory in extract operations (this isn't likely to be an insertion operation; if it was, nothing bad will happen, we might just fail in some cases). */ if (XEXP (x, 0) == from && GET_CODE (from) == REG && GET_CODE (to) == MEM && GET_CODE (XEXP (x, 1)) == CONST_INT && GET_CODE (XEXP (x, 2)) == CONST_INT && ! mode_dependent_address_p (XEXP (to, 0)) && ! MEM_VOLATILE_P (to)) { enum machine_mode wanted_mode = VOIDmode; enum machine_mode is_mode = GET_MODE (to); int width = INTVAL (XEXP (x, 1)); int pos = INTVAL (XEXP (x, 2));#ifdef HAVE_extzv if (code == ZERO_EXTRACT) wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];#endif#ifdef HAVE_extv if (code == SIGN_EXTRACT) wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];#endif /* If we have a narrower mode, we can do something. */ if (wanted_mode != VOIDmode && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode)) { int offset = pos / BITS_PER_UNIT; rtx newmem; /* If the bytes and bits are counted differently, we must adjust the offset. */#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode) - offset);#endif pos %= GET_MODE_BITSIZE (wanted_mode); newmem = gen_rtx (MEM, wanted_mode, plus_constant (XEXP (to, 0), offset)); RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (to); MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (to); MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (to); validate_change (object, &XEXP (x, 2), GEN_INT (pos), 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -