@@ -90,9 +90,10 @@ ccmp_tree_comparison_p (tree t, basic_block bb)
If all checks OK in expand_ccmp_expr, it emits insns in prep_seq, then
insns in gen_seq. */
-/* Check whether G is a potential conditional compare candidate. */
+/* Check whether G is a potential conditional compare candidate; OUTER is true if
+ G is the outer most AND/IOR. */
static bool
-ccmp_candidate_p (gimple *g)
+ccmp_candidate_p (gimple *g, bool outer = false)
{
tree lhs, op0, op1;
gimple *gs0, *gs1;
@@ -109,8 +110,9 @@ ccmp_candidate_p (gimple *g)
lhs = gimple_assign_lhs (g);
op0 = gimple_assign_rhs1 (g);
op1 = gimple_assign_rhs2 (g);
- if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME)
- || !has_single_use (lhs))
+ if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME))
+ return false;
+ if (!outer && !has_single_use (lhs))
return false;
bb = gimple_bb (g);
@@ -284,7 +286,7 @@ expand_ccmp_expr (gimple *g, machine_mode mode)
rtx_insn *last;
rtx tmp;
- if (!ccmp_candidate_p (g))
+ if (!ccmp_candidate_p (g, true))
return NULL_RTX;
last = get_last_insn ();
@@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see
#include "output.h"
#include "builtins.h"
#include "opts.h"
+#include "ccmp.h"
/* Some systems use __main in a way incompatible with its use in gcc, in these
cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
@@ -3858,6 +3859,50 @@ expand_clobber (tree lhs)
}
}
+/* A subroutine of expand_gimple_stmt_1, expanding one gimple assign statement
+ ASSIGN_STMT that has a SSA name on the lhs, using TARGET and TARGET_MODE
+ as the target if possible and mode for what mode we should expand into. */
+static rtx
+expand_gimple_assign_ssa (gassign *assign_stmt,
+ rtx target,
+ machine_mode target_mode)
+{
+ rtx temp;
+ tree lhs = gimple_assign_lhs (assign_stmt);
+ /* Try to expand conditonal compare. */
+ if (targetm.gen_ccmp_first
+ && gimple_assign_rhs_class (assign_stmt) == GIMPLE_BINARY_RHS)
+ {
+ machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+ gcc_checking_assert (targetm.gen_ccmp_next != NULL);
+ temp = expand_ccmp_expr (assign_stmt, mode);
+ if (temp)
+ return temp;
+ }
+
+ struct separate_ops ops;
+ ops.code = gimple_assign_rhs_code (assign_stmt);
+ ops.type = TREE_TYPE (lhs);
+ switch (get_gimple_rhs_class (ops.code))
+ {
+ case GIMPLE_TERNARY_RHS:
+ ops.op2 = gimple_assign_rhs3 (assign_stmt);
+ /* Fallthru */
+ case GIMPLE_BINARY_RHS:
+ ops.op1 = gimple_assign_rhs2 (assign_stmt);
+ /* Fallthru */
+ case GIMPLE_UNARY_RHS:
+ ops.op0 = gimple_assign_rhs1 (assign_stmt);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ ops.location = gimple_location (assign_stmt);
+
+ return expand_expr_real_2 (&ops, target, target_mode,
+ EXPAND_NORMAL);
+}
+
/* A subroutine of expand_gimple_stmt, expanding one gimple statement
STMT that doesn't require special handling for outgoing edges. That
is no tailcalls and no GIMPLE_COND. */
@@ -3971,37 +4016,17 @@ expand_gimple_stmt_1 (gimple *stmt)
{
rtx target, temp;
bool nontemporal = gimple_assign_nontemporal_move_p (assign_stmt);
- struct separate_ops ops;
bool promoted = false;
target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
promoted = true;
- ops.code = gimple_assign_rhs_code (assign_stmt);
- ops.type = TREE_TYPE (lhs);
- switch (get_gimple_rhs_class (ops.code))
- {
- case GIMPLE_TERNARY_RHS:
- ops.op2 = gimple_assign_rhs3 (assign_stmt);
- /* Fallthru */
- case GIMPLE_BINARY_RHS:
- ops.op1 = gimple_assign_rhs2 (assign_stmt);
- /* Fallthru */
- case GIMPLE_UNARY_RHS:
- ops.op0 = gimple_assign_rhs1 (assign_stmt);
- break;
- default:
- gcc_unreachable ();
- }
- ops.location = gimple_location (stmt);
-
- /* If we want to use a nontemporal store, force the value to
- register first. If we store into a promoted register,
- don't directly expand to target. */
+ /* If we want to use a nontemporal store, force the value to
+ register first. If we store into a promoted register,
+ don't directly expand to target. */
temp = nontemporal || promoted ? NULL_RTX : target;
- temp = expand_expr_real_2 (&ops, temp, GET_MODE (target),
- EXPAND_NORMAL);
+ temp = expand_gimple_assign_ssa (assign_stmt, temp, GET_MODE (target));
if (temp == target)
;
@@ -4013,7 +4038,7 @@ expand_gimple_stmt_1 (gimple *stmt)
if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
{
temp = convert_modes (GET_MODE (target),
- TYPE_MODE (ops.type),
+ TYPE_MODE (TREE_TYPE (lhs)),
temp, unsignedp);
temp = convert_modes (GET_MODE (SUBREG_REG (target)),
GET_MODE (target), temp, unsignedp);
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-options "-O2" } */
+/* PR target/100942 */
+
+void foo(void);
+int f1(int a, int b)
+{
+ int c = a == 0 || b == 0;
+ if (c) foo();
+ return c;
+}
+
+/* We should get one cmp followed by ccmp and one cset. */
+/* { dg-final { scan-assembler "\tccmp\t" } } */
+/* { dg-final { scan-assembler-times "\tcset\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tcmp\t" 1 } } */
+/* And not get 2 cmps and 2 (or more cset) and orr and a cbnz. */
+/* { dg-final { scan-assembler-not "\torr\t" } } */
+/* { dg-final { scan-assembler-not "\tcbnz\t" } } */
+/* { dg-final { scan-assembler-not "\tcbz\t" } } */
+
new file mode 100644
@@ -0,0 +1,35 @@
+/* { dg-options "-O2" } */
+/* PR target/100942 */
+
+void foo(void);
+int f1(int a, int b, int d)
+{
+ int c = a < 8 || b < 9;
+ int e = d < 11 || c;
+ if (e) foo();
+ return c;
+}
+
+/*
+ We really should get:
+ cmp w0, 7
+ ccmp w1, 8, 4, gt
+ cset w0, le
+ ccmp w2, 10, 4, gt
+ ble .L11
+
+ But we currently get:
+ cmp w0, 7
+ ccmp w1, 8, 4, gt
+ cset w0, le
+ cmp w0, 0
+ ccmp w2, 10, 4, eq
+ ble .L11
+ The middle cmp is not needed.
+ */
+
+/* We should end up with only one cmp and 2 ccmp and 1 cset but currently we get 2 cmp
+ though. */
+/* { dg-final { scan-assembler-times "\tccmp\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tcset\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tcmp\t" 1 { xfail *-*-* } } } */
new file mode 100644
@@ -0,0 +1,20 @@
+
+/* { dg-options "-O2" } */
+/* PR target/100942 */
+void f1(int a, int b, _Bool *x)
+{
+ x[0] = x[1] = a == 0 || b == 0;
+}
+
+void f2(int a, int b, int *x)
+{
+ x[0] = x[1] = a == 0 || b == 0;
+}
+
+
+/* Both functions should be using ccmp rather than 2 cset/orr. */
+/* { dg-final { scan-assembler-times "\tccmp\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tcset\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tcmp\t" 2 } } */
+/* { dg-final { scan-assembler-not "\torr\t" } } */
+