The generic conditional-move expansion currently expands both operands
before looking for a target-specific conditional negate or complement.
This misses idioms such as:

  X ? -X : X
  X ? ~X : X
and can allow recursive conditional-move expansion when the target-specific
expansion fails.

Recognize conditional negate and complement expressions including reversed
operands and SSA no-op conversions before the existing conditional-move
expansion. Keep the recursion guard active while trying the target
expansion and while falling back to the normal operand expansion.

Add the RISC-V neg<mode>cc pattern for TARGET_ZICOND_LIKE targets. The
pattern expands conditional negation as:

  CMP = X ? 1 : 0
  MASK = -CMP
  DEST = X ^ MASK
  DEST += CMP

Avoid this expansion when the operand is a materialized constant since
the existing constant sequence is shorter.

Regression tested on riscv64-unknown-elf. No new failures.

gcc/ChangeLog:
        PR target/126245
        * expr.cc (strip_ssa_nop_conversions): New.
        (get_unary_op_operand): New.
        (expand_cond_expr_using_cmove): Recognize conditional negate and
        complement expressions and use target-specific expansion while
        * riscv.md (neg<mode>cc): New.

gcc/testsuite/ChangeLog:
        PR target/126245
        * gcc.target/riscv/pr126245.c: New test.

co-authored-by: Andrew Pinski <[email protected]>

Signed-off-by: Naveen <[email protected]>
---
 gcc/config/riscv/riscv.md                 |  32 +++++
 gcc/expr.cc                               | 146 ++++++++++++++++++++++
 gcc/testsuite/gcc.target/riscv/pr126245.c |  24 ++++
 3 files changed, 202 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/riscv/pr126245.c

diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 8420b3d29e7..ae57b77cbd0 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -3504,6 +3504,38 @@
     FAIL;
 })
 
