+
+/* Given an insn INSN and condition COND, return the condition in a
+ canonical form to simplify testing by callers. Specifically:
+
+ (1) The code will always be a comparison operation (EQ, NE, GT, etc.).
+ (2) Both operands will be machine operands; (cc0) will have been replaced.
+ (3) If an operand is a constant, it will be the second operand.
+ (4) (LE x const) will be replaced with (LT x <const+1>) and similarly
+ for GE, GEU, and LEU.
+
+ If the condition cannot be understood, or is an inequality floating-point
+ comparison which needs to be reversed, 0 will be returned.
+
+ If REVERSE is nonzero, then reverse the condition prior to canonizing it.
+
+ If EARLIEST is nonzero, it is a pointer to a place where the earliest
+ insn used in locating the condition was found. If a replacement test
+ of the condition is desired, it should be placed in front of that
+ insn and we will be sure that the inputs are still valid.
+
+ If WANT_REG is nonzero, we wish the condition to be relative to that
+ register, if possible. Therefore, do not canonicalize the condition
+ further. If ALLOW_CC_MODE is nonzero, allow the condition returned
+ to be a compare to a CC mode register.
+
+ If VALID_AT_INSN_P, the condition must be valid at both *EARLIEST
+ and at INSN. */
+
+rtx
+canonicalize_condition (rtx insn, rtx cond, int reverse, rtx *earliest,
+ rtx want_reg, int allow_cc_mode, int valid_at_insn_p)
+{
+ enum rtx_code code;
+ rtx prev = insn;
+ const_rtx set;
+ rtx tem;
+ rtx op0, op1;
+ int reverse_code = 0;
+ enum machine_mode mode;
+ basic_block bb = BLOCK_FOR_INSN (insn);
+
+ code = GET_CODE (cond);
+ mode = GET_MODE (cond);
+ op0 = XEXP (cond, 0);
+ op1 = XEXP (cond, 1);
+
+ if (reverse)
+ code = reversed_comparison_code (cond, insn);
+ if (code == UNKNOWN)
+ return 0;
+
+ if (earliest)
+ *earliest = insn;
+
+ /* If we are comparing a register with zero, see if the register is set
+ in the previous insn to a COMPARE or a comparison operation. Perform
+ the same tests as a function of STORE_FLAG_VALUE as find_comparison_args
+ in cse.c */
+
+ while ((GET_RTX_CLASS (code) == RTX_COMPARE
+ || GET_RTX_CLASS (code) == RTX_COMM_COMPARE)
+ && op1 == CONST0_RTX (GET_MODE (op0))
+ && op0 != want_reg)
+ {
+ /* Set nonzero when we find something of interest. */
+ rtx x = 0;
+
+#ifdef HAVE_cc0
+ /* If comparison with cc0, import actual comparison from compare
+ insn. */
+ if (op0 == cc0_rtx)
+ {
+ if ((prev = prev_nonnote_insn (prev)) == 0
+ || !NONJUMP_INSN_P (prev)
+ || (set = single_set (prev)) == 0
+ || SET_DEST (set) != cc0_rtx)
+ return 0;
+
+ op0 = SET_SRC (set);
+ op1 = CONST0_RTX (GET_MODE (op0));
+ if (earliest)
+ *earliest = prev;
+ }
+#endif
+
+ /* If this is a COMPARE, pick up the two things being compared. */
+ if (GET_CODE (op0) == COMPARE)
+ {
+ op1 = XEXP (op0, 1);
+ op0 = XEXP (op0, 0);
+ continue;
+ }
+ else if (!REG_P (op0))
+ break;
+
+ /* Go back to the previous insn. Stop if it is not an INSN. We also
+ stop if it isn't a single set or if it has a REG_INC note because
+ we don't want to bother dealing with it. */
+
+ if ((prev = prev_nonnote_insn (prev)) == 0
+ || !NONJUMP_INSN_P (prev)
+ || FIND_REG_INC_NOTE (prev, NULL_RTX)
+ /* In cfglayout mode, there do not have to be labels at the
+ beginning of a block, or jumps at the end, so the previous
+ conditions would not stop us when we reach bb boundary. */
+ || BLOCK_FOR_INSN (prev) != bb)
+ break;
+
+ set = set_of (op0, prev);
+
+ if (set
+ && (GET_CODE (set) != SET
+ || !rtx_equal_p (SET_DEST (set), op0)))
+ break;
+
+ /* If this is setting OP0, get what it sets it to if it looks
+ relevant. */
+ if (set)
+ {
+ enum machine_mode inner_mode = GET_MODE (SET_DEST (set));
+#ifdef FLOAT_STORE_FLAG_VALUE
+ REAL_VALUE_TYPE fsfv;
+#endif
+
+ /* ??? We may not combine comparisons done in a CCmode with
+ comparisons not done in a CCmode. This is to aid targets
+ like Alpha that have an IEEE compliant EQ instruction, and
+ a non-IEEE compliant BEQ instruction. The use of CCmode is
+ actually artificial, simply to prevent the combination, but
+ should not affect other platforms.
+
+ However, we must allow VOIDmode comparisons to match either
+ CCmode or non-CCmode comparison, because some ports have
+ modeless comparisons inside branch patterns.
+
+ ??? This mode check should perhaps look more like the mode check
+ in simplify_comparison in combine. */
+
+ if ((GET_CODE (SET_SRC (set)) == COMPARE
+ || (((code == NE
+ || (code == LT
+ && GET_MODE_CLASS (inner_mode) == MODE_INT
+ && (GET_MODE_BITSIZE (inner_mode)
+ <= HOST_BITS_PER_WIDE_INT)
+ && (STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (inner_mode) - 1))))
+#ifdef FLOAT_STORE_FLAG_VALUE
+ || (code == LT
+ && SCALAR_FLOAT_MODE_P (inner_mode)
+ && (fsfv = FLOAT_STORE_FLAG_VALUE (inner_mode),
+ REAL_VALUE_NEGATIVE (fsfv)))
+#endif
+ ))
+ && COMPARISON_P (SET_SRC (set))))
+ && (((GET_MODE_CLASS (mode) == MODE_CC)
+ == (GET_MODE_CLASS (inner_mode) == MODE_CC))
+ || mode == VOIDmode || inner_mode == VOIDmode))
+ x = SET_SRC (set);
+ else if (((code == EQ
+ || (code == GE
+ && (GET_MODE_BITSIZE (inner_mode)
+ <= HOST_BITS_PER_WIDE_INT)
+ && GET_MODE_CLASS (inner_mode) == MODE_INT
+ && (STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (inner_mode) - 1))))
+#ifdef FLOAT_STORE_FLAG_VALUE
+ || (code == GE
+ && SCALAR_FLOAT_MODE_P (inner_mode)
+ && (fsfv = FLOAT_STORE_FLAG_VALUE (inner_mode),
+ REAL_VALUE_NEGATIVE (fsfv)))
+#endif
+ ))
+ && COMPARISON_P (SET_SRC (set))
+ && (((GET_MODE_CLASS (mode) == MODE_CC)
+ == (GET_MODE_CLASS (inner_mode) == MODE_CC))
+ || mode == VOIDmode || inner_mode == VOIDmode))
+
+ {
+ reverse_code = 1;
+ x = SET_SRC (set);
+ }
+ else
+ break;
+ }
+
+ else if (reg_set_p (op0, prev))
+ /* If this sets OP0, but not directly, we have to give up. */
+ break;
+
+ if (x)
+ {
+ /* If the caller is expecting the condition to be valid at INSN,
+ make sure X doesn't change before INSN. */
+ if (valid_at_insn_p)
+ if (modified_in_p (x, prev) || modified_between_p (x, prev, insn))
+ break;
+ if (COMPARISON_P (x))
+ code = GET_CODE (x);
+ if (reverse_code)
+ {
+ code = reversed_comparison_code (x, prev);
+ if (code == UNKNOWN)
+ return 0;
+ reverse_code = 0;
+ }
+
+ op0 = XEXP (x, 0), op1 = XEXP (x, 1);
+ if (earliest)
+ *earliest = prev;
+ }
+ }
+
+ /* If constant is first, put it last. */
+ if (CONSTANT_P (op0))
+ code = swap_condition (code), tem = op0, op0 = op1, op1 = tem;
+
+ /* If OP0 is the result of a comparison, we weren't able to find what
+ was really being compared, so fail. */
+ if (!allow_cc_mode
+ && GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC)
+ return 0;
+
+ /* Canonicalize any ordered comparison with integers involving equality
+ if we can do computations in the relevant mode and we do not
+ overflow. */
+
+ if (GET_MODE_CLASS (GET_MODE (op0)) != MODE_CC
+ && GET_CODE (op1) == CONST_INT
+ && GET_MODE (op0) != VOIDmode
+ && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT)
+ {
+ HOST_WIDE_INT const_val = INTVAL (op1);
+ unsigned HOST_WIDE_INT uconst_val = const_val;
+ unsigned HOST_WIDE_INT max_val
+ = (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (op0));
+
+ switch (code)
+ {
+ case LE:
+ if ((unsigned HOST_WIDE_INT) const_val != max_val >> 1)
+ code = LT, op1 = gen_int_mode (const_val + 1, GET_MODE (op0));
+ break;
+
+ /* When cross-compiling, const_val might be sign-extended from
+ BITS_PER_WORD to HOST_BITS_PER_WIDE_INT */
+ case GE:
+ if ((HOST_WIDE_INT) (const_val & max_val)
+ != (((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (op0)) - 1))))
+ code = GT, op1 = gen_int_mode (const_val - 1, GET_MODE (op0));
+ break;
+
+ case LEU:
+ if (uconst_val < max_val)
+ code = LTU, op1 = gen_int_mode (uconst_val + 1, GET_MODE (op0));
+ break;
+
+ case GEU:
+ if (uconst_val != 0)
+ code = GTU, op1 = gen_int_mode (uconst_val - 1, GET_MODE (op0));
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Never return CC0; return zero instead. */
+ if (CC0_P (op0))
+ return 0;
+
+ return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
+}
+
+/* Given a jump insn JUMP, return the condition that will cause it to branch
+ to its JUMP_LABEL. If the condition cannot be understood, or is an
+ inequality floating-point comparison which needs to be reversed, 0 will
+ be returned.
+
+ If EARLIEST is nonzero, it is a pointer to a place where the earliest
+ insn used in locating the condition was found. If a replacement test
+ of the condition is desired, it should be placed in front of that
+ insn and we will be sure that the inputs are still valid. If EARLIEST
+ is null, the returned condition will be valid at INSN.
+
+ If ALLOW_CC_MODE is nonzero, allow the condition returned to be a
+ compare CC mode register.
+
+ VALID_AT_INSN_P is the same as for canonicalize_condition. */
+
+rtx
+get_condition (rtx jump, rtx *earliest, int allow_cc_mode, int valid_at_insn_p)
+{
+ rtx cond;
+ int reverse;
+ rtx set;
+
+ /* If this is not a standard conditional jump, we can't parse it. */
+ if (!JUMP_P (jump)
+ || ! any_condjump_p (jump))
+ return 0;
+ set = pc_set (jump);
+
+ cond = XEXP (SET_SRC (set), 0);
+
+ /* If this branches to JUMP_LABEL when the condition is false, reverse
+ the condition. */
+ reverse
+ = GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
+ && XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (jump);
+
+ return canonicalize_condition (jump, cond, reverse, earliest, NULL_RTX,
+ allow_cc_mode, valid_at_insn_p);
+}
+
+/* Initialize the table NUM_SIGN_BIT_COPIES_IN_REP based on
+ TARGET_MODE_REP_EXTENDED.
+
+ Note that we assume that the property of
+ TARGET_MODE_REP_EXTENDED(B, C) is sticky to the integral modes
+ narrower than mode B. I.e., if A is a mode narrower than B then in
+ order to be able to operate on it in mode B, mode A needs to
+ satisfy the requirements set by the representation of mode B. */
+
+static void
+init_num_sign_bit_copies_in_rep (void)
+{
+ enum machine_mode mode, in_mode;
+
+ for (in_mode = GET_CLASS_NARROWEST_MODE (MODE_INT); in_mode != VOIDmode;
+ in_mode = GET_MODE_WIDER_MODE (mode))
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != in_mode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ enum machine_mode i;
+
+ /* Currently, it is assumed that TARGET_MODE_REP_EXTENDED
+ extends to the next widest mode. */
+ gcc_assert (targetm.mode_rep_extended (mode, in_mode) == UNKNOWN
+ || GET_MODE_WIDER_MODE (mode) == in_mode);
+
+ /* We are in in_mode. Count how many bits outside of mode
+ have to be copies of the sign-bit. */
+ for (i = mode; i != in_mode; i = GET_MODE_WIDER_MODE (i))
+ {
+ enum machine_mode wider = GET_MODE_WIDER_MODE (i);
+
+ if (targetm.mode_rep_extended (i, wider) == SIGN_EXTEND
+ /* We can only check sign-bit copies starting from the
+ top-bit. In order to be able to check the bits we
+ have already seen we pretend that subsequent bits
+ have to be sign-bit copies too. */
+ || num_sign_bit_copies_in_rep [in_mode][mode])
+ num_sign_bit_copies_in_rep [in_mode][mode]
+ += GET_MODE_BITSIZE (wider) - GET_MODE_BITSIZE (i);
+ }
+ }
+}
+
+/* Suppose that truncation from the machine mode of X to MODE is not a
+ no-op. See if there is anything special about X so that we can
+ assume it already contains a truncated value of MODE. */
+
+bool
+truncated_to_mode (enum machine_mode mode, const_rtx x)
+{
+ /* This register has already been used in MODE without explicit
+ truncation. */
+ if (REG_P (x) && rtl_hooks.reg_truncated_to_mode (mode, x))
+ return true;
+
+ /* See if we already satisfy the requirements of MODE. If yes we
+ can just switch to MODE. */
+ if (num_sign_bit_copies_in_rep[GET_MODE (x)][mode]
+ && (num_sign_bit_copies (x, GET_MODE (x))
+ >= num_sign_bit_copies_in_rep[GET_MODE (x)][mode] + 1))
+ return true;
+
+ return false;
+}
+\f
+/* Initialize non_rtx_starting_operands, which is used to speed up
+ for_each_rtx. */
+void
+init_rtlanal (void)
+{
+ int i;
+ for (i = 0; i < NUM_RTX_CODE; i++)
+ {
+ const char *format = GET_RTX_FORMAT (i);
+ const char *first = strpbrk (format, "eEV");
+ non_rtx_starting_operands[i] = first ? first - format : -1;
+ }
+
+ init_num_sign_bit_copies_in_rep ();
+}
+\f
+/* Check whether this is a constant pool constant. */
+bool
+constant_pool_constant_p (rtx x)
+{
+ x = avoid_constant_pool_reference (x);
+ return GET_CODE (x) == CONST_DOUBLE;
+}
+