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.




------------------ Original ------------------
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;

Reply via email to