Backport from trunk r237138.
2016-06-06 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
PR middle-end/37780
* config/arm/arm.md (ctzsi2): Convert to define_insn_and_split.
gcc/
Backport from trunk r237139.
* config/aarch64/aarch64.md (ctz<mode>2): Convert to
define_insn_and_split.
2016-06-06 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
PR middle-end/37780
gcc/
Backport from trunk r237141.
* ifcvt.c (noce_try_ifelse_collapse): New function.
Declare prototype.
(noce_process_if_block): Call noce_try_ifelse_collapse.
* simplify-rtx.c (simplify_cond_clz_ctz): New function.
(simplify_ternary_operation): Use the above to simplify
conditional CLZ/CTZ expressions.
2016-06-06 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
PR middle-end/37780
gcc/testsuite/
Backport from trunk r237141.
2016-06-06 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
PR middle-end/37780
* gcc.c-torture/execute/pr37780.c: New test.
* gcc.target/aarch64/pr37780_1.c: Likewise.
* gcc.target/arm/pr37780_1.c: Likewise.
gcc/
Backport from trunk r237180.
2016-06-07 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
* simplify-rtx.c (simplify_cond_clz_ctz): Delete 'mode' local
variable.
gcc/testsuite/
Backport from trunk r237308.
2016-06-10 Christophe Lyon <christophe.lyon@linaro.org>
* gcc.target/arm/pr37780_1.c: Use arm_arch_v6t2 effective target
and options.
Change-Id: I221bc478220826da3ffc3337596bcfa2a304e690
[(set_attr "type" "rbit")]
)
-(define_expand "ctz<mode>2"
- [(match_operand:GPI 0 "register_operand")
- (match_operand:GPI 1 "register_operand")]
+;; Split after reload into RBIT + CLZ. Since RBIT is represented as an UNSPEC
+;; it is unlikely to fold with any other operation, so keep this as a CTZ
+;; expression and split after reload to enable scheduling them apart if
+;; needed.
+
+(define_insn_and_split "ctz<mode>2"
+ [(set (match_operand:GPI 0 "register_operand" "=r")
+ (ctz:GPI (match_operand:GPI 1 "register_operand" "r")))]
""
- {
- emit_insn (gen_rbit<mode>2 (operands[0], operands[1]));
- emit_insn (gen_clz<mode>2 (operands[0], operands[0]));
- DONE;
- }
-)
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "
+ emit_insn (gen_rbit<mode>2 (operands[0], operands[1]));
+ emit_insn (gen_clz<mode>2 (operands[0], operands[0]));
+ DONE;
+")
(define_insn "*and<mode>_compare0"
[(set (reg:CC_NZ CC_REGNUM)
(set_attr "predicable_short_it" "no")
(set_attr "type" "clz")])
-(define_expand "ctzsi2"
- [(set (match_operand:SI 0 "s_register_operand" "")
- (ctz:SI (match_operand:SI 1 "s_register_operand" "")))]
+;; Keep this as a CTZ expression until after reload and then split
+;; into RBIT + CLZ. Since RBIT is represented as an UNSPEC it is unlikely
+;; to fold with any other expression.
+
+(define_insn_and_split "ctzsi2"
+ [(set (match_operand:SI 0 "s_register_operand" "=r")
+ (ctz:SI (match_operand:SI 1 "s_register_operand" "r")))]
"TARGET_32BIT && arm_arch_thumb2"
+ "#"
+ "&& reload_completed"
+ [(const_int 0)]
"
- {
- rtx tmp = gen_reg_rtx (SImode);
- emit_insn (gen_rbitsi2 (tmp, operands[1]));
- emit_insn (gen_clzsi2 (operands[0], tmp));
- }
- DONE;
- "
-)
+ emit_insn (gen_rbitsi2 (operands[0], operands[1]));
+ emit_insn (gen_clzsi2 (operands[0], operands[0]));
+ DONE;
+")
;; V5E instructions.
static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
static int noce_try_move (struct noce_if_info *);
+static int noce_try_ifelse_collapse (struct noce_if_info *);
static int noce_try_store_flag (struct noce_if_info *);
static int noce_try_addcc (struct noce_if_info *);
static int noce_try_store_flag_constants (struct noce_if_info *);
return FALSE;
}
+/* Try forming an IF_THEN_ELSE (cond, b, a) and collapsing that
+ through simplify_rtx. Sometimes that can eliminate the IF_THEN_ELSE.
+ If that is the case, emit the result into x. */
+
+static int
+noce_try_ifelse_collapse (struct noce_if_info * if_info)
+{
+ if (!noce_simple_bbs (if_info))
+ return FALSE;
+
+ machine_mode mode = GET_MODE (if_info->x);
+ rtx if_then_else = simplify_gen_ternary (IF_THEN_ELSE, mode, mode,
+ if_info->cond, if_info->b,
+ if_info->a);
+
+ if (GET_CODE (if_then_else) == IF_THEN_ELSE)
+ return FALSE;
+
+ rtx_insn *seq;
+ start_sequence ();
+ noce_emit_move_insn (if_info->x, if_then_else);
+ seq = end_ifcvt_sequence (if_info);
+ if (!seq)
+ return FALSE;
+
+ emit_insn_before_setloc (seq, if_info->jump,
+ INSN_LOCATION (if_info->insn_a));
+ return TRUE;
+}
+
+
/* Convert "if (test) x = 1; else x = 0".
Only try 0 and STORE_FLAG_VALUE here. Other combinations will be
if (noce_try_move (if_info))
goto success;
+ if (noce_try_ifelse_collapse (if_info))
+ goto success;
if (noce_try_store_flag (if_info))
goto success;
if (noce_try_bitop (if_info))
return 0;
}
+
+/* Recognize expressions of the form (X CMP 0) ? VAL : OP (X)
+ where OP is CLZ or CTZ and VAL is the value from CLZ_DEFINED_VALUE_AT_ZERO
+ or CTZ_DEFINED_VALUE_AT_ZERO respectively and return OP (X) if the expression
+ can be simplified to that or NULL_RTX if not.
+ Assume X is compared against zero with CMP_CODE and the true
+ arm is TRUE_VAL and the false arm is FALSE_VAL. */
+
+static rtx
+simplify_cond_clz_ctz (rtx x, rtx_code cmp_code, rtx true_val, rtx false_val)
+{
+ if (cmp_code != EQ && cmp_code != NE)
+ return NULL_RTX;
+
+ /* Result on X == 0 and X !=0 respectively. */
+ rtx on_zero, on_nonzero;
+ if (cmp_code == EQ)
+ {
+ on_zero = true_val;
+ on_nonzero = false_val;
+ }
+ else
+ {
+ on_zero = false_val;
+ on_nonzero = true_val;
+ }
+
+ rtx_code op_code = GET_CODE (on_nonzero);
+ if ((op_code != CLZ && op_code != CTZ)
+ || !rtx_equal_p (XEXP (on_nonzero, 0), x)
+ || !CONST_INT_P (on_zero))
+ return NULL_RTX;
+
+ HOST_WIDE_INT op_val;
+ if (((op_code == CLZ
+ && CLZ_DEFINED_VALUE_AT_ZERO (GET_MODE (on_nonzero), op_val))
+ || (op_code == CTZ
+ && CTZ_DEFINED_VALUE_AT_ZERO (GET_MODE (on_nonzero), op_val)))
+ && op_val == INTVAL (on_zero))
+ return on_nonzero;
+
+ return NULL_RTX;
+}
+
\f
/* Simplify CODE, an operation with result mode MODE and three operands,
OP0, OP1, and OP2. OP0_MODE was the mode of OP0 before it became
}
}
+ /* Convert x == 0 ? N : clz (x) into clz (x) when
+ CLZ_DEFINED_VALUE_AT_ZERO is defined to N for the mode of x.
+ Similarly for ctz (x). */
+ if (COMPARISON_P (op0) && !side_effects_p (op0)
+ && XEXP (op0, 1) == const0_rtx)
+ {
+ rtx simplified
+ = simplify_cond_clz_ctz (XEXP (op0, 0), GET_CODE (op0),
+ op1, op2);
+ if (simplified)
+ return simplified;
+ }
+
if (COMPARISON_P (op0) && ! side_effects_p (op0))
{
machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode
--- /dev/null
+/* PR middle-end/37780. */
+
+#define VAL (8 * sizeof (int))
+
+int __attribute__ ((noinline, noclone))
+fooctz (int i)
+{
+ return (i == 0) ? VAL : __builtin_ctz (i);
+}
+
+int __attribute__ ((noinline, noclone))
+fooctz2 (int i)
+{
+ return (i != 0) ? __builtin_ctz (i) : VAL;
+}
+
+unsigned int __attribute__ ((noinline, noclone))
+fooctz3 (unsigned int i)
+{
+ return (i > 0) ? __builtin_ctz (i) : VAL;
+}
+
+int __attribute__ ((noinline, noclone))
+fooclz (int i)
+{
+ return (i == 0) ? VAL : __builtin_clz (i);
+}
+
+int __attribute__ ((noinline, noclone))
+fooclz2 (int i)
+{
+ return (i != 0) ? __builtin_clz (i) : VAL;
+}
+
+unsigned int __attribute__ ((noinline, noclone))
+fooclz3 (unsigned int i)
+{
+ return (i > 0) ? __builtin_clz (i) : VAL;
+}
+
+int
+main (void)
+{
+ if (fooctz (0) != VAL || fooctz2 (0) != VAL || fooctz3 (0) != VAL
+ || fooclz (0) != VAL || fooclz2 (0) != VAL || fooclz3 (0) != VAL)
+ __builtin_abort ();
+
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+/* Test that we can remove the conditional move due to CLZ
+ and CTZ being defined at zero. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+int
+fooctz (int i)
+{
+ return (i == 0) ? 32 : __builtin_ctz (i);
+}
+
+int
+fooctz2 (int i)
+{
+ return (i != 0) ? __builtin_ctz (i) : 32;
+}
+
+unsigned int
+fooctz3 (unsigned int i)
+{
+ return (i > 0) ? __builtin_ctz (i) : 32;
+}
+
+/* { dg-final { scan-assembler-times "rbit\t*" 3 } } */
+
+int
+fooclz (int i)
+{
+ return (i == 0) ? 32 : __builtin_clz (i);
+}
+
+int
+fooclz2 (int i)
+{
+ return (i != 0) ? __builtin_clz (i) : 32;
+}
+
+unsigned int
+fooclz3 (unsigned int i)
+{
+ return (i > 0) ? __builtin_clz (i) : 32;
+}
+
+/* { dg-final { scan-assembler-times "clz\t" 6 } } */
+/* { dg-final { scan-assembler-not "cmp\t.*0" } } */
--- /dev/null
+/* Test that we can remove the conditional move due to CLZ
+ being defined at zero. */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v6t2_ok } */
+/* { dg-options "-O2" } */
+/* { dg-add-options arm_arch_v6t2 } */
+
+int
+fooctz (int i)
+{
+ return (i == 0) ? 32 : __builtin_ctz (i);
+}
+
+int
+fooctz2 (int i)
+{
+ return (i != 0) ? __builtin_ctz (i) : 32;
+}
+
+unsigned int
+fooctz3 (unsigned int i)
+{
+ return (i > 0) ? __builtin_ctz (i) : 32;
+}
+
+/* { dg-final { scan-assembler-times "rbit\t*" 3 } } */
+
+int
+fooclz (int i)
+{
+ return (i == 0) ? 32 : __builtin_clz (i);
+}
+
+int
+fooclz2 (int i)
+{
+ return (i != 0) ? __builtin_clz (i) : 32;
+}
+
+unsigned int
+fooclz3 (unsigned int i)
+{
+ return (i > 0) ? __builtin_clz (i) : 32;
+}
+
+/* { dg-final { scan-assembler-times "clz\t" 6 } } */
+/* { dg-final { scan-assembler-not "cmp\t.*0" } } */