📄 combine.c
字号:
total_sets = 1 + added_sets_1 + added_sets_2; newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets)); XVECEXP (newpat, 0, 0) = old; } if (added_sets_1) XVECEXP (newpat, 0, --total_sets) = (GET_CODE (PATTERN (i1)) == PARALLEL ? gen_rtx (SET, VOIDmode, i1dest, i1src) : PATTERN (i1)); if (added_sets_2) { /* If there is no I1, use I2's body as is. We used to also not do the subst call below if I2 was substituted into I3, but that could lose a simplification. */ if (i1 == 0) XVECEXP (newpat, 0, --total_sets) = i2pat; else /* See comment where i2pat is assigned. */ XVECEXP (newpat, 0, --total_sets) = subst (i2pat, i1dest, i1src, 0, 0); } } /* We come here when we are replacing a destination in I2 with the destination of I3. */ validate_replacement: /* Is the result of combination a valid instruction? */ insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); /* If the result isn't valid, see if it is a PARALLEL of two SETs where the second SET's destination is a register that is unused. In that case, we just need the first SET. This can occur when simplifying a divmod insn. We *must* test for this case here because the code below that splits two independent SETs doesn't handle this case correctly when it updates the register status. Also check the case where the first SET's destination is unused. That would not cause incorrect code, but does cause an unneeded insn to remain. */ if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL && XVECLEN (newpat, 0) == 2 && GET_CODE (XVECEXP (newpat, 0, 0)) == SET && GET_CODE (XVECEXP (newpat, 0, 1)) == SET && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == REG && find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 1))) && ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 1))) && asm_noperands (newpat) < 0) { newpat = XVECEXP (newpat, 0, 0); insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); } else if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL && XVECLEN (newpat, 0) == 2 && GET_CODE (XVECEXP (newpat, 0, 0)) == SET && GET_CODE (XVECEXP (newpat, 0, 1)) == SET && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) == REG && find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 0))) && ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 0))) && asm_noperands (newpat) < 0) { newpat = XVECEXP (newpat, 0, 1); insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); } /* See if this is an XOR. If so, perhaps the problem is that the constant is out of range. Replace it with a complemented XOR with a complemented constant; it might be in range. */ else if (insn_code_number < 0 && GET_CODE (newpat) == SET && GET_CODE (SET_SRC (newpat)) == XOR && GET_CODE (XEXP (SET_SRC (newpat), 1)) == CONST_INT && ((temp = simplify_unary_operation (NOT, GET_MODE (SET_SRC (newpat)), XEXP (SET_SRC (newpat), 1), GET_MODE (SET_SRC (newpat)))) != 0)) { enum machine_mode i_mode = GET_MODE (SET_SRC (newpat)); rtx pat = gen_rtx_combine (SET, VOIDmode, SET_DEST (newpat), gen_unary (NOT, i_mode, gen_binary (XOR, i_mode, XEXP (SET_SRC (newpat), 0), temp))); insn_code_number = recog_for_combine (&pat, i3, &new_i3_notes); if (insn_code_number >= 0) newpat = pat; } /* If we were combining three insns and the result is a simple SET with no ASM_OPERANDS that wasn't recognized, try to split it into two insns. There are two ways to do this. It can be split using a machine-specific method (like when you have an addition of a large constant) or by combine in the function find_split_point. */ if (i1 && insn_code_number < 0 && GET_CODE (newpat) == SET && asm_noperands (newpat) < 0) { rtx m_split, *split; rtx ni2dest = i2dest; /* See if the MD file can split NEWPAT. If it can't, see if letting it use I2DEST as a scratch register will help. In the latter case, convert I2DEST to the mode of the source of NEWPAT if we can. */ m_split = split_insns (newpat, i3); /* We can only use I2DEST as a scratch reg if it doesn't overlap any inputs of NEWPAT. */ /* ??? If I2DEST is not safe, and I1DEST exists, then it would be possible to try that as a scratch reg. This would require adding more code to make it work though. */ if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat)) { /* If I2DEST is a hard register or the only use of a pseudo, we can change its mode. */ if (GET_MODE (SET_DEST (newpat)) != GET_MODE (i2dest) && GET_MODE (SET_DEST (newpat)) != VOIDmode && GET_CODE (i2dest) == REG && (REGNO (i2dest) < FIRST_PSEUDO_REGISTER || (reg_n_sets[REGNO (i2dest)] == 1 && ! added_sets_2 && ! REG_USERVAR_P (i2dest)))) ni2dest = gen_rtx (REG, GET_MODE (SET_DEST (newpat)), REGNO (i2dest)); m_split = split_insns (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, newpat, gen_rtx (CLOBBER, VOIDmode, ni2dest))), i3); } if (m_split && GET_CODE (m_split) == SEQUENCE && XVECLEN (m_split, 0) == 2 && (next_real_insn (i2) == i3 || ! use_crosses_set_p (PATTERN (XVECEXP (m_split, 0, 0)), INSN_CUID (i2)))) { rtx i2set, i3set; rtx newi3pat = PATTERN (XVECEXP (m_split, 0, 1)); newi2pat = PATTERN (XVECEXP (m_split, 0, 0)); i3set = single_set (XVECEXP (m_split, 0, 1)); i2set = single_set (XVECEXP (m_split, 0, 0)); /* In case we changed the mode of I2DEST, replace it in the pseudo-register table here. We can't do it above in case this code doesn't get executed and we do a split the other way. */ if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER) SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest); i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); /* If I2 or I3 has multiple SETs, we won't know how to track register status, so don't use these insns. */ if (i2_code_number >= 0 && i2set && i3set) insn_code_number = recog_for_combine (&newi3pat, i3, &new_i3_notes); if (insn_code_number >= 0) newpat = newi3pat; /* It is possible that both insns now set the destination of I3. If so, we must show an extra use of it. */ if (insn_code_number >= 0 && GET_CODE (SET_DEST (i3set)) == REG && GET_CODE (SET_DEST (i2set)) == REG && REGNO (SET_DEST (i3set)) == REGNO (SET_DEST (i2set))) reg_n_sets[REGNO (SET_DEST (i2set))]++; } /* If we can split it and use I2DEST, go ahead and see if that helps things be recognized. Verify that none of the registers are set between I2 and I3. */ if (insn_code_number < 0 && (split = find_split_point (&newpat, i3)) != 0#ifdef HAVE_cc0 && GET_CODE (i2dest) == REG#endif /* We need I2DEST in the proper mode. If it is a hard register or the only use of a pseudo, we can change its mode. */ && (GET_MODE (*split) == GET_MODE (i2dest) || GET_MODE (*split) == VOIDmode || REGNO (i2dest) < FIRST_PSEUDO_REGISTER || (reg_n_sets[REGNO (i2dest)] == 1 && ! added_sets_2 && ! REG_USERVAR_P (i2dest))) && (next_real_insn (i2) == i3 || ! use_crosses_set_p (*split, INSN_CUID (i2))) /* We can't overwrite I2DEST if its value is still used by NEWPAT. */ && ! reg_referenced_p (i2dest, newpat)) { rtx newdest = i2dest; /* Get NEWDEST as a register in the proper mode. We have already validated that we can do this. */ if (GET_MODE (i2dest) != GET_MODE (*split) && GET_MODE (*split) != VOIDmode) { newdest = gen_rtx (REG, GET_MODE (*split), REGNO (i2dest)); if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER) SUBST (regno_reg_rtx[REGNO (i2dest)], newdest); } /* If *SPLIT is a (mult FOO (const_int pow2)), convert it to an ASHIFT. This can occur if it was inside a PLUS and hence appeared to be a memory address. This is a kludge. */ if (GET_CODE (*split) == MULT && GET_CODE (XEXP (*split, 1)) == CONST_INT && (i = exact_log2 (INTVAL (XEXP (*split, 1)))) >= 0) SUBST (*split, gen_rtx_combine (ASHIFT, GET_MODE (*split), XEXP (*split, 0), GEN_INT (i)));#ifdef INSN_SCHEDULING /* If *SPLIT is a paradoxical SUBREG, when we split it, it should be written as a ZERO_EXTEND. */ if (GET_CODE (*split) == SUBREG && GET_CODE (SUBREG_REG (*split)) == MEM) SUBST (*split, gen_rtx_combine (ZERO_EXTEND, GET_MODE (*split), XEXP (*split, 0)));#endif newi2pat = gen_rtx_combine (SET, VOIDmode, newdest, *split); SUBST (*split, newdest); i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); if (i2_code_number >= 0) insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); } } /* Check for a case where we loaded from memory in a narrow mode and then sign extended it, but we need both registers. In that case, we have a PARALLEL with both loads from the same memory location. We can split this into a load from memory followed by a register-register copy. This saves at least one insn, more if register allocation can eliminate the copy. */ else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0 && GET_CODE (newpat) == PARALLEL && XVECLEN (newpat, 0) == 2 && GET_CODE (XVECEXP (newpat, 0, 0)) == SET && GET_CODE (SET_SRC (XVECEXP (newpat, 0, 0))) == SIGN_EXTEND && GET_CODE (XVECEXP (newpat, 0, 1)) == SET && rtx_equal_p (SET_SRC (XVECEXP (newpat, 0, 1)), XEXP (SET_SRC (XVECEXP (newpat, 0, 0)), 0)) && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)), INSN_CUID (i2)) && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART && ! reg_overlap_mentioned_p (SET_DEST (XVECEXP (newpat, 0, 1)), SET_SRC (XVECEXP (newpat, 0, 1))) && ! find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 0)))) { rtx ni2dest; newi2pat = XVECEXP (newpat, 0, 0); ni2dest = SET_DEST (XVECEXP (newpat, 0, 0)); newpat = XVECEXP (newpat, 0, 1); SUBST (SET_SRC (newpat), gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)), ni2dest)); i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); if (i2_code_number >= 0) insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); if (insn_code_number >= 0) { rtx insn; rtx link; /* If we will be able to accept this, we have made a change to the destination of I3. This can invalidate a LOG_LINKS pointing to I3. No other part of combine.c makes such a transformation. The new I3 will have a destination that was previously the destination of I1 or I2 and which was used in i2 or I3. Call distribute_links to make a LOG_LINK from the next use of that destination. */ PATTERN (i3) = newpat; distribute_links (gen_rtx (INSN_LIST, VOIDmode, i3, NULL_RTX)); /* I3 now uses what used to be its destination and which is now I2's destination. That means we need a LOG_LINK from I3 to I2. But we used to have one, so we still will. However, some later insn might be using I2's dest and have a LOG_LINK pointing at I3. We must remove this link. The simplest way to remove the link is to point it at I1, which we know will be a NOTE. */ for (insn = NEXT_INSN (i3); insn && GET_CODE (insn) != CODE_LABEL && GET_CODE (PREV_INSN (insn)) != JUMP_INSN; insn = NEXT_INSN (insn)) { if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' && reg_referenced_p (ni2dest, PATTERN (insn))) { for (link = LOG_LINKS (insn); link; link = XEXP (link, 1)) if (XEXP (link, 0) == i3) XEXP (link, 0) = i1; break; } } } } /* Similarly, check for a case where we have a PARALLEL of two independent SETs but we started with three insns. In this case, we can do the sets as two separate insns. This case occurs when some SET allows two other insns to combine, but the destination of that SET is still live. */ else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0 && GET_CODE (newpat) == PARALLEL && XVECLEN (newpat, 0) == 2 && GET_CODE (XVECEXP (newpat, 0, 0)) == SET && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != ZERO_EXTRACT && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != STRICT_LOW_PART && GET_CODE (XVECEXP (newpat, 0, 1)) == SET && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)), INSN_CUID (i2)) /* Don't pass sets with (USE (MEM ...)) dests to the following. */ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != USE && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != USE && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 1)), XVECEXP (newpat, 0, 0)) && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 0)), XVECEXP (newpat, 0, 1))) { newi2pat = XVECEXP (newpat, 0, 1); newpat = XVECEXP (newpat, 0, 0); i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes); if (i2_code_number >= 0) insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes); } /* If it still isn't recognized, fail and change things back the way they were. */ if ((insn_code_number < 0 /* Is the result a reasonable ASM_OPERANDS? */ && (! check_asm_operands (newpat) || added_sets_1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -