/* Definitions for computing resource usage of specific insns.
- Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
- Free Software Foundation, Inc.
+ Copyright (C) 1999-2019 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
-#include "toplev.h"
+#include "backend.h"
#include "rtl.h"
+#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
-#include "hard-reg-set.h"
-#include "function.h"
#include "regs.h"
-#include "flags.h"
-#include "output.h"
+#include "emit-rtl.h"
#include "resource.h"
-#include "except.h"
#include "insn-attr.h"
#include "params.h"
-#include "df.h"
/* This structure is used to record liveness information at the targets or
fallthrough insns of branches. We will most likely need the information
static HARD_REG_SET pending_dead_regs;
\f
static void update_live_status (rtx, const_rtx, void *);
-static int find_basic_block (rtx, int);
-static rtx next_insn_no_annul (rtx);
-static rtx find_dead_or_set_registers (rtx, struct resources*,
- rtx*, int, struct resources,
- struct resources);
+static int find_basic_block (rtx_insn *, int);
+static rtx_insn *next_insn_no_annul (rtx_insn *);
+static rtx_insn *find_dead_or_set_registers (rtx_insn *, struct resources*,
+ rtx *, int, struct resources,
+ struct resources);
\f
/* Utility function called from mark_target_live_regs via note_stores.
It deadens any CLOBBERed registers and livens any SET registers. */
else
{
first_regno = REGNO (dest);
- last_regno = END_HARD_REGNO (dest);
+ last_regno = END_REGNO (dest);
}
if (GET_CODE (x) == CLOBBER)
for (i = first_regno; i < last_regno; i++)
CLEAR_HARD_REG_BIT (current_live_regs, i);
+ else if (GET_CODE (x) == CLOBBER_HIGH)
+ /* No current target supports both branch delay slots and CLOBBER_HIGH.
+ We'd need more elaborate liveness tracking to handle that
+ combination. */
+ gcc_unreachable ();
else
for (i = first_regno; i < last_regno; i++)
{
correct. */
static int
-find_basic_block (rtx insn, int search_limit)
+find_basic_block (rtx_insn *insn, int search_limit)
{
- basic_block bb;
-
/* Scan backwards to the previous BARRIER. Then see if we can find a
label that starts a basic block. Return the basic block number. */
for (insn = prev_nonnote_insn (insn);
/* The start of the function. */
else if (insn == 0)
- return ENTRY_BLOCK_PTR->next_bb->index;
+ return ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb->index;
/* See if any of the upcoming CODE_LABELs start a basic block. If we reach
anything other than a CODE_LABEL or note, we can't find this code. */
for (insn = next_nonnote_insn (insn);
insn && LABEL_P (insn);
insn = next_nonnote_insn (insn))
- {
- FOR_EACH_BB (bb)
- if (insn == BB_HEAD (bb))
- return bb->index;
- }
+ if (BLOCK_FOR_INSN (insn))
+ return BLOCK_FOR_INSN (insn)->index;
return -1;
}
/* Similar to next_insn, but ignores insns in the delay slots of
an annulled branch. */
-static rtx
-next_insn_no_annul (rtx insn)
+static rtx_insn *
+next_insn_no_annul (rtx_insn *insn)
{
if (insn)
{
/* If INSN is an annulled branch, skip any insns from the target
of the branch. */
- if (INSN_P (insn)
+ if (JUMP_P (insn)
&& INSN_ANNULLED_BRANCH_P (insn)
&& NEXT_INSN (PREV_INSN (insn)) != insn)
{
- rtx next = NEXT_INSN (insn);
- enum rtx_code code = GET_CODE (next);
+ rtx_insn *next = NEXT_INSN (insn);
- while ((code == INSN || code == JUMP_INSN || code == CALL_INSN)
+ while ((NONJUMP_INSN_P (next) || JUMP_P (next) || CALL_P (next))
&& INSN_FROM_TARGET_P (next))
{
insn = next;
next = NEXT_INSN (insn);
- code = GET_CODE (next);
}
}
insn = NEXT_INSN (insn);
if (insn && NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
- insn = XVECEXP (PATTERN (insn), 0, 0);
+ insn = as_a <rtx_sequence *> (PATTERN (insn))->insn (0);
}
return insn;
void
mark_referenced_resources (rtx x, struct resources *res,
- int include_delayed_effects)
+ bool include_delayed_effects)
{
enum rtx_code code = GET_CODE (x);
int i, j;
switch (code)
{
case CONST:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case PC:
case SYMBOL_REF:
case LABEL_REF:
+ case DEBUG_INSN:
return;
case SUBREG:
if (!REG_P (SUBREG_REG (x)))
- mark_referenced_resources (SUBREG_REG (x), res, 0);
+ mark_referenced_resources (SUBREG_REG (x), res, false);
else
{
unsigned int regno = subreg_regno (x);
case MEM:
/* If this memory shouldn't change, it really isn't referencing
memory. */
- if (MEM_READONLY_P (x))
- res->unch_memory = 1;
- else
+ if (! MEM_READONLY_P (x))
res->memory = 1;
res->volatil |= MEM_VOLATILE_P (x);
/* Mark registers used to access memory. */
- mark_referenced_resources (XEXP (x, 0), res, 0);
+ mark_referenced_resources (XEXP (x, 0), res, false);
return;
case CC0:
return;
case UNSPEC_VOLATILE:
+ case TRAP_IF:
case ASM_INPUT:
/* Traditional asm's are always volatile. */
- res->volatil = 1;
- return;
-
- case TRAP_IF:
res->volatil = 1;
break;
res->volatil |= MEM_VOLATILE_P (x);
/* For all ASM_OPERANDS, we must traverse the vector of input operands.
- We can not just fall through here since then we would be confused
+ We cannot just fall through here since then we would be confused
by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
traditional asms unlike their normal usage. */
for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
- mark_referenced_resources (ASM_OPERANDS_INPUT (x, i), res, 0);
+ mark_referenced_resources (ASM_OPERANDS_INPUT (x, i), res, false);
return;
case CALL:
/* The first operand will be a (MEM (xxx)) but doesn't really reference
memory. The second operand may be referenced, though. */
- mark_referenced_resources (XEXP (XEXP (x, 0), 0), res, 0);
- mark_referenced_resources (XEXP (x, 1), res, 0);
+ mark_referenced_resources (XEXP (XEXP (x, 0), 0), res, false);
+ mark_referenced_resources (XEXP (x, 1), res, false);
return;
case SET:
registers used to access memory are referenced. SET_DEST is
also referenced if it is a ZERO_EXTRACT. */
- mark_referenced_resources (SET_SRC (x), res, 0);
+ mark_referenced_resources (SET_SRC (x), res, false);
x = SET_DEST (x);
if (GET_CODE (x) == ZERO_EXTRACT
|| GET_CODE (x) == STRICT_LOW_PART)
- mark_referenced_resources (x, res, 0);
+ mark_referenced_resources (x, res, false);
else if (GET_CODE (x) == SUBREG)
x = SUBREG_REG (x);
if (MEM_P (x))
- mark_referenced_resources (XEXP (x, 0), res, 0);
+ mark_referenced_resources (XEXP (x, 0), res, false);
return;
case CLOBBER:
+ case CLOBBER_HIGH:
return;
case CALL_INSN:
However, we may have moved some of the parameter loading insns
into the delay slot of this CALL. If so, the USE's for them
don't count and should be skipped. */
- rtx insn = PREV_INSN (x);
- rtx sequence = 0;
+ rtx_insn *insn = PREV_INSN (as_a <rtx_insn *> (x));
+ rtx_sequence *sequence = 0;
int seq_size = 0;
int i;
/* If we are part of a delay slot sequence, point at the SEQUENCE. */
if (NEXT_INSN (insn) != x)
{
- sequence = PATTERN (NEXT_INSN (insn));
- seq_size = XVECLEN (sequence, 0);
+ sequence = as_a <rtx_sequence *> (PATTERN (NEXT_INSN (insn)));
+ seq_size = sequence->len ();
gcc_assert (GET_CODE (sequence) == SEQUENCE);
}
if (frame_pointer_needed)
{
SET_HARD_REG_BIT (res->regs, FRAME_POINTER_REGNUM);
-#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
- SET_HARD_REG_BIT (res->regs, HARD_FRAME_POINTER_REGNUM);
-#endif
+ if (!HARD_FRAME_POINTER_IS_FRAME_POINTER)
+ SET_HARD_REG_BIT (res->regs, HARD_FRAME_POINTER_REGNUM);
}
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
for (i = 1; i < seq_size; i++)
{
- rtx slot_pat = PATTERN (XVECEXP (sequence, 0, i));
+ rtx slot_pat = PATTERN (sequence->element (i));
if (GET_CODE (slot_pat) == SET
&& rtx_equal_p (SET_DEST (slot_pat),
XEXP (XEXP (link, 0), 0)))
}
if (i >= seq_size)
mark_referenced_resources (XEXP (XEXP (link, 0), 0),
- res, 0);
+ res, false);
}
}
}
/* ... fall through to other INSN processing ... */
+ gcc_fallthrough ();
case INSN:
case JUMP_INSN:
-#ifdef INSN_REFERENCES_ARE_DELAYED
+ if (GET_CODE (PATTERN (x)) == COND_EXEC)
+ /* In addition to the usual references, also consider all outputs
+ as referenced, to compensate for mark_set_resources treating
+ them as killed. This is similar to ZERO_EXTRACT / STRICT_LOW_PART
+ handling, execpt that we got a partial incidence instead of a partial
+ width. */
+ mark_set_resources (x, res, 0,
+ include_delayed_effects
+ ? MARK_SRC_DEST_CALL : MARK_SRC_DEST);
+
if (! include_delayed_effects
- && INSN_REFERENCES_ARE_DELAYED (x))
+ && INSN_REFERENCES_ARE_DELAYED (as_a <rtx_insn *> (x)))
return;
-#endif
/* No special processing, just speed up. */
mark_referenced_resources (PATTERN (x), res, include_delayed_effects);
Stop after passing a few conditional jumps, and/or a small
number of unconditional branches. */
-static rtx
-find_dead_or_set_registers (rtx target, struct resources *res,
+static rtx_insn *
+find_dead_or_set_registers (rtx_insn *target, struct resources *res,
rtx *jump_target, int jump_count,
struct resources set, struct resources needed)
{
HARD_REG_SET scratch;
- rtx insn, next;
- rtx jump_insn = 0;
+ rtx_insn *insn;
+ rtx_insn *next_insn;
+ rtx_insn *jump_insn = 0;
int i;
- for (insn = target; insn; insn = next)
+ for (insn = target; insn; insn = next_insn)
{
- rtx this_jump_insn = insn;
+ rtx_insn *this_insn = insn;
- next = NEXT_INSN (insn);
+ next_insn = NEXT_INSN (insn);
/* If this instruction can throw an exception, then we don't
know where we might end up next. That means that we have to
case BARRIER:
case NOTE:
+ case DEBUG_INSN:
continue;
case INSN:
}
else if (GET_CODE (PATTERN (insn)) == CLOBBER)
continue;
- else if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+ else if (rtx_sequence *seq =
+ dyn_cast <rtx_sequence *> (PATTERN (insn)))
{
/* An unconditional jump can be used to fill the delay slot
of a call, so search for a JUMP_INSN in any position. */
- for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ for (i = 0; i < seq->len (); i++)
{
- this_jump_insn = XVECEXP (PATTERN (insn), 0, i);
- if (JUMP_P (this_jump_insn))
+ this_insn = seq->insn (i);
+ if (JUMP_P (this_insn))
break;
}
}
break;
}
- if (JUMP_P (this_jump_insn))
+ if (rtx_jump_insn *this_jump_insn =
+ dyn_cast <rtx_jump_insn *> (this_insn))
{
if (jump_count++ < 10)
{
if (any_uncondjump_p (this_jump_insn)
- || GET_CODE (PATTERN (this_jump_insn)) == RETURN)
+ || ANY_RETURN_P (PATTERN (this_jump_insn)))
{
- next = JUMP_LABEL (this_jump_insn);
+ rtx lab_or_return = this_jump_insn->jump_label ();
+ if (ANY_RETURN_P (lab_or_return))
+ next_insn = NULL;
+ else
+ next_insn = as_a <rtx_insn *> (lab_or_return);
if (jump_insn == 0)
{
jump_insn = insn;
if (jump_count >= 10)
break;
- mark_referenced_resources (insn, &needed, 1);
+ mark_referenced_resources (insn, &needed, true);
/* For an annulled branch, mark_set_resources ignores slots
filled by instructions from the target. This is correct
if (GET_CODE (PATTERN (insn)) == SEQUENCE
&& INSN_ANNULLED_BRANCH_P (this_jump_insn))
{
- for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++)
- INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i))
- = ! INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i));
+ rtx_sequence *seq = as_a <rtx_sequence *> (PATTERN (insn));
+ for (i = 1; i < seq->len (); i++)
+ INSN_FROM_TARGET_P (seq->element (i))
+ = ! INSN_FROM_TARGET_P (seq->element (i));
target_set = set;
mark_set_resources (insn, &target_set, 0,
MARK_SRC_DEST_CALL);
- for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++)
- INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i))
- = ! INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i));
+ for (i = 1; i < seq->len (); i++)
+ INSN_FROM_TARGET_P (seq->element (i))
+ = ! INSN_FROM_TARGET_P (seq->element (i));
mark_set_resources (insn, &set, 0, MARK_SRC_DEST_CALL);
}
AND_COMPL_HARD_REG_SET (scratch, needed.regs);
AND_COMPL_HARD_REG_SET (fallthrough_res.regs, scratch);
- find_dead_or_set_registers (JUMP_LABEL (this_jump_insn),
- &target_res, 0, jump_count,
- target_set, needed);
- find_dead_or_set_registers (next,
+ if (!ANY_RETURN_P (this_jump_insn->jump_label ()))
+ find_dead_or_set_registers
+ (this_jump_insn->jump_target (),
+ &target_res, 0, jump_count, target_set, needed);
+ find_dead_or_set_registers (next_insn,
&fallthrough_res, 0, jump_count,
set, needed);
IOR_HARD_REG_SET (fallthrough_res.regs, target_res.regs);
}
}
- mark_referenced_resources (insn, &needed, 1);
+ mark_referenced_resources (insn, &needed, true);
mark_set_resources (insn, &set, 0, MARK_SRC_DEST_CALL);
COPY_HARD_REG_SET (scratch, set.regs);
case BARRIER:
case CODE_LABEL:
case USE:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case LABEL_REF:
case SYMBOL_REF:
case CONST:
case PC:
+ case DEBUG_INSN:
/* These don't set any resources. */
return;
if (mark_type == MARK_SRC_DEST_CALL)
{
+ rtx_call_insn *call_insn = as_a <rtx_call_insn *> (x);
rtx link;
+ HARD_REG_SET regs;
res->cc = res->memory = 1;
- IOR_HARD_REG_SET (res->regs, regs_invalidated_by_call);
+ get_call_reg_set_usage (call_insn, ®s, regs_invalidated_by_call);
+ IOR_HARD_REG_SET (res->regs, regs);
- for (link = CALL_INSN_FUNCTION_USAGE (x);
+ for (link = CALL_INSN_FUNCTION_USAGE (call_insn);
link; link = XEXP (link, 1))
- if (GET_CODE (XEXP (link, 0)) == CLOBBER)
- mark_set_resources (SET_DEST (XEXP (link, 0)), res, 1,
- MARK_SRC_DEST);
+ {
+ /* We could support CLOBBER_HIGH and treat it in the same way as
+ HARD_REGNO_CALL_PART_CLOBBERED, but no port needs that
+ yet. */
+ gcc_assert (GET_CODE (XEXP (link, 0)) != CLOBBER_HIGH);
+ if (GET_CODE (XEXP (link, 0)) == CLOBBER)
+ mark_set_resources (SET_DEST (XEXP (link, 0)), res, 1,
+ MARK_SRC_DEST);
+ }
/* Check for a REG_SETJMP. If it exists, then we must
assume that this call can clobber any register. */
- if (find_reg_note (x, REG_SETJMP, NULL))
+ if (find_reg_note (call_insn, REG_SETJMP, NULL))
SET_HARD_REG_SET (res->regs);
}
/* ... and also what its RTL says it modifies, if anything. */
+ gcc_fallthrough ();
case JUMP_INSN:
case INSN:
/* An insn consisting of just a CLOBBER (or USE) is just for flow
and doesn't actually do anything, so we ignore it. */
-#ifdef INSN_SETS_ARE_DELAYED
if (mark_type != MARK_SRC_DEST_CALL
- && INSN_SETS_ARE_DELAYED (x))
+ && INSN_SETS_ARE_DELAYED (as_a <rtx_insn *> (x)))
return;
-#endif
x = PATTERN (x);
if (GET_CODE (x) != USE && GET_CODE (x) != CLOBBER)
mark_set_resources (XEXP (x, 0), res, 1, MARK_SRC_DEST);
return;
+ case CLOBBER_HIGH:
+ /* No current target supports both branch delay slots and CLOBBER_HIGH.
+ We'd need more elaborate liveness tracking to handle that
+ combination. */
+ gcc_unreachable ();
+
case SEQUENCE:
- for (i = 0; i < XVECLEN (x, 0); i++)
- if (! (INSN_ANNULLED_BRANCH_P (XVECEXP (x, 0, 0))
- && INSN_FROM_TARGET_P (XVECEXP (x, 0, i))))
- mark_set_resources (XVECEXP (x, 0, i), res, 0, mark_type);
+ {
+ rtx_sequence *seq = as_a <rtx_sequence *> (x);
+ rtx control = seq->element (0);
+ bool annul_p = JUMP_P (control) && INSN_ANNULLED_BRANCH_P (control);
+
+ mark_set_resources (control, res, 0, mark_type);
+ for (i = seq->len () - 1; i >= 0; --i)
+ {
+ rtx elt = seq->element (i);
+ if (!annul_p && INSN_FROM_TARGET_P (elt))
+ mark_set_resources (elt, res, 0, mark_type);
+ }
+ }
return;
case POST_INC:
if (in_dest)
{
res->memory = 1;
- res->unch_memory |= MEM_READONLY_P (x);
res->volatil |= MEM_VOLATILE_P (x);
}
res->volatil |= MEM_VOLATILE_P (x);
/* For all ASM_OPERANDS, we must traverse the vector of input operands.
- We can not just fall through here since then we would be confused
+ We cannot just fall through here since then we would be confused
by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
traditional asms unlike their normal usage. */
static bool
return_insn_p (const_rtx insn)
{
- if (JUMP_P (insn) && GET_CODE (PATTERN (insn)) == RETURN)
+ if (JUMP_P (insn) && ANY_RETURN_P (PATTERN (insn)))
return true;
if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE)
(with no intervening active insns) to see if any of them start a basic
block. If we hit the start of the function first, we use block 0.
- Once we have found a basic block and a corresponding first insns, we can
- accurately compute the live status from basic_block_live_regs and
- reg_renumber. (By starting at a label following a BARRIER, we are immune
- to actions taken by reload and jump.) Then we scan all insns between
- that point and our target. For each CLOBBER (or for call-clobbered regs
- when we pass a CALL_INSN), mark the appropriate registers are dead. For
- a SET, mark them as live.
+ Once we have found a basic block and a corresponding first insn, we can
+ accurately compute the live status (by starting at a label following a
+ BARRIER, we are immune to actions taken by reload and jump.) Then we
+ scan all insns between that point and our target. For each CLOBBER (or
+ for call-clobbered regs when we pass a CALL_INSN), mark the appropriate
+ registers are dead. For a SET, mark them as live.
We have to be careful when using REG_DEAD notes because they are not
updated by such things as find_equiv_reg. So keep track of registers
init_resource_info () was invoked before we are called. */
void
-mark_target_live_regs (rtx insns, rtx target, struct resources *res)
+mark_target_live_regs (rtx_insn *insns, rtx target_maybe_return, struct resources *res)
{
int b = -1;
unsigned int i;
struct target_info *tinfo = NULL;
- rtx insn;
- rtx jump_insn = 0;
+ rtx_insn *insn;
rtx jump_target;
HARD_REG_SET scratch;
struct resources set, needed;
/* Handle end of function. */
- if (target == 0)
+ if (target_maybe_return == 0 || ANY_RETURN_P (target_maybe_return))
{
*res = end_of_function_needs;
return;
}
+ /* We've handled the case of RETURN/SIMPLE_RETURN; we should now have an
+ instruction. */
+ rtx_insn *target = as_a <rtx_insn *> (target_maybe_return);
+
/* Handle return insn. */
- else if (return_insn_p (target))
+ if (return_insn_p (target))
{
*res = end_of_function_needs;
- mark_referenced_resources (target, res, 0);
+ mark_referenced_resources (target, res, false);
return;
}
/* We have to assume memory is needed, but the CC isn't. */
res->memory = 1;
- res->volatil = res->unch_memory = 0;
+ res->volatil = 0;
res->cc = 0;
/* See if we have computed this value already. */
information, we can get it from there unless the insn at the
start of the basic block has been deleted. */
if (tinfo && tinfo->block != -1
- && ! INSN_DELETED_P (BB_HEAD (BASIC_BLOCK (tinfo->block))))
+ && ! BB_HEAD (BASIC_BLOCK_FOR_FN (cfun, tinfo->block))->deleted ())
b = tinfo->block;
}
/* If we found a basic block, get the live registers from it and update
them with anything set or killed between its start and the insn before
- TARGET. Otherwise, we must assume everything is live. */
+ TARGET; this custom life analysis is really about registers so we need
+ to use the LR problem. Otherwise, we must assume everything is live. */
if (b != -1)
{
- regset regs_live = DF_LR_IN (BASIC_BLOCK (b));
- rtx start_insn, stop_insn;
-
- /* Compute hard regs live at start of block -- this is the real hard regs
- marked live, plus live pseudo regs that have been renumbered to
- hard regs. */
+ regset regs_live = DF_LR_IN (BASIC_BLOCK_FOR_FN (cfun, b));
+ rtx_insn *start_insn, *stop_insn;
+ /* Compute hard regs live at start of block. */
REG_SET_TO_HARD_REG_SET (current_live_regs, regs_live);
/* Get starting and ending insn, handling the case where each might
be a SEQUENCE. */
- start_insn = (b == ENTRY_BLOCK_PTR->next_bb->index ?
- insns : BB_HEAD (BASIC_BLOCK (b)));
+ start_insn = (b == ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb->index ?
+ insns : BB_HEAD (BASIC_BLOCK_FOR_FN (cfun, b)));
stop_insn = target;
if (NONJUMP_INSN_P (start_insn)
&& GET_CODE (PATTERN (start_insn)) == SEQUENCE)
- start_insn = XVECEXP (PATTERN (start_insn), 0, 0);
+ start_insn = as_a <rtx_sequence *> (PATTERN (start_insn))->insn (0);
if (NONJUMP_INSN_P (stop_insn)
&& GET_CODE (PATTERN (stop_insn)) == SEQUENCE)
insn = next_insn_no_annul (insn))
{
rtx link;
- rtx real_insn = insn;
+ rtx_insn *real_insn = insn;
enum rtx_code code = GET_CODE (insn);
+ if (DEBUG_INSN_P (insn))
+ continue;
+
/* If this insn is from the target of a branch, it isn't going to
be used in the sequel. If it is used in both cases, this
test will not be true. */
/* If this insn is a USE made by update_block, we care about the
underlying insn. */
- if (code == INSN && GET_CODE (PATTERN (insn)) == USE
+ if (code == INSN
+ && GET_CODE (PATTERN (insn)) == USE
&& INSN_P (XEXP (PATTERN (insn), 0)))
- real_insn = XEXP (PATTERN (insn), 0);
+ real_insn = as_a <rtx_insn *> (XEXP (PATTERN (insn), 0));
if (CALL_P (real_insn))
{
- /* CALL clobbers all call-used regs that aren't fixed except
- sp, ap, and fp. Do this before setting the result of the
- call live. */
- AND_COMPL_HARD_REG_SET (current_live_regs,
- regs_invalidated_by_call);
+ /* Values in call-clobbered registers survive a COND_EXEC CALL
+ if that is not executed; this matters for resoure use because
+ they may be used by a complementarily (or more strictly)
+ predicated instruction, or if the CALL is NORETURN. */
+ if (GET_CODE (PATTERN (real_insn)) != COND_EXEC)
+ {
+ HARD_REG_SET regs_invalidated_by_this_call;
+ get_call_reg_set_usage (real_insn,
+ ®s_invalidated_by_this_call,
+ regs_invalidated_by_call);
+ /* CALL clobbers all call-used regs that aren't fixed except
+ sp, ap, and fp. Do this before setting the result of the
+ call live. */
+ AND_COMPL_HARD_REG_SET (current_live_regs,
+ regs_invalidated_by_this_call);
+ }
/* A CALL_INSN sets any global register live, since it may
have been modified by the call. */
else if (LABEL_P (real_insn))
{
+ basic_block bb;
+
/* A label clobbers the pending dead registers since neither
reload nor jump will propagate a value across a label. */
AND_COMPL_HARD_REG_SET (current_live_regs, pending_dead_regs);
CLEAR_HARD_REG_SET (pending_dead_regs);
+
+ /* We must conservatively assume that all registers that used
+ to be live here still are. The fallthrough edge may have
+ left a live register uninitialized. */
+ bb = BLOCK_FOR_INSN (real_insn);
+ if (bb)
+ {
+ HARD_REG_SET extra_live;
+
+ REG_SET_TO_HARD_REG_SET (extra_live, DF_LR_IN (bb));
+ IOR_HARD_REG_SET (current_live_regs, extra_live);
+ }
}
/* The beginning of the epilogue corresponds to the end of the
CLEAR_RESOURCE (&set);
CLEAR_RESOURCE (&needed);
- jump_insn = find_dead_or_set_registers (target, res, &jump_target, 0,
- set, needed);
+ rtx_insn *jump_insn = find_dead_or_set_registers (target, res, &jump_target,
+ 0, set, needed);
/* If we hit an unconditional branch, we have another way of finding out
what is live: we can see what is live at the branch target and include
if (jump_insn)
{
struct resources new_resources;
- rtx stop_insn = next_active_insn (jump_insn);
+ rtx_insn *stop_insn = next_active_insn (jump_insn);
- mark_target_live_regs (insns, next_active_insn (jump_target),
- &new_resources);
+ if (!ANY_RETURN_P (jump_target))
+ jump_target = next_active_insn (as_a<rtx_insn *> (jump_target));
+ mark_target_live_regs (insns, jump_target, &new_resources);
CLEAR_RESOURCE (&set);
CLEAR_RESOURCE (&needed);
/* Include JUMP_INSN in the needed registers. */
for (insn = target; insn != stop_insn; insn = next_active_insn (insn))
{
- mark_referenced_resources (insn, &needed, 1);
+ mark_referenced_resources (insn, &needed, true);
COPY_HARD_REG_SET (scratch, needed.regs);
AND_COMPL_HARD_REG_SET (scratch, set.regs);
This should be invoked before the first call to mark_target_live_regs. */
void
-init_resource_info (rtx epilogue_insn)
+init_resource_info (rtx_insn *epilogue_insn)
{
int i;
+ basic_block bb;
/* Indicate what resources are required to be valid at the end of the current
- function. The condition code never is and memory always is. If the
- frame pointer is needed, it is and so is the stack pointer unless
- EXIT_IGNORE_STACK is nonzero. If the frame pointer is not needed, the
- stack pointer is. Registers used to return the function value are
- needed. Registers holding global variables are needed. */
+ function. The condition code never is and memory always is.
+ The stack pointer is needed unless EXIT_IGNORE_STACK is true
+ and there is an epilogue that restores the original stack pointer
+ from the frame pointer. Registers used to return the function value
+ are needed. Registers holding global variables are needed. */
end_of_function_needs.cc = 0;
end_of_function_needs.memory = 1;
- end_of_function_needs.unch_memory = 0;
CLEAR_HARD_REG_SET (end_of_function_needs.regs);
if (frame_pointer_needed)
{
SET_HARD_REG_BIT (end_of_function_needs.regs, FRAME_POINTER_REGNUM);
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- SET_HARD_REG_BIT (end_of_function_needs.regs, HARD_FRAME_POINTER_REGNUM);
-#endif
- if (! EXIT_IGNORE_STACK
- || current_function_sp_is_unchanging)
- SET_HARD_REG_BIT (end_of_function_needs.regs, STACK_POINTER_REGNUM);
+ if (!HARD_FRAME_POINTER_IS_FRAME_POINTER)
+ SET_HARD_REG_BIT (end_of_function_needs.regs,
+ HARD_FRAME_POINTER_REGNUM);
}
- else
+ if (!(frame_pointer_needed
+ && EXIT_IGNORE_STACK
+ && epilogue_insn
+ && !crtl->sp_is_unchanging))
SET_HARD_REG_BIT (end_of_function_needs.regs, STACK_POINTER_REGNUM);
- if (current_function_return_rtx != 0)
- mark_referenced_resources (current_function_return_rtx,
- &end_of_function_needs, 1);
+ if (crtl->return_rtx != 0)
+ mark_referenced_resources (crtl->return_rtx,
+ &end_of_function_needs, true);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (global_regs[i]
-#ifdef EPILOGUE_USES
- || EPILOGUE_USES (i)
-#endif
- )
+ if (global_regs[i] || EPILOGUE_USES (i))
SET_HARD_REG_BIT (end_of_function_needs.regs, i);
/* The registers required to be live at the end of the function are
/* Allocate and initialize the tables used by mark_target_live_regs. */
target_hash_table = XCNEWVEC (struct target_info *, TARGET_HASH_PRIME);
- bb_ticks = XCNEWVEC (int, last_basic_block);
+ bb_ticks = XCNEWVEC (int, last_basic_block_for_fn (cfun));
+
+ /* Set the BLOCK_FOR_INSN of each label that starts a basic block. */
+ FOR_EACH_BB_FN (bb, cfun)
+ if (LABEL_P (BB_HEAD (bb)))
+ BLOCK_FOR_INSN (BB_HEAD (bb)) = bb;
}
\f
/* Free up the resources allocated to mark_target_live_regs (). This
void
free_resource_info (void)
{
+ basic_block bb;
+
if (target_hash_table != NULL)
{
int i;
free (bb_ticks);
bb_ticks = NULL;
}
+
+ FOR_EACH_BB_FN (bb, cfun)
+ if (LABEL_P (BB_HEAD (bb)))
+ BLOCK_FOR_INSN (BB_HEAD (bb)) = NULL;
}
\f
/* Clear any hashed information that we have stored for INSN. */
void
-clear_hashed_info_for_insn (rtx insn)
+clear_hashed_info_for_insn (rtx_insn *insn)
{
struct target_info *tinfo;
/* Increment the tick count for the basic block that contains INSN. */
void
-incr_ticks_for_insn (rtx insn)
+incr_ticks_for_insn (rtx_insn *insn)
{
int b = find_basic_block (insn, MAX_DELAY_SLOT_LIVE_SEARCH);
/* Add TRIAL to the set of resources used at the end of the current
function. */
void
-mark_end_of_function_resources (rtx trial, int include_delayed_effects)
+mark_end_of_function_resources (rtx trial, bool include_delayed_effects)
{
mark_referenced_resources (trial, &end_of_function_needs,
include_delayed_effects);