参照元

説明

引数

返り値

参考

実装

/* A subroutine of instantiate_virtual_regs.  Instantiate any virtual
   registers present inside of insn.  The result will be a valid insn.  */

static void
instantiate_virtual_regs_in_insn (rtx_insn *insn)
{
  poly_int64 offset;
  int insn_code, i;
  bool any_change = false;
  rtx set, new_rtx, x;
  rtx_insn *seq;
  /* There are some special cases to be handled first.  */
  set = single_set (insn);
  if (set)
    {
      /* We're allowed to assign to a virtual register.  This is interpreted
	 to mean that the underlying register gets assigned the inverse
	 transformation.  This is used, for example, in the handling of
	 non-local gotos.  */
      new_rtx = instantiate_new_reg (SET_DEST (set), &offset);
      if (new_rtx)
	{
	  start_sequence ();
	  instantiate_virtual_regs_in_rtx (&SET_SRC (set));
	  x = simplify_gen_binary (PLUS, GET_MODE (new_rtx), SET_SRC (set),
				   gen_int_mode (-offset, GET_MODE (new_rtx)));
	  x = force_operand (x, new_rtx);
	  if (x != new_rtx)
	    emit_move_insn (new_rtx, x);

	  seq = get_insns ();
	  end_sequence ();
	  emit_insn_before (seq, insn);
	  delete_insn (insn);
	  return;
	}
      /* Handle a straight copy from a virtual register by generating a
	 new add insn.  The difference between this and falling through
	 to the generic case is avoiding a new pseudo and eliminating a
	 move insn in the initial rtl stream.  */
      new_rtx = instantiate_new_reg (SET_SRC (set), &offset);
      if (new_rtx
	  && maybe_ne (offset, 0)
	  && REG_P (SET_DEST (set))
	  && REGNO (SET_DEST (set)) > LAST_VIRTUAL_REGISTER)
	{
	  start_sequence ();

	  x = expand_simple_binop (GET_MODE (SET_DEST (set)), PLUS, new_rtx,
				   gen_int_mode (offset,
						 GET_MODE (SET_DEST (set))),
				   SET_DEST (set), 1, OPTAB_LIB_WIDEN);
	  if (x != SET_DEST (set))
	    emit_move_insn (SET_DEST (set), x);
	  seq = get_insns ();
	  end_sequence ();
	  emit_insn_before (seq, insn);
	  delete_insn (insn);
	  return;
	}
      extract_insn (insn);
      insn_code = INSN_CODE (insn);
      /* Handle a plus involving a virtual register by determining if the
	 operands remain valid if they're modified in place.  */
      poly_int64 delta;
      if (GET_CODE (SET_SRC (set)) == PLUS
	  && recog_data.n_operands >= 3
	  && recog_data.operand_loc[1] == &XEXP (SET_SRC (set), 0)
	  && recog_data.operand_loc[2] == &XEXP (SET_SRC (set), 1)
	  && poly_int_rtx_p (recog_data.operand[2], &delta)
	  && (new_rtx = instantiate_new_reg (recog_data.operand[1], &offset)))
	{
	  offset += delta;

	  /* If the sum is zero, then replace with a plain move.  */
	  if (known_eq (offset, 0)
	      && REG_P (SET_DEST (set))
	      && REGNO (SET_DEST (set)) > LAST_VIRTUAL_REGISTER)
	    {
	      start_sequence ();
	      emit_move_insn (SET_DEST (set), new_rtx);
	      seq = get_insns ();
	      end_sequence ();
	      emit_insn_before (seq, insn);
	      delete_insn (insn);
	      return;
	    }

	  x = gen_int_mode (offset, recog_data.operand_mode[2]);

	  /* Using validate_change and apply_change_group here leaves
	     recog_data in an invalid state.  Since we know exactly what
	     we want to check, do those two by hand.  */
	  if (safe_insn_predicate (insn_code, 1, new_rtx)
	      && safe_insn_predicate (insn_code, 2, x))
	    {
	      *recog_data.operand_loc[1] = recog_data.operand[1] = new_rtx;
	      *recog_data.operand_loc[2] = recog_data.operand[2] = x;
	      any_change = true;

	      /* Fall through into the regular operand fixup loop in
		 order to take care of operands other than 1 and 2.  */
	    }
	}
    }
  else
    {
      extract_insn (insn);
      insn_code = INSN_CODE (insn);
    }
  /* In the general case, we expect virtual registers to appear only in
     operands, and then only as either bare registers or inside memories.  */
  for (i = 0; i < recog_data.n_operands; ++i)
    {
      x = recog_data.operand[i];
      switch (GET_CODE (x))
	{
	case MEM:
	  {
	    rtx addr = XEXP (x, 0);

	    if (!instantiate_virtual_regs_in_rtx (&addr))
	      continue;

	    start_sequence ();
	    x = replace_equiv_address (x, addr, true);
	    /* It may happen that the address with the virtual reg
	       was valid (e.g. based on the virtual stack reg, which might
	       be acceptable to the predicates with all offsets), whereas
	       the address now isn't anymore, for instance when the address
	       is still offsetted, but the base reg isn't virtual-stack-reg
	       anymore.  Below we would do a force_reg on the whole operand,
	       but this insn might actually only accept memory.  Hence,
	       before doing that last resort, try to reload the address into
	       a register, so this operand stays a MEM.  */
	    if (!safe_insn_predicate (insn_code, i, x))
	      {
		addr = force_reg (GET_MODE (addr), addr);
		x = replace_equiv_address (x, addr, true);
	      }
	    seq = get_insns ();
	    end_sequence ();
	    if (seq)
	      emit_insn_before (seq, insn);
	  }
	  break;

	case REG:
	  new_rtx = instantiate_new_reg (x, &offset);
	  if (new_rtx == NULL)
	    continue;
	  if (known_eq (offset, 0))
	    x = new_rtx;
	  else
	    {
	      start_sequence ();

	      /* Careful, special mode predicates may have stuff in
		 insn_data[insn_code].operand[i].mode that isn't useful
		 to us for computing a new value.  */
	      /* ??? Recognize address_operand and/or "p" constraints
		 to see if (plus new offset) is a valid before we put
		 this through expand_simple_binop.  */
	      x = expand_simple_binop (GET_MODE (x), PLUS, new_rtx,
				       gen_int_mode (offset, GET_MODE (x)),
				       NULL_RTX, 1, OPTAB_LIB_WIDEN);
	      seq = get_insns ();
	      end_sequence ();
	      emit_insn_before (seq, insn);
	    }
	  break;

	case SUBREG:
	  new_rtx = instantiate_new_reg (SUBREG_REG (x), &offset);
	  if (new_rtx == NULL)
	    continue;
	  if (maybe_ne (offset, 0))
	    {
	      start_sequence ();
	      new_rtx = expand_simple_binop
		(GET_MODE (new_rtx), PLUS, new_rtx,
		 gen_int_mode (offset, GET_MODE (new_rtx)),
		 NULL_RTX, 1, OPTAB_LIB_WIDEN);
	      seq = get_insns ();
	      end_sequence ();
	      emit_insn_before (seq, insn);
	    }
	  x = simplify_gen_subreg (recog_data.operand_mode[i], new_rtx,
				   GET_MODE (new_rtx), SUBREG_BYTE (x));
	  gcc_assert (x);
	  break;

	default:
	  continue;
	}

      /* At this point, X contains the new value for the operand.
	 Validate the new value vs the insn predicate.  Note that
	 asm insns will have insn_code -1 here.  */
      if (!safe_insn_predicate (insn_code, i, x))
	{
	  start_sequence ();
	  if (REG_P (x))
	    {
	      gcc_assert (REGNO (x) <= LAST_VIRTUAL_REGISTER);
	      x = copy_to_reg (x);
	    }
	  else
	    x = force_reg (insn_data[insn_code].operand[i].mode, x);
	  seq = get_insns ();
	  end_sequence ();
	  if (seq)
	    emit_insn_before (seq, insn);
	}

      *recog_data.operand_loc[i] = recog_data.operand[i] = x;
      any_change = true;
    }

  if (any_change)
    {
      /* Propagate operand changes into the duplicates.  */
      for (i = 0; i < recog_data.n_dups; ++i)
	*recog_data.dup_loc[i]
	  = copy_rtx (recog_data.operand[(unsigned)recog_data.dup_num[i]]);

      /* Force re-recognition of the instruction for validation.  */
      INSN_CODE (insn) = -1;
    }

  if (asm_noperands (PATTERN (insn)) >= 0)
    {
      if (!check_asm_operands (PATTERN (insn)))
	{
	  error_for_asm (insn, "impossible constraint in %<asm%>");
	  /* For asm goto, instead of fixing up all the edges
	     just clear the template and clear input operands
	     (asm goto doesn't have any output operands).  */
	  if (JUMP_P (insn))
	    {
	      rtx asm_op = extract_asm_operands (PATTERN (insn));
	      ASM_OPERANDS_TEMPLATE (asm_op) = ggc_strdup ("");
	      ASM_OPERANDS_INPUT_VEC (asm_op) = rtvec_alloc (0);
	      ASM_OPERANDS_INPUT_CONSTRAINT_VEC (asm_op) = rtvec_alloc (0);
	    }
	  else
	    delete_insn (insn);
	}
    }
  else
    {
      if (recog_memoized (insn) < 0)
	fatal_insn_not_found (insn);
    }
}

コメント


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS