From:	CSBVAX::MRGATE!rms@wheaties.ai.mit.edu@SMTP  9-SEP-1988 23:08
To:	ARISIA::EVERHART
Subj:	GCC 1.27 bug fix


Received: from sugar-bombs.ai.mit.edu by prep.ai.mit.edu; Fri, 9 Sep 88 19:37:02 EST
Received: by sugar-bombs.ai.mit.edu; Fri, 9 Sep 88 21:12:11 EDT
Date: Fri, 9 Sep 88 21:12:11 EDT
From: rms@wheaties.ai.mit.edu (Richard Stallman)
Message-Id: <8809100112.AA00818@sugar-bombs.ai.mit.edu>
To: info-gcc@prep.ai.mit.edu
Subject: GCC 1.27 bug fix

The following corrected function in combine.c should fix many of the
problems people have reported in 1.27 on various machines.

static rtx
try_distrib (insn, xprev1, xprev2)
     rtx insn, xprev1, xprev2;
{
  rtx pat = PATTERN (insn);
  rtx prev1, prev2, pat1, pat2, src1, src2;
  rtx to_prev, to_insn;
  enum rtx_code code;
  int insn_code_number, prev_code_number, regno, length;
  rtx new_insn_pat, new_prev_pat;

  distrib_attempts++;

  /* ??? Need to implement a test that PREV2 and PREV1
     are completely independent.  Right now their
     recognition ability is sufficiently limited that
     it should not be necessary, but better safe than sorry.  */

  /* Let PREV1 be the later of the two insns, and PREV2 the earlier.  */
  if (INSN_CUID (xprev1) > INSN_CUID (xprev2))
    {
      prev1 = xprev1;
      prev2 = xprev2;
    }
  else
    {
      prev1 = xprev2;
      prev2 = xprev1;
    }

  pat1 = PATTERN (prev1);
  pat2 = PATTERN (prev2);

  /* First, see if INSN, PREV1, and PREV2 have patterns we can expect
     to simplify.  */

  if (GET_CODE (pat) != SET
      || GET_CODE (pat1) != SET
      || GET_CODE (pat2) != SET)
    return 0;

  code = GET_CODE (SET_SRC (pat));
  src1 = SET_SRC (pat1);
  src2 = SET_SRC (pat2);

  if (GET_CODE (SET_DEST (pat1)) != REG
      || GET_CODE (SET_DEST (pat2)) != REG)
    return 0;

  switch (code)
    {
    default:
      return 0;

    case IOR:
    case AND:
    case XOR:
    case PLUS:
      ;
    }

  /* Insns PREV1 and PREV2 must provide the two operands of the arithmetic
     that is done in INSN.  */
  if (! ((XEXP (SET_SRC (pat), 0) == SET_DEST (pat1)
	  && XEXP (SET_SRC (pat), 1) == SET_DEST (pat2))
	 ||
	 (XEXP (SET_SRC (pat), 0) == SET_DEST (pat2)
	  && XEXP (SET_SRC (pat), 1) == SET_DEST (pat1))))
    return 0;

  /* They must not be used in any other way in INSN.
     In particular, they must not be used in a result memory address.  */
  if (reg_mentioned_p (SET_DEST (pat1), SET_DEST (pat))
      || reg_mentioned_p (SET_DEST (pat2), SET_DEST (pat)))
    return 0;

  /* Give up if the two operands' modes don't match.  */
  if (GET_MODE (src1) != GET_MODE (src2))
    return 0;

  /* PREV1 and PREV2 must compute the same operation.
     Actually, there are other cases that could be handled,
     but are not implemented.  For example:

     (set (reg:SI 94)
	  (and:SI (reg:SI 73)
		  (const_int 223)))

     (set (reg:SI 95)
	  (zero_extend:SI (subreg:QI (reg:SI 91) 0)))

     (set (reg:SI 96)
	  (ior:SI (reg:SI 94)
		  (reg:SI 95)))

     In this case, we know that because (reg:SI 94) has
     been anded with 223, there is no need to zero_extend
     (reg:SI 91), and we could eliminate (reg:SI 95).  */

  if (GET_CODE (src1) != GET_CODE (src2))
    return 0;

  /* The SETs in PREV1 and PREV2 do not need to be kept around.  */

  undobuf.num_undo = 0;
  undobuf.storage = 0;

  /* Substitute in the latest insn for the regs set by the earlier ones.  */
  subst_insn = insn;
  n_occurrences = 0;	/* `subst' counts here */

  switch (GET_CODE (src1))
    {
    case LSHIFTRT:
    case ASHIFTRT:
      if (code == PLUS)
	return 0;

    case AND:
    case LSHIFT:
    case ASHIFT:
    case IOR:
    case XOR:
      /* Try changing (+ (* x c) (* y c)) to (* (+ (x y) c)).  */

      if (GET_CODE (XEXP (src1, 1)) != CONST_INT
	  || GET_CODE (XEXP (src2, 1)) != CONST_INT
	  || INTVAL (XEXP (src1, 1)) != INTVAL (XEXP (src2, 1)))
	return 0;
      to_prev = gen_rtx (code, GET_MODE (src1),
			 XEXP (src1, 0), XEXP (src2, 0));
      to_insn = gen_rtx (GET_CODE (src1), GET_MODE (src1), SET_DEST (pat1), XEXP (src1, 1));
      break;

    case ZERO_EXTEND:
    case SIGN_EXTEND:
      {
	rtx inner1 = XEXP (src1, 0), inner2 = XEXP (src2, 0);
	int subreg_needed = 0;

	/* Try changing (+ ($ x) ($ y)) to ($ (+ (x y))).  */
	/* But keep extend insns together with their subregs.  */
	if (GET_CODE (inner1) == SUBREG)
	  if (SUBREG_WORD (inner1) != 0)
	    return 0;
	  else
	    {
	      subreg_needed = 1;
	      inner1 = SUBREG_REG (inner1);
	    }

	if (GET_CODE (inner2) == SUBREG)
	  if (SUBREG_WORD (inner2) != 0)
	    return 0;
	  else
	    {
	      subreg_needed = 1;
	      inner2 = SUBREG_REG (inner2);
	    }

	to_prev = gen_rtx (code, GET_MODE (src1), inner1, inner2);
	to_insn = gen_rtx (GET_CODE (src1), GET_MODE (src1),
			   subreg_needed
			   ? gen_rtx (SUBREG, GET_MODE (XEXP (src1, 0)),
				      SET_DEST (pat1), 0)
			   : SET_DEST (pat1));
      }
      break;

    default:
      return 0;
    }

  /* Are the results of this "substitution" a valid instruction?  */

  new_insn_pat = subst (PATTERN (insn), SET_SRC (PATTERN (insn)), to_insn);
  distrib_merges_1++;

  insn_code_number = recog (new_insn_pat, insn);
  if (insn_code_number < 0)
    {
      undo_all ();
      return 0;
    }

  subst_insn = prev1;
  new_prev_pat = subst (pat1, src1, to_prev);
  distrib_merges_2++;

  prev_code_number = recog (new_prev_pat, prev1);
  if (prev_code_number < 0)
    {
      undo_all ();
      return 0;
    }

  /* Everything worked; install the new patterns.  */
  INSN_CODE (insn) = insn_code_number;
  PATTERN (insn) = new_insn_pat;

  INSN_CODE (prev1) = prev_code_number;
  PATTERN (prev1) = new_prev_pat;

  /* Need to change LOG_LINKS around...PREV1 now gets
     whatever flowed into PREV2.  PREV2 is going to
     become a NOTE, so we clear out its LOG_LINKS.  */
  remove_links (insn, prev2);
  add_links (prev1, prev2, adjacent_insns_p (prev2, prev1));

  /* Registers which died in PREV2 now die in PREV1.
     Also, registers born in PREV2 dying in INSN now die in PREV1.  */
  move_deaths_2 (src2, INSN_CUID (prev2), insn, prev1);

  regno = REGNO (SET_DEST (pat2));

  reg_n_sets[regno]--;
  if (reg_n_sets[regno] == 0
      && ! (basic_block_live_at_start[0][regno / HOST_BITS_PER_INT]
	    & (1 << (regno % HOST_BITS_PER_INT))))
    reg_n_refs[regno] = 0;
  remove_death (regno, insn);

  PUT_CODE (prev2, NOTE);
  NOTE_LINE_NUMBER (prev2) = NOTE_INSN_DELETED;
  NOTE_SOURCE_FILE (prev2) = 0;

  distrib_successes++;
  return prev1;
}
