📄 reg-stack.c
字号:
clobber_reg[n_clobbers] = reg; n_clobbers++; } } } /* Enforce rule #4: Output operands must specifically indicate which reg an output appears in after an asm. "=f" is not allowed: the operand constraints must select a class with a single reg. Also enforce rule #5: Output operands must start at the top of the reg-stack: output operands may not "skip" a reg. */ bzero ((char *) reg_used_as_output, sizeof (reg_used_as_output)); for (i = 0; i < n_outputs; i++) if (STACK_REG_P (operands[i])) if (reg_class_size[(int) operand_class[i]] != 1) { error_for_asm (insn, "Output constraint %d must specify a single register", i); malformed_asm = 1; } else reg_used_as_output[REGNO (operands[i])] = 1; /* Search for first non-popped reg. */ for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) if (! reg_used_as_output[i]) break; /* If there are any other popped regs, that's an error. */ for (; i < LAST_STACK_REG + 1; i++) if (reg_used_as_output[i]) break; if (i != LAST_STACK_REG + 1) { error_for_asm (insn, "Output regs must be grouped at top of stack"); malformed_asm = 1; } /* Enforce rule #2: All implicitly popped input regs must be closer to the top of the reg-stack than any input that is not implicitly popped. */ bzero ((char *) implicitly_dies, sizeof (implicitly_dies)); for (i = first_input; i < first_input + n_inputs; i++) if (STACK_REG_P (operands[i])) { /* An input reg is implicitly popped if it is tied to an output, or if there is a CLOBBER for it. */ int j; for (j = 0; j < n_clobbers; j++) if (operands_match_p (clobber_reg[j], operands[i])) break; if (j < n_clobbers || operand_matches[i] >= 0) implicitly_dies[REGNO (operands[i])] = 1; } /* Search for first non-popped reg. */ for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++) if (! implicitly_dies[i]) break; /* If there are any other popped regs, that's an error. */ for (; i < LAST_STACK_REG + 1; i++) if (implicitly_dies[i]) break; if (i != LAST_STACK_REG + 1) { error_for_asm (insn, "Implicitly popped regs must be grouped at top of stack"); malformed_asm = 1; } /* Enfore rule #3: If any input operand uses the "f" constraint, all output constraints must use the "&" earlyclobber. ??? Detect this more deterministically by having constraint_asm_operands record any earlyclobber. */ for (i = first_input; i < first_input + n_inputs; i++) if (operand_matches[i] == -1) { int j; 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])) == NULL_RTX) 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. */static voidrecord_reg_life_pat (pat, src, dest, douse) rtx pat; HARD_REG_SET *src, *dest; int douse;{ register char *fmt; register int i; if (STACK_REG_P (pat) || GET_CODE (pat) == SUBREG && STACK_REG_P (SUBREG_REG (pat))) { if (src) mark_regs_pat (pat, src); if (dest) mark_regs_pat (pat, dest); return; } if (GET_CODE (pat) == SET) { record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest, 0); record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR, 0); return; } /* We don't need to consider either of these cases. */ if (GET_CODE (pat) == USE && !douse || 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, 0); } else if (fmt[i] == 'e') record_reg_life_pat (XEXP (pat, i), src, dest, 0); }}/* 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; } { HARD_REG_SET src, dest; int regno; CLEAR_HARD_REG_SET (src); CLEAR_HARD_REG_SET (dest); if (GET_CODE (insn) == CALL_INSN) for (note = CALL_INSN_FUNCTION_USAGE (insn); note; note = XEXP (note, 1)) if (GET_CODE (XEXP (note, 0)) == USE) record_reg_life_pat (SET_DEST (XEXP (note, 0)), &src, NULL_PTR, 0); record_reg_life_pat (PATTERN (insn), &src, &dest, 0); 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, 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, DFmode), REG_NOTES (insn)); } if (GET_CODE (insn) == CALL_INSN) { int reg; /* 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(). */ for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++) if (! TEST_HARD_REG_BIT (dest, reg) && 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, 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); } AND_COMPL_HARD_REG_SET (regstack->reg_set, dest); IOR_HARD_REG_SET (regstack->reg_set, src); }}/* 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; rtx label_value_list = 0; /* 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, but that these are not the same as those selected in flow.c. */ code = GET_CODE (insn); if (code == CODE_LABEL || (prev_code != INSN && prev_code != CALL_INSN && prev_code != CODE_LABEL && GET_RTX_CLASS (code) == 'i')) { block_begin[++block] = insn; block_end[block] = insn; block_drops_in[block] = prev_code != BARRIER; } else if (GET_RTX_CLASS (code) == 'i') block_end[block] = insn; if (GET_RTX_CLASS (code) == 'i') { rtx note; /* Make a list of all labels referred to other than by jumps. */ for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) if (REG_NOTE_KIND (note) == REG_LABEL) label_value_list = gen_rtx (EXPR_LIST, VOIDmode, XEXP (note, 0), label_value_list); } block_number[INSN_UID (insn)] = block; 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) { rtx pat = PATTERN (insn); int computed_jump = 0; rtx x; if (GET_CODE (pat) == PARALLEL) { int len = XVECLEN (pat, 0); int has_use_labelref = 0; int i; for (i = len - 1; i >= 0; i--) if (GET_CODE (XVECEXP (pat, 0, i)) == USE && GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == LABEL_REF) has_use_labelref = 1; if (! has_use_labelref) for (i = len - 1; i >= 0; i--) if (GET_CODE (XVECEXP (pat, 0, i)) == SET && SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx && uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i)))) computed_jump = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -