diff options
| author | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 | 
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 | 
| commit | a16f65c7d117419bd266c28a1901ef129a337569 (patch) | |
| tree | 2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /gnu/usr.bin/cc/lib/rtlanal.c | |
| parent | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff) | |
Diffstat (limited to 'gnu/usr.bin/cc/lib/rtlanal.c')
| -rw-r--r-- | gnu/usr.bin/cc/lib/rtlanal.c | 1594 | 
1 files changed, 1594 insertions, 0 deletions
| diff --git a/gnu/usr.bin/cc/lib/rtlanal.c b/gnu/usr.bin/cc/lib/rtlanal.c new file mode 100644 index 000000000000..549f9054f6ec --- /dev/null +++ b/gnu/usr.bin/cc/lib/rtlanal.c @@ -0,0 +1,1594 @@ +/* Analyze RTL for C-Compiler +   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 modify +it under the terms of the GNU General Public License as published by +the 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 of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING.  If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, 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'.  */ + +int +rtx_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) == 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.  */ + +int +rtx_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 == 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.  */ + +int +rtx_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 == 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.  */ + +int +rtx_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_INT +get_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.  */ + +rtx +get_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.  */ + +int +reg_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.  */ + +int +no_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).  */ + +int +reg_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))) +      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.  */ + +int +reg_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. */ + +int +reg_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))) +      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).  */ + +int +reg_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; + +void +reg_set_p_1 (x) +     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; +} + +int +reg_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.  */ +	      && ((GET_CODE (reg) == REG +		   && REGNO (reg) < FIRST_PSEUDO_REGISTER) +		  || GET_CODE (reg) == MEM))) +	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.  */ + +int +modified_between_p (x, start, end) +     rtx x; +     rtx start, end; +{ +  enum rtx_code code = GET_CODE (x); +  char *fmt; +  int i; + +  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; + +  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.  */ + +rtx +single_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; +} + +/* Return the last thing that X was assigned from before *PINSN.  Verify that +   the object is not modified up to VALID_TO.  If it was, if we hit +   a partial assignment to X, or hit a CODE_LABEL first, return X.  If we +   found an assignment, update *PINSN to point to it.  */ + +rtx +find_last_value (x, pinsn, valid_to) +     rtx x; +     rtx *pinsn; +     rtx valid_to; +{ +  rtx p; + +  for (p = PREV_INSN (*pinsn); p && GET_CODE (p) != CODE_LABEL; +       p = PREV_INSN (p)) +    if (GET_RTX_CLASS (GET_CODE (p)) == 'i') +      { +	rtx set = single_set (p); +	rtx note = find_reg_note (p, REG_EQUAL, NULL_RTX); + +	if (set && rtx_equal_p (x, SET_DEST (set))) +	  { +	    rtx src = SET_SRC (set); + +	    if (note && GET_CODE (XEXP (note, 0)) != EXPR_LIST) +	      src = XEXP (note, 0); + +	    if (! modified_between_p (src, PREV_INSN (p), valid_to) +		/* Reject hard registers because we don't usually want +		   to use them; we'd rather use a pseudo.  */ +		&& ! (GET_CODE (src) == REG +		      && REGNO (src) < FIRST_PSEUDO_REGISTER)) +	      { +		*pinsn = p; +		return src; +	      } +	  } +	   +	/* If set in non-simple way, we don't have a value.  */ +	if (reg_set_p (x, p)) +	  break; +      } + +  return x; +}      + +/* Return nonzero if register in range [REGNO, ENDREGNO) +   appears either explicitly or implicitly in X +   other than being stored into. + +   References contained within the substructure at LOC do not count. +   LOC may be zero, meaning don't ignore anything.  */ + +int +refers_to_regno_p (regno, endregno, x, loc) +     int regno, endregno; +     rtx x; +     rtx *loc; +{ +  register int i; +  register RTX_CODE code; +  register char *fmt; + + repeat: +  /* The contents of a REG_NONNEG note is always zero, so we must come here +     upon repeat in case the last REG_NOTE is a REG_NONNEG note.  */ +  if (x == 0) +    return 0; + +  code = GET_CODE (x); + +  switch (code) +    { +    case REG: +      i = REGNO (x); +      return (endregno > i +	      && regno < i + (i < FIRST_PSEUDO_REGISTER  +			      ? HARD_REGNO_NREGS (i, GET_MODE (x)) +			      : 1)); + +    case SUBREG: +      /* If this is a SUBREG of a hard reg, we can see exactly which +	 registers are being modified.  Otherwise, handle normally.  */ +      if (GET_CODE (SUBREG_REG (x)) == REG +	  && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER) +	{ +	  int inner_regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x); +	  int inner_endregno +	    = inner_regno + (inner_regno < FIRST_PSEUDO_REGISTER +			     ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); + +	  return endregno > inner_regno && regno < inner_endregno; +	} +      break; + +    case CLOBBER: +    case SET: +      if (&SET_DEST (x) != loc +	  /* Note setting a SUBREG counts as referring to the REG it is in for +	     a pseudo but not for hard registers since we can +	     treat each word individually.  */ +	  && ((GET_CODE (SET_DEST (x)) == SUBREG +	       && loc != &SUBREG_REG (SET_DEST (x)) +	       && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG +	       && REGNO (SUBREG_REG (SET_DEST (x))) >= FIRST_PSEUDO_REGISTER +	       && refers_to_regno_p (regno, endregno, +				     SUBREG_REG (SET_DEST (x)), loc)) +	      || (GET_CODE (SET_DEST (x)) != REG +		  && refers_to_regno_p (regno, endregno, SET_DEST (x), loc)))) +	return 1; + +      if (code == CLOBBER || loc == &SET_SRC (x)) +	return 0; +      x = SET_SRC (x); +      goto repeat; +    } + +  /* X does not match, so try its subexpressions.  */ + +  fmt = GET_RTX_FORMAT (code); +  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) +    { +      if (fmt[i] == 'e' && loc != &XEXP (x, i)) +	{ +	  if (i == 0) +	    { +	      x = XEXP (x, 0); +	      goto repeat; +	    } +	  else +	    if (refers_to_regno_p (regno, endregno, XEXP (x, i), loc)) +	      return 1; +	} +      else if (fmt[i] == 'E') +	{ +	  register int j; +	  for (j = XVECLEN (x, i) - 1; j >=0; j--) +	    if (loc != &XVECEXP (x, i, j) +		&& refers_to_regno_p (regno, endregno, XVECEXP (x, i, j), loc)) +	      return 1; +	} +    } +  return 0; +} + +/* Nonzero if modifying X will affect IN.  If X is a register or a SUBREG, +   we check if any register number in X conflicts with the relevant register +   numbers.  If X is a constant, return 0.  If X is a MEM, return 1 iff IN +   contains a MEM (we don't bother checking for memory addresses that can't +   conflict because we expect this to be a rare case.  */ + +int +reg_overlap_mentioned_p (x, in) +     rtx x, in; +{ +  int regno, endregno; + +  if (GET_CODE (x) == SUBREG) +    { +      regno = REGNO (SUBREG_REG (x)); +      if (regno < FIRST_PSEUDO_REGISTER) +	regno += SUBREG_WORD (x); +    } +  else if (GET_CODE (x) == REG) +    regno = REGNO (x); +  else if (CONSTANT_P (x)) +    return 0; +  else if (GET_CODE (x) == MEM) +    { +      char *fmt; +      int i; + +      if (GET_CODE (in) == MEM) +	return 1; + +      fmt = GET_RTX_FORMAT (GET_CODE (in)); + +      for (i = GET_RTX_LENGTH (GET_CODE (in)) - 1; i >= 0; i--) +	if (fmt[i] == 'e' && reg_overlap_mentioned_p (x, XEXP (in, i))) +	  return 1; + +      return 0; +    } +  else if (GET_CODE (x) == SCRATCH || GET_CODE (x) == PC +	   || GET_CODE (x) == CC0) +    return reg_mentioned_p (x, in); +  else +    abort (); + +  endregno = regno + (regno < FIRST_PSEUDO_REGISTER +		      ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1); + +  return refers_to_regno_p (regno, endregno, in, NULL_PTR); +} + +/* Used for communications between the next few functions.  */ + +static int reg_set_last_unknown; +static rtx reg_set_last_value; +static int reg_set_last_first_regno, reg_set_last_last_regno; + +/* Called via note_stores from reg_set_last.  */ + +static void +reg_set_last_1 (x, pat) +     rtx x; +     rtx pat; +{ +  int first, last; + +  /* If X is not a register, or is not one in the range we care +     about, ignore.  */ +  if (GET_CODE (x) != REG) +    return; + +  first = REGNO (x); +  last = first + (first < FIRST_PSEUDO_REGISTER +		  ? HARD_REGNO_NREGS (first, GET_MODE (x)) : 1); + +  if (first >= reg_set_last_last_regno +      || last <= reg_set_last_first_regno) +    return; + +  /* If this is a CLOBBER or is some complex LHS, or doesn't modify +     exactly the registers we care about, show we don't know the value.  */ +  if (GET_CODE (pat) == CLOBBER || SET_DEST (pat) != x +      || first != reg_set_last_first_regno +      || last != reg_set_last_last_regno) +    reg_set_last_unknown = 1; +  else +    reg_set_last_value = SET_SRC (pat); +} + +/* Return the last value to which REG was set prior to INSN.  If we can't +   find it easily, return 0. + +   We only return a REG, SUBREG, or constant because it is too hard to +   check if a MEM remains unchanged.  */ + +rtx +reg_set_last (x, insn) +     rtx x; +     rtx insn; +{ +  rtx orig_insn = insn; + +  reg_set_last_first_regno = REGNO (x); + +  reg_set_last_last_regno +    = reg_set_last_first_regno +      + (reg_set_last_first_regno < FIRST_PSEUDO_REGISTER +	 ? HARD_REGNO_NREGS (reg_set_last_first_regno, GET_MODE (x)) : 1); + +  reg_set_last_unknown = 0; +  reg_set_last_value = 0; + +  /* Scan backwards until reg_set_last_1 changed one of the above flags. +     Stop when we reach a label or X is a hard reg and we reach a +     CALL_INSN (if reg_set_last_last_regno is a hard reg). + +     If we find a set of X, ensure that its SET_SRC remains unchanged.  */ + +  /* We compare with <= here, because reg_set_last_last_regno +     is actually the number of the first reg *not* in X.  */ +  for (; +       insn && GET_CODE (insn) != CODE_LABEL +       && ! (GET_CODE (insn) == CALL_INSN +	     && reg_set_last_last_regno <= FIRST_PSEUDO_REGISTER); +       insn = PREV_INSN (insn)) +    if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') +      { +	note_stores (PATTERN (insn), reg_set_last_1); +	if (reg_set_last_unknown) +	  return 0; +	else if (reg_set_last_value) +	  { +	    if (CONSTANT_P (reg_set_last_value) +		|| ((GET_CODE (reg_set_last_value) == REG +		     || GET_CODE (reg_set_last_value) == SUBREG) +		    && ! reg_set_between_p (reg_set_last_value, +					    NEXT_INSN (insn), orig_insn))) +	      return reg_set_last_value; +	    else +	      return 0; +	  } +      } + +  return 0; +} + +/* This is 1 until after reload pass.  */ +int rtx_equal_function_value_matters; + +/* Return 1 if X and Y are identical-looking rtx's. +   This is the Lisp function EQUAL for rtx arguments.  */ + +int +rtx_equal_p (x, y) +     rtx x, y; +{ +  register int i; +  register int j; +  register enum rtx_code code; +  register char *fmt; + +  if (x == y) +    return 1; +  if (x == 0 || y == 0) +    return 0; + +  code = GET_CODE (x); +  /* Rtx's of different codes cannot be equal.  */ +  if (code != GET_CODE (y)) +    return 0; + +  /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. +     (REG:SI x) and (REG:HI x) are NOT equivalent.  */ + +  if (GET_MODE (x) != GET_MODE (y)) +    return 0; + +  /* REG, LABEL_REF, and SYMBOL_REF can be compared nonrecursively.  */ + +  if (code == REG) +    /* Until rtl generation is complete, don't consider a reference to the +       return register of the current function the same as the return from a +       called function.  This eases the job of function integration.  Once the +       distinction is no longer needed, they can be considered equivalent.  */ +    return (REGNO (x) == REGNO (y) +	    && (! rtx_equal_function_value_matters +		|| REG_FUNCTION_VALUE_P (x) == REG_FUNCTION_VALUE_P (y))); +  else if (code == LABEL_REF) +    return XEXP (x, 0) == XEXP (y, 0); +  else if (code == SYMBOL_REF) +    return XSTR (x, 0) == XSTR (y, 0); +  else if (code == SCRATCH || code == CONST_DOUBLE) +    return 0; + +  /* Compare the elements.  If any pair of corresponding elements +     fail to match, return 0 for the whole things.  */ + +  fmt = GET_RTX_FORMAT (code); +  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) +    { +      switch (fmt[i]) +	{ +	case 'w': +	  if (XWINT (x, i) != XWINT (y, i)) +	    return 0; +	  break; + +	case 'n': +	case 'i': +	  if (XINT (x, i) != XINT (y, i)) +	    return 0; +	  break; + +	case 'V': +	case 'E': +	  /* Two vectors must have the same length.  */ +	  if (XVECLEN (x, i) != XVECLEN (y, i)) +	    return 0; + +	  /* And the corresponding elements must match.  */ +	  for (j = 0; j < XVECLEN (x, i); j++) +	    if (rtx_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0) +	      return 0; +	  break; + +	case 'e': +	  if (rtx_equal_p (XEXP (x, i), XEXP (y, i)) == 0) +	    return 0; +	  break; + +	case 'S': +	case 's': +	  if (strcmp (XSTR (x, i), XSTR (y, i))) +	    return 0; +	  break; + +	case 'u': +	  /* These are just backpointers, so they don't matter.  */ +	  break; + +	case '0': +	  break; + +	  /* It is believed that rtx's at this level will never +	     contain anything but integers and other rtx's, +	     except for within LABEL_REFs and SYMBOL_REFs.  */ +	default: +	  abort (); +	} +    } +  return 1; +} + +/* Call FUN on each register or MEM that is stored into or clobbered by X. +   (X would be the pattern of an insn). +   FUN receives two arguments: +     the REG, MEM, CC0 or PC being stored in or clobbered, +     the SET or CLOBBER rtx that does the store. + +  If the item being stored in or clobbered is a SUBREG of a hard register, +  the SUBREG will be passed.  */ +      +void +note_stores (x, fun) +     register rtx x; +     void (*fun) (); +{ +  if ((GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)) +    { +      register rtx dest = SET_DEST (x); +      while ((GET_CODE (dest) == SUBREG +	      && (GET_CODE (SUBREG_REG (dest)) != REG +		  || REGNO (SUBREG_REG (dest)) >= FIRST_PSEUDO_REGISTER)) +	     || GET_CODE (dest) == ZERO_EXTRACT +	     || GET_CODE (dest) == SIGN_EXTRACT +	     || GET_CODE (dest) == STRICT_LOW_PART) +	dest = XEXP (dest, 0); +      (*fun) (dest, x); +    } +  else if (GET_CODE (x) == PARALLEL) +    { +      register int i; +      for (i = XVECLEN (x, 0) - 1; i >= 0; i--) +	{ +	  register rtx y = XVECEXP (x, 0, i); +	  if (GET_CODE (y) == SET || GET_CODE (y) == CLOBBER) +	    { +	      register rtx dest = SET_DEST (y); +	      while ((GET_CODE (dest) == SUBREG +		      && (GET_CODE (SUBREG_REG (dest)) != REG +			  || (REGNO (SUBREG_REG (dest)) +			      >= FIRST_PSEUDO_REGISTER))) +		     || GET_CODE (dest) == ZERO_EXTRACT +		     || GET_CODE (dest) == SIGN_EXTRACT +		     || GET_CODE (dest) == STRICT_LOW_PART) +		dest = XEXP (dest, 0); +	      (*fun) (dest, y); +	    } +	} +    } +} + +/* Return nonzero if X's old contents don't survive after INSN. +   This will be true if X is (cc0) or if X is a register and +   X dies in INSN or because INSN entirely sets X. + +   "Entirely set" means set directly and not through a SUBREG, +   ZERO_EXTRACT or SIGN_EXTRACT, so no trace of the old contents remains. +   Likewise, REG_INC does not count. + +   REG may be a hard or pseudo reg.  Renumbering is not taken into account, +   but for this use that makes no difference, since regs don't overlap +   during their lifetimes.  Therefore, this function may be used +   at any time after deaths have been computed (in flow.c). + +   If REG is a hard reg that occupies multiple machine registers, this +   function will only return 1 if each of those registers will be replaced +   by INSN.  */ + +int +dead_or_set_p (insn, x) +     rtx insn; +     rtx x; +{ +  register int regno, last_regno; +  register int i; + +  /* Can't use cc0_rtx below since this file is used by genattrtab.c.  */ +  if (GET_CODE (x) == CC0) +    return 1; + +  if (GET_CODE (x) != REG) +    abort (); + +  regno = REGNO (x); +  last_regno = (regno >= FIRST_PSEUDO_REGISTER ? regno +		: regno + HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1); + +  for (i = regno; i <= last_regno; i++) +    if (! dead_or_set_regno_p (insn, i)) +      return 0; + +  return 1; +} + +/* Utility function for dead_or_set_p to check an individual register.  Also +   called from flow.c.  */ + +int +dead_or_set_regno_p (insn, test_regno) +     rtx insn; +     int test_regno; +{ +  int regno, endregno; +  rtx link; + +  /* See if there is a death note for something that includes TEST_REGNO.  */ +  for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) +    { +      if (REG_NOTE_KIND (link) != REG_DEAD || GET_CODE (XEXP (link, 0)) != REG) +	continue; + +      regno = REGNO (XEXP (link, 0)); +      endregno = (regno >= FIRST_PSEUDO_REGISTER ? regno + 1 +		  : regno + HARD_REGNO_NREGS (regno, +					      GET_MODE (XEXP (link, 0)))); + +      if (test_regno >= regno && test_regno < endregno) +	return 1; +    } + +  if (GET_CODE (PATTERN (insn)) == SET) +    { +      rtx dest = SET_DEST (PATTERN (insn)); +  +      /* A value is totally replaced if it is the destination or the +	 destination is a SUBREG of REGNO that does not change the number of +	 words in it.  */ +     if (GET_CODE (dest) == SUBREG +	  && (((GET_MODE_SIZE (GET_MODE (dest)) +		+ UNITS_PER_WORD - 1) / UNITS_PER_WORD) +	      == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) +		   + UNITS_PER_WORD - 1) / UNITS_PER_WORD))) +	dest = SUBREG_REG (dest); + +      if (GET_CODE (dest) != REG) +	return 0; + +      regno = REGNO (dest); +      endregno = (regno >= FIRST_PSEUDO_REGISTER ? regno + 1 +		  : regno + HARD_REGNO_NREGS (regno, GET_MODE (dest))); + +      return (test_regno >= regno && test_regno < endregno); +    } +  else if (GET_CODE (PATTERN (insn)) == PARALLEL) +    { +      register int i; + +      for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) +	{ +	  rtx body = XVECEXP (PATTERN (insn), 0, i); + +	  if (GET_CODE (body) == SET || GET_CODE (body) == CLOBBER) +	    { +	      rtx dest = SET_DEST (body); + +	      if (GET_CODE (dest) == SUBREG +		  && (((GET_MODE_SIZE (GET_MODE (dest)) +			+ UNITS_PER_WORD - 1) / UNITS_PER_WORD) +		      == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) +			   + UNITS_PER_WORD - 1) / UNITS_PER_WORD))) +		dest = SUBREG_REG (dest); + +	      if (GET_CODE (dest) != REG) +		continue; + +	      regno = REGNO (dest); +	      endregno = (regno >= FIRST_PSEUDO_REGISTER ? regno + 1 +			  : regno + HARD_REGNO_NREGS (regno, GET_MODE (dest))); + +	      if (test_regno >= regno && test_regno < endregno) +		return 1; +	    } +	} +    } + +  return 0; +} + +/* Return the reg-note of kind KIND in insn INSN, if there is one. +   If DATUM is nonzero, look for one whose datum is DATUM.  */ + +rtx +find_reg_note (insn, kind, datum) +     rtx insn; +     enum reg_note kind; +     rtx datum; +{ +  register rtx link; + +  for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) +    if (REG_NOTE_KIND (link) == kind +	&& (datum == 0 || datum == XEXP (link, 0))) +      return link; +  return 0; +} + +/* Return the reg-note of kind KIND in insn INSN which applies to register +   number REGNO, if any.  Return 0 if there is no such reg-note.  Note that +   the REGNO of this NOTE need not be REGNO if REGNO is a hard register; +   it might be the case that the note overlaps REGNO.  */ + +rtx +find_regno_note (insn, kind, regno) +     rtx insn; +     enum reg_note kind; +     int regno; +{ +  register rtx link; + +  for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) +    if (REG_NOTE_KIND (link) == kind +	/* Verify that it is a register, so that scratch and MEM won't cause a +	   problem here.  */ +	&& GET_CODE (XEXP (link, 0)) == REG +	&& REGNO (XEXP (link, 0)) <= regno +	&& ((REGNO (XEXP (link, 0)) +	     + (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1 +		: HARD_REGNO_NREGS (REGNO (XEXP (link, 0)), +				    GET_MODE (XEXP (link, 0))))) +	    > regno)) +      return link; +  return 0; +} + +/* Remove register note NOTE from the REG_NOTES of INSN.  */ + +void +remove_note (insn, note) +     register rtx note; +     register rtx insn; +{ +  register rtx link; + +  if (REG_NOTES (insn) == note) +    { +      REG_NOTES (insn) = XEXP (note, 1); +      return; +    } + +  for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) +    if (XEXP (link, 1) == note) +      { +	XEXP (link, 1) = XEXP (note, 1); +	return; +      } + +  abort (); +} + +/* Nonzero if X contains any volatile memory references +   UNSPEC_VOLATILE operations or volatile ASM_OPERANDS expressions.  */ + +int +volatile_refs_p (x) +     rtx x; +{ +  register RTX_CODE code; + +  code = GET_CODE (x); +  switch (code) +    { +    case LABEL_REF: +    case SYMBOL_REF: +    case CONST_INT: +    case CONST: +    case CONST_DOUBLE: +    case CC0: +    case PC: +    case REG: +    case SCRATCH: +    case CLOBBER: +    case ASM_INPUT: +    case ADDR_VEC: +    case ADDR_DIFF_VEC: +      return 0; + +    case CALL: +    case UNSPEC_VOLATILE: + /* case TRAP_IF: This isn't clear yet.  */ +      return 1; + +    case MEM: +    case ASM_OPERANDS: +      if (MEM_VOLATILE_P (x)) +	return 1; +    } + +  /* Recursively scan the operands of this expression.  */ + +  { +    register char *fmt = GET_RTX_FORMAT (code); +    register int i; +     +    for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) +      { +	if (fmt[i] == 'e') +	  { +	    if (volatile_refs_p (XEXP (x, i))) +	      return 1; +	  } +	if (fmt[i] == 'E') +	  { +	    register int j; +	    for (j = 0; j < XVECLEN (x, i); j++) +	      if (volatile_refs_p (XVECEXP (x, i, j))) +		return 1; +	  } +      } +  } +  return 0; +} + +/* Similar to above, except that it also rejects register pre- and post- +   incrementing.  */ + +int +side_effects_p (x) +     rtx x; +{ +  register RTX_CODE code; + +  code = GET_CODE (x); +  switch (code) +    { +    case LABEL_REF: +    case SYMBOL_REF: +    case CONST_INT: +    case CONST: +    case CONST_DOUBLE: +    case CC0: +    case PC: +    case REG: +    case SCRATCH: +    case ASM_INPUT: +    case ADDR_VEC: +    case ADDR_DIFF_VEC: +      return 0; + +    case CLOBBER: +      /* Reject CLOBBER with a non-VOID mode.  These are made by combine.c +	 when some combination can't be done.  If we see one, don't think +	 that we can simplify the expression.  */ +      return (GET_MODE (x) != VOIDmode); + +    case PRE_INC: +    case PRE_DEC: +    case POST_INC: +    case POST_DEC: +    case CALL: +    case UNSPEC_VOLATILE: + /* case TRAP_IF: This isn't clear yet.  */ +      return 1; + +    case MEM: +    case ASM_OPERANDS: +      if (MEM_VOLATILE_P (x)) +	return 1; +    } + +  /* Recursively scan the operands of this expression.  */ + +  { +    register char *fmt = GET_RTX_FORMAT (code); +    register int i; +     +    for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) +      { +	if (fmt[i] == 'e') +	  { +	    if (side_effects_p (XEXP (x, i))) +	      return 1; +	  } +	if (fmt[i] == 'E') +	  { +	    register int j; +	    for (j = 0; j < XVECLEN (x, i); j++) +	      if (side_effects_p (XVECEXP (x, i, j))) +		return 1; +	  } +      } +  } +  return 0; +} + +/* Return nonzero if evaluating rtx X might cause a trap.  */ + +int +may_trap_p (x) +     rtx x; +{ +  int i; +  enum rtx_code code; +  char *fmt; + +  if (x == 0) +    return 0; +  code = GET_CODE (x); +  switch (code) +    { +      /* Handle these cases quickly.  */ +    case CONST_INT: +    case CONST_DOUBLE: +    case SYMBOL_REF: +    case LABEL_REF: +    case CONST: +    case PC: +    case CC0: +    case REG: +    case SCRATCH: +      return 0; + +      /* Conditional trap can trap!  */ +    case UNSPEC_VOLATILE: +    case TRAP_IF: +      return 1; + +      /* Memory ref can trap unless it's a static var or a stack slot.  */ +    case MEM: +      return rtx_addr_can_trap_p (XEXP (x, 0)); + +      /* Division by a non-constant might trap.  */ +    case DIV: +    case MOD: +    case UDIV: +    case UMOD: +      if (! CONSTANT_P (XEXP (x, 1))) +	return 1; +      /* This was const0_rtx, but by not using that, +	 we can link this file into other programs.  */ +      if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 0) +	return 1; +    default: +      /* Any floating arithmetic may trap.  */ +      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) +	return 1; +    } + +  fmt = GET_RTX_FORMAT (code); +  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) +    { +      if (fmt[i] == 'e') +	{ +	  if (may_trap_p (XEXP (x, i))) +	    return 1; +	} +      else if (fmt[i] == 'E') +	{ +	  register int j; +	  for (j = 0; j < XVECLEN (x, i); j++) +	    if (may_trap_p (XVECEXP (x, i, j))) +	      return 1; +	} +    } +  return 0; +} + +/* Return nonzero if X contains a comparison that is not either EQ or NE, +   i.e., an inequality.  */ + +int +inequality_comparisons_p (x) +     rtx x; +{ +  register char *fmt; +  register int len, i; +  register enum rtx_code code = GET_CODE (x); + +  switch (code) +    { +    case REG: +    case SCRATCH: +    case PC: +    case CC0: +    case CONST_INT: +    case CONST_DOUBLE: +    case CONST: +    case LABEL_REF: +    case SYMBOL_REF: +      return 0; + +    case LT: +    case LTU: +    case GT: +    case GTU: +    case LE: +    case LEU: +    case GE: +    case GEU: +      return 1; +    } + +  len = GET_RTX_LENGTH (code); +  fmt = GET_RTX_FORMAT (code); + +  for (i = 0; i < len; i++) +    { +      if (fmt[i] == 'e') +	{ +	  if (inequality_comparisons_p (XEXP (x, i))) +	    return 1; +	} +      else if (fmt[i] == 'E') +	{ +	  register int j; +	  for (j = XVECLEN (x, i) - 1; j >= 0; j--) +	    if (inequality_comparisons_p (XVECEXP (x, i, j))) +	      return 1; +	} +    } +	     +  return 0; +} + +/* Replace any occurrence of FROM in X with TO. + +   Note that copying is not done so X must not be shared unless all copies +   are to be modified.  */ + +rtx +replace_rtx (x, from, to) +     rtx x, from, to; +{ +  register int i, j; +  register char *fmt; + +  if (x == from) +    return to; + +  /* Allow this function to make replacements in EXPR_LISTs.  */ +  if (x == 0) +    return 0; + +  fmt = GET_RTX_FORMAT (GET_CODE (x)); +  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) +    { +      if (fmt[i] == 'e') +	XEXP (x, i) = replace_rtx (XEXP (x, i), from, to); +      else if (fmt[i] == 'E') +	for (j = XVECLEN (x, i) - 1; j >= 0; j--) +	  XVECEXP (x, i, j) = replace_rtx (XVECEXP (x, i, j), from, to); +    } + +  return x; +}   + +/* Throughout the rtx X, replace many registers according to REG_MAP. +   Return the replacement for X (which may be X with altered contents). +   REG_MAP[R] is the replacement for register R, or 0 for don't replace. +   NREGS is the length of REG_MAP; regs >= NREGS are not mapped.   + +   We only support REG_MAP entries of REG or SUBREG.  Also, hard registers +   should not be mapped to pseudos or vice versa since validate_change +   is not called. + +   If REPLACE_DEST is 1, replacements are also done in destinations; +   otherwise, only sources are replaced.  */ + +rtx +replace_regs (x, reg_map, nregs, replace_dest) +     rtx x; +     rtx *reg_map; +     int nregs; +     int replace_dest; +{ +  register enum rtx_code code; +  register int i; +  register char *fmt; + +  if (x == 0) +    return x; + +  code = GET_CODE (x); +  switch (code) +    { +    case SCRATCH: +    case PC: +    case CC0: +    case CONST_INT: +    case CONST_DOUBLE: +    case CONST: +    case SYMBOL_REF: +    case LABEL_REF: +      return x; + +    case REG: +      /* Verify that the register has an entry before trying to access it.  */ +      if (REGNO (x) < nregs && reg_map[REGNO (x)] != 0) +	return reg_map[REGNO (x)]; +      return x; + +    case SUBREG: +      /* Prevent making nested SUBREGs.  */ +      if (GET_CODE (SUBREG_REG (x)) == REG && REGNO (SUBREG_REG (x)) < nregs +	  && reg_map[REGNO (SUBREG_REG (x))] != 0 +	  && GET_CODE (reg_map[REGNO (SUBREG_REG (x))]) == SUBREG) +	{ +	  rtx map_val = reg_map[REGNO (SUBREG_REG (x))]; +	  rtx map_inner = SUBREG_REG (map_val); + +	  if (GET_MODE (x) == GET_MODE (map_inner)) +	    return map_inner; +	  else +	    { +	      /* We cannot call gen_rtx here since we may be linked with +		 genattrtab.c.  */ +	      /* Let's try clobbering the incoming SUBREG and see +		 if this is really safe.  */ +	      SUBREG_REG (x) = map_inner; +	      SUBREG_WORD (x) += SUBREG_WORD (map_val); +	      return x; +#if 0 +	      rtx new = rtx_alloc (SUBREG); +	      PUT_MODE (new, GET_MODE (x)); +	      SUBREG_REG (new) = map_inner; +	      SUBREG_WORD (new) = SUBREG_WORD (x) + SUBREG_WORD (map_val); +#endif +	    } +	} +      break; + +    case SET: +      if (replace_dest) +	SET_DEST (x) = replace_regs (SET_DEST (x), reg_map, nregs, 0); + +      else if (GET_CODE (SET_DEST (x)) == MEM +	       || GET_CODE (SET_DEST (x)) == STRICT_LOW_PART) +	/* Even if we are not to replace destinations, replace register if it +	   is CONTAINED in destination (destination is memory or +	   STRICT_LOW_PART).  */ +	XEXP (SET_DEST (x), 0) = replace_regs (XEXP (SET_DEST (x), 0), +					       reg_map, nregs, 0); +      else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT) +	/* Similarly, for ZERO_EXTRACT we replace all operands.  */ +	break; + +      SET_SRC (x) = replace_regs (SET_SRC (x), reg_map, nregs, 0); +      return x; +    } + +  fmt = GET_RTX_FORMAT (code); +  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) +    { +      if (fmt[i] == 'e') +	XEXP (x, i) = replace_regs (XEXP (x, i), reg_map, nregs, replace_dest); +      if (fmt[i] == 'E') +	{ +	  register int j; +	  for (j = 0; j < XVECLEN (x, i); j++) +	    XVECEXP (x, i, j) = replace_regs (XVECEXP (x, i, j), reg_map, +					      nregs, replace_dest); +	} +    } +  return x; +} | 
