> Just to probe the problem space a bit: how many registers does the widest
> possible destination have?  The reason for asking is that, for two registers,
> it's possible to arrange "no overlap of the high half" using:
>
> #include <stdint.h>
>
> #define TEST(X) \
>     __uint128_t res; \
>     asm ("foo\t%0, %1" : "=r,&r" (res) : "0,r" (X)); \
>     return res;
>
> __uint128_t f1(uint64_t x) { TEST(x); }
> __uint128_t f2(uint64_t x, uint64_t y) { TEST(y); }
> __uint128_t f3(uint64_t x, uint64_t y, uint64_t z) { TEST(z); }
>
> which produces:
>
> f1(unsigned long):
>         foo   x0, x0
>         ret
> f2(unsigned long, unsigned long):
>         mov     x2, x1
>         foo   x0, x2
>         ret
> f3(unsigned long, unsigned long, unsigned long):
>         foo   x0, x2
>         ret
>
> It sounds like the main difference here is that you want "no overlap
> with the low half".  Perhaps that could be handled using a "match high"
> constraint that would otherwise work similarly to the current 0-9
> constraints.
>
> If the destination is always 2 registers, an alternative approach would
> be to have register filters for odd and even registers, with three
> alternatives:
>
> - odd destination, even source
> - even destination, odd source
> - any destination, earlyclobbered
>
> I've no idea how well IRA would allocate that though.  (Probably not very.
> It wasn't an intended use case.)
>
> If nregs can be more than 1 in:
>
>   unsigned int wide_nregs = riscv_hard_regno_nregs (wide_regno, wide_mode);
>   unsigned int nregs = riscv_hard_regno_nregs (regno, mode);
>
>   /* Overlap is only allowed in the highest-numbered part of the wider
>      destination.  */
>   if (regno == wide_regno)
>     return false;
>
>   if (regno >= wide_regno + (wide_nregs - nregs))
>     return true;
>
>   /* No overlap is OK.  */
>   if (regno < wide_regno)
>     return true;
>
>   return false;
>
> then it looks like the regno + nregs - 1 == wide_regno is OK.
> Is that how it works?  Or is nregs always 1 in practice?
>
> Another alternative would be to have something like "earlyclobber
> low part", although "low part" might be difficult to define.
>
> I'm just playing devil's advocate here, rather than objecting to the
> proposal.

Perfectly fine to play devil's advocate.  If there is a simpler way, I'll be 
happy to go for it.

As Jeff wrote, we have groups of "1", 2, 4, 8, with nregs = 1, 2, 4, 8.  So the 
widest destination has 8 regs.

For a widening/zext insn from 2 to 8 (extend 4x), the allowed overlap would be 
in the last 2 regnos, analogous in the last 4 for "4 to 8" or in the last regno 
for "1 to 8".

If the source is a group of 2 and the destination is a group of 8 the following 
would be illegal
     vzext.vf4  v0,v0   # destination is v0,v1,v2,v3,v4,v5,v6,v7
but this would be OK:
     vzext.vf4  v0,v6

The opposite applies to narrowing, "2 to 8" allows overlap only in the first 2 
regnos, etc.

The source = destination part we currently handle by an earlyclobber, and for 
just halves a "half/lowpart early-clobber" might indeed work.  For the more 
advanced cases, we'd additionally need something like "7/8, 6/8 = 3/4, 4/8 = 
1/2" early-clobbers.  For narrowing, 1/8, 1/4, 1/2, unless I'm forgetting 
something.  Here it's always about source and destination.

However, there's another issue with our scatter instruction.  The spec 
prohibits reading the same register with different element size, so we'd need a 
constraint on one source dependent on the other, forcing them to be unequal.
This is PR113695 and not about the destination, i.e. not compatible with an 
earlyclobber.  Basically a "never-matching" constraint? :)

I'd say the problem space is indeed kind of regular still.  If there's an 
acceptable way of encoding these constraints without going "full dynamic", that 
might be preferable.  On the other hand, if used sparingly, maybe dependent 
constraints won't be too bad?

-- 
Regards
 Robin

Reply via email to