diff mbox

- C11 expressions and stdatomic.h - Just for current state

Message ID 51FC06CF.8080300@redhat.com
State New
Headers show

Commit Message

Andrew MacLeod Aug. 2, 2013, 7:21 p.m. UTC
On 07/30/2013 12:49 PM, Andrew MacLeod wrote:
> I split the original patch into some smaller hunks, and cleaned up a 
> few bit and pieces here and there... following:
>
>
Not looking for a review, but posting progress to this point since I'm 
off for 2 weeks, and someone might want to play with this.

I re-examined implementing the atomic type as its own canonical type 
instead of a qualified variant, as well as some variations... and 
ultimately, I think the current qualified implementation I posted is 
probably the best one to work with.  Its very convenient to be able to 
get to the non-atomic type by using TYPE_MAIN_VARIANT...  Anyway, so the 
original 5 patches stand, but will require closer auditing . (starting 
at http://gcc.gnu.org/ml/gcc-patches/2013-07/msg01478.html)

I've attached 2 patches anda  header file here.  One is the changes 
required for handling the C11 atomic expressions...  ie x += 4.2, and 
the other is a first cut at the content for the stdatomic.h include 
file, adna  required change for it.

The atomic expression patch works on many cases, but falls short with 
any kind of complexity on the RHS of an expression which needs to be 
mapped to an atomic_load.  Whilst parsing, we cant tell, and the rhs 
tends to become an expression before we really know. so In the comments 
I have my proposed solution which I will look at when I return in 2 weeks.
Basically, rather than have the ATOMIC_TYPE attribute ripple all the way 
through an expression, as soon as we see an ATOMIC_TYPE, we wrap it in a 
new node ATOMIC_EXPR, which holds the atomic expression, and sets it own 
expression type to the non-atomic variant.  This means the only 
expressions which will have the TYPE_ATOMIC flag set will be ones which 
are wrapped in  ATOMIC_EXPR, and those are the ones where we need to go 
and replace with an __atomic_load or whatever.

I have also punted on the floating point exception stuff which footnote 
113 requires.. we'll worry about that later.

Nevertheless, the patch that is there can do some interest8ing things... 
it can compile and execute this test file successfully as proof of 
concept... with all the right conmversions and use of lockfree routines 
when available.  (use -mcx16 on an x86_64 box, btw, or the __atomic_*_16 
routines will be unresolved )   complex types also work.  long double 
complex is 32 bytes in size, and will make calls into libatomic to 
resolve those operations.

double __attribute__((atomic)) a;
float __attribute__((atomic)) t;
long double __attribute__((atomic))  ld;
char __attribute__((atomic)) c;
int __attribute__((atomic)) i;
long long __attribute__((atomic)) l;
int g;
main ()
{
   g = 40;
   ld = t = a = 4.567;
   c = l = i = g + 2;
   printf ("%f == %f == %Lf == 4.567\n", t, a, ld);
   printf ("%d == %d == %ld == 42\n", c , i, l);
}


I have also attach a mockup of stdatomic.h...   I haven't even tried 
compiling or testing it yet, so it may have a syntax error or something, 
but I wanted to see if the functionality was possible.
THe only thing missing is the ability to find the non-atomic type of an 
atomic type.

Ive included an additional 2 line patch which should  change the meaning 
of __typeof__  (again untested, the joys of imminently leaving for 2 
weeks  :-).   Im not sure the normal practical uses of
__typeof__ have much meaning for an atomic type, it seems far more 
useful to have __typeof__ for an atomic qualified type to return the 
non-atomic variant.   If there is resistance to that definition, then 
I'll need to create a __nonatomic_typeof__ variant...  thats just a bit 
more work.  In any case, that patch is required for stdatomic.h to be 
implemented.  You can see where I used it to call the generic atomic 
operations with all the proper types for the non-atomic fields which 
need a temp vazr.     The header file is actually quite short.  I don't 
know how to implement it other than with macro wrappers around the builtins.

And thats it. I'll be back in 2 weeks to get back at wrapping this, and 
adding some testcases and error reporting.

unless *YOU*  beat me to it :-)

Andrew
typedef enum memory_order
{
  memory_order_relaxed,
  memory_order_consume,
  memory_order_acquire,
  memory_order_release,
  memory_order_acq_rel,
  memory_order_seq_cst
} memory_order;


typedef _Atomic _Bool 	       atomic_bool;
typedef _Atomic char	       atomic_char
typedef _Atomic schar	       atomic_schar
typedef _Atomic uchar	       atomic_uchar
typedef _Atomic short	       atomic_short
typedef _Atomic ushort	       atomic_ushort
typedef _Atomic int            atomic_int
typedef _Atomic uint           atomic_uint
typedef _Atomic long           atomic_long
typedef _Atomic ulong          atomic_ulong
typedef _Atomic llong          atomic_llong
typedef _Atomic ullong         atomic_ullong
typedef _Atomic char16_t       atomic_char16_t
typedef _Atomic char32_t       atomic_char32_t
typedef _Atomic wchar_t        atomic_wchar_t
typedef _Atomic int_least8_t   atomic_int_least8_t
typedef _Atomic uint_least8_t  atomic_uint_least8_t
typedef _Atomic int_least16_t  atomic_int_least16_t
typedef _Atomic uint_least16_t atomic_uint_least16_t
typedef _Atomic int_least32_t  atomic_int_least32_t
typedef _Atomic uint_least32_t atomic_uint_least32_t
typedef _Atomic int_least64_t  atomic_int_least64_t
typedef _Atomic uint_least64_t atomic_uint_least64_t
typedef _Atomic int_fast8_t    atomic_int_fast8_t
typedef _Atomic uint_fast8_t   atomic_uint_fast8_t
typedef _Atomic int_fast16_t   atomic_int_fast16_t
typedef _Atomic uint_fast16_t  atomic_uint_fast16_t
typedef _Atomic int_fast32_t   atomic_int_fast32_t
typedef _Atomic uint_fast32_t  atomic_uint_fast32_t
typedef _Atomic int_fast64_t   atomic_int_fast64_t
typedef _Atomic uint_fast64_t  atomic_uint_fast64_t
typedef _Atomic intptr_t       atomic_intptr_t
typedef _Atomic uintptr_t      atomic_uintptr_t
typedef _Atomic size_t         atomic_size_t
typedef _Atomic ptrdiff_t      atomic_ptrdiff_t
typedef _Atomic intmax_t       atomic_intmax_t
typedef _Atomic uintmax_t      atomic_uintmax_t        


#define ATOMIC_VAR_INIT(VALUE)	(VALUE)
#define atomic_init(PTR, VAL)	{ *(PTR) = (VAL); }

/* TODO actually kill the dependancy.  */
#define kill_dependency(Y)	(Y)

#define atomic_thread_fence 	__atomic_thread_fence
#define atomic_signal_fence 	__atomic_signal_fence 
#define atomic_is_lock_free(OBJ) __atomic_is_lock_free (sizeof (*(OBJ)), NULL)

#define ATOMIC_BOOL_LOCK_FREE			\
			__atomic_is_lock_free (sizeof (atomic_bool), NULL)
#define ATOMIC_CHAR_LOCK_FREE			\
			__atomic_is_lock_free (sizeof (atomic_char), NULL)
#define ATOMIC_CHAR16_T_LOCK_FREE		\
			__atomic_is_lock_free (sizeof (atomic_char16_t), NULL)
#define ATOMIC_CHAR32_T_LOCK_FREE		\
			__atomic_is_lock_free (sizeof (atomic_char32_t), NULL)
#define ATOMIC_WCHAR_T_LOCK_FREE		\
			__atomic_is_lock_free (sizeof (atomic_wchar_t), NULL)
#define ATOMIC_SHORT_LOCK_FREE 			\
			__atomic_is_lock_free (sizeof (atomic_short), NULL)
#define ATOMIC_INT_LOCK_FREE 			\
			__atomic_is_lock_free (sizeof (atomic_int), NULL)
#define ATOMIC_LONG_LOCK_FREE			\
			__atomic_is_lock_free (sizeof (atomic_long), NULL)
#define ATOMIC_LLONG_LOCK_FREE			\
			__atomic_is_lock_free (sizeof (atomic_llong), NULL)
#define ATOMIC_POINTER_LOCK_FREE		\
			__atomic_is_lock_free (sizeof (_Atomic void *), NULL)


/* TODO: Note this required the __typeof__ definition to drops the atomic
   qualifier, which means __typeof__ (atomic type) return the underlying 
   non-atomic type.
   I think this makes sense, as most meaningful uses of __typeof__ of an atomic
   object would want the non-atomic version to be useful, as it is above.

   If we dont want to change that meaning, we'll need to implement a __typeof__
   variant which does this. 
   
   Also note that the header file uses the generic form of __atomic builtins,
   which requires the address to be taken of the value parameter, and then
   we pass that value on.   This allows the macros to work for any type,
   and the compiler is smart enough to convert these to lock-free _N 
   variants if possible, and throw away the temps.  */

#define atomic_store_explicit(PTR, VAL, MO) ({		\
  __typeof__ (*(PTR)) __tmp  = (VAL); 			\
  __atomic_store ((PTR), &__tmp, (MO)); })

#define atomic_store(PTR, VAL)				\
  atomic_store_explicit (PTR, VAL, __ATOMIC_SEQ_CST)


#define atomic_load_explicit(PTR, MO) ({		\
  __typeof__ (*(PTR)) __tmp; 				\
  __atomic)load ((PTR), &__tmp, __ATOMIC_SEQ_CST);	\
  __tmp; })

#define atomic_load(PTR)  atomic_load_explicit (PTR, __ATOMIC_SEQ_CST)


#define atomic_exchange_explicit(PTR, VAL, MO) ({	\
  __typeof__ (*(PTR)) __tmp  = (VAL); 			\
  __atomic_exchange_n ((PTR), (VAL), (MO)); 		\
  __tmp; })

#define atomic_exchange(PTR, VAL) 			\
  atomic_exchange_explicit(PTR, VAL, __ATOMIC_SEQ_CST)


#define atomic_compare_exchange_strong_explicit(PTR, VAL, DES, SUC, FAIL) ({ \
  __typeof__ (*(PTR)) __tmp  = (DES); 			\
  __atomic_compare_exchange_n ((PTR), (VAL), &__tmp, 0, (SUC), (FAIL)); })

#define atomic_compare_exchange_strong(PTR, VAL, DES) 			   \
  atomic_compare_exchange_strong_explicit(PTR, VAL, DES, __ATOMIC_SEQ_CST, \
				          __ATOMIC_SEQ_CST)

#define atomic_compare_exchange_weak_explicit(PTR, VAL, DES, SUC, FAIL) ({  \
  __typeof__ (*(PTR)) __tmp  = (DES); 			\
  __atomic_compare_exchange_n ((PTR), (VAL), &__tmp, 1, (SUC), (FAIL)); })

#define atomic_compare_exchange_weak(PTR, VAL, DES) 			 \
  atomic_compare_exchange_weak_explicit(PTR, VAL, DES, __ATOMIC_SEQ_CST, \
					__ATOMIC_SEQ_CST)



#define atomic_fetch_add(PTR, VAL) __atomic_fetch_add ((PTR), (VAL), 	\
						       __ATOMIC_SEQ_CST)
#define atomic_fetch_add_explicit(PTR, VAL, MO) 			\
			  __atomic_fetch_add ((PTR), (VAL), (MO))

#define atomic_fetch_sub(PTR, VAL) __atomic_fetch_sub ((PTR), (VAL), 	\
						       __ATOMIC_SEQ_CST)
#define atomic_fetch_sub_explicit(PTR, VAL, MO) 			\
			  __atomic_fetch_sub ((PTR), (VAL), (MO))

#define atomic_fetch_or(PTR, VAL) __atomic_fetch_or ((PTR), (VAL), 	\
						       __ATOMIC_SEQ_CST)
#define atomic_fetch_or_explicit(PTR, VAL, MO) 			\
			  __atomic_fetch_or ((PTR), (VAL), (MO))

#define atomic_fetch_xor(PTR, VAL) __atomic_fetch_xor ((PTR), (VAL), 	\
						       __ATOMIC_SEQ_CST)
#define atomic_fetch_xor_explicit(PTR, VAL, MO) 			\
			  __atomic_fetch_xor ((PTR), (VAL), (MO))

#define atomic_fetch_and(PTR, VAL) __atomic_fetch_and ((PTR), (VAL), 	\
						       __ATOMIC_SEQ_CST)
#define atomic_fetch_and_explicit(PTR, VAL, MO) 			\
			  __atomic_fetch_and ((PTR), (VAL), (MO))


#if __GCC_ATOMIC_TEST_AND_SET_TRUEVAL == 1
    typedef bool atomic_flag;
#else
    typedef unsigned char atomic_flag;
