;; If we have the ZBA extension, then we can clear the upper half of a 64
;; bit object with a zext.w. So if we have AND where the constant would
;; require synthesis of two or more instructions, but 32->64 sign extension
-;; of the constant is a simm12, then we can use zext.w+andi. If the adjusted
-;; constant is a single bit constant, then we can use zext.w+bclri
+;; of the constant is a simm12, then we can use zext.w+andi.
+;;
+;; If the adjusted constant is a single bit constant, then we can use
+;; zext.w+bclri
+;;
+;; If the original constant uppermost bit was bit 31 and is a consecutive
+;; run of bits, leave the original form alone since it compresses better
+;; a srliw+slli
;;
;; With the mvconst_internal pattern claiming a single insn to synthesize
;; constants, this must be a define_insn_and_split.
implement with andi or bclri. */
&& ((SMALL_OPERAND (sext_hwi (INTVAL (operands[2]), 32))
|| (TARGET_ZBS && popcount_hwi (INTVAL (operands[2])) == 31))
- && INTVAL (operands[2]) != 0x7fffffff)"
+ && INTVAL (operands[2]) != 0x7fffffff)
+ && !(clz_hwi (UINTVAL (operands[2])) == 32
+ && consecutive_bits_operand (operands[2], word_mode))"
"#"
"&& 1"
[(set (match_dup 0) (zero_extend:DI (match_dup 3)))
return true;
}
+ /* For RV64 we can exploit srlw to mask off bits on both the
+ high and low ends, then shift it back into position. So
+ a two instruction sequence. */
+ t = UINTVAL (operands[2]);
+ if (TARGET_64BIT
+ && consecutive_bits_operand (operands[2], word_mode)
+ && budget >= 2
+ && clz_hwi (t) == 32)
+ {
+ /* The srliw will wipe the upper 32 bits and low bits at the
+ same time. */
+ rtx x = gen_rtx_LSHIFTRT (SImode,
+ gen_lowpart (SImode, operands[1]),
+ GEN_INT (ctz_hwi (t)));
+ x = gen_rtx_SIGN_EXTEND (DImode, x);
+ output = gen_reg_rtx (word_mode);
+ emit_insn (gen_rtx_SET (output, x));
+ input = output;
+
+ /* Now shift it back to its proper position. */
+ x = gen_rtx_ASHIFT (DImode, input, GEN_INT (ctz_hwi (t)));
+ emit_insn (gen_rtx_SET (operands[0], x));
+ return true;
+ }
+
+
/* If we shift right to eliminate the trailing zeros and
the result is a SMALL_OPERAND, then it's a shift right,
andi and shift left. */
--- /dev/null
+/* { dg-do compile { target rv64 } } */
+/* { dg-options "-march=rv64gcb -mabi=lp64d" } */
+
+#define T(L,S) unsigned long t_##L##_##S(unsigned long x) { return x & ((((1UL << L) - 1) << S)); }
+
+T(1, 31)
+T(2, 30)
+T(3, 29)
+T(4, 28)
+T(5, 27)
+T(6, 26)
+T(7, 25)
+T(8, 24)
+T(9, 23)
+T(10, 22)
+T(11, 21)
+T(12, 20)
+T(13, 19)
+T(14, 18)
+T(15, 17)
+T(16, 16)
+T(17, 15)
+T(18, 14)
+T(19, 13)
+T(20, 12)
+T(21, 11)
+T(22, 10)
+T(23, 9)
+T(24, 8)
+T(25, 7)
+T(26, 6)
+T(27, 5)
+T(28, 4)
+T(29, 3)
+T(30, 2)
+T(31, 1)
+
+/* { dg-final { scan-assembler-times "\\tsrliw" 30 } } */
+/* { dg-final { scan-assembler-times "\\tslli" 30 } } */
+/* { dg-final { scan-assembler-times "\\tbseti" 1 } } */
+/* { dg-final { scan-assembler-times "\\tand" 1 } } */
+