📄 flow.c
字号:
on certain machines which restore regs from stack frames rather than from the jmpbuf. But we don't need to do this for the user's variables, since ANSI says only volatile variables need this. */#ifdef LONGJMP_RESTORE_FROM_STACK for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++) if (regs_live_at_setjmp[i / REGSET_ELT_BITS] & (1 << (i % REGSET_ELT_BITS)) && regno_reg_rtx[i] != 0 && ! REG_USERVAR_P (regno_reg_rtx[i])) { reg_live_length[i] = -1; reg_basic_block[i] = -1; }#endif#endif /* We have a problem with any pseudoreg that lives across the setjmp. ANSI says that if a user variable does not change in value between the setjmp and the longjmp, then the longjmp preserves it. This includes longjmp from a place where the pseudo appears dead. (In principle, the value still exists if it is in scope.) If the pseudo goes in a hard reg, some other value may occupy that hard reg where this pseudo is dead, thus clobbering the pseudo. Conclusion: such a pseudo must not go in a hard reg. */ for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++) if (regs_live_at_setjmp[i / REGSET_ELT_BITS] & (1 << (i % REGSET_ELT_BITS)) && regno_reg_rtx[i] != 0) { reg_live_length[i] = -1; reg_basic_block[i] = -1; } obstack_free (&flow_obstack, 0);}/* Subroutines of life analysis. *//* Allocate the permanent data structures that represent the results of life analysis. Not static since used also for stupid life analysis. */voidallocate_for_life_analysis (){ register int i; register regset tem; regset_size = ((max_regno + REGSET_ELT_BITS - 1) / REGSET_ELT_BITS); regset_bytes = regset_size * sizeof (*(regset)0); reg_n_refs = (short *) oballoc (max_regno * sizeof (short)); bzero (reg_n_refs, max_regno * sizeof (short)); reg_n_sets = (short *) oballoc (max_regno * sizeof (short)); bzero (reg_n_sets, max_regno * sizeof (short)); reg_n_deaths = (short *) oballoc (max_regno * sizeof (short)); bzero (reg_n_deaths, max_regno * sizeof (short)); reg_first_use = (rtx *) oballoc (max_regno * sizeof (rtx)); bzero (reg_first_use, max_regno * sizeof (rtx)); reg_live_length = (int *) oballoc (max_regno * sizeof (int)); bzero (reg_live_length, max_regno * sizeof (int)); reg_n_calls_crossed = (int *) oballoc (max_regno * sizeof (int)); bzero (reg_n_calls_crossed, max_regno * sizeof (int)); reg_basic_block = (short *) oballoc (max_regno * sizeof (short)); for (i = 0; i < max_regno; i++) reg_basic_block[i] = REG_BLOCK_UNKNOWN; basic_block_live_at_start = (regset *) oballoc (n_basic_blocks * sizeof (regset)); tem = (regset) oballoc (n_basic_blocks * regset_bytes); bzero (tem, n_basic_blocks * regset_bytes); init_regset_vector (basic_block_live_at_start, tem, n_basic_blocks, regset_bytes); regs_live_at_setjmp = (regset) oballoc (regset_bytes); bzero (regs_live_at_setjmp, regset_bytes);}/* Make each element of VECTOR point at a regset, taking the space for all those regsets from SPACE. SPACE is of type regset, but it is really as long as NELTS regsets. BYTES_PER_ELT is the number of bytes in one regset. */static voidinit_regset_vector (vector, space, nelts, bytes_per_elt) regset *vector; regset space; int nelts; int bytes_per_elt;{ register int i; register regset p = space; for (i = 0; i < nelts; i++) { vector[i] = p; p += bytes_per_elt / sizeof (*p); }}/* Compute the registers live at the beginning of a basic block from those live at the end. When called, OLD contains those live at the end. On return, it contains those live at the beginning. FIRST and LAST are the first and last insns of the basic block. FINAL is nonzero if we are doing the final pass which is not for computing the life info (since that has already been done) but for acting on it. On this pass, we delete dead stores, set up the logical links and dead-variables lists of instructions, and merge instructions for autoincrement and autodecrement addresses. SIGNIFICANT is nonzero only the first time for each basic block. If it is nonzero, it points to a regset in which we store a 1 for each register that is set within the block. BNUM is the number of the basic block. */static voidpropagate_block (old, first, last, final, significant, bnum) register regset old; rtx first; rtx last; int final; regset significant; int bnum;{ register rtx insn; rtx prev; regset live; regset dead; /* The following variables are used only if FINAL is nonzero. */ /* This vector gets one element for each reg that has been live at any point in the basic block that has been scanned so far. SOMETIMES_MAX says how many elements are in use so far. In each element, OFFSET is the byte-number within a regset for the register described by the element, and BIT is a mask for that register's bit within the byte. */ register struct foo { short offset; short bit; } *regs_sometimes_live; int sometimes_max = 0; /* This regset has 1 for each reg that we have seen live so far. It and REGS_SOMETIMES_LIVE are updated together. */ regset maxlive; loop_depth = basic_block_loop_depth[bnum]; dead = (regset) alloca (regset_bytes); live = (regset) alloca (regset_bytes); if (final) { register int i, offset, bit; maxlive = (regset) alloca (regset_bytes); bcopy (old, maxlive, regset_bytes); regs_sometimes_live = (struct foo *) alloca (max_regno * sizeof (struct foo)); /* Process the regs live at the end of the block. Enter them in MAXLIVE and REGS_SOMETIMES_LIVE. Also mark them as not local to any one basic block. */ for (offset = 0, i = 0; offset < regset_size; offset++) for (bit = 1; bit; bit <<= 1, i++) { if (i == max_regno) break; if (old[offset] & bit) { reg_basic_block[i] = REG_BLOCK_GLOBAL; regs_sometimes_live[sometimes_max].offset = offset; regs_sometimes_live[sometimes_max].bit = i % REGSET_ELT_BITS; sometimes_max++; } } } /* Include any notes at the end of the block in the scan. This is in case the block ends with a call to setjmp. */ while (NEXT_INSN (last) != 0 && GET_CODE (NEXT_INSN (last)) == NOTE) last = NEXT_INSN (last); /* Scan the block an insn at a time from end to beginning. */ for (insn = last; ; insn = prev) { prev = PREV_INSN (insn); /* If this is a call to `setjmp' et al, warn if any non-volatile datum is live. */ if (final && GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP) { int i; for (i = 0; i < regset_size; i++) regs_live_at_setjmp[i] |= old[i]; } /* Update the life-status of regs for this insn. First DEAD gets which regs are set in this insn then LIVE gets which regs are used in this insn. Then the regs live before the insn are those live after, with DEAD regs turned off, and then LIVE regs turned on. */ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN) { register int i; rtx note = find_reg_note (insn, REG_RETVAL, 0); /* If an instruction consists of just dead store(s) on final pass, "delete" it by turning it into a NOTE of type NOTE_INSN_DELETED. We could really delete it with delete_insn, but that can cause trouble for first or last insn in a basic block. */ if (final && insn_dead_p (PATTERN (insn), old, 1) /* Don't delete something that refers to volatile storage! */ && ! INSN_VOLATILE (insn)) { rtx oldpat = PATTERN (insn); PUT_CODE (insn, NOTE); NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; /* If this insn is copying the return value from a library call, delete the entire library call. */ if (note && libcall_dead_p (oldpat, old)) { rtx first = XEXP (note, 0); rtx prev = insn; while (INSN_DELETED_P (first)) first = NEXT_INSN (first); while (prev != first) { prev = PREV_INSN (prev); PUT_CODE (prev, NOTE); NOTE_LINE_NUMBER (prev) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (prev) = 0; } } goto flushed; } for (i = 0; i < regset_size; i++) { dead[i] = 0; /* Faster than bzero here */ live[i] = 0; /* since regset_size is usually small */ } /* See if this is an increment or decrement that can be merged into a following memory address. */#ifdef AUTO_INC_DEC { register rtx x = PATTERN (insn); /* Does this instruction increment or decrement a register? */ if (final && GET_CODE (x) == SET && GET_CODE (SET_DEST (x)) == REG && (GET_CODE (SET_SRC (x)) == PLUS || GET_CODE (SET_SRC (x)) == MINUS) && XEXP (SET_SRC (x), 0) == SET_DEST (x) && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT /* Ok, look for a following memory ref we can combine with. If one is found, change the memory ref to a PRE_INC or PRE_DEC, cancel this insn, and return 1. Return 0 if nothing has been done. */ && try_pre_increment_1 (insn)) goto flushed; }#endif /* AUTO_INC_DEC */ /* If this is not the final pass, and this insn is copying the value of a library call and it's dead, don't scan the insns that perform the library call, so that the call's arguments are not marked live. */ if (note && insn_dead_p (PATTERN (insn), old, 1) && libcall_dead_p (PATTERN (insn), old)) { /* Mark the dest reg as `significant'. */ mark_set_regs (old, dead, PATTERN (insn), 0, significant); insn = XEXP (note, 0); prev = PREV_INSN (insn); } else if (GET_CODE (PATTERN (insn)) == SET && SET_DEST (PATTERN (insn)) == stack_pointer_rtx && GET_CODE (SET_SRC (PATTERN (insn))) == PLUS && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx && GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == CONST_INT) /* We have an insn to pop a constant amount off the stack. (Such insns use PLUS regardless of the direction of the stack, and any insn to adjust the stack by a constant is always a pop.) These insns, if not dead stores, have no effect on life. */ ; else { /* LIVE gets the regs used in INSN; DEAD gets those set by it. */ mark_set_regs (old, dead, PATTERN (insn), final ? insn : 0, significant); mark_used_regs (old, live, PATTERN (insn), final, insn); /* Update OLD for the registers used or set. */ for (i = 0; i < regset_size; i++) { old[i] &= ~dead[i]; old[i] |= live[i]; } if (GET_CODE (insn) == CALL_INSN) { register int i; /* Each call clobbers all call-clobbered regs. Note that the function-value reg is one of these, and mark_set_regs has already had a chance to handle it. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (call_used_regs[i]) dead[i / REGSET_ELT_BITS] |= (1 << (i % REGSET_ELT_BITS)); /* The stack ptr is used (honorarily) by a CALL insn. */ live[STACK_POINTER_REGNUM / REGSET_ELT_BITS] |= (1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS)); } /* Update OLD for the registers used or set. */ for (i = 0; i < regset_size; i++) { old[i] &= ~dead[i]; old[i] |= live[i]; } if (GET_CODE (insn) == CALL_INSN && final) { /* Any regs live at the time of a call instruction must not go in a register clobbered by calls. Find all regs now live and record this for them. */ register struct foo *p = regs_sometimes_live; for (i = 0; i < sometimes_max; i++, p++) if (old[p->offset] & (1 << p->bit)) reg_n_calls_crossed[p->offset * REGSET_ELT_BITS + p->bit]+= 1; } } /* On final pass, add any additional sometimes-live regs into MAXLIVE and REGS_SOMETIMES_LIVE. Also update counts of how many insns each reg is live at. */ if (final) { for (i = 0; i < regset_size; i++) { register int diff = live[i] & ~maxlive[i]; if (diff) { register int regno; maxlive[i] |= diff; for (regno = 0; diff && regno < REGSET_ELT_BITS; regno++) if (diff & (1 << regno)) { regs_sometimes_live[sometimes_max].offset = i; regs_sometimes_live[sometimes_max].bit = regno; diff &= ~ (1 << regno); sometimes_max++; } } } { register struct foo *p = regs_sometimes_live; for (i = 0; i < sometimes_max; i++, p++) { if (old[p->offset] & (1 << p->bit)) reg_live_length[p->offset * REGSET_ELT_BITS + p->bit]++; } } } } flushed: ; if (insn == first) break; }}/* Return 1 if X (the body of an insn, or part of it) is just dead stores (SET expressions whose destinations are registers dead after the insn). NEEDED is the regset that says which regs are alive after the insn. */static intinsn_dead_p (x, needed, strict_low_ok) rtx x; regset needed; int strict_low_ok;{ register RTX_CODE code = GET_CODE (x);#if 0 /* Make sure insns to set the stack pointer are never deleted. */ needed[STACK_POINTER_REGNUM / REGSET_ELT_BITS] |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);#endif /* If setting something that's a reg or part of one, see if that register's altered value will be live. */ if (code == SET) { register rtx r = SET_DEST (x); /* A SET that is a subroutine call cannot be dead. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -