This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
virtual register instantiation, final
- From: Richard Henderson <rth at twiddle dot net>
- To: gcc-patches at gcc dot gnu dot org
- Date: Fri, 29 Apr 2005 20:25:07 -0700
- Subject: virtual register instantiation, final
- Reply-to: rth at redhat dot com
The change from the previously posted patch is which mode gets checked
when replacing a register. Powerpc showed up an "interesting" failure
mode when using address_operand: it uses V4SImode in that case. Valid,
but not something I'd considered. I put a note about possible improvements
that could be made here.
Tested on i686, amd64, ia64, alphaev56, ppc, ppc64 linux by myself; some
mips sim variant by echristo; sparc64 linux by davem.
Committed.
r~
* function.c (instantiate_decls): Remove valid_only argument.
(instantiate_decls_1, instantiate_decl): Likewise.
(instantiate_virtual_regs_1): Delete.
(instantiate_virtual_regs_lossage): Delete.
(instantiate_virtual_regs_in_rtx): New.
(safe_insn_predicate): New.
(instantiate_virtual_regs_in_insn): New.
(instantiate_virtual_regs): Update to match all that. Only run
instantiate_decls once.
Index: function.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/function.c,v
retrieving revision 1.615
diff -u -p -c -r1.615 function.c
*** function.c 29 Apr 2005 14:09:36 -0000 1.615
--- function.c 30 Apr 2005 03:13:57 -0000
*************** struct temp_slot GTY(())
*** 189,199 ****
static rtx assign_stack_local_1 (enum machine_mode, HOST_WIDE_INT, int,
struct function *);
static struct temp_slot *find_temp_slot_from_address (rtx);
- static void instantiate_decls (tree, int);
- static void instantiate_decls_1 (tree, int);
- static void instantiate_decl (rtx, HOST_WIDE_INT, int);
- static rtx instantiate_new_reg (rtx, HOST_WIDE_INT *);
- static int instantiate_virtual_regs_1 (rtx *, rtx, int);
static void pad_to_arg_alignment (struct args_size *, int, struct args_size *);
static void pad_below (struct args_size *, enum machine_mode, tree);
static void reorder_blocks_1 (rtx, tree, varray_type *);
--- 189,194 ----
*************** static rtx keep_stack_depressed (rtx);
*** 214,220 ****
static void prepare_function_start (tree);
static void do_clobber_return_reg (rtx, void *);
static void do_use_return_reg (rtx, void *);
- static void instantiate_virtual_regs_lossage (rtx);
static void set_insn_locators (rtx, int) ATTRIBUTE_UNUSED;
/* Pointer to chain of `struct function' for containing functions. */
--- 209,214 ----
*************** static int cfa_offset;
*** 1217,1824 ****
#endif
! /* Pass through the INSNS of function FNDECL and convert virtual register
! references to hard register references. */
! void
! instantiate_virtual_regs (void)
{
! rtx insn;
!
! /* Compute the offsets to use for this function. */
! in_arg_offset = FIRST_PARM_OFFSET (current_function_decl);
! var_offset = STARTING_FRAME_OFFSET;
! dynamic_offset = STACK_DYNAMIC_OFFSET (current_function_decl);
! out_arg_offset = STACK_POINTER_OFFSET;
! cfa_offset = ARG_POINTER_CFA_OFFSET (current_function_decl);
!
! /* Scan all variables and parameters of this function. For each that is
! in memory, instantiate all virtual registers if the result is a valid
! address. If not, we do it later. That will handle most uses of virtual
! regs on many machines. */
! instantiate_decls (current_function_decl, 1);
!
! /* Initialize recognition, indicating that volatile is OK. */
! init_recog ();
!
! /* Scan through all the insns, instantiating every virtual register still
! present. */
! for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
! if (INSN_P (insn))
! {
! instantiate_virtual_regs_1 (&PATTERN (insn), insn, 1);
! if (INSN_DELETED_P (insn))
! continue;
! instantiate_virtual_regs_1 (®_NOTES (insn), NULL_RTX, 0);
! /* Instantiate any virtual registers in CALL_INSN_FUNCTION_USAGE. */
! if (CALL_P (insn))
! instantiate_virtual_regs_1 (&CALL_INSN_FUNCTION_USAGE (insn),
! NULL_RTX, 0);
!
! /* Past this point all ASM statements should match. Verify that
! to avoid failures later in the compilation process. */
! if (asm_noperands (PATTERN (insn)) >= 0
! && ! check_asm_operands (PATTERN (insn)))
! instantiate_virtual_regs_lossage (insn);
! }
! /* Now instantiate the remaining register equivalences for debugging info.
! These will not be valid addresses. */
! instantiate_decls (current_function_decl, 0);
! /* Indicate that, from now on, assign_stack_local should use
! frame_pointer_rtx. */
! virtuals_instantiated = 1;
}
! /* Scan all decls in FNDECL (both variables and parameters) and instantiate
! all virtual registers in their DECL_RTL's.
! If VALID_ONLY, do this only if the resulting address is still valid.
! Otherwise, always do it. */
!
! static void
! instantiate_decls (tree fndecl, int valid_only)
{
! tree decl;
! /* Process all parameters of the function. */
! for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
{
! HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl));
! HOST_WIDE_INT size_rtl;
! instantiate_decl (DECL_RTL (decl), size, valid_only);
! /* If the parameter was promoted, then the incoming RTL mode may be
! larger than the declared type size. We must use the larger of
! the two sizes. */
! size_rtl = GET_MODE_SIZE (GET_MODE (DECL_INCOMING_RTL (decl)));
! size = MAX (size_rtl, size);
! instantiate_decl (DECL_INCOMING_RTL (decl), size, valid_only);
}
! /* Now process all variables defined in the function or its subblocks. */
! instantiate_decls_1 (DECL_INITIAL (fndecl), valid_only);
}
! /* Subroutine of instantiate_decls: Process all decls in the given
! BLOCK node and all its subblocks. */
! static void
! instantiate_decls_1 (tree let, int valid_only)
{
! tree t;
! for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
! if (DECL_RTL_SET_P (t))
! instantiate_decl (DECL_RTL (t),
! int_size_in_bytes (TREE_TYPE (t)),
! valid_only);
! /* Process all subblocks. */
! for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t))
! instantiate_decls_1 (t, valid_only);
! }
! /* Subroutine of the preceding procedures: Given RTL representing a
! decl and the size of the object, do any instantiation required.
! If VALID_ONLY is nonzero, it means that the RTL should only be
! changed if the new address is valid. */
static void
! instantiate_decl (rtx x, HOST_WIDE_INT size, int valid_only)
{
! enum machine_mode mode;
! rtx addr;
! if (x == 0)
! return;
! /* If this is a CONCAT, recurse for the pieces. */
! if (GET_CODE (x) == CONCAT)
! {
! instantiate_decl (XEXP (x, 0), size / 2, valid_only);
! instantiate_decl (XEXP (x, 1), size / 2, valid_only);
! return;
! }
! /* If this is not a MEM, no need to do anything. Similarly if the
! address is a constant or a register that is not a virtual register. */
! if (!MEM_P (x))
! return;
! addr = XEXP (x, 0);
! if (CONSTANT_P (addr)
! || (REG_P (addr)
! && (REGNO (addr) < FIRST_VIRTUAL_REGISTER
! || REGNO (addr) > LAST_VIRTUAL_REGISTER)))
! return;
! /* If we should only do this if the address is valid, copy the address.
! We need to do this so we can undo any changes that might make the
! address invalid. This copy is unfortunate, but probably can't be
! avoided. */
! if (valid_only)
! addr = copy_rtx (addr);
! instantiate_virtual_regs_1 (&addr, NULL_RTX, 0);
! if (valid_only && size >= 0)
! {
! unsigned HOST_WIDE_INT decl_size = size;
! /* Now verify that the resulting address is valid for every integer or
! floating-point mode up to and including SIZE bytes long. We do this
! since the object might be accessed in any mode and frame addresses
! are shared. */
! for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
! mode != VOIDmode && GET_MODE_SIZE (mode) <= decl_size;
! mode = GET_MODE_WIDER_MODE (mode))
! if (! memory_address_p (mode, addr))
! return;
! for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
! mode != VOIDmode && GET_MODE_SIZE (mode) <= decl_size;
! mode = GET_MODE_WIDER_MODE (mode))
! if (! memory_address_p (mode, addr))
! return;
}
! /* Put back the address now that we have updated it and we either know
! it is valid or we don't care whether it is valid. */
! XEXP (x, 0) = addr;
! }
!
! /* Given a piece of RTX and a pointer to a HOST_WIDE_INT, if the RTX
! is a virtual register, return the equivalent hard register and set the
! offset indirectly through the pointer. Otherwise, return 0. */
! static rtx
! instantiate_new_reg (rtx x, HOST_WIDE_INT *poffset)
! {
! rtx new;
! HOST_WIDE_INT offset;
! if (x == virtual_incoming_args_rtx)
! new = arg_pointer_rtx, offset = in_arg_offset;
! else if (x == virtual_stack_vars_rtx)
! new = frame_pointer_rtx, offset = var_offset;
! else if (x == virtual_stack_dynamic_rtx)
! new = stack_pointer_rtx, offset = dynamic_offset;
! else if (x == virtual_outgoing_args_rtx)
! new = stack_pointer_rtx, offset = out_arg_offset;
! else if (x == virtual_cfa_rtx)
! new = arg_pointer_rtx, offset = cfa_offset;
! else
! return 0;
! *poffset = offset;
! return new;
! }
!
! /* Called when instantiate_virtual_regs has failed to update the instruction.
! Usually this means that non-matching instruction has been emit, however for
! asm statements it may be the problem in the constraints. */
! static void
! instantiate_virtual_regs_lossage (rtx insn)
! {
! gcc_assert (asm_noperands (PATTERN (insn)) >= 0);
! error_for_asm (insn, "impossible constraint in %<asm%>");
! delete_insn (insn);
! }
! /* Given a pointer to a piece of rtx and an optional pointer to the
! containing object, instantiate any virtual registers present in it.
! If EXTRA_INSNS, we always do the replacement and generate
! any extra insns before OBJECT. If it zero, we do nothing if replacement
! is not valid.
! Return 1 if we either had nothing to do or if we were able to do the
! needed replacement. Return 0 otherwise; we only return zero if
! EXTRA_INSNS is zero.
! We first try some simple transformations to avoid the creation of extra
! pseudos. */
! static int
! instantiate_virtual_regs_1 (rtx *loc, rtx object, int extra_insns)
! {
! rtx x;
! RTX_CODE code;
! rtx new = 0;
! HOST_WIDE_INT offset = 0;
! rtx temp;
! rtx seq;
! int i, j;
! const char *fmt;
! /* Re-start here to avoid recursion in common cases. */
! restart:
! x = *loc;
! if (x == 0)
! return 1;
! /* We may have detected and deleted invalid asm statements. */
! if (object && INSN_P (object) && INSN_DELETED_P (object))
! return 1;
! code = GET_CODE (x);
! /* Check for some special cases. */
! switch (code)
! {
! case CONST_INT:
! case CONST_DOUBLE:
! case CONST_VECTOR:
! case CONST:
! case SYMBOL_REF:
! case CODE_LABEL:
! case PC:
! case CC0:
! case ASM_INPUT:
! case ADDR_VEC:
! case ADDR_DIFF_VEC:
! case RETURN:
! return 1;
! case SET:
! /* We are allowed to set the virtual registers. This means that
! the actual register should receive the source minus the
! appropriate offset. This is used, for example, in the handling
! of non-local gotos. */
! if ((new = instantiate_new_reg (SET_DEST (x), &offset)) != 0)
! {
! rtx src = SET_SRC (x);
!
! /* We are setting the register, not using it, so the relevant
! offset is the negative of the offset to use were we using
! the register. */
! offset = - offset;
! instantiate_virtual_regs_1 (&src, NULL_RTX, 0);
!
! /* The only valid sources here are PLUS or REG. Just do
! the simplest possible thing to handle them. */
! if (!REG_P (src) && GET_CODE (src) != PLUS)
! {
! instantiate_virtual_regs_lossage (object);
! return 1;
! }
! start_sequence ();
! if (!REG_P (src))
! temp = force_operand (src, NULL_RTX);
! else
! temp = src;
! temp = force_operand (plus_constant (temp, offset), NULL_RTX);
! seq = get_insns ();
! end_sequence ();
! emit_insn_before (seq, object);
! SET_DEST (x) = new;
! if (! validate_change (object, &SET_SRC (x), temp, 0)
! || ! extra_insns)
! instantiate_virtual_regs_lossage (object);
! return 1;
! }
! instantiate_virtual_regs_1 (&SET_DEST (x), object, extra_insns);
! loc = &SET_SRC (x);
! goto restart;
! case PLUS:
! /* Handle special case of virtual register plus constant. */
! if (CONSTANT_P (XEXP (x, 1)))
! {
! rtx old, new_offset;
! /* Check for (plus (plus VIRT foo) (const_int)) first. */
! if (GET_CODE (XEXP (x, 0)) == PLUS)
! {
! if ((new = instantiate_new_reg (XEXP (XEXP (x, 0), 0), &offset)))
! {
! instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object,
! extra_insns);
! new = gen_rtx_PLUS (Pmode, new, XEXP (XEXP (x, 0), 1));
! }
! else
! {
! loc = &XEXP (x, 0);
! goto restart;
! }
! }
! #ifdef POINTERS_EXTEND_UNSIGNED
! /* If we have (plus (subreg (virtual-reg)) (const_int)), we know
! we can commute the PLUS and SUBREG because pointers into the
! frame are well-behaved. */
! else if (GET_CODE (XEXP (x, 0)) == SUBREG && GET_MODE (x) == ptr_mode
! && GET_CODE (XEXP (x, 1)) == CONST_INT
! && 0 != (new
! = instantiate_new_reg (SUBREG_REG (XEXP (x, 0)),
! &offset))
! && validate_change (object, loc,
! plus_constant (gen_lowpart (ptr_mode,
! new),
! offset
! + INTVAL (XEXP (x, 1))),
! 0))
! return 1;
! #endif
! else if ((new = instantiate_new_reg (XEXP (x, 0), &offset)) == 0)
! {
! /* We know the second operand is a constant. Unless the
! first operand is a REG (which has been already checked),
! it needs to be checked. */
! if (!REG_P (XEXP (x, 0)))
! {
! loc = &XEXP (x, 0);
! goto restart;
! }
! return 1;
! }
! new_offset = plus_constant (XEXP (x, 1), offset);
! /* If the new constant is zero, try to replace the sum with just
! the register. */
! if (new_offset == const0_rtx
! && validate_change (object, loc, new, 0))
! return 1;
!
! /* Next try to replace the register and new offset.
! There are two changes to validate here and we can't assume that
! in the case of old offset equals new just changing the register
! will yield a valid insn. In the interests of a little efficiency,
! however, we only call validate change once (we don't queue up the
! changes and then call apply_change_group). */
!
! old = XEXP (x, 0);
! if (offset == 0
! ? ! validate_change (object, &XEXP (x, 0), new, 0)
! : (XEXP (x, 0) = new,
! ! validate_change (object, &XEXP (x, 1), new_offset, 0)))
! {
! if (! extra_insns)
! {
! XEXP (x, 0) = old;
! return 0;
! }
! /* Otherwise copy the new constant into a register and replace
! constant with that register. */
! temp = gen_reg_rtx (Pmode);
! XEXP (x, 0) = new;
! if (validate_change (object, &XEXP (x, 1), temp, 0))
! emit_insn_before (gen_move_insn (temp, new_offset), object);
! else
! {
! /* If that didn't work, replace this expression with a
! register containing the sum. */
! XEXP (x, 0) = old;
! new = gen_rtx_PLUS (Pmode, new, new_offset);
! start_sequence ();
! temp = force_operand (new, NULL_RTX);
! seq = get_insns ();
! end_sequence ();
!
! emit_insn_before (seq, object);
! if (! validate_change (object, loc, temp, 0)
! && ! validate_replace_rtx (x, temp, object))
! {
! instantiate_virtual_regs_lossage (object);
! return 1;
! }
! }
! }
! return 1;
! }
! /* Fall through to generic two-operand expression case. */
! case EXPR_LIST:
! case CALL:
! case COMPARE:
! case MINUS:
! case MULT:
! case DIV: case UDIV:
! case MOD: case UMOD:
! case AND: case IOR: case XOR:
! case ROTATERT: case ROTATE:
! case ASHIFTRT: case LSHIFTRT: case ASHIFT:
! case NE: case EQ:
! case GE: case GT: case GEU: case GTU:
! case LE: case LT: case LEU: case LTU:
! if (XEXP (x, 1) && ! CONSTANT_P (XEXP (x, 1)))
! instantiate_virtual_regs_1 (&XEXP (x, 1), object, extra_insns);
! loc = &XEXP (x, 0);
! goto restart;
!
! case MEM:
! /* Most cases of MEM that convert to valid addresses have already been
! handled by our scan of decls. The only special handling we
! need here is to make a copy of the rtx to ensure it isn't being
! shared if we have to change it to a pseudo.
!
! If the rtx is a simple reference to an address via a virtual register,
! it can potentially be shared. In such cases, first try to make it
! a valid address, which can also be shared. Otherwise, copy it and
! proceed normally.
!
! First check for common cases that need no processing. These are
! usually due to instantiation already being done on a previous instance
! of a shared rtx. */
!
! temp = XEXP (x, 0);
! if (CONSTANT_ADDRESS_P (temp)
! #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
! || temp == arg_pointer_rtx
! #endif
! #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
! || temp == hard_frame_pointer_rtx
! #endif
! || temp == frame_pointer_rtx)
! return 1;
!
! if (GET_CODE (temp) == PLUS
! && CONSTANT_ADDRESS_P (XEXP (temp, 1))
! && (XEXP (temp, 0) == frame_pointer_rtx
! #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
! || XEXP (temp, 0) == hard_frame_pointer_rtx
! #endif
! #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
! || XEXP (temp, 0) == arg_pointer_rtx
! #endif
! ))
! return 1;
!
! if (temp == virtual_stack_vars_rtx
! || temp == virtual_incoming_args_rtx
! || (GET_CODE (temp) == PLUS
! && CONSTANT_ADDRESS_P (XEXP (temp, 1))
! && (XEXP (temp, 0) == virtual_stack_vars_rtx
! || XEXP (temp, 0) == virtual_incoming_args_rtx)))
! {
! /* This MEM may be shared. If the substitution can be done without
! the need to generate new pseudos, we want to do it in place
! so all copies of the shared rtx benefit. The call below will
! only make substitutions if the resulting address is still
! valid.
!
! Note that we cannot pass X as the object in the recursive call
! since the insn being processed may not allow all valid
! addresses. However, if we were not passed on object, we can
! only modify X without copying it if X will have a valid
! address.
!
! ??? Also note that this can still lose if OBJECT is an insn that
! has less restrictions on an address that some other insn.
! In that case, we will modify the shared address. This case
! doesn't seem very likely, though. One case where this could
! happen is in the case of a USE or CLOBBER reference, but we
! take care of that below. */
!
! if (instantiate_virtual_regs_1 (&XEXP (x, 0),
! object ? object : x, 0))
! return 1;
!
! /* Otherwise make a copy and process that copy. We copy the entire
! RTL expression since it might be a PLUS which could also be
! shared. */
! *loc = x = copy_rtx (x);
! }
!
! /* Fall through to generic unary operation case. */
! case PREFETCH:
! case SUBREG:
! case STRICT_LOW_PART:
! case NEG: case NOT:
! case PRE_DEC: case PRE_INC: case POST_DEC: case POST_INC:
! case SIGN_EXTEND: case ZERO_EXTEND:
! case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE:
! case FLOAT: case FIX:
! case UNSIGNED_FIX: case UNSIGNED_FLOAT:
! case ABS:
! case SQRT:
! case FFS:
! case CLZ: case CTZ:
! case POPCOUNT: case PARITY:
! /* These case either have just one operand or we know that we need not
! check the rest of the operands. */
! loc = &XEXP (x, 0);
! goto restart;
!
! case USE:
! case CLOBBER:
! /* If the operand is a MEM, see if the change is a valid MEM. If not,
! go ahead and make the invalid one, but do it to a copy. For a REG,
! just make the recursive call, since there's no chance of a problem. */
!
! if ((MEM_P (XEXP (x, 0))
! && instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 0), XEXP (x, 0),
! 0))
! || (REG_P (XEXP (x, 0))
! && instantiate_virtual_regs_1 (&XEXP (x, 0), object, 0)))
! return 1;
!
! XEXP (x, 0) = copy_rtx (XEXP (x, 0));
! loc = &XEXP (x, 0);
! goto restart;
! case REG:
! /* Try to replace with a PLUS. If that doesn't work, compute the sum
! in front of this insn and substitute the temporary. */
! if ((new = instantiate_new_reg (x, &offset)) != 0)
! {
! temp = plus_constant (new, offset);
! if (!validate_change (object, loc, temp, 0))
! {
! if (! extra_insns)
! return 0;
! start_sequence ();
! temp = force_operand (temp, NULL_RTX);
! seq = get_insns ();
! end_sequence ();
! emit_insn_before (seq, object);
! if (! validate_change (object, loc, temp, 0)
! && ! validate_replace_rtx (x, temp, object))
! instantiate_virtual_regs_lossage (object);
! }
! }
! return 1;
! default:
! break;
! }
! /* Scan all subexpressions. */
! fmt = GET_RTX_FORMAT (code);
! for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
! if (*fmt == 'e')
! {
! if (!instantiate_virtual_regs_1 (&XEXP (x, i), object, extra_insns))
! return 0;
}
- else if (*fmt == 'E')
- for (j = 0; j < XVECLEN (x, i); j++)
- if (! instantiate_virtual_regs_1 (&XVECEXP (x, i, j), object,
- extra_insns))
- return 0;
! return 1;
}
/* Return 1 if EXP is an aggregate type (or a value with aggregate type).
--- 1211,1656 ----
#endif
! /* Given a piece of RTX and a pointer to a HOST_WIDE_INT, if the RTX
! is a virtual register, return the equivalent hard register and set the
! offset indirectly through the pointer. Otherwise, return 0. */
! static rtx
! instantiate_new_reg (rtx x, HOST_WIDE_INT *poffset)
{
! rtx new;
! HOST_WIDE_INT offset;
! if (x == virtual_incoming_args_rtx)
! new = arg_pointer_rtx, offset = in_arg_offset;
! else if (x == virtual_stack_vars_rtx)
! new = frame_pointer_rtx, offset = var_offset;
! else if (x == virtual_stack_dynamic_rtx)
! new = stack_pointer_rtx, offset = dynamic_offset;
! else if (x == virtual_outgoing_args_rtx)
! new = stack_pointer_rtx, offset = out_arg_offset;
! else if (x == virtual_cfa_rtx)
! new = arg_pointer_rtx, offset = cfa_offset;
! else
! return NULL_RTX;
! *poffset = offset;
! return new;
}
! /* A subroutine of instantiate_virtual_regs, called via for_each_rtx.
! Instantiate any virtual registers present inside of *LOC. The expression
! is simplified, as much as possible, but is not to be considered "valid"
! in any sense implied by the target. If any change is made, set CHANGED
! to true. */
! static int
! instantiate_virtual_regs_in_rtx (rtx *loc, void *data)
{
! HOST_WIDE_INT offset;
! bool *changed = (bool *) data;
! rtx x, new;
! x = *loc;
! if (x == 0)
! return 0;
!
! switch (GET_CODE (x))
{
! case REG:
! new = instantiate_new_reg (x, &offset);
! if (new)
! {
! *loc = plus_constant (new, offset);
! if (changed)
! *changed = true;
! }
! return -1;
!
! case PLUS:
! new = instantiate_new_reg (XEXP (x, 0), &offset);
! if (new)
! {
! new = plus_constant (new, offset);
! *loc = simplify_gen_binary (PLUS, GET_MODE (x), new, XEXP (x, 1));
! if (changed)
! *changed = true;
! return -1;
! }
! /* FIXME -- from old code */
! /* If we have (plus (subreg (virtual-reg)) (const_int)), we know
! we can commute the PLUS and SUBREG because pointers into the
! frame are well-behaved. */
! break;
! default:
! break;
}
! return 0;
}
! /* A subroutine of instantiate_virtual_regs_in_insn. Return true if X
! matches the predicate for insn CODE operand OPERAND. */
! static int
! safe_insn_predicate (int code, int operand, rtx x)
{
! const struct insn_operand_data *op_data;
! if (code < 0)
! return true;
! op_data = &insn_data[code].operand[operand];
! if (op_data->predicate == NULL)
! return true;
! return op_data->predicate (x, op_data->mode);
! }
! /* 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)
{
! HOST_WIDE_INT offset;
! int insn_code, i;
! bool any_change;
! rtx set, new, x, 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 = instantiate_new_reg (SET_DEST (set), &offset);
! if (new)
! {
! start_sequence ();
! for_each_rtx (&SET_SRC (set), instantiate_virtual_regs_in_rtx, NULL);
! x = simplify_gen_binary (PLUS, GET_MODE (new), SET_SRC (set),
! GEN_INT (-offset));
! x = force_operand (x, new);
! if (x != new)
! emit_move_insn (new, 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 = instantiate_new_reg (SET_SRC (set), &offset);
! if (new && 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, GEN_INT (offset), 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);
! /* Handle a plus involving a virtual register by determining if the
! operands remain valid if they're modified in place. */
! 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)
! && GET_CODE (recog_data.operand[2]) == CONST_INT
! && (new = instantiate_new_reg (recog_data.operand[1], &offset)))
! {
! offset += INTVAL (recog_data.operand[2]);
! /* If the sum is zero, then replace with a plain move. */
! if (offset == 0)
! {
! start_sequence ();
! emit_move_insn (SET_DEST (set), new);
! seq = get_insns ();
! end_sequence ();
! emit_insn_before (seq, insn);
! delete_insn (insn);
! return;
! }
!
! x = gen_int_mode (offset, recog_data.operand_mode[2]);
! insn_code = INSN_CODE (insn);
!
! /* 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)
! && safe_insn_predicate (insn_code, 2, x))
! {
! *recog_data.operand_loc[1] = recog_data.operand[1] = new;
! *recog_data.operand_loc[2] = recog_data.operand[2] = x;
! any_change = true;
! goto verify;
! }
! }
}
+ else
+ extract_insn (insn);
! insn_code = INSN_CODE (insn);
! any_change = false;
! /* 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);
! bool changed = false;
! for_each_rtx (&addr, instantiate_virtual_regs_in_rtx, &changed);
! if (!changed)
! continue;
! start_sequence ();
! x = replace_equiv_address (x, addr);
! seq = get_insns ();
! end_sequence ();
! if (seq)
! emit_insn_before (seq, insn);
! }
! break;
! case REG:
! new = instantiate_new_reg (x, &offset);
! if (new == NULL)
! continue;
! if (offset == 0)
! x = new;
! 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,
! GEN_INT (offset), NULL_RTX,
! 1, OPTAB_LIB_WIDEN);
! seq = get_insns ();
! end_sequence ();
! emit_insn_before (seq, insn);
! }
! break;
! case SUBREG:
! new = instantiate_new_reg (SUBREG_REG (x), &offset);
! if (new == NULL)
! continue;
! if (offset != 0)
! {
! start_sequence ();
! new = expand_simple_binop (GET_MODE (new), PLUS, new,
! GEN_INT (offset), NULL_RTX,
! 1, OPTAB_LIB_WIDEN);
! seq = get_insns ();
! end_sequence ();
! emit_insn_before (seq, insn);
! }
! x = simplify_gen_subreg (insn_data[insn_code].operand[i].mode,
! new, GET_MODE (new), SUBREG_BYTE (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))
! x = force_reg (insn_data[insn_code].operand[i].mode, x);
! *recog_data.operand_loc[i] = recog_data.operand[i] = x;
! any_change = true;
! }
! verify:
! if (any_change)
! {
! /* Propagate operand changes into the duplicates. */
! for (i = 0; i < recog_data.n_dups; ++i)
! *recog_data.dup_loc[i]
! = 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%>");
! delete_insn (insn);
! }
! }
! else
! {
! if (recog_memoized (insn) < 0)
! fatal_insn_not_found (insn);
! }
! }
! /* Subroutine of instantiate_decls. Given RTL representing a decl,
! do any instantiation required. */
! static void
! instantiate_decl (rtx x)
! {
! rtx addr;
! if (x == 0)
! return;
! /* If this is a CONCAT, recurse for the pieces. */
! if (GET_CODE (x) == CONCAT)
! {
! instantiate_decl (XEXP (x, 0));
! instantiate_decl (XEXP (x, 1));
! return;
! }
! /* If this is not a MEM, no need to do anything. Similarly if the
! address is a constant or a register that is not a virtual register. */
! if (!MEM_P (x))
! return;
! addr = XEXP (x, 0);
! if (CONSTANT_P (addr)
! || (REG_P (addr)
! && (REGNO (addr) < FIRST_VIRTUAL_REGISTER
! || REGNO (addr) > LAST_VIRTUAL_REGISTER)))
! return;
! for_each_rtx (&XEXP (x, 0), instantiate_virtual_regs_in_rtx, NULL);
! }
! /* Subroutine of instantiate_decls: Process all decls in the given
! BLOCK node and all its subblocks. */
! static void
! instantiate_decls_1 (tree let)
! {
! tree t;
! for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
! if (DECL_RTL_SET_P (t))
! instantiate_decl (DECL_RTL (t));
! /* Process all subblocks. */
! for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t))
! instantiate_decls_1 (t);
! }
! /* Scan all decls in FNDECL (both variables and parameters) and instantiate
! all virtual registers in their DECL_RTL's. */
! static void
! instantiate_decls (tree fndecl)
! {
! tree decl;
! /* Process all parameters of the function. */
! for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
! {
! instantiate_decl (DECL_RTL (decl));
! instantiate_decl (DECL_INCOMING_RTL (decl));
! }
! /* Now process all variables defined in the function or its subblocks. */
! instantiate_decls_1 (DECL_INITIAL (fndecl));
! }
! /* Pass through the INSNS of function FNDECL and convert virtual register
! references to hard register references. */
! void
! instantiate_virtual_regs (void)
! {
! rtx insn;
! /* Compute the offsets to use for this function. */
! in_arg_offset = FIRST_PARM_OFFSET (current_function_decl);
! var_offset = STARTING_FRAME_OFFSET;
! dynamic_offset = STACK_DYNAMIC_OFFSET (current_function_decl);
! out_arg_offset = STACK_POINTER_OFFSET;
! cfa_offset = ARG_POINTER_CFA_OFFSET (current_function_decl);
! /* Initialize recognition, indicating that volatile is OK. */
! init_recog ();
! /* Scan through all the insns, instantiating every virtual register still
! present. */
! for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
! if (INSN_P (insn))
! {
! /* These patterns in the instruction stream can never be recognized.
! Fortunately, they shouldn't contain virtual registers either. */
! if (GET_CODE (PATTERN (insn)) == USE
! || GET_CODE (PATTERN (insn)) == CLOBBER
! || GET_CODE (PATTERN (insn)) == ADDR_VEC
! || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
! || GET_CODE (PATTERN (insn)) == ASM_INPUT)
! continue;
! instantiate_virtual_regs_in_insn (insn);
! if (INSN_DELETED_P (insn))
! continue;
! for_each_rtx (®_NOTES (insn), instantiate_virtual_regs_in_rtx, NULL);
! /* Instantiate any virtual registers in CALL_INSN_FUNCTION_USAGE. */
! if (GET_CODE (insn) == CALL_INSN)
! for_each_rtx (&CALL_INSN_FUNCTION_USAGE (insn),
! instantiate_virtual_regs_in_rtx, NULL);
}
! /* Instantiate the virtual registers in the DECLs for debugging purposes. */
! instantiate_decls (current_function_decl);
!
! /* Indicate that, from now on, assign_stack_local should use
! frame_pointer_rtx. */
! virtuals_instantiated = 1;
}
/* Return 1 if EXP is an aggregate type (or a value with aggregate type).