Exemple #1
0
static void
init_label_info (rtx f)
{
  rtx insn;

  for (insn = f; insn; insn = NEXT_INSN (insn))
    {
      if (LABEL_P (insn))
	LABEL_NUSES (insn) = (LABEL_PRESERVE_P (insn) != 0);

      /* REG_LABEL_TARGET notes (including the JUMP_LABEL field) are
	 sticky and not reset here; that way we won't lose association
	 with a label when e.g. the source for a target register
	 disappears out of reach for targets that may use jump-target
	 registers.  Jump transformations are supposed to transform
	 any REG_LABEL_TARGET notes.  The target label reference in a
	 branch may disappear from the branch (and from the
	 instruction before it) for other reasons, like register
	 allocation.  */

      if (INSN_P (insn))
	{
	  rtx note, next;

	  for (note = REG_NOTES (insn); note; note = next)
	    {
	      next = XEXP (note, 1);
	      if (REG_NOTE_KIND (note) == REG_LABEL_OPERAND
		  && ! reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
		remove_note (insn, note);
	    }
	}
    }
}
Exemple #2
0
/* INSN is being scheduled after LAST.  Update counters.  */
static void
begin_schedule_ready (rtx insn, rtx last)
{
  sched_rgn_n_insns++;

  if (BLOCK_FOR_INSN (insn) == last_bb
      /* INSN is a jump in the last block, ...  */
      && control_flow_insn_p (insn)
      /* that is going to be moved over some instructions.  */
      && last != PREV_INSN (insn))
    {
      edge e;
      basic_block bb;

      /* An obscure special case, where we do have partially dead
	 instruction scheduled after last control flow instruction.
	 In this case we can create new basic block.  It is
	 always exactly one basic block last in the sequence.  */

      e = find_fallthru_edge (last_bb->succs);

      gcc_checking_assert (!e || !(e->flags & EDGE_COMPLEX));

      gcc_checking_assert (BLOCK_FOR_INSN (insn) == last_bb
			   && !IS_SPECULATION_CHECK_P (insn)
			   && BB_HEAD (last_bb) != insn
			   && BB_END (last_bb) == insn);

      {
	rtx x;

	x = NEXT_INSN (insn);
	if (e)
	  gcc_checking_assert (NOTE_P (x) || LABEL_P (x));
	else
	  gcc_checking_assert (BARRIER_P (x));
      }

      if (e)
	{
	  bb = split_edge (e);
	  gcc_assert (NOTE_INSN_BASIC_BLOCK_P (BB_END (bb)));
	}
      else
	/* Create an empty unreachable block after the INSN.  */
	bb = create_basic_block (NEXT_INSN (insn), NULL_RTX, last_bb);

      /* split_edge () creates BB before E->DEST.  Keep in mind, that
	 this operation extends scheduling region till the end of BB.
	 Hence, we need to shift NEXT_TAIL, so haifa-sched.c won't go out
	 of the scheduling region.  */
      current_sched_info->next_tail = NEXT_INSN (BB_END (bb));
      gcc_assert (current_sched_info->next_tail);

      /* Append new basic block to the end of the ebb.  */
      sched_init_only_bb (bb, last_bb);
      gcc_assert (last_bb == bb);
    }
}
Exemple #3
0
static rtx
label_for_bb (basic_block bb)
{
  rtx label = BB_HEAD (bb);

  if (!LABEL_P (label))
    {
      if (dump_file)
	fprintf (dump_file, "Emitting label for block %d\n", bb->index);

      label = block_label (bb);
    }

  return label;
}
Exemple #4
0
enum rtx_code
classify_insn (rtx x)
{
  if (LABEL_P (x))
    return CODE_LABEL;
  if (GET_CODE (x) == CALL)
    return CALL_INSN;
  if (ANY_RETURN_P (x))
    return JUMP_INSN;
  if (GET_CODE (x) == SET)
    {
      if (GET_CODE (SET_DEST (x)) == PC)
	return JUMP_INSN;
      else if (GET_CODE (SET_SRC (x)) == CALL)
	return CALL_INSN;
      else
	return INSN;
    }
  if (GET_CODE (x) == PARALLEL)
    {
      int j;
      bool has_return_p = false;
      for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
	if (GET_CODE (XVECEXP (x, 0, j)) == CALL)
	  return CALL_INSN;
	else if (ANY_RETURN_P (XVECEXP (x, 0, j)))
	  has_return_p = true;
	else if (GET_CODE (XVECEXP (x, 0, j)) == SET
		 && GET_CODE (SET_DEST (XVECEXP (x, 0, j))) == PC)
	  return JUMP_INSN;
	else if (GET_CODE (XVECEXP (x, 0, j)) == SET
		 && GET_CODE (SET_SRC (XVECEXP (x, 0, j))) == CALL)
	  return CALL_INSN;
      if (has_return_p)
	return JUMP_INSN;
    }
#ifdef GENERATOR_FILE
  if (GET_CODE (x) == MATCH_OPERAND
      || GET_CODE (x) == MATCH_OPERATOR
      || GET_CODE (x) == MATCH_PARALLEL
      || GET_CODE (x) == MATCH_OP_DUP
      || GET_CODE (x) == MATCH_DUP
      || GET_CODE (x) == PARALLEL)
    return UNKNOWN;
#endif
  return INSN;
}
Exemple #5
0
/* This function rebuilds the JUMP_LABEL field and REG_LABEL_TARGET
   notes in jumping insns and REG_LABEL_OPERAND notes in non-jumping
   instructions and jumping insns that have labels as operands
   (e.g. cbranchsi4).  */
void
rebuild_jump_labels (rtx f)
{
  rtx insn;

  timevar_push (TV_REBUILD_JUMP);
  init_label_info (f);
  mark_all_labels (f);

  /* Keep track of labels used from static data; we don't track them
     closely enough to delete them here, so make sure their reference
     count doesn't drop to zero.  */

  for (insn = forced_labels; insn; insn = XEXP (insn, 1))
    if (LABEL_P (XEXP (insn, 0)))
      LABEL_NUSES (XEXP (insn, 0))++;
  timevar_pop (TV_REBUILD_JUMP);
}
Exemple #6
0
static int
optimize_mode_switching (void)
{
  int e;
  basic_block bb;
  bool need_commit = false;
  static const int num_modes[] = NUM_MODES_FOR_MODE_SWITCHING;
#define N_ENTITIES ARRAY_SIZE (num_modes)
  int entity_map[N_ENTITIES];
  struct bb_info *bb_info[N_ENTITIES];
  int i, j;
  int n_entities = 0;
  int max_num_modes = 0;
  bool emitted ATTRIBUTE_UNUSED = false;
  basic_block post_entry = 0;
  basic_block pre_exit = 0;
  struct edge_list *edge_list = 0;

  /* These bitmaps are used for the LCM algorithm.  */
  sbitmap *kill, *del, *insert, *antic, *transp, *comp;
  sbitmap *avin, *avout;

  for (e = N_ENTITIES - 1; e >= 0; e--)
    if (OPTIMIZE_MODE_SWITCHING (e))
      {
	int entry_exit_extra = 0;

	/* Create the list of segments within each basic block.
	   If NORMAL_MODE is defined, allow for two extra
	   blocks split from the entry and exit block.  */
	if (targetm.mode_switching.entry && targetm.mode_switching.exit)
	  entry_exit_extra = 3;

	bb_info[n_entities]
	  = XCNEWVEC (struct bb_info,
		      last_basic_block_for_fn (cfun) + entry_exit_extra);
	entity_map[n_entities++] = e;
	if (num_modes[e] > max_num_modes)
	  max_num_modes = num_modes[e];
      }

  if (! n_entities)
    return 0;

  /* Make sure if MODE_ENTRY is defined MODE_EXIT is defined.  */
  gcc_assert ((targetm.mode_switching.entry && targetm.mode_switching.exit)
	      || (!targetm.mode_switching.entry
		  && !targetm.mode_switching.exit));

  if (targetm.mode_switching.entry && targetm.mode_switching.exit)
    {
      /* Split the edge from the entry block, so that we can note that
	 there NORMAL_MODE is supplied.  */
      post_entry = split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
      pre_exit = create_pre_exit (n_entities, entity_map, num_modes);
    }

  df_analyze ();

  /* Create the bitmap vectors.  */
  antic = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
				n_entities * max_num_modes);
  transp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
				 n_entities * max_num_modes);
  comp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
			       n_entities * max_num_modes);
  avin = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
			       n_entities * max_num_modes);
  avout = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
				n_entities * max_num_modes);
  kill = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
			       n_entities * max_num_modes);

  bitmap_vector_ones (transp, last_basic_block_for_fn (cfun));
  bitmap_vector_clear (antic, last_basic_block_for_fn (cfun));
  bitmap_vector_clear (comp, last_basic_block_for_fn (cfun));

  for (j = n_entities - 1; j >= 0; j--)
    {
      int e = entity_map[j];
      int no_mode = num_modes[e];
      struct bb_info *info = bb_info[j];
      rtx_insn *insn;

      /* Determine what the first use (if any) need for a mode of entity E is.
	 This will be the mode that is anticipatable for this block.
	 Also compute the initial transparency settings.  */
      FOR_EACH_BB_FN (bb, cfun)
	{
	  struct seginfo *ptr;
	  int last_mode = no_mode;
	  bool any_set_required = false;
	  HARD_REG_SET live_now;

	  info[bb->index].mode_out = info[bb->index].mode_in = no_mode;

	  REG_SET_TO_HARD_REG_SET (live_now, df_get_live_in (bb));

	  /* Pretend the mode is clobbered across abnormal edges.  */
	  {
	    edge_iterator ei;
	    edge eg;
	    FOR_EACH_EDGE (eg, ei, bb->preds)
	      if (eg->flags & EDGE_COMPLEX)
		break;
	    if (eg)
	      {
		rtx_insn *ins_pos = BB_HEAD (bb);
		if (LABEL_P (ins_pos))
		  ins_pos = NEXT_INSN (ins_pos);
		gcc_assert (NOTE_INSN_BASIC_BLOCK_P (ins_pos));
		if (ins_pos != BB_END (bb))
		  ins_pos = NEXT_INSN (ins_pos);
		ptr = new_seginfo (no_mode, ins_pos, bb->index, live_now);
		add_seginfo (info + bb->index, ptr);
		for (i = 0; i < no_mode; i++)
		  clear_mode_bit (transp[bb->index], j, i);
	      }
	  }

	  FOR_BB_INSNS (bb, insn)
	    {
	      if (INSN_P (insn))
		{
		  int mode = targetm.mode_switching.needed (e, insn);
		  rtx link;

		  if (mode != no_mode && mode != last_mode)
		    {
		      any_set_required = true;
		      last_mode = mode;
		      ptr = new_seginfo (mode, insn, bb->index, live_now);
		      add_seginfo (info + bb->index, ptr);
		      for (i = 0; i < no_mode; i++)
			clear_mode_bit (transp[bb->index], j, i);
		    }

		  if (targetm.mode_switching.after)
		    last_mode = targetm.mode_switching.after (e, last_mode,
							      insn);

		  /* Update LIVE_NOW.  */
		  for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
		    if (REG_NOTE_KIND (link) == REG_DEAD)
		      reg_dies (XEXP (link, 0), &live_now);

		  note_stores (PATTERN (insn), reg_becomes_live, &live_now);
		  for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
		    if (REG_NOTE_KIND (link) == REG_UNUSED)
		      reg_dies (XEXP (link, 0), &live_now);
		}
	    }

	  info[bb->index].computing = last_mode;
	  /* Check for blocks without ANY mode requirements.
	     N.B. because of MODE_AFTER, last_mode might still
	     be different from no_mode, in which case we need to
	     mark the block as nontransparent.  */
	  if (!any_set_required)
	    {
	      ptr = new_seginfo (no_mode, BB_END (bb), bb->index, live_now);
	      add_seginfo (info + bb->index, ptr);
	      if (last_mode != no_mode)
		for (i = 0; i < no_mode; i++)
		  clear_mode_bit (transp[bb->index], j, i);
	    }
	}
      if (targetm.mode_switching.entry && targetm.mode_switching.exit)
	{
	  int mode = targetm.mode_switching.entry (e);

	  info[post_entry->index].mode_out =
	    info[post_entry->index].mode_in = no_mode;
	  if (pre_exit)
	    {
	      info[pre_exit->index].mode_out =
		info[pre_exit->index].mode_in = no_mode;
	    }

	  if (mode != no_mode)
	    {
	      bb = post_entry;

	      /* By always making this nontransparent, we save
		 an extra check in make_preds_opaque.  We also
		 need this to avoid confusing pre_edge_lcm when
		 antic is cleared but transp and comp are set.  */
	      for (i = 0; i < no_mode; i++)
		clear_mode_bit (transp[bb->index], j, i);

	      /* Insert a fake computing definition of MODE into entry
		 blocks which compute no mode. This represents the mode on
		 entry.  */
	      info[bb->index].computing = mode;

	      if (pre_exit)
		info[pre_exit->index].seginfo->mode =
		  targetm.mode_switching.exit (e);
	    }
	}

      /* Set the anticipatable and computing arrays.  */
      for (i = 0; i < no_mode; i++)
	{
	  int m = targetm.mode_switching.priority (entity_map[j], i);

	  FOR_EACH_BB_FN (bb, cfun)
	    {
	      if (info[bb->index].seginfo->mode == m)
		set_mode_bit (antic[bb->index], j, m);

	      if (info[bb->index].computing == m)
		set_mode_bit (comp[bb->index], j, m);
	    }
	}
    }

  /* Calculate the optimal locations for the
     placement mode switches to modes with priority I.  */

  FOR_EACH_BB_FN (bb, cfun)
    bitmap_not (kill[bb->index], transp[bb->index]);

  edge_list = pre_edge_lcm_avs (n_entities * max_num_modes, transp, comp, antic,
				kill, avin, avout, &insert, &del);

  for (j = n_entities - 1; j >= 0; j--)
    {
      int no_mode = num_modes[entity_map[j]];

      /* Insert all mode sets that have been inserted by lcm.  */

      for (int ed = NUM_EDGES (edge_list) - 1; ed >= 0; ed--)
	{
	  edge eg = INDEX_EDGE (edge_list, ed);

	  eg->aux = (void *)(intptr_t)-1;

	  for (i = 0; i < no_mode; i++)
	    {
	      int m = targetm.mode_switching.priority (entity_map[j], i);
	      if (mode_bit_p (insert[ed], j, m))
		{
		  eg->aux = (void *)(intptr_t)m;
		  break;
		}
	    }
	}

      FOR_EACH_BB_FN (bb, cfun)
	{
	  struct bb_info *info = bb_info[j];
	  int last_mode = no_mode;

	  /* intialize mode in availability for bb.  */
	  for (i = 0; i < no_mode; i++)
	    if (mode_bit_p (avout[bb->index], j, i))
	      {
		if (last_mode == no_mode)
		  last_mode = i;
		if (last_mode != i)
		  {
		    last_mode = no_mode;
		    break;
		  }
	      }
	  info[bb->index].mode_out = last_mode;

	  /* intialize mode out availability for bb.  */
	  last_mode = no_mode;
	  for (i = 0; i < no_mode; i++)
	    if (mode_bit_p (avin[bb->index], j, i))
	      {
		if (last_mode == no_mode)
		  last_mode = i;
		if (last_mode != i)
		  {
		    last_mode = no_mode;
		    break;
		  }
	      }
	  info[bb->index].mode_in = last_mode;

	  for (i = 0; i < no_mode; i++)
	    if (mode_bit_p (del[bb->index], j, i))
	      info[bb->index].seginfo->mode = no_mode;
	}

      /* Now output the remaining mode sets in all the segments.  */

      /* In case there was no mode inserted. the mode information on the edge
	 might not be complete.
	 Update mode info on edges and commit pending mode sets.  */
      need_commit |= commit_mode_sets (edge_list, entity_map[j], bb_info[j]);

      /* Reset modes for next entity.  */
      clear_aux_for_edges ();

      FOR_EACH_BB_FN (bb, cfun)
	{
	  struct seginfo *ptr, *next;
	  int cur_mode = bb_info[j][bb->index].mode_in;

	  for (ptr = bb_info[j][bb->index].seginfo; ptr; ptr = next)
	    {
	      next = ptr->next;
	      if (ptr->mode != no_mode)
		{
		  rtx_insn *mode_set;

		  rtl_profile_for_bb (bb);
		  start_sequence ();

		  targetm.mode_switching.emit (entity_map[j], ptr->mode,
					       cur_mode, ptr->regs_live);
		  mode_set = get_insns ();
		  end_sequence ();

		  /* modes kill each other inside a basic block.  */
		  cur_mode = ptr->mode;

		  /* Insert MODE_SET only if it is nonempty.  */
		  if (mode_set != NULL_RTX)
		    {
		      emitted = true;
		      if (NOTE_INSN_BASIC_BLOCK_P (ptr->insn_ptr))
			/* We need to emit the insns in a FIFO-like manner,
			   i.e. the first to be emitted at our insertion
			   point ends up first in the instruction steam.
			   Because we made sure that NOTE_INSN_BASIC_BLOCK is
			   only used for initially empty basic blocks, we
			   can achieve this by appending at the end of
			   the block.  */
			emit_insn_after
			  (mode_set, BB_END (NOTE_BASIC_BLOCK (ptr->insn_ptr)));
		      else
			emit_insn_before (mode_set, ptr->insn_ptr);
		    }

		  default_rtl_profile ();
		}

	      free (ptr);
	    }
	}

      free (bb_info[j]);
    }

  free_edge_list (edge_list);

  /* Finished. Free up all the things we've allocated.  */
  sbitmap_vector_free (del);
  sbitmap_vector_free (insert);
  sbitmap_vector_free (kill);
  sbitmap_vector_free (antic);
  sbitmap_vector_free (transp);
  sbitmap_vector_free (comp);
  sbitmap_vector_free (avin);
  sbitmap_vector_free (avout);

  if (need_commit)
    commit_edge_insertions ();

  if (targetm.mode_switching.entry && targetm.mode_switching.exit)
    cleanup_cfg (CLEANUP_NO_INSN_DEL);
  else if (!need_commit && !emitted)
    return 0;

  return 1;
}
Exemple #7
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);
	      }
	}
    }
Exemple #8
0
static basic_block
expand_gimple_tailcall (basic_block bb, tree stmt, bool *can_fallthru)
{
  rtx last2, last;
  edge e;
  edge_iterator ei;
  int probability;
  gcov_type count;

  last2 = last = get_last_insn ();

  expand_expr_stmt (stmt);

  for (last = NEXT_INSN (last); last; last = NEXT_INSN (last))
    if (CALL_P (last) && SIBLING_CALL_P (last))
      goto found;

  maybe_dump_rtl_for_tree_stmt (stmt, last2);

  *can_fallthru = true;
  return NULL;

 found:
  /* ??? Wouldn't it be better to just reset any pending stack adjust?
     Any instructions emitted here are about to be deleted.  */
  do_pending_stack_adjust ();

  /* Remove any non-eh, non-abnormal edges that don't go to exit.  */
  /* ??? I.e. the fallthrough edge.  HOWEVER!  If there were to be
     EH or abnormal edges, we shouldn't have created a tail call in
     the first place.  So it seems to me we should just be removing
     all edges here, or redirecting the existing fallthru edge to
     the exit block.  */

  probability = 0;
  count = 0;

  for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
    {
      if (!(e->flags & (EDGE_ABNORMAL | EDGE_EH)))
	{
	  if (e->dest != EXIT_BLOCK_PTR)
	    {
	      e->dest->count -= e->count;
	      e->dest->frequency -= EDGE_FREQUENCY (e);
	      if (e->dest->count < 0)
	        e->dest->count = 0;
	      if (e->dest->frequency < 0)
	        e->dest->frequency = 0;
	    }
	  count += e->count;
	  probability += e->probability;
	  remove_edge (e);
	}
      else
	ei_next (&ei);
    }

  /* This is somewhat ugly: the call_expr expander often emits instructions
     after the sibcall (to perform the function return).  These confuse the
     find_sub_basic_blocks code, so we need to get rid of these.  */
  last = NEXT_INSN (last);
  gcc_assert (BARRIER_P (last));

  *can_fallthru = false;
  while (NEXT_INSN (last))
    {
      /* For instance an sqrt builtin expander expands if with
	 sibcall in the then and label for `else`.  */
      if (LABEL_P (NEXT_INSN (last)))
	{
	  *can_fallthru = true;
	  break;
	}
      delete_insn (NEXT_INSN (last));
    }

  e = make_edge (bb, EXIT_BLOCK_PTR, EDGE_ABNORMAL | EDGE_SIBCALL);
  e->probability += probability;
  e->count += count;
  BB_END (bb) = last;
  update_bb_for_insn (bb);

  if (NEXT_INSN (last))
    {
      bb = create_basic_block (NEXT_INSN (last), get_last_insn (), bb);

      last = BB_END (bb);
      if (BARRIER_P (last))
	BB_END (bb) = PREV_INSN (last);
    }

  maybe_dump_rtl_for_tree_stmt (stmt, last2);

  return bb;
}
Exemple #9
0
static void
print_rtx (rtx in_rtx)
{
  int i = 0;
  int j;
  const char *format_ptr;
  int is_insn;

  if (sawclose)
    {
      if (flag_simple)
        fputc (' ', outfile);
      else
        fprintf (outfile, "\n%s%*s", print_rtx_head, indent * 2, "");
      sawclose = 0;
    }

  if (in_rtx == 0)
    {
      fputs ("(nil)", outfile);
      sawclose = 1;
      return;
    }
  else if (GET_CODE (in_rtx) > NUM_RTX_CODE)
    {
       fprintf (outfile, "(??? bad code %d\n)", GET_CODE (in_rtx));
       sawclose = 1;
       return;
    }

  is_insn = INSN_P (in_rtx);

  /* When printing in VCG format we write INSNs, NOTE, LABEL, and BARRIER
     in separate nodes and therefore have to handle them special here.  */
  if (dump_for_graph
      && (is_insn || NOTE_P (in_rtx)
          || LABEL_P (in_rtx) || BARRIER_P (in_rtx)))
    {
      i = 3;
      indent = 0;
    }
  else
    {
      /* Print name of expression code.  */
      if (flag_simple && GET_CODE (in_rtx) == CONST_INT)
        fputc ('(', outfile);
      else
        fprintf (outfile, "(%s", GET_RTX_NAME (GET_CODE (in_rtx)));

      if (! flag_simple)
        {
          if (RTX_FLAG (in_rtx, in_struct))
            fputs ("/s", outfile);

          if (RTX_FLAG (in_rtx, volatil))
            fputs ("/v", outfile);

          if (RTX_FLAG (in_rtx, unchanging))
            fputs ("/u", outfile);

          if (RTX_FLAG (in_rtx, frame_related))
            fputs ("/f", outfile);

          if (RTX_FLAG (in_rtx, jump))
            fputs ("/j", outfile);

          if (RTX_FLAG (in_rtx, call))
            fputs ("/c", outfile);

          if (RTX_FLAG (in_rtx, return_val))
            fputs ("/i", outfile);

          /* Print REG_NOTE names for EXPR_LIST and INSN_LIST.  */
          if (GET_CODE (in_rtx) == EXPR_LIST
              || GET_CODE (in_rtx) == INSN_LIST)
            fprintf (outfile, ":%s",
                     GET_REG_NOTE_NAME (GET_MODE (in_rtx)));

          /* For other rtl, print the mode if it's not VOID.  */
          else if (GET_MODE (in_rtx) != VOIDmode)
            fprintf (outfile, ":%s", GET_MODE_NAME (GET_MODE (in_rtx)));
        }
    }

#ifndef GENERATOR_FILE
  if (GET_CODE (in_rtx) == CONST_DOUBLE && FLOAT_MODE_P (GET_MODE (in_rtx)))
    i = 5;
#endif

  /* Get the format string and skip the first elements if we have handled
     them already.  */
  format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx)) + i;
  for (; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++)
    switch (*format_ptr++)
      {
        const char *str;

      case 'T':
        str = XTMPL (in_rtx, i);
        goto string;

      case 'S':
      case 's':
        str = XSTR (in_rtx, i);
      string:

        if (str == 0)
          fputs (dump_for_graph ? " \\\"\\\"" : " \"\"", outfile);
        else
          {
            if (dump_for_graph)
              fprintf (outfile, " (\\\"%s\\\")", str);
            else
              fprintf (outfile, " (\"%s\")", str);
          }
        sawclose = 1;
        break;

        /* 0 indicates a field for internal use that should not be printed.
           An exception is the third field of a NOTE, where it indicates
           that the field has several different valid contents.  */
      case '0':
        if (i == 1 && REG_P (in_rtx))
          {
            if (REGNO (in_rtx) != ORIGINAL_REGNO (in_rtx))
              fprintf (outfile, " [%d]", ORIGINAL_REGNO (in_rtx));
          }
#ifndef GENERATOR_FILE
        else if (i == 1 && GET_CODE (in_rtx) == SYMBOL_REF)
          {
            int flags = SYMBOL_REF_FLAGS (in_rtx);
            if (flags)
              fprintf (outfile, " [flags 0x%x]", flags);
          }
        else if (i == 2 && GET_CODE (in_rtx) == SYMBOL_REF)
          {
            tree decl = SYMBOL_REF_DECL (in_rtx);
            if (decl)
              print_node_brief (outfile, "", decl, 0);
          }
#endif
        else if (i == 4 && NOTE_P (in_rtx))
          {
            switch (NOTE_LINE_NUMBER (in_rtx))
              {
              case NOTE_INSN_EH_REGION_BEG:
              case NOTE_INSN_EH_REGION_END:
                if (flag_dump_unnumbered)
                  fprintf (outfile, " #");
                else
                  fprintf (outfile, " %d", NOTE_EH_HANDLER (in_rtx));
                sawclose = 1;
                break;

              case NOTE_INSN_BLOCK_BEG:
              case NOTE_INSN_BLOCK_END:
#ifndef GENERATOR_FILE
                dump_addr (outfile, " ", NOTE_BLOCK (in_rtx));
#endif
                sawclose = 1;
                break;

              case NOTE_INSN_BASIC_BLOCK:
                {
#ifndef GENERATOR_FILE
                  basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
                  if (bb != 0)
                    fprintf (outfile, " [bb %d]", bb->index);
#endif
                  break;
                }

              case NOTE_INSN_EXPECTED_VALUE:
                indent += 2;
                if (!sawclose)
                  fprintf (outfile, " ");
                print_rtx (NOTE_EXPECTED_VALUE (in_rtx));
                indent -= 2;
                break;

              case NOTE_INSN_DELETED_LABEL:
                {
                  const char *label = NOTE_DELETED_LABEL_NAME (in_rtx);
                  if (label)
                    fprintf (outfile, " (\"%s\")", label);
                  else
                    fprintf (outfile, " \"\"");
                }
                break;

              case NOTE_INSN_SWITCH_TEXT_SECTIONS:
                {
#ifndef GENERATOR_FILE
                  basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
                  if (bb != 0)
                    fprintf (outfile, " [bb %d]", bb->index);
#endif
                  break;
                }
                
              case NOTE_INSN_VAR_LOCATION:
#ifndef GENERATOR_FILE
                fprintf (outfile, " (");
                print_mem_expr (outfile, NOTE_VAR_LOCATION_DECL (in_rtx));
                fprintf (outfile, " ");
                print_rtx (NOTE_VAR_LOCATION_LOC (in_rtx));
                fprintf (outfile, ")");
#endif
                break;

              default:
                {
                  const char * const str = X0STR (in_rtx, i);

                  if (NOTE_LINE_NUMBER (in_rtx) < 0)
                    ;
                  else if (str == 0)
                    fputs (dump_for_graph ? " \\\"\\\"" : " \"\"", outfile);
                  else
                    {
                      if (dump_for_graph)
                        fprintf (outfile, " (\\\"%s\\\")", str);
                      else
                        fprintf (outfile, " (\"%s\")", str);
                    }
                  break;
                }
              }
          }
        break;

      case 'e':
      do_e:
        indent += 2;
        if (!sawclose)
          fprintf (outfile, " ");
        print_rtx (XEXP (in_rtx, i));
        indent -= 2;
        break;

      case 'E':
      case 'V':
        indent += 2;
        if (sawclose)
          {
            fprintf (outfile, "\n%s%*s",
                     print_rtx_head, indent * 2, "");
            sawclose = 0;
          }
        fputs (" [", outfile);
        if (NULL != XVEC (in_rtx, i))
          {
            indent += 2;
            if (XVECLEN (in_rtx, i))
              sawclose = 1;

            for (j = 0; j < XVECLEN (in_rtx, i); j++)
              print_rtx (XVECEXP (in_rtx, i, j));

            indent -= 2;
          }
        if (sawclose)
          fprintf (outfile, "\n%s%*s", print_rtx_head, indent * 2, "");

        fputs ("]", outfile);
        sawclose = 1;
        indent -= 2;
        break;

      case 'w':
        if (! flag_simple)
          fprintf (outfile, " ");
        fprintf (outfile, HOST_WIDE_INT_PRINT_DEC, XWINT (in_rtx, i));
        if (! flag_simple)
          fprintf (outfile, " [" HOST_WIDE_INT_PRINT_HEX "]",
                   XWINT (in_rtx, i));
        break;

      case 'i':
        if (i == 4 && INSN_P (in_rtx))
          {
#ifndef GENERATOR_FILE
            /*  Pretty-print insn locators.  Ignore scoping as it is mostly
                redundant with line number information and do not print anything
                when there is no location information available.  */
            if (INSN_LOCATOR (in_rtx) && insn_file (in_rtx))
              fprintf(outfile, " %s:%i", insn_file (in_rtx), insn_line (in_rtx));
#endif
          }
        else if (i == 6 && NOTE_P (in_rtx))
          {
            /* This field is only used for NOTE_INSN_DELETED_LABEL, and
               other times often contains garbage from INSN->NOTE death.  */
            if (NOTE_LINE_NUMBER (in_rtx) == NOTE_INSN_DELETED_LABEL)
              fprintf (outfile, " %d",  XINT (in_rtx, i));
          }
        else
          {
            int value = XINT (in_rtx, i);
            const char *name;

#ifndef GENERATOR_FILE
            if (REG_P (in_rtx) && value < FIRST_PSEUDO_REGISTER)
              fprintf (outfile, " %d %s", REGNO (in_rtx),
                       reg_names[REGNO (in_rtx)]);
            else if (REG_P (in_rtx)
                     && value <= LAST_VIRTUAL_REGISTER)
              {
                if (value == VIRTUAL_INCOMING_ARGS_REGNUM)
                  fprintf (outfile, " %d virtual-incoming-args", value);
                else if (value == VIRTUAL_STACK_VARS_REGNUM)
                  fprintf (outfile, " %d virtual-stack-vars", value);
                else if (value == VIRTUAL_STACK_DYNAMIC_REGNUM)
                  fprintf (outfile, " %d virtual-stack-dynamic", value);
                else if (value == VIRTUAL_OUTGOING_ARGS_REGNUM)
                  fprintf (outfile, " %d virtual-outgoing-args", value);
                else if (value == VIRTUAL_CFA_REGNUM)
                  fprintf (outfile, " %d virtual-cfa", value);
                else
                  fprintf (outfile, " %d virtual-reg-%d", value,
                           value-FIRST_VIRTUAL_REGISTER);
              }
            else
#endif
              if (flag_dump_unnumbered
                     && (is_insn || NOTE_P (in_rtx)))
              fputc ('#', outfile);
            else
              fprintf (outfile, " %d", value);

#ifndef GENERATOR_FILE
            if (REG_P (in_rtx) && REG_ATTRS (in_rtx))
              {
                fputs (" [", outfile);
                if (ORIGINAL_REGNO (in_rtx) != REGNO (in_rtx))
                  fprintf (outfile, "orig:%i", ORIGINAL_REGNO (in_rtx));
                if (REG_EXPR (in_rtx))
                  print_mem_expr (outfile, REG_EXPR (in_rtx));

                if (REG_OFFSET (in_rtx))
                  fprintf (outfile, "+" HOST_WIDE_INT_PRINT_DEC,
                           REG_OFFSET (in_rtx));
                fputs (" ]", outfile);
              }
#endif

            if (is_insn && &INSN_CODE (in_rtx) == &XINT (in_rtx, i)
                && XINT (in_rtx, i) >= 0
                && (name = get_insn_name (XINT (in_rtx, i))) != NULL)
              fprintf (outfile, " {%s}", name);
            sawclose = 0;
          }
        break;

      /* Print NOTE_INSN names rather than integer codes.  */

      case 'n':
        if (XINT (in_rtx, i) >= (int) NOTE_INSN_BIAS
            && XINT (in_rtx, i) < (int) NOTE_INSN_MAX)
          fprintf (outfile, " %s", GET_NOTE_INSN_NAME (XINT (in_rtx, i)));
        else
          fprintf (outfile, " %d", XINT (in_rtx, i));
        sawclose = 0;
        break;

      case 'u':
        if (XEXP (in_rtx, i) != NULL)
          {
            rtx sub = XEXP (in_rtx, i);
            enum rtx_code subc = GET_CODE (sub);

            if (GET_CODE (in_rtx) == LABEL_REF)
              {
                if (subc == NOTE
                    && NOTE_LINE_NUMBER (sub) == NOTE_INSN_DELETED_LABEL)
                  {
                    if (flag_dump_unnumbered)
                      fprintf (outfile, " [# deleted]");
                    else
                      fprintf (outfile, " [%d deleted]", INSN_UID (sub));
                    sawclose = 0;
                    break;
                  }

                if (subc != CODE_LABEL)
                  goto do_e;
              }

            if (flag_dump_unnumbered)
              fputs (" #", outfile);
            else
              fprintf (outfile, " %d", INSN_UID (sub));
          }
        else
          fputs (" 0", outfile);
        sawclose = 0;
        break;

      case 'b':
#ifndef GENERATOR_FILE
        if (XBITMAP (in_rtx, i) == NULL)
          fputs (" {null}", outfile);
        else
          bitmap_print (outfile, XBITMAP (in_rtx, i), " {", "}");
#endif
        sawclose = 0;
        break;

      case 't':
#ifndef GENERATOR_FILE
        dump_addr (outfile, " ", XTREE (in_rtx, i));
#endif
        break;

      case '*':
        fputs (" Unknown", outfile);
        sawclose = 0;
        break;

      case 'B':
#ifndef GENERATOR_FILE
        if (XBBDEF (in_rtx, i))
          fprintf (outfile, " %i", XBBDEF (in_rtx, i)->index);
#endif
        break;

      default:
        gcc_unreachable ();
      }

  switch (GET_CODE (in_rtx))
    {
#ifndef GENERATOR_FILE
    case MEM:
      fprintf (outfile, " [" HOST_WIDE_INT_PRINT_DEC, MEM_ALIAS_SET (in_rtx));

      if (MEM_EXPR (in_rtx))
        print_mem_expr (outfile, MEM_EXPR (in_rtx));

      if (MEM_OFFSET (in_rtx))
        fprintf (outfile, "+" HOST_WIDE_INT_PRINT_DEC,
                 INTVAL (MEM_OFFSET (in_rtx)));

      if (MEM_SIZE (in_rtx))
        fprintf (outfile, " S" HOST_WIDE_INT_PRINT_DEC,
                 INTVAL (MEM_SIZE (in_rtx)));

      if (MEM_ALIGN (in_rtx) != 1)
        fprintf (outfile, " A%u", MEM_ALIGN (in_rtx));

      fputc (']', outfile);
      break;

    case CONST_DOUBLE:
      if (FLOAT_MODE_P (GET_MODE (in_rtx)))
        {
          char s[60];

          real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (in_rtx),
                           sizeof (s), 0, 1);
          fprintf (outfile, " %s", s);

          real_to_hexadecimal (s, CONST_DOUBLE_REAL_VALUE (in_rtx),
                               sizeof (s), 0, 1);
          fprintf (outfile, " [%s]", s);
        }
      break;
#endif

    case CODE_LABEL:
      fprintf (outfile, " [%d uses]", LABEL_NUSES (in_rtx));
      switch (LABEL_KIND (in_rtx))
        {
          case LABEL_NORMAL: break;
          case LABEL_STATIC_ENTRY: fputs (" [entry]", outfile); break;
          case LABEL_GLOBAL_ENTRY: fputs (" [global entry]", outfile); break;
          case LABEL_WEAK_ENTRY: fputs (" [weak entry]", outfile); break;
          default: gcc_unreachable ();
        }
      break;

    default:
      break;
    }

  if (dump_for_graph
      && (is_insn || NOTE_P (in_rtx)
          || LABEL_P (in_rtx) || BARRIER_P (in_rtx)))
    sawclose = 0;
  else
    {
      fputc (')', outfile);
      sawclose = 1;
    }
}
Exemple #10
0
static void
fixup_reorder_chain (void)
{
  basic_block bb, prev_bb;
  int index;
  rtx insn = NULL;

  if (cfg_layout_function_header)
    {
      set_first_insn (cfg_layout_function_header);
      insn = cfg_layout_function_header;
      while (NEXT_INSN (insn))
	insn = NEXT_INSN (insn);
    }

  /* First do the bulk reordering -- rechain the blocks without regard to
     the needed changes to jumps and labels.  */

  for (bb = ENTRY_BLOCK_PTR->next_bb, index = NUM_FIXED_BLOCKS;
       bb != 0;
       bb = bb->aux, index++)
    {
      if (bb->il.rtl->header)
	{
	  if (insn)
	    NEXT_INSN (insn) = bb->il.rtl->header;
	  else
	    set_first_insn (bb->il.rtl->header);
	  PREV_INSN (bb->il.rtl->header) = insn;
	  insn = bb->il.rtl->header;
	  while (NEXT_INSN (insn))
	    insn = NEXT_INSN (insn);
	}
      if (insn)
	NEXT_INSN (insn) = BB_HEAD (bb);
      else
	set_first_insn (BB_HEAD (bb));
      PREV_INSN (BB_HEAD (bb)) = insn;
      insn = BB_END (bb);
      if (bb->il.rtl->footer)
	{
	  NEXT_INSN (insn) = bb->il.rtl->footer;
	  PREV_INSN (bb->il.rtl->footer) = insn;
	  while (NEXT_INSN (insn))
	    insn = NEXT_INSN (insn);
	}
    }

  gcc_assert (index == n_basic_blocks);

  NEXT_INSN (insn) = cfg_layout_function_footer;
  if (cfg_layout_function_footer)
    PREV_INSN (cfg_layout_function_footer) = insn;

  while (NEXT_INSN (insn))
    insn = NEXT_INSN (insn);

  set_last_insn (insn);
#ifdef ENABLE_CHECKING
  verify_insn_chain ();
#endif
  delete_dead_jumptables ();

  /* Now add jumps and labels as needed to match the blocks new
     outgoing edges.  */

  for (bb = ENTRY_BLOCK_PTR->next_bb; bb ; bb = bb->aux)
    {
      edge e_fall, e_taken, e;
      rtx bb_end_insn;
      basic_block nb;
      edge_iterator ei;

      if (EDGE_COUNT (bb->succs) == 0)
	continue;

      /* Find the old fallthru edge, and another non-EH edge for
	 a taken jump.  */
      e_taken = e_fall = NULL;

      FOR_EACH_EDGE (e, ei, bb->succs)
	if (e->flags & EDGE_FALLTHRU)
	  e_fall = e;
	else if (! (e->flags & EDGE_EH))
	  e_taken = e;

      bb_end_insn = BB_END (bb);
      if (JUMP_P (bb_end_insn))
	{
	  if (any_condjump_p (bb_end_insn))
	    {
	      /* If the old fallthru is still next, nothing to do.  */
	      if (bb->aux == e_fall->dest
		  || e_fall->dest == EXIT_BLOCK_PTR)
		continue;

	      /* The degenerated case of conditional jump jumping to the next
		 instruction can happen for jumps with side effects.  We need
		 to construct a forwarder block and this will be done just
		 fine by force_nonfallthru below.  */
	      if (!e_taken)
		;

	      /* There is another special case: if *neither* block is next,
		 such as happens at the very end of a function, then we'll
		 need to add a new unconditional jump.  Choose the taken
		 edge based on known or assumed probability.  */
	      else if (bb->aux != e_taken->dest)
		{
		  rtx note = find_reg_note (bb_end_insn, REG_BR_PROB, 0);

		  if (note
		      && INTVAL (XEXP (note, 0)) < REG_BR_PROB_BASE / 2
		      && invert_jump (bb_end_insn,
				      (e_fall->dest == EXIT_BLOCK_PTR
				       ? NULL_RTX
				       : label_for_bb (e_fall->dest)), 0))
		    {
		      e_fall->flags &= ~EDGE_FALLTHRU;
#ifdef ENABLE_CHECKING
		      gcc_assert (could_fall_through
				  (e_taken->src, e_taken->dest));
#endif
		      e_taken->flags |= EDGE_FALLTHRU;
		      update_br_prob_note (bb);
		      e = e_fall, e_fall = e_taken, e_taken = e;
		    }
		}

	      /* If the "jumping" edge is a crossing edge, and the fall
		 through edge is non-crossing, leave things as they are.  */
	      else if ((e_taken->flags & EDGE_CROSSING)
		       && !(e_fall->flags & EDGE_CROSSING))
		continue;

	      /* Otherwise we can try to invert the jump.  This will
		 basically never fail, however, keep up the pretense.  */
	      else if (invert_jump (bb_end_insn,
				    (e_fall->dest == EXIT_BLOCK_PTR
				     ? NULL_RTX
				     : label_for_bb (e_fall->dest)), 0))
		{
		  e_fall->flags &= ~EDGE_FALLTHRU;
#ifdef ENABLE_CHECKING
		  gcc_assert (could_fall_through
			      (e_taken->src, e_taken->dest));
#endif
		  e_taken->flags |= EDGE_FALLTHRU;
		  update_br_prob_note (bb);
		  continue;
		}
	    }
	  else
	    {
	      /* Otherwise we have some return, switch or computed
		 jump.  In the 99% case, there should not have been a
		 fallthru edge.  */
	      gcc_assert (returnjump_p (bb_end_insn) || !e_fall);
	      continue;
	    }
	}
      else
	{
	  /* No fallthru implies a noreturn function with EH edges, or
	     something similarly bizarre.  In any case, we don't need to
	     do anything.  */
	  if (! e_fall)
	    continue;

	  /* If the fallthru block is still next, nothing to do.  */
	  if (bb->aux == e_fall->dest)
	    continue;

	  /* A fallthru to exit block.  */
	  if (e_fall->dest == EXIT_BLOCK_PTR)
	    continue;
	}

      /* We got here if we need to add a new jump insn.  */
      nb = force_nonfallthru (e_fall);
      if (nb)
	{
	  nb->il.rtl->visited = 1;
	  nb->aux = bb->aux;
	  bb->aux = nb;
	  /* Don't process this new block.  */
	  bb = nb;

	  /* Make sure new bb is tagged for correct section (same as
	     fall-thru source, since you cannot fall-throu across
	     section boundaries).  */
	  BB_COPY_PARTITION (e_fall->src, single_pred (bb));
	  if (flag_reorder_blocks_and_partition
	      && targetm.have_named_sections
	      && JUMP_P (BB_END (bb))
	      && !any_condjump_p (BB_END (bb))
	      && (EDGE_SUCC (bb, 0)->flags & EDGE_CROSSING))
	    REG_NOTES (BB_END (bb)) = gen_rtx_EXPR_LIST
	      (REG_CROSSING_JUMP, NULL_RTX, REG_NOTES (BB_END (bb)));
	}
    }

  /* Put basic_block_info in the new order.  */

  if (dump_file)
    {
      fprintf (dump_file, "Reordered sequence:\n");
      for (bb = ENTRY_BLOCK_PTR->next_bb, index = NUM_FIXED_BLOCKS;
	   bb;
	   bb = bb->aux, index++)
	{
	  fprintf (dump_file, " %i ", index);
	  if (get_bb_original (bb))
	    fprintf (dump_file, "duplicate of %i ",
		     get_bb_original (bb)->index);
	  else if (forwarder_block_p (bb)
		   && !LABEL_P (BB_HEAD (bb)))
	    fprintf (dump_file, "compensation ");
	  else
	    fprintf (dump_file, "bb %i ", bb->index);
	  fprintf (dump_file, " [%i]\n", bb->frequency);
	}
    }

  prev_bb = ENTRY_BLOCK_PTR;
  bb = ENTRY_BLOCK_PTR->next_bb;
  index = NUM_FIXED_BLOCKS;

  for (; bb; prev_bb = bb, bb = bb->aux, index ++)
    {
      bb->index = index;
      SET_BASIC_BLOCK (index, bb);

      bb->prev_bb = prev_bb;
      prev_bb->next_bb = bb;
    }
  prev_bb->next_bb = EXIT_BLOCK_PTR;
  EXIT_BLOCK_PTR->prev_bb = prev_bb;

  /* Annoying special case - jump around dead jumptables left in the code.  */
  FOR_EACH_BB (bb)
    {
      edge e;
      edge_iterator ei;

      FOR_EACH_EDGE (e, ei, bb->succs)
	if (e->flags & EDGE_FALLTHRU)
	  break;

      if (e && !can_fallthru (e->src, e->dest))
	force_nonfallthru (e);
    }
}
Exemple #11
0
static void
print_rtx (const_rtx in_rtx)
{
  int i = 0;
  int j;
  const char *format_ptr;
  int is_insn;

  if (sawclose)
    {
      if (flag_simple)
	fputc (' ', outfile);
      else
	fprintf (outfile, "\n%s%*s", print_rtx_head, indent * 2, "");
      sawclose = 0;
    }

  if (in_rtx == 0)
    {
      fputs ("(nil)", outfile);
      sawclose = 1;
      return;
    }
  else if (GET_CODE (in_rtx) > NUM_RTX_CODE)
    {
       fprintf (outfile, "(??? bad code %d\n%s%*s)", GET_CODE (in_rtx),
		print_rtx_head, indent * 2, "");
       sawclose = 1;
       return;
    }

  is_insn = INSN_P (in_rtx);

  /* Print name of expression code.  */
  if (flag_simple && CONST_INT_P (in_rtx))
    fputc ('(', outfile);
  else
    fprintf (outfile, "(%s", GET_RTX_NAME (GET_CODE (in_rtx)));

  if (! flag_simple)
    {
      if (RTX_FLAG (in_rtx, in_struct))
	fputs ("/s", outfile);

      if (RTX_FLAG (in_rtx, volatil))
	fputs ("/v", outfile);

      if (RTX_FLAG (in_rtx, unchanging))
	fputs ("/u", outfile);

      if (RTX_FLAG (in_rtx, frame_related))
	fputs ("/f", outfile);

      if (RTX_FLAG (in_rtx, jump))
	fputs ("/j", outfile);

      if (RTX_FLAG (in_rtx, call))
	fputs ("/c", outfile);

      if (RTX_FLAG (in_rtx, return_val))
	fputs ("/i", outfile);

      /* Print REG_NOTE names for EXPR_LIST and INSN_LIST.  */
      if ((GET_CODE (in_rtx) == EXPR_LIST
	   || GET_CODE (in_rtx) == INSN_LIST
	   || GET_CODE (in_rtx) == INT_LIST)
	  && (int)GET_MODE (in_rtx) < REG_NOTE_MAX)
	fprintf (outfile, ":%s",
		 GET_REG_NOTE_NAME (GET_MODE (in_rtx)));

      /* For other rtl, print the mode if it's not VOID.  */
      else if (GET_MODE (in_rtx) != VOIDmode)
	fprintf (outfile, ":%s", GET_MODE_NAME (GET_MODE (in_rtx)));

#ifndef GENERATOR_FILE
      if (GET_CODE (in_rtx) == VAR_LOCATION)
	{
	  if (TREE_CODE (PAT_VAR_LOCATION_DECL (in_rtx)) == STRING_CST)
	    fputs (" <debug string placeholder>", outfile);
	  else
	    print_mem_expr (outfile, PAT_VAR_LOCATION_DECL (in_rtx));
	  fputc (' ', outfile);
	  print_rtx (PAT_VAR_LOCATION_LOC (in_rtx));
	  if (PAT_VAR_LOCATION_STATUS (in_rtx)
	      == VAR_INIT_STATUS_UNINITIALIZED)
	    fprintf (outfile, " [uninit]");
	  sawclose = 1;
	  i = GET_RTX_LENGTH (VAR_LOCATION);
	}
#endif
    }

#ifndef GENERATOR_FILE
  if (CONST_DOUBLE_AS_FLOAT_P (in_rtx))
    i = 5;
#endif

  /* Get the format string and skip the first elements if we have handled
     them already.  */
  format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx)) + i;
  for (; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++)
    switch (*format_ptr++)
      {
	const char *str;

      case 'T':
	str = XTMPL (in_rtx, i);
	goto string;

      case 'S':
      case 's':
	str = XSTR (in_rtx, i);
      string:

	if (str == 0)
	  fputs (" \"\"", outfile);
	else
	  fprintf (outfile, " (\"%s\")", str);
	sawclose = 1;
	break;

	/* 0 indicates a field for internal use that should not be printed.
	   An exception is the third field of a NOTE, where it indicates
	   that the field has several different valid contents.  */
      case '0':
	if (i == 1 && REG_P (in_rtx))
	  {
	    if (REGNO (in_rtx) != ORIGINAL_REGNO (in_rtx))
	      fprintf (outfile, " [%d]", ORIGINAL_REGNO (in_rtx));
	  }
#ifndef GENERATOR_FILE
	else if (i == 1 && GET_CODE (in_rtx) == SYMBOL_REF)
	  {
	    int flags = SYMBOL_REF_FLAGS (in_rtx);
	    if (flags)
	      fprintf (outfile, " [flags %#x]", flags);
	  }
	else if (i == 2 && GET_CODE (in_rtx) == SYMBOL_REF)
	  {
	    tree decl = SYMBOL_REF_DECL (in_rtx);
	    if (decl)
	      print_node_brief (outfile, "", decl, dump_flags);
	  }
#endif
	else if (i == 4 && NOTE_P (in_rtx))
	  {
	    switch (NOTE_KIND (in_rtx))
	      {
	      case NOTE_INSN_EH_REGION_BEG:
	      case NOTE_INSN_EH_REGION_END:
		if (flag_dump_unnumbered)
		  fprintf (outfile, " #");
		else
		  fprintf (outfile, " %d", NOTE_EH_HANDLER (in_rtx));
		sawclose = 1;
		break;

	      case NOTE_INSN_BLOCK_BEG:
	      case NOTE_INSN_BLOCK_END:
#ifndef GENERATOR_FILE
		dump_addr (outfile, " ", NOTE_BLOCK (in_rtx));
#endif
		sawclose = 1;
		break;

	      case NOTE_INSN_BASIC_BLOCK:
		{
#ifndef GENERATOR_FILE
		  basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
		  if (bb != 0)
		    fprintf (outfile, " [bb %d]", bb->index);
#endif
		  break;
	        }

	      case NOTE_INSN_DELETED_LABEL:
	      case NOTE_INSN_DELETED_DEBUG_LABEL:
		{
		  const char *label = NOTE_DELETED_LABEL_NAME (in_rtx);
		  if (label)
		    fprintf (outfile, " (\"%s\")", label);
		  else
		    fprintf (outfile, " \"\"");
		}
		break;

	      case NOTE_INSN_SWITCH_TEXT_SECTIONS:
		{
#ifndef GENERATOR_FILE
		  basic_block bb = NOTE_BASIC_BLOCK (in_rtx);
		  if (bb != 0)
		    fprintf (outfile, " [bb %d]", bb->index);
#endif
		  break;
		}

	      case NOTE_INSN_VAR_LOCATION:
	      case NOTE_INSN_CALL_ARG_LOCATION:
#ifndef GENERATOR_FILE
		fputc (' ', outfile);
		print_rtx (NOTE_VAR_LOCATION (in_rtx));
#endif
		break;

	      case NOTE_INSN_CFI:
#ifndef GENERATOR_FILE
		fputc ('\n', outfile);
		output_cfi_directive (outfile, NOTE_CFI (in_rtx));
		fputc ('\t', outfile);
#endif
		break;

	      default:
		break;
	      }
	  }
	else if (i == 8 && JUMP_P (in_rtx) && JUMP_LABEL (in_rtx) != NULL)
	  {
	    /* Output the JUMP_LABEL reference.  */
	    fprintf (outfile, "\n%s%*s -> ", print_rtx_head, indent * 2, "");
	    if (GET_CODE (JUMP_LABEL (in_rtx)) == RETURN)
	      fprintf (outfile, "return");
	    else if (GET_CODE (JUMP_LABEL (in_rtx)) == SIMPLE_RETURN)
	      fprintf (outfile, "simple_return");
	    else
	      fprintf (outfile, "%d", INSN_UID (JUMP_LABEL (in_rtx)));
	  }
	else if (i == 0 && GET_CODE (in_rtx) == VALUE)
	  {
#ifndef GENERATOR_FILE
	    cselib_val *val = CSELIB_VAL_PTR (in_rtx);

	    fprintf (outfile, " %u:%u", val->uid, val->hash);
	    dump_addr (outfile, " @", in_rtx);
	    dump_addr (outfile, "/", (void*)val);
#endif
	  }
	else if (i == 0 && GET_CODE (in_rtx) == DEBUG_EXPR)
	  {
#ifndef GENERATOR_FILE
	    fprintf (outfile, " D#%i",
		     DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (in_rtx)));
#endif
	  }
	else if (i == 0 && GET_CODE (in_rtx) == ENTRY_VALUE)
	  {
	    indent += 2;
	    if (!sawclose)
	      fprintf (outfile, " ");
	    print_rtx (ENTRY_VALUE_EXP (in_rtx));
	    indent -= 2;
	  }
	break;

      case 'e':
      do_e:
	indent += 2;
	if (i == 7 && INSN_P (in_rtx))
	  /* Put REG_NOTES on their own line.  */
	  fprintf (outfile, "\n%s%*s",
		   print_rtx_head, indent * 2, "");
	if (!sawclose)
	  fprintf (outfile, " ");
	print_rtx (XEXP (in_rtx, i));
	indent -= 2;
	break;

      case 'E':
      case 'V':
	indent += 2;
	if (sawclose)
	  {
	    fprintf (outfile, "\n%s%*s",
		     print_rtx_head, indent * 2, "");
	    sawclose = 0;
	  }
	fputs (" [", outfile);
	if (NULL != XVEC (in_rtx, i))
	  {
	    indent += 2;
	    if (XVECLEN (in_rtx, i))
	      sawclose = 1;

	    for (j = 0; j < XVECLEN (in_rtx, i); j++)
	      print_rtx (XVECEXP (in_rtx, i, j));

	    indent -= 2;
	  }
	if (sawclose)
	  fprintf (outfile, "\n%s%*s", print_rtx_head, indent * 2, "");

	fputs ("]", outfile);
	sawclose = 1;
	indent -= 2;
	break;

      case 'w':
	if (! flag_simple)
	  fprintf (outfile, " ");
	fprintf (outfile, HOST_WIDE_INT_PRINT_DEC, XWINT (in_rtx, i));
	if (! flag_simple)
	  fprintf (outfile, " [" HOST_WIDE_INT_PRINT_HEX "]",
		   (unsigned HOST_WIDE_INT) XWINT (in_rtx, i));
	break;

      case 'i':
	if (i == 5 && INSN_P (in_rtx))
	  {
#ifndef GENERATOR_FILE
	    /*  Pretty-print insn locations.  Ignore scoping as it is mostly
		redundant with line number information and do not print anything
		when there is no location information available.  */
	    if (INSN_LOCATION (in_rtx) && insn_file (in_rtx))
	      fprintf(outfile, " %s:%i", insn_file (in_rtx), insn_line (in_rtx));
#endif
	  }
	else if (i == 6 && GET_CODE (in_rtx) == ASM_OPERANDS)
	  {
#ifndef GENERATOR_FILE
	    fprintf (outfile, " %s:%i",
		     LOCATION_FILE (ASM_OPERANDS_SOURCE_LOCATION (in_rtx)),
		     LOCATION_LINE (ASM_OPERANDS_SOURCE_LOCATION (in_rtx)));
#endif
	  }
	else if (i == 1 && GET_CODE (in_rtx) == ASM_INPUT)
	  {
#ifndef GENERATOR_FILE
	    fprintf (outfile, " %s:%i",
		     LOCATION_FILE (ASM_INPUT_SOURCE_LOCATION (in_rtx)),
		     LOCATION_LINE (ASM_INPUT_SOURCE_LOCATION (in_rtx)));
#endif
	  }
	else if (i == 6 && NOTE_P (in_rtx))
	  {
	    /* This field is only used for NOTE_INSN_DELETED_LABEL, and
	       other times often contains garbage from INSN->NOTE death.  */
	    if (NOTE_KIND (in_rtx) == NOTE_INSN_DELETED_LABEL
		|| NOTE_KIND (in_rtx) == NOTE_INSN_DELETED_DEBUG_LABEL)
	      fprintf (outfile, " %d",  XINT (in_rtx, i));
	  }
#if !defined(GENERATOR_FILE) && NUM_UNSPECV_VALUES > 0
	else if (i == 1
		 && GET_CODE (in_rtx) == UNSPEC_VOLATILE
		 && XINT (in_rtx, 1) >= 0
		 && XINT (in_rtx, 1) < NUM_UNSPECV_VALUES)
	  fprintf (outfile, " %s", unspecv_strings[XINT (in_rtx, 1)]);
#endif
#if !defined(GENERATOR_FILE) && NUM_UNSPEC_VALUES > 0
	else if (i == 1
		 && (GET_CODE (in_rtx) == UNSPEC
		     || GET_CODE (in_rtx) == UNSPEC_VOLATILE)
		 && XINT (in_rtx, 1) >= 0
		 && XINT (in_rtx, 1) < NUM_UNSPEC_VALUES)
	  fprintf (outfile, " %s", unspec_strings[XINT (in_rtx, 1)]);
#endif
	else
	  {
	    int value = XINT (in_rtx, i);
	    const char *name;

#ifndef GENERATOR_FILE
	    if (REG_P (in_rtx) && (unsigned) value < FIRST_PSEUDO_REGISTER)
	      fprintf (outfile, " %d %s", value, reg_names[value]);
	    else if (REG_P (in_rtx)
		     && (unsigned) value <= LAST_VIRTUAL_REGISTER)
	      {
		if (value == VIRTUAL_INCOMING_ARGS_REGNUM)
		  fprintf (outfile, " %d virtual-incoming-args", value);
		else if (value == VIRTUAL_STACK_VARS_REGNUM)
		  fprintf (outfile, " %d virtual-stack-vars", value);
		else if (value == VIRTUAL_STACK_DYNAMIC_REGNUM)
		  fprintf (outfile, " %d virtual-stack-dynamic", value);
		else if (value == VIRTUAL_OUTGOING_ARGS_REGNUM)
		  fprintf (outfile, " %d virtual-outgoing-args", value);
		else if (value == VIRTUAL_CFA_REGNUM)
		  fprintf (outfile, " %d virtual-cfa", value);
		else if (value == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM)
		  fprintf (outfile, " %d virtual-preferred-stack-boundary",
			   value);
		else
		  fprintf (outfile, " %d virtual-reg-%d", value,
			   value-FIRST_VIRTUAL_REGISTER);
	      }
	    else
#endif
	      if (flag_dump_unnumbered
		     && (is_insn || NOTE_P (in_rtx)))
	      fputc ('#', outfile);
	    else
	      fprintf (outfile, " %d", value);

#ifndef GENERATOR_FILE
	    if (REG_P (in_rtx) && REG_ATTRS (in_rtx))
	      {
		fputs (" [", outfile);
		if (ORIGINAL_REGNO (in_rtx) != REGNO (in_rtx))
		  fprintf (outfile, "orig:%i", ORIGINAL_REGNO (in_rtx));
		if (REG_EXPR (in_rtx))
		  print_mem_expr (outfile, REG_EXPR (in_rtx));

		if (REG_OFFSET (in_rtx))
		  fprintf (outfile, "+" HOST_WIDE_INT_PRINT_DEC,
			   REG_OFFSET (in_rtx));
		fputs (" ]", outfile);
	      }
#endif

	    if (is_insn && &INSN_CODE (in_rtx) == &XINT (in_rtx, i)
		&& XINT (in_rtx, i) >= 0
		&& (name = get_insn_name (XINT (in_rtx, i))) != NULL)
	      fprintf (outfile, " {%s}", name);
	    sawclose = 0;
	  }
	break;

      /* Print NOTE_INSN names rather than integer codes.  */

      case 'n':
	fprintf (outfile, " %s", GET_NOTE_INSN_NAME (XINT (in_rtx, i)));
	sawclose = 0;
	break;

      case 'u':
	if (XEXP (in_rtx, i) != NULL)
	  {
	    rtx sub = XEXP (in_rtx, i);
	    enum rtx_code subc = GET_CODE (sub);

	    if (GET_CODE (in_rtx) == LABEL_REF)
	      {
		if (subc == NOTE
		    && NOTE_KIND (sub) == NOTE_INSN_DELETED_LABEL)
		  {
		    if (flag_dump_unnumbered)
		      fprintf (outfile, " [# deleted]");
		    else
		      fprintf (outfile, " [%d deleted]", INSN_UID (sub));
		    sawclose = 0;
		    break;
		  }

		if (subc != CODE_LABEL)
		  goto do_e;
	      }

	    if (flag_dump_unnumbered
		|| (flag_dump_unnumbered_links && (i == 1 || i == 2)
		    && (INSN_P (in_rtx) || NOTE_P (in_rtx)
			|| LABEL_P (in_rtx) || BARRIER_P (in_rtx))))
	      fputs (" #", outfile);
	    else
	      fprintf (outfile, " %d", INSN_UID (sub));
	  }
	else
	  fputs (" 0", outfile);
	sawclose = 0;
	break;

      case 't':
#ifndef GENERATOR_FILE
	if (i == 0 && GET_CODE (in_rtx) == DEBUG_IMPLICIT_PTR)
	  print_mem_expr (outfile, DEBUG_IMPLICIT_PTR_DECL (in_rtx));
	else if (i == 0 && GET_CODE (in_rtx) == DEBUG_PARAMETER_REF)
	  print_mem_expr (outfile, DEBUG_PARAMETER_REF_DECL (in_rtx));
	else
	  dump_addr (outfile, " ", XTREE (in_rtx, i));
#endif
	break;

      case '*':
	fputs (" Unknown", outfile);
	sawclose = 0;
	break;

      case 'B':
#ifndef GENERATOR_FILE
	if (XBBDEF (in_rtx, i))
	  fprintf (outfile, " %i", XBBDEF (in_rtx, i)->index);
#endif
	break;

      default:
	gcc_unreachable ();
      }

  switch (GET_CODE (in_rtx))
    {
#ifndef GENERATOR_FILE
    case MEM:
      if (__builtin_expect (final_insns_dump_p, false))
	fprintf (outfile, " [");
      else
	fprintf (outfile, " [" HOST_WIDE_INT_PRINT_DEC,
		 (HOST_WIDE_INT) MEM_ALIAS_SET (in_rtx));

      if (MEM_EXPR (in_rtx))
	print_mem_expr (outfile, MEM_EXPR (in_rtx));

      if (MEM_OFFSET_KNOWN_P (in_rtx))
	fprintf (outfile, "+" HOST_WIDE_INT_PRINT_DEC, MEM_OFFSET (in_rtx));

      if (MEM_SIZE_KNOWN_P (in_rtx))
	fprintf (outfile, " S" HOST_WIDE_INT_PRINT_DEC, MEM_SIZE (in_rtx));

      if (MEM_ALIGN (in_rtx) != 1)
	fprintf (outfile, " A%u", MEM_ALIGN (in_rtx));

      if (!ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (in_rtx)))
	fprintf (outfile, " AS%u", MEM_ADDR_SPACE (in_rtx));

      fputc (']', outfile);
      break;

    case CONST_DOUBLE:
      if (FLOAT_MODE_P (GET_MODE (in_rtx)))
	{
	  char s[60];

	  real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (in_rtx),
			   sizeof (s), 0, 1);
	  fprintf (outfile, " %s", s);

	  real_to_hexadecimal (s, CONST_DOUBLE_REAL_VALUE (in_rtx),
			       sizeof (s), 0, 1);
	  fprintf (outfile, " [%s]", s);
	}
      break;
#endif

    case CODE_LABEL:
      fprintf (outfile, " [%d uses]", LABEL_NUSES (in_rtx));
      switch (LABEL_KIND (in_rtx))
	{
	  case LABEL_NORMAL: break;
	  case LABEL_STATIC_ENTRY: fputs (" [entry]", outfile); break;
	  case LABEL_GLOBAL_ENTRY: fputs (" [global entry]", outfile); break;
	  case LABEL_WEAK_ENTRY: fputs (" [weak entry]", outfile); break;
	  default: gcc_unreachable ();
	}
      break;

    default:
      break;
    }

  fputc (')', outfile);
  sawclose = 1;
}
Exemple #12
0
static int
reload_cse_simplify_operands (rtx insn, rtx testreg)
{
  int i, j;

  /* For each operand, all registers that are equivalent to it.  */
  HARD_REG_SET equiv_regs[MAX_RECOG_OPERANDS];

  const char *constraints[MAX_RECOG_OPERANDS];

  /* Vector recording how bad an alternative is.  */
  int *alternative_reject;
  /* Vector recording how many registers can be introduced by choosing
     this alternative.  */
  int *alternative_nregs;
  /* Array of vectors recording, for each operand and each alternative,
     which hard register to substitute, or -1 if the operand should be
     left as it is.  */
  int *op_alt_regno[MAX_RECOG_OPERANDS];
  /* Array of alternatives, sorted in order of decreasing desirability.  */
  int *alternative_order;

  extract_insn (insn);

  if (recog_data.n_alternatives == 0 || recog_data.n_operands == 0)
    return 0;

  /* Figure out which alternative currently matches.  */
  if (! constrain_operands (1))
    fatal_insn_not_found (insn);

  alternative_reject = XALLOCAVEC (int, recog_data.n_alternatives);
  alternative_nregs = XALLOCAVEC (int, recog_data.n_alternatives);
  alternative_order = XALLOCAVEC (int, recog_data.n_alternatives);
  memset (alternative_reject, 0, recog_data.n_alternatives * sizeof (int));
  memset (alternative_nregs, 0, recog_data.n_alternatives * sizeof (int));

  /* For each operand, find out which regs are equivalent.  */
  for (i = 0; i < recog_data.n_operands; i++)
    {
      cselib_val *v;
      struct elt_loc_list *l;
      rtx op;
      enum machine_mode mode;

      CLEAR_HARD_REG_SET (equiv_regs[i]);

      /* cselib blows up on CODE_LABELs.  Trying to fix that doesn't seem
	 right, so avoid the problem here.  Likewise if we have a constant
         and the insn pattern doesn't tell us the mode we need.  */
      if (LABEL_P (recog_data.operand[i])
	  || (CONSTANT_P (recog_data.operand[i])
	      && recog_data.operand_mode[i] == VOIDmode))
	continue;

      op = recog_data.operand[i];
      mode = GET_MODE (op);
#ifdef LOAD_EXTEND_OP
      if (MEM_P (op)
	  && GET_MODE_BITSIZE (mode) < BITS_PER_WORD
	  && LOAD_EXTEND_OP (mode) != UNKNOWN)
	{
	  rtx set = single_set (insn);

	  /* We might have multiple sets, some of which do implicit
	     extension.  Punt on this for now.  */
	  if (! set)
	    continue;
	  /* If the destination is also a MEM or a STRICT_LOW_PART, no
	     extension applies.
	     Also, if there is an explicit extension, we don't have to
	     worry about an implicit one.  */
	  else if (MEM_P (SET_DEST (set))
		   || GET_CODE (SET_DEST (set)) == STRICT_LOW_PART
		   || GET_CODE (SET_SRC (set)) == ZERO_EXTEND
		   || GET_CODE (SET_SRC (set)) == SIGN_EXTEND)
	    ; /* Continue ordinary processing.  */
#ifdef CANNOT_CHANGE_MODE_CLASS
	  /* If the register cannot change mode to word_mode, it follows that
	     it cannot have been used in word_mode.  */
	  else if (REG_P (SET_DEST (set))
		   && CANNOT_CHANGE_MODE_CLASS (GET_MODE (SET_DEST (set)),
						word_mode,
						REGNO_REG_CLASS (REGNO (SET_DEST (set)))))
	    ; /* Continue ordinary processing.  */
#endif
	  /* If this is a straight load, make the extension explicit.  */
	  else if (REG_P (SET_DEST (set))
		   && recog_data.n_operands == 2
		   && SET_SRC (set) == op
		   && SET_DEST (set) == recog_data.operand[1-i])
	    {
	      validate_change (insn, recog_data.operand_loc[i],
			       gen_rtx_fmt_e (LOAD_EXTEND_OP (mode),
					      word_mode, op),
			       1);
	      validate_change (insn, recog_data.operand_loc[1-i],
			       gen_rtx_REG (word_mode, REGNO (SET_DEST (set))),
			       1);
	      if (! apply_change_group ())
		return 0;
	      return reload_cse_simplify_operands (insn, testreg);
	    }
	  else
	    /* ??? There might be arithmetic operations with memory that are
	       safe to optimize, but is it worth the trouble?  */
	    continue;
	}
#endif /* LOAD_EXTEND_OP */
      v = cselib_lookup (op, recog_data.operand_mode[i], 0);
      if (! v)
	continue;

      for (l = v->locs; l; l = l->next)
	if (REG_P (l->loc))
	  SET_HARD_REG_BIT (equiv_regs[i], REGNO (l->loc));
    }

  for (i = 0; i < recog_data.n_operands; i++)
    {
      enum machine_mode mode;
      int regno;
      const char *p;

      op_alt_regno[i] = XALLOCAVEC (int, recog_data.n_alternatives);
      for (j = 0; j < recog_data.n_alternatives; j++)
	op_alt_regno[i][j] = -1;

      p = constraints[i] = recog_data.constraints[i];
      mode = recog_data.operand_mode[i];

      /* Add the reject values for each alternative given by the constraints
	 for this operand.  */
      j = 0;
      while (*p != '\0')
	{
	  char c = *p++;
	  if (c == ',')
	    j++;
	  else if (c == '?')
	    alternative_reject[j] += 3;
	  else if (c == '!')
	    alternative_reject[j] += 300;
	}

      /* We won't change operands which are already registers.  We
	 also don't want to modify output operands.  */
      regno = true_regnum (recog_data.operand[i]);
      if (regno >= 0
	  || constraints[i][0] == '='
	  || constraints[i][0] == '+')
	continue;

      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
	{
	  int rclass = (int) NO_REGS;

	  if (! TEST_HARD_REG_BIT (equiv_regs[i], regno))
	    continue;

	  SET_REGNO (testreg, regno);
	  PUT_MODE (testreg, mode);

	  /* We found a register equal to this operand.  Now look for all
	     alternatives that can accept this register and have not been
	     assigned a register they can use yet.  */
	  j = 0;
	  p = constraints[i];
	  for (;;)
	    {
	      char c = *p;

	      switch (c)
		{
		case '=':  case '+':  case '?':
		case '#':  case '&':  case '!':
		case '*':  case '%':
		case '0':  case '1':  case '2':  case '3':  case '4':
		case '5':  case '6':  case '7':  case '8':  case '9':
		case '<':  case '>':  case 'V':  case 'o':
		case 'E':  case 'F':  case 'G':  case 'H':
		case 's':  case 'i':  case 'n':
		case 'I':  case 'J':  case 'K':  case 'L':
		case 'M':  case 'N':  case 'O':  case 'P':
		case 'p':  case 'X':  case TARGET_MEM_CONSTRAINT:
		  /* These don't say anything we care about.  */
		  break;

		case 'g': case 'r':
		  rclass = reg_class_subunion[(int) rclass][(int) GENERAL_REGS];
		  break;

		default:
		  rclass
		    = (reg_class_subunion
		       [(int) rclass]
		       [(int) REG_CLASS_FROM_CONSTRAINT ((unsigned char) c, p)]);
		  break;

		case ',': case '\0':
		  /* See if REGNO fits this alternative, and set it up as the
		     replacement register if we don't have one for this
		     alternative yet and the operand being replaced is not
		     a cheap CONST_INT.  */
		  if (op_alt_regno[i][j] == -1
		      && reg_fits_class_p (testreg, rclass, 0, mode)
		      && (GET_CODE (recog_data.operand[i]) != CONST_INT
			  || (rtx_cost (recog_data.operand[i], SET,
			  		optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)))
			      > rtx_cost (testreg, SET,
			  		optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn))))))
		    {
		      alternative_nregs[j]++;
		      op_alt_regno[i][j] = regno;
		    }
		  j++;
		  rclass = (int) NO_REGS;
		  break;
		}
	      p += CONSTRAINT_LEN (c, p);

	      if (c == '\0')
		break;
	    }
	}
    }

  /* Record all alternatives which are better or equal to the currently
     matching one in the alternative_order array.  */
  for (i = j = 0; i < recog_data.n_alternatives; i++)
    if (alternative_reject[i] <= alternative_reject[which_alternative])
      alternative_order[j++] = i;
  recog_data.n_alternatives = j;

  /* Sort it.  Given a small number of alternatives, a dumb algorithm
     won't hurt too much.  */
  for (i = 0; i < recog_data.n_alternatives - 1; i++)
    {
      int best = i;
      int best_reject = alternative_reject[alternative_order[i]];
      int best_nregs = alternative_nregs[alternative_order[i]];
      int tmp;

      for (j = i + 1; j < recog_data.n_alternatives; j++)
	{
	  int this_reject = alternative_reject[alternative_order[j]];
	  int this_nregs = alternative_nregs[alternative_order[j]];

	  if (this_reject < best_reject
	      || (this_reject == best_reject && this_nregs > best_nregs))
	    {
	      best = j;
	      best_reject = this_reject;
	      best_nregs = this_nregs;
	    }
	}

      tmp = alternative_order[best];
      alternative_order[best] = alternative_order[i];
      alternative_order[i] = tmp;
    }

  /* Substitute the operands as determined by op_alt_regno for the best
     alternative.  */
  j = alternative_order[0];

  for (i = 0; i < recog_data.n_operands; i++)
    {
      enum machine_mode mode = recog_data.operand_mode[i];
      if (op_alt_regno[i][j] == -1)
	continue;

      validate_change (insn, recog_data.operand_loc[i],
		       gen_rtx_REG (mode, op_alt_regno[i][j]), 1);
    }

  for (i = recog_data.n_dups - 1; i >= 0; i--)
    {
      int op = recog_data.dup_num[i];
      enum machine_mode mode = recog_data.operand_mode[op];

      if (op_alt_regno[op][j] == -1)
	continue;

      validate_change (insn, recog_data.dup_loc[i],
		       gen_rtx_REG (mode, op_alt_regno[op][j]), 1);
    }

  return apply_change_group ();
}