Ejemplo n.º 1
0
static void
adjust_frame_related_expr (rtx last_sp_set, rtx insn,
			   HOST_WIDE_INT this_adjust)
{
  rtx note = find_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, NULL_RTX);
  rtx new_expr = NULL_RTX;

  if (note == NULL_RTX && RTX_FRAME_RELATED_P (insn))
    return;

  if (note
      && GET_CODE (XEXP (note, 0)) == SEQUENCE
      && XVECLEN (XEXP (note, 0), 0) >= 2)
    {
      rtx expr = XEXP (note, 0);
      rtx last = XVECEXP (expr, 0, XVECLEN (expr, 0) - 1);
      int i;

      if (GET_CODE (last) == SET
	  && RTX_FRAME_RELATED_P (last) == RTX_FRAME_RELATED_P (insn)
	  && SET_DEST (last) == stack_pointer_rtx
	  && GET_CODE (SET_SRC (last)) == PLUS
	  && XEXP (SET_SRC (last), 0) == stack_pointer_rtx
	  && CONST_INT_P (XEXP (SET_SRC (last), 1)))
	{
	  XEXP (SET_SRC (last), 1)
	    = GEN_INT (INTVAL (XEXP (SET_SRC (last), 1)) + this_adjust);
	  return;
	}

      new_expr = gen_rtx_SEQUENCE (VOIDmode,
				   rtvec_alloc (XVECLEN (expr, 0) + 1));
      for (i = 0; i < XVECLEN (expr, 0); i++)
	XVECEXP (new_expr, 0, i) = XVECEXP (expr, 0, i);
    }
  else
    {
      new_expr = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2));
      if (note)
	XVECEXP (new_expr, 0, 0) = XEXP (note, 0);
      else
	{
	  rtx expr = copy_rtx (single_set_for_csa (last_sp_set));

	  XEXP (SET_SRC (expr), 1)
	    = GEN_INT (INTVAL (XEXP (SET_SRC (expr), 1)) - this_adjust);
	  RTX_FRAME_RELATED_P (expr) = 1;
	  XVECEXP (new_expr, 0, 0) = expr;
	}
    }

  XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1)
    = copy_rtx (single_set_for_csa (insn));
  RTX_FRAME_RELATED_P (XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1))
    = RTX_FRAME_RELATED_P (insn);
  if (note)
    XEXP (note, 0) = new_expr;
  else
    add_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, new_expr);
}
Ejemplo n.º 2
0
rtx
compare_and_jump_seq (rtx op0, rtx op1, enum rtx_code comp, rtx label, int prob,
		      rtx cinsn)
{
  rtx seq, jump, cond;
  enum machine_mode mode;

  mode = GET_MODE (op0);
  if (mode == VOIDmode)
    mode = GET_MODE (op1);

  start_sequence ();
  if (GET_MODE_CLASS (mode) == MODE_CC)
    {
      /* A hack -- there seems to be no easy generic way how to make a
	 conditional jump from a ccmode comparison.  */
      gcc_assert (cinsn);
      cond = XEXP (SET_SRC (pc_set (cinsn)), 0);
      gcc_assert (GET_CODE (cond) == comp);
      gcc_assert (rtx_equal_p (op0, XEXP (cond, 0)));
      gcc_assert (rtx_equal_p (op1, XEXP (cond, 1)));
      emit_jump_insn (copy_insn (PATTERN (cinsn)));
      jump = get_last_insn ();
      gcc_assert (JUMP_P (jump));
      JUMP_LABEL (jump) = JUMP_LABEL (cinsn);
      LABEL_NUSES (JUMP_LABEL (jump))++;
      redirect_jump (jump, label, 0);
    }
  else
    {
      gcc_assert (!cinsn);

      op0 = force_operand (op0, NULL_RTX);
      op1 = force_operand (op1, NULL_RTX);
      do_compare_rtx_and_jump (op0, op1, comp, 0,
			       mode, NULL_RTX, NULL_RTX, label, -1);
      jump = get_last_insn ();
      gcc_assert (JUMP_P (jump));
      JUMP_LABEL (jump) = label;
      LABEL_NUSES (label)++;
    }
  add_reg_note (jump, REG_BR_PROB, GEN_INT (prob));

  seq = get_insns ();
  end_sequence ();

  return seq;
}
Ejemplo n.º 3
0
static void
maybe_move_args_size_note (rtx last, rtx insn, bool after)
{
    rtx note, last_note;

    note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
    if (note == NULL)
        return;

    last_note = find_reg_note (last, REG_ARGS_SIZE, NULL_RTX);
    if (last_note)
    {
        /* The ARGS_SIZE notes are *not* cumulative.  They represent an
        absolute value, and the "most recent" note wins.  */
        if (!after)
            XEXP (last_note, 0) = XEXP (note, 0);
    }
    else
        add_reg_note (last, REG_ARGS_SIZE, XEXP (note, 0));
}
Ejemplo n.º 4
0
void
do_compare_rtx_and_jump (rtx op0, rtx op1, enum rtx_code code, int unsignedp,
			 enum machine_mode mode, rtx size, rtx if_false_label,
			 rtx if_true_label, int prob)
{
  rtx tem;
  rtx dummy_label = NULL_RTX;
  rtx last;

  /* Reverse the comparison if that is safe and we want to jump if it is
     false.  Also convert to the reverse comparison if the target can
     implement it.  */
  if ((! if_true_label
       || ! can_compare_p (code, mode, ccp_jump))
      && (! FLOAT_MODE_P (mode)
	  || code == ORDERED || code == UNORDERED
	  || (! HONOR_NANS (mode) && (code == LTGT || code == UNEQ))
	  || (! HONOR_SNANS (mode) && (code == EQ || code == NE))))
    {
      enum rtx_code rcode;
      if (FLOAT_MODE_P (mode))
        rcode = reverse_condition_maybe_unordered (code);
      else
        rcode = reverse_condition (code);

      /* Canonicalize to UNORDERED for the libcall.  */
      if (can_compare_p (rcode, mode, ccp_jump)
	  || (code == ORDERED && ! can_compare_p (ORDERED, mode, ccp_jump)))
	{
          tem = if_true_label;
          if_true_label = if_false_label;
          if_false_label = tem;
	  code = rcode;
	  prob = inv (prob);
	}
    }

  /* If one operand is constant, make it the second one.  Only do this
     if the other operand is not constant as well.  */

  if (swap_commutative_operands_p (op0, op1))
    {
      tem = op0;
      op0 = op1;
      op1 = tem;
      code = swap_condition (code);
    }

  do_pending_stack_adjust ();

  code = unsignedp ? unsigned_condition (code) : code;
  if (0 != (tem = simplify_relational_operation (code, mode, VOIDmode,
						 op0, op1)))
    {
      if (CONSTANT_P (tem))
	{
	  rtx label = (tem == const0_rtx || tem == CONST0_RTX (mode))
		      ? if_false_label : if_true_label;
	  if (label)
	    emit_jump (label);
	  return;
	}

      code = GET_CODE (tem);
      mode = GET_MODE (tem);
      op0 = XEXP (tem, 0);
      op1 = XEXP (tem, 1);
      unsignedp = (code == GTU || code == LTU || code == GEU || code == LEU);
    }

  if (! if_true_label)
    dummy_label = if_true_label = gen_label_rtx ();

  if (GET_MODE_CLASS (mode) == MODE_INT
      && ! can_compare_p (code, mode, ccp_jump))
    {
      switch (code)
	{
	case LTU:
	  do_jump_by_parts_greater_rtx (mode, 1, op1, op0,
					if_false_label, if_true_label, prob);
	  break;

	case LEU:
	  do_jump_by_parts_greater_rtx (mode, 1, op0, op1,
					if_true_label, if_false_label,
					inv (prob));
	  break;

	case GTU:
	  do_jump_by_parts_greater_rtx (mode, 1, op0, op1,
					if_false_label, if_true_label, prob);
	  break;

	case GEU:
	  do_jump_by_parts_greater_rtx (mode, 1, op1, op0,
					if_true_label, if_false_label,
					inv (prob));
	  break;

	case LT:
	  do_jump_by_parts_greater_rtx (mode, 0, op1, op0,
					if_false_label, if_true_label, prob);
	  break;

	case LE:
	  do_jump_by_parts_greater_rtx (mode, 0, op0, op1,
					if_true_label, if_false_label,
					inv (prob));
	  break;

	case GT:
	  do_jump_by_parts_greater_rtx (mode, 0, op0, op1,
					if_false_label, if_true_label, prob);
	  break;

	case GE:
	  do_jump_by_parts_greater_rtx (mode, 0, op1, op0,
					if_true_label, if_false_label,
					inv (prob));
	  break;

	case EQ:
	  do_jump_by_parts_equality_rtx (mode, op0, op1, if_false_label,
					 if_true_label, prob);
	  break;

	case NE:
	  do_jump_by_parts_equality_rtx (mode, op0, op1, if_true_label,
					 if_false_label, inv (prob));
	  break;

	default:
	  gcc_unreachable ();
	}
    }
  else
    {
      if (GET_MODE_CLASS (mode) == MODE_FLOAT
	  && ! can_compare_p (code, mode, ccp_jump)
	  && can_compare_p (swap_condition (code), mode, ccp_jump))
	{
	  rtx tmp;
	  code = swap_condition (code);
	  tmp = op0;
	  op0 = op1;
	  op1 = tmp;
	}

      else if (GET_MODE_CLASS (mode) == MODE_FLOAT
	       && ! can_compare_p (code, mode, ccp_jump)

	       /* Never split ORDERED and UNORDERED.  These must be implemented.  */
	       && (code != ORDERED && code != UNORDERED)

               /* Split a floating-point comparison if we can jump on other
	          conditions...  */
	       && (have_insn_for (COMPARE, mode)

	           /* ... or if there is no libcall for it.  */
	           || code_to_optab[code] == NULL))
        {
	  enum rtx_code first_code;
	  bool and_them = split_comparison (code, mode, &first_code, &code);

	  /* If there are no NaNs, the first comparison should always fall
	     through.  */
	  if (!HONOR_NANS (mode))
	    gcc_assert (first_code == (and_them ? ORDERED : UNORDERED));

	  else
	    {
	      if (and_them)
		{
		  rtx dest_label;
		  /* If we only jump if true, just bypass the second jump.  */
		  if (! if_false_label)
		    {
		      if (! dummy_label)
		        dummy_label = gen_label_rtx ();
		      dest_label = dummy_label;
		    }
		  else
		    dest_label = if_false_label;
                  do_compare_rtx_and_jump (op0, op1, first_code, unsignedp, mode,
					   size, dest_label, NULL_RTX, prob);
		}
              else
                do_compare_rtx_and_jump (op0, op1, first_code, unsignedp, mode,
					 size, NULL_RTX, if_true_label, prob);
	    }
	}

      last = get_last_insn ();
      emit_cmp_and_jump_insns (op0, op1, code, size, mode, unsignedp,
			       if_true_label);
      if (prob != -1 && profile_status != PROFILE_ABSENT)
	{
	  for (last = NEXT_INSN (last);
	       last && NEXT_INSN (last);
	       last = NEXT_INSN (last))
	    if (JUMP_P (last))
	      break;
	  if (!last
	      || !JUMP_P (last)
	      || NEXT_INSN (last)
	      || !any_condjump_p (last))
	    {
	      if (dump_file)
		fprintf (dump_file, "Failed to add probability note\n");
	    }
	  else
	    {
	      gcc_assert (!find_reg_note (last, REG_BR_PROB, 0));
	      add_reg_note (last, REG_BR_PROB, GEN_INT (prob));
	    }
	}
    }

  if (if_false_label)
    emit_jump (if_false_label);
  if (dummy_label)
    emit_label (dummy_label);
}
Ejemplo n.º 5
0
static void
force_move_args_size_note (basic_block bb, rtx prev, rtx insn)
{
    rtx note, test, next_candidate, prev_candidate;

    /* If PREV exists, tail-call to the logic in the other function.  */
    if (prev)
    {
        maybe_move_args_size_note (prev, insn, false);
        return;
    }

    /* First, make sure there's anything that needs doing.  */
    note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
    if (note == NULL)
        return;

    /* We need to find a spot between the previous and next exception points
       where we can place the note and "properly" deallocate the arguments.  */
    next_candidate = prev_candidate = NULL;

    /* It is often the case that we have insns in the order:
    call
    add sp (previous deallocation)
    sub sp (align for next arglist)
    push arg
       and the add/sub cancel.  Therefore we begin by searching forward.  */

    test = insn;
    while ((test = next_active_insn_bb (bb, test)) != NULL)
    {
        /* Found an existing note: nothing to do.  */
        if (find_reg_note (test, REG_ARGS_SIZE, NULL_RTX))
            return;
        /* Found something that affects unwinding.  Stop searching.  */
        if (CALL_P (test) || !insn_nothrow_p (test))
            break;
        if (next_candidate == NULL)
            next_candidate = test;
    }

    test = insn;
    while ((test = prev_active_insn_bb (bb, test)) != NULL)
    {
        rtx tnote;
        /* Found a place that seems logical to adjust the stack.  */
        tnote = find_reg_note (test, REG_ARGS_SIZE, NULL_RTX);
        if (tnote)
        {
            XEXP (tnote, 0) = XEXP (note, 0);
            return;
        }
        if (prev_candidate == NULL)
            prev_candidate = test;
        /* Found something that affects unwinding.  Stop searching.  */
        if (CALL_P (test) || !insn_nothrow_p (test))
            break;
    }

    if (prev_candidate)
        test = prev_candidate;
    else if (next_candidate)
        test = next_candidate;
    else
    {
        /* ??? We *must* have a place, lest we ICE on the lost adjustment.
        Options are: dummy clobber insn, nop, or prevent the removal of
         the sp += 0 insn.  */
        /* TODO: Find another way to indicate to the dwarf2 code that we
        have not in fact lost an adjustment.  */
        test = emit_insn_before (gen_rtx_CLOBBER (VOIDmode, const0_rtx), insn);
    }
    add_reg_note (test, REG_ARGS_SIZE, XEXP (note, 0));
}
Ejemplo n.º 6
0
static bool
attempt_change (rtx new_addr, rtx inc_reg)
{
  /* There are four cases: For the two cases that involve an add
     instruction, we are going to have to delete the add and insert a
     mov.  We are going to assume that the mov is free.  This is
     fairly early in the backend and there are a lot of opportunities
     for removing that move later.  In particular, there is the case
     where the move may be dead, this is what dead code elimination
     passes are for.  The two cases where we have an inc insn will be
     handled mov free.  */

  basic_block bb = BLOCK_FOR_INSN (mem_insn.insn);
  rtx mov_insn = NULL;
  int regno;
  rtx mem = *mem_insn.mem_loc;
  enum machine_mode mode = GET_MODE (mem);
  rtx new_mem;
  int old_cost = 0;
  int new_cost = 0;
  bool speed = optimize_bb_for_speed_p (bb);

  PUT_MODE (mem_tmp, mode);
  XEXP (mem_tmp, 0) = new_addr;

  old_cost = (set_src_cost (mem, speed)
	      + set_rtx_cost (PATTERN (inc_insn.insn), speed));
  new_cost = set_src_cost (mem_tmp, speed);

  /* The first item of business is to see if this is profitable.  */
  if (old_cost < new_cost)
    {
      if (dump_file)
	fprintf (dump_file, "cost failure old=%d new=%d\n", old_cost, new_cost);
      return false;
    }

  /* Jump through a lot of hoops to keep the attributes up to date.  We
     do not want to call one of the change address variants that take
     an offset even though we know the offset in many cases.  These
     assume you are changing where the address is pointing by the
     offset.  */
  new_mem = replace_equiv_address_nv (mem, new_addr);
  if (! validate_change (mem_insn.insn, mem_insn.mem_loc, new_mem, 0))
    {
      if (dump_file)
	fprintf (dump_file, "validation failure\n");
      return false;
    }

  /* From here to the end of the function we are committed to the
     change, i.e. nothing fails.  Generate any necessary movs, move
     any regnotes, and fix up the reg_next_{use,inc_use,def}.  */
  switch (inc_insn.form)
    {
    case FORM_PRE_ADD:
      /* Replace the addition with a move.  Do it at the location of
	 the addition since the operand of the addition may change
	 before the memory reference.  */
      mov_insn = insert_move_insn_before (inc_insn.insn,
					  inc_insn.reg_res, inc_insn.reg0);
      move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0);

      regno = REGNO (inc_insn.reg_res);
      reg_next_def[regno] = mov_insn;
      reg_next_use[regno] = NULL;
      regno = REGNO (inc_insn.reg0);
      reg_next_use[regno] = mov_insn;
      df_recompute_luids (bb);
      break;

    case FORM_POST_INC:
      regno = REGNO (inc_insn.reg_res);
      if (reg_next_use[regno] == reg_next_inc_use[regno])
	reg_next_inc_use[regno] = NULL;

      /* Fallthru.  */
    case FORM_PRE_INC:
      regno = REGNO (inc_insn.reg_res);
      reg_next_def[regno] = mem_insn.insn;
      reg_next_use[regno] = NULL;

      break;

    case FORM_POST_ADD:
      mov_insn = insert_move_insn_before (mem_insn.insn,
					  inc_insn.reg_res, inc_insn.reg0);
      move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0);

      /* Do not move anything to the mov insn because the instruction
	 pointer for the main iteration has not yet hit that.  It is
	 still pointing to the mem insn. */
      regno = REGNO (inc_insn.reg_res);
      reg_next_def[regno] = mem_insn.insn;
      reg_next_use[regno] = NULL;

      regno = REGNO (inc_insn.reg0);
      reg_next_use[regno] = mem_insn.insn;
      if ((reg_next_use[regno] == reg_next_inc_use[regno])
	  || (reg_next_inc_use[regno] == inc_insn.insn))
	reg_next_inc_use[regno] = NULL;
      df_recompute_luids (bb);
      break;

    case FORM_last:
    default:
      gcc_unreachable ();
    }

  if (!inc_insn.reg1_is_const)
    {
      regno = REGNO (inc_insn.reg1);
      reg_next_use[regno] = mem_insn.insn;
      if ((reg_next_use[regno] == reg_next_inc_use[regno])
	  || (reg_next_inc_use[regno] == inc_insn.insn))
	reg_next_inc_use[regno] = NULL;
    }

  delete_insn (inc_insn.insn);

  if (dump_file && mov_insn)
    {
      fprintf (dump_file, "inserting mov ");
      dump_insn_slim (dump_file, mov_insn);
    }

  /* Record that this insn has an implicit side effect.  */
  add_reg_note (mem_insn.insn, REG_INC, inc_reg);

  if (dump_file)
    {
      fprintf (dump_file, "****success ");
      dump_insn_slim (dump_file, mem_insn.insn);
    }

  return true;
}
Ejemplo n.º 7
0
void
eliminate_regs_in_insn (rtx_insn *insn, bool replace_p, bool first_p,
			HOST_WIDE_INT update_sp_offset)
{
  int icode = recog_memoized (insn);
  rtx old_set = single_set (insn);
  bool validate_p;
  int i;
  rtx substed_operand[MAX_RECOG_OPERANDS];
  rtx orig_operand[MAX_RECOG_OPERANDS];
  struct lra_elim_table *ep;
  rtx plus_src, plus_cst_src;
  lra_insn_recog_data_t id;
  struct lra_static_insn_data *static_id;

  if (icode < 0 && asm_noperands (PATTERN (insn)) < 0 && ! DEBUG_INSN_P (insn))
    {
      lra_assert (GET_CODE (PATTERN (insn)) == USE
		  || GET_CODE (PATTERN (insn)) == CLOBBER
		  || GET_CODE (PATTERN (insn)) == ASM_INPUT);
      return;
    }

  /* Check for setting an eliminable register.	*/
  if (old_set != 0 && REG_P (SET_DEST (old_set))
      && (ep = get_elimination (SET_DEST (old_set))) != NULL)
    {
      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
	if (ep->from_rtx == SET_DEST (old_set) && ep->can_eliminate)
	  {
	    bool delete_p = replace_p;
	    
#ifdef HARD_FRAME_POINTER_REGNUM
	    if (ep->from == FRAME_POINTER_REGNUM
		&& ep->to == HARD_FRAME_POINTER_REGNUM)
	      /* If this is setting the frame pointer register to the
		 hardware frame pointer register and this is an
		 elimination that will be done (tested above), this
		 insn is really adjusting the frame pointer downward
		 to compensate for the adjustment done before a
		 nonlocal goto.  */
	      {
		rtx src = SET_SRC (old_set);
		rtx off = remove_reg_equal_offset_note (insn, ep->to_rtx);
		
		/* We should never process such insn with non-zero
		   UPDATE_SP_OFFSET.  */
		lra_assert (update_sp_offset == 0);
		
		if (off != NULL_RTX
		    || src == ep->to_rtx
		    || (GET_CODE (src) == PLUS
			&& XEXP (src, 0) == ep->to_rtx
			&& CONST_INT_P (XEXP (src, 1))))
		  {
		    HOST_WIDE_INT offset;
		    
		    if (replace_p)
		      {
			SET_DEST (old_set) = ep->to_rtx;
			lra_update_insn_recog_data (insn);
			return;
		      }
		    offset = (off != NULL_RTX ? INTVAL (off)
			      : src == ep->to_rtx ? 0 : INTVAL (XEXP (src, 1)));
		    offset -= (ep->offset - ep->previous_offset);
		    src = plus_constant (Pmode, ep->to_rtx, offset);
		    
		    /* First see if this insn remains valid when we
		       make the change.  If not, keep the INSN_CODE
		       the same and let the constraint pass fit it
		       up.  */
		    validate_change (insn, &SET_SRC (old_set), src, 1);
		    validate_change (insn, &SET_DEST (old_set),
				     ep->from_rtx, 1);
		    if (! apply_change_group ())
		      {
			SET_SRC (old_set) = src;
			SET_DEST (old_set) = ep->from_rtx;
		      }
		    lra_update_insn_recog_data (insn);
		    /* Add offset note for future updates.  */
		    add_reg_note (insn, REG_EQUAL, src);
		    return;
		  }
	      }
#endif
	    
	    /* This insn isn't serving a useful purpose.  We delete it
	       when REPLACE is set.  */
	    if (delete_p)
	      lra_delete_dead_insn (insn);
	    return;
	  }
    }

  /* We allow one special case which happens to work on all machines we
     currently support: a single set with the source or a REG_EQUAL
     note being a PLUS of an eliminable register and a constant.  */
  plus_src = plus_cst_src = 0;
  if (old_set && REG_P (SET_DEST (old_set)))
    {
      if (GET_CODE (SET_SRC (old_set)) == PLUS)
	plus_src = SET_SRC (old_set);
      /* First see if the source is of the form (plus (...) CST).  */
      if (plus_src
	  && CONST_INT_P (XEXP (plus_src, 1)))
	plus_cst_src = plus_src;
      /* Check that the first operand of the PLUS is a hard reg or
	 the lowpart subreg of one.  */
      if (plus_cst_src)
	{
	  rtx reg = XEXP (plus_cst_src, 0);

	  if (GET_CODE (reg) == SUBREG && subreg_lowpart_p (reg))
	    reg = SUBREG_REG (reg);

	  if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
	    plus_cst_src = 0;
	}
    }
  if (plus_cst_src)
    {
      rtx reg = XEXP (plus_cst_src, 0);
      HOST_WIDE_INT offset = INTVAL (XEXP (plus_cst_src, 1));

      if (GET_CODE (reg) == SUBREG)
	reg = SUBREG_REG (reg);

      if (REG_P (reg) && (ep = get_elimination (reg)) != NULL)
	{
	  rtx to_rtx = replace_p ? ep->to_rtx : ep->from_rtx;

	  if (! replace_p)
	    {
	      if (update_sp_offset == 0)
		offset += (ep->offset - ep->previous_offset);
	      if (ep->to_rtx == stack_pointer_rtx)
		{
		  if (first_p)
		    offset -= lra_get_insn_recog_data (insn)->sp_offset;
		  else
		    offset += update_sp_offset;
		}
	      offset = trunc_int_for_mode (offset, GET_MODE (plus_cst_src));
	    }

	  if (GET_CODE (XEXP (plus_cst_src, 0)) == SUBREG)
	    to_rtx = gen_lowpart (GET_MODE (XEXP (plus_cst_src, 0)), to_rtx);
	  /* If we have a nonzero offset, and the source is already a
	     simple REG, the following transformation would increase
	     the cost of the insn by replacing a simple REG with (plus
	     (reg sp) CST).  So try only when we already had a PLUS
	     before.  */
	  if (offset == 0 || plus_src)
	    {
	      rtx new_src = plus_constant (GET_MODE (to_rtx), to_rtx, offset);

	      old_set = single_set (insn);

	      /* First see if this insn remains valid when we make the
		 change.  If not, try to replace the whole pattern
		 with a simple set (this may help if the original insn
		 was a PARALLEL that was only recognized as single_set
		 due to REG_UNUSED notes).  If this isn't valid
		 either, keep the INSN_CODE the same and let the
		 constraint pass fix it up.  */
	      if (! validate_change (insn, &SET_SRC (old_set), new_src, 0))
		{
		  rtx new_pat = gen_rtx_SET (SET_DEST (old_set), new_src);

		  if (! validate_change (insn, &PATTERN (insn), new_pat, 0))
		    SET_SRC (old_set) = new_src;
		}
	      lra_update_insn_recog_data (insn);
	      /* This can't have an effect on elimination offsets, so skip
		 right to the end.  */
	      return;
	    }
	}
    }

  /* Eliminate all eliminable registers occurring in operands that
     can be handled by the constraint pass.  */
  id = lra_get_insn_recog_data (insn);
  static_id = id->insn_static_data;
  validate_p = false;
  for (i = 0; i < static_id->n_operands; i++)
    {
      orig_operand[i] = *id->operand_loc[i];
      substed_operand[i] = *id->operand_loc[i];

      /* For an asm statement, every operand is eliminable.  */
      if (icode < 0 || insn_data[icode].operand[i].eliminable)
	{
	  /* Check for setting a hard register that we know about.  */
	  if (static_id->operand[i].type != OP_IN
	      && REG_P (orig_operand[i]))
	    {
	      /* If we are assigning to a hard register that can be
		 eliminated, it must be as part of a PARALLEL, since
		 the code above handles single SETs.  This reg can not
		 be longer eliminated -- it is forced by
		 mark_not_eliminable.  */
	      for (ep = reg_eliminate;
		   ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
		   ep++)
		lra_assert (ep->from_rtx != orig_operand[i]
			    || ! ep->can_eliminate);
	    }

	  /* Companion to the above plus substitution, we can allow
	     invariants as the source of a plain move.	*/
	  substed_operand[i]
	    = lra_eliminate_regs_1 (insn, *id->operand_loc[i], VOIDmode,
				    replace_p, ! replace_p && ! first_p,
				    update_sp_offset, first_p);
	  if (substed_operand[i] != orig_operand[i])
	    validate_p = true;
	}
    }

  if (! validate_p)
    return;

  /* Substitute the operands; the new values are in the substed_operand
     array.  */
  for (i = 0; i < static_id->n_operands; i++)
    *id->operand_loc[i] = substed_operand[i];
  for (i = 0; i < static_id->n_dups; i++)
    *id->dup_loc[i] = substed_operand[(int) static_id->dup_num[i]];

  /* If we had a move insn but now we don't, re-recognize it.
     This will cause spurious re-recognition if the old move had a
     PARALLEL since the new one still will, but we can't call
     single_set without having put new body into the insn and the
     re-recognition won't hurt in this rare case.  */
  id = lra_update_insn_recog_data (insn);
  static_id = id->insn_static_data;
}