+enum rtx_code
+prepare_cbranch_operands (rtx *operands, enum machine_mode mode,
+ enum rtx_code comparison)
+{
+ rtx op1;
+ rtx scratch = NULL_RTX;
+
+ if (comparison == CODE_FOR_nothing)
+ comparison = GET_CODE (operands[0]);
+ else
+ scratch = operands[4];
+ if (GET_CODE (operands[1]) == CONST_INT
+ && GET_CODE (operands[2]) != CONST_INT)
+ {
+ rtx tmp = operands[1];
+
+ operands[1] = operands[2];
+ operands[2] = tmp;
+ comparison = swap_condition (comparison);
+ }
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ HOST_WIDE_INT val = INTVAL (operands[2]);
+ if ((val == -1 || val == -0x81)
+ && (comparison == GT || comparison == LE))
+ {
+ comparison = (comparison == GT) ? GE : LT;
+ operands[2] = gen_int_mode (val + 1, mode);
+ }
+ else if ((val == 1 || val == 0x80)
+ && (comparison == GE || comparison == LT))
+ {
+ comparison = (comparison == GE) ? GT : LE;
+ operands[2] = gen_int_mode (val - 1, mode);
+ }
+ else if (val == 1 && (comparison == GEU || comparison == LTU))
+ {
+ comparison = (comparison == GEU) ? NE : EQ;
+ operands[2] = CONST0_RTX (mode);
+ }
+ else if (val == 0x80 && (comparison == GEU || comparison == LTU))
+ {
+ comparison = (comparison == GEU) ? GTU : LEU;
+ operands[2] = gen_int_mode (val - 1, mode);
+ }
+ else if (val == 0 && (comparison == GTU || comparison == LEU))
+ comparison = (comparison == GTU) ? NE : EQ;
+ else if (mode == SImode
+ && ((val == 0x7fffffff
+ && (comparison == GTU || comparison == LEU))
+ || ((unsigned HOST_WIDE_INT) val
+ == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
+ && (comparison == GEU || comparison == LTU))))
+ {
+ comparison = (comparison == GTU || comparison == GEU) ? LT : GE;
+ operands[2] = CONST0_RTX (mode);
+ }
+ }
+ op1 = operands[1];
+ if (can_create_pseudo_p ())
+ operands[1] = force_reg (mode, op1);
+ /* When we are handling DImode comparisons, we want to keep constants so
+ that we can optimize the component comparisons; however, memory loads
+ are better issued as a whole so that they can be scheduled well.
+ SImode equality comparisons allow I08 constants, but only when they
+ compare r0. Hence, if operands[1] has to be loaded from somewhere else
+ into a register, that register might as well be r0, and we allow the
+ constant. If it is already in a register, this is likely to be
+ allocated to a different hard register, thus we load the constant into
+ a register unless it is zero. */
+ if (!REG_P (operands[2])
+ && (GET_CODE (operands[2]) != CONST_INT
+ || (mode == SImode && operands[2] != CONST0_RTX (SImode)
+ && ((comparison != EQ && comparison != NE)
+ || (REG_P (op1) && REGNO (op1) != R0_REG)
+ || !satisfies_constraint_I08 (operands[2])))))
+ {
+ if (scratch && GET_MODE (scratch) == mode)
+ {
+ emit_move_insn (scratch, operands[2]);
+ operands[2] = scratch;
+ }
+ else if (can_create_pseudo_p ())
+ operands[2] = force_reg (mode, operands[2]);
+ }
+ return comparison;
+}
+
+void
+expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
+{
+ rtx (*branch_expander) (rtx) = gen_branch_true;
+ rtx jump;
+
+ comparison = prepare_cbranch_operands (operands, SImode, comparison);
+ switch (comparison)
+ {
+ case NE: case LT: case LE: case LTU: case LEU:
+ comparison = reverse_condition (comparison);
+ branch_expander = gen_branch_false;
+ default: ;
+ }
+ emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, T_REG),
+ gen_rtx_fmt_ee (comparison, SImode,
+ operands[1], operands[2])));
+ jump = emit_jump_insn (branch_expander (operands[3]));
+ if (probability >= 0)
+ REG_NOTES (jump)
+ = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (probability),
+ REG_NOTES (jump));
+
+}
+
+/* ??? How should we distribute probabilities when more than one branch
+ is generated. So far we only have soem ad-hoc observations:
+ - If the operands are random, they are likely to differ in both parts.
+ - If comparing items in a hash chain, the operands are random or equal;
+ operation should be EQ or NE.
+ - If items are searched in an ordered tree from the root, we can expect
+ the highpart to be unequal about half of the time; operation should be
+ an inequality comparison, operands non-constant, and overall probability
+ about 50%. Likewise for quicksort.
+ - Range checks will be often made against constants. Even if we assume for
+ simplicity an even distribution of the non-constant operand over a
+ sub-range here, the same probability could be generated with differently
+ wide sub-ranges - as long as the ratio of the part of the subrange that
+ is before the threshold to the part that comes after the threshold stays
+ the same. Thus, we can't really tell anything here;
+ assuming random distribution is at least simple.
+ */
+
+bool
+expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
+{
+ enum rtx_code msw_taken, msw_skip, lsw_taken;
+ rtx skip_label = NULL_RTX;
+ rtx op1h, op1l, op2h, op2l;
+ int num_branches;
+ int prob, rev_prob;
+ int msw_taken_prob = -1, msw_skip_prob = -1, lsw_taken_prob = -1;
+ rtx scratch = operands[4];
+
+ comparison = prepare_cbranch_operands (operands, DImode, comparison);
+ op1h = gen_highpart_mode (SImode, DImode, operands[1]);
+ op2h = gen_highpart_mode (SImode, DImode, operands[2]);
+ op1l = gen_lowpart (SImode, operands[1]);
+ op2l = gen_lowpart (SImode, operands[2]);
+ msw_taken = msw_skip = lsw_taken = CODE_FOR_nothing;
+ prob = split_branch_probability;
+ rev_prob = REG_BR_PROB_BASE - prob;
+ switch (comparison)
+ {
+ /* ??? Should we use the cmpeqdi_t pattern for equality comparisons?
+ That costs 1 cycle more when the first branch can be predicted taken,
+ but saves us mispredicts because only one branch needs prediction.
+ It also enables generating the cmpeqdi_t-1 pattern. */
+ case EQ:
+ if (TARGET_CMPEQDI_T)
+ {
+ emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
+ emit_jump_insn (gen_branch_true (operands[3]));
+ return true;
+ }
+ msw_skip = NE;
+ lsw_taken = EQ;
+ if (prob >= 0)
+ {
+ /* If we had more precision, we'd use rev_prob - (rev_prob >> 32) .
+ */
+ msw_skip_prob = rev_prob;
+ if (REG_BR_PROB_BASE <= 65535)
+ lsw_taken_prob = prob ? REG_BR_PROB_BASE : 0;
+ else
+ {
+ gcc_assert (HOST_BITS_PER_WIDEST_INT >= 64);
+ lsw_taken_prob
+ = (prob
+ ? (REG_BR_PROB_BASE
+ - ((HOST_WIDEST_INT) REG_BR_PROB_BASE * rev_prob
+ / ((HOST_WIDEST_INT) prob << 32)))
+ : 0);
+ }
+ }
+ break;
+ case NE:
+ if (TARGET_CMPEQDI_T)
+ {
+ emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
+ emit_jump_insn (gen_branch_false (operands[3]));
+ return true;
+ }
+ msw_taken = NE;
+ msw_taken_prob = prob;
+ lsw_taken = NE;
+ lsw_taken_prob = 0;
+ break;
+ case GTU: case GT:
+ msw_taken = comparison;
+ if (GET_CODE (op2l) == CONST_INT && INTVAL (op2l) == -1)
+ break;
+ if (comparison != GTU || op2h != CONST0_RTX (SImode))
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = GTU;
+ break;
+ case GEU: case GE:
+ if (op2l == CONST0_RTX (SImode))
+ msw_taken = comparison;
+ else
+ {
+ msw_taken = comparison == GE ? GT : GTU;
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = GEU;
+ }
+ break;
+ case LTU: case LT:
+ msw_taken = comparison;
+ if (op2l == CONST0_RTX (SImode))
+ break;
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = LTU;
+ break;
+ case LEU: case LE:
+ if (GET_CODE (op2l) == CONST_INT && INTVAL (op2l) == -1)
+ msw_taken = comparison;
+ else
+ {
+ lsw_taken = LEU;
+ if (comparison == LE)
+ msw_taken = LT;
+ else if (op2h != CONST0_RTX (SImode))
+ msw_taken = LTU;
+ else
+ break;
+ msw_skip = swap_condition (msw_taken);
+ }
+ break;
+ default: return false;
+ }
+ num_branches = ((msw_taken != CODE_FOR_nothing)
+ + (msw_skip != CODE_FOR_nothing)
+ + (lsw_taken != CODE_FOR_nothing));
+ if (comparison != EQ && comparison != NE && num_branches > 1)
+ {
+ if (!CONSTANT_P (operands[2])
+ && prob >= (int) (REG_BR_PROB_BASE * 3 / 8U)
+ && prob <= (int) (REG_BR_PROB_BASE * 5 / 8U))
+ {
+ msw_taken_prob = prob / 2U;
+ msw_skip_prob
+ = REG_BR_PROB_BASE * rev_prob / (REG_BR_PROB_BASE + rev_prob);
+ lsw_taken_prob = prob;
+ }
+ else
+ {
+ msw_taken_prob = prob;
+ msw_skip_prob = REG_BR_PROB_BASE;
+ /* ??? If we have a constant op2h, should we use that when
+ calculating lsw_taken_prob? */
+ lsw_taken_prob = prob;
+ }
+ }
+ operands[1] = op1h;
+ operands[2] = op2h;
+ operands[4] = NULL_RTX;
+ if (reload_completed
+ && ! arith_reg_or_0_operand (op2h, SImode) && true_regnum (op1h)
+ && (msw_taken != CODE_FOR_nothing || msw_skip != CODE_FOR_nothing))
+ {
+ emit_move_insn (scratch, operands[2]);
+ operands[2] = scratch;
+ }
+ if (msw_taken != CODE_FOR_nothing)
+ expand_cbranchsi4 (operands, msw_taken, msw_taken_prob);
+ if (msw_skip != CODE_FOR_nothing)
+ {
+ rtx taken_label = operands[3];
+
+ /* Operands were possibly modified, but msw_skip doesn't expect this.
+ Always use the original ones. */
+ if (msw_taken != CODE_FOR_nothing)
+ {
+ operands[1] = op1h;
+ operands[2] = op2h;
+ }
+
+ operands[3] = skip_label = gen_label_rtx ();
+ expand_cbranchsi4 (operands, msw_skip, msw_skip_prob);
+ operands[3] = taken_label;
+ }
+ operands[1] = op1l;
+ operands[2] = op2l;
+ if (lsw_taken != CODE_FOR_nothing)
+ {
+ if (reload_completed
+ && ! arith_reg_or_0_operand (op2l, SImode) && true_regnum (op1l))
+ operands[4] = scratch;
+ expand_cbranchsi4 (operands, lsw_taken, lsw_taken_prob);
+ }
+ if (msw_skip != CODE_FOR_nothing)
+ emit_label (skip_label);
+ return true;
+}
+