📄 reorg.c
字号:
{ rtx temp = PREV_INSN (PREV_INSN (insn)); end_of_function_label = gen_label_rtx (); LABEL_NUSES (end_of_function_label) = 0; /* Put the label before an USE insns that may proceed the RETURN insn. */ while (GET_CODE (temp) == USE) temp = PREV_INSN (temp); emit_label_after (end_of_function_label, temp); } else if (GET_CODE (insn) == CODE_LABEL) end_of_function_label = insn; else { /* Otherwise, make a new label and emit a RETURN and BARRIER, if needed. */ end_of_function_label = gen_label_rtx (); LABEL_NUSES (end_of_function_label) = 0; emit_label (end_of_function_label);#ifdef HAVE_return if (HAVE_return) { /* The return we make may have delay slots too. */ rtx insn = gen_return (); insn = emit_jump_insn (insn); emit_barrier (); if (num_delay_slots (insn) > 0) obstack_ptr_grow (&unfilled_slots_obstack, insn); }#endif } /* Show one additional use for this label so it won't go away until we are done. */ ++LABEL_NUSES (end_of_function_label); return end_of_function_label;}/* Put INSN and LIST together in a SEQUENCE rtx of LENGTH, and replace the pattern of INSN with the SEQUENCE. Chain the insns so that NEXT_INSN of each insn in the sequence points to the next and NEXT_INSN of the last insn in the sequence points to the first insn after the sequence. Similarly for PREV_INSN. This makes it easier to scan all insns. Returns the SEQUENCE that replaces INSN. */static rtxemit_delay_sequence (insn, list, length, avail) rtx insn; rtx list; int length; int avail;{ register int i = 1; register rtx li; int had_barrier = 0; /* Allocate the the rtvec to hold the insns and the SEQUENCE. */ rtvec seqv = rtvec_alloc (length + 1); rtx seq = gen_rtx (SEQUENCE, VOIDmode, seqv); rtx seq_insn = make_insn_raw (seq); rtx first = get_insns (); rtx last = get_last_insn (); /* Make a copy of the insn having delay slots. */ rtx delay_insn = copy_rtx (insn); /* If INSN is followed by a BARRIER, delete the BARRIER since it will only confuse further processing. Update LAST in case it was the last insn. We will put the BARRIER back in later. */ if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == BARRIER) { delete_insn (NEXT_INSN (insn)); last = get_last_insn (); had_barrier = 1; } /* Splice our SEQUENCE into the insn stream where INSN used to be. */ NEXT_INSN (seq_insn) = NEXT_INSN (insn); PREV_INSN (seq_insn) = PREV_INSN (insn); if (insn != last) PREV_INSN (NEXT_INSN (seq_insn)) = seq_insn; if (insn != first) NEXT_INSN (PREV_INSN (seq_insn)) = seq_insn; /* Note the calls to set_new_first_and_last_insn must occur after SEQ_INSN has been completely spliced into the insn stream. Otherwise CUR_INSN_UID will get set to an incorrect value because set_new_first_and_last_insn will not find SEQ_INSN in the chain. */ if (insn == last) set_new_first_and_last_insn (first, seq_insn); if (insn == first) set_new_first_and_last_insn (seq_insn, last); /* Build our SEQUENCE and rebuild the insn chain. */ XVECEXP (seq, 0, 0) = delay_insn; INSN_DELETED_P (delay_insn) = 0; PREV_INSN (delay_insn) = PREV_INSN (seq_insn); for (li = list; li; li = XEXP (li, 1), i++) { rtx tem = XEXP (li, 0); rtx note; /* Show that this copy of the insn isn't deleted. */ INSN_DELETED_P (tem) = 0; XVECEXP (seq, 0, i) = tem; PREV_INSN (tem) = XVECEXP (seq, 0, i - 1); NEXT_INSN (XVECEXP (seq, 0, i - 1)) = tem; /* Remove any REG_DEAD notes because we can't rely on them now that the insn has been moved. */ for (note = REG_NOTES (tem); note; note = XEXP (note, 1)) if (REG_NOTE_KIND (note) == REG_DEAD) XEXP (note, 0) = const0_rtx; } NEXT_INSN (XVECEXP (seq, 0, length)) = NEXT_INSN (seq_insn); /* If the previous insn is a SEQUENCE, update the NEXT_INSN pointer on the last insn in that SEQUENCE to point to us. Similarly for the first insn in the following insn if it is a SEQUENCE. */ if (PREV_INSN (seq_insn) && GET_CODE (PREV_INSN (seq_insn)) == INSN && GET_CODE (PATTERN (PREV_INSN (seq_insn))) == SEQUENCE) NEXT_INSN (XVECEXP (PATTERN (PREV_INSN (seq_insn)), 0, XVECLEN (PATTERN (PREV_INSN (seq_insn)), 0) - 1)) = seq_insn; if (NEXT_INSN (seq_insn) && GET_CODE (NEXT_INSN (seq_insn)) == INSN && GET_CODE (PATTERN (NEXT_INSN (seq_insn))) == SEQUENCE) PREV_INSN (XVECEXP (PATTERN (NEXT_INSN (seq_insn)), 0, 0)) = seq_insn; /* If there used to be a BARRIER, put it back. */ if (had_barrier) emit_barrier_after (seq_insn); if (i != length + 1) abort (); return seq_insn;}/* Add INSN to DELAY_LIST and return the head of the new list. The list must be in the order in which the insns are to be executed. */static rtxadd_to_delay_list (insn, delay_list) rtx insn; rtx delay_list;{ /* If we have an empty list, just make a new list element. If INSN has it's block number recorded, clear it since we may be moving the insn to a new block. */ if (delay_list == 0) { struct target_info *tinfo; for (tinfo = target_hash_table[INSN_UID (insn) % TARGET_HASH_PRIME]; tinfo; tinfo = tinfo->next) if (tinfo->uid == INSN_UID (insn)) break; if (tinfo) tinfo->block = -1; return gen_rtx (INSN_LIST, VOIDmode, insn, NULL_RTX); } /* Otherwise this must be an INSN_LIST. Add INSN to the end of the list. */ XEXP (delay_list, 1) = add_to_delay_list (insn, XEXP (delay_list, 1)); return delay_list;} /* Delete INSN from the the delay slot of the insn that it is in. This may produce an insn without anything in its delay slots. */static voiddelete_from_delay_slot (insn) rtx insn;{ rtx trial, seq_insn, seq, prev; rtx delay_list = 0; int i; /* We first must find the insn containing the SEQUENCE with INSN in its delay slot. Do this by finding an insn, TRIAL, where PREV_INSN (NEXT_INSN (TRIAL)) != TRIAL. */ for (trial = insn; PREV_INSN (NEXT_INSN (trial)) == trial; trial = NEXT_INSN (trial)) ; seq_insn = PREV_INSN (NEXT_INSN (trial)); seq = PATTERN (seq_insn); /* Create a delay list consisting of all the insns other than the one we are deleting (unless we were the only one). */ if (XVECLEN (seq, 0) > 2) for (i = 1; i < XVECLEN (seq, 0); i++) if (XVECEXP (seq, 0, i) != insn) delay_list = add_to_delay_list (XVECEXP (seq, 0, i), delay_list); /* Delete the old SEQUENCE, re-emit the insn that used to have the delay list, and rebuild the delay list if non-empty. */ prev = PREV_INSN (seq_insn); trial = XVECEXP (seq, 0, 0); delete_insn (seq_insn); add_insn_after (trial, prev); if (GET_CODE (trial) == JUMP_INSN && (simplejump_p (trial) || GET_CODE (PATTERN (trial)) == RETURN)) emit_barrier_after (trial); /* If there are any delay insns, remit them. Otherwise clear the annul flag. */ if (delay_list) trial = emit_delay_sequence (trial, delay_list, XVECLEN (seq, 0) - 2, 0); else INSN_ANNULLED_BRANCH_P (trial) = 0; INSN_FROM_TARGET_P (insn) = 0; /* Show we need to fill this insn again. */ obstack_ptr_grow (&unfilled_slots_obstack, trial);}/* Delete INSN, a JUMP_INSN. If it is a conditional jump, we must track down the insn that sets CC0 for it and delete it too. */static voiddelete_scheduled_jump (insn) rtx insn;{ /* Delete the insn that sets cc0 for us. On machines without cc0, we could delete the insn that sets the condition code, but it is hard to find it. Since this case is rare anyway, don't bother trying; there would likely be other insns that became dead anyway, which we wouldn't know to delete. */#ifdef HAVE_cc0 if (reg_mentioned_p (cc0_rtx, insn)) { rtx note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX); /* If a reg-note was found, it points to an insn to set CC0. This insn is in the delay list of some other insn. So delete it from the delay list it was in. */ if (note) { if (! FIND_REG_INC_NOTE (XEXP (note, 0), NULL_RTX) && sets_cc0_p (PATTERN (XEXP (note, 0))) == 1) delete_from_delay_slot (XEXP (note, 0)); } else { /* The insn setting CC0 is our previous insn, but it may be in a delay slot. It will be the last insn in the delay slot, if it is. */ rtx trial = previous_insn (insn); if (GET_CODE (trial) == NOTE) trial = prev_nonnote_insn (trial); if (sets_cc0_p (PATTERN (trial)) != 1 || FIND_REG_INC_NOTE (trial, 0)) return; if (PREV_INSN (NEXT_INSN (trial)) == trial) delete_insn (trial); else delete_from_delay_slot (trial); } }#endif delete_insn (insn);}/* Counters for delay-slot filling. */#define NUM_REORG_FUNCTIONS 2#define MAX_DELAY_HISTOGRAM 3#define MAX_REORG_PASSES 2static int num_insns_needing_delays[NUM_REORG_FUNCTIONS][MAX_REORG_PASSES];static int num_filled_delays[NUM_REORG_FUNCTIONS][MAX_DELAY_HISTOGRAM+1][MAX_REORG_PASSES];static int reorg_pass_number;static voidnote_delay_statistics (slots_filled, index) int slots_filled, index;{ num_insns_needing_delays[index][reorg_pass_number]++; if (slots_filled > MAX_DELAY_HISTOGRAM) slots_filled = MAX_DELAY_HISTOGRAM; num_filled_delays[index][slots_filled][reorg_pass_number]++;}#if defined(ANNUL_IFFALSE_SLOTS) || defined(ANNUL_IFTRUE_SLOTS)/* Optimize the following cases: 1. When a conditional branch skips over only one instruction, use an annulling branch and put that insn in the delay slot. Use either a branch that annuls when the condition if true or invert the test with a branch that annuls when the condition is false. This saves insns, since otherwise we must copy an insn from the L1 target. (orig) (skip) (otherwise) Bcc.n L1 Bcc',a L1 Bcc,a L1' insn insn insn2 L1: L1: L1: insn2 insn2 insn2 insn3 insn3 L1': insn3 2. When a conditional branch skips over only one instruction, and after that, it unconditionally branches somewhere else, perform the similar optimization. This saves executing the second branch in the case where the inverted condition is true. Bcc.n L1 Bcc',a L2 insn insn L1: L1: Bra L2 Bra L2 INSN is a JUMP_INSN. This should be expanded to skip over N insns, where N is the number of delay slots required. */static rtxoptimize_skip (insn) register rtx insn;{ register rtx trial = next_nonnote_insn (insn); rtx next_trial = next_active_insn (trial); rtx delay_list = 0; rtx target_label; int flags; flags = get_jump_flags (insn, JUMP_LABEL (insn)); if (trial == 0 || GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) == SEQUENCE || recog_memoized (trial) < 0 || (! eligible_for_annul_false (insn, 0, trial, flags) && ! eligible_for_annul_true (insn, 0, trial, flags))) return 0; /* There are two cases where we are just executing one insn (we assume here that a branch requires only one insn; this should be generalized at some point): Where the branch goes around a single insn or where we have one insn followed by a branch to the same label we branch to. In both of these cases, inverting the jump and annulling the delay slot give the same effect in fewer insns. */ if ((next_trial == next_active_insn (JUMP_LABEL (insn))) || (next_trial != 0 && GET_CODE (next_trial) == JUMP_INSN && JUMP_LABEL (insn) == JUMP_LABEL (next_trial) && (simplejump_p (next_trial) || GET_CODE (PATTERN (next_trial)) == RETURN))) { if (eligible_for_annul_false (insn, 0, trial, flags)) { if (invert_jump (insn, JUMP_LABEL (insn))) INSN_FROM_TARGET_P (trial) = 1; else if (! eligible_for_annul_true (insn, 0, trial, flags)) return 0; } delay_list = add_to_delay_list (trial, NULL_RTX); next_trial = next_active_insn (trial); update_block (trial, trial); delete_insn (trial); /* Also, if we are targeting an unconditional branch, thread our jump to the target of that branch. Don't change this into a RETURN here, because it may not accept what we have in the delay slot. We'll fix this up later. */ if (next_trial && GET_CODE (next_trial) == JUMP_INSN && (simplejump_p (next_trial) || GET_CODE (PATTERN (next_trial)) == RETURN)) { target_label = JUMP_LABEL (next_trial); if (target_label == 0) target_label = find_end_label (); /* Recompute the flags based on TARGET_LABEL since threading
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -