NetBSD Problem Report #60272
From www@netbsd.org Sat May 16 14:50:59 2026
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits)
client-signature RSA-PSS (2048 bits))
(Client CN "mail.netbsd.org", Issuer "R13" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id E742E1A923A
for <gnats-bugs@gnats.NetBSD.org>; Sat, 16 May 2026 14:50:58 +0000 (UTC)
Message-Id: <20260516145057.9D5291A923C@mollari.NetBSD.org>
Date: Sat, 16 May 2026 14:50:57 +0000 (UTC)
From: campbell+netbsd@mumble.net
Reply-To: campbell+netbsd@mumble.net
To: gnats-bugs@NetBSD.org
Subject: sys/atomic.h: unnecessary stack spillage
X-Send-Pr-Version: www-1.0
X-From4GNATS: "campbell+netbsd@mumble.net via gnats" <gnats-admin@NetBSD.org>
>Number: 60272
>Category: kern
>Synopsis: sys/atomic.h: unnecessary stack spillage
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: riastradh
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat May 16 14:55:00 +0000 2026
>Last-Modified: Sun May 17 01:40:01 +0000 2026
>Originator: Taylor R Campbell
>Release: current, 11, 10, 9
>Organization:
The AtomicBSD Volition
>Environment:
>Description:
The sys/atomic.h load/store operations do unnecessary
load/store operations on the stack.
Example 1, atomic_load_relaxed:
https://nxr.netbsd.org/xref/src/sys/dev/ic/tpm.c?r=1.29#809
809 static void
810 tpm_rng_get(size_t nbytes, void *cookie)
811 {
812 struct tpm_softc *sc = cookie;
813
814 if (atomic_load_relaxed(&sc->sc_rnddisabled))
815 return; /* tough */
...
819 }
GCC generates the following code, with an unnecessary store to
stack and reload from stack marked with (*):
0000000000000480 <tpm_rng_get>:
tpm_rng_get():
/home/riastradh/netbsd/current/src/sys/dev/ic/tpm.c:811
480: a9bd7bfd stp x29, x30, [sp, #-48]!
484: 910003fd mov x29, sp
488: f9000bf3 str x19, [sp, #16]
48c: aa0103f3 mov x19, x1
/home/riastradh/netbsd/current/src/sys/dev/ic/tpm.c:814
490: 39445022 ldrb w2, [x1, #276]
494: 12001c42 and w2, w2, #0xff
498: 3900bfe2 strb w2, [sp, #47] // (*)
49c: 3940bfe1 ldrb w1, [sp, #47] // (*)
4a0: 72001c3f tst w1, #0xff
4a4: 54000080 b.eq 4b4 <tpm_rng_get+0x34> // b.none
Example 2, atomic_store_relaxed:
https://nxr.netbsd.org/xref/src/sys/dev/pci/if_aq.c?r=1.50#6050
6012 static void
6013 aq_handle_reset_work(struct work *work, void *arg)
6014 {
...
6048 IFNET_UNLOCK(ifp);
6049
6050 atomic_store_relaxed(&sc->sc_reset_pending, 0);
6051 }
GCC generates the following code:
/home/riastradh/netbsd/current/src/sys/dev/pci/if_aq.c:6050
5fd8: b9007fff str wzr, [sp, #124] // (*)
5fdc: f240067f tst x19, #0x3
5fe0: 54000101 b.ne 6000 <aq_handle_reset_work+0x1bc> // b.any
/home/riastradh/netbsd/current/src/sys/dev/pci/if_aq.c:6050 (discriminator 3)
5fe4: b9407fe0 ldr w0, [sp, #124] // (*)
5fe8: b90f2ac0 str w0, [x22, #3880]
This happens because of the following definitions (ignoring the
kcsan variants, and the hash-locked versions for hppa/sparc):
https://nxr.netbsd.org/xref/src/sys/sys/atomic.h#432
432 #define __BEGIN_ATOMIC_LOAD(p, v) \
433 __typeof__(*(p)) v = *(p)
434 #define __END_ATOMIC_LOAD(v) \
435 v
...
440 #define __DO_ATOMIC_STORE(p, v) \
441 *p = v
445 #define atomic_load_relaxed(p) \
446 ({ \
447 const volatile __typeof__(*(p)) *__al_ptr = (p); \
448 __ATOMIC_PTR_CHECK(__al_ptr); \
449 __BEGIN_ATOMIC_LOAD(__al_ptr, __al_val); \
450 __END_ATOMIC_LOAD(__al_val); \
451 })
...
471 #define atomic_store_relaxed(p,v) \
472 ({ \
473 volatile __typeof__(*(p)) *__as_ptr = (p); \
474 __typeof__(*(p)) __as_val = (v); \
475 __ATOMIC_PTR_CHECK(__as_ptr); \
476 __DO_ATOMIC_STORE(__as_ptr, __as_val); \
477 })
The net effect is that atomic_load_relaxed(p) roughly expands
to:
volatile typeof(*p) *__al_ptr = p;
typeof(*__al_ptr) __al_val = *p;
return __al_val;
And atomic_store_relaxed(p, v) roughly expands to:
volatile typeof(*p) *__as_ptr = p;
typeof(*p) __as_val = v;
*__as_ptr = __as_val;
Note that the intermediate variable __al_val/__as_val will have
all the qualifiers of p, which is generally volatile-qualified,
which provokes the compiler to issue an extra store/load.
>How-To-Repeat:
code inspection
>Fix:
Several options:
1. Teach the kernel build to just use C11 stdatomic.h.
The code is already there to take advantage of it -- it's
just not usable right now because of -nostdinc, so it's a
matter of setting up the toolchain the right way to make
stdatomic.h available.
2. Use the compiler __atomic_* builtins.
Requires some research to find how stable they are, and
whether they're compatible between gcc and clang (and,
ideally, how onerous they are for pcc).
3. Use __typeof_unqual__ instead of __typeof__ to strip the
volatile qualifier.
Caveat: This won't work in the version of clang we have in
tree. But it will work for gcc.
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: kern-bug-people->riastradh
Responsible-Changed-By: riastradh@NetBSD.org
Responsible-Changed-When: Sat, 16 May 2026 15:08:32 +0000
Responsible-Changed-Why:
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60272 CVS commit: src/sys/sys
Date: Sat, 16 May 2026 15:10:55 +0000
Module Name: src
Committed By: riastradh
Date: Sat May 16 15:10:55 UTC 2026
Modified Files:
src/sys/sys: atomic.h
Log Message:
sys/atomic.h: Avoid needless stack spillage with typeof_unqual.
Using typeof_unqual strips the volatile qualifier on the local
temporaries, so the compiler doesn't issue store/load cycles for
access to them.
PR kern/60272: sys/atomic.h: unnecessary stack spillage
To generate a diff of this commit:
cvs rdiff -u -r1.27 -r1.28 src/sys/sys/atomic.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60272 CVS commit: src/sys/sys
Date: Sun, 17 May 2026 01:36:21 +0000
Module Name: src
Committed By: riastradh
Date: Sun May 17 01:36:21 UTC 2026
Modified Files:
src/sys/sys: atomic.h
Log Message:
sys/atomic.h: Nix const qualifiers on temporaries added in previous.
The temporaries aren't modified, so in principle const is correct,
but on some ports (notably, sparc) we pass pointers to them through
__UNVOLATILE which objects to the const qualifier:
> In file included from /home/source/ab/HEAD/src/lib/librump/../../sys/rump/librump/rumpkern/sleepq.c:28:
> /home/source/ab/HEAD/src/lib/librump/../../sys/rump/librump/rumpkern/sleepq.c: In function 'lwp_unlock_to':
> /home/source/ab/HEAD/src/lib/librump/../../sys/rump/../sys/cdefs.h:202:49: error: cast discards 'const' qualifier from pointer target type [-Werror=cast-qual]
> 202 | #define __UNVOLATILE(a) ((void *)(unsigned long)(volatile void *)(a))
> | ^
> /home/source/ab/HEAD/src/lib/librump/../../sys/rump/../sys/atomic.h:448:30: note: in expansion of macro '__UNVOLATILE'
> 448 | __do_atomic_store(p, __UNVOLATILE(&v), sizeof(v))
> | ^~~~~~~~~~~~
> /home/source/ab/HEAD/src/lib/librump/../../sys/rump/../sys/atomic.h:495:9: note: in expansion of macro '__DO_ATOMIC_STORE'
> 495 | __DO_ATOMIC_STORE(__as_ptr, __as_val); \
> | ^~~~~~~~~~~~~~~~~
> /home/source/ab/HEAD/src/lib/librump/../../sys/rump/librump/rumpkern/sleepq.c:187:9: note: in expansion of macro 'atomic_store_release'
> 187 | atomic_store_release(&l->l_mutex, new);
> | ^~~~~~~~~~~~~~~~~~~~
> cc1: all warnings being treated as errors
PR kern/60272: sys/atomic.h: unnecessary stack spillage
To generate a diff of this commit:
cvs rdiff -u -r1.28 -r1.29 src/sys/sys/atomic.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.49 2026/05/14 01:52:41 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.