/* If .POPCOUNT call has 2 arguments, match_single_bit_test marked it
because the result is only used in an equality comparison against 1.
Use rtx costs in that case to determine if .POPCOUNT (arg) == 1
- or (arg ^ (arg - 1)) > arg - 1 is cheaper. */
+ or (arg ^ (arg - 1)) > arg - 1 is cheaper.
+ If .POPCOUNT second argument is 0, we additionally know that arg
+ is non-zero, so use arg & (arg - 1) == 0 instead. */
bool speed_p = optimize_insn_for_speed_p ();
tree lhs = gimple_call_lhs (stmt);
tree arg = gimple_call_arg (stmt, 0);
+ bool nonzero_arg = integer_zerop (gimple_call_arg (stmt, 1));
tree type = TREE_TYPE (arg);
machine_mode mode = TYPE_MODE (type);
do_pending_stack_adjust ();
1, OPTAB_DIRECT);
if (argm1 == NULL_RTX)
goto fail;
- rtx argxorargm1 = expand_simple_binop (mode, XOR, op0, argm1, NULL_RTX,
- 1, OPTAB_DIRECT);
+ rtx argxorargm1 = expand_simple_binop (mode, nonzero_arg ? AND : XOR, op0,
+ argm1, NULL_RTX, 1, OPTAB_DIRECT);
if (argxorargm1 == NULL_RTX)
goto fail;
- rtx cmp = emit_store_flag (NULL_RTX, GTU, argxorargm1, argm1, mode, 1, 1);
+ rtx cmp;
+ if (nonzero_arg)
+ cmp = emit_store_flag (NULL_RTX, EQ, argxorargm1, const0_rtx, mode, 1, 1);
+ else
+ cmp = emit_store_flag (NULL_RTX, GTU, argxorargm1, argm1, mode, 1, 1);
if (cmp == NULL_RTX)
goto fail;
rtx_insn *cmp_insns = get_insns ();
--- /dev/null
+/* PR tree-optimization/90693 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-abm -mno-popcnt -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-not "POPCOUNT \\\(" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_popcount(ll)? \\\(" "optimized" } } */
+
+int
+foo (unsigned int x)
+{
+ if (x == 0)
+ __builtin_unreachable ();
+ return __builtin_popcount (x) == 1;
+}
+
+int
+bar (unsigned int x)
+{
+ return (x & (x - 1)) == 0;
+}
+
+int
+baz (unsigned long long x)
+{
+ if (x == 0)
+ __builtin_unreachable ();
+ return __builtin_popcountll (x) == 1;
+}
+
+int
+qux (unsigned long long x)
+{
+ return (x & (x - 1)) == 0;
+}
tree type = TREE_TYPE (arg);
if (!INTEGRAL_TYPE_P (type))
return;
+ bool nonzero_arg = tree_expr_nonzero_p (arg);
if (direct_internal_fn_supported_p (IFN_POPCOUNT, type, OPTIMIZE_FOR_BOTH))
{
/* Tell expand_POPCOUNT the popcount result is only used in equality
comparison with one, so that it can decide based on rtx costs. */
gimple *g = gimple_build_call_internal (IFN_POPCOUNT, 2, arg,
- integer_one_node);
+ nonzero_arg ? integer_zero_node
+ : integer_one_node);
gimple_call_set_lhs (g, gimple_call_lhs (call));
gimple_stmt_iterator gsi2 = gsi_for_stmt (call);
gsi_replace (&gsi2, g, true);
gimple *g = gimple_build_assign (argm1, PLUS_EXPR, arg,
build_int_cst (type, -1));
gsi_insert_before (gsi, g, GSI_SAME_STMT);
- g = gimple_build_assign (make_ssa_name (type), BIT_XOR_EXPR, arg, argm1);
+ g = gimple_build_assign (make_ssa_name (type),
+ nonzero_arg ? BIT_AND_EXPR : BIT_XOR_EXPR,
+ arg, argm1);
gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ tree_code cmpcode;
+ if (nonzero_arg)
+ {
+ argm1 = build_zero_cst (type);
+ cmpcode = code;
+ }
+ else
+ cmpcode = code == EQ_EXPR ? GT_EXPR : LE_EXPR;
if (gcond *cond = dyn_cast <gcond *> (stmt))
{
gimple_cond_set_lhs (cond, gimple_assign_lhs (g));
gimple_cond_set_rhs (cond, argm1);
- gimple_cond_set_code (cond, code == EQ_EXPR ? GT_EXPR : LE_EXPR);
+ gimple_cond_set_code (cond, cmpcode);
}
else
{
gimple_assign_set_rhs1 (stmt, gimple_assign_lhs (g));
gimple_assign_set_rhs2 (stmt, argm1);
- gimple_assign_set_rhs_code (stmt, code == EQ_EXPR ? GT_EXPR : LE_EXPR);
+ gimple_assign_set_rhs_code (stmt, cmpcode);
}
update_stmt (stmt);
gimple_stmt_iterator gsi2 = gsi_for_stmt (call);