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

"Ben Shi" <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Shall we still use `define_insn_and_split` at first ? At least it does emit more efficient assembly. The solution you suggested involved changes to immediate materializing, which seems tricky, and I need more time to calculate a best form.




------------------&nbsp;Original&nbsp;------------------
From:                                                                                                                        "Xi Ruoyao"                                                                                    <[email protected]&gt;;
Date:&nbsp;Sun, Aug 2, 2026 05:11 AM
To:&nbsp;"Jeffrey Law"<[email protected]&gt;;"Ben Shi"<[email protected]&gt;;"gcc-patches"<[email protected]&gt;;
Cc:&nbsp;"chenglulu"<[email protected]&gt;;"郭杰"<[email protected]&gt;;
Subject:&nbsp;Re: [PATCH] LoongArch: improve 64-bit bitwise AND operation



On Sun, 2026-08-02 at 04:49 +0800, Xi Ruoyao wrote:
&gt; On Sat, 2026-08-01 at 05:54 -0600, Jeffrey Law wrote:
&gt; &gt; 
&gt; &gt; 
&gt; &gt; On 7/31/2026 3:47 AM, Ben Shi wrote:
&gt; &gt; &gt; It usually costs 4-5 instructions for 64-bit bitwise AND with a large
&gt; &gt; &gt; immediate. For some immediates the operation can be simplified to two
&gt; &gt; &gt; 'BSTRINS.D' instructions if the immediate satisfies:
&gt; &gt; &gt; 1. its top bit is not zero.
&gt; &gt; &gt; 2. has two sections of consecutive zero bits.
&gt; &gt; &gt; 
&gt; &gt; &gt; gcc/ChangeLog:
&gt; &gt; &gt; 	* config/loongarch/loongarch.md: Add a new RTL expression
&gt; &gt; &gt; 	&nbsp; (define_insn_and_split "bstrins_bstrins_for_and_imm").
&gt; &gt; &gt; 
&gt; &gt; &gt; 	* config/loongarch/loongarch.cc: Add a helper function
&gt; &gt; &gt; 	&nbsp; 'loongarch_use_bstrins_bstrins_for_and' for the above RTL expression.
&gt; &gt; &gt; 
&gt; &gt; &gt; 	* config/loongarch/loongarch-protos.h: Add prototype of function
&gt; &gt; &gt; 	&nbsp; 'loongarch_use_bstrins_bstrins_for_and'.
&gt; &gt; &gt; 
&gt; &gt; &gt; 	* testsuite/gcc.target/loongarch/la64/and-large-immediate-opt-2.c:
&gt; &gt; &gt; 	&nbsp; Add a new test.
&gt; &gt; You might want ot look at how RISC-V handles this.&nbsp; There's all kinds of 
&gt; &gt; primitives you can use to clear bits and you're usually better using
&gt; &gt; those primitives to synthesize the logical operation during initial 
&gt; &gt; expansion without constructing the constant.&nbsp; &nbsp; For example you can use 
&gt; &gt; shifts in pairs or triplets, bit clear style instructions, zero 
&gt; &gt; extensions, and-immediate if you have them, rotates in combination with 
&gt; &gt; and-immediate, and so-on.&nbsp; These are composable.
&gt; &gt; 
&gt; &gt; In general define_insn_and_split is not a great way to solve these 
&gt; &gt; problems because it effectively lies about the cost of the patterns it
&gt; &gt; matches and it will tend to inhibit further optimizations because of
&gt; &gt; those lies about the cost (and to be clear, I'm not talking about 
&gt; &gt; rtx_cost or insn cost, but instead the internal costing done by combine 
&gt; &gt; which relies solely on insn counts, particularly with regards to insn 
&gt; &gt; splitting).
&gt; &gt; 
&gt; &gt; I'm not at all familiar with the details of the loongarch port and this 
&gt; &gt; should not be considered a review.&nbsp; Just some advice based on working in 
&gt; &gt; this space on the RISC-V port.
&gt; 
&gt; I tried doing this in define_expand but then combine insists to
&gt; transform the bstrins pair back to materializing the mask.&nbsp; The reason
&gt; is we have
&gt; 
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* When not optimizing for size, we care more about the cost
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; of hot code, and hot code is often in a loop.&nbsp; If a constant
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; operand needs to be forced into a register, we will often be
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; able to hoist the constant load out of the loop, so the load
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; should not contribute to the cost.&nbsp; */
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (speed || loongarch_immediate_operand_p (outer_code, INTVAL (x))) 
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; 
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *total = 0;
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true; 
&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp; 
&gt; 
&gt; in loongarch_rtx_costs for CONST_INT, i.e. the cost for materializing
&gt; the immediate is considered 0 unless optimizing for size...
&gt; 
&gt; Perhaps this loongarch_rtx_costs logic is incorrect but I'm not sure.

Tweaking loongarch_rtx_costs prevents the combine:

diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
index a0f91ab2843..7a86b904264 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -4090,18 +4090,13 @@ loongarch_rtx_costs (rtx x, machine_mode mode, int outer_code,
&nbsp;&nbsp; switch (code)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp; case CONST_INT:
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (TARGET_64BIT &amp;&amp; outer_code == AND &amp;&amp; UINTVAL (x) == 0xffffffff)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (outer_code == AND &amp;&amp; and_operand (x, mode))
&nbsp;	{
&nbsp;	&nbsp; *total = 0;
&nbsp;	&nbsp; return true;
&nbsp;	}
&nbsp;
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* When not optimizing for size, we care more about the cost
-	 of hot code, and hot code is often in a loop.&nbsp; If a constant
-	 operand needs to be forced into a register, we will often be
-	 able to hoist the constant load out of the loop, so the load
-	 should not contribute to the cost.&nbsp; */
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (speed || loongarch_immediate_operand_p (outer_code, INTVAL (x)))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (loongarch_immediate_operand_p (outer_code, INTVAL (x)))
&nbsp;	{
&nbsp;	&nbsp; *total = 0;
&nbsp;	&nbsp; return true;
@@ -4118,8 +4113,6 @@ loongarch_rtx_costs (rtx x, machine_mode mode, int outer_code,
&nbsp;	&nbsp; if (cost == 1 &amp;&amp; outer_code == SET
&nbsp;	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !(float_mode_p &amp;&amp; TARGET_HARD_FLOAT))
&nbsp;	&nbsp;&nbsp;&nbsp; cost = 0;
-	&nbsp; else if ((outer_code == SET || GET_MODE (x) == VOIDmode))
-	&nbsp;&nbsp;&nbsp; cost = 1;
&nbsp;	&nbsp; *total = COSTS_N_INSNS (cost);
&nbsp;	&nbsp; return true;
&nbsp;	}

The first diff is obviously correct, while the following diffs remove
some special cases for const materialization.&nbsp; I don't know if removing
them will cause a net win, or lose.

P.S. even if the logic in the comment removed above is incorrect, there
might be still a valid reason not to penalty const_int materialization:
the micro architecture likely has some optimization for it.

-- 
Xi Ruoyao <[email protected]&gt;
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.