It usually costs 4-5 instructions for 64-bit bitwise AND with a large
immediate. For some immediates the operation can be simplified to two
'BSTRINS.D' instructions if the immediate satisfies:
1. its top bit is not zero.
2. has two sections of consecutive zero bits.
gcc/ChangeLog:
* config/loongarch/loongarch.md: Add a new RTL expression
(define_insn_and_split "bstrins_bstrins_for_and_imm").
* config/loongarch/loongarch.cc: Add a helper function
'loongarch_use_bstrins_bstrins_for_and' for the above RTL expression.
* config/loongarch/loongarch-protos.h: Add prototype of function
'loongarch_use_bstrins_bstrins_for_and'.
* testsuite/gcc.target/loongarch/la64/and-large-immediate-opt-2.c:
Add a new test.
Signed-off-by: Ben Shi <[email protected]>
---
gcc/config/loongarch/loongarch-protos.h | 1 +
gcc/config/loongarch/loongarch.cc | 30 +++++++++++++++++++
gcc/config/loongarch/loongarch.md | 30 +++++++++++++++++++
.../la64/and-large-immediate-opt-2.c | 13 ++++++++
4 files changed, 74 insertions(+)
create mode 100644
gcc/testsuite/gcc.target/loongarch/la64/and-large-immediate-opt-2.c
diff --git a/gcc/config/loongarch/loongarch-protos.h
b/gcc/config/loongarch/loongarch-protos.h
index e67addf36e3..93e78972a9a 100644
--- a/gcc/config/loongarch/loongarch-protos.h
+++ b/gcc/config/loongarch/loongarch-protos.h
@@ -162,6 +162,7 @@ extern bool loongarch_check_zero_div_p (void);
extern bool loongarch_pre_reload_split (void);
extern int loongarch_use_bstrins_for_ior_with_mask (machine_mode, rtx *);
extern bool loongarch_use_bstrins_bstrpick_for_and (rtx, machine_mode);
+extern bool loongarch_use_bstrins_bstrins_for_and (rtx, machine_mode);
extern rtx loongarch_rewrite_mem_for_simple_ldst (rtx);
union loongarch_gen_fn_ptrs
diff --git a/gcc/config/loongarch/loongarch.cc
b/gcc/config/loongarch/loongarch.cc
index b0f7abc0483..986a6594322 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -4034,6 +4034,36 @@ loongarch_use_bstrins_bstrpick_for_and (rtx op,
machine_mode mode)
return false;
}
+/* Check if it is possible to optimize AND operation with an immediate:
+ a. immediate is loaded by more than 2 instruction
+ b. can use two 'bstrins.d' instructions.
+ This immediate must be satisfy:
+ a. its top bit is not zero
+ b. has two sections of consecutive zero bits. */
+
+bool
+loongarch_use_bstrins_bstrins_for_and (rtx op, machine_mode mode)
+{
+ if (loongarch_integer_cost (UINTVAL (op)) <= 2)
+ return false;
+
+ /* It's meaningless if the OP is not splittable
+ and skip the cases already supported in AND operation. */
+ if (!splittable_const_int_operand (op, mode) || and_operand (op, mode))
+ return false;
+
+ /* If the leading one bits are set to zero, the value should match
+ the pattern of 'bstrins_bstrpick_for_and_imm'. */
+ int lead_one_bits = clz_hwi (~UINTVAL (op));
+ unsigned HOST_WIDE_INT one = 1;
+ unsigned HOST_WIDE_INT lead_one_mask = (one
+ << (HOST_BITS_PER_WIDE_INT
+ - lead_one_bits))
+ - 1;
+ unsigned HOST_WIDE_INT new_val = lead_one_mask & UINTVAL (op);
+ return loongarch_use_bstrins_bstrpick_for_and (GEN_INT (new_val), mode);
+}
+
/* Return the cost of moving between two registers of mode MODE. */
static int
diff --git a/gcc/config/loongarch/loongarch.md
b/gcc/config/loongarch/loongarch.md
index 37ae5a2223e..5f9a11875c2 100644
--- a/gcc/config/loongarch/loongarch.md
+++ b/gcc/config/loongarch/loongarch.md
@@ -1783,6 +1783,36 @@
}
[(set_attr "length" "8")])
+(define_insn_and_split "bstrins_bstrins_for_and_imm"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (and:DI (match_operand:DI 1 "register_operand" "r")
+ (match_operand:DI 2 "const_int_operand" "i")))]
+ "TARGET_64BIT
+ && loongarch_use_bstrins_bstrins_for_and (operands[2], DImode)"
+ "#"
+ "&& true"
+ [(const_int 0)]
+{
+ unsigned HOST_WIDE_INT op2 = INTVAL (operands[2]);
+ int lead_one_bits = clz_hwi (~op2);
+ unsigned HOST_WIDE_INT zero = 0, one = 1;
+ unsigned HOST_WIDE_INT lead_one_mask = (~zero)
+ << (HOST_BITS_PER_WIDE_INT
+ - lead_one_bits);
+ unsigned HOST_WIDE_INT new_val = op2 & ~lead_one_mask;
+ int bit_num = clz_hwi (new_val);
+ unsigned HOST_WIDE_INT mask_hi = lead_one_mask
+ | ((one
+ << (HOST_BITS_PER_WIDE_INT -
bit_num))
+ - 1);
+ unsigned HOST_WIDE_INT mask_lo = (~zero)
+ << (HOST_BITS_PER_WIDE_INT - bit_num);
+ emit_move_insn (operands[0], operands[1]);
+ emit_insn (gen_anddi3 (operands[0], operands[0], GEN_INT (mask_hi)));
+ emit_insn (gen_anddi3 (operands[0], operands[0],
+ GEN_INT (new_val | mask_lo)));
+})
+
(define_insn "*iorhi3"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(ior:HI (match_operand:HI 1 "register_operand" "%r,r")
diff --git
a/gcc/testsuite/gcc.target/loongarch/la64/and-large-immediate-opt-2.c
b/gcc/testsuite/gcc.target/loongarch/la64/and-large-immediate-opt-2.c
new file mode 100644
index 00000000000..c2121b374eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/la64/and-large-immediate-opt-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile { target { loongarch64*-*-* } } } */
+/* { dg-options "-O3" } */
+/* { dg-final { scan-assembler-not "\tlu12i.w" } } */
+/* { dg-final { scan-assembler-not "\tori" } } */
+/* { dg-final { scan-assembler-not "\tlu52i.d" } } */
+/* { dg-final { scan-assembler-not "\tand" } } */
+/* { dg-final { scan-assembler "\tbstrins.d" } } */
+
+long
+test (long a)
+{
+ return a & 0xff0fffffffff0fffLL;
+}</[email protected]>