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)) &lt;= 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
+                                         &lt;&lt; (HOST_BITS_PER_WIDE_INT
+                                             - lead_one_bits))
+                                        - 1;
+  unsigned HOST_WIDE_INT new_val = lead_one_mask &amp; 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
+   &amp;&amp; loongarch_use_bstrins_bstrins_for_and (operands[2], DImode)"
+  "#"
+  "&amp;&amp; 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)
+                                        &lt;&lt; (HOST_BITS_PER_WIDE_INT
+                                            - lead_one_bits);
+  unsigned HOST_WIDE_INT new_val = op2 &amp; ~lead_one_mask;
+  int bit_num = clz_hwi (new_val);
+  unsigned HOST_WIDE_INT mask_hi = lead_one_mask
+                                  | ((one
+                                      &lt;&lt; (HOST_BITS_PER_WIDE_INT - 
bit_num))
+                                    - 1);
+  unsigned HOST_WIDE_INT mask_lo = (~zero)
+                                  &lt;&lt; (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 &amp; 0xff0fffffffff0fffLL;
+}</[email protected]>

Reply via email to