📄 reg-stack.c
字号:
for (j = 0; j < n_outputs; j++) if (operands_match_p (operands[j], operands[i])) { error_for_asm (insn, "Output operand %d must use `&' constraint", j); malformed_asm = 1; } } if (malformed_asm) { /* Avoid further trouble with this insn. */ PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx); PUT_MODE (insn, VOIDmode); return; } /* Process all outputs */ for (i = 0; i < n_outputs; i++) { rtx op = operands[i]; if (! STACK_REG_P (op)) if (stack_regs_mentioned_p (op)) abort (); else continue; /* Each destination is dead before this insn. If the destination is not used after this insn, record this with REG_UNUSED. */ if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op))) REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, op, REG_NOTES (insn)); CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (op)); } /* Process all inputs */ for (i = first_input; i < first_input + n_inputs; i++) { if (! STACK_REG_P (operands[i])) if (stack_regs_mentioned_p (operands[i])) abort (); else continue; /* If an input is dead after the insn, record a death note. But don't record a death note if there is already a death note, or if the input is also an output. */ if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])) && operand_matches[i] == -1 && ! find_regno_note (insn, REG_DEAD, REGNO (operands[i]))) REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, operands[i], REG_NOTES (insn)); SET_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])); }}/* Scan PAT, which is part of INSN, and record registers appearing in a SET_DEST in DEST, and other registers in SRC. This function does not know about SET_DESTs that are both input and output (such as ZERO_EXTRACT) - this cannot happen on a 387. */voidrecord_reg_life_pat (pat, src, dest) rtx pat; HARD_REG_SET *src, *dest;{ register char *fmt; register int i; if (STACK_REG_P (pat)) { if (src) SET_HARD_REG_BIT (*src, REGNO (pat)); if (dest) SET_HARD_REG_BIT (*dest, REGNO (pat)); return; } if (GET_CODE (pat) == SET) { record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest); record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR); return; } /* We don't need to consider either of these cases. */ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER) return; fmt = GET_RTX_FORMAT (GET_CODE (pat)); for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--) { if (fmt[i] == 'E') { register int j; for (j = XVECLEN (pat, i) - 1; j >= 0; j--) record_reg_life_pat (XVECEXP (pat, i, j), src, dest); } else if (fmt[i] == 'e') record_reg_life_pat (XEXP (pat, i), src, dest); }}/* Calculate the number of inputs and outputs in BODY, an asm_operands. N_OPERANDS is the total number of operands, and N_INPUTS and N_OUTPUTS are pointers to ints into which the results are placed. */static voidget_asm_operand_lengths (body, n_operands, n_inputs, n_outputs) rtx body; int n_operands; int *n_inputs, *n_outputs;{ if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)); else if (GET_CODE (body) == ASM_OPERANDS) *n_inputs = ASM_OPERANDS_INPUT_LENGTH (body); else if (GET_CODE (body) == PARALLEL && GET_CODE (XVECEXP (body, 0, 0)) == SET) *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0))); else if (GET_CODE (body) == PARALLEL && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) *n_inputs = ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0)); else abort (); *n_outputs = n_operands - *n_inputs;}/* Scan INSN, which is in BLOCK, and record the life & death of stack registers in REGSTACK. This function is called to process insns from the last insn in a block to the first. The actual scanning is done in record_reg_life_pat. If a register is live after a CALL_INSN, but is not a value return register for that CALL_INSN, then code is emitted to initialize that register. The block_end[] data is kept accurate. Existing death and unset notes for stack registers are deleted before processing the insn. */static voidrecord_reg_life (insn, block, regstack) rtx insn; int block; stack regstack;{ rtx note, *note_link; int n_operands; if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) || INSN_DELETED_P (insn)) return; /* Strip death notes for stack regs from this insn */ note_link = ®_NOTES(insn); for (note = *note_link; note; note = XEXP (note, 1)) if (STACK_REG_P (XEXP (note, 0)) && (REG_NOTE_KIND (note) == REG_DEAD || REG_NOTE_KIND (note) == REG_UNUSED)) *note_link = XEXP (note, 1); else note_link = &XEXP (note, 1); /* Process all patterns in the insn. */ n_operands = asm_noperands (PATTERN (insn)); if (n_operands >= 0) { /* This insn is an `asm' with operands. Decode the operands, decide how many are inputs, and record the life information. */ rtx operands[MAX_RECOG_OPERANDS]; rtx body = PATTERN (insn); int n_inputs, n_outputs; char **constraints = (char **) alloca (n_operands * sizeof (char *)); decode_asm_operands (body, operands, NULL_PTR, constraints, NULL_PTR); get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs); record_asm_reg_life (insn, regstack, operands, constraints, n_inputs, n_outputs); return; } /* An insn referencing a stack reg has a mode of QImode. */ if (GET_MODE (insn) == QImode) { HARD_REG_SET src, dest; int regno; CLEAR_HARD_REG_SET (src); CLEAR_HARD_REG_SET (dest); record_reg_life_pat (PATTERN (insn), &src, &dest); for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++) if (! TEST_HARD_REG_BIT (regstack->reg_set, regno)) { if (TEST_HARD_REG_BIT (src, regno) && ! TEST_HARD_REG_BIT (dest, regno)) REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, FP_mode_reg[regno][(int) DFmode], REG_NOTES (insn)); else if (TEST_HARD_REG_BIT (dest, regno)) REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, FP_mode_reg[regno][(int) DFmode], REG_NOTES (insn)); } AND_COMPL_HARD_REG_SET (regstack->reg_set, dest); IOR_HARD_REG_SET (regstack->reg_set, src); } /* There might be a reg that is live after a function call. Initialize it to zero so that the program does not crash. See comment towards the end of stack_reg_life_analysis(). */ if (GET_CODE (insn) == CALL_INSN) { int reg = FIRST_FLOAT_REG; /* If a stack reg is mentioned in a CALL_INSN, it must be as the return value. */ if (stack_regs_mentioned_p (PATTERN (insn))) reg++; for (; reg <= LAST_STACK_REG; reg++) if (TEST_HARD_REG_BIT (regstack->reg_set, reg)) { rtx init, pat; /* The insn will use virtual register numbers, and so convert_regs is expected to process these. But BLOCK_NUM cannot be used on these insns, because they do not appear in block_number[]. */ pat = gen_rtx (SET, VOIDmode, FP_mode_reg[reg][(int) DFmode], CONST0_RTX (DFmode)); init = emit_insn_after (pat, insn); PUT_MODE (init, QImode); CLEAR_HARD_REG_BIT (regstack->reg_set, reg); /* If the CALL_INSN was the end of a block, move the block_end to point to the new insn. */ if (block_end[block] == insn) block_end[block] = init; } /* Some regs do not survive a CALL */ AND_COMPL_HARD_REG_SET (regstack->reg_set, call_used_reg_set); }}/* Find all basic blocks of the function, which starts with FIRST. For each JUMP_INSN, build the chain of LABEL_REFS on each CODE_LABEL. */static voidfind_blocks (first) rtx first;{ register rtx insn; register int block; register RTX_CODE prev_code = BARRIER; register RTX_CODE code; /* Record where all the blocks start and end. Record which basic blocks control can drop in to. */ block = -1; for (insn = first; insn; insn = NEXT_INSN (insn)) { /* Note that this loop must select the same block boundaries as code in reg_to_stack. */ code = GET_CODE (insn); if (code == CODE_LABEL || (prev_code != INSN && prev_code != CALL_INSN && prev_code != CODE_LABEL && (code == INSN || code == CALL_INSN || code == JUMP_INSN))) { block_begin[++block] = insn; block_end[block] = insn; block_drops_in[block] = prev_code != BARRIER; } else if (code == INSN || code == CALL_INSN || code == JUMP_INSN) block_end[block] = insn; BLOCK_NUM (insn) = block; if (code == CODE_LABEL) LABEL_REFS (insn) = insn; /* delete old chain */ if (code != NOTE) prev_code = code; } if (block + 1 != blocks) abort (); /* generate all label references to the corresponding jump insn */ for (block = 0; block < blocks; block++) { insn = block_end[block]; if (GET_CODE (insn) == JUMP_INSN) record_label_references (insn, PATTERN (insn)); }}/* Determine the which registers are live at the start of each basic block of the function whose first insn is FIRST. First, if the function returns a real_type, mark the function return type as live at each return point, as the RTL may not give any hint that the register is live. Then, start with the last block and work back to the first block. Similarly, work backwards within each block, insn by insn, recording which regs are die and which are used (and therefore live) in the hard reg set of block_stack_in[]. After processing each basic block, if there is a label at the start of the block, propagate the live registers to all jumps to this block. As a special case, if there are regs live in this block, that are not live in a block containing a jump to this label, and the block containing the jump has already been processed, we must propagate this block's entry register life back to the block containing the jump, and restart life analysis from there. In the worst case, this function may traverse the insns REG_STACK_SIZE times. This is necessary, since a jump towards the end of the insns may not know that a reg is live at a target that is early in the insns. So we back up and start over with the new reg live. If there are registers that are live at the start of the function, insns are emitted to initialize these registers. Something similar is done after CALL_INSNs in record_reg_life. */static voidstack_reg_life_analysis (first) rtx first;{ int reg, block; struct stack_def regstack; if (current_function_returns_real && STACK_REG_P (DECL_RTL (DECL_RESULT (current_function_decl)))) { /* Find all RETURN insns and mark them. */ int value_regno = REGNO (DECL_RTL (DECL_RESULT (current_function_decl))); for (block = blocks - 1; block >= 0; block--) if (GET_CODE (block_end[block]) == JUMP_INSN && GET_CODE (PATTERN (block_end[block])) == RETURN) SET_HARD_REG_BIT (block_out_reg_set[block], value_regno); /* Mark of the end of last block if we "fall off" the end of the function into the epilogue. */ if (GET_CODE (block_end[blocks-1]) != JUMP_INSN || GET_CODE (PATTERN (block_end[blocks-1])) == RETURN) SET_HARD_REG_BIT (block_out_reg_set[blocks-1], value_regno); } /* now scan all blocks backward for stack register use */ block = blocks - 1; while (block >= 0) { register rtx insn, prev; /* current register status at last instruction */ COPY_HARD_REG_SET (regstack.reg_set, block_out_reg_set[block]); prev = block_end[block]; do { insn = prev; prev = PREV_INSN (insn); /* If the insn is a CALL_INSN, we need to ensure that everything dies. But otherwise don't process unless there are some stack regs present. */ if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN) record_reg_life (insn, block, ®stack); } while (insn != block_begin[block]); /* Set the state at the start of the block. Mark that no register mapping information known yet. */ COPY_HARD_REG_SET (block_stack_in[block].reg_set, regstack.reg_set); block_stack_in[block].top = -2; /* If there is a label, propagate our register life to all jumps to this label. */ if (GET_CODE (insn) == CODE_LABEL) { register rtx label; int must_restart = 0; for (label = LABEL_REFS (insn); label != insn; label = LABEL_NEXTREF (label)) { int jump_block = BLOCK_NUM (CONTAINING_INSN (label)); if (jump_block < block) IOR_HARD_REG_SET (block_out_reg_set[jump_block], block_stack_in[block].reg_set); else { /* The block containing the jump has already been processed. If there are registers that were not known to be live then, but are live now, we must back up and restart life analysis from that point with the new life information. */ GO_IF_HARD_REG_SUBSET (block_stack_in[block].reg_set, block_out_reg_set[jump_block],
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -