> 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