NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
lib/49995: m68k: atomic_cas_N and __sync_bool_compare_and_swap_N doesn't work correctly
>Number: 49995
>Category: lib
>Synopsis: m68k: atomic_cas_N and __sync_bool_compare_and_swap_N doesn't work correctly
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Tue Jun 23 10:10:00 +0000 2015
>Originator: Tetsuya Isaki
>Release: NetBSD-7.0_RC1
>Organization:
>Environment:
NetBSD XXXXX 7.0_RC1 NetBSD 7.0_RC1 (GENERIC) #3: Sun Jun 21 14:08:10 JST 2015 isaki@XXXXX:/var/obj/7/x68k/obj/sys/arch/x68k/compile/GENERIC x68k
>Description:
On m68k, atomic_cas_{8,16}
(in src/common/lib/libc/arch/m68k/atomic/atomic_cas.S) does not
work correctly. It's wrong that the offset retrieving the arguments
from the stack. The arguments are pushed in Long (= 4 byte) by
caller even if the argument's size is Byte or Word. It can be
reproduced easily by calling atomic_cas_{8,16} in equal case.
__sync_bool_compare_and_swap_{1,2} has the same problem as above.
In addition, __sync_bool_compare_and_swap_{1,2,4} break D3 register
which should be preserved in subroutines.
By the way, I could not reproduce it with gcc on netbsd-7, because
gcc 4.8 has its own built-in (and correct :)
__sync_bool_compare_and_swap_N functions and I didn't know how to
disable it (--fno-builtin seems to have no effect for this).
Instead, I reproduced it using pcc (pkgsrc/lang/pcc-current) which
does not have these built-in functions.
I confirmed it only on 7.0_BETA/RC1 but the -current has the same
problem.
The Patch is attached. May I commit it?
>How-To-Repeat:
Here is the test code.
--------------------------------------
#include <stdio.h>
#include <stdint.h>
#include <sys/atomic.h>
#if defined(__m68k__)
#define SAVE_D3() __asm__("movl %%d3,%0" : "=m"(d3before))
#define CHECK_D3() do { \
__asm__("movl %%d3,%0" : "=m"(d3after)); \
if (d3before != d3after) \
printf("\tD3 is broken\n"),errcnt++; \
} while (0)
#else
#define SAVE_D3()
#define CHECK_D3()
#endif
#define SET(r, t, o, n) do { \
rv = r; \
target = t; \
oldval = o; \
newval = n; \
SAVE_D3(); \
} while (0)
#define CHECK(r, t, o, n) do { \
CHECK_D3(); \
if (rv != r) \
printf("\trv expects %d but %d\n", r, rv),errcnt++; \
if (target != t) \
printf("\ttarget expects %d but %d\n", t, target),errcnt++;\
if (oldval != o) \
printf("\toldval expects %d but %d\n", o, oldval),errcnt++;\
if (newval != n) \
printf("\tnewval expects %d but %d\n", n, newval),errcnt++;\
} while (0)
volatile uint32_t d3before;
volatile uint32_t d3after;
int errcnt;
void
test32()
{
register uint32_t oldval;
register uint32_t newval;
volatile uint32_t target;
uint32_t rv;
printf("atomic_cas_32 (EQ)\n");
SET(0, 10, 10, 30);
rv = atomic_cas_32(&target, oldval, newval);
CHECK(10, 30, 10, 30);
printf("atomic_cas_32 (NE)\n");
SET(0, 10, 20, 30);
rv = atomic_cas_32(&target, oldval, newval);
CHECK(10, 10, 20, 30);
printf("__sync_bool_compare_and_swap_4 (EQ)\n");
SET(0, 10, 10, 30);
rv = __sync_bool_compare_and_swap_4(&target, oldval, newval);
CHECK(1, 30, 10, 30);
printf("__sync_bool_compare_and_swap_4 (NE)\n");
SET(0, 10, 20, 30);
rv = __sync_bool_compare_and_swap_4(&target, oldval, newval);
CHECK(0, 10, 20, 30);
}
void
test16()
{
#if defined(__m68k__)
register uint16_t oldval;
register uint16_t newval;
volatile uint16_t target;
uint16_t rv;
printf("atomic_cas_16 (EQ)\n");
SET(0, 10, 10, 30);
rv = atomic_cas_16(&target, oldval, newval);
CHECK(10, 30, 10, 30);
printf("atomic_cas_16 (NE)\n");
SET(0, 10, 20, 30);
rv = atomic_cas_16(&target, oldval, newval);
CHECK(10, 10, 20, 30);
printf("__sync_bool_compare_and_swap_2 (EQ)\n");
SET(0, 10, 10, 30);
rv = __sync_bool_compare_and_swap_2(&target, oldval, newval);
CHECK(1, 30, 10, 30);
printf("__sync_bool_compare_and_swap_2 (NE)\n");
SET(0, 10, 20, 30);
rv = __sync_bool_compare_and_swap_2(&target, oldval, newval);
CHECK(0, 10, 20, 30);
#endif
}
void
test8()
{
#if defined(__m68k__)
register uint8_t oldval;
register uint8_t newval;
volatile uint8_t target;
uint8_t rv;
printf("atomic_cas_8 (EQ)\n");
SET(0, 10, 10, 30);
rv = atomic_cas_8(&target, oldval, newval);
CHECK(10, 30, 10, 30);
printf("atomic_cas_8 (NE)\n");
SET(0, 10, 20, 30);
rv = atomic_cas_8(&target, oldval, newval);
CHECK(10, 10, 20, 30);
printf("__sync_bool_compare_and_swap_1 (EQ)\n");
SET(0, 10, 10, 30);
rv = __sync_bool_compare_and_swap_1(&target, oldval, newval);
CHECK(1, 30, 10, 30);
printf("__sync_bool_compare_and_swap_1 (NE)\n");
SET(0, 10, 20, 30);
rv = __sync_bool_compare_and_swap_1(&target, oldval, newval);
CHECK(0, 10, 20, 30);
#endif
}
int
main()
{
test32();
test16();
test8();
printf("%d failed\n", errcnt);
return 0;
}
--------------------------------------
And here is the log of the test code BEFORE applying the patch.
--------------------------------------
xm6i7# uname -srm
NetBSD 7.0_RC1 x68k
xm6i7# gcc -O2 cas1.c -o cas1-O2
xm6i7# ./cas1-O2
atomic_cas_32 (EQ)
atomic_cas_32 (NE)
__sync_bool_compare_and_swap_4 (EQ)
__sync_bool_compare_and_swap_4 (NE)
atomic_cas_16 (EQ)
target expects 30 but 10
atomic_cas_16 (NE)
__sync_bool_compare_and_swap_2 (EQ)
__sync_bool_compare_and_swap_2 (NE)
atomic_cas_8 (EQ)
target expects 30 but 10
atomic_cas_8 (NE)
__sync_bool_compare_and_swap_1 (EQ)
__sync_bool_compare_and_swap_1 (NE)
2 failed
xm6i7# pcc --version
pcc 1.2.0.DEVEL 20141228 for m68k--netbsdelf
xm6i7# pcc -O2 cas1.c -o cas1-pcc-O2
xm6i7# ./cas1-pcc-O2
atomic_cas_32 (EQ)
atomic_cas_32 (NE)
__sync_bool_compare_and_swap_4 (EQ)
D3 is broken
newval expects 30 but 10
__sync_bool_compare_and_swap_4 (NE)
D3 is broken
newval expects 30 but 10
atomic_cas_16 (EQ)
target expects 30 but 10
atomic_cas_16 (NE)
__sync_bool_compare_and_swap_2 (EQ)
D3 is broken
rv expects 1 but 0
target expects 30 but 10
newval expects 30 but 10
__sync_bool_compare_and_swap_2 (NE)
D3 is broken
newval expects 30 but 10
atomic_cas_8 (EQ)
target expects 30 but 10
atomic_cas_8 (NE)
__sync_bool_compare_and_swap_1 (EQ)
D3 is broken
rv expects 1 but 0
target expects 30 but 10
newval expects 30 but 10
__sync_bool_compare_and_swap_1 (NE)
D3 is broken
newval expects 30 but 10
18 failed
--------------------------------------
>Fix:
--- common/lib/libc/arch/m68k/atomic/atomic_cas.S.ORG 2014-08-17 14:16:52.000000000 +0900
+++ common/lib/libc/arch/m68k/atomic/atomic_cas.S 2015-06-22 18:09:46.000000000 +0900
@@ -63,11 +63,9 @@
ENTRY(__sync_bool_compare_and_swap_4)
movl 4(%sp), %a0
- movl 8(%sp), %d3
- movl %d3, %d2
+ movl 8(%sp), %d0
movl 12(%sp), %d1
- casl %d3, %d1, (%a0)
- /* %d3 now contains the old value */
+ casl %d0, %d1, (%a0)
beq 1f
clrl %d0 /* return false */
rts
@@ -77,8 +75,8 @@
ENTRY(_atomic_cas_16)
movl 4(%sp), %a0
- movw 8(%sp), %d0
- movw 10(%sp), %d1
+ movw 8+2(%sp), %d0 /* lower word */
+ movw 12+2(%sp), %d1 /* lower word */
casw %d0, %d1, (%a0)
/* %d0 now contains the old value */
rts
@@ -89,10 +87,9 @@
ENTRY(__sync_bool_compare_and_swap_2)
movl 4(%sp), %a0
- movw 8(%sp), %d3
- movw %d3, %d2
- movw 10(%sp), %d1
- casw %d3, %d1, (%a0)
+ movw 8+2(%sp), %d0 /* lower word */
+ movw 12+2(%sp), %d1 /* lower word */
+ casw %d0, %d1, (%a0)
/* %d3 now contains the old value */
beq 1f
clrl %d0 /* return failure */
@@ -103,8 +100,8 @@
ENTRY(_atomic_cas_8)
movl 4(%sp), %a0
- movb 8(%sp), %d0
- movb 9(%sp), %d1
+ movb 8+3(%sp), %d0 /* lower byte */
+ movb 12+3(%sp), %d1 /* lower byte */
casb %d0, %d1, (%a0)
/* %d0 now contains the old value */
rts
@@ -116,10 +113,9 @@
ENTRY(__sync_bool_compare_and_swap_1)
movl 4(%sp), %a0
- movb 8(%sp), %d3
- movb %d3, %d2
- movb 9(%sp), %d1
- casb %d3, %d1, (%a0)
+ movb 8+3(%sp), %d0 /* lower byte */
+ movb 12+3(%sp), %d1 /* lower byte */
+ casb %d0, %d1, (%a0)
/* %d3 now contains the old value */
beq 1f
clrl %d0 /* return failure */
Home |
Main Index |
Thread Index |
Old Index