[PATCH] LoongArch: improve 64-bit bitwise AND operation

"Ben Shi" <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.