NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: port-sh3/60773: sh3 __sync_val_compare_and_swap_1 test failures
The following reply was made to PR toolchain/60773; it has been noted by GNATS.
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: Valery Ushakov <uwe%stderr.spb.ru@localhost>
Cc: gnats-bugs%netbsd.org@localhost, Nick Hudson <skrll%NetBSD.org@localhost>
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@localhost>
>=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.
Home |
Main Index |
Thread Index |
Old Index