-/* Marks registers possibly live at the current place being scanned by
- mark_target_live_regs. Used only by next two function. */
-
-static HARD_REG_SET current_live_regs;
-
-/* Marks registers for which we have seen a REG_DEAD note but no assignment.
- Also only used by the next two functions. */
-
-static HARD_REG_SET pending_dead_regs;
-
-/* Utility function called from mark_target_live_regs via note_stores.
- It deadens any CLOBBERed registers and livens any SET registers. */
-
-static void
-update_live_status (dest, x)
- rtx dest;
- rtx x;
-{
- int first_regno, last_regno;
- int i;
-
- if (GET_CODE (dest) != REG
- && (GET_CODE (dest) != SUBREG || GET_CODE (SUBREG_REG (dest)) != REG))
- return;
-
- if (GET_CODE (dest) == SUBREG)
- first_regno = REGNO (SUBREG_REG (dest)) + SUBREG_WORD (dest);
- else
- first_regno = REGNO (dest);
-
- last_regno = first_regno + HARD_REGNO_NREGS (first_regno, GET_MODE (dest));
-
- if (GET_CODE (x) == CLOBBER)
- for (i = first_regno; i < last_regno; i++)
- CLEAR_HARD_REG_BIT (current_live_regs, i);
- else
- for (i = first_regno; i < last_regno; i++)
- {
- SET_HARD_REG_BIT (current_live_regs, i);
- CLEAR_HARD_REG_BIT (pending_dead_regs, i);
- }
-}
-
-/* Similar to next_insn, but ignores insns in the delay slots of
- an annulled branch. */
-
-static rtx
-next_insn_no_annul (insn)
- rtx insn;
-{
- if (insn)
- {
- /* If INSN is an annulled branch, skip any insns from the target
- of the branch. */
- if (INSN_ANNULLED_BRANCH_P (insn)
- && NEXT_INSN (PREV_INSN (insn)) != insn)
- while (INSN_FROM_TARGET_P (NEXT_INSN (insn)))
- insn = NEXT_INSN (insn);
-
- insn = NEXT_INSN (insn);
- if (insn && GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == SEQUENCE)
- insn = XVECEXP (PATTERN (insn), 0, 0);
- }
-
- return insn;
-}
-\f
-/* A subroutine of mark_target_live_regs. Search forward from TARGET
- looking for registers that are set before they are used. These are dead.
- Stop after passing a few conditional jumps, and/or a small
- number of unconditional branches. */
-
-static rtx
-find_dead_or_set_registers (target, res, jump_target, jump_count, set, needed)
- rtx target;
- struct resources *res;
- rtx *jump_target;
- int jump_count;
- struct resources set, needed;
-{
- HARD_REG_SET scratch;
- rtx insn, next;
- rtx jump_insn = 0;
- int i;
-
- for (insn = target; insn; insn = next)
- {
- rtx this_jump_insn = insn;
-
- next = NEXT_INSN (insn);
- switch (GET_CODE (insn))
- {
- case CODE_LABEL:
- /* After a label, any pending dead registers that weren't yet
- used can be made dead. */
- AND_COMPL_HARD_REG_SET (pending_dead_regs, needed.regs);
- AND_COMPL_HARD_REG_SET (res->regs, pending_dead_regs);
- CLEAR_HARD_REG_SET (pending_dead_regs);
-
- continue;
-
- case BARRIER:
- case NOTE:
- continue;
-
- case INSN:
- if (GET_CODE (PATTERN (insn)) == USE)
- {
- /* If INSN is a USE made by update_block, we care about the
- underlying insn. Any registers set by the underlying insn
- are live since the insn is being done somewhere else. */
- if (GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i')
- mark_set_resources (XEXP (PATTERN (insn), 0), res, 0, 1);
-
- /* All other USE insns are to be ignored. */
- continue;
- }
- else if (GET_CODE (PATTERN (insn)) == CLOBBER)
- continue;
- else if (GET_CODE (PATTERN (insn)) == SEQUENCE)
- {
- /* 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++)
- {
- this_jump_insn = XVECEXP (PATTERN (insn), 0, i);
- if (GET_CODE (this_jump_insn) == JUMP_INSN)
- break;
- }
- }
-
- default:
- break;
- }
-
- if (GET_CODE (this_jump_insn) == JUMP_INSN)
- {
- if (jump_count++ < 10)
- {
- if (simplejump_p (this_jump_insn)
- || GET_CODE (PATTERN (this_jump_insn)) == RETURN)
- {
- next = JUMP_LABEL (this_jump_insn);
- if (jump_insn == 0)
- {
- jump_insn = insn;
- if (jump_target)
- *jump_target = JUMP_LABEL (this_jump_insn);
- }
- }
- else if (condjump_p (this_jump_insn)
- || condjump_in_parallel_p (this_jump_insn))
- {
- struct resources target_set, target_res;
- struct resources fallthrough_res;
-
- /* We can handle conditional branches here by following
- both paths, and then IOR the results of the two paths
- together, which will give us registers that are dead
- on both paths. Since this is expensive, we give it
- a much higher cost than unconditional branches. The
- cost was chosen so that we will follow at most 1
- conditional branch. */
-
- jump_count += 4;
- if (jump_count >= 10)
- break;
-
- mark_referenced_resources (insn, &needed, 1);
-
- /* For an annulled branch, mark_set_resources ignores slots
- filled by instructions from the target. This is correct
- if the branch is not taken. Since we are following both
- paths from the branch, we must also compute correct info
- if the branch is taken. We do this by inverting all of
- the INSN_FROM_TARGET_P bits, calling mark_set_resources,
- and then inverting the INSN_FROM_TARGET_P bits again. */
-
- 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));
-
- target_set = set;
- mark_set_resources (insn, &target_set, 0, 1);
-
- 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));
-
- mark_set_resources (insn, &set, 0, 1);
- }
- else
- {
- mark_set_resources (insn, &set, 0, 1);
- target_set = set;
- }
-
- target_res = *res;
- COPY_HARD_REG_SET (scratch, target_set.regs);
- AND_COMPL_HARD_REG_SET (scratch, needed.regs);
- AND_COMPL_HARD_REG_SET (target_res.regs, scratch);
-
- fallthrough_res = *res;
- COPY_HARD_REG_SET (scratch, set.regs);
- 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,
- &fallthrough_res, 0, jump_count,
- set, needed);
- IOR_HARD_REG_SET (fallthrough_res.regs, target_res.regs);
- AND_HARD_REG_SET (res->regs, fallthrough_res.regs);
- break;
- }
- else
- break;
- }
- else
- {
- /* Don't try this optimization if we expired our jump count
- above, since that would mean there may be an infinite loop
- in the function being compiled. */
- jump_insn = 0;
- break;
- }
- }
-
- mark_referenced_resources (insn, &needed, 1);
- mark_set_resources (insn, &set, 0, 1);
-
- COPY_HARD_REG_SET (scratch, set.regs);
- AND_COMPL_HARD_REG_SET (scratch, needed.regs);
- AND_COMPL_HARD_REG_SET (res->regs, scratch);
- }
-
- return jump_insn;
-}
-
-/* Set the resources that are live at TARGET.
-
- If TARGET is zero, we refer to the end of the current function and can
- return our precomputed value.
-
- Otherwise, we try to find out what is live by consulting the basic block
- information. This is tricky, because we must consider the actions of
- reload and jump optimization, which occur after the basic block information
- has been computed.
-
- Accordingly, we proceed as follows::
-
- We find the previous BARRIER and look at all immediately following labels
- (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.
-
- 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
- marked as dead that haven't been assigned to, and mark them dead at the
- next CODE_LABEL since reload and jump won't propagate values across labels.
-
- If we cannot find the start of a basic block (should be a very rare
- case, if it can happen at all), mark everything as potentially live.
-
- Next, scan forward from TARGET looking for things set or clobbered
- before they are used. These are not live.
-
- Because we can be called many times on the same target, save our results
- in a hash table indexed by INSN_UID. */
-
-static void
-mark_target_live_regs (target, res)
- rtx target;
- struct resources *res;
-{
- int b = -1;
- int i;
- struct target_info *tinfo;
- rtx insn;
- rtx jump_insn = 0;
- rtx jump_target;
- HARD_REG_SET scratch;
- struct resources set, needed;
-
- /* Handle end of function. */
- if (target == 0)
- {
- *res = end_of_function_needs;
- return;
- }
-
- /* We have to assume memory is needed, but the CC isn't. */
- res->memory = 1;
- res->volatil = res->unch_memory = 0;
- res->cc = 0;
-
- /* See if we have computed this value already. */
- for (tinfo = target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME];
- tinfo; tinfo = tinfo->next)
- if (tinfo->uid == INSN_UID (target))
- break;
-
- /* Start by getting the basic block number. If we have saved 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 (basic_block_head[tinfo->block]))
- b = tinfo->block;
-
- if (b == -1)
- b = find_basic_block (target);
-
- if (tinfo)
- {
- /* If the information is up-to-date, use it. Otherwise, we will
- update it below. */
- if (b == tinfo->block && b != -1 && tinfo->bb_tick == bb_ticks[b])
- {
- COPY_HARD_REG_SET (res->regs, tinfo->live_regs);
- return;
- }
- }
- else
- {
- /* Allocate a place to put our results and chain it into the
- hash table. */
- tinfo = (struct target_info *) oballoc (sizeof (struct target_info));
- tinfo->uid = INSN_UID (target);
- tinfo->block = b;
- tinfo->next = target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME];
- target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME] = tinfo;
- }
-
- CLEAR_HARD_REG_SET (pending_dead_regs);
-
- /* 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. */
- if (b != -1)
- {
- regset regs_live = basic_block_live_at_start[b];
- int j;
- int regno;
- 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. */
-
- REG_SET_TO_HARD_REG_SET (current_live_regs, regs_live);
-
- EXECUTE_IF_SET_IN_REG_SET
- (regs_live, FIRST_PSEUDO_REGISTER, i,
- {
- if ((regno = reg_renumber[i]) >= 0)
- for (j = regno;
- j < regno + HARD_REGNO_NREGS (regno,
- PSEUDO_REGNO_MODE (i));
- j++)
- SET_HARD_REG_BIT (current_live_regs, j);
- });
-
- /* Get starting and ending insn, handling the case where each might
- be a SEQUENCE. */
- start_insn = (b == 0 ? get_insns () : basic_block_head[b]);
- stop_insn = target;
-
- if (GET_CODE (start_insn) == INSN
- && GET_CODE (PATTERN (start_insn)) == SEQUENCE)
- start_insn = XVECEXP (PATTERN (start_insn), 0, 0);
-
- if (GET_CODE (stop_insn) == INSN
- && GET_CODE (PATTERN (stop_insn)) == SEQUENCE)
- stop_insn = next_insn (PREV_INSN (stop_insn));
-
- for (insn = start_insn; insn != stop_insn;
- insn = next_insn_no_annul (insn))
- {
- rtx link;
- rtx real_insn = insn;
-
- /* 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 (INSN_FROM_TARGET_P (insn))
- continue;
-
- /* If this insn is a USE made by update_block, we care about the
- underlying insn. */
- if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == USE
- && GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i')
- real_insn = XEXP (PATTERN (insn), 0);
-
- if (GET_CODE (real_insn) == CALL_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. */
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (call_used_regs[i]
- && i != STACK_POINTER_REGNUM && i != FRAME_POINTER_REGNUM
- && i != ARG_POINTER_REGNUM
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && i != HARD_FRAME_POINTER_REGNUM
-#endif
-#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && ! (i == ARG_POINTER_REGNUM && fixed_regs[i])
-#endif
-#ifdef PIC_OFFSET_TABLE_REGNUM
- && ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic)
-#endif
- )
- CLEAR_HARD_REG_BIT (current_live_regs, i);
-
- /* A CALL_INSN sets any global register live, since it may
- have been modified by the call. */
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (global_regs[i])
- SET_HARD_REG_BIT (current_live_regs, i);
- }
-
- /* Mark anything killed in an insn to be deadened at the next
- label. Ignore USE insns; the only REG_DEAD notes will be for
- parameters. But they might be early. A CALL_INSN will usually
- clobber registers used for parameters. It isn't worth bothering
- with the unlikely case when it won't. */
- if ((GET_CODE (real_insn) == INSN
- && GET_CODE (PATTERN (real_insn)) != USE
- && GET_CODE (PATTERN (real_insn)) != CLOBBER)
- || GET_CODE (real_insn) == JUMP_INSN
- || GET_CODE (real_insn) == CALL_INSN)
- {
- for (link = REG_NOTES (real_insn); link; link = XEXP (link, 1))
- if (REG_NOTE_KIND (link) == REG_DEAD
- && GET_CODE (XEXP (link, 0)) == REG
- && REGNO (XEXP (link, 0)) < FIRST_PSEUDO_REGISTER)
- {
- int first_regno = REGNO (XEXP (link, 0));
- int last_regno
- = (first_regno
- + HARD_REGNO_NREGS (first_regno,
- GET_MODE (XEXP (link, 0))));
-
- for (i = first_regno; i < last_regno; i++)
- SET_HARD_REG_BIT (pending_dead_regs, i);
- }
-
- note_stores (PATTERN (real_insn), update_live_status);
-
- /* If any registers were unused after this insn, kill them.
- These notes will always be accurate. */
- for (link = REG_NOTES (real_insn); link; link = XEXP (link, 1))
- if (REG_NOTE_KIND (link) == REG_UNUSED
- && GET_CODE (XEXP (link, 0)) == REG
- && REGNO (XEXP (link, 0)) < FIRST_PSEUDO_REGISTER)
- {
- int first_regno = REGNO (XEXP (link, 0));
- int last_regno
- = (first_regno
- + HARD_REGNO_NREGS (first_regno,
- GET_MODE (XEXP (link, 0))));
-
- for (i = first_regno; i < last_regno; i++)
- CLEAR_HARD_REG_BIT (current_live_regs, i);
- }
- }
-
- else if (GET_CODE (real_insn) == CODE_LABEL)
- {
- /* 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);
- }
-
- /* The beginning of the epilogue corresponds to the end of the
- RTL chain when there are no epilogue insns. Certain resources
- are implicitly required at that point. */
- else if (GET_CODE (real_insn) == NOTE
- && NOTE_LINE_NUMBER (real_insn) == NOTE_INSN_EPILOGUE_BEG)
- IOR_HARD_REG_SET (current_live_regs, start_of_epilogue_needs.regs);
- }
-
- COPY_HARD_REG_SET (res->regs, current_live_regs);
- tinfo->block = b;
- tinfo->bb_tick = bb_ticks[b];
- }
- else
- /* We didn't find the start of a basic block. Assume everything
- in use. This should happen only extremely rarely. */
- SET_HARD_REG_SET (res->regs);
-
- CLEAR_RESOURCE (&set);
- CLEAR_RESOURCE (&needed);
-
- 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
- anything used but not set before the branch. The only things that are
- live are those that are live using the above test and the test below. */
-
- if (jump_insn)
- {
- struct resources new_resources;
- rtx stop_insn = next_active_insn (jump_insn);
-
- mark_target_live_regs (next_active_insn (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);
-
- COPY_HARD_REG_SET (scratch, needed.regs);
- AND_COMPL_HARD_REG_SET (scratch, set.regs);
- IOR_HARD_REG_SET (new_resources.regs, scratch);
-
- mark_set_resources (insn, &set, 0, 1);
- }
-
- AND_HARD_REG_SET (res->regs, new_resources.regs);
- }
-
- COPY_HARD_REG_SET (tinfo->live_regs, res->regs);
-}
-\f