/* Optimize jump instructions, for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997
- 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
/* This is the pathetic reminder of old fame of the jump-optimization pass
of the compiler. Now it contains basically a set of utility functions to
static void init_label_info (rtx);
static void mark_all_labels (rtx);
-static void delete_computation (rtx);
+static void mark_jump_label_1 (rtx, rtx, bool, bool);
static void redirect_exp_1 (rtx *, rtx, rtx, rtx);
static int invert_exp_1 (rtx, rtx);
static int returnjump_p_1 (rtx *, void *);
-static void delete_prior_computation (rtx, rtx);
\f
-/* Alternate entry into the jump optimizer. This entry point only rebuilds
- the JUMP_LABEL field in jumping insns and REG_LABEL notes in non-jumping
- instructions. */
+/* 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)
{
return 0;
}
-struct tree_opt_pass pass_cleanup_barriers =
+struct rtl_opt_pass pass_cleanup_barriers =
{
+ {
+ RTL_PASS,
"barriers", /* name */
NULL, /* gate */
cleanup_barriers, /* execute */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
+ TODO_dump_func /* todo_flags_finish */
+ }
};
\f
-/* Initialize LABEL_NUSES and JUMP_LABEL fields. Delete any REG_LABEL
- notes whose labels don't occur in the insn any more. Returns the
- largest INSN_UID found. */
+/* Initialize LABEL_NUSES and JUMP_LABEL fields, add REG_LABEL_TARGET
+ for remaining targets for JUMP_P. Delete any REG_LABEL_OPERAND
+ notes whose labels don't occur in the insn any more. */
+
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);
- else if (JUMP_P (insn))
- JUMP_LABEL (insn) = 0;
- else if (NONJUMP_INSN_P (insn) || CALL_P (insn))
- {
- rtx note, next;
+ {
+ 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
- && ! reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
- remove_note (insn, note);
- }
- }
+ 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);
+ }
+ }
+ }
}
/* Mark the label each jump jumps to.
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 (! INSN_DELETED_P (insn) && JUMP_P (insn))
+
+ /* 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)
{
- /* When we know the LABEL_REF contained in a REG used in
- an indirect jump, we'll have a REG_LABEL note so that
- flow can tell where it's going. */
- if (JUMP_LABEL (insn) == 0)
+ 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_note = find_reg_note (insn, REG_LABEL, NULL_RTX);
- if (label_note)
+ 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))))))
+
{
- /* But a LABEL_REF around the REG_LABEL note, so
- that we can canonicalize it. */
- rtx label_ref = gen_rtx_LABEL_REF (Pmode,
- XEXP (label_note, 0));
-
- mark_jump_label (label_ref, insn, 0);
- XEXP (label_note, 0) = XEXP (label_ref, 0);
- JUMP_LABEL (insn) = XEXP (label_note, 0);
+ /* 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. */
}
}
\f
-/* Move all block-beg, block-end and loop-beg notes between START and END out
- before START. START and END may be such notes. Returns the values of the
- new starting and ending insns, which may be different if the original ones
- were such notes. Return true if there were only such notes and no real
- instructions. */
-
-bool
-squeeze_notes (rtx* startp, rtx* endp)
-{
- rtx start = *startp;
- rtx end = *endp;
-
- rtx insn;
- rtx next;
- rtx last = NULL;
- rtx past_end = NEXT_INSN (end);
-
- for (insn = start; insn != past_end; insn = next)
- {
- next = NEXT_INSN (insn);
- if (NOTE_P (insn)
- && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END
- || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG))
- {
- /* BLOCK_BEG or BLOCK_END notes only exist in the `final' pass. */
- gcc_assert (NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_BEG
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_END);
-
- if (insn == start)
- start = next;
- else
- {
- rtx prev = PREV_INSN (insn);
- PREV_INSN (insn) = PREV_INSN (start);
- NEXT_INSN (insn) = start;
- NEXT_INSN (PREV_INSN (insn)) = insn;
- PREV_INSN (NEXT_INSN (insn)) = insn;
- NEXT_INSN (prev) = next;
- PREV_INSN (next) = prev;
- }
- }
- else
- last = insn;
- }
-
- /* There were no real instructions. */
- if (start == past_end)
- return true;
-
- end = last;
-
- *startp = start;
- *endp = end;
- return false;
-}
-\f
/* Given a comparison (CODE ARG0 ARG1), inside an insn, INSN, return a code
of reversed comparison if it is possible to do so. Otherwise return UNKNOWN.
UNKNOWN may be returned in case we are having CC_MODE compare and we don't
description should define REVERSIBLE_CC_MODE and REVERSE_CONDITION macros
to help this function avoid overhead in these cases. */
enum rtx_code
-reversed_comparison_code_parts (enum rtx_code code, rtx arg0, rtx arg1, rtx insn)
+reversed_comparison_code_parts (enum rtx_code code, const_rtx arg0,
+ const_rtx arg1, const_rtx insn)
{
enum machine_mode mode;
if (GET_MODE_CLASS (mode) == MODE_CC || CC0_P (arg0))
{
- rtx prev;
+ const_rtx prev;
/* Try to search for the comparison to determine the real mode.
This code is expensive, but with sane machine description it
will be never used, since REVERSIBLE_CC_MODE will return true
if (! insn)
return UNKNOWN;
- for (prev = prev_nonnote_insn (insn);
+ /* These CONST_CAST's are okay because prev_nonnote_insn just
+ returns its argument and we assign it to a const_rtx
+ variable. */
+ for (prev = prev_nonnote_insn (CONST_CAST_RTX(insn));
prev != 0 && !LABEL_P (prev);
- prev = prev_nonnote_insn (prev))
+ prev = prev_nonnote_insn (CONST_CAST_RTX(prev)))
{
- rtx set = set_of (arg0, prev);
+ const_rtx set = set_of (arg0, prev);
if (set && GET_CODE (set) == SET
&& rtx_equal_p (SET_DEST (set), arg0))
{
/* A wrapper around the previous function to take COMPARISON as rtx
expression. This simplifies many callers. */
enum rtx_code
-reversed_comparison_code (rtx comparison, rtx insn)
+reversed_comparison_code (const_rtx comparison, const_rtx insn)
{
if (!COMPARISON_P (comparison))
return UNKNOWN;
/* Return comparison with reversed code of EXP.
Return NULL_RTX in case we fail to do the reversal. */
rtx
-reversed_comparison (rtx exp, enum machine_mode mode)
+reversed_comparison (const_rtx exp, enum machine_mode mode)
{
enum rtx_code reversed_code = reversed_comparison_code (exp, NULL_RTX);
if (reversed_code == UNKNOWN)
/* Return 1 if INSN is an unconditional jump and nothing else. */
int
-simplejump_p (rtx insn)
+simplejump_p (const_rtx insn)
{
return (JUMP_P (insn)
&& GET_CODE (PATTERN (insn)) == SET
branch and compare insns. Use any_condjump_p instead whenever possible. */
int
-condjump_p (rtx insn)
+condjump_p (const_rtx insn)
{
- rtx x = PATTERN (insn);
+ const_rtx x = PATTERN (insn);
if (GET_CODE (x) != SET
|| GET_CODE (SET_DEST (x)) != PC)
branch and compare insns. Use any_condjump_p instead whenever possible. */
int
-condjump_in_parallel_p (rtx insn)
+condjump_in_parallel_p (const_rtx insn)
{
- rtx x = PATTERN (insn);
+ const_rtx x = PATTERN (insn);
if (GET_CODE (x) != PARALLEL)
return 0;
/* Return set of PC, otherwise NULL. */
rtx
-pc_set (rtx insn)
+pc_set (const_rtx insn)
{
rtx pat;
if (!JUMP_P (insn))
possibly bundled inside a PARALLEL. */
int
-any_uncondjump_p (rtx insn)
+any_uncondjump_p (const_rtx insn)
{
- rtx x = pc_set (insn);
+ const_rtx x = pc_set (insn);
if (!x)
return 0;
if (GET_CODE (SET_SRC (x)) != LABEL_REF)
Note that unlike condjump_p it returns false for unconditional jumps. */
int
-any_condjump_p (rtx insn)
+any_condjump_p (const_rtx insn)
{
- rtx x = pc_set (insn);
+ const_rtx x = pc_set (insn);
enum rtx_code a, b;
if (!x)
/* Return the label of a conditional jump. */
rtx
-condjump_label (rtx insn)
+condjump_label (const_rtx insn)
{
rtx x = pc_set (insn);
nothing more. */
int
-onlyjump_p (rtx insn)
+onlyjump_p (const_rtx insn)
{
rtx set;
and has no side effects. */
int
-only_sets_cc0_p (rtx x)
+only_sets_cc0_p (const_rtx x)
{
if (! x)
return 0;
but also does other things. */
int
-sets_cc0_p (rtx x)
+sets_cc0_p (const_rtx x)
{
if (! x)
return 0;
}
#endif
\f
-/* Find all CODE_LABELs referred to in X, and increment their use counts.
- If INSN is a JUMP_INSN and there is at least one CODE_LABEL referenced
- in INSN, then store one of them in JUMP_LABEL (INSN).
- If INSN is an INSN or a CALL_INSN and there is at least one CODE_LABEL
- referenced in INSN, add a REG_LABEL note containing that label to INSN.
- Also, when there are consecutive labels, canonicalize on the last of them.
+/* Find all CODE_LABELs referred to in X, and increment their use
+ counts. If INSN is a JUMP_INSN and there is at least one
+ CODE_LABEL referenced in INSN as a jump target, then store the last
+ one in JUMP_LABEL (INSN). For a tablejump, this must be the label
+ for the ADDR_VEC. Store any other jump targets as REG_LABEL_TARGET
+ notes. If INSN is an INSN or a CALL_INSN or non-target operands of
+ a JUMP_INSN, and there is at least one CODE_LABEL referenced in
+ INSN, add a REG_LABEL_OPERAND note containing that label to INSN.
Note that two labels separated by a loop-beginning note
must be kept distinct if we have not yet done loop-optimization,
void
mark_jump_label (rtx x, rtx insn, int in_mem)
{
+ mark_jump_label_1 (x, insn, in_mem != 0,
+ (insn != NULL && x == PATTERN (insn) && JUMP_P (insn)));
+}
+
+/* Worker function for mark_jump_label. IN_MEM is TRUE when X occurs
+ within a (MEM ...). IS_TARGET is TRUE when X is to be treated as a
+ jump-target; when the JUMP_LABEL field of INSN should be set or a
+ REG_LABEL_TARGET note should be added, not a REG_LABEL_OPERAND
+ note. */
+
+static void
+mark_jump_label_1 (rtx x, rtx insn, bool in_mem, bool is_target)
+{
RTX_CODE code = GET_CODE (x);
int i;
const char *fmt;
return;
case MEM:
- in_mem = 1;
+ in_mem = true;
break;
+ case SEQUENCE:
+ for (i = 0; i < XVECLEN (x, 0); i++)
+ mark_jump_label (PATTERN (XVECEXP (x, 0, i)),
+ XVECEXP (x, 0, i), 0);
+ return;
+
case SYMBOL_REF:
if (!in_mem)
return;
/* If this is a constant-pool reference, see if it is a label. */
if (CONSTANT_POOL_ADDRESS_P (x))
- mark_jump_label (get_pool_constant (x), insn, in_mem);
+ mark_jump_label_1 (get_pool_constant (x), insn, in_mem, is_target);
break;
+ /* Handle operands in the condition of an if-then-else as for a
+ non-jump insn. */
+ case IF_THEN_ELSE:
+ if (!is_target)
+ break;
+ mark_jump_label_1 (XEXP (x, 0), insn, in_mem, false);
+ mark_jump_label_1 (XEXP (x, 1), insn, in_mem, true);
+ mark_jump_label_1 (XEXP (x, 2), insn, in_mem, true);
+ return;
+
case LABEL_REF:
{
rtx label = XEXP (x, 0);
/* Ignore remaining references to unreachable labels that
have been deleted. */
if (NOTE_P (label)
- && NOTE_LINE_NUMBER (label) == NOTE_INSN_DELETED_LABEL)
+ && NOTE_KIND (label) == NOTE_INSN_DELETED_LABEL)
break;
gcc_assert (LABEL_P (label));
if (insn)
{
- if (JUMP_P (insn))
+ if (is_target
+ /* Do not change a previous setting of JUMP_LABEL. If the
+ JUMP_LABEL slot is occupied by a different label,
+ create a note for this label. */
+ && (JUMP_LABEL (insn) == NULL || JUMP_LABEL (insn) == label))
JUMP_LABEL (insn) = label;
else
{
- /* Add a REG_LABEL note for LABEL unless there already
- is one. All uses of a label, except for labels
- that are the targets of jumps, must have a
- REG_LABEL note. */
- if (! find_reg_note (insn, REG_LABEL, label))
- REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_LABEL, label,
- REG_NOTES (insn));
+ enum reg_note kind
+ = is_target ? REG_LABEL_TARGET : REG_LABEL_OPERAND;
+
+ /* Add a REG_LABEL_OPERAND or REG_LABEL_TARGET note
+ for LABEL unless there already is one. All uses of
+ a label, except for the primary target of a jump,
+ must have such a note. */
+ if (! find_reg_note (insn, kind, label))
+ add_reg_note (insn, kind, label);
}
}
return;
int eltnum = code == ADDR_DIFF_VEC ? 1 : 0;
for (i = 0; i < XVECLEN (x, eltnum); i++)
- mark_jump_label (XVECEXP (x, eltnum, i), NULL_RTX, in_mem);
+ mark_jump_label_1 (XVECEXP (x, eltnum, i), NULL_RTX, in_mem,
+ is_target);
}
return;
}
fmt = GET_RTX_FORMAT (code);
+
+ /* The primary target of a tablejump is the label of the ADDR_VEC,
+ which is canonically mentioned *last* in the insn. To get it
+ marked as JUMP_LABEL, we iterate over items in reverse order. */
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- mark_jump_label (XEXP (x, i), insn, in_mem);
+ mark_jump_label_1 (XEXP (x, i), insn, in_mem, is_target);
else if (fmt[i] == 'E')
{
int j;
- for (j = 0; j < XVECLEN (x, i); j++)
- mark_jump_label (XVECEXP (x, i, j), insn, in_mem);
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ mark_jump_label_1 (XVECEXP (x, i, j), insn, in_mem,
+ is_target);
}
}
}
rtx lab = JUMP_LABEL (insn), lab_next;
if (LABEL_NUSES (lab) == 0)
- {
- /* This can delete NEXT or PREV,
- either directly if NEXT is JUMP_LABEL (INSN),
- or indirectly through more levels of jumps. */
- delete_related_insns (lab);
-
- /* I feel a little doubtful about this loop,
- but I see no clean and sure alternative way
- to find the first insn after INSN that is not now deleted.
- I hope this works. */
- while (next && INSN_DELETED_P (next))
- next = NEXT_INSN (next);
- return next;
- }
+ /* This can delete NEXT or PREV,
+ either directly if NEXT is JUMP_LABEL (INSN),
+ or indirectly through more levels of jumps. */
+ delete_related_insns (lab);
else if (tablejump_p (insn, NULL, &lab_next))
{
/* If we're deleting the tablejump, delete the dispatch table.
return next;
}
- /* Likewise for an ordinary INSN / CALL_INSN with a REG_LABEL note. */
- if (NONJUMP_INSN_P (insn) || CALL_P (insn))
+ /* Likewise for any JUMP_P / INSN / CALL_INSN with a
+ REG_LABEL_OPERAND or REG_LABEL_TARGET note. */
+ if (INSN_P (insn))
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
- if (REG_NOTE_KIND (note) == REG_LABEL
+ if ((REG_NOTE_KIND (note) == REG_LABEL_OPERAND
+ || REG_NOTE_KIND (note) == REG_LABEL_TARGET)
/* This could also be a NOTE_INSN_DELETED_LABEL note. */
&& LABEL_P (XEXP (note, 0)))
if (LABEL_NUSES (XEXP (note, 0)) == 0)
}
}
+ /* I feel a little doubtful about this loop,
+ but I see no clean and sure alternative way
+ to find the first insn after INSN that is not now deleted.
+ I hope this works. */
+ while (next && INSN_DELETED_P (next))
+ next = NEXT_INSN (next);
return next;
}
\f
return;
}
+ if (code == IF_THEN_ELSE)
+ {
+ /* Skip the condition of an IF_THEN_ELSE. We only want to
+ change jump destinations, not eventual label comparisons. */
+ redirect_exp_1 (&XEXP (x, 1), olabel, nlabel, insn);
+ redirect_exp_1 (&XEXP (x, 2), olabel, nlabel, insn);
+ return;
+ }
+
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
{
rtx note;
+ gcc_assert (JUMP_LABEL (jump) == olabel);
+
/* Negative DELETE_UNUSED used to be used to signalize behavior on
moving FUNCTION_END note. Just sanity check that no user still worry
about this. */
reversed. */
int
-rtx_renumbered_equal_p (rtx x, rtx y)
+rtx_renumbered_equal_p (const_rtx x, const_rtx y)
{
int i;
- enum rtx_code code = GET_CODE (x);
+ const enum rtx_code code = GET_CODE (x);
const char *fmt;
if (x == y)
if (reg_renumber[reg_x] >= 0)
{
+ if (!subreg_offset_representable_p (reg_renumber[reg_x],
+ GET_MODE (SUBREG_REG (x)),
+ byte_x,
+ GET_MODE (x)))
+ return 0;
reg_x = subreg_regno_offset (reg_renumber[reg_x],
GET_MODE (SUBREG_REG (x)),
byte_x,
GET_MODE (x));
byte_x = 0;
}
+ else if (!subreg_offset_representable_p (reg_x,
+ GET_MODE (SUBREG_REG (x)),
+ byte_x,
+ GET_MODE (x)))
+ return 0;
}
else
{
if (reg_renumber[reg_y] >= 0)
{
+ if (!subreg_offset_representable_p (reg_renumber[reg_y],
+ GET_MODE (SUBREG_REG (y)),
+ byte_y,
+ GET_MODE (y)))
+ return 0;
reg_y = subreg_regno_offset (reg_renumber[reg_y],
GET_MODE (SUBREG_REG (y)),
byte_y,
GET_MODE (y));
byte_y = 0;
}
+ else if (!subreg_offset_representable_p (reg_y,
+ GET_MODE (SUBREG_REG (y)),
+ byte_y,
+ GET_MODE (y)))
+ return 0;
}
else
{
return -1. Any rtx is valid for X. */
int
-true_regnum (rtx x)
+true_regnum (const_rtx x)
{
if (REG_P (x))
{
/* Return regno of the register REG and handle subregs too. */
unsigned int
-reg_or_subregno (rtx reg)
+reg_or_subregno (const_rtx reg)
{
if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg);