Message ID | 20111006175404.GE12770@davesworkthinkpad |
---|---|
State | New |
Headers | show |
On 6 October 2011 18:54, Dr. David Alan Gilbert <david.gilbert@linaro.org> wrote: > Add ARM 64bit sync helpers for use on older ARMs. Based on 32bit > versions but with check for sufficiently new kernel version. > > gcc/ > * config/arm/linux-atomic-64bit.c: New (based on linux-atomic.c) > * config/arm/linux-atomic.c: Change comment to point to 64bit version > (SYNC_LOCK_RELEASE): Instantiate 64bit version. > * config/arm/t-linux-eabi: Pull in linux-atomic-64bit.c OK. Ramana > > > diff --git a/gcc/config/arm/linux-atomic-64bit.c b/gcc/config/arm/linux-atomic-64bit.c > new file mode 100644 > index 0000000..6966e66 > --- /dev/null > +++ b/gcc/config/arm/linux-atomic-64bit.c > @@ -0,0 +1,166 @@ > +/* 64bit Linux-specific atomic operations for ARM EABI. > + Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc. > + Based on linux-atomic.c > + > + 64 bit additions david.gilbert@linaro.org > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +Under Section 7 of GPL version 3, you are granted additional > +permissions described in the GCC Runtime Library Exception, version > +3.1, as published by the Free Software Foundation. > + > +You should have received a copy of the GNU General Public License and > +a copy of the GCC Runtime Library Exception along with this program; > +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see > +<http://www.gnu.org/licenses/>. */ > + > +/* 64bit helper functions for atomic operations; the compiler will > + call these when the code is compiled for a CPU without ldrexd/strexd. > + (If the CPU had those then the compiler inlines the operation). > + > + These helpers require a kernel helper that's only present on newer > + kernels; we check for that in an init section and bail out rather > + unceremoneously. */ > + > +extern unsigned int __write (int fd, const void *buf, unsigned int count); > +extern void abort (void); > + > +/* Kernel helper for compare-and-exchange. */ > +typedef int (__kernel_cmpxchg64_t) (const long long* oldval, > + const long long* newval, > + long long *ptr); > +#define __kernel_cmpxchg64 (*(__kernel_cmpxchg64_t *) 0xffff0f60) > + > +/* Kernel helper page version number. */ > +#define __kernel_helper_version (*(unsigned int *)0xffff0ffc) > + > +/* Check that the kernel has a new enough version at load. */ > +static void __check_for_sync8_kernelhelper (void) > +{ > + if (__kernel_helper_version < 5) > + { > + const char err[] = "A newer kernel is required to run this binary. " > + "(__kernel_cmpxchg64 helper)\n"; > + /* At this point we need a way to crash with some information > + for the user - I'm not sure I can rely on much else being > + available at this point, so do the same as generic-morestack.c > + write () and abort (). */ > + __write (2 /* stderr. */, err, sizeof (err)); > + abort (); > + } > +}; > + > +static void (*__sync8_kernelhelper_inithook[]) (void) > + __attribute__ ((used, section (".init_array"))) = { > + &__check_for_sync8_kernelhelper > +}; > + > +#define HIDDEN __attribute__ ((visibility ("hidden"))) > + > +#define FETCH_AND_OP_WORD64(OP, PFX_OP, INF_OP) \ > + long long HIDDEN \ > + __sync_fetch_and_##OP##_8 (long long *ptr, long long val) \ > + { \ > + int failure; \ > + long long tmp,tmp2; \ > + \ > + do { \ > + tmp = *ptr; \ > + tmp2 = PFX_OP (tmp INF_OP val); \ > + failure = __kernel_cmpxchg64 (&tmp, &tmp2, ptr); \ > + } while (failure != 0); \ > + \ > + return tmp; \ > + } > + > +FETCH_AND_OP_WORD64 (add, , +) > +FETCH_AND_OP_WORD64 (sub, , -) > +FETCH_AND_OP_WORD64 (or, , |) > +FETCH_AND_OP_WORD64 (and, , &) > +FETCH_AND_OP_WORD64 (xor, , ^) > +FETCH_AND_OP_WORD64 (nand, ~, &) > + > +#define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH > +#define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH > + > +/* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for > + subword-sized quantities. */ > + > +#define OP_AND_FETCH_WORD64(OP, PFX_OP, INF_OP) \ > + long long HIDDEN \ > + __sync_##OP##_and_fetch_8 (long long *ptr, long long val) \ > + { \ > + int failure; \ > + long long tmp,tmp2; \ > + \ > + do { \ > + tmp = *ptr; \ > + tmp2 = PFX_OP (tmp INF_OP val); \ > + failure = __kernel_cmpxchg64 (&tmp, &tmp2, ptr); \ > + } while (failure != 0); \ > + \ > + return tmp2; \ > + } > + > +OP_AND_FETCH_WORD64 (add, , +) > +OP_AND_FETCH_WORD64 (sub, , -) > +OP_AND_FETCH_WORD64 (or, , |) > +OP_AND_FETCH_WORD64 (and, , &) > +OP_AND_FETCH_WORD64 (xor, , ^) > +OP_AND_FETCH_WORD64 (nand, ~, &) > + > +long long HIDDEN > +__sync_val_compare_and_swap_8 (long long *ptr, long long oldval, > + long long newval) > +{ > + int failure; > + long long actual_oldval; > + > + while (1) > + { > + actual_oldval = *ptr; > + > + if (__builtin_expect (oldval != actual_oldval, 0)) > + return actual_oldval; > + > + failure = __kernel_cmpxchg64 (&actual_oldval, &newval, ptr); > + > + if (__builtin_expect (!failure, 1)) > + return oldval; > + } > +} > + > +typedef unsigned char bool; > + > +bool HIDDEN > +__sync_bool_compare_and_swap_8 (long long *ptr, long long oldval, > + long long newval) > +{ > + int failure = __kernel_cmpxchg64 (&oldval, &newval, ptr); > + return (failure == 0); > +} > + > +long long HIDDEN > +__sync_lock_test_and_set_8 (long long *ptr, long long val) > +{ > + int failure; > + long long oldval; > + > + do { > + oldval = *ptr; > + failure = __kernel_cmpxchg64 (&oldval, &val, ptr); > + } while (failure != 0); > + > + return oldval; > +} > diff --git a/gcc/config/arm/linux-atomic.c b/gcc/config/arm/linux-atomic.c > index 57065a6..80f161d 100644 > --- a/gcc/config/arm/linux-atomic.c > +++ b/gcc/config/arm/linux-atomic.c > @@ -32,8 +32,8 @@ typedef void (__kernel_dmb_t) (void); > #define __kernel_dmb (*(__kernel_dmb_t *) 0xffff0fa0) > > /* Note: we implement byte, short and int versions of atomic operations using > - the above kernel helpers, but there is no support for "long long" (64-bit) > - operations as yet. */ > + the above kernel helpers; see linux-atomic-64bit.c for "long long" (64-bit) > + operations. */ > > #define HIDDEN __attribute__ ((visibility ("hidden"))) > > @@ -273,6 +273,7 @@ SUBWORD_TEST_AND_SET (unsigned char, 1) > *ptr = 0; \ > } > > +SYNC_LOCK_RELEASE (long long, 8) > SYNC_LOCK_RELEASE (int, 4) > SYNC_LOCK_RELEASE (short, 2) > SYNC_LOCK_RELEASE (char, 1) > diff --git a/gcc/config/arm/t-linux-eabi b/gcc/config/arm/t-linux-eabi > index a328a00..3814cc0 100644 > --- a/gcc/config/arm/t-linux-eabi > +++ b/gcc/config/arm/t-linux-eabi > @@ -36,3 +36,4 @@ LIB1ASMFUNCS := $(filter-out _dvmd_tls,$(LIB1ASMFUNCS)) _dvmd_lnx _clear_cache > EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o > > LIB2FUNCS_STATIC_EXTRA += $(srcdir)/config/arm/linux-atomic.c > +LIB2FUNCS_STATIC_EXTRA += $(srcdir)/config/arm/linux-atomic-64bit.c >
diff --git a/gcc/config/arm/linux-atomic-64bit.c b/gcc/config/arm/linux-atomic-64bit.c new file mode 100644 index 0000000..6966e66 --- /dev/null +++ b/gcc/config/arm/linux-atomic-64bit.c @@ -0,0 +1,166 @@ +/* 64bit Linux-specific atomic operations for ARM EABI. + Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc. + Based on linux-atomic.c + + 64 bit additions david.gilbert@linaro.org + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +/* 64bit helper functions for atomic operations; the compiler will + call these when the code is compiled for a CPU without ldrexd/strexd. + (If the CPU had those then the compiler inlines the operation). + + These helpers require a kernel helper that's only present on newer + kernels; we check for that in an init section and bail out rather + unceremoneously. */ + +extern unsigned int __write (int fd, const void *buf, unsigned int count); +extern void abort (void); + +/* Kernel helper for compare-and-exchange. */ +typedef int (__kernel_cmpxchg64_t) (const long long* oldval, + const long long* newval, + long long *ptr); +#define __kernel_cmpxchg64 (*(__kernel_cmpxchg64_t *) 0xffff0f60) + +/* Kernel helper page version number. */ +#define __kernel_helper_version (*(unsigned int *)0xffff0ffc) + +/* Check that the kernel has a new enough version at load. */ +static void __check_for_sync8_kernelhelper (void) +{ + if (__kernel_helper_version < 5) + { + const char err[] = "A newer kernel is required to run this binary. " + "(__kernel_cmpxchg64 helper)\n"; + /* At this point we need a way to crash with some information + for the user - I'm not sure I can rely on much else being + available at this point, so do the same as generic-morestack.c + write () and abort (). */ + __write (2 /* stderr. */, err, sizeof (err)); + abort (); + } +}; + +static void (*__sync8_kernelhelper_inithook[]) (void) + __attribute__ ((used, section (".init_array"))) = { + &__check_for_sync8_kernelhelper +}; + +#define HIDDEN __attribute__ ((visibility ("hidden"))) + +#define FETCH_AND_OP_WORD64(OP, PFX_OP, INF_OP) \ + long long HIDDEN \ + __sync_fetch_and_##OP##_8 (long long *ptr, long long val) \ + { \ + int failure; \ + long long tmp,tmp2; \ + \ + do { \ + tmp = *ptr; \ + tmp2 = PFX_OP (tmp INF_OP val); \ + failure = __kernel_cmpxchg64 (&tmp, &tmp2, ptr); \ + } while (failure != 0); \ + \ + return tmp; \ + } + +FETCH_AND_OP_WORD64 (add, , +) +FETCH_AND_OP_WORD64 (sub, , -) +FETCH_AND_OP_WORD64 (or, , |) +FETCH_AND_OP_WORD64 (and, , &) +FETCH_AND_OP_WORD64 (xor, , ^) +FETCH_AND_OP_WORD64 (nand, ~, &) + +#define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH +#define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH + +/* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for + subword-sized quantities. */ + +#define OP_AND_FETCH_WORD64(OP, PFX_OP, INF_OP) \ + long long HIDDEN \ + __sync_##OP##_and_fetch_8 (long long *ptr, long long val) \ + { \ + int failure; \ + long long tmp,tmp2; \ + \ + do { \ + tmp = *ptr; \ + tmp2 = PFX_OP (tmp INF_OP val); \ + failure = __kernel_cmpxchg64 (&tmp, &tmp2, ptr); \ + } while (failure != 0); \ + \ + return tmp2; \ + } + +OP_AND_FETCH_WORD64 (add, , +) +OP_AND_FETCH_WORD64 (sub, , -) +OP_AND_FETCH_WORD64 (or, , |) +OP_AND_FETCH_WORD64 (and, , &) +OP_AND_FETCH_WORD64 (xor, , ^) +OP_AND_FETCH_WORD64 (nand, ~, &) + +long long HIDDEN +__sync_val_compare_and_swap_8 (long long *ptr, long long oldval, + long long newval) +{ + int failure; + long long actual_oldval; + + while (1) + { + actual_oldval = *ptr; + + if (__builtin_expect (oldval != actual_oldval, 0)) + return actual_oldval; + + failure = __kernel_cmpxchg64 (&actual_oldval, &newval, ptr); + + if (__builtin_expect (!failure, 1)) + return oldval; + } +} + +typedef unsigned char bool; + +bool HIDDEN +__sync_bool_compare_and_swap_8 (long long *ptr, long long oldval, + long long newval) +{ + int failure = __kernel_cmpxchg64 (&oldval, &newval, ptr); + return (failure == 0); +} + +long long HIDDEN +__sync_lock_test_and_set_8 (long long *ptr, long long val) +{ + int failure; + long long oldval; + + do { + oldval = *ptr; + failure = __kernel_cmpxchg64 (&oldval, &val, ptr); + } while (failure != 0); + + return oldval; +} diff --git a/gcc/config/arm/linux-atomic.c b/gcc/config/arm/linux-atomic.c index 57065a6..80f161d 100644 --- a/gcc/config/arm/linux-atomic.c +++ b/gcc/config/arm/linux-atomic.c @@ -32,8 +32,8 @@ typedef void (__kernel_dmb_t) (void); #define __kernel_dmb (*(__kernel_dmb_t *) 0xffff0fa0) /* Note: we implement byte, short and int versions of atomic operations using - the above kernel helpers, but there is no support for "long long" (64-bit) - operations as yet. */ + the above kernel helpers; see linux-atomic-64bit.c for "long long" (64-bit) + operations. */ #define HIDDEN __attribute__ ((visibility ("hidden"))) @@ -273,6 +273,7 @@ SUBWORD_TEST_AND_SET (unsigned char, 1) *ptr = 0; \ } +SYNC_LOCK_RELEASE (long long, 8) SYNC_LOCK_RELEASE (int, 4) SYNC_LOCK_RELEASE (short, 2) SYNC_LOCK_RELEASE (char, 1) diff --git a/gcc/config/arm/t-linux-eabi b/gcc/config/arm/t-linux-eabi index a328a00..3814cc0 100644 --- a/gcc/config/arm/t-linux-eabi +++ b/gcc/config/arm/t-linux-eabi @@ -36,3 +36,4 @@ LIB1ASMFUNCS := $(filter-out _dvmd_tls,$(LIB1ASMFUNCS)) _dvmd_lnx _clear_cache EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o LIB2FUNCS_STATIC_EXTRA += $(srcdir)/config/arm/linux-atomic.c +LIB2FUNCS_STATIC_EXTRA += $(srcdir)/config/arm/linux-atomic-64bit.c