NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/60426 (Signal handler corrupts AVX (YMM) registers)



The following reply was made to PR kern/60426; it has been noted by GNATS.

From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: Robert Elz <kre%munnari.OZ.AU@localhost>
Cc: gnats-bugs%netbsd.org@localhost, gnats-admin%netbsd.org@localhost, netbsd-bugs%netbsd.org@localhost,
	bsiegert%netbsd.org@localhost, martin%netbsd.org@localhost
Subject: Re: kern/60426 (Signal handler corrupts AVX (YMM) registers)
Date: Thu, 9 Jul 2026 10:29:18 +0000

 > Date: Thu, 09 Jul 2026 10:33:14 +0700
 > From: Robert Elz <kre%munnari.OZ.AU@localhost>
 >
 >     Date:        Wed,  8 Jul 2026 23:48:44 +0000 (UTC)
 >     From:        riastradh%NetBSD.org@localhost
 >=20
 >   | But the signal delivery logic, and return-from-signal logic, simply
 >   | doesn't save and restore anything beyond the xmm registers.
 >=20
 > Does it really need to?   There are lots of restrictions on what
 > signal handlers are allowed to do, couldn't one of them simply be
 > that if these YMM registers are to be touched in the signal handler,
 > (or anything it calls) then it becomes the handler's responsibility
 > to save and restore them?
 
 The xmm registers, which have always been available on amd64 since the
 beginning, are aliases for the low halves of the ymm registers.  Any
 SSE2 instruction (again: available on amd64 since the beginning)
 writing to xmmN will generally zero the high half of ymmN as a side
 effect.
 
 So it's not enough to say that signal handlers must avoid using new
 instructions that modify the ymm registers, because old instructions
 which have always been reliable in signal handlers modify the ymm
 registers too.
 
 > Since (as I understand things) not all processors even have the things
 > (if running on one old enough) and I think I have seen firmware options
 > to disable them as well, I am guessing that the compilers don't just
 > emit instructions to use those registers for everyday normal code, and
 > that the application needs to do something special to access them.
 
 Compilers will routinely generate SSE2 instructions for amd64 and i686
 code.  And libraries often use SSE2 instructions for basic (and
 async-signal-safe) functions like memcpy and strlen, like these in
 glibc (used on i686 and amd64 targets, respectively):
 
 https://sourceware.org/git/?p=3Dglibc.git;a=3Dblob;f=3Dsysdeps/i386/i686/mu=
 ltiarch/memcpy-sse2-unaligned.S;h=3Df5d3582d638c80043e77459deaa6a76d411e28c=
 0;hb=3Df762ccf84f122d1354f103a151cba8bde797d521
 https://sourceware.org/git/?p=3Dglibc.git;a=3Dblob;f=3Dsysdeps/x86_64/multi=
 arch/strlen-sse2.S;h=3D1b118a0245b4a40e4f8e1c434cbb7a0426efdfc5;hb=3Df762cc=
 f84f122d1354f103a151cba8bde797d521
 
 I expect golang is doing similarly, either in a library or in compiler
 codegen, in its signal handler, e.g. to copy a 16-byte object on the
 stack using a pair of MOVDQA instructions to move from memory to xmmN
 and back to memory, which will clear the high half of ymmN.
 
 Here's an example of gcc16 doing something like that, with only the
 -O2 option, nothing to ask it to go out of its way to use the vector
 unit:
 
 /* input */
 struct bigstruct {
 	long x[2];
 };
 
 void
 copybigstruct(struct bigstruct *dst, const struct bigstruct *src)
 {
 	*dst =3D *src;
 }
 
 /* output */
 "copybigstruct":
         movdqu  xmm0, XMMWORD PTR [rsi]
         movups  XMMWORD PTR [rdi], xmm0
         ret
 
 https://godbolt.org/z/1ccGYTEdE
 



Home | Main Index | Thread Index | Old Index