On Mon, 15 Sept 2025 at 13:02, Richard Biener
<[email protected]> wrote:
>
> On Mon, Sep 15, 2025 at 11:55 AM Christophe Lyon
> <[email protected]> wrote:
> >
> > On Thu, 11 Sept 2025 at 19:44, Jakub Jelinek <[email protected]> wrote:
> > >
> > > On Thu, Sep 11, 2025 at 07:14:51PM +0200, Richard Biener wrote:
> > > > Yes, RTL expansion inserts an AND operation when !SHIFT_COUNT_TRUNCATED.
> > > > What I was saying there's no way to get a negative shift count flip 
> > > > shift
> > > > direction to RTL - it would require a target specific intrinsic that's 
> > > > a builtin
> > > > call before RTL expansion and an UNSPEC after.
> > >
> > > Of course the target can have patterns like:
> > >   (set (match_operand:M 0)
> > >     (if_then_else:M
> > >       (lt:SI (match_operand:SI 2) (const_int 0))
> > >       (ashiftrt:M (match_operand:M 1) (neg:SI (match_dup 2)))
> > >       (ashift:M (match_dup 1) (match_dup 2))))
> > > or so, it doesn't need to use UNSPEC for that.
> > >
> >
> > Hi!
> > Thanks for all the feedback, that was very useful.
> > I've implemented the suggestion above, but so far I couldn't make the
> > 'if' condition match (the shift amount is never seen as negative with
> > my tests).
> > Since the constraint is "rPg", where "Pg" means "constant in range 1
> > to 32", out-of-range values are always passed in register: can the
> > above if_then_else work when operand 2 is a register?
>
> I think the above might match during combine when you write C code like
>
>  if (n < 0)
>    res = x >> -n;
>  else
>    res = x << n;
>
> which of course would need to be if-converted (possibly trying to match
> exactly that insn pattern).  A variant with UB, like
>
>  res1 = x >> -n;
>  res2 = x << n;
>  res = n < 0 ? res1 : res2;
>
> might be an easier match.
>

so, with:
(define_insn "mve_lsll"
  [(set (match_operand:DI 0 "arm_general_register_operand" "=r")
(if_then_else:DI
  (lt:SI (match_operand:SI 2 "arm_reg_or_long_shift_imm" "rPg")
(const_int 0))
  (ashiftrt:DI (match_operand:DI 1 "arm_general_register_operand" "0")
       (neg:SI (match_dup 2)))
  (ashift:DI (match_dup 1) (match_dup 2))))]
  "TARGET_HAVE_MVE"
  "lsll%?\\t%Q0, %R1, %2"
  [(set_attr "predicable" "yes")])

both foo1 and foo3 generate the same code (the shift amount is known
at compile time, so only the needed part of the condition is kept)

int64_t
foo1 (int64_t value)
{
  int32_t shift = -10;
  if (shift < 0)
    return value << -shift;
  else
    return value >> shift;
}
int64_t
foo3 (int64_t value)
{
  int32_t shift = -10;
  int64_t res1 = value << -shift;
  int64_t res2 = value >> shift;
  return shift < 0 ? res1 : res2;
}
foo1:
mov r2, r0 @ 18 [c=4 l=2]  *thumb2_movsi_vfp/0
mov r3, r1 @ 19 [c=4 l=2]  *thumb2_movsi_vfp/0
lsll r2, r3, #10 @ 6 [c=112 l=4]  mve_lsll
mov r0, r2 @ 24 [c=4 l=2]  *thumb2_movsi_vfp/0
mov r1, r3 @ 25 [c=4 l=2]  *thumb2_movsi_vfp/0
bx lr @ 28 [c=8 l=4]  *thumb2_return


similarly foo2 and foo4 generate the same code:
int64_t
foo2 (int64_t value, int32_t shift)
{
  if (shift < 0)
    return value << -shift;
  else
    return value >> shift;
}
int64_t
foo4 (int64_t value, int32_t shift)
{
  int64_t res1 = value << -shift;
  int64_t res2 = value >> shift;
  return shift < 0 ? res1 : res2;
}

foo2:
cmp r2, #0 @ 7 [c=4 l=2]  *movsi_compare0/0
itte lt
rsblt r2, r2, #0 @ 10 [c=8 l=6]  *p *arm_negsi2/0
lslllt r0, r1, r2 @ 11 [c=8 l=6]  *p mve_lsll
asrlge r0, r1, r2 @ 16 [c=8 l=6]  *p mve_asrl
bx lr @ 57 [c=8 l=4]  *thumb2_return


With foo calling the intrinsic:
int64_t
foo (int64_t value)
{
  return asrl (value, -10);
}
the generated code is the expected:
foo:
mvn r3, #9 @ 6 [c=4 l=4]  *thumb2_movsi_vfp/3   ;; r3 = -10
asrl r0, r1, r3 @ 11 [c=172 l=4]  mve_asrl    ;; shift-right by -10
bx lr @ 19 [c=8 l=4]  *thumb2_return

with the final RTL being:

(insn:TI 6 7 11 2 (set (reg:SI 3 r3 [96])
        (const_int -10 [0xfffffffffffffff6])) "xxx" 543 {*thumb2_movsi_vfp}
     (expr_list:REG_EQUIV (const_int -10 [0xfffffffffffffff6])
        (nil)))
(insn 11 6 12 2 (set (reg/i:DI 0 r0)
        (if_then_else:DI (lt:SI (reg:SI 3 r3 [96])
                (const_int 0 [0]))
            (ashift:DI (reg:DI 0 r0 [orig:98 value ] [98])
                (neg:SI (reg:SI 3 r3 [96])))
            (ashiftrt:DI (reg:DI 0 r0 [orig:98 value ] [98])
                (reg:SI 3 r3 [96])))) "xxx" 4849 {mve_asrl}
     (expr_list:REG_DEAD (reg:SI 3 r3 [96])
        (nil)))
(insn 12 11 23 2 (use (reg/i:DI 0 r0)) "xxx" -1
     (nil))
(note 23 12 19 2 NOTE_INSN_EPILOGUE_BEG)
(jump_insn:TI 19 23 20 2 (simple_return) "xxx" 826 {*thumb2_return}
     (nil)
 -> simple_return)

So finally, given the "Pg" constraint, I'm wondering if there's any
interest in adding the if_then_else part?

Thanks,

Christophe

> Richard.
>
> > Christophe
> >
> > >         Jakub
> > >

Reply via email to