https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123884

Jeffrey A. Law <law at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
   Last reconfirmed|                            |2026-06-23
     Ever confirmed|0                           |1
             Blocks|                            |120763
             Status|UNCONFIRMED                 |NEW

--- Comment #2 from Jeffrey A. Law <law at gcc dot gnu.org> ---
The problem I see with the patch is it doesn't preserve semantics.

(ior/xor (sign_extend (ashift (const_int 1) (reg X)))
     (reg Y))

For XOR, consider if X is any value other than 31.  In that case the upper 33
bits of the result are direct copies of the bits from reg Y.  If X is the value
31, then the upper 33 bits of Y are inverted in the result.

For IOR, if X is any value other than 31, then the upper 33 bits are also
direct copies of the bits from reg Y.  If X is 31, then the upper 33 bits in
the result are all on.

Those semantics aren't preserved in your patch.  But I think I see a good path
forward.

We can query for the number of sign bits in Y.  If the number of sign bits in Y
is 33, then the xor/ior + sext.w sequence preserves semantics.  So a pattern
like this seems like it might work.

Note it's a define_split rather than a define_insn_and_split.  If we're just
generating 2 insns, then it's highly advisable to use a define_split.  It's
also the case that if we were to use a define_insn_and_split we can't guarantee
that the result of num_sign_bit_copies doesn't change.  A pass like combine
uses a refined num_sign_bit_copies implementation, but the refinement isn't
necessarily  valid outside the context of the combine pass.  So we could end up
in a scenario where the insn's condition passes during combine, but fails
later.

Using a define_split avoids those problems.  Once we make the transformation,
we don't care of something makes it harder to track num_sign_bit_copies for the
relevant pseudo register.  Using a define_split also avoids the problems with
define_insn_and_split spoiling the cost model within the combine pass.


So I'm thinking a patch like this (very lightly tested):

(define_split
  [(set (match_operand:DI 0 "register_operand")
        (any_or:DI
          (sign_extend:DI
            (ashift:SI
              (const_int 1)
              (match_operand:QI 1 "register_operand")))
          (match_operand:DI 2 "register_operand")))]
  "(TARGET_64BIT
    && TARGET_ZBS
    && num_sign_bit_copies (operands[2], DImode) == 33)"
  [(set (match_dup 0) (any_or:DI (ashift:DI (const_int 1) (match_dup 1))
                                 (match_dup 2)))
   (set (match_dup 0)
        (sign_extend:DI (subreg:SI (match_dup 0) 0)))])

I'm going to sleep on this tonight, but it's the best option I've seen in this
space since I started working on RISC-V in 2022 and this issue first came to my
attention :-)

I'm putting this back on the weekly tracker to discuss in tomorrow's patchwork
meeting.


Referenced Bugs:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120763
[Bug 120763] [meta-bug] Tracker for bugs to visit during weekly RISC-V meeting

Reply via email to