https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124798
Bug ID: 124798
Summary: no_caller_saved_registers attribute vs. spills in the
caller
Product: gcc
Version: 16.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: jakub at gcc dot gnu.org
Target Milestone: ---
Seems we spill exactly the same registers in a caller to
no_caller_saved_registers functions like we do for functions without that
attribute. Can't we rely on the different calling convention?
[[gnu::no_caller_saved_registers]] void foo (void);
[[gnu::no_callee_saved_registers]] void bar (void);
[[gnu::preserve_none]] void baz (void);
void corge (void);
void
qux (void)
{
int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p;
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : "=r" (a), "=r" (b), "=r" (c),
"=r" (d), "=r" (e), "=r" (f), "=r" (g), "=r" (h));
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : "=r" (i), "=r" (j), "=r" (k),
"=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
foo ();
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : : "r" (a), "r" (b), "r" (c), "r"
(d), "r" (e), "r" (f), "r" (g), "r" (h));
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : : "r" (i), "r" (j), "r" (k), "r"
(l), "r" (m), "r" (n), "r" (o), "r" (p));
}
void
fred (void)
{
int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p;
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : "=r" (a), "=r" (b), "=r" (c),
"=r" (d), "=r" (e), "=r" (f), "=r" (g), "=r" (h));
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : "=r" (i), "=r" (j), "=r" (k),
"=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
bar ();
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : : "r" (a), "r" (b), "r" (c), "r"
(d), "r" (e), "r" (f), "r" (g), "r" (h));
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : : "r" (i), "r" (j), "r" (k), "r"
(l), "r" (m), "r" (n), "r" (o), "r" (p));
}
void
garply (void)
{
int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p;
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : "=r" (a), "=r" (b), "=r" (c),
"=r" (d), "=r" (e), "=r" (f), "=r" (g), "=r" (h));
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : "=r" (i), "=r" (j), "=r" (k),
"=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
baz ();
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : : "r" (a), "r" (b), "r" (c), "r"
(d), "r" (e), "r" (f), "r" (g), "r" (h));
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : : "r" (i), "r" (j), "r" (k), "r"
(l), "r" (m), "r" (n), "r" (o), "r" (p));
}
void
plugh (void)
{
int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p;
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : "=r" (a), "=r" (b), "=r" (c),
"=r" (d), "=r" (e), "=r" (f), "=r" (g), "=r" (h));
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : "=r" (i), "=r" (j), "=r" (k),
"=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
corge ();
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : : "r" (a), "r" (b), "r" (c), "r"
(d), "r" (e), "r" (f), "r" (g), "r" (h));
asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7" : : "r" (i), "r" (j), "r" (k), "r"
(l), "r" (m), "r" (n), "r" (o), "r" (p));
}
I see qux and plugh spilling 10 registers across the calls, i.e. relying on
just %rbx, %rbp, %r12 to %r15 being preserved, while the other two functions
spilling 15 registers and relying just on %rbp being preserved.
Can't qux rely also on %rcx/%rdi/%rsi/%rbx/%r8/%r9/%r10/%r11? Whether it can
rely on %rax in functions returning void (if returning something in %rax that
obviously isn't preserved)? Or %rdx in functions not returning something in
%rdx:%rax pair?