The patch was using the existing negcc/notcc path through
emit_conditional_neg_or_complement. The two expansions it tried were:
first the target conditional negate/complement sequence and then the
existing conditional-move sequence with a cost comparison when both
succeeded.
Reworked the patch to do the recognition in gimple-isel instead.
It now matches scalar COND_EXPRs of the form
cond ? -x : x
cond ? ~x : x
including the reversed form and rewrites them to the corresponding
direct internal function only when direct_internal_fn_supported_p says
the optab is available.
The existing RISC-V neg<mode>cc expander covers the negcc optab used by RTL
if-conversion but the direct internal function path uses cond_neg. Hence,
this patch adds the corresponding RISC-V cond_neg<mode> expander.
Regression tested on riscv target with no extra failures.
gcc/ChangeLog:
PR target/126245
* gimple-isel.cc (strip_ssa_nop_conversions): New.
(get_unary_op_operand): New.
(get_conditional_unary_operand): New.
(gimple_expand_cond_unary_expr): Expand scalar conditional
negation and complement into conditional internal functions.
(pass_gimple_isel::execute): Use gimple_expand_cond_unary_expr.
* config/riscv/riscv.md (cond_neg<mode>): New RISC-V expansion for
branchless conditional negation.
gcc/testsuite/ChangeLog:
PR target/126245
* gcc.target/riscv/pr126245-cond-neg.c: New test.
Signed-off-by: Naveen <[email protected]>
---
gcc/config/riscv/riscv.md | 32 +++++
gcc/gimple-isel.cc | 131 ++++++++++++++++++
.../gcc.target/riscv/pr126245-cond-neg.c | 18 +++
3 files changed, 181 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/pr126245-cond-neg.c
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index cfac4c960bf..a2003248b21 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -3543,6 +3543,38 @@
DONE;
})
+(define_expand "cond_neg<mode>"
+ [(set (match_operand:GPR 0 "register_operand")
+ (if_then_else:GPR (match_operand 1 "register_operand")
+ (neg:GPR (match_operand:GPR 2 "register_operand"))
+ (match_operand:GPR 3 "register_operand")))]
+ ""
+{
+ rtx cmp_result = gen_reg_rtx (<MODE>mode);
+ convert_move (cmp_result, operands[1], true);
+
+ rtx mask = gen_reg_rtx (<MODE>mode);
+ riscv_emit_unary (NEG, mask, cmp_result);
+
+ if (rtx_equal_p (operands[2], operands[3]))
+ {
+ rtx xor_result = gen_reg_rtx (<MODE>mode);
+ riscv_emit_binary (XOR, xor_result, operands[2], mask);
+ riscv_emit_binary (PLUS, operands[0], xor_result, cmp_result);
+ DONE;
+ }
+
+ rtx neg = gen_reg_rtx (<MODE>mode);
+ riscv_emit_unary (NEG, neg, operands[2]);
+ rtx true_part = gen_reg_rtx (<MODE>mode);
+ riscv_emit_binary (AND, true_part, neg, mask);
+ riscv_emit_unary (NOT, mask, mask);
+ rtx false_part = gen_reg_rtx (<MODE>mode);
+ riscv_emit_binary (AND, false_part, operands[3], mask);
+ riscv_emit_binary (IOR, operands[0], true_part, false_part);
+ DONE;
+})
+
(define_expand "add<mode>cc"
[(match_operand:GPR 0 "register_operand")
(match_operand 1 "comparison_operator")
diff --git a/gcc/gimple-isel.cc b/gcc/gimple-isel.cc
index 1d346ee124f..196ee7c12f9 100644
--- a/gcc/gimple-isel.cc
+++ b/gcc/gimple-isel.cc
@@ -315,6 +315,128 @@ gimple_expand_vec_cond_expr (gimple_stmt_iterator *gsi)
return gimple_build_call_internal (IFN_VCOND_MASK, 3, op0, op1, op2);
}
+/* Strip no-op conversions represented by SSA assignments from EXP. */
+
+static tree
+strip_ssa_nop_conversions (tree exp)
+{
+ while (TREE_CODE (exp) == SSA_NAME)
+ {
+ gassign *stmt = dyn_cast<gassign *> (SSA_NAME_DEF_STMT (exp));
+ if (!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);
+
+ if (TREE_CODE (exp) == SSA_NAME)
+ {
+ gassign *stmt = dyn_cast<gassign *> (SSA_NAME_DEF_STMT (exp));
+ if (stmt && gimple_assign_rhs_code (stmt) == code)
+ return gimple_assign_rhs1 (stmt);
+ }
+
+ return NULL_TREE;
+}
+
+/* Return the operand of a conditional negate or complement in EXP and set
+ IFN to the corresponding conditional internal function. */
+
+static tree
+get_conditional_unary_operand (tree exp, internal_fn *ifn)
+{
+ tree operand = get_unary_op_operand (exp, NEGATE_EXPR);
+ if (operand)
+ {
+ *ifn = IFN_COND_NEG;
+ return operand;
+ }
+
+ operand = get_unary_op_operand (exp, BIT_NOT_EXPR);
+ if (operand)
+ *ifn = IFN_COND_NOT;
+ return operand;
+}
+
+/* Expand a scalar COND_EXPR of the form A ? -B : B or A ? ~B : B into a
+ conditional internal function when the target supports the optab. */
+
+static gimple *
+gimple_expand_cond_unary_expr (gimple_stmt_iterator *gsi)
+{
+ gassign *stmt = dyn_cast<gassign *> (gsi_stmt (*gsi));
+ if (!stmt || gimple_assign_rhs_code (stmt) != COND_EXPR)
+ return NULL;
+
+ tree lhs = gimple_assign_lhs (stmt);
+ tree type = TREE_TYPE (lhs);
+ if (VECTOR_TYPE_P (type))
+ return NULL;
+
+ tree op1 = gimple_assign_rhs2 (stmt);
+ tree op2 = gimple_assign_rhs3 (stmt);
+ tree op1_nops = strip_ssa_nop_conversions (op1);
+ tree op2_nops = strip_ssa_nop_conversions (op2);
+ internal_fn ifn = IFN_LAST;
+ bool reverse = false;
+ tree value = NULL_TREE;
+
+ tree operand = get_conditional_unary_operand (op1, &ifn);
+ if (operand
+ && operand_equal_p (strip_ssa_nop_conversions (operand), op2_nops, 0))
+ value = op2;
+ else
+ {
+ operand = get_conditional_unary_operand (op2, &ifn);
+ if (operand
+ && operand_equal_p (strip_ssa_nop_conversions (operand), op1_nops, 0))
+ {
+ value = op1;
+ reverse = true;
+ }
+ }
+
+ if (!value
+ || !types_compatible_p (TREE_TYPE (value), type)
+ || !direct_internal_fn_supported_p (ifn, type, OPTIMIZE_FOR_BOTH))
+ return NULL;
+
+ location_t loc = gimple_location (stmt);
+ gimple_seq stmts = NULL;
+ tree cond = force_gimple_operand (gimple_assign_rhs1 (stmt), &stmts, true,
+ NULL_TREE);
+ if (TREE_CODE (TREE_TYPE (cond)) != BOOLEAN_TYPE)
+ cond = gimple_build (&stmts, loc, NE_EXPR, boolean_type_node, cond,
+ build_zero_cst (TREE_TYPE (cond)));
+ if (reverse)
+ cond = gimple_build (&stmts, loc, EQ_EXPR, boolean_type_node, cond,
+ build_zero_cst (TREE_TYPE (cond)));
+ if (stmts)
+ gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+
+ gcall *call = gimple_build_call_internal (ifn, 3, cond, value, value);
+ gimple_call_set_lhs (call, lhs);
+ gimple_set_location (call, loc);
+ gimple_call_set_nothrow (call, true);
+ return call;
+}
+
/* Duplicate COND_EXPR condition defs of STMT located in BB when they are
comparisons so RTL expansion with the help of TER
can perform better if conversion. */
@@ -1379,6 +1501,15 @@ pass_gimple_isel::execute (struct function *fun)
gsi_replace (&gsi, g, false);
}
+ /* Pre-expand scalar conditional negation and complement to
+ conditional internal functions that map to optabs. */
+ g = gimple_expand_cond_unary_expr (&gsi);
+ if (g != NULL)
+ {
+ gsi_replace (&gsi, g, false);
+ continue;
+ }
+
/* Recognize .VEC_SET and .VEC_EXTRACT patterns. */
cfg_changed |= gimple_expand_vec_set_extract_expr (fun, &gsi);
if (gsi_end_p (gsi))
diff --git a/gcc/testsuite/gcc.target/riscv/pr126245-cond-neg.c
b/gcc/testsuite/gcc.target/riscv/pr126245-cond-neg.c
new file mode 100644
index 00000000000..6ce517be7f1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr126245-cond-neg.c
@@ -0,0 +1,18 @@
+/* PR target/126245 */
+/* { dg-do compile { target { ! riscv_abi_e } } } */
+/* { dg-options "-O2 -march=rv64g -mabi=lp64d -fdump-tree-isel" { target {
rv64 } } } */
+/* { dg-options "-O2 -march=rv32g -mabi=ilp32d -fdump-tree-isel" { target {
rv32 } } } */
+
+unsigned long
+f_cond_neg (unsigned long x, unsigned long y)
+{
+ unsigned long cmp = x < y;
+ return (x ^ -cmp) + cmp;
+}
+
+/* { dg-final { scan-tree-dump {\.COND_NEG} "isel" } } */
+/* { dg-final { scan-assembler-times {\msltu\M} 1 } } */
+/* { dg-final { scan-assembler-times "neg\t" 1 } } */
+/* { dg-final { scan-assembler-times {\mxor\M} 1 } } */
+/* { dg-final { scan-assembler-times {\madd\M} 1 } } */
+/* { dg-final { scan-assembler-not {\m(?:beq|bne|blt|bge|ble|bgt)(?:u|z)?\M} }
} */
--
2.34.1