📄 flow.c
字号:
Keep doing this until, in one pass, no blocks have been marked. Then blocks_live and blocks_marked are identical and correct. In addition, all jumps actually reachable have been marked. */ while (something_marked) { something_marked = 0; for (i = 0; i < n_basic_blocks; i++) if (block_live[i] && !block_marked[i]) { block_marked[i] = 1; something_marked = 1; if (i + 1 < n_basic_blocks && basic_block_drops_in[i + 1]) block_live[i + 1] = 1; insn = basic_block_end[i]; if (GET_CODE (insn) == JUMP_INSN) mark_label_ref (PATTERN (insn), insn, 0); } } /* Now delete the code for any basic blocks that can't be reached. They can occur because jump_optimize does not recognize unreachable loops as unreachable. */ for (i = 0; i < n_basic_blocks; i++) if (!block_live[i]) { insn = basic_block_head[i]; while (1) { if (GET_CODE (insn) == BARRIER) abort (); if (GET_CODE (insn) != NOTE) { PUT_CODE (insn, NOTE); NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; } if (insn == basic_block_end[i]) { /* BARRIERs are between basic blocks, not part of one. Delete a BARRIER if the preceding jump is deleted. We cannot alter a BARRIER into a NOTE because it is too short; but we can really delete it because it is not part of a basic block. */ if (NEXT_INSN (insn) != 0 && GET_CODE (NEXT_INSN (insn)) == BARRIER) delete_insn (NEXT_INSN (insn)); break; } insn = NEXT_INSN (insn); } /* Each time we delete some basic blocks, see if there is a jump around them that is being turned into a no-op. If so, delete it. */ if (block_live[i - 1]) { register int j; for (j = i; j < n_basic_blocks; j++) if (block_live[j]) { rtx label; insn = basic_block_end[i - 1]; if (GET_CODE (insn) == JUMP_INSN /* An unconditional jump is the only possibility we must check for, since a conditional one would make these blocks live. */ && simplejump_p (insn) && (label = XEXP (SET_SRC (PATTERN (insn)), 0), 1) && INSN_UID (label) != 0 && BLOCK_NUM (label) == j) { PUT_CODE (insn, NOTE); NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; if (GET_CODE (NEXT_INSN (insn)) != BARRIER) abort (); delete_insn (NEXT_INSN (insn)); } break; } } } }}/* Check expression X for label references; if one is found, add INSN to the label's chain of references. CHECKDUP means check for and avoid creating duplicate references from the same insn. Such duplicates do no serious harm but can slow life analysis. CHECKDUP is set only when duplicates are likely. */static voidmark_label_ref (x, insn, checkdup) rtx x, insn; int checkdup;{ register RTX_CODE code = GET_CODE (x); register int i; register char *fmt; if (code == LABEL_REF) { register rtx label = XEXP (x, 0); register rtx y; if (GET_CODE (label) != CODE_LABEL) abort (); /* If the label was never emitted, this insn is junk, but avoid a crash trying to refer to BLOCK_NUM (label). This can happen as a result of a syntax error and a diagnostic has already been printed. */ if (INSN_UID (label) == 0) return; CONTAINING_INSN (x) = insn; /* if CHECKDUP is set, check for duplicate ref from same insn and don't insert. */ if (checkdup) for (y = LABEL_REFS (label); y != label; y = LABEL_NEXTREF (y)) if (CONTAINING_INSN (y) == insn) return; LABEL_NEXTREF (x) = LABEL_REFS (label); LABEL_REFS (label) = x; block_live_static[BLOCK_NUM (label)] = 1; return; } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') mark_label_ref (XEXP (x, i), insn, 0); if (fmt[i] == 'E') { register int j; for (j = 0; j < XVECLEN (x, i); j++) mark_label_ref (XVECEXP (x, i, j), insn, 1); } }}/* Determine the which registers are live at the start of each basic block of the function whose first insn is F. NREGS is the number of registers used in F. We allocate the vector basic_block_live_at_start and the regsets that it points to, and fill them with the data. regset_size and regset_bytes are also set here. */static voidlife_analysis (f, nregs) rtx f; int nregs;{ register regset tem; int first_pass; int changed; /* For each basic block, a bitmask of regs live on exit from the block. */ regset *basic_block_live_at_end; /* For each basic block, a bitmask of regs live on entry to a successor-block of this block. If this does not match basic_block_live_at_end, that must be updated, and the block must be rescanned. */ regset *basic_block_new_live_at_end; /* For each basic block, a bitmask of regs whose liveness at the end of the basic block can make a difference in which regs are live on entry to the block. These are the regs that are set within the basic block, possibly excluding those that are used after they are set. */ regset *basic_block_significant; register int i; rtx insn; struct obstack flow_obstack; obstack_init (&flow_obstack); max_regno = nregs; bzero (regs_ever_live, sizeof regs_ever_live); /* Allocate and zero out many data structures that will record the data from lifetime analysis. */ allocate_for_life_analysis (); reg_next_use = (rtx *) alloca (nregs * sizeof (rtx)); bzero (reg_next_use, nregs * sizeof (rtx)); /* Set up several regset-vectors used internally within this function. Their meanings are documented above, with their declarations. */ basic_block_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset)); /* Don't use alloca since that leads to a crash rather than an error message if there isn't enough space. Don't use oballoc since we may need to allocate other things during this function on the temporary obstack. */ tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); bzero (tem, n_basic_blocks * regset_bytes); init_regset_vector (basic_block_live_at_end, tem, n_basic_blocks, regset_bytes); basic_block_new_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset)); tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); bzero (tem, n_basic_blocks * regset_bytes); init_regset_vector (basic_block_new_live_at_end, tem, n_basic_blocks, regset_bytes); basic_block_significant = (regset *) alloca (n_basic_blocks * sizeof (regset)); tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); bzero (tem, n_basic_blocks * regset_bytes); init_regset_vector (basic_block_significant, tem, n_basic_blocks, regset_bytes); /* Record which insns refer to any volatile memory or for any reason can't be deleted just because they are dead stores. Also, delete any insns that copy a register to itself. */ for (insn = f; insn; insn = NEXT_INSN (insn)) { enum rtx_code code1 = GET_CODE (insn); if (code1 == CALL_INSN) INSN_VOLATILE (insn) = 1; else if (code1 == INSN || code1 == JUMP_INSN) { if (GET_CODE (PATTERN (insn)) == SET && GET_CODE (SET_DEST (PATTERN (insn))) == REG && GET_CODE (SET_SRC (PATTERN (insn))) == REG && REGNO (SET_DEST (PATTERN (insn))) == REGNO (SET_SRC (PATTERN (insn)))) { PUT_CODE (insn, NOTE); NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; } else if (GET_CODE (PATTERN (insn)) != USE) INSN_VOLATILE (insn) = volatile_refs_p (PATTERN (insn)); /* A SET that makes space on the stack cannot be dead. (Such SETs occur only for allocating variable-size data, so they will always have a PLUS or MINUS according to the direction of stack growth.) Even if this function never uses this stack pointer value, signal handlers do! */ else if (code1 == INSN && GET_CODE (PATTERN (insn)) == SET && SET_DEST (PATTERN (insn)) == stack_pointer_rtx#ifdef STACK_GROWS_DOWNWARD && GET_CODE (SET_SRC (PATTERN (insn))) == MINUS#else && GET_CODE (SET_SRC (PATTERN (insn))) == PLUS#endif && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx) INSN_VOLATILE (insn) = 1; } } if (n_basic_blocks > 0)#ifdef EXIT_IGNORE_STACK if (! (EXIT_IGNORE_STACK) || ! frame_pointer_needed)#endif { /* If exiting needs the right stack value, consider the stack pointer live at the end of the function. */ basic_block_live_at_end[n_basic_blocks - 1] [STACK_POINTER_REGNUM / REGSET_ELT_BITS] |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS); basic_block_new_live_at_end[n_basic_blocks - 1] [STACK_POINTER_REGNUM / REGSET_ELT_BITS] |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS); } /* Propagate life info through the basic blocks around the graph of basic blocks. This is a relaxation process: each time a new register is live at the end of the basic block, we must scan the block to determine which registers are, as a consequence, live at the beginning of that block. These registers must then be marked live at the ends of all the blocks that can transfer control to that block. The process continues until it reaches a fixed point. */ first_pass = 1; changed = 1; while (changed) { changed = 0; for (i = n_basic_blocks - 1; i >= 0; i--) { int consider = first_pass; int must_rescan = first_pass; register int j; /* Set CONSIDER if this block needs thinking about at all (that is, if the regs live now at the end of it are not the same as were live at the end of it when we last thought about it). Set must_rescan if it needs to be thought about instruction by instruction (that is, if any additional reg that is live at the end now but was not live there before is one of the significant regs of this basic block). */ for (j = 0; j < regset_size; j++) { register int x = basic_block_new_live_at_end[i][j] & ~basic_block_live_at_end[i][j]; if (x) consider = 1; if (x & basic_block_significant[i][j]) { must_rescan = 1; consider = 1; break; } } if (! consider) continue; /* The live_at_start of this block may be changing, so another pass will be required after this one. */ changed = 1; if (! must_rescan) { /* No complete rescan needed; just record those variables newly known live at end as live at start as well. */ for (j = 0; j < regset_size; j++) { register int x = basic_block_new_live_at_end[i][j] & ~basic_block_live_at_end[i][j]; basic_block_live_at_start[i][j] |= x; basic_block_live_at_end[i][j] |= x; } } else { /* Update the basic_block_live_at_start by propagation backwards through the block. */ bcopy (basic_block_new_live_at_end[i], basic_block_live_at_end[i], regset_bytes); bcopy (basic_block_live_at_end[i], basic_block_live_at_start[i], regset_bytes); propagate_block (basic_block_live_at_start[i], basic_block_head[i], basic_block_end[i], 0, first_pass ? basic_block_significant[i] : 0, i); } { register rtx jump, head; /* Update the basic_block_new_live_at_end's of the block that falls through into this one (if any). */ head = basic_block_head[i]; jump = PREV_INSN (head); if (basic_block_drops_in[i]) { register int from_block = BLOCK_NUM (jump); register int j; for (j = 0; j < regset_size; j++) basic_block_new_live_at_end[from_block][j] |= basic_block_live_at_start[i][j]; } /* Update the basic_block_new_live_at_end's of all the blocks that jump to this one. */ if (GET_CODE (head) == CODE_LABEL) for (jump = LABEL_REFS (head); jump != head; jump = LABEL_NEXTREF (jump)) { register int from_block = BLOCK_NUM (CONTAINING_INSN (jump)); register int j; for (j = 0; j < regset_size; j++) basic_block_new_live_at_end[from_block][j] |= basic_block_live_at_start[i][j]; } }#ifdef USE_C_ALLOCA alloca (0);#endif } first_pass = 0; }#if 0 /* This seems unnecessary; life at start of function shouldn't mean that the reg is live in more than one basic block. */ /* Process the regs live at the beginning of the function. Mark them as not local to any one basic block. */ if (n_basic_blocks > 0) for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) if (basic_block_live_at_start[0][i / REGSET_ELT_BITS] & (1 << (i % REGSET_ELT_BITS))) reg_basic_block[i] = REG_BLOCK_GLOBAL;#endif /* Now the life information is accurate. Make one more pass over each basic block to delete dead stores, create autoincrement addressing and record how many times each register is used, is set, or dies. To save time, we operate directly in basic_block_live_at_end[i], thus destroying it (in fact, converting it into a copy of basic_block_live_at_start[i]). This is ok now because basic_block_live_at_end[i] is no longer used past this point. */ for (i = 0; i < n_basic_blocks; i++) { propagate_block (basic_block_live_at_end[i], basic_block_head[i], basic_block_end[i], 1, 0, i);#ifdef USE_C_ALLOCA alloca (0);#endif }#if 0 /* Something live during a setjmp should not be put in a register
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -