@@ -2046,9 +2046,10 @@ expand_gimple_cond (basic_block bb, gimple stmt)
op0 = gimple_assign_rhs1 (second);
op1 = gimple_assign_rhs2 (second);
}
- /* If jumps are cheap turn some more codes into
- jumpy sequences. */
- else if (BRANCH_COST (optimize_insn_for_speed_p (), false) < 4)
+ /* If jumps are cheap and the target does not support conditional
+ compare, turn some more codes into jumpy sequences. */
+ else if (BRANCH_COST (optimize_insn_for_speed_p (), false) < 4
+ && (targetm.gen_ccmp_first == NULL))
{
if ((code2 == BIT_AND_EXPR
&& TYPE_PRECISION (TREE_TYPE (op0)) == 1
@@ -6206,6 +6206,42 @@ A typical @code{ctrap} pattern looks like
"@dots{}")
@end smallexample
+@cindex @code{ccmp} instruction pattern
+@item @samp{ccmp}
+Conditional compare instruction. Operand 2 and 5 are RTLs which perform
+two comparisons. Operand 1 is AND or IOR, which operates on the result of
+Operand 2 and 5. Operand 0 is the result of operand 1.
+It uses recursive method to support more than two compares. e.g.
+
+ CC0 = CMP (a, b);
+ CC1 = CCMP (NE (CC0, 0), CMP (e, f));
+ ...
+ CCn = CCMP (NE (CCn-1, 0), CMP (...));
+
+Two target hooks are used to generate conditional compares. GEN_CCMP_FISRT
+is used to generate the first CMP. And GEN_CCMP_NEXT is used to generate the
+following CCMPs. Operand 1 is AND or IOR. Operand 3 is the result of
+GEN_CCMP_FISRT or a previous GEN_CCMP_NEXT. Operand 2 is NE.
+Operand 4, 5 and 6 is another compare expression.
+
+A typical CCMP pattern looks like
+
+@smallexample
+(define_insn "*ccmp_and_ior"
+ [(set (match_operand 6 "dominant_cc_register" "")
+ (compare
+ (match_operator 1
+ (match_operator 2 "comparison_operator"
+ [(match_operand 3 "dominant_cc_register")
+ (const_int 0)])
+ (match_operator 4 "comparison_operator"
+ [(match_operand 5 "register_operand")
+ (match_operand 6 "compare_operand"]))
+ (const_int 0)))]
+ ""
+ "@dots{}")
+@end smallexample
+
@cindex @code{prefetch} instruction pattern
@item @samp{prefetch}
@@ -11180,6 +11180,32 @@ This target hook is required only when the target has several different
modes and they have different conditional execution capability, such as ARM.
@end deftypefn
+@deftypefn {Target Hook} int TARGET_SELECT_CCMP_CMP_ORDER (int @var{code1}, int @var{code2})
+For some target (like ARM), the order of two compares is sensitive for
+conditional compare. cmp0-cmp1 might be an invalid combination. But when
+swapping the order, cmp1-cmp0 is valid. The function will return
+ 1: Keep current order.
+ -1: Swap the two compares.
+ 0: Invalid combination.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_GEN_CCMP_FIRST (int @var{code}, rtx @var{op0}, rtx @var{op1})
+This function emits a comparison insn for the first of a sequence of
+ conditional comparisions. It returns a comparison expression appropriate
+ for passing to @code{gen_ccmp_next} or to @code{cbranch_optab}.
+ @code{unsignedp} is used when converting @code{op0} and @code{op1}'s mode.
+@end deftypefn
+
+@deftypefn {Target Hook} rtx TARGET_GEN_CCMP_NEXT (rtx @var{prev}, int @var{cmp_code}, rtx @var{op0}, rtx @var{op1}, int @var{bit_code})
+This function emits a conditional comparison within a sequence of
+ conditional comparisons. The @code{prev} expression is the result of a
+ prior call to @code{gen_ccmp_first} or @code{gen_ccmp_next}. It may return
+ @code{NULL} if the combination of @code{prev} and this comparison is
+ not supported, otherwise the result must be appropriate for passing to
+ @code{gen_ccmp_next} or @code{cbranch_optab}. @code{bit_code}
+ is AND or IOR, which is the op on the two compares.
+@end deftypefn
+
@deftypefn {Target Hook} unsigned TARGET_LOOP_UNROLL_ADJUST (unsigned @var{nunroll}, struct loop *@var{loop})
This target hook returns a new value for the number of times @var{loop}
should be unrolled. The parameter @var{nunroll} is the number of times
@@ -8283,6 +8283,12 @@ build_type_attribute_variant (@var{mdecl},
@hook TARGET_HAVE_CONDITIONAL_EXECUTION
+@hook TARGET_SELECT_CCMP_CMP_ORDER
+
+@hook TARGET_GEN_CCMP_FIRST
+
+@hook TARGET_GEN_CCMP_NEXT
+
@hook TARGET_LOOP_UNROLL_ADJUST
@defmac POWI_MAX_MULTS
@@ -5160,7 +5160,7 @@ expand_and (enum machine_mode mode, rtx op0, rtx op1, rtx target)
}
/* Helper function for emit_store_flag. */
-static rtx
+rtx
emit_cstore (rtx target, enum insn_code icode, enum rtx_code code,
enum machine_mode mode, enum machine_mode compare_mode,
int unsignedp, rtx x, rtx y, int normalizep,
@@ -67,6 +67,8 @@ along with GCC; see the file COPYING3. If not see
#include "params.h"
#include "tree-ssa-address.h"
#include "cfgexpand.h"
+#include "tree-phinodes.h"
+#include "ssa-iterators.h"
/* Decide whether a function's arguments should be processed
from first to last or from last to first.
@@ -9228,6 +9230,299 @@ expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode,
}
#undef REDUCE_BIT_FIELD
+/* The following functions expand conditional compare (CCMP) instructions.
+ Here is a short description about the over all algorithm:
+ * ccmp_candidate_p is used to identify the CCMP candidate
+
+ * expand_ccmp_expr is the main entry, which calls expand_ccmp_expr_1
+ to expand CCMP.
+
+ * expand_ccmp_expr_1 uses a recursive algorithm to expand CCMP.
+ It calls two target hooks gen_ccmp_first and gen_ccmp_next to generate
+ CCMP instructions.
+ - gen_ccmp_first expands the first compare in CCMP.
+ - gen_ccmp_next expands the following compares.
+
+ Another hook select_ccmp_cmp_order is called to determine which compare
+ is done first since not all combination of compares are legal in some
+ target like ARM. We might get more chance when swapping the compares.
+
+ During expanding, we must make sure that no instruction can clobber the
+ CC reg except the compares. So clobber_cc_p and check_clobber_cc are
+ introduced to do the check.
+
+ * If the final result is not used in a COND_EXPR (checked by function
+ used_in_cond_stmt_p), it calls cstorecc4 pattern to store the CC to a
+ general register. */
+
+/* Check whether G is a potential conditional compare candidate. */
+static bool
+ccmp_candidate_p (gimple g)
+{
+ tree rhs = gimple_assign_rhs_to_tree (g);
+ tree lhs, op0, op1;
+ gimple gs0, gs1;
+ enum tree_code tcode, tcode0, tcode1;
+ tcode = TREE_CODE (rhs);
+
+ if (tcode != BIT_AND_EXPR && tcode != BIT_IOR_EXPR)
+ return false;
+
+ lhs = gimple_assign_lhs (g);
+ op0 = TREE_OPERAND (rhs, 0);
+ op1 = TREE_OPERAND (rhs, 1);
+
+ if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME)
+ || !has_single_use (lhs))
+ return false;
+
+ gs0 = get_gimple_for_ssa_name (op0);
+ gs1 = get_gimple_for_ssa_name (op1);
+ if (!gs0 || !gs1 || !is_gimple_assign (gs0) || !is_gimple_assign (gs1)
+ /* g, gs0 and gs1 must be in the same basic block, since current stage
+ is out-of-ssa. We can not guarantee the correctness when forwording
+ the gs0 and gs1 into g whithout DATAFLOW analysis. */
+ || gimple_bb (gs0) != gimple_bb (gs1)
+ || gimple_bb (gs0) != gimple_bb (g))
+ return false;
+
+ if (!(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0)))
+ || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0))))
+ || !(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1)))
+ || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1)))))
+ return false;
+
+ tcode0 = gimple_assign_rhs_code (gs0);
+ tcode1 = gimple_assign_rhs_code (gs1);
+ if (TREE_CODE_CLASS (tcode0) == tcc_comparison
+ && TREE_CODE_CLASS (tcode1) == tcc_comparison)
+ return true;
+ if (TREE_CODE_CLASS (tcode0) == tcc_comparison
+ && ccmp_candidate_p (gs1))
+ return true;
+ else if (TREE_CODE_CLASS (tcode1) == tcc_comparison
+ && ccmp_candidate_p (gs0))
+ return true;
+ /* We skip ccmp_candidate_p (gs1) && ccmp_candidate_p (gs0) since
+ there is no way to set the CC flag. */
+ return false;
+}
+
+/* Check whether EXP is used in a GIMPLE_COND statement or not. */
+static bool
+used_in_cond_stmt_p (tree exp)
+{
+ bool expand_cond = false;
+ imm_use_iterator ui;
+ gimple use_stmt;
+ FOR_EACH_IMM_USE_STMT (use_stmt, ui, exp)
+ if (gimple_code (use_stmt) == GIMPLE_COND)
+ {
+ tree op1 = gimple_cond_rhs (use_stmt);
+ /* TBD: If we can convert all
+ _Bool t;
+
+ if (t == 1)
+ goto <bb 3>;
+ else
+ goto <bb 4>;
+ to
+ if (t != 0)
+ goto <bb 3>;
+ else
+ goto <bb 4>;
+ we can remove the following check. */
+ if (integer_zerop (op1))
+ expand_cond = true;
+ BREAK_FROM_IMM_USE_STMT (ui);
+ }
+ return expand_cond;
+}
+
+/* If SETTER clobber CC reg, set DATA to TRUE. */
+
+static void
+check_clobber_cc (rtx reg, const_rtx setter, void *data)
+{
+ if (GET_CODE (setter) == CLOBBER && GET_MODE (reg) == CCmode)
+ *(bool *)data = true;
+}
+
+/* Check whether INSN and all its NEXT_INSN clobber CC reg or not. */
+
+static bool
+clobber_cc_p (rtx insn)
+{
+ bool clobber = false;
+ for (; insn; insn = NEXT_INSN (insn))
+ {
+ note_stores (PATTERN (insn), check_clobber_cc, &clobber);
+ if (clobber)
+ return true;
+ }
+ return false;
+}
+
+/* Help function to generate conditional compare. PREV is the result of
+ GEN_CCMP_FIRST or GEN_CCMP_NEXT. G is the next compare.
+ CODE is BIT_AND_EXPR or BIT_IOR_EXPR. */
+
+static rtx
+gen_ccmp_next (rtx prev, gimple g, enum tree_code code)
+{
+ rtx op0, op1;
+ int unsignedp = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (g)));
+ enum rtx_code rcode = get_rtx_code (gimple_assign_rhs_code (g), unsignedp);
+ rtx last = get_last_insn ();
+
+ expand_operands (gimple_assign_rhs1 (g),
+ gimple_assign_rhs2 (g),
+ NULL_RTX, &op0, &op1, EXPAND_NORMAL);
+
+ /* If any operand clobbers CC reg, we will give up. */
+ if (clobber_cc_p (NEXT_INSN (last)))
+ return NULL_RTX;
+
+ return targetm.gen_ccmp_next (prev, rcode, op0, op1, get_rtx_code (code, 0));
+}
+
+/* Expand conditional compare gimple G. A typical CCMP sequence is like:
+
+ CC0 = CMP (a, b);
+ CC1 = CCMP (NE (CC0, 0), CMP (e, f));
+ ...
+ CCn = CCMP (NE (CCn-1, 0), CMP (...));
+
+ hook gen_ccmp_first is used to expand the first compare.
+ hook gen_ccmp_next is used to expand the following CCMP. */
+
+static rtx
+expand_ccmp_expr_1 (gimple g)
+{
+ tree exp = gimple_assign_rhs_to_tree (g);
+ enum tree_code code = TREE_CODE (exp);
+ gimple gs0 = get_gimple_for_ssa_name (TREE_OPERAND (exp, 0));
+ gimple gs1 = get_gimple_for_ssa_name (TREE_OPERAND (exp, 1));
+ rtx tmp;
+ enum tree_code code0 = gimple_assign_rhs_code (gs0);
+ enum tree_code code1 = gimple_assign_rhs_code (gs1);
+
+ gcc_assert (code == BIT_AND_EXPR || code == BIT_IOR_EXPR);
+ gcc_assert (gs0 && gs1 && is_gimple_assign (gs0) && is_gimple_assign (gs1));
+
+ if (TREE_CODE_CLASS (code0) == tcc_comparison)
+ {
+ if (TREE_CODE_CLASS (code1) == tcc_comparison)
+ {
+ int unsignedp0, unsignedp1, cmp_order;
+ enum rtx_code rcode0, rcode1, rcode;
+ rtx op0, op1, op2, op3, tmp;
+ gimple first, next;
+
+ unsignedp0 = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs0)));
+ rcode0 = get_rtx_code (code0, unsignedp0);
+ unsignedp1 = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs1)));
+ rcode1 = get_rtx_code (code1, unsignedp1);
+
+ /* For some target (like ARM), the order of two compares is sensitive
+ for conditional compare. cmp0-cmp1 might be an invalid combination.
+ But when swapping the order, cmp1-cmp0 is valid. The target hook
+ select_ccmp_cmp_order will return
+ 1: Keep current order.
+ -1: Swap the two compares.
+ 0: Invalid combination. */
+
+ cmp_order = targetm.select_ccmp_cmp_order (rcode0, rcode1);
+ /* Invalid combination. */
+ if (!cmp_order)
+ return NULL_RTX;
+ /* Swap the compare order. Do gs1 first. */
+ if (cmp_order == -1)
+ {
+ first = gs1;
+ next = gs0;
+ rcode = rcode1;
+ }
+ else
+ {
+ first = gs0;
+ next = gs1;
+ rcode = rcode0;
+ }
+ expand_operands (gimple_assign_rhs1 (first),
+ gimple_assign_rhs2 (first),
+ NULL_RTX, &op0, &op1, EXPAND_NORMAL);
+
+ /* Since the operands of NEXT might clobber CC reg, we expand the
+ operands of NEXT before GEN_CCMP_FIRST. */
+ expand_operands (gimple_assign_rhs1 (next),
+ gimple_assign_rhs2 (next),
+ NULL_RTX, &op2, &op3, EXPAND_NORMAL);
+ tmp = targetm.gen_ccmp_first (rcode, op0, op1);
+ if (!tmp)
+ return NULL_RTX;
+
+ return targetm.gen_ccmp_next (tmp,
+ rcode == rcode1 ? rcode0 : rcode1,
+ op2, op3, get_rtx_code (code, 0));
+ }
+ gcc_assert (code1 == BIT_AND_EXPR || code1 == BIT_IOR_EXPR);
+ tmp = expand_ccmp_expr_1 (gs1);
+ if (tmp)
+ return gen_ccmp_next (tmp, gs0, code);
+ }
+ else
+ {
+ gcc_assert (gimple_assign_rhs_code (gs0) == BIT_AND_EXPR
+ || gimple_assign_rhs_code (gs0) == BIT_IOR_EXPR);
+ if (TREE_CODE_CLASS (gimple_assign_rhs_code (gs1)) == tcc_comparison)
+ {
+ tmp = expand_ccmp_expr_1 (gs0);
+ if (tmp)
+ return gen_ccmp_next (tmp, gs1, code);
+ }
+ else
+ {
+ gcc_assert (gimple_assign_rhs_code (gs1) == BIT_AND_EXPR
+ || gimple_assign_rhs_code (gs1) == BIT_IOR_EXPR);
+ }
+ }
+
+ return NULL_RTX;
+}
+
+static rtx
+expand_ccmp_expr (gimple g)
+{
+ rtx last = get_last_insn ();
+ tree lhs = gimple_assign_lhs (g);
+ rtx tmp = expand_ccmp_expr_1 (g);
+
+ if (tmp)
+ {
+ enum insn_code icode;
+ /* TMP should be CC. If it is used in a GIMPLE_COND, just return it.
+ Note: Target needs to define "cbranchcc4". */
+ if (used_in_cond_stmt_p (lhs))
+ return tmp;
+
+ /* If TMP is not used in a GIMPLE_COND, store it with a csctorecc4_optab.
+ Note: Target needs to define "cstorecc4". */
+ icode = optab_handler (cstore_optab, CCmode);
+ if (icode != CODE_FOR_nothing)
+ {
+ rtx target = gen_reg_rtx (word_mode);
+ tmp = emit_cstore (target, icode, NE, CCmode, CCmode,
+ 0, tmp, const0_rtx, 1, word_mode);
+ if (tmp)
+ return tmp;
+ }
+ }
+
+ /* Clean up. */
+ delete_insns_since (last);
+ return NULL_RTX;
+}
/* Return TRUE if expression STMT is suitable for replacement.
Never consider memory loads as replaceable, because those don't ever lead
@@ -9392,10 +9687,22 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
{
rtx r;
location_t saved_loc = curr_insn_location ();
+ tree rhs = gimple_assign_rhs_to_tree (g);
set_curr_insn_location (gimple_location (g));
- r = expand_expr_real (gimple_assign_rhs_to_tree (g), target,
- tmode, modifier, NULL, inner_reference_p);
+
+ if ((targetm.gen_ccmp_first != NULL) && ccmp_candidate_p (g))
+ {
+ gcc_checking_assert (targetm.gen_ccmp_next != NULL);
+ r = expand_ccmp_expr (g);
+ if (!r)
+ r = expand_expr_real (rhs, target, tmode, modifier,
+ NULL, inner_reference_p);
+ }
+ else
+ r = expand_expr_real (rhs, target, tmode, modifier,
+ NULL, inner_reference_p);
+
set_curr_insn_location (saved_loc);
if (REG_P (r) && !REG_EXPR (r))
set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (exp), r);
@@ -6398,7 +6398,7 @@ gen_cond_trap (enum rtx_code code, rtx op1, rtx op2, rtx tcode)
/* Return rtx code for TCODE. Use UNSIGNEDP to select signed
or unsigned operation code. */
-static enum rtx_code
+enum rtx_code
get_rtx_code (enum tree_code tcode, bool unsignedp)
{
enum rtx_code code;
@@ -6448,6 +6448,12 @@ get_rtx_code (enum tree_code tcode, bool unsignedp)
code = LTGT;
break;
+ case BIT_AND_EXPR:
+ code = AND;
+ break;
+ case BIT_IOR_EXPR:
+ code = IOR;
+ break;
default:
gcc_unreachable ();
}
@@ -91,7 +91,7 @@ extern rtx expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
extern rtx expand_ternary_op (enum machine_mode mode, optab ternary_optab,
rtx op0, rtx op1, rtx op2, rtx target,
int unsignedp);
-
+extern enum rtx_code get_rtx_code (enum tree_code tcode, bool unsignedp);
/* Expand a binary operation given optab and rtx operands. */
extern rtx expand_binop (enum machine_mode, optab, rtx, rtx, rtx, int,
enum optab_methods);
@@ -556,4 +556,9 @@ extern void gen_satfractuns_conv_libfunc (convert_optab, const char *,
enum machine_mode);
extern void init_tree_optimization_optabs (tree);
+extern rtx emit_cstore (rtx target, enum insn_code icode, enum rtx_code code,
+ enum machine_mode mode, enum machine_mode compare_mode,
+ int unsignedp, rtx x, rtx y, int normalizep,
+ enum machine_mode target_mode);
+
#endif /* GCC_OPTABS_H */
@@ -556,6 +556,19 @@ cancel_changes (int num)
#define CODE_FOR_extzv CODE_FOR_nothing
#endif
+static bool
+ccmp_insn_p (rtx object)
+{
+ rtx x = PATTERN (object);
+ if (targetm.gen_ccmp_first
+ && GET_CODE (x) == SET
+ && GET_CODE (XEXP (x, 1)) == COMPARE
+ && (GET_CODE (XEXP (XEXP (x, 1), 0)) == IOR
+ || GET_CODE (XEXP (XEXP (x, 1), 0)) == AND))
+ return true;
+ return false;
+}
+
/* A subroutine of validate_replace_rtx_1 that tries to simplify the resulting
rtx. */
@@ -567,7 +580,8 @@ simplify_while_replacing (rtx *loc, rtx to, rtx object,
enum rtx_code code = GET_CODE (x);
rtx new_rtx = NULL_RTX;
- if (SWAPPABLE_OPERANDS_P (x)
+ /* Do not swap compares in conditional compare instruction. */
+ if (SWAPPABLE_OPERANDS_P (x) && !ccmp_insn_p (object)
&& swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
{
validate_unshare_change (object, loc,
@@ -2334,6 +2334,38 @@ modes and they have different conditional execution capability, such as ARM.",
bool, (void),
default_have_conditional_execution)
+DEFHOOK
+(select_ccmp_cmp_order,
+ "For some target (like ARM), the order of two compares is sensitive for\n\
+conditional compare. cmp0-cmp1 might be an invalid combination. But when\n\
+swapping the order, cmp1-cmp0 is valid. The function will return\n\
+ 1: Keep current order.\n\
+ -1: Swap the two compares.\n\
+ 0: Invalid combination.",
+ int, (int code1, int code2),
+ default_select_ccmp_cmp_order)
+
+DEFHOOK
+(gen_ccmp_first,
+ "This function emits a comparison insn for the first of a sequence of\n\
+ conditional comparisions. It returns a comparison expression appropriate\n\
+ for passing to @code{gen_ccmp_next} or to @code{cbranch_optab}.\n\
+ @code{unsignedp} is used when converting @code{op0} and @code{op1}'s mode.",
+ rtx, (int code, rtx op0, rtx op1),
+ NULL)
+
+DEFHOOK
+(gen_ccmp_next,
+ "This function emits a conditional comparison within a sequence of\n\
+ conditional comparisons. The @code{prev} expression is the result of a\n\
+ prior call to @code{gen_ccmp_first} or @code{gen_ccmp_next}. It may return\n\
+ @code{NULL} if the combination of @code{prev} and this comparison is\n\
+ not supported, otherwise the result must be appropriate for passing to\n\
+ @code{gen_ccmp_next} or @code{cbranch_optab}. @code{bit_code}\n\
+ is AND or IOR, which is the op on the two compares.",
+ rtx, (rtx prev, int cmp_code, rtx op0, rtx op1, int bit_code),
+ NULL)
+
/* Return a new value for loop unroll size. */
DEFHOOK
(loop_unroll_adjust,
@@ -76,6 +76,12 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssanames.h"
#include "insn-codes.h"
+/* Default return 1 to keep current order. */
+int
+default_select_ccmp_cmp_order (int, int)
+{
+ return 1;
+}
bool
default_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
@@ -208,3 +208,4 @@ extern tree build_va_arg_indirect_ref (tree);
extern tree std_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
extern bool can_use_doloop_if_innermost (double_int, double_int,
unsigned int, bool);
+extern int default_select_ccmp_cmp_order (int, int);