Hi,
On Wed, Jun 24, 2026 at 02:53:06PM +0200, Michael Matz wrote:
> Hello,
>
> On Wed, 24 Jun 2026, Stefan Schulze Frielinghaus via Gcc wrote:
>
> > Live range splitting for hard regs which are assigned to some pseudo is
> > already implemented. However, live ranges of hard regs which are live
> > due to hard register assignments
>
> Define "hard register assignments". Because ...
Basically any set insn where the left-hand side is a hard register.
Such insns may stem from register asm (assuming that
-fstrict-extended-asm [1] isn't used) during expand but could also be
directly emitted by a target.
We already forbid certain forms of optimizations in e.g. cselib/combine
which could lead to those scenarios. So instead of fighting
optimization passes I was rather hoping for a general solution.
>
> > 1: r5=...
> > 2: r101=exp(r100)
> > ...
> > 9: // some user of r5
>
> ... this situation should be unachievable with well-formed source code
> (and absent specific bugs in gcc). I could only imagine r5 to be
> associated with a global register variable, in which case that would make
> r5 a fixed reg, inherently conflicting with a single-register constraint
> forcing r5 on insn 2. Any other situation (that I can imagine) would have
> to do with insns 1 and 9 coming from inline asm, with either local
> register variables or single-reg constraints in their respective output
> (insn 1) and input (insn 9) constraints. In such cases the value from
> output to input should already flow through a pseudo register, because we
> explicitely _do not guarantee_ that a hardreg set in one inline asm still
> contains the value at a different later inline asm. Pseudo code:
>
> int x;
> asm("" : "=r5" (x)); // insn 1 'r5 = ...something'
> code;
> asm("use r5"); // insn 9, but not using correct constraints
>
> Not supported. Or:
>
> register int x asm("r5");
> x = exp; // insn 1
> code;
> foo(x); // insn 9, use of r5
>
> Not supported (in the sense that the user couldn't expect that r5 is
> actually used in insn 1 or 9, local regvars are only guaranteed to sit in
> their specified register when used as inline-asm operands). Or random
> other combinations of that idea that hinge on the expectation that from
> insn 1 to insn 9 the register 5 magically retains its value.
>
> This example is similar but supported:
>
> int x;
> asm("" : "=r5" (x)); // insn 1 'r5 = ...something'
> code;
> asm("use %0" : : "r5" (x)); // insn 9 'use r5'
>
> here the output/input are r5, as in your situation, but the actual value
> from 1 to 9 will be carried through the local variable 'x': a pseudo or
> memory:
>
> 1: r5=...
> 1': r102=r5 // from inline-asm output setup
> 2: r101=exp(r100)
> ...
> 9': r5=r102 // from inline-asm input setup
> 9: // some user of r5
>
> The value doesn't (or shouldn't) flow through r5, but through some
> pseudo/memory. It's the reg-allocators duty then to not assign it to r5
> itself, on the grounds that it would conflict with r100, which is
> constrained to r5 itself, or if it does, generated the appropriate
> compensation code.
>
> So, make an example to show us?
See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125780 where we are
given
uint32_t xdiv (uint32_t a, uint32_t b)
{
register int r18 __asm("18") = 0x1122;
__asm volatile ("seh ; %0" : "+r" (r18), "+r" (a));
uint32_t c = a / b; // r18 is live here
__asm volatile ("clh ; %0" : "+r" (r18), "+r" (c));
return c;
}
Due to register asm, hard register r18 is live across the division. The
insn implementing the division makes use of a single-register
constraint referring to r18 which means we finally ICE during RA which
is pretty annoying. I'm not willing to declare this a user error
especially in light of the clear documentation of local register asm and
where they should have an effect. Again, I would like to stress that
this is not restricted to register asm only but we may also run into
similar scenarios if targets emit hard register assignments directly.
Cheers,
Stefan
[1] https://gcc.gnu.org/pipermail/gcc-patches/2026-May/717805.html