예제 #1
0
파일: aarch-common.c 프로젝트: daniel-k/gcc
int
aarch_crypto_can_dual_issue (rtx_insn *producer_insn, rtx_insn *consumer_insn)
{
  rtx producer_set, consumer_set;
  rtx producer_src, consumer_src;

  producer_set = single_set (producer_insn);
  consumer_set = single_set (consumer_insn);

  producer_src = producer_set ? SET_SRC (producer_set) : NULL;
  consumer_src = consumer_set ? SET_SRC (consumer_set) : NULL;

  if (producer_src && consumer_src
      && GET_CODE (producer_src) == UNSPEC && GET_CODE (consumer_src) == UNSPEC
      && ((XINT (producer_src, 1) == UNSPEC_AESE
           && XINT (consumer_src, 1) == UNSPEC_AESMC)
          || (XINT (producer_src, 1) == UNSPEC_AESD
              && XINT (consumer_src, 1) == UNSPEC_AESIMC)))
  {
    unsigned int regno = REGNO (SET_DEST (producer_set));

    return REGNO (SET_DEST (consumer_set)) == regno
           && REGNO (XVECEXP (consumer_src, 0, 0)) == regno;
  }

  return 0;
}
예제 #2
0
int
aarch_crypto_can_dual_issue (rtx_insn *producer_insn, rtx_insn *consumer_insn)
{
  rtx producer_set, consumer_set;
  rtx producer_src, consumer_src;

  producer_set = single_set (producer_insn);
  consumer_set = single_set (consumer_insn);

  producer_src = producer_set ? SET_SRC (producer_set) : NULL;
  consumer_src = consumer_set ? SET_SRC (consumer_set) : NULL;

  if (producer_src && consumer_src
      && GET_CODE (producer_src) == UNSPEC && GET_CODE (consumer_src) == UNSPEC
      && ((XINT (producer_src, 1) == UNSPEC_AESE
           && XINT (consumer_src, 1) == UNSPEC_AESMC)
          || (XINT (producer_src, 1) == UNSPEC_AESD
              && XINT (consumer_src, 1) == UNSPEC_AESIMC)))
  {
    unsigned int regno = REGNO (SET_DEST (producer_set));

    /* Before reload the registers are virtual, so the destination of
       consumer_set doesn't need to match.  */

    return (REGNO (SET_DEST (consumer_set)) == regno || !reload_completed)
	    && REGNO (XVECEXP (consumer_src, 0, 0)) == regno;
  }

  return 0;
}
예제 #3
0
파일: rtl-tests.c 프로젝트: T-J-Teru/gcc
static void
test_single_set ()
{
  /* A label is not a SET.  */
  ASSERT_EQ (NULL_RTX, single_set (gen_label_rtx ()));

  /* An unconditional jump insn is a single SET.  */
  rtx set_pc = gen_rtx_SET (pc_rtx,
			    gen_rtx_LABEL_REF (VOIDmode,
					       gen_label_rtx ()));
  rtx_insn *jump_insn = emit_jump_insn (set_pc);
  ASSERT_EQ (set_pc, single_set (jump_insn));

  /* etc */
}
예제 #4
0
/* Return true if insn is an instruction that sets a target register.
   if CHECK_CONST is true, only return true if the source is constant.
   If such a set is found and REGNO is nonzero, assign the register number
   of the destination register to *REGNO.  */
static int
insn_sets_btr_p (const_rtx insn, int check_const, int *regno)
{
  rtx set;

  if (NONJUMP_INSN_P (insn)
      && (set = single_set (insn)))
    {
      rtx dest = SET_DEST (set);
      rtx src = SET_SRC (set);

      if (GET_CODE (dest) == SUBREG)
	dest = XEXP (dest, 0);

      if (REG_P (dest)
	  && TEST_HARD_REG_BIT (all_btrs, REGNO (dest)))
	{
	  gcc_assert (!btr_referenced_p (src, NULL));

	  if (!check_const || CONSTANT_P (src))
	    {
	      if (regno)
		*regno = REGNO (dest);
	      return 1;
	    }
	}
    }
  return 0;
}
예제 #5
0
파일: compare-elim.c 프로젝트: gmarkall/gcc
static rtx
conforming_compare (rtx_insn *insn)
{
  rtx set, src, dest;

  set = single_set (insn);
  if (set == NULL)
    return NULL;

  src = SET_SRC (set);
  if (GET_CODE (src) != COMPARE)
    return NULL;

  dest = SET_DEST (set);
  if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum)
    return NULL;

  if (!REG_P (XEXP (src, 0)))
    return NULL;

  if (CONSTANT_P (XEXP (src, 1)) || REG_P (XEXP (src, 1)))
    return src;

  if (GET_CODE (XEXP (src, 1)) == UNSPEC)
    {
      for (int i = 0; i < XVECLEN (XEXP (src, 1), 0); i++)
	if (!REG_P (XVECEXP (XEXP (src, 1), 0, i)))
	  return NULL;
      return src;
    }

  return NULL;
}
예제 #6
0
static rtx
single_set_for_csa (rtx insn)
{
    int i;
    rtx tmp = single_set (insn);
    if (tmp)
        return tmp;

    if (!NONJUMP_INSN_P (insn)
            || GET_CODE (PATTERN (insn)) != PARALLEL)
        return NULL_RTX;

    tmp = PATTERN (insn);
    if (GET_CODE (XVECEXP (tmp, 0, 0)) != SET)
        return NULL_RTX;

    for (i = 1; i < XVECLEN (tmp, 0); ++i)
    {
        rtx this_rtx = XVECEXP (tmp, 0, i);

        /* The special case is allowing a no-op set.  */
        if (GET_CODE (this_rtx) == SET
                && SET_SRC (this_rtx) == SET_DEST (this_rtx))
            ;
        else if (GET_CODE (this_rtx) != CLOBBER
                 && GET_CODE (this_rtx) != USE)
            return NULL_RTX;
    }

    return XVECEXP (tmp, 0, 0);
}
예제 #7
0
파일: fwprop.c 프로젝트: ollie314/gcc
/* Check if all uses in DEF_INSN can be used in TARGET_INSN.  This
   would require full computation of available expressions;
   we check only restricted conditions, see use_killed_between.  */
