NetBSD Problem Report #60773

From martin@last-of-the-heroes.duskware.de  Wed Sep 23 13:42:52 2026
Return-Path: <martin@last-of-the-heroes.duskware.de>
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 "YR1" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id C59E11A923B
	for <gnats-bugs@gnats.NetBSD.org>; Wed, 23 Sep 2026 13:42:51 +0000 (UTC)
Message-Id: <20260923134248.47C8AE1FFBB@mail.duskware.de>
Date: Wed, 23 Sep 2026 15:42:43 +0200 (CEST)
From: martin@NetBSD.org
Reply-To: martin@NetBSD.org
To: gnats-bugs@NetBSD.org
Subject: __sync_val_compare_and_swap_1 test failures
X-Send-Pr-Version: 3.95
X-From4GNATS: "martin@NetBSD.org via gnats" <gnats-admin@NetBSD.org>

>Number:         60773
>Notify-List:    skrll, rin
>Category:       toolchain
>Synopsis:       __sync_val_compare_and_swap_1 test failures
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    toolchain-manager
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Sep 23 13:45:00 +0000 2026
>Last-Modified:  Sat Sep 26 14:30:01 +0000 2026
>Originator:     Martin Husemann
>Release:        NetBSD 11.99.8
>Organization:
The NetBSD Foundation, Inc.
>Environment:
System: NetBSD last-of-the-heroes.duskware.de 11.99.8 NetBSD 11.99.8 (GENERIC) #412: Tue Sep 22 09:43:24 CEST 2026 martin@seven-days-to-the-wolves.aprisoft.de:/work/src/sys/arch/landisk/compile/GENERIC landisk
Architecture: sh3el
Machine: landisk
>Description:

Some CAS test cases go wrong on the landisk tests
https://www.NetBSD.org/~martin/landisk-atf

>How-To-Repeat:

cd /usr/tests/lib/libc/atomic && ./t___sync_compare_and_swap __sync_val_compare_and_swap_1

note:

failed: /work/src/tests/lib/libc/atomic/t___sync_compare_and_swap.c:189: successful case: res expects 0x88 but 0xffffff88

>Fix:
n/a

>Release-Note:

>Audit-Trail:
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Thu, 24 Sep 2026 03:18:03 +0300

 On Wed, Sep 23, 2026 at 13:45:00 +0000, martin@NetBSD.org via gnats wrote:

 > failed: /work/src/tests/lib/libc/atomic/t___sync_compare_and_swap.c:189: successful case: res expects 0x88 but 0xffffff88

 common/lib/libc/atomic/atomic_cas_8_cas.c defines
 sync_val_compare_and_swap_1 to return signed int8_t

 _atomic_cas_8 returns correct uin8_t value 0x88, then
 sync_val_compare_and_swap_1 sign extends it before returning.


 The change was in common/lib/libc/atomic/atomic_init_testset.c

   revision 1.22
   date: 2026-05-03 14:48:02 +0300;  author: skrll;  state: Exp;  lines: +40 -5;  commitid: z77RclIjL3bYwmEG;
   PR/56839 GCC emits wrong codes for compare_and_swap_1 bultins on armv5 (el & eb)

   There is mismatch in signedness of the GCC builtin __sync_* function arguments
   and the _atomic_* functions so we cannot directly alias them. Instead write
   the __sync_* functions with signed arguments and pass them the unsigned
   _atomics_* functions.

 So the testcase and the function declaration do not match.

 PS: Please, can someone write a manual page for these?  It's scores of
 function names that all look alike with a sprinkle of scrabble bag
 spillover randomly glued in unexpected places.

 gcc info only talks about "__sync_val_compare_and_swap" and refers to
 Intel's Itanium psABI, which is probably printed on fine leafs of
 unobtanium (if you are lucky - with a microfiche copy on archive.org
 w/out an OCR layer).  Anyway, finding this information shouldn't be a
 wild goose chase or an exercise in RTFS and repo archeology.

 -uwe

Responsible-Changed-From-To: port-sh3-maintainer->toolchain-manager
Responsible-Changed-By: riastradh@NetBSD.org
Responsible-Changed-When: Thu, 24 Sep 2026 00:47:41 +0000
Responsible-Changed-Why:
not sh3-specific, also applies to sparc


From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc: port-sh3-maintainer@netbsd.org, gnats-admin@netbsd.org,
	netbsd-bugs@netbsd.org, martin@NetBSD.org, skrll@NetBSD.org,
	uwe@NetBSD.org
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Thu, 24 Sep 2026 00:50:36 +0000

 Maybe we should just close this as a duplicate of PR 56839 (GCC emits
 wrong codes for compare_and_swap_1 bultins on armv5 (el & eb))?

 The same issue is turning up on sparc:

 FAILED: /tmp/build/2026.09.22.23.22.21-sparc/src/tests/lib/libc/atomic/t___=
 sync_compare_and_swap.c:189: successful case: res expects 0x88 but 0xffffff=
 88

 https://releng.netbsd.org/b5reports/sparc/2026/2026.09.22.23.22.21/test.htm=
 l#lib_libc_atomic_t___sync_compare_and_swap___sync_val_compare_and_swap_1

 But not on any other architectures I checked.

 > Date: Thu, 24 Sep 2026 03:18:03 +0300
 > From: Valery Ushakov <uwe@stderr.spb.ru>
 >=20
 > PS: Please, can someone write a manual page for these?  It's scores of
 > function names that all look alike with a sprinkle of scrabble bag
 > spillover randomly glued in unexpected places.

 If we do that, we will still have to be careful to distinguish:

 (a) the compiler builtins that gcc/clang recognize
 from
 (b) the elf library symbols that gcc/clang generate calls to.

 And I suspect they are not the same...

 > gcc info only talks about "__sync_val_compare_and_swap" and refers to
 > Intel's Itanium psABI, which is probably printed on fine leafs of
 > unobtanium (if you are lucky - with a microfiche copy on archive.org
 > w/out an OCR layer).  Anyway, finding this information shouldn't be a
 > wild goose chase or an exercise in RTFS and repo archeology.

 Didn't seem too hard to find a plausible referent with a web search
 for `intel itanium psabi':

 https://refspecs.linuxfoundation.org/elf/IA64-SysV-psABI.pdf

 We should archive this ourselves, and at least link to it from
 <https://wiki.NetBSD.org/abi>.

 Unfortunately, it is not entirely clear on signedness, also appears to
 be more about the compiler builtins than the elf library symbols
 judging by the ellipsis part...  (I also have no idea what the
 sentence about the ellipsis is supposed to mean!)

     7.4.3 Atomic Compare-and-swap Operation

     "int __sync_bool_compare_and_swap (type* ptr, type oldvalue, type
      newvalue, ...)"

     "type __sync_val_compare_and_swap (type* ptr, type oldvalue, type
      newvalue, ...)"

     Where type may be one of int, long, long long, unsigned int,
     unsigned long, unsigned long long.  The ellipsis (...) refers to
     an optional list of variables protected by the memory barrier.

 I just did a quick search for the string `__sync_val'; perhaps if you
 actually read the rest of the document it will say how the symbols are
 supposed to be defined.

 But I bet it's just ambiguous and got randomly chosen differently on
 different architectures for gcc's codegen.

From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc: Taylor R Campbell <riastradh@NetBSD.org>,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Thu, 24 Sep 2026 13:34:24 +0300

 On Thu, Sep 24, 2026 at 00:50:36 +0000, Taylor R Campbell wrote:

 > Maybe we should just close this as a duplicate of PR 56839 (GCC emits
 > wrong codes for compare_and_swap_1 bultins on armv5 (el & eb))?

 That makes sense probably.

 I'd still love to see this documented.  I did have Itanium psABI
 stashed, apparently, (from my Intel days), so I looked up what is
 written there and the gcc info seems to be just a TLDR sumamry for
 that.

 Which leaves me with the question, what is the relation between
 __sync_val_compare_and_swap and __sync_val_compare_and_swap_1 &c?  Is
 __sync_val_compare_and_swap supposed to be a kind of type-generic
 macro?  Or if the "type" in its signature is implementaion defined and
 there's only one instance of it, with that specific type? Etc, etc...

 We already have atomic*(3), but nothing for __sync*.  I'll do the mdoc
 part if given the prose for the manpage.

 -uwe

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60773 CVS commit: src/tests/lib/libc/atomic
Date: Thu, 24 Sep 2026 12:07:53 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Thu Sep 24 12:07:53 UTC 2026

 Modified Files:
 	src/tests/lib/libc/atomic: t___sync_compare_and_swap.c

 Log Message:
 t___sync_compare_and_swap: Check both signednesses of everything.

 PR 60773: sh3 __sync_val_compare_and_swap_1 test failures


 To generate a diff of this commit:
 cvs rdiff -u -r1.5 -r1.6 \
     src/tests/lib/libc/atomic/t___sync_compare_and_swap.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc: Taylor R Campbell <riastradh@NetBSD.org>,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Thu, 24 Sep 2026 15:14:33 +0300

 Tangential to the topic at hand, but since I had to read the code, I
 might as well ask - why is atomic_init_testset.c statically
 initializes the _fn variables to _up functions only immediately to set
 them to _mp functions in __libc_atomic_init (and later set them back
 to _up if we are on a UP system and rasctl(2) was successful)?  Any
 reason not to init the vars to _mp directly?  _mp functions use the
 locks array that is statically initialized, so it should be safe to
 call them before __libc_atomic_init, shouldn't it?

 If there's a reason why it's done in this way, it probably warrants a
 comment.

 TIA.

 -uwe

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Valery Ushakov <uwe@stderr.spb.ru>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Thu, 24 Sep 2026 12:37:51 +0000

 > Date: Thu, 24 Sep 2026 15:14:33 +0300
 > From: Valery Ushakov <uwe@stderr.spb.ru>
 >=20
 > Tangential to the topic at hand, but since I had to read the code, I
 > might as well ask - why is atomic_init_testset.c statically
 > initializes the _fn variables to _up functions only immediately to set
 > them to _mp functions in __libc_atomic_init (and later set them back
 > to _up if we are on a UP system and rasctl(2) was successful)?  Any
 > reason not to init the vars to _mp directly?  _mp functions use the
 > locks array that is statically initialized, so it should be safe to
 > call them before __libc_atomic_init, shouldn't it?

 The code was originally added with no initializer by ad@ in 2008,
 under the assumption the constructor would always run before use, and
 the constructor started with the always-safe _mp variants before
 deciding on the basis of sysctl whether to use the cheaper _up
 variants if safe:

 https://mail-index.netbsd.org/source-changes/2008/02/09/msg002110.html
 https://nxr.netbsd.org/xref/src/common/lib/libc/atomic/atomic_init_testset.=
 c?r=3D1.1#63

 skrll@ added the initializer because the constructor didn't always run
 before use, but it's unclear why he chose _up:

 https://mail-index.netbsd.org/source-changes/2009/01/30/msg216500.html
 https://nxr.netbsd.org/xref/src/common/lib/libc/atomic/atomic_init_testset.=
 c?r=3D1.6#63

 This actually caused some trouble for us later on with the _up
 variants getting inadvertently used in ld.elf_so until martin@ changed
 it to call __libc_atomic_init in 2023:

 PR lib/57628: multithread programs may deadlock in ld.elf_so (sparc on
 sparc64)
 https://gnats.NetBSD.org/57628
 https://mail-index.netbsd.org/source-changes/2023/10/03/msg147830.html
 https://nxr.netbsd.org/xref/src/libexec/ld.elf_so/rtld.c?r=3D1.216#72

 > If there's a reason why it's done in this way, it probably warrants a
 > comment.

 I think the reason is `hysterical raisins' and this warrants a commit
 to make the static initializers use _mp and then delete the
 now-redundant initial assignments in __libc_atomic_init.

From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc: Taylor R Campbell <riastradh@NetBSD.org>,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Thu, 24 Sep 2026 20:17:00 +0300

 On Thu, Sep 24, 2026 at 00:20:02 +0000, Valery Ushakov via gnats wrote:

 >  common/lib/libc/atomic/atomic_cas_8_cas.c defines
 >  sync_val_compare_and_swap_1 to return signed int8_t
 >  
 >  _atomic_cas_8 returns correct uin8_t value 0x88, then
 >  sync_val_compare_and_swap_1 sign extends it before returning.

 For the record, just to make it explicit.  Consider simplified:

 #include <inttypes.h>
 #include <stdint.h>
 #include <stdio.h>

 #define OLDVAL (0x1122334455667788UL)
 #define NEWVAL (0x8090a0b0c0d0e0f0UL)


 int
 main()
 {
     volatile uint8_t val;
     uint8_t oldval;
     uint8_t newval;
     uint8_t expval;
     uint8_t expres;
     uint8_t res;


     val    = (uint8_t)OLDVAL;
     oldval = (uint8_t)OLDVAL;
     newval = (uint8_t)NEWVAL;
     expval = (uint8_t)NEWVAL;
     expres = (uint8_t)OLDVAL;
     res = __sync_val_compare_and_swap_1(&val, oldval, newval);

     printf("expected %x, res = %x\n", expres, res);
     return 0;
 }



 => 0x7a095778 <_atomic_cas_8+16>:     jsr     @r0
 (gdb) si
 0x7a09563c in ?? () from /usr/lib/libc.so.12
 1: x/i $pc
 => 0x7a09563c:     extu.b  r5,r5

 We have entered _atomic_cas_8_up (static, so no symbol):

 (gdb) finish
 Run till exit from #0  0x7a09563c in ?? () from /usr/lib/libc.so.12
 0x7a09577c in _atomic_cas_8 () from /usr/lib/libc.so.12
 1: x/i $pc
 => 0x7a09577c <_atomic_cas_8+20>:     lds.l   @r15+,pr
 (gdb) p/x $r0
 $1 = 0x88

 We've got correct old uint8_t value (unsigned)

 (gdb) si
 0x7a09577e in _atomic_cas_8 () from /usr/lib/libc.so.12
 1: x/i $pc
 => 0x7a09577e <_atomic_cas_8+22>:     rts
 (gdb)
 0x7a031536 in __sync_val_compare_and_swap_1 () from /usr/lib/libc.so.12
 1: x/i $pc
 => 0x7a031536 <__sync_val_compare_and_swap_1+18>:     exts.b  r0,r0
 (gdb) si
 0x7a031538 in __sync_val_compare_and_swap_1 () from /usr/lib/libc.so.12
 1: x/i $pc
 => 0x7a031538 <__sync_val_compare_and_swap_1+20>:     lds.l   @r15+,pr
 (gdb) p/x $r0
 $2 = 0xffffff88

 Now we are back to __sync_val_compare_and_swap_1 and it needs to
 return int8_t (signed) value so it converts the uint8_t result of
 _atomic_cas_8_up by sign-extending (exts.b) the r0.


 -uwe

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Valery Ushakov <uwe@stderr.spb.ru>
Cc: gnats-bugs@netbsd.org, Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Fri, 25 Sep 2026 14:41:44 +0000

 > Date: Thu, 24 Sep 2026 20:17:00 +0300
 > From: Valery Ushakov <uwe@stderr.spb.ru>
 >=20
 > For the record, just to make it explicit.  Consider simplified:
 > [...]

 There's probably some shenanigans going on because __sync_* are not
 normal functions but magic builtins with implicit prototypes.  Is
 gcc's implicit prototype signed or unsigned?

 uint8_t u8_sync_val_compare_and_swap_1(volatile uint8_t *, uint8_t, uint8_t=
 );
 int8_t s8_sync_val_compare_and_swap_1(volatile int8_t *, int8_t, int8_t);

 Under these prototypes, I compared three functions:

 - main__ calls the __sync_* builtin
 - main_u8 calls u8_sync_*
 - main_s8 calls s8_sync_*

 All three actually use uint8_t for all local variables, just like
 t___sync_compare_and_swap.c does.

 (I don't natively speak sh3, so please correct me if I've guessed any
 of the meaning wrong.)

 Here are the relevant excerpts -- in each case, the first jsr calls
 the sync function, and the second calls printf, so what's interesting
 is what happens between the two:

 00000000 <main__>:
 ...
   10:	d0 07       	mov.l	30 <main__+0x30>,r0	! 0 <main__>
   12:	96 0c       	mov.w	2e <main__+0x2e>,r6	! f0
   14:	40 0b       	jsr	@r0
   16:	74 03       	add	#3,r4
   18:	66 03       	mov	r0,r6
   1a:	d0 06       	mov.l	34 <main__+0x34>,r0	! 0 <main__>
   1c:	d4 06       	mov.l	38 <main__+0x38>,r4	! 0 <main__>
   1e:	40 0b       	jsr	@r0
 ...
 			30: R_SH_DIR32	__sync_val_compare_and_swap_1
 			34: R_SH_DIR32	printf
 ...
 0000003c <main_u8>:
 ...
   4c:	d0 07       	mov.l	6c <main_u8+0x30>,r0	! 0 <main__>
   4e:	96 0c       	mov.w	6a <main_u8+0x2e>,r6	! f0
   50:	40 0b       	jsr	@r0
   52:	74 03       	add	#3,r4
   54:	66 03       	mov	r0,r6
   56:	d0 06       	mov.l	70 <main_u8+0x34>,r0	! 0 <main__>
   58:	d4 06       	mov.l	74 <main_u8+0x38>,r4	! 0 <main__>
   5a:	40 0b       	jsr	@r0
 ...
 			6c: R_SH_DIR32	u8_sync_val_compare_and_swap_1
 			70: R_SH_DIR32	printf
 ...
 00000078 <main_s8>:
 ...
   82:	d0 08       	mov.l	a4 <main_s8+0x2c>,r0	! 0 <main__>
   84:	e6 f0       	mov	#-16,r6
   86:	e5 88       	mov	#-120,r5
   88:	40 0b       	jsr	@r0
   8a:	74 03       	add	#3,r4
   8c:	66 0c       	extu.b	r0,r6
   8e:	d0 06       	mov.l	a8 <main_s8+0x30>,r0	! 0 <main__>
   90:	95 07       	mov.w	a2 <main_s8+0x2a>,r5	! 88
   92:	d4 06       	mov.l	ac <main_s8+0x34>,r4	! 0 <main__>
   94:	40 0b       	jsr	@r0
 ...
 			a4: R_SH_DIR32	s8_sync_val_compare_and_swap_1
 			a8: R_SH_DIR32	printf
 ...

 Note that main_s8 has an additional extu.b instruction in it!  I'm
 guessing this means `extend unsigned byte', i.e., zero-extend result
 in r0 to printf argument in r6.  So it looks like gcc treats
 __sync_val_compare_and_swap_1 as if it has the signed prototype:
 main__ matches main_u8, not main_s8.

 If I disassemble t___sync_compare_and_swap.o, I see there's also an
 extu.b instruction:

 000009b8 <atfu___sync_val_compare_and_swap_1_body>:
 ...

 	// Load expval 0xf0 into r11, which is callee-saves so
 	// __sync_val_compare_and_swap_1 should preserve it:
      9d0:       9b 52           mov.w   a78 <atfu___sync_val_compare_and_sw=
 ap_1_body+0xc0>,r11  ! f0
 ...
 	// Load expres 0x88 into r10, which is also callee-saves so
 	// __sync_val_compare_and_swap_1 should preserve it:
      9d4:       9a 51           mov.w   a7a <atfu___sync_val_compare_and_sw=
 ap_1_body+0xc2>,r10  ! 88
 ...
 	// Load __sync_val_compare_and_swap_1 function into r1 to
 	// call:
      9d8:       d1 2a           mov.l   a84 <atfu___sync_val_compare_and_sw=
 ap_1_body+0xcc>,r1   ! 98
 ...
      9e8:       01 03           bsrf    r1
 	// res in r0.  Move res to r9, reuse r0 for the stored val,
 	// move stored val to r6, and check whether the stored val is
 	// what we expect:
      9ea:       64 d3           mov     r13,r4
      9ec:       69 03           mov     r0,r9
      9ee:       84 8f           mov.b   @(15,r8),r0
      9f0:       66 0c           extu.b  r0,r6
 	// val =3D=3D expval?
      9f2:       36 b0           cmp/eq  r11,r6
      9f4:       8d 0b           bt.s    a0e <atfu___sync_val_compare_and_sw=
 ap_1_body+0x56>
 	// res =3D=3D expres?
      9f6:       39 a0           cmp/eq  r10,r9
 ...
      a78:       00 f0           .word 0x00f0
      a7a:       00 88           .word 0x0088
 ...
      a84:       00 00           .word 0x0000
                         a84: R_SH_PLT32 __sync_val_compare_and_swap_1

 Now I'm a little lost with the sh3 branch delay slot shenanigans (is
 that two comparisons fed into a single conditional branch with a delay
 slot??), but what I see here is that the _stored_ val is zero-extended
 with extu.b, but the _returned_ res is not!  That's in stark contrast
 to the attached program I examined, where the returned result _is_
 zero-extended with extu.b.

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Valery Ushakov <uwe@stderr.spb.ru>,
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Fri, 25 Sep 2026 15:51:47 +0000

 This is a multi-part message in MIME format.
 --=_8xiQiGqtxVZ2LhiPkIgdNm2J38qsNMwx
 Content-Transfer-Encoding: quoted-printable

 Oops, attached now...


 > Date: Fri, 25 Sep 2026 14:41:44 +0000
 > From: Taylor R Campbell <riastradh@NetBSD.org>
 >=20
 > > Date: Thu, 24 Sep 2026 20:17:00 +0300
 > > From: Valery Ushakov <uwe@stderr.spb.ru>
 > >=20
 > > For the record, just to make it explicit.  Consider simplified:
 > > [...]
 >=20
 > There's probably some shenanigans going on because __sync_* are not
 > normal functions but magic builtins with implicit prototypes.  Is
 > gcc's implicit prototype signed or unsigned?
 >=20
 > uint8_t u8_sync_val_compare_and_swap_1(volatile uint8_t *, uint8_t, uint8=
 _t);
 > int8_t s8_sync_val_compare_and_swap_1(volatile int8_t *, int8_t, int8_t);
 >=20
 > Under these prototypes, I compared three functions:
 >=20
 > - main__ calls the __sync_* builtin
 > - main_u8 calls u8_sync_*
 > - main_s8 calls s8_sync_*
 >=20
 > All three actually use uint8_t for all local variables, just like
 > t___sync_compare_and_swap.c does.
 >=20
 > (I don't natively speak sh3, so please correct me if I've guessed any
 > of the meaning wrong.)
 >=20
 > Here are the relevant excerpts -- in each case, the first jsr calls
 > the sync function, and the second calls printf, so what's interesting
 > is what happens between the two:
 >=20
 > 00000000 <main__>:
 > ...
 >   10:	d0 07       	mov.l	30 <main__+0x30>,r0	! 0 <main__>
 >   12:	96 0c       	mov.w	2e <main__+0x2e>,r6	! f0
 >   14:	40 0b       	jsr	@r0
 >   16:	74 03       	add	#3,r4
 >   18:	66 03       	mov	r0,r6
 >   1a:	d0 06       	mov.l	34 <main__+0x34>,r0	! 0 <main__>
 >   1c:	d4 06       	mov.l	38 <main__+0x38>,r4	! 0 <main__>
 >   1e:	40 0b       	jsr	@r0
 > ...
 > 			30: R_SH_DIR32	__sync_val_compare_and_swap_1
 > 			34: R_SH_DIR32	printf
 > ...
 > 0000003c <main_u8>:
 > ...
 >   4c:	d0 07       	mov.l	6c <main_u8+0x30>,r0	! 0 <main__>
 >   4e:	96 0c       	mov.w	6a <main_u8+0x2e>,r6	! f0
 >   50:	40 0b       	jsr	@r0
 >   52:	74 03       	add	#3,r4
 >   54:	66 03       	mov	r0,r6
 >   56:	d0 06       	mov.l	70 <main_u8+0x34>,r0	! 0 <main__>
 >   58:	d4 06       	mov.l	74 <main_u8+0x38>,r4	! 0 <main__>
 >   5a:	40 0b       	jsr	@r0
 > ...
 > 			6c: R_SH_DIR32	u8_sync_val_compare_and_swap_1
 > 			70: R_SH_DIR32	printf
 > ...
 > 00000078 <main_s8>:
 > ...
 >   82:	d0 08       	mov.l	a4 <main_s8+0x2c>,r0	! 0 <main__>
 >   84:	e6 f0       	mov	#-16,r6
 >   86:	e5 88       	mov	#-120,r5
 >   88:	40 0b       	jsr	@r0
 >   8a:	74 03       	add	#3,r4
 >   8c:	66 0c       	extu.b	r0,r6
 >   8e:	d0 06       	mov.l	a8 <main_s8+0x30>,r0	! 0 <main__>
 >   90:	95 07       	mov.w	a2 <main_s8+0x2a>,r5	! 88
 >   92:	d4 06       	mov.l	ac <main_s8+0x34>,r4	! 0 <main__>
 >   94:	40 0b       	jsr	@r0
 > ...
 > 			a4: R_SH_DIR32	s8_sync_val_compare_and_swap_1
 > 			a8: R_SH_DIR32	printf
 > ...
 >=20
 > Note that main_s8 has an additional extu.b instruction in it!  I'm
 > guessing this means `extend unsigned byte', i.e., zero-extend result
 > in r0 to printf argument in r6.  So it looks like gcc treats
 > __sync_val_compare_and_swap_1 as if it has the signed prototype:
 > main__ matches main_u8, not main_s8.
 >=20
 > If I disassemble t___sync_compare_and_swap.o, I see there's also an
 > extu.b instruction:
 >=20
 > 000009b8 <atfu___sync_val_compare_and_swap_1_body>:
 > ...
 >=20
 > 	// Load expval 0xf0 into r11, which is callee-saves so
 > 	// __sync_val_compare_and_swap_1 should preserve it:
 >      9d0:       9b 52           mov.w   a78 <atfu___sync_val_compare_and_=
 swap_1_body+0xc0>,r11  ! f0
 > ...
 > 	// Load expres 0x88 into r10, which is also callee-saves so
 > 	// __sync_val_compare_and_swap_1 should preserve it:
 >      9d4:       9a 51           mov.w   a7a <atfu___sync_val_compare_and_=
 swap_1_body+0xc2>,r10  ! 88
 > ...
 > 	// Load __sync_val_compare_and_swap_1 function into r1 to
 > 	// call:
 >      9d8:       d1 2a           mov.l   a84 <atfu___sync_val_compare_and_=
 swap_1_body+0xcc>,r1   ! 98
 > ...
 >      9e8:       01 03           bsrf    r1
 > 	// res in r0.  Move res to r9, reuse r0 for the stored val,
 > 	// move stored val to r6, and check whether the stored val is
 > 	// what we expect:
 >      9ea:       64 d3           mov     r13,r4
 >      9ec:       69 03           mov     r0,r9
 >      9ee:       84 8f           mov.b   @(15,r8),r0
 >      9f0:       66 0c           extu.b  r0,r6
 > 	// val =3D=3D expval?
 >      9f2:       36 b0           cmp/eq  r11,r6
 >      9f4:       8d 0b           bt.s    a0e <atfu___sync_val_compare_and_=
 swap_1_body+0x56>
 > 	// res =3D=3D expres?
 >      9f6:       39 a0           cmp/eq  r10,r9
 > ...
 >      a78:       00 f0           .word 0x00f0
 >      a7a:       00 88           .word 0x0088
 > ...
 >      a84:       00 00           .word 0x0000
 >                         a84: R_SH_PLT32 __sync_val_compare_and_swap_1
 >=20
 > Now I'm a little lost with the sh3 branch delay slot shenanigans (is
 > that two comparisons fed into a single conditional branch with a delay
 > slot??), but what I see here is that the _stored_ val is zero-extended
 > with extu.b, but the _returned_ res is not!  That's in stark contrast
 > to the attached program I examined, where the returned result _is_
 > zero-extended with extu.b.

 --=_8xiQiGqtxVZ2LhiPkIgdNm2J38qsNMwx
 Content-Type: text/plain; charset="ISO-8859-1"; name="sh3atomic"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="sh3atomic.c"

 #include <inttypes.h>
 #include <stdint.h>
 #include <stdio.h>

 #define OLDVAL (0x1122334455667788UL)
 #define NEWVAL (0x8090a0b0c0d0e0f0UL)

 uint8_t u8_sync_val_compare_and_swap_1(volatile uint8_t *, uint8_t, uint8_t=
 );
 int8_t s8_sync_val_compare_and_swap_1(volatile int8_t *, int8_t, int8_t);

 int
 main__()
 {
     volatile uint8_t val;
     uint8_t oldval;
     uint8_t newval;
     uint8_t expval;
     uint8_t expres;
     uint8_t res;


     val    =3D (uint8_t)OLDVAL;
     oldval =3D (uint8_t)OLDVAL;
     newval =3D (uint8_t)NEWVAL;
     expval =3D (uint8_t)NEWVAL;
     expres =3D (uint8_t)OLDVAL;
     res =3D __sync_val_compare_and_swap_1(&val, oldval, newval);

     printf("expected %x, res =3D %x\n", expres, res);
     return 0;
 }

 int
 main_u8()
 {
     volatile uint8_t val;
     uint8_t oldval;
     uint8_t newval;
     uint8_t expval;
     uint8_t expres;
     uint8_t res;


     val    =3D (uint8_t)OLDVAL;
     oldval =3D (uint8_t)OLDVAL;
     newval =3D (uint8_t)NEWVAL;
     expval =3D (uint8_t)NEWVAL;
     expres =3D (uint8_t)OLDVAL;
     res =3D u8_sync_val_compare_and_swap_1(&val, oldval, newval);

     printf("expected %x, res =3D %x\n", expres, res);
     return 0;
 }

 int
 main_s8()
 {
     volatile uint8_t val;
     uint8_t oldval;
     uint8_t newval;
     uint8_t expval;
     uint8_t expres;
     uint8_t res;


     val    =3D (uint8_t)OLDVAL;
     oldval =3D (uint8_t)OLDVAL;
     newval =3D (uint8_t)NEWVAL;
     expval =3D (uint8_t)NEWVAL;
     expres =3D (uint8_t)OLDVAL;
     res =3D s8_sync_val_compare_and_swap_1(&val, oldval, newval);

     printf("expected %x, res =3D %x\n", expres, res);
     return 0;
 }

 --=_8xiQiGqtxVZ2LhiPkIgdNm2J38qsNMwx--

From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc: Taylor R Campbell <riastradh@NetBSD.org>,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Fri, 25 Sep 2026 18:52:34 +0300

 On Fri, Sep 25, 2026 at 14:41:44 +0000, Taylor R Campbell wrote:

 > There's probably some shenanigans going on because __sync_* are not
 > normal functions but magic builtins with implicit prototypes.  Is
 > gcc's implicit prototype signed or unsigned?

 init_sync_libfuncs_1 in external/gpl3/gcc/dist/gcc/optabs-libfuncs.cc
 seems to be the place where they are materialized.  My gcc-fu is too
 weak to understand what's going on there.  It seems that it is
 eventually used in expand_atomic_compare_and_swap in optabs.cc.  I'm
 lost after that.

 -uwe

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Valery Ushakov <uwe@stderr.spb.ru>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Fri, 25 Sep 2026 16:14:25 +0000

 > Date: Fri, 25 Sep 2026 14:41:44 +0000
 > From: Taylor R Campbell <riastradh@NetBSD.org>
 >=20
 > Note that main_s8 has an additional extu.b instruction in it!  I'm
 > guessing this means `extend unsigned byte', i.e., zero-extend result
 > in r0 to printf argument in r6.  So it looks like gcc treats
 > __sync_val_compare_and_swap_1 as if it has the signed prototype:
 > main__ matches main_u8, not main_s8.
 > [...]
 > Now I'm a little lost with the sh3 branch delay slot shenanigans (is
 > that two comparisons fed into a single conditional branch with a delay
 > slot??), but what I see here is that the _stored_ val is zero-extended
 > with extu.b, but the _returned_ res is not!  That's in stark contrast
 > to the attached program I examined, where the returned result _is_
 > zero-extended with extu.b.

 Err, no, I just confused myself; it's consistent: in both cases gcc
 treats __sync_val_compare_and_swap as if it returned uint8_t, not
 int8_t, because so there's no extu.b instruction involved to convert
 int8_t to uint8_t.

 Here's how the builtin is declared:

     209 DEF_SYNC_BUILTIN (BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_1,
     210 		  "__sync_val_compare_and_swap_1",
     211 		  BT_FN_I1_VPTR_I1_I1, ATTR_NOTHROWCALL_LEAF_LIST)

 https://nxr.netbsd.org/xref/src/external/gpl3/gcc/dist/gcc/sync-builtins.de=
 f?r=3D1.1.1.12#209

 I'm a little fuzzy on what BT_FN_I1_VPTR_I1_I1 means.  It appears to
 be defined like this:

     209 DEF_PRIMITIVE_TYPE (BT_I1, builtin_type_for_size (BITS_PER_UNIT*1, =
 1))
 ...
     787 DEF_FUNCTION_TYPE_3 (BT_FN_I1_VPTR_I1_I1, BT_I1, BT_VOLATILE_PTR, B=
 T_I1, BT_I1)

 https://nxr.netbsd.org/xref/src/external/gpl3/gcc/dist/gcc/builtin-types.de=
 f?r=3D1.1.1.12#787

 Note that BT_I1 is not clearly defined to be either int8_t _or_
 uint8_t, for which there are BT_INT8 and BT_UINT8:

      70 DEF_PRIMITIVE_TYPE (BT_INT8, signed_char_type_node)
 ...
      72 DEF_PRIMITIVE_TYPE (BT_UINT8, unsigned_char_type_node)

 But builtin_type_for_size is defined here and perhaps it illuminates:

    7234 tree
    7235 builtin_type_for_size (int size, bool unsignedp)
    7236 {
    7237   tree type =3D c_common_type_for_size (size, unsignedp);
    7238   return type ? type : error_mark_node;
    7239 }

 https://nxr.netbsd.org/xref/src/external/gpl3/gcc/dist/gcc/c-family/c-commo=
 n.cc?r=3D1.1.1.3#7228

    2332 tree
    2333 c_common_type_for_size (unsigned int bits, int unsignedp)
    2334 {
 ...
    2340   if (bits =3D=3D TYPE_PRECISION (signed_char_type_node))
    2341     return unsignedp ? unsigned_char_type_node : signed_char_type_n=
 ode;

 https://nxr.netbsd.org/xref/src/external/gpl3/gcc/dist/gcc/c-family/c-commo=
 n.cc?r=3D1.1.1.3#2328

 That suggests to me that gcc will treat BT_I1 as unsigned char, and
 thus always treat __sync_val_compare_and_swap_1 as if it had the
 prototype

 uint8_t __sync_val_compare_and_swap_1(volatile uint8_t *, uint8_t, uint8_t);

 Which makes the arm bug of PR lib/56839 (GCC emits wrong codes for
 compare_and_swap_1 bultins on armv5 (el & eb)) all the more puzzling!

 However, I haven't connected the notation in sync-builtins.def to the
 logic that gcc actually uses for code generation.  So perhaps there is
 more magic yet to be discovered down that path.

From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc: Taylor R Campbell <riastradh@NetBSD.org>,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Fri, 25 Sep 2026 19:32:04 +0300

 On Fri, Sep 25, 2026 at 14:41:44 +0000, Taylor R Campbell wrote:

 > 00000078 <main_s8>:
 > ...
 >   82:	d0 08       	mov.l	a4 <main_s8+0x2c>,r0	! 0 <main__>
 >   84:	e6 f0       	mov	#-16,r6
 >   86:	e5 88       	mov	#-120,r5
 >   88:	40 0b       	jsr	@r0
 >   8a:	74 03       	add	#3,r4
 >   8c:	66 0c       	extu.b	r0,r6
 [...]
 >
 > Note that main_s8 has an additional extu.b instruction in it!  I'm
 > guessing this means `extend unsigned byte', i.e., zero-extend result
 > in r0 to printf argument in r6.  So it looks like gcc treats
 > __sync_val_compare_and_swap_1 as if it has the signed prototype:
 > main__ matches main_u8, not main_s8.

 You are right.  sh3 psABI specifies that unsigned char is returned in
 the LSB of r0 and is zero-extended, while signed char is returned
 sign-extended.  So that extu.b does the coercion of res to its
 declared uint8_t type and it can be elided for main_u8, b/c ABI
 already requires r0 to be zero-extended.



 > If I disassemble t___sync_compare_and_swap.o, I see there's also an
 > extu.b instruction:
 > 
 > 000009b8 <atfu___sync_val_compare_and_swap_1_body>:
 > ...
 > 
 > 	// Load expval 0xf0 into r11, which is callee-saves so
 > 	// __sync_val_compare_and_swap_1 should preserve it:
 >      9d0:       9b 52           mov.w   a78 <atfu___sync_val_compare_and_swap_1_body+0xc0>,r11  ! f0
 > ...
 > 	// Load expres 0x88 into r10, which is also callee-saves so
 > 	// __sync_val_compare_and_swap_1 should preserve it:
 >      9d4:       9a 51           mov.w   a7a <atfu___sync_val_compare_and_swap_1_body+0xc2>,r10  ! 88
 > ...

 Side note, there's no mov.b variant for pc-relative load.  mov.w
 sign-extends, but 0x0088 is positive, so we get the the right uint8_t
 value in r10.


 > 	// Load __sync_val_compare_and_swap_1 function into r1 to
 > 	// call:
 >      9d8:       d1 2a           mov.l   a84 <atfu___sync_val_compare_and_swap_1_body+0xcc>,r1   ! 98
 > ...
 >      9e8:       01 03           bsrf    r1
 >      9ea:       64 d3           mov     r13,r4
 > 	// res in r0.  Move res to r9, reuse r0 for the stored val,
 > 	// move stored val to r6, and check whether the stored val is
 > 	// what we expect:
 >      9ec:       69 03           mov     r0,r9
 >      9ee:       84 8f           mov.b   @(15,r8),r0
 >      9f0:       66 0c           extu.b  r0,r6

 Side note - on sh3 mov.b from memory (and mov.w) can only load to R0,
 hence the extra song and dance.  Both implicitly sign-extend R0.

 > 	// val == expval?
 >      9f2:       36 b0           cmp/eq  r11,r6
 >      9f4:       8d 0b           bt.s    a0e <atfu___sync_val_compare_and_swap_1_body+0x56>
 > 	// res == expres?
 >      9f6:       39 a0           cmp/eq  r10,r9
 > ...

 Right, and r10 is (uint8_t)0x88, and r9 (returned as uint8_t) is
 expected to be zero-extended b/c the ABI.

 As we know from the observation - it's not, b/c the actual function
 was defined as returning int8_t and hence it retutned sign-extended
 value, but the caller expects otherwise.


 > Now I'm a little lost with the sh3 branch delay slot shenanigans (is
 > that two comparisons fed into a single conditional branch with a
 > delay slot??),

 We do the first comparison and branch on its result.  We do the second
 comarison in the delay slot of that branch to not waste a perfectly
 good clock cycle, the value of that comparison will be used later.


 > but what I see here is that the _stored_ val is zero-extended
 > with extu.b, but the _returned_ res is not!  That's in stark contrast
 > to the attached program I examined, where the returned result _is_
 > zero-extended with extu.b.

 As I mentioned above, the loaded value must be zero-extended b/c the
 instruction sign-extends it on load.



 -uwe

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Valery Ushakov <uwe@stderr.spb.ru>,
	gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org,
	Nick Hudson <skrll@NetBSD.org>
Cc: 
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Fri, 25 Sep 2026 19:47:19 +0000

 I did the same for Arm with -march=armv5t -mfloat-abi=soft, and here's
 how it came out -- gcc treats __sync_* as signed, not unsigned (note
 the `and r2, r2, #255' in main__ and main_u8 but not main_s8):

 00000000 <main__>:
    0:	e3e01077 	mvn	r1, #119	@ 0x77
    4:	e52de004 	push	{lr}		@ (str lr, [sp, #-4]!)
    8:	e24dd00c 	sub	sp, sp, #12
    c:	e3e0200f 	mvn	r2, #15
   10:	e28d0007 	add	r0, sp, #7
   14:	e5cd1007 	strb	r1, [sp, #7]
   18:	ebfffffe 	bl	0 <__sync_val_compare_and_swap_1>
 			18: R_ARM_CALL	__sync_val_compare_and_swap_1
   1c:	e1a02000 	mov	r2, r0
   20:	e3a01088 	mov	r1, #136	@ 0x88
   24:	e20220ff 	and	r2, r2, #255	@ 0xff
   28:	e59f000c 	ldr	r0, [pc, #12]	@ 3c <main__+0x3c>
   2c:	ebfffffe 	bl	0 <printf>
 ...
 00000040 <main_u8>:
   40:	e3e03077 	mvn	r3, #119	@ 0x77
   44:	e52de004 	push	{lr}		@ (str lr, [sp, #-4]!)
   48:	e24dd00c 	sub	sp, sp, #12
   4c:	e3a020f0 	mov	r2, #240	@ 0xf0
   50:	e3a01088 	mov	r1, #136	@ 0x88
   54:	e28d0007 	add	r0, sp, #7
   58:	e5cd3007 	strb	r3, [sp, #7]
   5c:	ebfffffe 	bl	0 <u8_sync_val_compare_and_swap_1>
 			5c: R_ARM_CALL	u8_sync_val_compare_and_swap_1
   60:	e3a01088 	mov	r1, #136	@ 0x88
   64:	e1a02000 	mov	r2, r0
   68:	e59f000c 	ldr	r0, [pc, #12]	@ 7c <main_u8+0x3c>
   6c:	ebfffffe 	bl	0 <printf>
 ...
 00000080 <main_s8>:
   80:	e3e01077 	mvn	r1, #119	@ 0x77
   84:	e52de004 	push	{lr}		@ (str lr, [sp, #-4]!)
   88:	e24dd00c 	sub	sp, sp, #12
   8c:	e3e0200f 	mvn	r2, #15
   90:	e28d0007 	add	r0, sp, #7
   94:	e5cd1007 	strb	r1, [sp, #7]
   98:	ebfffffe 	bl	0 <s8_sync_val_compare_and_swap_1>
 			98: R_ARM_CALL	s8_sync_val_compare_and_swap_1
   9c:	e1a02000 	mov	r2, r0
   a0:	e3a01088 	mov	r1, #136	@ 0x88
   a4:	e20220ff 	and	r2, r2, #255	@ 0xff
   a8:	e59f000c 	ldr	r0, [pc, #12]	@ bc <main_s8+0x3c>
   ac:	ebfffffe 	bl	0 <printf>
 			ac: R_ARM_CALL	printf
 ...


 On sparc, though, as on sh3, __sync_* is treated as unsigned (note the
 `and %o0, 0xff, %o2' in main_s8 but not in main__ or main_u8):

 00000000 <main__>:
 main__():
    0:	9d e3 bf 98 	save  %sp, -104, %sp
    4:	82 10 3f 88 	mov  -120, %g1
    8:	94 10 20 f0 	mov  0xf0, %o2
    c:	c2 2f bf ff 	stb  %g1, [ %fp + -1 ]
   10:	92 10 20 88 	mov  0x88, %o1
   14:	40 00 00 00 	call  14 <main__+0x14>
 			14: R_SPARC_WDISP30	__sync_val_compare_and_swap_1
   18:	90 07 bf ff 	add  %fp, -1, %o0
   1c:	92 10 20 88 	mov  0x88, %o1
   20:	94 10 00 08 	mov  %o0, %o2
   24:	b0 10 20 00 	clr  %i0
   28:	11 00 00 00 	sethi  %hi(0), %o0
 			28: R_SPARC_HI22	.rodata.str1.8
   2c:	40 00 00 00 	call  2c <main__+0x2c>
 			2c: R_SPARC_WDISP30	printf
   30:	90 12 20 00 	mov  %o0, %o0	! 0 <main__>
 			30: R_SPARC_LO10	.rodata.str1.8
   34:	81 c7 e0 08 	ret 
   38:	81 e8 00 00 	restore 

 0000003c <main_u8>:
 main_u8():
   3c:	9d e3 bf 98 	save  %sp, -104, %sp
   40:	82 10 3f 88 	mov  -120, %g1
   44:	94 10 20 f0 	mov  0xf0, %o2
   48:	c2 2f bf ff 	stb  %g1, [ %fp + -1 ]
   4c:	92 10 20 88 	mov  0x88, %o1
   50:	40 00 00 00 	call  50 <main_u8+0x14>
 			50: R_SPARC_WDISP30	u8_sync_val_compare_and_swap_1
   54:	90 07 bf ff 	add  %fp, -1, %o0
   58:	92 10 20 88 	mov  0x88, %o1
   5c:	94 10 00 08 	mov  %o0, %o2
   60:	b0 10 20 00 	clr  %i0
   64:	11 00 00 00 	sethi  %hi(0), %o0
 			64: R_SPARC_HI22	.rodata.str1.8
   68:	40 00 00 00 	call  68 <main_u8+0x2c>
 			68: R_SPARC_WDISP30	printf
   6c:	90 12 20 00 	mov  %o0, %o0	! 0 <main__>
 			6c: R_SPARC_LO10	.rodata.str1.8
   70:	81 c7 e0 08 	ret 
   74:	81 e8 00 00 	restore 

 00000078 <main_s8>:
 main_s8():
   78:	9d e3 bf 98 	save  %sp, -104, %sp
   7c:	82 10 3f 88 	mov  -120, %g1
   80:	94 10 3f f0 	mov  -16, %o2
   84:	c2 2f bf ff 	stb  %g1, [ %fp + -1 ]
   88:	92 10 3f 88 	mov  -120, %o1
   8c:	40 00 00 00 	call  8c <main_s8+0x14>
 			8c: R_SPARC_WDISP30	s8_sync_val_compare_and_swap_1
   90:	90 07 bf ff 	add  %fp, -1, %o0
   94:	92 10 20 88 	mov  0x88, %o1
   98:	94 0a 20 ff 	and  %o0, 0xff, %o2
   9c:	b0 10 20 00 	clr  %i0
   a0:	11 00 00 00 	sethi  %hi(0), %o0
 			a0: R_SPARC_HI22	.rodata.str1.8
   a4:	40 00 00 00 	call  a4 <main_s8+0x2c>
 			a4: R_SPARC_WDISP30	printf
   a8:	90 12 20 00 	mov  %o0, %o0	! 0 <main__>
 			a8: R_SPARC_LO10	.rodata.str1.8
   ac:	81 c7 e0 08 	ret 
   b0:	81 e8 00 00 	restore 


 So something screwy is going on with gcc's internal concept of what
 signedness to use for __sync_*.

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Valery Ushakov <uwe@stderr.spb.ru>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org,
	Nick Hudson <skrll@NetBSD.org>
Subject: Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
Date: Sat, 26 Sep 2026 14:25:12 +0000

 I filed a bug report upstream with GCC:

 GCC bug 127626: arm: __sync_val_compare_and_swap calls generated with
 wrong signedness
 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127626

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 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.