+(define_expand "neg<mode>cc"
+  [(set (match_operand:GPR 0 "register_operand")
+       (if_then_else:GPR (match_operand 1 "comparison_operator")
+                         (neg:GPR (match_operand:GPR 2 "register_operand"))
+                         (match_operand:GPR 3 "register_operand")))]
+  "TARGET_ZICOND_LIKE"
+{
+  rtx cmp = operands[1];
+  rtx cmp0 = XEXP (cmp, 0);
+  rtx cmp1 = XEXP (cmp, 1);
+
+  if (!INTEGRAL_MODE_P (GET_MODE (cmp0))
+      || !rtx_equal_p (operands[2], operands[3]))
+    FAIL;
+
+  rtx_insn *last = get_last_insn_anywhere ();
+  rtx set = last ? single_set (last) : NULL_RTX;
+  if (set
+      && rtx_equal_p (SET_DEST (set), operands[2])
+      && CONST_INT_P (SET_SRC (set)))
+    FAIL;
+
+  rtx cmp_result = gen_reg_rtx (<MODE>mode);
+  riscv_expand_int_scc (cmp_result, GET_CODE (cmp), cmp0, cmp1);
+
+  rtx mask = gen_reg_rtx (<MODE>mode);
+  riscv_emit_unary (NEG, mask, cmp_result);
+  riscv_emit_binary (XOR, operands[0], operands[2], mask);
+  riscv_emit_binary (PLUS, operands[0], operands[0], cmp_result);
+  DONE;
+})
+
 (define_expand "add<mode>cc"
   [(match_operand:GPR 0 "register_operand")
    (match_operand     1 "comparison_operator")
diff --git a/gcc/expr.cc b/gcc/expr.cc
index a9157dc5d41..f865ff47c8a 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -9657,6 +9657,41 @@ expand_expr_real (tree exp, rtx target, machine_mode 
tmode,
   return ret;
 }
 
+/* Strip no-op conversions represented by SSA assignments from EXP.  */
+
+static tree
+strip_ssa_nop_conversions (tree exp)
+{
+  while (TREE_CODE (exp) == SSA_NAME)
+    {
+      gimple *stmt = get_gimple_for_ssa_name (exp);
+      if (!stmt
+         || !is_gimple_assign (stmt)
+         || !CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
+         || !tree_nop_conversion_p (TREE_TYPE (exp),
+                                     TREE_TYPE (gimple_assign_rhs1 (stmt))))
+       break;
+      exp = gimple_assign_rhs1 (stmt);
+    }
+
+  return tree_strip_nop_conversions (exp);
+}
+
+/* Return the operand of a unary operation CODE in EXP looking through
+   no-op conversions represented by SSA assignments.  */
+
+static tree
+get_unary_op_operand (tree exp, enum tree_code code)
+{
+  exp = strip_ssa_nop_conversions (exp);
+
+  if (TREE_CODE (exp) == code)
+    return TREE_OPERAND (exp, 0);
+
+  gimple *stmt = get_def_for_expr (exp, code);
+  return stmt ? gimple_assign_rhs1 (stmt) : NULL_TREE;
+}
+
 /* Try to expand the conditional expression which is represented by
    TREEOP0 ? TREEOP1 : TREEOP2 using conditional moves.  If it succeeds
    return the rtl reg which represents the result.  Otherwise return
@@ -9701,6 +9736,117 @@ expand_cond_expr_using_cmove (tree treeop0 
ATTRIBUTE_UNUSED,
 
   expanding_cond_expr_using_cmove = true;
   start_sequence ();
+
+  /* Try to expand a conditional negate or complement directly. Expanding
+     the negation first would lose this opportunity and leave the target with
+     a conditional move between X and -X.  */
+  tree conditional_neg_operand = NULL_TREE;
+  rtx_code conditional_neg_code = UNKNOWN;
+  bool reverse_conditional_neg = false;
+
+  tree other_tree = strip_ssa_nop_conversions (treeop2);
+  tree neg_operand = get_unary_op_operand (treeop1, NEGATE_EXPR);
+  if (neg_operand)
+    conditional_neg_code = NEG;
+  else
+    {
+      neg_operand = get_unary_op_operand (treeop1, BIT_NOT_EXPR);
+      if (neg_operand)
+       conditional_neg_code = NOT;
+    }
+
+  if (neg_operand
+      && operand_equal_p (strip_ssa_nop_conversions (neg_operand),
+                         other_tree, 0))
+    conditional_neg_operand = treeop2;
+  else
+    {
+      tree other_neg_operand
+       = get_unary_op_operand (treeop2, NEGATE_EXPR);
+      if (other_neg_operand)
+       conditional_neg_code = NEG;
+      else
+       {
+         other_neg_operand = get_unary_op_operand (treeop2, BIT_NOT_EXPR);
+         if (other_neg_operand)
+           conditional_neg_code = NOT;
+       }
+
+      if (other_neg_operand
+         && operand_equal_p (strip_ssa_nop_conversions (other_neg_operand),
+                              strip_ssa_nop_conversions (treeop1), 0))
+       {
+         conditional_neg_operand = treeop1;
+         reverse_conditional_neg = true;
+       }
+    }
+
+  if (conditional_neg_operand != NULL_TREE
+      && !TREE_SIDE_EFFECTS (conditional_neg_operand))
+    {
+      rtx_insn *last = get_last_insn ();
+
+      rtx value = expand_normal (conditional_neg_operand);
+
+      if (TREE_CODE (treeop0) == SSA_NAME
+         && (srcstmt = get_def_for_expr_class (treeop0, tcc_comparison))
+         && !VECTOR_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (srcstmt))))
+       {
+         type = TREE_TYPE (gimple_assign_rhs1 (srcstmt));
+         enum tree_code cmpcode = gimple_assign_rhs_code (srcstmt);
+         op00 = expand_normal (gimple_assign_rhs1 (srcstmt));
+         op01 = expand_normal (gimple_assign_rhs2 (srcstmt));
+         comparison_mode = TYPE_MODE (type);
+         unsignedp = TYPE_UNSIGNED (type);
+         comparison_code = convert_tree_comp_to_rtx (cmpcode, unsignedp);
+       }
+      else if (COMPARISON_CLASS_P (treeop0)
+              && !VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (treeop0, 0))))
+       {
+         type = TREE_TYPE (TREE_OPERAND (treeop0, 0));
+         enum tree_code cmpcode = TREE_CODE (treeop0);
+         op00 = expand_normal (TREE_OPERAND (treeop0, 0));
+         op01 = expand_normal (TREE_OPERAND (treeop0, 1));
+         unsignedp = TYPE_UNSIGNED (type);
+         comparison_mode = TYPE_MODE (type);
+         comparison_code = convert_tree_comp_to_rtx (cmpcode, unsignedp);
+       }
+      else
+       {
+         op00 = expand_normal (treeop0);
+         op01 = const0_rtx;
+         comparison_code = NE;
+         comparison_mode = GET_MODE (op00);
+         if (comparison_mode == VOIDmode)
+           comparison_mode = TYPE_MODE (TREE_TYPE (treeop0));
+       }
+
+      if (reverse_conditional_neg)
+       comparison_code = reverse_condition (comparison_code);
+
+      if (comparison_code != UNKNOWN)
+       {
+         rtx cond = gen_rtx_fmt_ee (comparison_code, comparison_mode,
+                                     op00, op01);
+
+         if (GET_MODE (value) != mode)
+           value = gen_lowpart (mode, value);
+
+         insn = emit_conditional_neg_or_complement (temp,
+                                                      conditional_neg_code,
+                                                      mode, cond, value, 
value);
+         if (insn)
+           {
+             rtx_insn *seq = end_sequence ();
+             emit_insn (seq);
+             expanding_cond_expr_using_cmove = false;
+             return convert_modes (orig_mode, mode, temp, 0);
+           }
+       }
+
+      delete_insns_since (last);
+    }
+
   expand_operands (treeop1, treeop2,
                   mode == orig_mode ? temp : NULL_RTX, &op1, &op2,
                   EXPAND_NORMAL);
diff --git a/gcc/testsuite/gcc.target/riscv/pr126245.c 
b/gcc/testsuite/gcc.target/riscv/pr126245.c
new file mode 100644
index 00000000000..cd0a4a4edd8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr126245.c
@@ -0,0 +1,24 @@
+/* PR target/126245 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64g_zicond -mabi=lp64d" { target { rv64 } } } */
+/* { dg-options "-O2 -march=rv32g_zicond -mabi=ilp32d" { target { rv32 } } } */
+
+long
+f_cmp_lt (long x, long y)
+{
+  long cmp = x < y;
+  return (x ^ -cmp) + cmp;
+}
+
+/* { dg-final { scan-assembler-times {\mslt\M} 1 } } */
+/* { dg-final { scan-assembler-times {\mneg\M} 1 } } */
+/* { dg-final { scan-assembler-times {\mxor\M} 1 } } */
+/* { dg-final { scan-assembler {f_cmp_lt:(?:[^\n]*\n)*?\tadd\t} } } */
+
+long
+f_cmove_guard_reset (long cond, long a, long b)
+{
+  return cond ? a : b;
+}
+
+/* { dg-final { scan-assembler {\mczero\.(eqz|nez)\M} } } */
-- 
2.34.1

Reply via email to