#endif

#define ATOMIC_FLAG_INIT	0


#define atomic_flag_test_and_set(PTR) 					\
			__atomic_flag_test_and_set ((PTR), __ATOMIC_SEQ_CST)
#define atomic_flag_test_and_set_explicit(PTR, MO)			\
			__atomic_flag_test_and_set ((PTR), (MO))

#define atomic_flag_clear(PTR)	__atomic_flag_clear ((PTR), __ATOMIC_SEQ_CST)
#define atomic_flag_clear_explicit(PTR, MO)   __atomic_flag_clear ((PTR), (MO))

Comments

Joseph Myers Aug. 7, 2013, 11:06 p.m. UTC | #1
On Fri, 2 Aug 2013, Andrew MacLeod wrote:

> The atomic expression patch works on many cases, but falls short with any kind
> of complexity on the RHS of an expression which needs to be mapped to an
> atomic_load.  Whilst parsing, we cant tell, and the rhs tends to become an
> expression before we really know. so In the comments I have my proposed
> solution which I will look at when I return in 2 weeks.
> Basically, rather than have the ATOMIC_TYPE attribute ripple all the way
> through an expression, as soon as we see an ATOMIC_TYPE, we wrap it in a new
> node ATOMIC_EXPR, which holds the atomic expression, and sets it own
> expression type to the non-atomic variant.  This means the only expressions
> which will have the TYPE_ATOMIC flag set will be ones which are wrapped in
> ATOMIC_EXPR, and those are the ones where we need to go and replace with an
> __atomic_load or whatever.

My understanding is that the issue here about loads applies exactly when 
an atomic lvalue is converted to an rvalue.

Thus, this should be handled not when the expression is parsed (it might 
after all be used as an operand of sizeof, unary &, increment, decrement 
or assignment operators, on the LHS of ".", or in some other GNU C cases 
such as __real__ and __imag__) but when the lvalue is converted to an 
rvalue.

Unfortunately there isn't a single well-defined place in the C front end 
at present that does such a conversion (and for non-atomics, it's not 
clear that e.g. inserting conversions to remove type qualifiers would be 
particularly useful).  There are calls to mark_exp_read in many relevant 
places, but that's not exactly the same (and, similarly, calls to 
default_function_array_conversion or 
default_function_array_read_conversion in some such places).  So I think 
you simply need to go through everywhere an expression appears in the 
syntax and make sure that if the context indicates lvalue-to-rvalue 
conversion happens, then a function that carries out such conversion (for 
the case of atomics) gets called.

Certainly it doesn't seem right to set the type of a containing expression 
to the non-atomic type immediately, given that lvalue conversion does not 
occur for operands of sizeof, and an atomic type may have a different type 
from the non-atomic variant.  So sizeof applied to an atomic lvalue needs 
to have the size of the atomic type.

A more ambiguous case is what happens with a cast to atomic type, or 
function returning atomic type, if the result is immediately passed to 
sizeof (and the atomic type has a different size from the non-atomic 
type).  This is related to DR#423; I'll raise it with WG14.

> Ive included an additional 2 line patch which should  change the meaning of
> __typeof__  (again untested, the joys of imminently leaving for 2 weeks  :-).
> Im not sure the normal practical uses of
> __typeof__ have much meaning for an atomic type, it seems far more useful to
> have __typeof__ for an atomic qualified type to return the non-atomic variant.

What typeof should do in general for qualified types is unclear 
(especially in the case of rvalues, where the movement in ISO C seems to 
be to say that rvalues can't have qualified types at all) - returning the 
non-atomic type seems reasonable to me.
diff mbox

Patch


	* cp/parser.c (cp_parser_simple_type_specifier): Change TYPEOF for
	atomic types to return the non-atomic type.

Index: gcc/cp/parser.c
===================================================================
*** gcc/cp/parser.c	(revision 201248)
--- gcc/cp/parser.c	(working copy)
*************** cp_parser_simple_type_specifier (cp_pars
*** 14262,14267 ****
--- 14262,14269 ----
        /* If it is not already a TYPE, take its type.  */
        if (!TYPE_P (type))
  	type = finish_typeof (type);
+       if (TYPE_ATOMIC (type))
+         type = TYPE_MAIN_VARIANT (type);
  
        if (decl_specs)
  	cp_parser_set_decl_spec_type (decl_specs, type,