static bool
all_uses_available_at (rtx_insn *def_insn, rtx_insn *target_insn)
{
  df_ref use;
  struct df_insn_info *insn_info = DF_INSN_INFO_GET (def_insn);
  rtx def_set = single_set (def_insn);
  rtx_insn *next;

  gcc_assert (def_set);

  /* If target_insn comes right after def_insn, which is very common
     for addresses, we can use a quicker test.  Ignore debug insns
     other than target insns for this.  */
  next = NEXT_INSN (def_insn);
  while (next && next != target_insn && DEBUG_INSN_P (next))
    next = NEXT_INSN (next);
  if (next == target_insn && REG_P (SET_DEST (def_set)))
    {
      rtx def_reg = SET_DEST (def_set);

      /* If the insn uses the reg that it defines, the substitution is
         invalid.  */
      FOR_EACH_INSN_INFO_USE (use, insn_info)
	if (rtx_equal_p (DF_REF_REG (use), def_reg))
	  return false;
      FOR_EACH_INSN_INFO_EQ_USE (use, insn_info)
	if (rtx_equal_p (DF_REF_REG (use), def_reg))
	  return false;
    }
예제 #8
0
파일: sibcall.c 프로젝트: robinsonb5/zpugcc
/* In case function does not return value,  we get clobber of pseudo followed
   by set to hard return value.  */
static rtx
skip_unreturned_value (rtx orig_insn)
{
  rtx insn = next_nonnote_insn (orig_insn);

  /* Skip possible clobber of pseudo return register.  */
  if (insn
      && GET_CODE (insn) == INSN
      && GET_CODE (PATTERN (insn)) == CLOBBER
      && REG_P (XEXP (PATTERN (insn), 0))
      && (REGNO (XEXP (PATTERN (insn), 0)) >= FIRST_PSEUDO_REGISTER))
    {
      rtx set_insn = next_nonnote_insn (insn);
      rtx set;
      if (!set_insn)
	return insn;
      set = single_set (set_insn);
      if (!set
	  || SET_SRC (set) != XEXP (PATTERN (insn), 0)
	  || SET_DEST (set) != current_function_return_rtx)
	return insn;
      return set_insn;
    }
  return orig_insn;
}
예제 #9
0
static unsigned int
postreload_load (void)
{
  basic_block bb;

  init_alias_analysis ();

  FOR_EACH_BB (bb)
    {
      rtx insn;

      htab_load = htab_create (10, load_htab_hash, load_htab_eq, NULL);

      FOR_BB_INSNS (bb, insn)
	{
	  rtx set;
	  struct load **load;

	  /* Set reg_kill, invalidate entries if there is an
	     aliasing store or if the registers making up the address
	     change.  */
	  htab_traverse_noresize
	    (htab_load, find_reg_kill_and_mem_invalidate, insn);	

	  set = single_set (insn);
	  if (interesting_second_load (set, &load, insn))
	    {
	      rtx move;

	      move = gen_move_insn (SET_DEST (set), (*load)->reg);
	      /* Make sure we can generate a move.  */
	      extract_insn (move);
	      if (! constrain_operands (1))
		continue;

	      move = emit_insn_before (move, (*load)->reg_kill);
	      delete_insn (insn);

	      if (dump_file)
		{
		  fputs ("Replaced this load:\n  ", dump_file);
		  print_inline_rtx (dump_file, insn, 2);
		  fputs ("\n  with this move:\n  ", dump_file);
		  print_inline_rtx (dump_file, move, 2);
		  fputs ("\n\n", dump_file);
		}
	    }
	  else if (interesting_load (set))
	    alloc_load (set);
	  else if (CALL_P (insn))
	    htab_empty (htab_load);
	}

      htab_empty (htab_load);
    }
예제 #10
0
파일: sibcall.c 프로젝트: robinsonb5/zpugcc
static rtx
skip_pic_restore (rtx orig_insn)
{
  rtx insn, set = NULL_RTX;

  insn = next_nonnote_insn (orig_insn);

  if (insn)
    set = single_set (insn);

  if (insn && set && SET_DEST (set) == pic_offset_table_rtx)
    return insn;

  return orig_insn;
}
예제 #11
0
static void
maybe_propagate_label_ref (rtx jump_insn, rtx prev_nonjump_insn)
{
  rtx label_note, pc, pc_src;

  pc = pc_set (jump_insn);
  pc_src = pc != NULL ? SET_SRC (pc) : NULL;
  label_note = find_reg_note (prev_nonjump_insn, REG_LABEL_OPERAND, NULL);

  /* If the previous non-jump insn sets something to a label,
     something that this jump insn uses, make that label the primary
     target of this insn if we don't yet have any.  That previous
     insn must be a single_set and not refer to more than one label.
     The jump insn must not refer to other labels as jump targets
     and must be a plain (set (pc) ...), maybe in a parallel, and
     may refer to the item being set only directly or as one of the
     arms in an IF_THEN_ELSE.  */

  if (label_note != NULL && pc_src != NULL)
    {
      rtx label_set = single_set (prev_nonjump_insn);
      rtx label_dest = label_set != NULL ? SET_DEST (label_set) : NULL;

      if (label_set != NULL
	  /* The source must be the direct LABEL_REF, not a
	     PLUS, UNSPEC, IF_THEN_ELSE etc.  */
	  && GET_CODE (SET_SRC (label_set)) == LABEL_REF
	  && (rtx_equal_p (label_dest, pc_src)
	      || (GET_CODE (pc_src) == IF_THEN_ELSE
		  && (rtx_equal_p (label_dest, XEXP (pc_src, 1))
		      || rtx_equal_p (label_dest, XEXP (pc_src, 2))))))
	{
	  /* The CODE_LABEL referred to in the note must be the
	     CODE_LABEL in the LABEL_REF of the "set".  We can
	     conveniently use it for the marker function, which
	     requires a LABEL_REF wrapping.  */
	  gcc_assert (XEXP (label_note, 0) == XEXP (SET_SRC (label_set), 0));

	  mark_jump_label_1 (label_set, jump_insn, false, true);

	  gcc_assert (JUMP_LABEL (jump_insn) == XEXP (label_note, 0));
	}
    }
}
예제 #12
0
파일: ree.c 프로젝트: aixoss/gcc
static bool
is_cond_copy_insn (rtx insn, rtx *reg1, rtx *reg2)
{
  rtx expr = single_set (insn);

  if (expr != NULL_RTX
      && GET_CODE (expr) == SET
      && GET_CODE (SET_DEST (expr)) == REG
      && GET_CODE (SET_SRC (expr))  == IF_THEN_ELSE
      && GET_CODE (XEXP (SET_SRC (expr), 1)) == REG
      && GET_CODE (XEXP (SET_SRC (expr), 2)) == REG)
    {
      *reg1 = XEXP (SET_SRC (expr), 1);
      *reg2 = XEXP (SET_SRC (expr), 2);
      return true;
    }

  return false;
}
예제 #13
0
파일: web.c 프로젝트: CookieChen/gcc
void
union_defs (df_ref use, struct web_entry *def_entry,
	    unsigned int *used, struct web_entry *use_entry,
 	    bool (*fun) (struct web_entry *, struct web_entry *))
{
  struct df_insn_info *insn_info = DF_REF_INSN_INFO (use);
  struct df_link *link = DF_REF_CHAIN (use);
  rtx set;

  if (insn_info)
    {
      df_ref eq_use;

      set = single_set (insn_info->insn);
      FOR_EACH_INSN_INFO_EQ_USE (eq_use, insn_info)
	if (use != eq_use
	    && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (eq_use))
	  (*fun) (use_entry + DF_REF_ID (use), use_entry + DF_REF_ID (eq_use));
    }
  else
예제 #14
0
파일: sibcall.c 프로젝트: robinsonb5/zpugcc
static rtx
skip_stack_adjustment (rtx orig_insn)
{
  rtx insn, set = NULL_RTX;

  insn = next_nonnote_insn (orig_insn);

  if (insn)
    set = single_set (insn);

  if (insn
      && set
      && GET_CODE (SET_SRC (set)) == PLUS
      && XEXP (SET_SRC (set), 0) == stack_pointer_rtx
      && GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT
      && SET_DEST (set) == stack_pointer_rtx)
    return insn;

  return orig_insn;
}
예제 #15
0
파일: compare-elim.c 프로젝트: jtramm/gcc
static rtx
conforming_compare (rtx_insn *insn)
{
  rtx set, src, dest;

  set = single_set (insn);
  if (set == NULL)
    return NULL;

  src = SET_SRC (set);
  if (GET_CODE (src) != COMPARE)
    return NULL;

  dest = SET_DEST (set);
  if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum)
    return NULL;

  if (REG_P (XEXP (src, 0))
      && (REG_P (XEXP (src, 1)) || CONSTANT_P (XEXP (src, 1))))
    return src;

  return NULL;
}
예제 #16
0
/* Find the group that the target register definition DEF belongs
   to in the list starting with *ALL_BTR_DEF_GROUPS.  If no such
   group exists, create one.  Add def to the group.  */
static void
find_btr_def_group (btr_def_group *all_btr_def_groups, btr_def def)
{
  if (insn_sets_btr_p (def->insn, 1, NULL))
    {
      btr_def_group this_group;
      rtx def_src = SET_SRC (single_set (def->insn));

      /* ?? This linear search is an efficiency concern, particularly
	 as the search will almost always fail to find a match.  */
      for (this_group = *all_btr_def_groups;
	   this_group != NULL;
	   this_group = this_group->next)
	if (rtx_equal_p (def_src, this_group->src))
	  break;

      if (!this_group)
	{
	  this_group = XOBNEW (&migrate_btrl_obstack, struct btr_def_group_s);
	  this_group->src = def_src;
	  this_group->members = NULL;
	  this_group->next = *all_btr_def_groups;
	  *all_btr_def_groups = this_group;
	}
예제 #17
0
/* Do transform 2) on INSN if applicable.  */
static bool
mod_pow2_value_transform (rtx insn)
{
  rtx set, set_src, set_dest, op1, op2, value, histogram;
  enum rtx_code code;
  enum machine_mode mode;
  gcov_type wrong_values, count;
  edge e;
  int i, all, prob;

  set = single_set (insn);
  if (!set)
    return false;

  set_src = SET_SRC (set);
  set_dest = SET_DEST (set);
  code = GET_CODE (set_src);
  mode = GET_MODE (set_dest);
  
  if (code != UMOD)
    return false;
  op1 = XEXP (set_src, 0);
  op2 = XEXP (set_src, 1);

  for (histogram = REG_NOTES (insn);
       histogram;
       histogram = XEXP (histogram, 1))
    if (REG_NOTE_KIND (histogram) == REG_VALUE_PROFILE
	&& XEXP (XEXP (histogram, 0), 0) == GEN_INT (HIST_TYPE_POW2))
      break;

  if (!histogram)
    return false;

  histogram = XEXP (XEXP (histogram, 0), 1);
  value = XEXP (histogram, 0);
  histogram = XEXP (histogram, 1);
  wrong_values =INTVAL (XEXP (histogram, 0));
  histogram = XEXP (histogram, 1);

  count = 0;
  for (i = 0; i < GET_MODE_BITSIZE (mode); i++)
    {
      count += INTVAL (XEXP (histogram, 0));
      histogram = XEXP (histogram, 1);
    }

  if (!rtx_equal_p (op2, value))
    return false;

  /* We require that we hit a power of two at least half of all evaluations.  */
  if (count < wrong_values)
    return false;

  if (dump_file)
    fprintf (dump_file, "Mod power of 2 transformation on insn %d\n",
	     INSN_UID (insn));

  /* Compute probability of taking the optimal path.  */
  all = count + wrong_values;
  prob = (count * REG_BR_PROB_BASE + all / 2) / all;

  e = split_block (BLOCK_FOR_INSN (insn), PREV_INSN (insn));
  delete_insn (insn);
  
  insert_insn_on_edge (
	gen_mod_pow2 (mode, code, set_dest, op1, op2, prob), e);

  return true;
}
예제 #18
0
static void
mark_all_labels (rtx f)
{
  rtx insn;
  rtx prev_nonjump_insn = NULL;

  for (insn = f; insn; insn = NEXT_INSN (insn))
    if (INSN_P (insn))
      {
	mark_jump_label (PATTERN (insn), insn, 0);

	/* If the previous non-jump insn sets something to a label,
	   something that this jump insn uses, make that label the primary
	   target of this insn if we don't yet have any.  That previous
	   insn must be a single_set and not refer to more than one label.
	   The jump insn must not refer to other labels as jump targets
	   and must be a plain (set (pc) ...), maybe in a parallel, and
	   may refer to the item being set only directly or as one of the
	   arms in an IF_THEN_ELSE.  */
	if (! INSN_DELETED_P (insn)
	    && JUMP_P (insn)
	    && JUMP_LABEL (insn) == NULL)
	  {
	    rtx label_note = NULL;
	    rtx pc = pc_set (insn);
	    rtx pc_src = pc != NULL ? SET_SRC (pc) : NULL;

	    if (prev_nonjump_insn != NULL)
	      label_note
		= find_reg_note (prev_nonjump_insn, REG_LABEL_OPERAND, NULL);

	    if (label_note != NULL && pc_src != NULL)
	      {
		rtx label_set = single_set (prev_nonjump_insn);
		rtx label_dest
		  = label_set != NULL ? SET_DEST (label_set) : NULL;

		if (label_set != NULL
		    /* The source must be the direct LABEL_REF, not a
		       PLUS, UNSPEC, IF_THEN_ELSE etc.  */
		    && GET_CODE (SET_SRC (label_set)) == LABEL_REF
		    && (rtx_equal_p (label_dest, pc_src)
			|| (GET_CODE (pc_src) == IF_THEN_ELSE
			    && (rtx_equal_p (label_dest, XEXP (pc_src, 1))
				|| rtx_equal_p (label_dest,
						XEXP (pc_src, 2))))))

		  {
		    /* The CODE_LABEL referred to in the note must be the
		       CODE_LABEL in the LABEL_REF of the "set".  We can
		       conveniently use it for the marker function, which
		       requires a LABEL_REF wrapping.  */
		    gcc_assert (XEXP (label_note, 0)
				== XEXP (SET_SRC (label_set), 0));

		    mark_jump_label_1 (label_set, insn, false, true);
		    gcc_assert (JUMP_LABEL (insn)
				== XEXP (SET_SRC (label_set), 0));
		  }
	      }
	  }
	else if (! INSN_DELETED_P (insn))
	  prev_nonjump_insn = insn;
      }
    else if (LABEL_P (insn))
      prev_nonjump_insn = NULL;

  /* If we are in cfglayout mode, there may be non-insns between the
     basic blocks.  If those non-insns represent tablejump data, they
     contain label references that we must record.  */
  if (current_ir_type () == IR_RTL_CFGLAYOUT)
    {
      basic_block bb;
      rtx insn;
      FOR_EACH_BB (bb)
	{
	  for (insn = bb->il.rtl->header; insn; insn = NEXT_INSN (insn))
	    if (INSN_P (insn))
	      {
		gcc_assert (JUMP_TABLE_DATA_P (insn));
		mark_jump_label (PATTERN (insn), insn, 0);
	      }

	  for (insn = bb->il.rtl->footer; insn; insn = NEXT_INSN (insn))
	    if (INSN_P (insn))
	      {
		gcc_assert (JUMP_TABLE_DATA_P (insn));
		mark_jump_label (PATTERN (insn), insn, 0);
	      }
	}
    }
예제 #19
0
파일: dce.c 프로젝트: AHelper/gcc
static bool
find_call_stack_args (rtx_call_insn *call_insn, bool do_mark, bool fast,
		      bitmap arg_stores)
{
  rtx p;
  rtx_insn *insn, *prev_insn;
  bool ret;
  HOST_WIDE_INT min_sp_off, max_sp_off;
  bitmap sp_bytes;

  gcc_assert (CALL_P (call_insn));
  if (!ACCUMULATE_OUTGOING_ARGS)
    return true;

  if (!do_mark)
    {
      gcc_assert (arg_stores);
      bitmap_clear (arg_stores);
    }

  min_sp_off = INTTYPE_MAXIMUM (HOST_WIDE_INT);
  max_sp_off = 0;

  /* First determine the minimum and maximum offset from sp for
     stored arguments.  */
  for (p = CALL_INSN_FUNCTION_USAGE (call_insn); p; p = XEXP (p, 1))
    if (GET_CODE (XEXP (p, 0)) == USE
	&& MEM_P (XEXP (XEXP (p, 0), 0)))
      {
	rtx mem = XEXP (XEXP (p, 0), 0), addr;
	HOST_WIDE_INT off = 0, size;
	if (!MEM_SIZE_KNOWN_P (mem))
	  return false;
	size = MEM_SIZE (mem);
	addr = XEXP (mem, 0);
	if (GET_CODE (addr) == PLUS
	    && REG_P (XEXP (addr, 0))
	    && CONST_INT_P (XEXP (addr, 1)))
	  {
	    off = INTVAL (XEXP (addr, 1));
	    addr = XEXP (addr, 0);
	  }
	if (addr != stack_pointer_rtx)
	  {
	    if (!REG_P (addr))
	      return false;
	    /* If not fast, use chains to see if addr wasn't set to
	       sp + offset.  */
	    if (!fast)
	      {
		df_ref use;
		struct df_link *defs;
		rtx set;

		FOR_EACH_INSN_USE (use, call_insn)
		  if (rtx_equal_p (addr, DF_REF_REG (use)))
		    break;

		if (use == NULL)
		  return false;

		for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
		  if (! DF_REF_IS_ARTIFICIAL (defs->ref))
		    break;

		if (defs == NULL)
		  return false;

		set = single_set (DF_REF_INSN (defs->ref));
		if (!set)
		  return false;

		if (GET_CODE (SET_SRC (set)) != PLUS
		    || XEXP (SET_SRC (set), 0) != stack_pointer_rtx
		    || !CONST_INT_P (XEXP (SET_SRC (set), 1)))
		  return false;

		off += INTVAL (XEXP (SET_SRC (set), 1));
	      }
	    else
	      return false;
예제 #20
0
파일: sibcall.c 프로젝트: robinsonb5/zpugcc
static rtx
skip_copy_to_return_value (rtx orig_insn)
{
  rtx insn, set = NULL_RTX;
  rtx hardret, softret;

  /* If there is no return value, we have nothing to do.  */
  if (! identify_call_return_value (PATTERN (orig_insn), &hardret, &softret))
    return orig_insn;

  insn = next_nonnote_insn (orig_insn);
  if (! insn)
    return orig_insn;

  set = single_set (insn);
  if (! set)
    return orig_insn;

  if (return_value_pseudo)
    {
      if (SET_DEST (set) == return_value_pseudo
	  && SET_SRC (set) == softret)
	return insn;
      return orig_insn;
    }

  /* The destination must be the same as the called function's return
     value to ensure that any return value is put in the same place by the
     current function and the function we're calling.

     Further, the source must be the same as the pseudo into which the
     called function's return value was copied.  Otherwise we're returning
     some other value.  */

#ifndef OUTGOING_REGNO
#define OUTGOING_REGNO(N) (N)
#endif

  if (SET_DEST (set) == current_function_return_rtx
      && REG_P (SET_DEST (set))
      && OUTGOING_REGNO (REGNO (SET_DEST (set))) == REGNO (hardret)
      && SET_SRC (set) == softret)
    return insn;

  /* Recognize the situation when the called function's return value
     is copied in two steps: first into an intermediate pseudo, then
     the into the calling functions return value register.  */

  if (REG_P (SET_DEST (set))
      && SET_SRC (set) == softret)
    {
      rtx x = SET_DEST (set);

      insn = next_nonnote_insn (insn);
      if (! insn)
	return orig_insn;

      set = single_set (insn);
      if (! set)
	return orig_insn;

      if (SET_DEST (set) == current_function_return_rtx
	  && REG_P (SET_DEST (set))
	  && OUTGOING_REGNO (REGNO (SET_DEST (set))) == REGNO (hardret)
	  && SET_SRC (set) == x)
	return insn;
    }

  /* It did not look like a copy of the return value, so return the
     same insn we were passed.  */
  return orig_insn;
}
예제 #21
0
static basic_block
create_pre_exit (int n_entities, int *entity_map, const int *num_modes)
{
  edge eg;
  edge_iterator ei;
  basic_block pre_exit;

  /* The only non-call predecessor at this stage is a block with a
     fallthrough edge; there can be at most one, but there could be
     none at all, e.g. when exit is called.  */
  pre_exit = 0;
  FOR_EACH_EDGE (eg, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
    if (eg->flags & EDGE_FALLTHRU)
      {
	basic_block src_bb = eg->src;
	rtx_insn *last_insn;
	rtx ret_reg;

	gcc_assert (!pre_exit);
	/* If this function returns a value at the end, we have to
	   insert the final mode switch before the return value copy
	   to its hard register.  */
	if (EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 1
	    && NONJUMP_INSN_P ((last_insn = BB_END (src_bb)))
	    && GET_CODE (PATTERN (last_insn)) == USE
	    && GET_CODE ((ret_reg = XEXP (PATTERN (last_insn), 0))) == REG)
	  {
	    int ret_start = REGNO (ret_reg);
	    int nregs = hard_regno_nregs[ret_start][GET_MODE (ret_reg)];
	    int ret_end = ret_start + nregs;
	    bool short_block = false;
	    bool multi_reg_return = false;
	    bool forced_late_switch = false;
	    rtx_insn *before_return_copy;

	    do
	      {
		rtx_insn *return_copy = PREV_INSN (last_insn);
		rtx return_copy_pat, copy_reg;
		int copy_start, copy_num;
		int j;

		if (NONDEBUG_INSN_P (return_copy))
		  {
		    /* When using SJLJ exceptions, the call to the
		       unregister function is inserted between the
		       clobber of the return value and the copy.
		       We do not want to split the block before this
		       or any other call; if we have not found the
		       copy yet, the copy must have been deleted.  */
		    if (CALL_P (return_copy))
		      {
			short_block = true;
			break;
		      }
		    return_copy_pat = PATTERN (return_copy);
		    switch (GET_CODE (return_copy_pat))
		      {
		      case USE:
			/* Skip USEs of multiple return registers.
			   __builtin_apply pattern is also handled here.  */
			if (GET_CODE (XEXP (return_copy_pat, 0)) == REG
			    && (targetm.calls.function_value_regno_p
				(REGNO (XEXP (return_copy_pat, 0)))))
			  {
			    multi_reg_return = true;
			    last_insn = return_copy;
			    continue;
			  }
			break;

		      case ASM_OPERANDS:
			/* Skip barrier insns.  */
			if (!MEM_VOLATILE_P (return_copy_pat))
			  break;

			/* Fall through.  */

		      case ASM_INPUT:
		      case UNSPEC_VOLATILE:
			last_insn = return_copy;
			continue;

		      default:
			break;
		      }

		    /* If the return register is not (in its entirety)
		       likely spilled, the return copy might be
		       partially or completely optimized away.  */
		    return_copy_pat = single_set (return_copy);
		    if (!return_copy_pat)
		      {
			return_copy_pat = PATTERN (return_copy);
			if (GET_CODE (return_copy_pat) != CLOBBER)
			  break;
			else if (!optimize)
			  {
			    /* This might be (clobber (reg [<result>]))
			       when not optimizing.  Then check if
			       the previous insn is the clobber for
			       the return register.  */
			    copy_reg = SET_DEST (return_copy_pat);
			    if (GET_CODE (copy_reg) == REG
				&& !HARD_REGISTER_NUM_P (REGNO (copy_reg)))
			      {
				if (INSN_P (PREV_INSN (return_copy)))
				  {
				    return_copy = PREV_INSN (return_copy);
				    return_copy_pat = PATTERN (return_copy);
				    if (GET_CODE (return_copy_pat) != CLOBBER)
				      break;
				  }
			      }
			  }
		      }
		    copy_reg = SET_DEST (return_copy_pat);
		    if (GET_CODE (copy_reg) == REG)
		      copy_start = REGNO (copy_reg);
		    else if (GET_CODE (copy_reg) == SUBREG
			     && GET_CODE (SUBREG_REG (copy_reg)) == REG)
		      copy_start = REGNO (SUBREG_REG (copy_reg));
		    else
		      {
			/* When control reaches end of non-void function,
			   there are no return copy insns at all.  This
			   avoids an ice on that invalid function.  */
			if (ret_start + nregs == ret_end)
			  short_block = true;
			break;
		      }
		    if (!targetm.calls.function_value_regno_p (copy_start))
		      copy_num = 0;
		    else
		      copy_num
			= hard_regno_nregs[copy_start][GET_MODE (copy_reg)];

		    /* If the return register is not likely spilled, - as is
		       the case for floating point on SH4 - then it might
		       be set by an arithmetic operation that needs a
		       different mode than the exit block.  */
		    for (j = n_entities - 1; j >= 0; j--)
		      {
			int e = entity_map[j];
			int mode =
			  targetm.mode_switching.needed (e, return_copy);

			if (mode != num_modes[e]
			    && mode != targetm.mode_switching.exit (e))
			  break;
		      }
		    if (j >= 0)
		      {
			/* __builtin_return emits a sequence of loads to all
			   return registers.  One of them might require
			   another mode than MODE_EXIT, even if it is
			   unrelated to the return value, so we want to put
			   the final mode switch after it.  */
			if (multi_reg_return
			    && targetm.calls.function_value_regno_p
			        (copy_start))
			  forced_late_switch = true;

			/* For the SH4, floating point loads depend on fpscr,
			   thus we might need to put the final mode switch
			   after the return value copy.  That is still OK,
			   because a floating point return value does not
			   conflict with address reloads.  */
			if (copy_start >= ret_start
			    && copy_start + copy_num <= ret_end
			    && OBJECT_P (SET_SRC (return_copy_pat)))
			  forced_late_switch = true;
			break;
		      }
		    if (copy_num == 0)
		      {
			last_insn = return_copy;
			continue;
		      }

		    if (copy_start >= ret_start
			&& copy_start + copy_num <= ret_end)
		      nregs -= copy_num;
		    else if (!multi_reg_return
			     || !targetm.calls.function_value_regno_p
				 (copy_start))
		      break;
		    last_insn = return_copy;
		  }
		/* ??? Exception handling can lead to the return value
		   copy being already separated from the return value use,
		   as in  unwind-dw2.c .
		   Similarly, conditionally returning without a value,
		   and conditionally using builtin_return can lead to an
		   isolated use.  */
		if (return_copy == BB_HEAD (src_bb))
		  {
		    short_block = true;
		    break;
		  }
		last_insn = return_copy;
	      }
	    while (nregs);

	    /* If we didn't see a full return value copy, verify that there
	       is a plausible reason for this.  If some, but not all of the
	       return register is likely spilled, we can expect that there
	       is a copy for the likely spilled part.  */
	    gcc_assert (!nregs
			|| forced_late_switch
			|| short_block
			|| !(targetm.class_likely_spilled_p
			     (REGNO_REG_CLASS (ret_start)))
			|| (nregs
			    != hard_regno_nregs[ret_start][GET_MODE (ret_reg)])
			/* For multi-hard-register floating point
		   	   values, sometimes the likely-spilled part
		   	   is ordinarily copied first, then the other
		   	   part is set with an arithmetic operation.
		   	   This doesn't actually cause reload
		   	   failures, so let it pass.  */
			|| (GET_MODE_CLASS (GET_MODE (ret_reg)) != MODE_INT
			    && nregs != 1));

	    if (!NOTE_INSN_BASIC_BLOCK_P (last_insn))
	      {
		before_return_copy
		  = emit_note_before (NOTE_INSN_DELETED, last_insn);
		/* Instructions preceding LAST_INSN in the same block might
		   require a different mode than MODE_EXIT, so if we might
		   have such instructions, keep them in a separate block
		   from pre_exit.  */
		src_bb = split_block (src_bb,
				      PREV_INSN (before_return_copy))->dest;
	      }
	    else
	      before_return_copy = last_insn;
	    pre_exit = split_block (src_bb, before_return_copy)->src;
	  }
	else
	  {
	    pre_exit = split_edge (eg);
	  }
      }

  return pre_exit;
}
예제 #22
0
static bool
parse_add_or_inc (rtx insn, bool before_mem)
{
  rtx pat = single_set (insn);
  if (!pat)
    return false;

  /* Result must be single reg.  */
  if (!REG_P (SET_DEST (pat)))
    return false;

  if ((GET_CODE (SET_SRC (pat)) != PLUS)
      && (GET_CODE (SET_SRC (pat)) != MINUS))
    return false;

  if (!REG_P (XEXP (SET_SRC (pat), 0)))
    return false;

  inc_insn.insn = insn;
  inc_insn.pat = pat;
  inc_insn.reg_res = SET_DEST (pat);
  inc_insn.reg0 = XEXP (SET_SRC (pat), 0);
  if (rtx_equal_p (inc_insn.reg_res, inc_insn.reg0))
    inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC;
  else
    inc_insn.form = before_mem ? FORM_PRE_ADD : FORM_POST_ADD;

  if (CONST_INT_P (XEXP (SET_SRC (pat), 1)))
    {
      /* Process a = b + c where c is a const.  */
      inc_insn.reg1_is_const = true;
      if (GET_CODE (SET_SRC (pat)) == PLUS)
	{
	  inc_insn.reg1 = XEXP (SET_SRC (pat), 1);
	  inc_insn.reg1_val = INTVAL (inc_insn.reg1);
	}
      else
	{
	  inc_insn.reg1_val = -INTVAL (XEXP (SET_SRC (pat), 1));
	  inc_insn.reg1 = GEN_INT (inc_insn.reg1_val);
	}
      return true;
    }
  else if ((HAVE_PRE_MODIFY_REG || HAVE_POST_MODIFY_REG)
	   && (REG_P (XEXP (SET_SRC (pat), 1)))
	   && GET_CODE (SET_SRC (pat)) == PLUS)
    {
      /* Process a = b + c where c is a reg.  */
      inc_insn.reg1 = XEXP (SET_SRC (pat), 1);
      inc_insn.reg1_is_const = false;

      if (inc_insn.form == FORM_PRE_INC
	  || inc_insn.form == FORM_POST_INC)
	return true;
      else if (rtx_equal_p (inc_insn.reg_res, inc_insn.reg1))
	{
	  /* Reverse the two operands and turn *_ADD into *_INC since
	     a = c + a.  */
	  reverse_inc ();
	  inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC;
	  return true;
	}
      else
	return true;
    }

  return false;
}
예제 #23
0
/* The major function for aggressive pseudo coalescing of moves only
   if the both pseudos were spilled and not special reload pseudos.  */
bool
lra_coalesce (void)
{
  basic_block bb;
  rtx mv, set, insn, next, *sorted_moves;
  int i, mv_num, sregno, dregno;
  int coalesced_moves;
  int max_regno = max_reg_num ();
  bitmap_head involved_insns_bitmap;

  timevar_push (TV_LRA_COALESCE);

  if (lra_dump_file != NULL)
    fprintf (lra_dump_file,
	     "\n********** Pseudos coalescing #%d: **********\n\n",
	     ++lra_coalesce_iter);
  first_coalesced_pseudo = XNEWVEC (int, max_regno);
  next_coalesced_pseudo = XNEWVEC (int, max_regno);
  for (i = 0; i < max_regno; i++)
    first_coalesced_pseudo[i] = next_coalesced_pseudo[i] = i;
  sorted_moves = XNEWVEC (rtx, get_max_uid ());
  mv_num = 0;
  /* Collect moves.  */
  coalesced_moves = 0;
  FOR_EACH_BB (bb)
    {
      FOR_BB_INSNS_SAFE (bb, insn, next)
	if (INSN_P (insn)
	    && (set = single_set (insn)) != NULL_RTX
	    && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set))
	    && (sregno = REGNO (SET_SRC (set))) >= FIRST_PSEUDO_REGISTER
	    && (dregno = REGNO (SET_DEST (set))) >= FIRST_PSEUDO_REGISTER
	    && mem_move_p (sregno, dregno)
	    && coalescable_pseudo_p (sregno) && coalescable_pseudo_p (dregno)
	    && ! side_effects_p (set)
	    && !(lra_intersected_live_ranges_p
		 (lra_reg_info[sregno].live_ranges,
		  lra_reg_info[dregno].live_ranges)))
	  sorted_moves[mv_num++] = insn;
    }
  qsort (sorted_moves, mv_num, sizeof (rtx), move_freq_compare_func);
  /* Coalesced copies, most frequently executed first.	*/
  bitmap_initialize (&coalesced_pseudos_bitmap, &reg_obstack);
  bitmap_initialize (&involved_insns_bitmap, &reg_obstack);
  for (i = 0; i < mv_num; i++)
    {
      mv = sorted_moves[i];
      set = single_set (mv);
      lra_assert (set != NULL && REG_P (SET_SRC (set))
		  && REG_P (SET_DEST (set)));
      sregno = REGNO (SET_SRC (set));
      dregno = REGNO (SET_DEST (set));
      if (first_coalesced_pseudo[sregno] == first_coalesced_pseudo[dregno])
	{
	  coalesced_moves++;
	  if (lra_dump_file != NULL)
	    fprintf
	      (lra_dump_file, "      Coalescing move %i:r%d-r%d (freq=%d)\n",
	       INSN_UID (mv), sregno, dregno,
	       BLOCK_FOR_INSN (mv)->frequency);
	  /* We updated involved_insns_bitmap when doing the merge.  */
	}
      else if (!(lra_intersected_live_ranges_p
		 (lra_reg_info[first_coalesced_pseudo[sregno]].live_ranges,
		  lra_reg_info[first_coalesced_pseudo[dregno]].live_ranges)))
	{
	  coalesced_moves++;
	  if (lra_dump_file != NULL)
	    fprintf
	      (lra_dump_file,
	       "  Coalescing move %i:r%d(%d)-r%d(%d) (freq=%d)\n",
	       INSN_UID (mv), sregno, ORIGINAL_REGNO (SET_SRC (set)),
	       dregno, ORIGINAL_REGNO (SET_DEST (set)),
	       BLOCK_FOR_INSN (mv)->frequency);
	  bitmap_ior_into (&involved_insns_bitmap,
			   &lra_reg_info[sregno].insn_bitmap);
	  bitmap_ior_into (&involved_insns_bitmap,
			   &lra_reg_info[dregno].insn_bitmap);
	  merge_pseudos (sregno, dregno);
	}
    }
  bitmap_initialize (&used_pseudos_bitmap, &reg_obstack);
  FOR_EACH_BB (bb)
    {
      update_live_info (df_get_live_in (bb));
      update_live_info (df_get_live_out (bb));
      FOR_BB_INSNS_SAFE (bb, insn, next)
	if (INSN_P (insn)
	    && bitmap_bit_p (&involved_insns_bitmap, INSN_UID (insn)))
	  {
	    if (! substitute (&insn))
	      continue;
	    lra_update_insn_regno_info (insn);
	    if ((set = single_set (insn)) != NULL_RTX && set_noop_p (set))
	      {
		/* Coalesced move.  */
		if (lra_dump_file != NULL)
		  fprintf (lra_dump_file, "	 Removing move %i (freq=%d)\n",
			 INSN_UID (insn), BLOCK_FOR_INSN (insn)->frequency);
		lra_set_insn_deleted (insn);
	      }
	  }
    }
  bitmap_clear (&used_pseudos_bitmap);
  bitmap_clear (&involved_insns_bitmap);
  bitmap_clear (&coalesced_pseudos_bitmap);
  if (lra_dump_file != NULL && coalesced_moves != 0)
    fprintf (lra_dump_file, "Coalesced Moves = %d\n", coalesced_moves);
  free (sorted_moves);
  free (next_coalesced_pseudo);
  free (first_coalesced_pseudo);
  timevar_pop (TV_LRA_COALESCE);
  return coalesced_moves != 0;
}
예제 #24
0
파일: sibcall.c 프로젝트: robinsonb5/zpugcc
static int
identify_call_return_value (rtx cp, rtx *p_hard_return, rtx *p_soft_return)
{
  rtx insn, set, hard, soft;

  insn = XEXP (cp, 0);
  /* Search backward through the "normal" call sequence to the CALL insn.  */
  while (NEXT_INSN (insn))
    insn = NEXT_INSN (insn);
  while (GET_CODE (insn) != CALL_INSN)
    insn = PREV_INSN (insn);

  /* Assume the pattern is (set (dest) (call ...)), or that the first
     member of a parallel is.  This is the hard return register used
     by the function.  */
  if (GET_CODE (PATTERN (insn)) == SET
      && GET_CODE (SET_SRC (PATTERN (insn))) == CALL)
    hard = SET_DEST (PATTERN (insn));
  else if (GET_CODE (PATTERN (insn)) == PARALLEL
	   && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET
	   && GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == CALL)
    hard = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
  else
    return 0;

  /* If we didn't get a single hard register (e.g. a parallel), give up.  */
  if (GET_CODE (hard) != REG)
    return 0;

  /* Stack adjustment done after call may appear here.  */
  insn = skip_stack_adjustment (insn);
  if (! insn)
    return 0;

  /* Restore of GP register may appear here.  */
  insn = skip_pic_restore (insn);
  if (! insn)
    return 0;

  /* If there's nothing after, there's no soft return value.  */
  insn = NEXT_INSN (insn);
  if (! insn)
    return 0;

  /* We're looking for a source of the hard return register.  */
  set = single_set (insn);
  if (! set || SET_SRC (set) != hard)
    return 0;

  soft = SET_DEST (set);
  insn = NEXT_INSN (insn);

  /* Allow this first destination to be copied to a second register,
     as might happen if the first register wasn't the particular pseudo
     we'd been expecting.  */
  if (insn
      && (set = single_set (insn)) != NULL_RTX
      && SET_SRC (set) == soft)
    {
      soft = SET_DEST (set);
      insn = NEXT_INSN (insn);
    }

  /* Don't fool with anything but pseudo registers.  */
  if (GET_CODE (soft) != REG || REGNO (soft) < FIRST_PSEUDO_REGISTER)
    return 0;

  /* This value must not be modified before the end of the sequence.  */
  if (reg_set_between_p (soft, insn, NULL_RTX))
    return 0;

  *p_hard_return = hard;
  *p_soft_return = soft;

  return 1;
}
예제 #25
0
파일: sibcall.c 프로젝트: robinsonb5/zpugcc
void
optimize_sibling_and_tail_recursive_calls (void)
{
  rtx insn, insns;
  basic_block alternate_exit = EXIT_BLOCK_PTR;
  bool no_sibcalls_this_function = false;
  bool successful_replacement = false;
  bool replaced_call_placeholder = false;
  edge e;

  insns = get_insns ();

  cleanup_cfg (CLEANUP_PRE_SIBCALL | CLEANUP_PRE_LOOP);

  /* If there are no basic blocks, then there is nothing to do.  */
  if (n_basic_blocks == 0)
    return;

  /* If we are using sjlj exceptions, we may need to add a call to
     _Unwind_SjLj_Unregister at exit of the function.  Which means
     that we cannot do any sibcall transformations.  */
  if (USING_SJLJ_EXCEPTIONS && current_function_has_exception_handlers ())
    no_sibcalls_this_function = true;

  return_value_pseudo = NULL_RTX;

  /* Find the exit block.

     It is possible that we have blocks which can reach the exit block
     directly.  However, most of the time a block will jump (or fall into)
     N_BASIC_BLOCKS - 1, which in turn falls into the exit block.  */
  for (e = EXIT_BLOCK_PTR->pred;
       e && alternate_exit == EXIT_BLOCK_PTR;
       e = e->pred_next)
    {
      rtx insn;

      if (e->dest != EXIT_BLOCK_PTR || e->succ_next != NULL)
	continue;

      /* Walk forwards through the last normal block and see if it
	 does nothing except fall into the exit block.  */
      for (insn = BB_HEAD (EXIT_BLOCK_PTR->prev_bb);
	   insn;
	   insn = NEXT_INSN (insn))
	{
	  rtx set;
	  /* This should only happen once, at the start of this block.  */
	  if (GET_CODE (insn) == CODE_LABEL)
	    continue;

	  if (GET_CODE (insn) == NOTE)
	    continue;

	  if (GET_CODE (insn) == INSN
	      && GET_CODE (PATTERN (insn)) == USE)
	    continue;

	  /* Exit block also may contain copy from pseudo containing
	     return value to hard register.  */
	  if (GET_CODE (insn) == INSN
	      && (set = single_set (insn))
	      && SET_DEST (set) == current_function_return_rtx
	      && REG_P (SET_SRC (set))
	      && !return_value_pseudo)
	    {
	      return_value_pseudo = SET_SRC (set);
	      continue;
	    }

	  break;
	}

      /* If INSN is zero, then the search walked all the way through the
	 block without hitting anything interesting.  This block is a
	 valid alternate exit block.  */
      if (insn == NULL)
	alternate_exit = e->src;
      else
	return_value_pseudo = NULL;
    }

  /* If the function uses ADDRESSOF, we can't (easily) determine
     at this point if the value will end up on the stack.  */
  no_sibcalls_this_function |= sequence_uses_addressof (insns);

  /* Walk the insn chain and find any CALL_PLACEHOLDER insns.  We need to
     select one of the insn sequences attached to each CALL_PLACEHOLDER.

     The different sequences represent different ways to implement the call,
     ie, tail recursion, sibling call or normal call.

     Since we do not create nested CALL_PLACEHOLDERs, the scan
     continues with the insn that was after a replaced CALL_PLACEHOLDER;
     we don't rescan the replacement insns.  */
  for (insn = insns; insn; insn = NEXT_INSN (insn))
    {
      if (GET_CODE (insn) == CALL_INSN
	  && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
	{
	  int sibcall = (XEXP (PATTERN (insn), 1) != NULL_RTX);
	  int tailrecursion = (XEXP (PATTERN (insn), 2) != NULL_RTX);
	  basic_block call_block = BLOCK_FOR_INSN (insn);

	  /* alloca (until we have stack slot life analysis) inhibits
	     sibling call optimizations, but not tail recursion.
	     Similarly if we use varargs or stdarg since they implicitly
	     may take the address of an argument.  */
	  if (current_function_calls_alloca || current_function_stdarg)
	    sibcall = 0;

	  /* See if there are any reasons we can't perform either sibling or
	     tail call optimizations.  We must be careful with stack slots
	     which are live at potential optimization sites.  */
	  if (no_sibcalls_this_function
	      /* ??? Overly conservative.  */
	      || frame_offset
	      /* Any function that calls setjmp might have longjmp called from
		 any called function.  ??? We really should represent this
		 properly in the CFG so that this needn't be special cased.  */
	      || current_function_calls_setjmp
	      /* Can't if more than one successor or single successor is not
		 exit block.  These two tests prevent tail call optimization
		 in the presence of active exception handlers.  */
	      || call_block->succ == NULL
	      || call_block->succ->succ_next != NULL
	      || (call_block->succ->dest != EXIT_BLOCK_PTR
		  && call_block->succ->dest != alternate_exit)
	      /* If this call doesn't end the block, there are operations at
		 the end of the block which we must execute after returning.  */
	      || ! call_ends_block_p (insn, BB_END (call_block)))
	    sibcall = 0, tailrecursion = 0;

	  /* Select a set of insns to implement the call and emit them.
	     Tail recursion is the most efficient, so select it over
	     a tail/sibling call.  */

	  if (sibcall || tailrecursion)
	    successful_replacement = true;
	  replaced_call_placeholder = true;

	  replace_call_placeholder (insn,
				    tailrecursion != 0
				      ? sibcall_use_tail_recursion
				      : sibcall != 0
					 ? sibcall_use_sibcall
					 : sibcall_use_normal);
	}
    }

  if (successful_replacement)
    {
      rtx insn;
      tree arg;

      /* A sibling call sequence invalidates any REG_EQUIV notes made for
	 this function's incoming arguments.

	 At the start of RTL generation we know the only REG_EQUIV notes
	 in the rtl chain are those for incoming arguments, so we can safely
	 flush any REG_EQUIV note.

	 This is (slight) overkill.  We could keep track of the highest
	 argument we clobber and be more selective in removing notes, but it
	 does not seem to be worth the effort.  */
      purge_reg_equiv_notes ();

      /* A sibling call sequence also may invalidate RTX_UNCHANGING_P
	 flag of some incoming arguments MEM RTLs, because it can write into
	 those slots.  We clear all those bits now.

	 This is (slight) overkill, we could keep track of which arguments
	 we actually write into.  */
      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
	{
	  if (INSN_P (insn))
	    purge_mem_unchanging_flag (PATTERN (insn));
	}

      /* Similarly, invalidate RTX_UNCHANGING_P for any incoming
	 arguments passed in registers.  */
      for (arg = DECL_ARGUMENTS (current_function_decl);
	   arg;
	   arg = TREE_CHAIN (arg))
	{
	  if (REG_P (DECL_RTL (arg)))
	    RTX_UNCHANGING_P (DECL_RTL (arg)) = false;
	}
    }

  /* There may have been NOTE_INSN_BLOCK_{BEGIN,END} notes in the
     CALL_PLACEHOLDER alternatives that we didn't emit.  Rebuild the
     lexical block tree to correspond to the notes that still exist.  */
  if (replaced_call_placeholder)
    reorder_blocks ();

  /* This information will be invalid after inline expansion.  Kill it now.  */
  free_basic_block_vars (0);
  free_EXPR_LIST_list (&tail_recursion_label_list);
}
예제 #26
0
static bool
copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
{
  bool anything_changed = false;
  rtx insn;

  for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
    {
      int n_ops, i, alt, predicated;
      bool is_asm, any_replacements;
      rtx set;
      bool replaced[MAX_RECOG_OPERANDS];
      bool changed = false;
      struct kill_set_value_data ksvd;

      if (!NONDEBUG_INSN_P (insn))
	{
	  if (DEBUG_INSN_P (insn))
	    {
	      rtx loc = INSN_VAR_LOCATION_LOC (insn);
	      if (!VAR_LOC_UNKNOWN_P (loc))
		replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn),
					   ALL_REGS, GET_MODE (loc),
					   ADDR_SPACE_GENERIC, insn, vd);
	    }

	  if (insn == BB_END (bb))
	    break;
	  else
	    continue;
	}

      set = single_set (insn);
      extract_insn (insn);
      if (! constrain_operands (1))
	fatal_insn_not_found (insn);
      preprocess_constraints ();
      alt = which_alternative;
      n_ops = recog_data.n_operands;
      is_asm = asm_noperands (PATTERN (insn)) >= 0;

      /* Simplify the code below by rewriting things to reflect
	 matching constraints.  Also promote OP_OUT to OP_INOUT
	 in predicated instructions.  */

      predicated = GET_CODE (PATTERN (insn)) == COND_EXEC;
      for (i = 0; i < n_ops; ++i)
	{
	  int matches = recog_op_alt[i][alt].matches;
	  if (matches >= 0)
	    recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl;
	  if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
	      || (predicated && recog_data.operand_type[i] == OP_OUT))
	    recog_data.operand_type[i] = OP_INOUT;
	}

      /* Apply changes to earlier DEBUG_INSNs if possible.  */
      if (vd->n_debug_insn_changes)
	note_uses (&PATTERN (insn), cprop_find_used_regs, vd);

      /* For each earlyclobber operand, zap the value data.  */
      for (i = 0; i < n_ops; i++)
	if (recog_op_alt[i][alt].earlyclobber)
	  kill_value (recog_data.operand[i], vd);

      /* Within asms, a clobber cannot overlap inputs or outputs.
	 I wouldn't think this were true for regular insns, but
	 scan_rtx treats them like that...  */
      note_stores (PATTERN (insn), kill_clobbered_value, vd);

      /* Kill all auto-incremented values.  */
      /* ??? REG_INC is useless, since stack pushes aren't done that way.  */
      for_each_rtx (&PATTERN (insn), kill_autoinc_value, vd);

      /* Kill all early-clobbered operands.  */
      for (i = 0; i < n_ops; i++)
	if (recog_op_alt[i][alt].earlyclobber)
	  kill_value (recog_data.operand[i], vd);

      /* Special-case plain move instructions, since we may well
	 be able to do the move from a different register class.  */
      if (set && REG_P (SET_SRC (set)))
	{
	  rtx src = SET_SRC (set);
	  unsigned int regno = REGNO (src);
	  enum machine_mode mode = GET_MODE (src);
	  unsigned int i;
	  rtx new_rtx;

	  /* If we are accessing SRC in some mode other that what we
	     set it in, make sure that the replacement is valid.  */
	  if (mode != vd->e[regno].mode)
	    {
	      if (hard_regno_nregs[regno][mode]
		  > hard_regno_nregs[regno][vd->e[regno].mode])
		goto no_move_special_case;

	      /* And likewise, if we are narrowing on big endian the transformation
		 is also invalid.  */
	      if (hard_regno_nregs[regno][mode]
		  < hard_regno_nregs[regno][vd->e[regno].mode]
		  && (GET_MODE_SIZE (vd->e[regno].mode) > UNITS_PER_WORD
		      ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
		goto no_move_special_case;
	    }

	  /* If the destination is also a register, try to find a source
	     register in the same class.  */
	  if (REG_P (SET_DEST (set)))
	    {
	      new_rtx = find_oldest_value_reg (REGNO_REG_CLASS (regno), src, vd);
	      if (new_rtx && validate_change (insn, &SET_SRC (set), new_rtx, 0))
		{
		  if (dump_file)
		    fprintf (dump_file,
			     "insn %u: replaced reg %u with %u\n",
			     INSN_UID (insn), regno, REGNO (new_rtx));
		  changed = true;
		  goto did_replacement;
		}
	      /* We need to re-extract as validate_change clobbers
		 recog_data.  */
	      extract_insn (insn);
	      if (! constrain_operands (1))
		fatal_insn_not_found (insn);
	      preprocess_constraints ();
	    }

	  /* Otherwise, try all valid registers and see if its valid.  */
	  for (i = vd->e[regno].oldest_regno; i != regno;
	       i = vd->e[i].next_regno)
	    {
	      new_rtx = maybe_mode_change (vd->e[i].mode, vd->e[regno].mode,
				       mode, i, regno);
	      if (new_rtx != NULL_RTX)
		{
		  if (validate_change (insn, &SET_SRC (set), new_rtx, 0))
		    {
		      ORIGINAL_REGNO (new_rtx) = ORIGINAL_REGNO (src);
		      REG_ATTRS (new_rtx) = REG_ATTRS (src);
		      REG_POINTER (new_rtx) = REG_POINTER (src);
		      if (dump_file)
			fprintf (dump_file,
				 "insn %u: replaced reg %u with %u\n",
				 INSN_UID (insn), regno, REGNO (new_rtx));
		      changed = true;
		      goto did_replacement;
		    }
		  /* We need to re-extract as validate_change clobbers
		     recog_data.  */
		  extract_insn (insn);
		  if (! constrain_operands (1))
		    fatal_insn_not_found (insn);
		  preprocess_constraints ();
		}
	    }
	}
      no_move_special_case:

      any_replacements = false;

      /* For each input operand, replace a hard register with the
	 eldest live copy that's in an appropriate register class.  */
      for (i = 0; i < n_ops; i++)
	{
	  replaced[i] = false;

	  /* Don't scan match_operand here, since we've no reg class
	     information to pass down.  Any operands that we could
	     substitute in will be represented elsewhere.  */
	  if (recog_data.constraints[i][0] == '\0')
	    continue;

	  /* Don't replace in asms intentionally referencing hard regs.  */
	  if (is_asm && REG_P (recog_data.operand[i])
	      && (REGNO (recog_data.operand[i])
		  == ORIGINAL_REGNO (recog_data.operand[i])))
	    continue;

	  if (recog_data.operand_type[i] == OP_IN)
	    {
	      if (recog_op_alt[i][alt].is_address)
		replaced[i]
		  = replace_oldest_value_addr (recog_data.operand_loc[i],
					       recog_op_alt[i][alt].cl,
					       VOIDmode, ADDR_SPACE_GENERIC,
					       insn, vd);
	      else if (REG_P (recog_data.operand[i]))
		replaced[i]
		  = replace_oldest_value_reg (recog_data.operand_loc[i],
					      recog_op_alt[i][alt].cl,
					      insn, vd);
	      else if (MEM_P (recog_data.operand[i]))
		replaced[i] = replace_oldest_value_mem (recog_data.operand[i],
							insn, vd);
	    }
	  else if (MEM_P (recog_data.operand[i]))
	    replaced[i] = replace_oldest_value_mem (recog_data.operand[i],
						    insn, vd);

	  /* If we performed any replacement, update match_dups.  */
	  if (replaced[i])
	    {
	      int j;
	      rtx new_rtx;

	      new_rtx = *recog_data.operand_loc[i];
	      recog_data.operand[i] = new_rtx;
	      for (j = 0; j < recog_data.n_dups; j++)
		if (recog_data.dup_num[j] == i)
		  validate_unshare_change (insn, recog_data.dup_loc[j], new_rtx, 1);

	      any_replacements = true;
	    }
	}

      if (any_replacements)
	{
	  if (! apply_change_group ())
	    {
	      for (i = 0; i < n_ops; i++)
		if (replaced[i])
		  {
		    rtx old = *recog_data.operand_loc[i];
		    recog_data.operand[i] = old;
		  }

	      if (dump_file)
		fprintf (dump_file,
			 "insn %u: reg replacements not verified\n",
			 INSN_UID (insn));
	    }
	  else
	    changed = true;
	}

    did_replacement:
      if (changed)
	{
	  anything_changed = true;

	  /* If something changed, perhaps further changes to earlier
	     DEBUG_INSNs can be applied.  */
	  if (vd->n_debug_insn_changes)
	    note_uses (&PATTERN (insn), cprop_find_used_regs, vd);
	}

      ksvd.vd = vd;
      ksvd.ignore_set_reg = NULL_RTX;

      /* Clobber call-clobbered registers.  */
      if (CALL_P (insn))
	{
	  unsigned int set_regno = INVALID_REGNUM;
	  unsigned int set_nregs = 0;
	  unsigned int regno;
	  rtx exp;
	  hard_reg_set_iterator hrsi;

	  for (exp = CALL_INSN_FUNCTION_USAGE (insn); exp; exp = XEXP (exp, 1))
	    {
	      rtx x = XEXP (exp, 0);
	      if (GET_CODE (x) == SET)
		{
		  rtx dest = SET_DEST (x);
		  kill_value (dest, vd);
		  set_value_regno (REGNO (dest), GET_MODE (dest), vd);
		  copy_value (dest, SET_SRC (x), vd);
		  ksvd.ignore_set_reg = dest;
		  set_regno = REGNO (dest);
		  set_nregs
		    = hard_regno_nregs[set_regno][GET_MODE (dest)];
		  break;
		}
	    }

	  EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi)
	    if (regno < set_regno || regno >= set_regno + set_nregs)
	      kill_value_regno (regno, 1, vd);

	  /* If SET was seen in CALL_INSN_FUNCTION_USAGE, and SET_SRC
	     of the SET isn't in regs_invalidated_by_call hard reg set,
	     but instead among CLOBBERs on the CALL_INSN, we could wrongly
	     assume the value in it is still live.  */
	  if (ksvd.ignore_set_reg)
	    note_stores (PATTERN (insn), kill_clobbered_value, vd);
	}

      /* Notice stores.  */
      note_stores (PATTERN (insn), kill_set_value, &ksvd);

      /* Notice copies.  */
      if (set && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set)))
	copy_value (SET_DEST (set), SET_SRC (set), vd);

      if (insn == BB_END (bb))
	break;
    }

  return anything_changed;
}
예제 #27
0
static unsigned int
rest_of_handle_simplify_got (void)
{
  df_ref ref;
  rtx use = NULL_RTX;
  int i, n_symbol, n_access = 0;
  struct got_access_info* got_accesses;
  htab_t var_table = htab_create (VAR_TABLE_SIZE,
				  htab_hash_pointer,
				  htab_eq_pointer,
				  NULL);
  rtx pic_reg = targetm.got_access.get_pic_reg ();
  gcc_assert (pic_reg);

  ref = DF_REG_USE_CHAIN (REGNO (pic_reg));
  got_accesses = XNEWVEC(struct got_access_info,
			 DF_REG_USE_COUNT (REGNO (pic_reg)));

  /* Check if all uses of pic_reg are loading global address through the
     default method.  */
  while (ref)
    {
      rtx_insn* insn = DF_REF_INSN (ref);

      /* Check for the special USE insn, it is not a real usage of pic_reg.  */
      if (GET_CODE (PATTERN (insn)) == USE)
	use = insn;
      else
	{
	  /* If an insn both set and use pic_reg, it is in the process of
	     constructing the value of pic_reg. We should also ignore it.  */
	  rtx set = single_set (insn);
	  if (!(set && SET_DEST (set) == pic_reg))
	    {
	      rtx offset_reg;
	      rtx offset_insn;
	      rtx symbol = targetm.got_access.loaded_global_var (insn,
								 &offset_reg,
								 &offset_insn);
	      if (symbol)
		{
		  rtx* slot = (rtx*) htab_find_slot (var_table, symbol, INSERT);
		  if (*slot == HTAB_EMPTY_ENTRY)
		    *slot = symbol;

		  gcc_assert (set);
		  got_accesses[n_access].symbol = symbol;
		  got_accesses[n_access].offset_reg = offset_reg;
		  got_accesses[n_access].address_reg = SET_DEST (set);
		  got_accesses[n_access].load_insn = insn;
		  got_accesses[n_access].offset_insn = offset_insn;
		  n_access++;
		}
	      else
		{
		  /* This insn doesn't load a global address, but it has
		     other unexpected usage of pic_reg, give up.  */
		  free (got_accesses);
		  htab_delete (var_table);
		  return 0;
		}
	    }
	}
      ref = DF_REF_NEXT_REG(ref);
    }

  /* Check if we can simplify it.  */
  n_symbol = htab_elements (var_table);
  gcc_assert (n_symbol <= n_access);
  if (!targetm.got_access.can_simplify_got_access (n_symbol, n_access))
    {
      free (got_accesses);
      htab_delete (var_table);
      return 0;
    }

  /* Rewrite the global address loading insns.  */
  for (i=0; i<n_access; i++)
    targetm.got_access.load_global_address (got_accesses[i].symbol,
					    got_accesses[i].offset_reg,
					    got_accesses[i].address_reg,
					    got_accesses[i].load_insn,
					    got_accesses[i].offset_insn);

  /* Since there is no usage of pic_reg now, we can remove it.  */
  if (use)
    remove_insn (use);
  targetm.got_access.clear_pic_reg ();
  free (got_accesses);
  htab_delete (var_table);
  return 0;
}
예제 #28
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;
}
예제 #29
0
파일: web.c 프로젝트: AhmadTux/DragonFlyBSD
static void
union_defs (struct df *df, struct ref *use, struct web_entry *def_entry,
            struct web_entry *use_entry)
{
  rtx insn = DF_REF_INSN (use);
  struct df_link *link = DF_REF_CHAIN (use);
  struct df_link *use_link = DF_INSN_USES (df, insn);
  struct df_link *def_link = DF_INSN_DEFS (df, insn);
  rtx set = single_set (insn);

  /* Some instructions may use match_dup for their operands.  In case the
     operands are dead, we will assign them different pseudos, creating
     invalid instructions, so union all uses of the same operand for each
     insn.  */

  while (use_link)
    {
      if (use != use_link->ref
	  && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (use_link->ref))
	unionfind_union (use_entry + DF_REF_ID (use),
		         use_entry + DF_REF_ID (use_link->ref));
      use_link = use_link->next;
    }

  /* Recognize trivial noop moves and attempt to keep them as noop.
     While most of noop moves should be removed, we still keep some
     of them at libcall boundaries and such.  */

  if (set
      && SET_SRC (set) == DF_REF_REG (use)
      && SET_SRC (set) == SET_DEST (set))
    {
      while (def_link)
	{
	  if (DF_REF_REAL_REG (use) == DF_REF_REAL_REG (def_link->ref))
	    unionfind_union (use_entry + DF_REF_ID (use),
			     def_entry + DF_REF_ID (def_link->ref));
	  def_link = def_link->next;
	}
    }
  while (link)
    {
      unionfind_union (use_entry + DF_REF_ID (use),
		       def_entry + DF_REF_ID (link->ref));
      link = link->next;
    }

  /* A READ_WRITE use requires the corresponding def to be in the same
     register.  Find it and union.  */
  if (use->flags & DF_REF_READ_WRITE)
    {
      struct df_link *link = DF_INSN_DEFS (df, DF_REF_INSN (use));

      while (link)
	{
	  if (DF_REF_REAL_REG (link->ref) == DF_REF_REAL_REG (use))
	    unionfind_union (use_entry + DF_REF_ID (use),
			     def_entry + DF_REF_ID (link->ref));
	  link = link->next;
	}
    }
}
예제 #30
0
/* Do transforms 3) and 4) on INSN if applicable.  */
static bool
mod_subtract_transform (rtx insn)
{
  rtx set, set_src, set_dest, op1, op2, value, histogram;
  enum rtx_code code;
  enum machine_mode mode;
  gcov_type wrong_values, counts[2], count, all;
  edge e;
  int i, prob1, prob2;

  set = single_set (insn);
  if (!set)
    return false;

  set_src = SET_SRC (set);
  set_dest = SET_DEST (set);
  code = GET_CODE (set_src);
  mode = GET_MODE (set_dest);
  
  if (code != UMOD)
    return false;
  op1 = XEXP (set_src, 0);
  op2 = XEXP (set_src, 1);

  for (histogram = REG_NOTES (insn);
       histogram;
       histogram = XEXP (histogram, 1))
    if (REG_NOTE_KIND (histogram) == REG_VALUE_PROFILE
	&& XEXP (XEXP (histogram, 0), 0) == GEN_INT (HIST_TYPE_INTERVAL))
      break;

  if (!histogram)
    return false;

  histogram = XEXP (XEXP (histogram, 0), 1);
  value = XEXP (histogram, 0);
  histogram = XEXP (histogram, 1);

  all = 0;
  for (i = 0; i < 2; i++)
    {
      counts[i] = INTVAL (XEXP (histogram, 0));
      all += counts[i];
      histogram = XEXP (histogram, 1);
    }
  wrong_values = INTVAL (XEXP (histogram, 0));
  histogram = XEXP (histogram, 1);
  wrong_values += INTVAL (XEXP (histogram, 0));
  all += wrong_values;

  /* We require that we use just subtractions in at least 50% of all
     evaluations.  */
  count = 0;
  for (i = 0; i < 2; i++)
    {
      count += counts[i];
      if (count * 2 >= all)
	break;
    }
  
  if (i == 2)
    return false;

  if (dump_file)
    fprintf (dump_file, "Mod subtract transformation on insn %d\n",
	     INSN_UID (insn));

  /* Compute probability of taking the optimal path(s).  */
  prob1 = (counts[0] * REG_BR_PROB_BASE + all / 2) / all;
  prob2 = (counts[1] * REG_BR_PROB_BASE + all / 2) / all;

  e = split_block (BLOCK_FOR_INSN (insn), PREV_INSN (insn));
  delete_insn (insn);
  
  insert_insn_on_edge (
	gen_mod_subtract (mode, code, set_dest,
			  op1, op2, i, prob1, prob2), e);

  return true;
}