]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR middle-end/14311 (builtins for atomic operations needed)
authorRichard Henderson <rth@gcc.gnu.org>
Thu, 14 Apr 2005 23:37:47 +0000 (16:37 -0700)
committerRichard Henderson <rth@gcc.gnu.org>
Thu, 14 Apr 2005 23:37:47 +0000 (16:37 -0700)
PR middle-end/14311
* builtin-types.def (BT_BOOL, BT_VOLATILE_PTR, BT_I1, BT_I2,
BT_I4, BT_I8, BT_FN_VOID_VPTR, BT_FN_I1_VPTR_I1, BT_FN_I2_VPTR_I2,
BT_FN_I4_VPTR_I4, BT_FN_I8_VPTR_I8, BT_FN_BOOL_VPTR_I1_I1,
BT_FN_BOOL_VPTR_I2_I2, BT_FN_BOOL_VPTR_I4_I4, BT_FN_BOOL_VPTR_I8_I8,
BT_FN_I1_VPTR_I1_I1, BT_FN_I2_VPTR_I2_I2, BT_FN_I4_VPTR_I4_I4,
BT_FN_I8_VPTR_I8_I8): New.
* builtins.def (DEF_SYNC_BUILTIN): New.
(BUILT_IN_FETCH_AND_ADD_N, BUILT_IN_FETCH_AND_ADD_1,
BUILT_IN_FETCH_AND_ADD_2, BUILT_IN_FETCH_AND_ADD_4,
BUILT_IN_FETCH_AND_ADD_8, BUILT_IN_FETCH_AND_SUB_N,
BUILT_IN_FETCH_AND_SUB_1, BUILT_IN_FETCH_AND_SUB_2,
BUILT_IN_FETCH_AND_SUB_4, BUILT_IN_FETCH_AND_SUB_8,
BUILT_IN_FETCH_AND_OR_N, BUILT_IN_FETCH_AND_OR_1,
BUILT_IN_FETCH_AND_OR_2, BUILT_IN_FETCH_AND_OR_4,
BUILT_IN_FETCH_AND_OR_8, BUILT_IN_FETCH_AND_AND_N,
BUILT_IN_FETCH_AND_AND_1, BUILT_IN_FETCH_AND_AND_2,
BUILT_IN_FETCH_AND_AND_4, BUILT_IN_FETCH_AND_AND_8,
BUILT_IN_FETCH_AND_XOR_N, BUILT_IN_FETCH_AND_XOR_1,
BUILT_IN_FETCH_AND_XOR_2, BUILT_IN_FETCH_AND_XOR_4,
BUILT_IN_FETCH_AND_XOR_8, BUILT_IN_FETCH_AND_NAND_N,
BUILT_IN_FETCH_AND_NAND_1, BUILT_IN_FETCH_AND_NAND_2,
BUILT_IN_FETCH_AND_NAND_4, BUILT_IN_FETCH_AND_NAND_8,
BUILT_IN_ADD_AND_FETCH_N, BUILT_IN_ADD_AND_FETCH_1,
BUILT_IN_ADD_AND_FETCH_2, BUILT_IN_ADD_AND_FETCH_4,
BUILT_IN_ADD_AND_FETCH_8, BUILT_IN_SUB_AND_FETCH_N,
BUILT_IN_SUB_AND_FETCH_1, BUILT_IN_SUB_AND_FETCH_2,
BUILT_IN_SUB_AND_FETCH_4, BUILT_IN_SUB_AND_FETCH_8,
BUILT_IN_OR_AND_FETCH_N, BUILT_IN_OR_AND_FETCH_1,
BUILT_IN_OR_AND_FETCH_2, BUILT_IN_OR_AND_FETCH_4,
BUILT_IN_OR_AND_FETCH_8, BUILT_IN_AND_AND_FETCH_N,
BUILT_IN_AND_AND_FETCH_1, BUILT_IN_AND_AND_FETCH_2,
BUILT_IN_AND_AND_FETCH_4, BUILT_IN_AND_AND_FETCH_8,
BUILT_IN_XOR_AND_FETCH_N, BUILT_IN_XOR_AND_FETCH_1,
BUILT_IN_XOR_AND_FETCH_2, BUILT_IN_XOR_AND_FETCH_4,
BUILT_IN_XOR_AND_FETCH_8, BUILT_IN_NAND_AND_FETCH_N,
BUILT_IN_NAND_AND_FETCH_1, BUILT_IN_NAND_AND_FETCH_2,
BUILT_IN_NAND_AND_FETCH_4, BUILT_IN_NAND_AND_FETCH_8,
BUILT_IN_BOOL_COMPARE_AND_SWAP_N, BUILT_IN_BOOL_COMPARE_AND_SWAP_1,
BUILT_IN_BOOL_COMPARE_AND_SWAP_2, BUILT_IN_BOOL_COMPARE_AND_SWAP_4,
BUILT_IN_BOOL_COMPARE_AND_SWAP_8, BUILT_IN_VAL_COMPARE_AND_SWAP_N,
BUILT_IN_VAL_COMPARE_AND_SWAP_1, BUILT_IN_VAL_COMPARE_AND_SWAP_2,
BUILT_IN_VAL_COMPARE_AND_SWAP_4, BUILT_IN_VAL_COMPARE_AND_SWAP_8,
BUILT_IN_LOCK_TEST_AND_SET_N, BUILT_IN_LOCK_TEST_AND_SET_1,
BUILT_IN_LOCK_TEST_AND_SET_2, BUILT_IN_LOCK_TEST_AND_SET_4,
BUILT_IN_LOCK_TEST_AND_SET_8, BUILT_IN_LOCK_RELEASE_N,
BUILT_IN_LOCK_RELEASE_1, BUILT_IN_LOCK_RELEASE_2,
BUILT_IN_LOCK_RELEASE_4, BUILT_IN_LOCK_RELEASE_8,
BUILT_IN_SYNCHRONIZE: New.
* builtins.c (called_as_built_in): Rewrite from CALLED_AS_BUILT_IN
as a function.  Accept __sync_ as a prefix as well.
(expand_builtin_sync_operation, expand_builtin_compare_and_swap,
expand_builtin_lock_test_and_set, expand_builtin_synchronize,
expand_builtin_lock_release): New.
(expand_builtin): Call them.
* c-common.c (DEF_BUILTIN): Don't require __builtin_ prefix if
neither BOTH_P nor FALLBACK_P are defined.
(builtin_type_for_size): New.
(sync_resolve_size, sync_resolve_params, sync_resolve_return): New.
(resolve_overloaded_builtin): New.
* c-common.h (resolve_overloaded_builtin): Declare.
(builtin_type_for_size): Declare.
* c-typeck.c (build_function_call): Invoke resolve_overloaded_builtin.
* expr.c (sync_add_optab, sync_sub_optab, sync_ior_optab,
sync_and_optab, sync_xor_optab, sync_nand_optab, sync_old_add_optab,
sync_old_sub_optab, sync_old_ior_optab, sync_old_and_optab,
sync_old_xor_optab, sync_old_nand_optab, sync_new_add_optab,
sync_new_sub_optab, sync_new_ior_optab, sync_new_and_optab,
sync_new_xor_optab, sync_new_nand_optab, sync_compare_and_swap,
sync_compare_and_swap_cc, sync_lock_test_and_set,
sync_lock_release): New.
* optabs.h: Declare them.
* expr.h (expand_val_compare_and_swap, expand_bool_compare_and_swap,
expand_sync_operation, expand_sync_fetch_operation,
expand_sync_lock_test_and_set): Declare.
* genopinit.c (optabs): Add sync optabs.
* optabs.c (init_optabs): Initialize sync optabs.
(expand_val_compare_and_swap_1, expand_val_compare_and_swap,
expand_bool_compare_and_swap, expand_compare_and_swap_loop,
expand_sync_operation, expand_sync_fetch_operation,
expand_sync_lock_test_and_set): New.
* doc/extend.texi (Atomic Builtins): New section
* doc/md.texi (Standard Names): Add sync patterns.

From-SVN: r98154

16 files changed:
gcc/ChangeLog
gcc/builtin-types.def
gcc/builtins.c
gcc/builtins.def
gcc/c-common.c
gcc/c-common.h
gcc/c-typeck.c
gcc/doc/extend.texi
gcc/doc/md.texi
gcc/expr.c
gcc/expr.h
gcc/genopinit.c
gcc/optabs.c
gcc/optabs.h
gcc/testsuite/gcc.c-torture/compile/sync-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/sync-1.c [new file with mode: 0644]

index 53aa00c82b36ea84eed4bddf10f205d04096b8c8..88c3a037497296d2e8780192e0ffdd2133818ec5 100644 (file)
@@ -1,3 +1,89 @@
+2004-04-14  Richard Henderson  <rth@redhat.com>
+
+       PR middle-end/14311
+       * builtin-types.def (BT_BOOL, BT_VOLATILE_PTR, BT_I1, BT_I2,
+       BT_I4, BT_I8, BT_FN_VOID_VPTR, BT_FN_I1_VPTR_I1, BT_FN_I2_VPTR_I2,
+       BT_FN_I4_VPTR_I4, BT_FN_I8_VPTR_I8, BT_FN_BOOL_VPTR_I1_I1,
+       BT_FN_BOOL_VPTR_I2_I2, BT_FN_BOOL_VPTR_I4_I4, BT_FN_BOOL_VPTR_I8_I8,
+       BT_FN_I1_VPTR_I1_I1, BT_FN_I2_VPTR_I2_I2, BT_FN_I4_VPTR_I4_I4,
+       BT_FN_I8_VPTR_I8_I8): New.
+       * builtins.def (DEF_SYNC_BUILTIN): New.
+       (BUILT_IN_FETCH_AND_ADD_N, BUILT_IN_FETCH_AND_ADD_1,
+       BUILT_IN_FETCH_AND_ADD_2, BUILT_IN_FETCH_AND_ADD_4,
+       BUILT_IN_FETCH_AND_ADD_8, BUILT_IN_FETCH_AND_SUB_N,
+       BUILT_IN_FETCH_AND_SUB_1, BUILT_IN_FETCH_AND_SUB_2,
+       BUILT_IN_FETCH_AND_SUB_4, BUILT_IN_FETCH_AND_SUB_8,
+       BUILT_IN_FETCH_AND_OR_N, BUILT_IN_FETCH_AND_OR_1,
+       BUILT_IN_FETCH_AND_OR_2, BUILT_IN_FETCH_AND_OR_4,
+       BUILT_IN_FETCH_AND_OR_8, BUILT_IN_FETCH_AND_AND_N,
+       BUILT_IN_FETCH_AND_AND_1, BUILT_IN_FETCH_AND_AND_2,
+       BUILT_IN_FETCH_AND_AND_4, BUILT_IN_FETCH_AND_AND_8,
+       BUILT_IN_FETCH_AND_XOR_N, BUILT_IN_FETCH_AND_XOR_1,
+       BUILT_IN_FETCH_AND_XOR_2, BUILT_IN_FETCH_AND_XOR_4,
+       BUILT_IN_FETCH_AND_XOR_8, BUILT_IN_FETCH_AND_NAND_N,
+       BUILT_IN_FETCH_AND_NAND_1, BUILT_IN_FETCH_AND_NAND_2,
+       BUILT_IN_FETCH_AND_NAND_4, BUILT_IN_FETCH_AND_NAND_8,
+       BUILT_IN_ADD_AND_FETCH_N, BUILT_IN_ADD_AND_FETCH_1,
+       BUILT_IN_ADD_AND_FETCH_2, BUILT_IN_ADD_AND_FETCH_4,
+       BUILT_IN_ADD_AND_FETCH_8, BUILT_IN_SUB_AND_FETCH_N,
+       BUILT_IN_SUB_AND_FETCH_1, BUILT_IN_SUB_AND_FETCH_2,
+       BUILT_IN_SUB_AND_FETCH_4, BUILT_IN_SUB_AND_FETCH_8,
+       BUILT_IN_OR_AND_FETCH_N, BUILT_IN_OR_AND_FETCH_1,
+       BUILT_IN_OR_AND_FETCH_2, BUILT_IN_OR_AND_FETCH_4,
+       BUILT_IN_OR_AND_FETCH_8, BUILT_IN_AND_AND_FETCH_N,
+       BUILT_IN_AND_AND_FETCH_1, BUILT_IN_AND_AND_FETCH_2,
+       BUILT_IN_AND_AND_FETCH_4, BUILT_IN_AND_AND_FETCH_8,
+       BUILT_IN_XOR_AND_FETCH_N, BUILT_IN_XOR_AND_FETCH_1,
+       BUILT_IN_XOR_AND_FETCH_2, BUILT_IN_XOR_AND_FETCH_4,
+       BUILT_IN_XOR_AND_FETCH_8, BUILT_IN_NAND_AND_FETCH_N,
+       BUILT_IN_NAND_AND_FETCH_1, BUILT_IN_NAND_AND_FETCH_2,
+       BUILT_IN_NAND_AND_FETCH_4, BUILT_IN_NAND_AND_FETCH_8,
+       BUILT_IN_BOOL_COMPARE_AND_SWAP_N, BUILT_IN_BOOL_COMPARE_AND_SWAP_1,
+       BUILT_IN_BOOL_COMPARE_AND_SWAP_2, BUILT_IN_BOOL_COMPARE_AND_SWAP_4,
+       BUILT_IN_BOOL_COMPARE_AND_SWAP_8, BUILT_IN_VAL_COMPARE_AND_SWAP_N,
+       BUILT_IN_VAL_COMPARE_AND_SWAP_1, BUILT_IN_VAL_COMPARE_AND_SWAP_2,
+       BUILT_IN_VAL_COMPARE_AND_SWAP_4, BUILT_IN_VAL_COMPARE_AND_SWAP_8,
+       BUILT_IN_LOCK_TEST_AND_SET_N, BUILT_IN_LOCK_TEST_AND_SET_1,
+       BUILT_IN_LOCK_TEST_AND_SET_2, BUILT_IN_LOCK_TEST_AND_SET_4,
+       BUILT_IN_LOCK_TEST_AND_SET_8, BUILT_IN_LOCK_RELEASE_N,
+       BUILT_IN_LOCK_RELEASE_1, BUILT_IN_LOCK_RELEASE_2,
+       BUILT_IN_LOCK_RELEASE_4, BUILT_IN_LOCK_RELEASE_8,
+       BUILT_IN_SYNCHRONIZE: New.
+       * builtins.c (called_as_built_in): Rewrite from CALLED_AS_BUILT_IN
+       as a function.  Accept __sync_ as a prefix as well.
+       (expand_builtin_sync_operation, expand_builtin_compare_and_swap,
+       expand_builtin_lock_test_and_set, expand_builtin_synchronize,
+       expand_builtin_lock_release): New.
+       (expand_builtin): Call them.
+       * c-common.c (DEF_BUILTIN): Don't require __builtin_ prefix if
+       neither BOTH_P nor FALLBACK_P are defined.
+       (builtin_type_for_size): New.
+       (sync_resolve_size, sync_resolve_params, sync_resolve_return): New.
+       (resolve_overloaded_builtin): New.
+       * c-common.h (resolve_overloaded_builtin): Declare.
+       (builtin_type_for_size): Declare.
+       * c-typeck.c (build_function_call): Invoke resolve_overloaded_builtin.
+       * expr.c (sync_add_optab, sync_sub_optab, sync_ior_optab,
+       sync_and_optab, sync_xor_optab, sync_nand_optab, sync_old_add_optab,
+       sync_old_sub_optab, sync_old_ior_optab, sync_old_and_optab,
+       sync_old_xor_optab, sync_old_nand_optab, sync_new_add_optab,
+       sync_new_sub_optab, sync_new_ior_optab, sync_new_and_optab,
+       sync_new_xor_optab, sync_new_nand_optab, sync_compare_and_swap,
+       sync_compare_and_swap_cc, sync_lock_test_and_set,
+       sync_lock_release): New.
+       * optabs.h: Declare them.
+       * expr.h (expand_val_compare_and_swap, expand_bool_compare_and_swap,
+       expand_sync_operation, expand_sync_fetch_operation,
+       expand_sync_lock_test_and_set): Declare.
+       * genopinit.c (optabs): Add sync optabs.
+       * optabs.c (init_optabs): Initialize sync optabs.
+       (expand_val_compare_and_swap_1, expand_val_compare_and_swap,
+       expand_bool_compare_and_swap, expand_compare_and_swap_loop,
+       expand_sync_operation, expand_sync_fetch_operation,
+       expand_sync_lock_test_and_set): New.
+       * doc/extend.texi (Atomic Builtins): New section
+       * doc/md.texi (Standard Names): Add sync patterns.
+
 2005-04-14  Alexandre Oliva  <aoliva@redhat.com>
 
        * tree-eh.c (lower_try_finally_copy): Generate new code in
 
 2005-04-13  Dale Johannesen  <dalej@apple.com>
 
-        * objc/Make-lang.in (objc-lang.o):  Depend on tree-gimple.h.
-        (objc-act.o):  Ditto.
-        * objc/objc-act.c (objc_gimplify_expr):  New.
-        (objc_get_callee_fndecl):  New.
-        * objc/objc-act.h:  Include tree-gimple.h.  Declare new functions.
-        * objc/objc-lang.c (LANG_HOOKS_GIMPLIFY_EXPR):  Define.
-        (LANG_HOOKS_GET_CALLEE_FNDECL):  Define.
+       * objc/Make-lang.in (objc-lang.o):  Depend on tree-gimple.h.
+       (objc-act.o):  Ditto.
+       * objc/objc-act.c (objc_gimplify_expr):  New.
+       (objc_get_callee_fndecl):  New.
+       * objc/objc-act.h:  Include tree-gimple.h.  Declare new functions.
+       * objc/objc-lang.c (LANG_HOOKS_GIMPLIFY_EXPR):  Define.
+       (LANG_HOOKS_GET_CALLEE_FNDECL):  Define.
 
 2005-04-13  Devang Patel  <dpatel@apple.com>
 
 
 2005-04-11  Devang Patel  <dpatel@apple.com>
 
-        * tree-data-ref.c (build_classic_dist_vector,
-        compute_subscript_distance): Make externally visible.
-        * tree-data-ref.h (build_classic_dist_vector,
-        compute_subscript_distance): Same.
-        * tree-vect-analyze.c (vect_analyze_data_ref_dependence):
-        Check distance vector against vectorization factor.
-        (vect_analyze_loop): Determine vectorizaion factor before
-        analyzing data dependences.
-        * tree-vectorizer.c (loops_num): Make it externally visible and
-        rename ...
-        * tree-vectorizer.c (vect_loops_num): ... new name.
-        * tree-vectorizer.h  (vect_loops_num): New.
+       * tree-data-ref.c (build_classic_dist_vector,
+       compute_subscript_distance): Make externally visible.
+       * tree-data-ref.h (build_classic_dist_vector,
+       compute_subscript_distance): Same.
+       * tree-vect-analyze.c (vect_analyze_data_ref_dependence):
+       Check distance vector against vectorization factor.
+       (vect_analyze_loop): Determine vectorizaion factor before
+       analyzing data dependences.
+       * tree-vectorizer.c (loops_num): Make it externally visible and
+       rename ...
+       * tree-vectorizer.c (vect_loops_num): ... new name.
+       * tree-vectorizer.h  (vect_loops_num): New.
 
 2005-04-11  Devang Patel  <dpatel@apple.com>
 
-        * tree-vect-analyze.c (vect_analyze_operations): Check
-        vectorizable codition.
-        * tree-vect-transform.c (vect_is_simple_cond): New function.
-        (vectorizable_condition): New function.
-        (vect_transform_stmt): Handle condition_vec_info_type.
-        * tree-vectorizer.h (enum stmt_vec_info_type): Add
-        condition_vec_info_type.
-        (vectorizable_condition): New.
+       * tree-vect-analyze.c (vect_analyze_operations): Check
+       vectorizable codition.
+       * tree-vect-transform.c (vect_is_simple_cond): New function.
+       (vectorizable_condition): New function.
+       (vect_transform_stmt): Handle condition_vec_info_type.
+       * tree-vectorizer.h (enum stmt_vec_info_type): Add
+       condition_vec_info_type.
+       (vectorizable_condition): New.
        
 2005-04-11  Geoffrey Keating  <geoffk@apple.com>
 
index d9b87df309fccb22ad46a4adbd291a1e29fe5784..4b5f35351e49f69315d446a1976fbb779678663f 100644 (file)
@@ -60,6 +60,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
     the type pointed to.  */
 
 DEF_PRIMITIVE_TYPE (BT_VOID, void_type_node)
+DEF_PRIMITIVE_TYPE (BT_BOOL, boolean_type_node)
 DEF_PRIMITIVE_TYPE (BT_INT, integer_type_node)
 DEF_PRIMITIVE_TYPE (BT_UINT, unsigned_type_node)
 DEF_PRIMITIVE_TYPE (BT_LONG, long_integer_type_node)
@@ -79,6 +80,10 @@ DEF_PRIMITIVE_TYPE (BT_COMPLEX_LONGDOUBLE, complex_long_double_type_node)
 DEF_PRIMITIVE_TYPE (BT_PTR, ptr_type_node)
 DEF_PRIMITIVE_TYPE (BT_FILEPTR, fileptr_type_node)
 DEF_PRIMITIVE_TYPE (BT_CONST_PTR, const_ptr_type_node)
+DEF_PRIMITIVE_TYPE (BT_VOLATILE_PTR,
+                   build_pointer_type
+                    (build_qualified_type (void_type_node,
+                                           TYPE_QUAL_VOLATILE)))
 DEF_PRIMITIVE_TYPE (BT_PTRMODE, (*lang_hooks.types.type_for_mode)(ptr_mode, 0))
 DEF_PRIMITIVE_TYPE (BT_INT_PTR, integer_ptr_type_node)
 DEF_PRIMITIVE_TYPE (BT_FLOAT_PTR, float_ptr_type_node)
@@ -94,6 +99,11 @@ DEF_PRIMITIVE_TYPE (BT_CONST_STRING, const_string_type_node)
 DEF_PRIMITIVE_TYPE (BT_VALIST_REF, va_list_ref_type_node)
 DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
 
+DEF_PRIMITIVE_TYPE (BT_I1, builtin_type_for_size (BITS_PER_UNIT*1, 1))
+DEF_PRIMITIVE_TYPE (BT_I2, builtin_type_for_size (BITS_PER_UNIT*2, 1))
+DEF_PRIMITIVE_TYPE (BT_I4, builtin_type_for_size (BITS_PER_UNIT*4, 1))
+DEF_PRIMITIVE_TYPE (BT_I8, builtin_type_for_size (BITS_PER_UNIT*8, 1))
+
 DEF_POINTER_TYPE (BT_PTR_CONST_STRING, BT_CONST_STRING)
 
 DEF_FUNCTION_TYPE_0 (BT_FN_VOID, BT_VOID)
@@ -160,6 +170,7 @@ DEF_FUNCTION_TYPE_1 (BT_FN_STRING_CONST_STRING, BT_STRING, BT_CONST_STRING)
 DEF_FUNCTION_TYPE_1 (BT_FN_WORD_PTR, BT_WORD, BT_PTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_INT_WINT, BT_INT, BT_WINT)
 DEF_FUNCTION_TYPE_1 (BT_FN_WINT_WINT, BT_WINT, BT_WINT)
+DEF_FUNCTION_TYPE_1 (BT_FN_VOID_VPTR, BT_VOID, BT_VOLATILE_PTR)
 
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_INT, BT_VOID, BT_PTR, BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_STRING_STRING_CONST_STRING, 
@@ -241,6 +252,10 @@ DEF_FUNCTION_TYPE_2 (BT_FN_COMPLEX_LONGDOUBLE_COMPLEX_LONGDOUBLE_COMPLEX_LONGDOU
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_PTR, BT_VOID, BT_PTR, BT_PTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING,
                     BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING)
+DEF_FUNCTION_TYPE_2 (BT_FN_I1_VPTR_I1, BT_I1, BT_VOLATILE_PTR, BT_I1)
+DEF_FUNCTION_TYPE_2 (BT_FN_I2_VPTR_I2, BT_I2, BT_VOLATILE_PTR, BT_I2)
+DEF_FUNCTION_TYPE_2 (BT_FN_I4_VPTR_I4, BT_I4, BT_VOLATILE_PTR, BT_I4)
+DEF_FUNCTION_TYPE_2 (BT_FN_I8_VPTR_I8, BT_I8, BT_VOLATILE_PTR, BT_I8)
 
 DEF_FUNCTION_TYPE_3 (BT_FN_STRING_STRING_CONST_STRING_SIZE,
                     BT_STRING, BT_STRING, BT_CONST_STRING, BT_SIZE)
@@ -285,6 +300,18 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_LONGDOUBLE_LONGDOUBLEPTR_LONGDOUBLEPTR,
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_PTR_PTR, BT_VOID, BT_PTR, BT_PTR, BT_PTR)
 DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING_PTR_CONST_STRING,
                     BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING, BT_PTR_CONST_STRING)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_VPTR_I1_I1, BT_BOOL, BT_VOLATILE_PTR,
+                    BT_I1, BT_I1)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_VPTR_I2_I2, BT_BOOL, BT_VOLATILE_PTR,
+                    BT_I2, BT_I2)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_VPTR_I4_I4, BT_BOOL, BT_VOLATILE_PTR,
+                    BT_I4, BT_I4)
+DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_VPTR_I8_I8, BT_BOOL, BT_VOLATILE_PTR,
+                    BT_I8, BT_I8)
+DEF_FUNCTION_TYPE_3 (BT_FN_I1_VPTR_I1_I1, BT_I1, BT_VOLATILE_PTR, BT_I1, BT_I1)
+DEF_FUNCTION_TYPE_3 (BT_FN_I2_VPTR_I2_I2, BT_I2, BT_VOLATILE_PTR, BT_I2, BT_I2)
+DEF_FUNCTION_TYPE_3 (BT_FN_I4_VPTR_I4_I4, BT_I4, BT_VOLATILE_PTR, BT_I4, BT_I4)
+DEF_FUNCTION_TYPE_3 (BT_FN_I8_VPTR_I8_I8, BT_I8, BT_VOLATILE_PTR, BT_I8, BT_I8)
 
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
                     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
index b7ddb747495126204f55daebb82979212034dc2e..1a54a06854b275352796ea9d209ed48aac069bf3 100644 (file)
@@ -48,9 +48,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "basic-block.h"
 #include "tree-mudflap.h"
 
-#define CALLED_AS_BUILT_IN(NODE) \
-   (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
-
 #ifndef PAD_VARARGS_DOWN
 #define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN
 #endif
@@ -191,6 +188,19 @@ static tree fold_builtin_strspn (tree);
 static tree fold_builtin_strcspn (tree);
 static tree fold_builtin_sprintf (tree, int);
 
+/* Return true if NODE should be considered for inline expansion regardless
+   of the optimization level.  This means whenever a function is invoked with
+   its "internal" name, which normally contains the prefix "__builtin".  */
+
+static bool called_as_built_in (tree node)
+{
+  const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
+  if (strncmp (name, "__builtin_", 10) == 0)
+    return true;
+  if (strncmp (name, "__sync_", 7) == 0)
+    return true;
+  return false;
+}
 
 /* Return the alignment in bits of EXP, a pointer valued expression.
    But don't return more than MAX_ALIGN no matter what.
@@ -5225,6 +5235,166 @@ expand_builtin_fork_or_exec (tree fn, tree arglist, rtx target, int ignore)
 
   return expand_call (call, target, ignore);
 }
+
+\f
+/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics.
+   ARGLIST is the operands list to the function.  CODE is the rtx code 
+   that corresponds to the arithmetic or logical operation from the name;
+   an exception here is that NOT actually means NAND.  TARGET is an optional
+   place for us to store the results; AFTER is true if this is the
+   fetch_and_xxx form.  IGNORE is true if we don't actually care about
+   the result of the operation at all.  */
+
+static rtx
+expand_builtin_sync_operation (tree arglist, enum rtx_code code, bool after,
+                              rtx target, bool ignore)
+{
+  enum machine_mode mode;
+  rtx addr, val, mem;
+
+  /* Expand the operands.  */
+  addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_SUM);
+  mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+  arglist = TREE_CHAIN (arglist);
+  val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the full barrier semantics of the intrinsic.  */
+  mem = validize_mem (gen_rtx_MEM (mode, addr));
+  MEM_VOLATILE_P (mem) = 1;
+
+  if (ignore)
+    return expand_sync_operation (mem, val, code);
+  else
+    return expand_sync_fetch_operation (mem, val, code, after, target);
+}
+
+/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap
+   intrinsics.  ARGLIST is the operands list to the function.  IS_BOOL is
+   true if this is the boolean form.  TARGET is a place for us to store the
+   results; this is NOT optional if IS_BOOL is true.  */
+
+static rtx
+expand_builtin_compare_and_swap (tree arglist, bool is_bool, rtx target)
+{
+  enum machine_mode mode;
+  rtx addr, old_val, new_val, mem;
+
+  /* Expand the operands.  */
+  addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_SUM);
+  mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+  arglist = TREE_CHAIN (arglist);
+  old_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+  arglist = TREE_CHAIN (arglist);
+  new_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the full barrier semantics of the intrinsic.  */
+  mem = validize_mem (gen_rtx_MEM (mode, addr));
+  MEM_VOLATILE_P (mem) = 1;
+
+  if (is_bool)
+    return expand_bool_compare_and_swap (mem, old_val, new_val, target);
+  else
+    return expand_val_compare_and_swap (mem, old_val, new_val, target);
+}
+
+/* Expand the __sync_lock_test_and_set intrinsic.  Note that the most
+   general form is actually an atomic exchange, and some targets only
+   support a reduced form with the second argument being a constant 1.
+   ARGLIST is the operands list to the function; TARGET is an optional
+   place for us to store the results.  */
+
+static rtx
+expand_builtin_lock_test_and_set (tree arglist, rtx target)
+{
+  enum machine_mode mode;
+  rtx addr, val, mem;
+
+  /* Expand the operands.  */
+  addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_NORMAL);
+  mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+  arglist = TREE_CHAIN (arglist);
+  val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the barrier semantics of the intrinsic.  */
+  mem = validize_mem (gen_rtx_MEM (mode, addr));
+  MEM_VOLATILE_P (mem) = 1;
+
+  return expand_sync_lock_test_and_set (mem, val, target);
+}
+
+/* Expand the __sync_synchronize intrinsic.  */
+
+static void
+expand_builtin_synchronize (void)
+{
+  rtx body;
+
+#ifdef HAVE_memory_barrier
+  if (HAVE_memory_barrier)
+    {
+      emit_insn (gen_memory_barrier ());
+      return;
+    }
+#endif
+
+  /* If no explicit memory barrier instruction is available, create an empty
+     asm stmt that will prevent compiler movement across the barrier.  */
+  body = gen_rtx_ASM_INPUT (VOIDmode, "");
+  MEM_VOLATILE_P (body) = 1;
+  emit_insn (body);
+}
+
+/* Expand the __sync_lock_release intrinsic.  ARGLIST is the operands list
+   to the function.  */
+
+static void
+expand_builtin_lock_release (tree arglist)
+{
+  enum machine_mode mode;
+  enum insn_code icode;
+  rtx addr, val, mem, insn;
+
+  /* Expand the operands.  */
+  addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_NORMAL);
+  mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+  val = const0_rtx;
+
+  /* Note that we explicitly do not want any alias information for this
+     memory, so that we kill all other live memories.  Otherwise we don't
+     satisfy the barrier semantics of the intrinsic.  */
+  mem = validize_mem (gen_rtx_MEM (mode, addr));
+  MEM_VOLATILE_P (mem) = 1;
+
+  /* If there is an explicit operation in the md file, use it.  */
+  icode = sync_lock_release[mode];
+  if (icode != CODE_FOR_nothing)
+    {
+      if (!insn_data[icode].operand[1].predicate (val, mode))
+       val = force_reg (mode, val);
+
+      insn = GEN_FCN (icode) (mem, val);
+      if (insn)
+       {
+         emit_insn (insn);
+         return;
+       }
+    }
+
+  /* Otherwise we can implement this operation by emitting a barrier
+     followed by a store of zero.  */
+  expand_builtin_synchronize ();
+  emit_move_insn (mem, val);
+}
 \f
 /* Expand an expression EXP that calls a built-in function,
    with result going to TARGET if that's convenient
@@ -5247,7 +5417,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
   /* When not optimizing, generate calls to library functions for a certain
      set of builtins.  */
   if (!optimize
-      && !CALLED_AS_BUILT_IN (fndecl)
+      && !called_as_built_in (fndecl)
       && DECL_ASSEMBLER_NAME_SET_P (fndecl)
       && fcode != BUILT_IN_ALLOCA)
     return expand_call (exp, target, ignore);
@@ -5881,6 +6051,166 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
        return target;
       break;
 
+    case BUILT_IN_FETCH_AND_ADD_1:
+    case BUILT_IN_FETCH_AND_ADD_2:
+    case BUILT_IN_FETCH_AND_ADD_4:
+    case BUILT_IN_FETCH_AND_ADD_8:
+      target = expand_builtin_sync_operation (arglist, PLUS,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_SUB_1:
+    case BUILT_IN_FETCH_AND_SUB_2:
+    case BUILT_IN_FETCH_AND_SUB_4:
+    case BUILT_IN_FETCH_AND_SUB_8:
+      target = expand_builtin_sync_operation (arglist, MINUS,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_OR_1:
+    case BUILT_IN_FETCH_AND_OR_2:
+    case BUILT_IN_FETCH_AND_OR_4:
+    case BUILT_IN_FETCH_AND_OR_8:
+      target = expand_builtin_sync_operation (arglist, IOR,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_AND_1:
+    case BUILT_IN_FETCH_AND_AND_2:
+    case BUILT_IN_FETCH_AND_AND_4:
+    case BUILT_IN_FETCH_AND_AND_8:
+      target = expand_builtin_sync_operation (arglist, AND,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_XOR_1:
+    case BUILT_IN_FETCH_AND_XOR_2:
+    case BUILT_IN_FETCH_AND_XOR_4:
+    case BUILT_IN_FETCH_AND_XOR_8:
+      target = expand_builtin_sync_operation (arglist, XOR,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_FETCH_AND_NAND_1:
+    case BUILT_IN_FETCH_AND_NAND_2:
+    case BUILT_IN_FETCH_AND_NAND_4:
+    case BUILT_IN_FETCH_AND_NAND_8:
+      target = expand_builtin_sync_operation (arglist, NOT,
+                                             false, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_ADD_AND_FETCH_1:
+    case BUILT_IN_ADD_AND_FETCH_2:
+    case BUILT_IN_ADD_AND_FETCH_4:
+    case BUILT_IN_ADD_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, PLUS,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_SUB_AND_FETCH_1:
+    case BUILT_IN_SUB_AND_FETCH_2:
+    case BUILT_IN_SUB_AND_FETCH_4:
+    case BUILT_IN_SUB_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, MINUS,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_OR_AND_FETCH_1:
+    case BUILT_IN_OR_AND_FETCH_2:
+    case BUILT_IN_OR_AND_FETCH_4:
+    case BUILT_IN_OR_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, IOR,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_AND_AND_FETCH_1:
+    case BUILT_IN_AND_AND_FETCH_2:
+    case BUILT_IN_AND_AND_FETCH_4:
+    case BUILT_IN_AND_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, AND,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_XOR_AND_FETCH_1:
+    case BUILT_IN_XOR_AND_FETCH_2:
+    case BUILT_IN_XOR_AND_FETCH_4:
+    case BUILT_IN_XOR_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, XOR,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_NAND_AND_FETCH_1:
+    case BUILT_IN_NAND_AND_FETCH_2:
+    case BUILT_IN_NAND_AND_FETCH_4:
+    case BUILT_IN_NAND_AND_FETCH_8:
+      target = expand_builtin_sync_operation (arglist, NOT,
+                                             true, target, ignore);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_1:
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_2:
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_4:
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_8:
+      if (!target || !register_operand (target, mode))
+       target = gen_reg_rtx (mode);
+      target = expand_builtin_compare_and_swap (arglist, true, target);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_1:
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_2:
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_4:
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_8:
+      target = expand_builtin_compare_and_swap (arglist, false, target);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_LOCK_TEST_AND_SET_1:
+    case BUILT_IN_LOCK_TEST_AND_SET_2:
+    case BUILT_IN_LOCK_TEST_AND_SET_4:
+    case BUILT_IN_LOCK_TEST_AND_SET_8:
+      target = expand_builtin_lock_test_and_set (arglist, target);
+      if (target)
+       return target;
+      break;
+
+    case BUILT_IN_LOCK_RELEASE_1:
+    case BUILT_IN_LOCK_RELEASE_2:
+    case BUILT_IN_LOCK_RELEASE_4:
+    case BUILT_IN_LOCK_RELEASE_8:
+      expand_builtin_lock_release (arglist);
+      return const0_rtx;
+
+    case BUILT_IN_SYNCHRONIZE:
+      expand_builtin_synchronize ();
+      return const0_rtx;
+
     default:   /* just do library call, if unknown builtin */
       break;
     }
index 51d87ca362e4c0e3fce69116384f34f042e079bf..134bf98a12909e28997681a2d59e593faade0f75 100644 (file)
@@ -71,6 +71,12 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, BT_LAST,        \
                false, false, false, ATTRS, true, true)
 
+/* Like DEF_GCC_BUILTIN, except we don't prepend "__builtin_".  */
+#undef DEF_SYNC_BUILTIN
+#define DEF_SYNC_BUILTIN(ENUM, NAME, TYPE, ATTRS)              \
+  DEF_BUILTIN (ENUM, NAME, BUILT_IN_NORMAL, TYPE, BT_LAST,     \
+               false, false, false, ATTRS, true, true)
+
 /* A library builtin (like __builtin_strchr) is a builtin equivalent
    of an ANSI/ISO standard library function.  In addition to the
    `__builtin' version, we will create an ordinary version (e.g,
@@ -657,3 +663,196 @@ DEF_BUILTIN_STUB (BUILT_IN_STACK_RESTORE, "__builtin_stack_restore")
 /* Profiling hooks.  */
 DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_ENTER, "profile_func_enter")
 DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_EXIT, "profile_func_exit")
+
+/* Synchronization Primitives.  The "_N" version is the one that the user
+   is supposed to be using.  It's overloaded, and is resolved to one of the
+   "_1" through "_8" versions, plus some extra casts.  */
+
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_ADD_N, "__sync_fetch_and_add",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_ADD_1, "__sync_fetch_and_add_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_ADD_2, "__sync_fetch_and_add_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_ADD_4, "__sync_fetch_and_add_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_ADD_8, "__sync_fetch_and_add_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_SUB_N, "__sync_fetch_and_sub",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_SUB_1, "__sync_fetch_and_sub_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_SUB_2, "__sync_fetch_and_sub_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_SUB_4, "__sync_fetch_and_sub_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_SUB_8, "__sync_fetch_and_sub_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_OR_N, "__sync_fetch_and_or",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_OR_1, "__sync_fetch_and_or_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_OR_2, "__sync_fetch_and_or_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_OR_4, "__sync_fetch_and_or_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_OR_8, "__sync_fetch_and_or_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_AND_N, "__sync_fetch_and_and",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_AND_1, "__sync_fetch_and_and_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_AND_2, "__sync_fetch_and_and_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_AND_4, "__sync_fetch_and_and_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_AND_8, "__sync_fetch_and_and_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_XOR_N, "__sync_fetch_and_xor",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_XOR_1, "__sync_fetch_and_xor_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_XOR_2, "__sync_fetch_and_xor_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_XOR_4, "__sync_fetch_and_xor_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_XOR_8, "__sync_fetch_and_xor_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_NAND_N, "__sync_fetch_and_nand",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_NAND_1, "__sync_fetch_and_nand_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_NAND_2, "__sync_fetch_and_nand_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_NAND_4, "__sync_fetch_and_nand_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_FETCH_AND_NAND_8, "__sync_fetch_and_nand_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_ADD_AND_FETCH_N, "__sync_add_and_fetch",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_ADD_AND_FETCH_1, "__sync_add_and_fetch_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_ADD_AND_FETCH_2, "__sync_add_and_fetch_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_ADD_AND_FETCH_4, "__sync_add_and_fetch_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_ADD_AND_FETCH_8, "__sync_add_and_fetch_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_SUB_AND_FETCH_N, "__sync_sub_and_fetch",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_SUB_AND_FETCH_1, "__sync_sub_and_fetch_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_SUB_AND_FETCH_2, "__sync_sub_and_fetch_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_SUB_AND_FETCH_4, "__sync_sub_and_fetch_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_SUB_AND_FETCH_8, "__sync_sub_and_fetch_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_OR_AND_FETCH_N, "__sync_or_and_fetch",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_OR_AND_FETCH_1, "__sync_or_and_fetch_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_OR_AND_FETCH_2, "__sync_or_and_fetch_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_OR_AND_FETCH_4, "__sync_or_and_fetch_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_OR_AND_FETCH_8, "__sync_or_and_fetch_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_AND_AND_FETCH_N, "__sync_and_and_fetch",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_AND_AND_FETCH_1, "__sync_and_and_fetch_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_AND_AND_FETCH_2, "__sync_and_and_fetch_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_AND_AND_FETCH_4, "__sync_and_and_fetch_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_AND_AND_FETCH_8, "__sync_and_and_fetch_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_XOR_AND_FETCH_N, "__sync_xor_and_fetch",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_XOR_AND_FETCH_1, "__sync_xor_and_fetch_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_XOR_AND_FETCH_2, "__sync_xor_and_fetch_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_XOR_AND_FETCH_4, "__sync_xor_and_fetch_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_XOR_AND_FETCH_8, "__sync_xor_and_fetch_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_NAND_AND_FETCH_N, "__sync_nand_and_fetch",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_NAND_AND_FETCH_1, "__sync_nand_and_fetch_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_NAND_AND_FETCH_2, "__sync_nand_and_fetch_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_NAND_AND_FETCH_4, "__sync_nand_and_fetch_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_NAND_AND_FETCH_8, "__sync_nand_and_fetch_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_BOOL_COMPARE_AND_SWAP_N,
+                 "__sync_bool_compare_and_swap",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_BOOL_COMPARE_AND_SWAP_1,
+                 "__sync_bool_compare_and_swap_1",
+                 BT_FN_BOOL_VPTR_I1_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_BOOL_COMPARE_AND_SWAP_2,
+                 "__sync_bool_compare_and_swap_2",
+                 BT_FN_BOOL_VPTR_I2_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_BOOL_COMPARE_AND_SWAP_4,
+                 "__sync_bool_compare_and_swap_4",
+                 BT_FN_BOOL_VPTR_I4_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_BOOL_COMPARE_AND_SWAP_8,
+                 "__sync_bool_compare_and_swap_8",
+                 BT_FN_BOOL_VPTR_I8_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_VAL_COMPARE_AND_SWAP_N,
+                 "__sync_val_compare_and_swap",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_VAL_COMPARE_AND_SWAP_1,
+                 "__sync_val_compare_and_swap_1",
+                 BT_FN_I1_VPTR_I1_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_VAL_COMPARE_AND_SWAP_2,
+                 "__sync_val_compare_and_swap_2",
+                 BT_FN_I2_VPTR_I2_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_VAL_COMPARE_AND_SWAP_4,
+                 "__sync_val_compare_and_swap_4",
+                 BT_FN_I4_VPTR_I4_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_VAL_COMPARE_AND_SWAP_8,
+                 "__sync_val_compare_and_swap_8",
+                 BT_FN_I8_VPTR_I8_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_TEST_AND_SET_N, "__sync_lock_test_and_set",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_TEST_AND_SET_1, "__sync_lock_test_and_set_1",
+                 BT_FN_I1_VPTR_I1, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_TEST_AND_SET_2, "__sync_lock_test_and_set_2",
+                 BT_FN_I2_VPTR_I2, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_TEST_AND_SET_4, "__sync_lock_test_and_set_4",
+                 BT_FN_I4_VPTR_I4, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_TEST_AND_SET_8, "__sync_lock_test_and_set_8",
+                 BT_FN_I8_VPTR_I8, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_RELEASE_N, "__sync_lock_release",
+                 BT_FN_VOID_VAR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_RELEASE_1, "__sync_lock_release_1",
+                 BT_FN_VOID_VPTR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_RELEASE_2, "__sync_lock_release_2",
+                 BT_FN_VOID_VPTR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_RELEASE_4, "__sync_lock_release_4",
+                 BT_FN_VOID_VPTR, ATTR_NOTHROW_LIST)
+DEF_SYNC_BUILTIN (BUILT_IN_LOCK_RELEASE_8, "__sync_lock_release_8",
+                 BT_FN_VOID_VPTR, ATTR_NOTHROW_LIST)
+
+DEF_SYNC_BUILTIN (BUILT_IN_SYNCHRONIZE, "__sync_synchronize",
+                 BT_FN_VOID, ATTR_NOTHROW_LIST)
index 050a1eb979149187d9f3efa60649c1020833ee4d..da6be4e21a6a94e17a4a05201a05d57dea0c7af2 100644 (file)
@@ -3242,8 +3242,9 @@ c_common_nodes_and_builtins (void)
     {                                                                  \
       tree decl;                                                       \
                                                                        \
-      gcc_assert (!strncmp (NAME, "__builtin_",                                \
-                           strlen ("__builtin_")));                    \
+      gcc_assert ((!BOTH_P && !FALLBACK_P)                             \
+                 || !strncmp (NAME, "__builtin_",                      \
+                              strlen ("__builtin_")));                 \
                                                                        \
       if (!BOTH_P)                                                     \
        decl = lang_hooks.builtin_function (NAME, builtin_types[TYPE],  \
@@ -5836,4 +5837,170 @@ complete_array_type (tree *ptype, tree initial_value, bool do_default)
   return failure;
 }
 
+\f
+/* Used to help initialize the builtin-types.def table.  When a type of
+   the correct size doesn't exist, use error_mark_node instead of NULL.
+   The later results in segfaults even when a decl using the type doesn't
+   get invoked.  */
+
+tree
+builtin_type_for_size (int size, bool unsignedp)
+{
+  tree type = lang_hooks.types.type_for_size (size, unsignedp);
+  return type ? type : error_mark_node;
+}
+
+/* A helper function for resolve_overloaded_builtin in resolving the
+   overloaded __sync_ builtins.  Returns a positive power of 2 if the
+   first operand of PARAMS is a pointer to a supported data type.
+   Returns 0 if an error is encountered.  */
+
+static int
+sync_resolve_size (tree function, tree params)
+{
+  tree type;
+  int size;
+
+  if (params == NULL)
+    {
+      error ("too few arguments to function %qE", function);
+      return 0;
+    }
+
+  type = TREE_TYPE (TREE_VALUE (params));
+  if (TREE_CODE (type) != POINTER_TYPE)
+    goto incompatible;
+
+  type = TREE_TYPE (type);
+  if (!INTEGRAL_TYPE_P (type) && !POINTER_TYPE_P (type))
+    goto incompatible;
+
+  size = tree_low_cst (TYPE_SIZE_UNIT (type), 1);
+  if (size == 1 || size == 2 || size == 4 || size == 8)
+    return size;
+
+ incompatible:
+  error ("incompatible type for argument %d of %qE", 1, function);
+  return 0;
+}
+
+/* A helper function for resolve_overloaded_builtin.  Adds casts to 
+   PARAMS to make arguments match up with those of FUNCTION.  Drops
+   the variadic arguments at the end.  Returns false if some error
+   was encountered; true on success.  */
+
+static bool
+sync_resolve_params (tree orig_function, tree function, tree params)
+{
+  tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (function));
+  tree ptype;
+  int number;
+
+  /* We've declared the implementation functions to use "volatile void *"
+     as the pointer parameter, so we shouldn't get any complaints from the
+     call to check_function_arguments what ever type the user used.  */
+  arg_types = TREE_CHAIN (arg_types);
+  ptype = TREE_TYPE (TREE_TYPE (TREE_VALUE (params)));
+  number = 2;
+
+  /* For the rest of the values, we need to cast these to FTYPE, so that we
+     don't get warnings for passing pointer types, etc.  */
+  while (arg_types != void_list_node)
+    {
+      tree val;
+
+      params = TREE_CHAIN (params);
+      if (params == NULL)
+       {
+         error ("too few arguments to function %qE", orig_function);
+         return false;
+       }
+
+      /* ??? Ideally for the first conversion we'd use convert_for_assignment
+        so that we get warnings for anything that doesn't match the pointer
+        type.  This isn't portable across the C and C++ front ends atm.  */
+      val = TREE_VALUE (params);
+      val = convert (ptype, val);
+      val = convert (TREE_VALUE (arg_types), val);
+      TREE_VALUE (params) = val;
+
+      arg_types = TREE_CHAIN (arg_types);
+      number++;
+    }
+
+  /* The definition of these primitives is variadic, with the remaining
+     being "an optional list of variables protected by the memory barrier".
+     No clue what that's supposed to mean, precisely, but we consider all
+     call-clobbered variables to be protected so we're safe.  */
+  TREE_CHAIN (params) = NULL;
+
+  return true;
+}
+
+/* A helper function for resolve_overloaded_builtin.  Adds a cast to 
+   RESULT to make it match the type of the first pointer argument in
+   PARAMS.  */
+
+static tree
+sync_resolve_return (tree params, tree result)
+{
+  tree ptype = TREE_TYPE (TREE_TYPE (TREE_VALUE (params)));
+  return convert (ptype, result);
+}
+
+/* Some builtin functions are placeholders for other expressions.  This
+   function should be called immediately after parsing the call expression
+   before surrounding code has committed to the type of the expression.
+
+   FUNCTION is the DECL that has been invoked; it is known to be a builtin.
+   PARAMS is the argument list for the call.  The return value is non-null
+   when expansion is complete, and null if normal processing should
+   continue.  */
+
+tree
+resolve_overloaded_builtin (tree function, tree params)
+{
+  enum built_in_function orig_code = DECL_FUNCTION_CODE (function);
+  switch (orig_code)
+    {
+    case BUILT_IN_FETCH_AND_ADD_N:
+    case BUILT_IN_FETCH_AND_SUB_N:
+    case BUILT_IN_FETCH_AND_OR_N:
+    case BUILT_IN_FETCH_AND_AND_N:
+    case BUILT_IN_FETCH_AND_XOR_N:
+    case BUILT_IN_FETCH_AND_NAND_N:
+    case BUILT_IN_ADD_AND_FETCH_N:
+    case BUILT_IN_SUB_AND_FETCH_N:
+    case BUILT_IN_OR_AND_FETCH_N:
+    case BUILT_IN_AND_AND_FETCH_N:
+    case BUILT_IN_XOR_AND_FETCH_N:
+    case BUILT_IN_NAND_AND_FETCH_N:
+    case BUILT_IN_BOOL_COMPARE_AND_SWAP_N:
+    case BUILT_IN_VAL_COMPARE_AND_SWAP_N:
+    case BUILT_IN_LOCK_TEST_AND_SET_N:
+    case BUILT_IN_LOCK_RELEASE_N:
+      {
+       int n = sync_resolve_size (function, params);
+       tree new_function, result;
+
+       if (n == 0)
+         return error_mark_node;
+
+       new_function = built_in_decls[orig_code + exact_log2 (n) + 1];
+       if (!sync_resolve_params (function, new_function, params))
+         return error_mark_node;
+
+       result = build_function_call (new_function, params);
+       if (orig_code != BUILT_IN_BOOL_COMPARE_AND_SWAP_N
+           && orig_code != BUILT_IN_LOCK_RELEASE_N)
+         result = sync_resolve_return (params, result);
+
+       return result;
+      }
+
+    default:
+      return NULL;
+    }
+}
+
 #include "gt-c-common.h"
index d2b9a4a9cbdb6984f2f9be6b283763ce5f8d74f6..cf6e88b9fa65318424bcd79e4252a2c560d3d518 100644 (file)
@@ -798,6 +798,8 @@ extern void c_do_switch_warnings (splay_tree, location_t, tree, tree);
 
 extern tree build_function_call (tree, tree);
 
+extern tree resolve_overloaded_builtin (tree, tree);
+
 extern tree finish_label_address_expr (tree);
 
 /* Same function prototype, but the C and C++ front ends have
@@ -860,6 +862,8 @@ extern void lvalue_error (enum lvalue_use);
 
 extern int complete_array_type (tree *, tree, bool);
 
+extern tree builtin_type_for_size (int, bool);
+
 /* In c-gimplify.c  */
 extern void c_genericize (tree);
 extern int c_gimplify_expr (tree *, tree *, tree *);
index 8c3996613e30b3990c0a2deb1ad75b832719b891..9c82bbe601954f9360f63fed4ddad3c8e20869c8 100644 (file)
@@ -1978,6 +1978,13 @@ build_function_call (tree function, tree params)
   /* Convert anything with function type to a pointer-to-function.  */
   if (TREE_CODE (function) == FUNCTION_DECL)
     {
+      if (DECL_BUILT_IN_CLASS (function) == BUILT_IN_NORMAL)
+       {
+         tem = resolve_overloaded_builtin (function, params);
+         if (tem)
+           return tem;
+       }
+
       name = DECL_NAME (function);
 
       /* Differs from default_conversion by not setting TREE_ADDRESSABLE
index 23b046d1bd1f562ca5a6f1e13f149cc5379d8e62..5fc85a45abbb11a28ee51b34082ebabc428de1d3 100644 (file)
@@ -70,6 +70,7 @@ extensions, accepted by GCC in C89 mode and in C++.
 * Return Address::      Getting the return or frame address of a function.
 * Vector Extensions::   Using vector instructions through built-in functions.
 * Offsetof::            Special syntax for implementing @code{offsetof}.
+* Atomic Builtins::    Built-in functions for atomic memory access.
 * Other Builtins::      Other built-in functions.
 * Target Builtins::     Built-in functions specific to particular targets.
 * Target Format Checks:: Format checks specific to particular targets.
@@ -4581,6 +4582,133 @@ is a suitable definition of the @code{offsetof} macro.  In C++, @var{type}
 may be dependent.  In either case, @var{member} may consist of a single
 identifier, or a sequence of member accesses and array references.
 
+@node Atomic Builtins
+@section Built-in functions for atomic memory access
+
+The following builtins are intended to be compatible with those described
+in the @cite{Intel Itanium Processor-specific Application Binary Interface},
+section 7.4.  As such, they depart from the normal GCC practice of using
+the ``__builtin_'' prefix, and further that they are overloaded such that
+they work on multiple types.
+
+The definition given in the Intel documentation allows only for the use of
+the types @code{int}, @code{long}, @code{long long} as well as their unsigned
+counterparts.  GCC will allow any integral scalar or pointer type that is
+1, 2, 4 or 8 bytes in length.
+
+Not all operations are supported by all target processors.  If a particular
+operation cannot be implemented on the target processor, a warning will be
+generated and a call an external function will be generated.  The external
+function will carry the same name as the builtin, with an additional suffix
+@samp{_@var{n}} where @var{n} is the size of the data type.
+
+@c ??? Should we have a mechanism to suppress this warning?  This is almost
+@c useful for implementing the operation under the control of an external
+@c mutex.
+
+In most cases, these builtins are considered a @dfn{full barrier}.  That is,
+no memory operand will be moved across the operation, either forward or
+backward.  Further, instructions will be issued as necessary to prevent the
+processor from speculating loads across the operation and from queuing stores
+after the operation.
+
+All of the routines are are described in the Intel documentation to take
+``an optional list of variables protected by the memory barrier''.  It's
+not clear what is meant by that; it could mean that @emph{only} the
+following variables are protected, or it could mean that these variables
+should in addition be protected.  At present GCC ignores this list and
+protects all variables which are globally accessible.  If in the future
+we make some use of this list, an empty list will continue to mean all
+globally accessible variables.
+
+@table @code
+@item @var{type} __sync_fetch_and_add (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_fetch_and_sub (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_fetch_and_or (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_fetch_and_and (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_fetch_and_xor (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_fetch_and_nand (@var{type} *ptr, @var{type} value, ...)
+@findex __sync_fetch_and_add
+@findex __sync_fetch_and_sub
+@findex __sync_fetch_and_or
+@findex __sync_fetch_and_and
+@findex __sync_fetch_and_xor
+@findex __sync_fetch_and_nand
+These builtins perform the operation suggested by the name, and
+returns the value that had previously been in memory.  That is,
+
+@smallexample
+@{ tmp = *ptr; *ptr @var{op}= value; return tmp; @}
+@end smallexample
+
+The builtin @code{__sync_fetch_and_nand} could be implemented by
+@code{__sync_fetch_and_and(ptr, ~value)}.
+
+@item @var{type} __sync_add_and_fetch (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_sub_and_fetch (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_or_and_fetch (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_and_and_fetch (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_xor_and_fetch (@var{type} *ptr, @var{type} value, ...)
+@itemx @var{type} __sync_nand_and_fetch (@var{type} *ptr, @var{type} value, ...)
+@findex __sync_add_and_fetch
+@findex __sync_sub_and_fetch
+@findex __sync_or_and_fetch
+@findex __sync_and_and_fetch
+@findex __sync_xor_and_fetch
+@findex __sync_nand_and_fetch
+These builtins perform the operation suggested by the name, and
+return the new value.  That is,
+
+@smallexample
+@{ *ptr @var{op}= value; return *ptr; @}
+@end smallexample
+
+@item bool __sync_bool_compare_and_swap (@var{type} *ptr, @var{type} oldval @var{type} newval, ...)
+@itemx @var{type} __sync_val_compare_and_swap (@var{type} *ptr, @var{type} oldval @var{type} newval, ...)
+@findex __sync_bool_compare_and_swap
+@findex __sync_val_compare_and_swap
+These builtins perform an atomic compare and swap.  That is, if the current
+value of @code{*@var{ptr}} is @var{oldval}, then write @var{newval} into
+@code{*@var{ptr}}.
+
+The ``bool'' version returns true if the comparison is successful and 
+@var{newval} was written.  The ``val'' version returns the contents
+of @code{*@var{ptr}} after the operation.
+
+@item __sync_synchronize (...)
+@findex __sync_synchronize
+This builtin issues a full memory barrier.
+
+@item @var{type} __sync_lock_test_and_set (@var{type} *ptr, @var{type} value, ...)
+@findex __sync_lock_test_and_set
+This builtin, as described by Intel, is not a traditional test-and-set
+operation, but rather an atomic exchange operation.  It writes @var{value}
+into @code{*@var{ptr}}, and returns the previous contents of
+@code{*@var{ptr}}.
+
+Many targets have only minimal support for such locks, and do not support
+a full exchange operation.  In this case, a target may support reduced
+functionality here by which the @emph{only} valid value to store is the
+immediate constant 1.  The exact value actually stored in @code{*@var{ptr}}
+is implementation defined.
+
+This builtin is not a full barrier, but rather an @dfn{acquire barrier}.
+This means that references after the builtin cannot move to (or be
+speculated to) before the builtin, but previous memory stores may not
+be globally visible yet, and previous memory loads may not yet be 
+satisfied.
+
+@item void __sync_lock_release (@var{type} *ptr, ...)
+@findex __sync_lock_release
+This builtin releases the lock acquired by @code{__sync_lock_test_and_set}.
+Normally this means writing the constant 0 to @code{*@var{ptr}}.
+
+This builtin is not a full barrier, but rather a @dfn{release barrier}.
+This means that all previous memory stores are globally visible, and all
+previous memory loads have been satisfied, but following memory reads
+are not prevented from being speculated to before the barrier.
+@end table
+
 @node Other Builtins
 @section Other built-in functions provided by GCC
 @cindex built-in functions
index 460023091f4e71f270e95cf91e594d7a836eb26b..4deacfb67d7c5b3f978e55065f283cc2c8f700d8 100644 (file)
@@ -3936,6 +3936,140 @@ respectively, a low or moderate degree of temporal locality.
 Targets that do not support write prefetches or locality hints can ignore
 the values of operands 1 and 2.
 
+@cindex @code{memory_barrier} instruction pattern
+@item @samp{memory_barrier}
+
+If the target memory model is not fully synchronous, then this pattern
+should be defined to an instruction that orders both loads and stores
+before the instruction with respect to loads and stores after the instruction.
+This pattern has no operands.
+
+@cindex @code{sync_compare_and_swap@var{mode}} instruction pattern
+@item @samp{sync_compare_and_swap@var{mode}}
+
+This pattern, if defined, emits code for an atomic compare-and-swap
+operation.  Operand 1 is the memory on which the atomic operation is
+performed.  Operand 2 is the ``old'' value to be compared against the
+current contents of the memory location.  Operand 3 is the ``new'' value
+to store in the memory if the compare succeeds.  Operand 0 is the result
+of the operation; it should contain the current contents of the memory
+after the operation.  If the compare succeeds, this should obviously be
+a copy of operand 3.
+
+This pattern must show that both operand 0 and operand 1 are modified.
+
+This pattern must issue any memory barrier instructions such that the
+pattern as a whole acts as a full barrier.
+
+@cindex @code{sync_compare_and_swap_cc@var{mode}} instruction pattern
+@item @samp{sync_compare_and_swap_cc@var{mode}}
+
+This pattern is just like @code{sync_compare_and_swap@var{mode}}, except
+it should act as if compare part of the compare-and-swap were issued via
+@code{cmp@var{m}}.  This comparison will only be used with @code{EQ} and
+@code{NE} branches and @code{setcc} operations.
+
+Some targets do expose the success or failure of the compare-and-swap
+operation via the status flags.  Ideally we wouldn't need a separate
+named pattern in order to take advantage of this, but the combine pass
+does not handle patterns with multiple sets, which is required by
+definition for @code{sync_compare_and_swap@var{mode}}.
+
+@cindex @code{sync_add@var{mode}} instruction pattern
+@cindex @code{sync_sub@var{mode}} instruction pattern
+@cindex @code{sync_ior@var{mode}} instruction pattern
+@cindex @code{sync_and@var{mode}} instruction pattern
+@cindex @code{sync_xor@var{mode}} instruction pattern
+@cindex @code{sync_nand@var{mode}} instruction pattern
+@item @samp{sync_add@var{mode}}, @samp{sync_sub@var{mode}}
+@itemx @samp{sync_ior@var{mode}}, @samp{sync_and@var{mode}}
+@itemx @samp{sync_xor@var{mode}}, @samp{sync_nand@var{mode}}
+
+These patterns emit code for an atomic operation on memory.
+Operand 0 is the memory on which the atomic operation is performed.
+Operand 1 is the second operand to the binary operator.
+
+The ``nand'' operation is @code{op0 & ~op1}.
+
+This pattern must issue any memory barrier instructions such that the
+pattern as a whole acts as a full barrier.
+
+If these patterns are not defined, the operation will be constructed
+from a compare-and-swap operation, if defined.
+
+@cindex @code{sync_old_add@var{mode}} instruction pattern
+@cindex @code{sync_old_sub@var{mode}} instruction pattern
+@cindex @code{sync_old_ior@var{mode}} instruction pattern
+@cindex @code{sync_old_and@var{mode}} instruction pattern
+@cindex @code{sync_old_xor@var{mode}} instruction pattern
+@cindex @code{sync_old_nand@var{mode}} instruction pattern
+@item @samp{sync_old_add@var{mode}}, @samp{sync_old_sub@var{mode}}
+@itemx @samp{sync_old_ior@var{mode}}, @samp{sync_old_and@var{mode}}
+@itemx @samp{sync_old_xor@var{mode}}, @samp{sync_old_nand@var{mode}}
+
+These patterns are emit code for an atomic operation on memory,
+and return the value that the memory contained before the operation.
+Operand 0 is the result value, operand 1 is the memory on which the
+atomic operation is performed, and operand 2 is the second operand
+to the binary operator.
+
+This pattern must issue any memory barrier instructions such that the
+pattern as a whole acts as a full barrier.
+
+If these patterns are not defined, the operation will be constructed
+from a compare-and-swap operation, if defined.
+
+@cindex @code{sync_new_add@var{mode}} instruction pattern
+@cindex @code{sync_new_sub@var{mode}} instruction pattern
+@cindex @code{sync_new_ior@var{mode}} instruction pattern
+@cindex @code{sync_new_and@var{mode}} instruction pattern
+@cindex @code{sync_new_xor@var{mode}} instruction pattern
+@cindex @code{sync_new_nand@var{mode}} instruction pattern
+@item @samp{sync_new_add@var{mode}}, @samp{sync_new_sub@var{mode}}
+@itemx @samp{sync_new_ior@var{mode}}, @samp{sync_new_and@var{mode}}
+@itemx @samp{sync_new_xor@var{mode}}, @samp{sync_new_nand@var{mode}}
+
+These patterns are like their @code{sync_old_@var{op}} counterparts,
+except that they return the value that exists in the memory location
+after the operation, rather than before the operation.
+
+@cindex @code{sync_lock_test_and_set@var{mode}} instruction pattern
+@item @samp{sync_lock_test_and_set@var{mode}}
+
+This pattern takes two forms, based on the capabilities of the target.
+In either case, operand 0 is the result of the operand, operand 1 is
+the memory on which the atomic operation is performed, and operand 2
+is the value to set in the lock.
+
+In the ideal case, this operation is an atomic exchange operation, in
+which the previous value in memory operand is copied into the result
+operand, and the value operand is stored in the memory operand.
+
+For less capable targets, any value operand that is not the constant 1
+should be rejected with @code{FAIL}.  In this case the target may use
+an atomic test-and-set bit operation.  The result operand should contain
+1 if the bit was previously set and 0 if the bit was previously clear.
+The true contents of the memory operand are implementation defined.
+
+This pattern must issue any memory barrier instructions such that the
+pattern as a whole acts as an acquire barrier.
+
+If this pattern is not defined, the operation will be constructed from
+a compare-and-swap operation, if defined.
+
+@cindex @code{sync_lock_release@var{mode}} instruction pattern
+@item @samp{sync_lock_release@var{mode}}
+
+This pattern, if defined, releases a lock set by
+@code{sync_lock_test_and_set@var{mode}}.  Operand 0 is the memory
+that contains the lock.
+
+This pattern must issue any memory barrier instructions such that the
+pattern as a whole acts as a release barrier.
+
+If this pattern is not defined, then a @code{memory_barrier} pattern
+will be emitted, followed by a store of zero to the memory operand.
+
 @end table
 
 @end ifset
index c2a6e7db9a79db47726a1c0da0d99a469cd28a9a..b334453efd597677284aedc53389a2ace9483b88 100644 (file)
@@ -208,6 +208,30 @@ enum insn_code clrmem_optab[NUM_MACHINE_MODES];
 enum insn_code cmpstr_optab[NUM_MACHINE_MODES];
 enum insn_code cmpmem_optab[NUM_MACHINE_MODES];
 
+/* Synchronization primitives.  */
+enum insn_code sync_add_optab[NUM_MACHINE_MODES];
+enum insn_code sync_sub_optab[NUM_MACHINE_MODES];
+enum insn_code sync_ior_optab[NUM_MACHINE_MODES];
+enum insn_code sync_and_optab[NUM_MACHINE_MODES];
+enum insn_code sync_xor_optab[NUM_MACHINE_MODES];
+enum insn_code sync_nand_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_add_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_sub_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_ior_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_and_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_xor_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_nand_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_add_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_sub_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_ior_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_and_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_xor_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_nand_optab[NUM_MACHINE_MODES];
+enum insn_code sync_compare_and_swap[NUM_MACHINE_MODES];
+enum insn_code sync_compare_and_swap_cc[NUM_MACHINE_MODES];
+enum insn_code sync_lock_test_and_set[NUM_MACHINE_MODES];
+enum insn_code sync_lock_release[NUM_MACHINE_MODES];
+
 /* SLOW_UNALIGNED_ACCESS is nonzero if unaligned accesses are very slow.  */
 
 #ifndef SLOW_UNALIGNED_ACCESS
index b624413e080fb7af5cf7b10f11cf0bea4e127d51..f462dc84b47216c623d722f35a3b4e7e81dc188e 100644 (file)
@@ -310,6 +310,11 @@ int can_conditionally_move_p (enum machine_mode mode);
 rtx emit_conditional_add (rtx, enum rtx_code, rtx, rtx, enum machine_mode,
                          rtx, rtx, enum machine_mode, int);
 
+rtx expand_val_compare_and_swap (rtx, rtx, rtx, rtx);
+rtx expand_bool_compare_and_swap (rtx, rtx, rtx, rtx);
+rtx expand_sync_operation (rtx, rtx, enum rtx_code);
+rtx expand_sync_fetch_operation (rtx, rtx, enum rtx_code, bool, rtx);
+rtx expand_sync_lock_test_and_set (rtx, rtx, rtx);
 \f
 /* Functions from expmed.c:  */
 
index da80f782789a47756778edeeaf200f88b7d9bdbd..d58f8811812365516750531ff33f42c8bbf85eed 100644 (file)
@@ -171,12 +171,35 @@ static const char * const optabs[] =
   "clrmem_optab[$A] = CODE_FOR_$(clrmem$a$)",
   "cmpstr_optab[$A] = CODE_FOR_$(cmpstr$a$)",
   "cmpmem_optab[$A] = CODE_FOR_$(cmpmem$a$)",
+  "sync_add_optab[$A] = CODE_FOR_$(sync_add$I$a$)",
+  "sync_sub_optab[$A] = CODE_FOR_$(sync_sub$I$a$)",
+  "sync_ior_optab[$A] = CODE_FOR_$(sync_ior$I$a$)",
+  "sync_and_optab[$A] = CODE_FOR_$(sync_and$I$a$)",
+  "sync_xor_optab[$A] = CODE_FOR_$(sync_xor$I$a$)",
+  "sync_nand_optab[$A] = CODE_FOR_$(sync_nand$I$a$)",
+  "sync_old_add_optab[$A] = CODE_FOR_$(sync_old_add$I$a$)",
+  "sync_old_sub_optab[$A] = CODE_FOR_$(sync_old_sub$I$a$)",
+  "sync_old_ior_optab[$A] = CODE_FOR_$(sync_old_ior$I$a$)",
+  "sync_old_and_optab[$A] = CODE_FOR_$(sync_old_and$I$a$)",
+  "sync_old_xor_optab[$A] = CODE_FOR_$(sync_old_xor$I$a$)",
+  "sync_old_nand_optab[$A] = CODE_FOR_$(sync_old_nand$I$a$)",
+  "sync_new_add_optab[$A] = CODE_FOR_$(sync_new_add$I$a$)",
+  "sync_new_sub_optab[$A] = CODE_FOR_$(sync_new_sub$I$a$)",
+  "sync_new_ior_optab[$A] = CODE_FOR_$(sync_new_ior$I$a$)",
+  "sync_new_and_optab[$A] = CODE_FOR_$(sync_new_and$I$a$)",
+  "sync_new_xor_optab[$A] = CODE_FOR_$(sync_new_xor$I$a$)",
+  "sync_new_nand_optab[$A] = CODE_FOR_$(sync_new_nand$I$a$)",
+  "sync_compare_and_swap[$A] = CODE_FOR_$(sync_compare_and_swap$I$a$)",
+  "sync_compare_and_swap_cc[$A] = CODE_FOR_$(sync_compare_and_swap_cc$I$a$)",
+  "sync_lock_test_and_set[$A] = CODE_FOR_$(sync_lock_test_and_set$I$a$)",
+  "sync_lock_release[$A] = CODE_FOR_$(sync_lock_release$I$a$)",
   "vec_set_optab->handlers[$A].insn_code = CODE_FOR_$(vec_set$a$)",
   "vec_extract_optab->handlers[$A].insn_code = CODE_FOR_$(vec_extract$a$)",
   "vec_init_optab->handlers[$A].insn_code = CODE_FOR_$(vec_init$a$)",
   "vec_realign_load_optab->handlers[$A].insn_code = CODE_FOR_$(vec_realign_load_$a$)",
   "vcond_gen_code[$A] = CODE_FOR_$(vcond$a$)",
-  "vcondu_gen_code[$A] = CODE_FOR_$(vcondu$a$)" };
+  "vcondu_gen_code[$A] = CODE_FOR_$(vcondu$a$)"
+};
 
 static void gen_insn (rtx);
 
index 6835a9a76657eeadffe2e24c9c43fd1b23ec23c4..47eec4bbcf3b9460b2d988aefe0d5b86990ac749 100644 (file)
@@ -5093,6 +5093,29 @@ init_optabs (void)
       cmpstr_optab[i] = CODE_FOR_nothing;
       cmpmem_optab[i] = CODE_FOR_nothing;
 
+      sync_add_optab[i] = CODE_FOR_nothing;
+      sync_sub_optab[i] = CODE_FOR_nothing;
+      sync_ior_optab[i] = CODE_FOR_nothing;
+      sync_and_optab[i] = CODE_FOR_nothing;
+      sync_xor_optab[i] = CODE_FOR_nothing;
+      sync_nand_optab[i] = CODE_FOR_nothing;
+      sync_old_add_optab[i] = CODE_FOR_nothing;
+      sync_old_sub_optab[i] = CODE_FOR_nothing;
+      sync_old_ior_optab[i] = CODE_FOR_nothing;
+      sync_old_and_optab[i] = CODE_FOR_nothing;
+      sync_old_xor_optab[i] = CODE_FOR_nothing;
+      sync_old_nand_optab[i] = CODE_FOR_nothing;
+      sync_new_add_optab[i] = CODE_FOR_nothing;
+      sync_new_sub_optab[i] = CODE_FOR_nothing;
+      sync_new_ior_optab[i] = CODE_FOR_nothing;
+      sync_new_and_optab[i] = CODE_FOR_nothing;
+      sync_new_xor_optab[i] = CODE_FOR_nothing;
+      sync_new_nand_optab[i] = CODE_FOR_nothing;
+      sync_compare_and_swap[i] = CODE_FOR_nothing;
+      sync_compare_and_swap_cc[i] = CODE_FOR_nothing;
+      sync_lock_test_and_set[i] = CODE_FOR_nothing;
+      sync_lock_release[i] = CODE_FOR_nothing;
+
 #ifdef HAVE_SECONDARY_RELOADS
       reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
 #endif
@@ -5486,4 +5509,517 @@ expand_vec_cond_expr (tree vec_cond_expr, rtx target)
 
   return target;
 }
+
+\f
+/* This is an internal subroutine of the other compare_and_swap expanders.
+   MEM, OLD_VAL and NEW_VAL are as you'd expect for a compare-and-swap
+   operation.  TARGET is an optional place to store the value result of
+   the operation.  ICODE is the particular instruction to expand.  Return
+   the result of the operation.  */
+
+static rtx
+expand_val_compare_and_swap_1 (rtx mem, rtx old_val, rtx new_val,
+                              rtx target, enum insn_code icode)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  rtx insn;
+
+  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
+    target = gen_reg_rtx (mode);
+
+  if (GET_MODE (old_val) != VOIDmode && GET_MODE (old_val) != mode)
+    old_val = convert_modes (mode, GET_MODE (old_val), old_val, 1);
+  if (!insn_data[icode].operand[2].predicate (old_val, mode))
+    old_val = force_reg (mode, old_val);
+
+  if (GET_MODE (new_val) != VOIDmode && GET_MODE (new_val) != mode)
+    new_val = convert_modes (mode, GET_MODE (new_val), new_val, 1);
+  if (!insn_data[icode].operand[3].predicate (new_val, mode))
+    new_val = force_reg (mode, new_val);
+
+  insn = GEN_FCN (icode) (target, mem, old_val, new_val);
+  if (insn == NULL_RTX)
+    return NULL_RTX;
+  emit_insn (insn);
+
+  return target;
+}
+
+/* Expand a compare-and-swap operation and return its value.  */
+
+rtx
+expand_val_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  enum insn_code icode = sync_compare_and_swap[mode];
+
+  if (icode == CODE_FOR_nothing)
+    return NULL_RTX;
+
+  return expand_val_compare_and_swap_1 (mem, old_val, new_val, target, icode);
+}
+
+/* Expand a compare-and-swap operation and store true into the result if
+   the operation was successful and false otherwise.  Return the result.
+   Unlike other routines, TARGET is not optional.  */
+
+rtx
+expand_bool_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  enum insn_code icode;
+  rtx subtarget, label0, label1;
+
+  /* If the target supports a compare-and-swap pattern that simultaneously
+     sets some flag for success, then use it.  Otherwise use the regular
+     compare-and-swap and follow that immediately with a compare insn.  */
+  icode = sync_compare_and_swap_cc[mode];
+  switch (icode)
+    {
+    default:
+      subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val,
+                                                NULL_RTX, icode);
+      if (subtarget != NULL_RTX)
+       break;
+
+      /* FALLTHRU */
+    case CODE_FOR_nothing:
+      icode = sync_compare_and_swap[mode];
+      if (icode == CODE_FOR_nothing)
+       return NULL_RTX;
+
+      subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val,
+                                                NULL_RTX, icode);
+      if (subtarget == NULL_RTX)
+       return NULL_RTX;
+
+      emit_cmp_insn (subtarget, new_val, EQ, const0_rtx, mode, true);
+    }
+
+  /* If the target has a sane STORE_FLAG_VALUE, then go ahead and use a
+     setcc instruction from the beginning.  We don't work too hard here,
+     but it's nice to not be stupid about initial code gen either.  */
+  if (STORE_FLAG_VALUE == 1)
+    {
+      icode = setcc_gen_code[EQ];
+      if (icode != CODE_FOR_nothing)
+       {
+         enum machine_mode cmode = insn_data[icode].operand[0].mode;
+         rtx insn;
+
+         subtarget = target;
+         if (!insn_data[icode].operand[0].predicate (target, cmode))
+           subtarget = gen_reg_rtx (cmode);
+
+         insn = GEN_FCN (icode) (subtarget);
+         if (insn)
+           {
+             emit_insn (insn);
+             if (GET_MODE (target) != GET_MODE (subtarget))
+               {
+                 convert_move (target, subtarget, 1);
+                 subtarget = target;
+               }
+             return subtarget;
+           }
+       }
+    }
+
+  /* Without an appropriate setcc instruction, use a set of branches to 
+     get 1 and 0 stored into target.  Presumably if the target has a 
+     STORE_FLAG_VALUE that isn't 1, then this will get cleaned up by ifcvt.  */
+
+  label0 = gen_label_rtx ();
+  label1 = gen_label_rtx ();
+
+  emit_jump_insn (bcc_gen_fctn[EQ] (label0));
+  emit_move_insn (target, const0_rtx);
+  emit_jump_insn (gen_jump (label1));
+  emit_label (label0);
+  emit_move_insn (target, const1_rtx);
+  emit_label (label1);
+
+  return target;
+}
+
+/* This is a helper function for the other atomic operations.  This function
+   emits a loop that contains SEQ that iterates until a compare-and-swap
+   operation at the end succeeds.  MEM is the memory to be modified.  SEQ is
+   a set of instructions that takes a value from OLD_REG as an input and
+   produces a value in NEW_REG as an output.  Before SEQ, OLD_REG will be
+   set to the current contents of MEM.  After SEQ, a compare-and-swap will
+   attempt to update MEM with NEW_REG.  The function returns true when the
+   loop was generated successfully.  */
+
+static bool
+expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  enum insn_code icode;
+  rtx label, subtarget;
+
+  /* The loop we want to generate looks like
+
+       old_reg = mem;
+      label:
+       seq;
+       old_reg = compare-and-swap(mem, old_reg, new_reg)
+       if (old_reg != new_reg)
+         goto label;
+
+     Note that we only do the plain load from memory once.  Subsequent
+     iterations use the value loaded by the compare-and-swap pattern.  */
+
+  label = gen_label_rtx ();
+
+  emit_move_insn (old_reg, mem);
+  emit_label (label);
+  if (seq)
+    emit_insn (seq);
+
+  /* If the target supports a compare-and-swap pattern that simultaneously
+     sets some flag for success, then use it.  Otherwise use the regular
+     compare-and-swap and follow that immediately with a compare insn.  */
+  icode = sync_compare_and_swap_cc[mode];
+  switch (icode)
+    {
+    default:
+      subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg,
+                                                old_reg, icode);
+      if (subtarget != NULL_RTX)
+       break;
+
+      /* FALLTHRU */
+    case CODE_FOR_nothing:
+      icode = sync_compare_and_swap[mode];
+      if (icode == CODE_FOR_nothing)
+       return false;
+
+      subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg,
+                                                old_reg, icode);
+      if (subtarget == NULL_RTX)
+       return false;
+
+      emit_cmp_insn (subtarget, new_reg, EQ, const0_rtx, mode, true);
+    }
+
+  /* ??? Mark this jump predicted not taken?  */
+  emit_jump_insn (bcc_gen_fctn[NE] (label));
+
+  return true;
+}
+
+/* This function generates the atomic operation MEM CODE= VAL.  In this
+   case, we do not care about any resulting value.  Returns NULL if we 
+   cannot generate the operation.  */
+
+rtx
+expand_sync_operation (rtx mem, rtx val, enum rtx_code code)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  enum insn_code icode;
+  rtx insn;
+
+  /* Look to see if the target supports the operation directly.  */
+  switch (code)
+    {
+    case PLUS:
+      icode = sync_add_optab[mode];
+      break;
+    case IOR:
+      icode = sync_ior_optab[mode];
+      break;
+    case XOR:
+      icode = sync_xor_optab[mode];
+      break;
+    case AND:
+      icode = sync_and_optab[mode];
+      break;
+
+    case MINUS:
+      icode = sync_sub_optab[mode];
+      if (icode == CODE_FOR_nothing)
+       {
+         icode = sync_add_optab[mode];
+         if (icode != CODE_FOR_nothing)
+           {
+             val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1);
+             code = PLUS;
+           }
+       }
+      break;
+
+    case NOT:
+      icode = sync_nand_optab[mode];
+      if (icode != CODE_FOR_nothing)
+       {
+         icode = sync_and_optab[mode];
+         if (icode != CODE_FOR_nothing)
+           {
+             val = expand_simple_unop (mode, NOT, val, NULL_RTX, 1);
+             code = AND;
+           }
+       }
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* Generate the direct operation, if present.  */
+  if (icode != CODE_FOR_nothing)
+    {
+      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+       val = convert_modes (mode, GET_MODE (val), val, 1);
+      if (!insn_data[icode].operand[1].predicate (val, mode))
+       val = force_reg (mode, val);
+      
+      insn = GEN_FCN (icode) (mem, val);
+      if (insn)
+       {
+         emit_insn (insn);
+         return const0_rtx;
+       }
+    }
+
+  /* Failing that, generate a compare-and-swap loop in which we perform the
+     operation with normal arithmetic instructions.  */
+  if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+    {
+      rtx t0 = gen_reg_rtx (mode), t1;
+
+      start_sequence ();
+
+      if (code == NOT)
+       {
+         val = expand_simple_unop (mode, NOT, val, NULL_RTX, true);
+         code = AND;
+       }
+      t1 = expand_simple_binop (mode, code, t0, val, NULL_RTX,
+                               true, OPTAB_LIB_WIDEN);
+
+      insn = get_insns ();
+      end_sequence ();
+
+      if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn))
+       return const0_rtx;
+    }
+
+  return NULL_RTX;
+}
+
+/* This function generates the atomic operation MEM CODE= VAL.  In this
+   case, we do care about the resulting value: if AFTER is true then
+   return the value MEM holds after the operation, if AFTER is false 
+   then return the value MEM holds before the operation.  TARGET is an
+   optional place for the result value to be stored.  */
+
+rtx
+expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code,
+                            bool after, rtx target)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  enum insn_code old_code, new_code, icode;
+  bool compensate;
+  rtx insn;
+
+  /* Look to see if the target supports the operation directly.  */
+  switch (code)
+    {
+    case PLUS:
+      old_code = sync_old_add_optab[mode];
+      new_code = sync_new_add_optab[mode];
+      break;
+    case IOR:
+      old_code = sync_old_ior_optab[mode];
+      new_code = sync_new_ior_optab[mode];
+      break;
+    case XOR:
+      old_code = sync_old_xor_optab[mode];
+      new_code = sync_new_xor_optab[mode];
+      break;
+    case AND:
+      old_code = sync_old_and_optab[mode];
+      new_code = sync_new_and_optab[mode];
+      break;
+
+    case MINUS:
+      old_code = sync_old_sub_optab[mode];
+      new_code = sync_new_sub_optab[mode];
+      if (old_code == CODE_FOR_nothing && new_code == CODE_FOR_nothing)
+       {
+         old_code = sync_old_add_optab[mode];
+         new_code = sync_new_add_optab[mode];
+         if (old_code != CODE_FOR_nothing || new_code != CODE_FOR_nothing)
+           {
+             val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1);
+             code = PLUS;
+           }
+       }
+      break;
+
+    case NOT:
+      old_code = sync_old_nand_optab[mode];
+      new_code = sync_new_nand_optab[mode];
+      if (old_code == CODE_FOR_nothing && new_code == CODE_FOR_nothing)
+       {
+         old_code = sync_old_sub_optab[mode];
+         new_code = sync_new_sub_optab[mode];
+         if (old_code != CODE_FOR_nothing || new_code != CODE_FOR_nothing)
+           {
+             val = expand_simple_unop (mode, NOT, val, NULL_RTX, 1);
+             code = AND;
+           }
+       }
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* If the target does supports the proper new/old operation, great.  But
+     if we only support the opposite old/new operation, check to see if we
+     can compensate.  In the case in which the old value is supported, then
+     we can always perform the operation again with normal arithmetic.  In
+     the case in which the new value is supported, then we can only handle
+     this in the case the operation is reversible.  */
+  compensate = false;
+  if (after)
+    {
+      icode = new_code;
+      if (icode == CODE_FOR_nothing)
+       {
+         icode = old_code;
+         if (icode != CODE_FOR_nothing)
+           compensate = true;
+       }
+    }
+  else
+    {
+      icode = old_code;
+      if (icode == CODE_FOR_nothing
+         && (code == PLUS || code == MINUS || code == XOR))
+       {
+         icode = new_code;
+         if (icode != CODE_FOR_nothing)
+           compensate = true;
+       }
+    }
+
+  /* If we found something supported, great.  */
+  if (icode != CODE_FOR_nothing)
+    {
+      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
+       target = gen_reg_rtx (mode);
+
+      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+       val = convert_modes (mode, GET_MODE (val), val, 1);
+      if (!insn_data[icode].operand[2].predicate (val, mode))
+       val = force_reg (mode, val);
+      
+      insn = GEN_FCN (icode) (target, mem, val);
+      if (insn)
+       {
+         emit_insn (insn);
+
+         /* If we need to compensate for using an operation with the
+            wrong return value, do so now.  */
+         if (compensate)
+           {
+             if (!after)
+               {
+                 if (code == PLUS)
+                   code = MINUS;
+                 else if (code == MINUS)
+                   code = PLUS;
+               }
+             target = expand_simple_binop (mode, code, target, val, NULL_RTX,
+                                           true, OPTAB_LIB_WIDEN);
+           }
+
+         return target;
+       }
+    }
+
+  /* Failing that, generate a compare-and-swap loop in which we perform the
+     operation with normal arithmetic instructions.  */
+  if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+    {
+      rtx t0 = gen_reg_rtx (mode), t1;
+
+      if (!target || !register_operand (target, mode))
+       target = gen_reg_rtx (mode);
+
+      start_sequence ();
+
+      if (code == NOT)
+       {
+         val = expand_simple_unop (mode, NOT, val, NULL_RTX, true);
+         code = AND;
+       }
+      if (!after)
+       emit_move_insn (target, t0);
+      t1 = expand_simple_binop (mode, code, t0, val, NULL_RTX,
+                               true, OPTAB_LIB_WIDEN);
+      if (after)
+       emit_move_insn (target, t1);
+
+      insn = get_insns ();
+      end_sequence ();
+
+      if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn))
+       return target;
+    }
+
+  return NULL_RTX;
+}
+
+/* This function expands a test-and-set operation.  Ideally we atomically
+   store VAL in MEM and return the previous value in MEM.  Some targets
+   may not support this operation and only support VAL with the constant 1;
+   in this case while the return value will be 0/1, but the exact value 
+   stored in MEM is target defined.  TARGET is an option place to stick
+   the return value.  */
+
+rtx
+expand_sync_lock_test_and_set (rtx mem, rtx val, rtx target)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  enum insn_code icode;
+  rtx insn;
+
+  /* If the target supports the test-and-set directly, great.  */
+  icode = sync_lock_test_and_set[mode];
+  if (icode != CODE_FOR_nothing)
+    {
+      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
+       target = gen_reg_rtx (mode);
+
+      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+       val = convert_modes (mode, GET_MODE (val), val, 1);
+      if (!insn_data[icode].operand[2].predicate (val, mode))
+       val = force_reg (mode, val);
+
+      insn = GEN_FCN (icode) (target, mem, val);
+      if (insn)
+       {
+         emit_insn (insn);
+         return target;
+       }
+    }
+
+  /* Otherwise, use a compare-and-swap loop for the exchange.  */
+  if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+    {
+      if (!target || !register_operand (target, mode))
+       target = gen_reg_rtx (mode);
+      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+       val = convert_modes (mode, GET_MODE (val), val, 1);
+      if (expand_compare_and_swap_loop (mem, target, val, NULL_RTX))
+       return target;
+    }
+
+  return NULL_RTX;
+}
+
 #include "gt-optabs.h"
index dea332e47569aeefb6c42487cf462b0cc9eb8fba..1426e570fb9659c1e99839b33d630dd3601e07d2 100644 (file)
@@ -432,6 +432,43 @@ extern enum insn_code clrmem_optab[NUM_MACHINE_MODES];
 extern enum insn_code cmpstr_optab[NUM_MACHINE_MODES];
 extern enum insn_code cmpmem_optab[NUM_MACHINE_MODES];
 
+/* Synchronization primitives.  This first set is atomic operation for
+   which we don't care about the resulting value.  */
+extern enum insn_code sync_add_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_sub_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_ior_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_and_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_xor_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_nand_optab[NUM_MACHINE_MODES];
+
+/* This second set is atomic operations in which we return the value
+   that existed in memory before the operation.  */
+extern enum insn_code sync_old_add_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_old_sub_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_old_ior_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_old_and_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_old_xor_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_old_nand_optab[NUM_MACHINE_MODES];
+
+/* This third set is atomic operations in which we return the value
+   that resulted after performing the operation.  */
+extern enum insn_code sync_new_add_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_new_sub_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_new_ior_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_new_and_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_new_xor_optab[NUM_MACHINE_MODES];
+extern enum insn_code sync_new_nand_optab[NUM_MACHINE_MODES];
+
+/* Atomic compare and swap.  */
+extern enum insn_code sync_compare_and_swap[NUM_MACHINE_MODES];
+extern enum insn_code sync_compare_and_swap_cc[NUM_MACHINE_MODES];
+
+/* Atomic exchange with acquire semantics.  */
+extern enum insn_code sync_lock_test_and_set[NUM_MACHINE_MODES];
+
+/* Atomic clear with release semantics.  */
+extern enum insn_code sync_lock_release[NUM_MACHINE_MODES];
+
 /* Define functions given in optabs.c.  */
 
 extern rtx expand_ternary_op (enum machine_mode mode, optab ternary_optab, 
diff --git a/gcc/testsuite/gcc.c-torture/compile/sync-1.c b/gcc/testsuite/gcc.c-torture/compile/sync-1.c
new file mode 100644 (file)
index 0000000..1703aaf
--- /dev/null
@@ -0,0 +1,276 @@
+/* Validate that each of the __sync builtins compiles.  This won't 
+   necessarily link, since the target might not support the builtin,
+   so this may result in external library calls.  */
+
+signed char sc;
+unsigned char uc;
+signed short ss;
+unsigned short us;
+signed int si;
+unsigned int ui;
+signed long sl;
+unsigned long ul;
+signed long long sll;
+unsigned long long ull;
+void *vp;
+int *ip;
+struct S { struct S *next; int x; } *sp;
+
+void test_op_ignore (void)
+{
+  (void) __sync_fetch_and_add (&sc, 1);
+  (void) __sync_fetch_and_add (&uc, 1);
+  (void) __sync_fetch_and_add (&ss, 1);
+  (void) __sync_fetch_and_add (&us, 1);
+  (void) __sync_fetch_and_add (&si, 1);
+  (void) __sync_fetch_and_add (&ui, 1);
+  (void) __sync_fetch_and_add (&sl, 1);
+  (void) __sync_fetch_and_add (&ul, 1);
+  (void) __sync_fetch_and_add (&sll, 1);
+  (void) __sync_fetch_and_add (&ull, 1);
+
+  (void) __sync_fetch_and_sub (&sc, 1);
+  (void) __sync_fetch_and_sub (&uc, 1);
+  (void) __sync_fetch_and_sub (&ss, 1);
+  (void) __sync_fetch_and_sub (&us, 1);
+  (void) __sync_fetch_and_sub (&si, 1);
+  (void) __sync_fetch_and_sub (&ui, 1);
+  (void) __sync_fetch_and_sub (&sl, 1);
+  (void) __sync_fetch_and_sub (&ul, 1);
+  (void) __sync_fetch_and_sub (&sll, 1);
+  (void) __sync_fetch_and_sub (&ull, 1);
+
+  (void) __sync_fetch_and_or (&sc, 1);
+  (void) __sync_fetch_and_or (&uc, 1);
+  (void) __sync_fetch_and_or (&ss, 1);
+  (void) __sync_fetch_and_or (&us, 1);
+  (void) __sync_fetch_and_or (&si, 1);
+  (void) __sync_fetch_and_or (&ui, 1);
+  (void) __sync_fetch_and_or (&sl, 1);
+  (void) __sync_fetch_and_or (&ul, 1);
+  (void) __sync_fetch_and_or (&sll, 1);
+  (void) __sync_fetch_and_or (&ull, 1);
+
+  (void) __sync_fetch_and_xor (&sc, 1);
+  (void) __sync_fetch_and_xor (&uc, 1);
+  (void) __sync_fetch_and_xor (&ss, 1);
+  (void) __sync_fetch_and_xor (&us, 1);
+  (void) __sync_fetch_and_xor (&si, 1);
+  (void) __sync_fetch_and_xor (&ui, 1);
+  (void) __sync_fetch_and_xor (&sl, 1);
+  (void) __sync_fetch_and_xor (&ul, 1);
+  (void) __sync_fetch_and_xor (&sll, 1);
+  (void) __sync_fetch_and_xor (&ull, 1);
+
+  (void) __sync_fetch_and_and (&sc, 1);
+  (void) __sync_fetch_and_and (&uc, 1);
+  (void) __sync_fetch_and_and (&ss, 1);
+  (void) __sync_fetch_and_and (&us, 1);
+  (void) __sync_fetch_and_and (&si, 1);
+  (void) __sync_fetch_and_and (&ui, 1);
+  (void) __sync_fetch_and_and (&sl, 1);
+  (void) __sync_fetch_and_and (&ul, 1);
+  (void) __sync_fetch_and_and (&sll, 1);
+  (void) __sync_fetch_and_and (&ull, 1);
+
+  (void) __sync_fetch_and_nand (&sc, 1);
+  (void) __sync_fetch_and_nand (&uc, 1);
+  (void) __sync_fetch_and_nand (&ss, 1);
+  (void) __sync_fetch_and_nand (&us, 1);
+  (void) __sync_fetch_and_nand (&si, 1);
+  (void) __sync_fetch_and_nand (&ui, 1);
+  (void) __sync_fetch_and_nand (&sl, 1);
+  (void) __sync_fetch_and_nand (&ul, 1);
+  (void) __sync_fetch_and_nand (&sll, 1);
+  (void) __sync_fetch_and_nand (&ull, 1);
+}
+
+void test_fetch_and_op (void)
+{
+  sc = __sync_fetch_and_add (&sc, 11);
+  uc = __sync_fetch_and_add (&uc, 11);
+  ss = __sync_fetch_and_add (&ss, 11);
+  us = __sync_fetch_and_add (&us, 11);
+  si = __sync_fetch_and_add (&si, 11);
+  ui = __sync_fetch_and_add (&ui, 11);
+  sl = __sync_fetch_and_add (&sl, 11);
+  ul = __sync_fetch_and_add (&ul, 11);
+  sll = __sync_fetch_and_add (&sll, 11);
+  ull = __sync_fetch_and_add (&ull, 11);
+
+  sc = __sync_fetch_and_sub (&sc, 11);
+  uc = __sync_fetch_and_sub (&uc, 11);
+  ss = __sync_fetch_and_sub (&ss, 11);
+  us = __sync_fetch_and_sub (&us, 11);
+  si = __sync_fetch_and_sub (&si, 11);
+  ui = __sync_fetch_and_sub (&ui, 11);
+  sl = __sync_fetch_and_sub (&sl, 11);
+  ul = __sync_fetch_and_sub (&ul, 11);
+  sll = __sync_fetch_and_sub (&sll, 11);
+  ull = __sync_fetch_and_sub (&ull, 11);
+
+  sc = __sync_fetch_and_or (&sc, 11);
+  uc = __sync_fetch_and_or (&uc, 11);
+  ss = __sync_fetch_and_or (&ss, 11);
+  us = __sync_fetch_and_or (&us, 11);
+  si = __sync_fetch_and_or (&si, 11);
+  ui = __sync_fetch_and_or (&ui, 11);
+  sl = __sync_fetch_and_or (&sl, 11);
+  ul = __sync_fetch_and_or (&ul, 11);
+  sll = __sync_fetch_and_or (&sll, 11);
+  ull = __sync_fetch_and_or (&ull, 11);
+
+  sc = __sync_fetch_and_xor (&sc, 11);
+  uc = __sync_fetch_and_xor (&uc, 11);
+  ss = __sync_fetch_and_xor (&ss, 11);
+  us = __sync_fetch_and_xor (&us, 11);
+  si = __sync_fetch_and_xor (&si, 11);
+  ui = __sync_fetch_and_xor (&ui, 11);
+  sl = __sync_fetch_and_xor (&sl, 11);
+  ul = __sync_fetch_and_xor (&ul, 11);
+  sll = __sync_fetch_and_xor (&sll, 11);
+  ull = __sync_fetch_and_xor (&ull, 11);
+
+  sc = __sync_fetch_and_and (&sc, 11);
+  uc = __sync_fetch_and_and (&uc, 11);
+  ss = __sync_fetch_and_and (&ss, 11);
+  us = __sync_fetch_and_and (&us, 11);
+  si = __sync_fetch_and_and (&si, 11);
+  ui = __sync_fetch_and_and (&ui, 11);
+  sl = __sync_fetch_and_and (&sl, 11);
+  ul = __sync_fetch_and_and (&ul, 11);
+  sll = __sync_fetch_and_and (&sll, 11);
+  ull = __sync_fetch_and_and (&ull, 11);
+
+  sc = __sync_fetch_and_nand (&sc, 11);
+  uc = __sync_fetch_and_nand (&uc, 11);
+  ss = __sync_fetch_and_nand (&ss, 11);
+  us = __sync_fetch_and_nand (&us, 11);
+  si = __sync_fetch_and_nand (&si, 11);
+  ui = __sync_fetch_and_nand (&ui, 11);
+  sl = __sync_fetch_and_nand (&sl, 11);
+  ul = __sync_fetch_and_nand (&ul, 11);
+  sll = __sync_fetch_and_nand (&sll, 11);
+  ull = __sync_fetch_and_nand (&ull, 11);
+}
+
+void test_op_and_fetch (void)
+{
+  sc = __sync_add_and_fetch (&sc, uc);
+  uc = __sync_add_and_fetch (&uc, uc);
+  ss = __sync_add_and_fetch (&ss, uc);
+  us = __sync_add_and_fetch (&us, uc);
+  si = __sync_add_and_fetch (&si, uc);
+  ui = __sync_add_and_fetch (&ui, uc);
+  sl = __sync_add_and_fetch (&sl, uc);
+  ul = __sync_add_and_fetch (&ul, uc);
+  sll = __sync_add_and_fetch (&sll, uc);
+  ull = __sync_add_and_fetch (&ull, uc);
+
+  sc = __sync_sub_and_fetch (&sc, uc);
+  uc = __sync_sub_and_fetch (&uc, uc);
+  ss = __sync_sub_and_fetch (&ss, uc);
+  us = __sync_sub_and_fetch (&us, uc);
+  si = __sync_sub_and_fetch (&si, uc);
+  ui = __sync_sub_and_fetch (&ui, uc);
+  sl = __sync_sub_and_fetch (&sl, uc);
+  ul = __sync_sub_and_fetch (&ul, uc);
+  sll = __sync_sub_and_fetch (&sll, uc);
+  ull = __sync_sub_and_fetch (&ull, uc);
+
+  sc = __sync_or_and_fetch (&sc, uc);
+  uc = __sync_or_and_fetch (&uc, uc);
+  ss = __sync_or_and_fetch (&ss, uc);
+  us = __sync_or_and_fetch (&us, uc);
+  si = __sync_or_and_fetch (&si, uc);
+  ui = __sync_or_and_fetch (&ui, uc);
+  sl = __sync_or_and_fetch (&sl, uc);
+  ul = __sync_or_and_fetch (&ul, uc);
+  sll = __sync_or_and_fetch (&sll, uc);
+  ull = __sync_or_and_fetch (&ull, uc);
+
+  sc = __sync_xor_and_fetch (&sc, uc);
+  uc = __sync_xor_and_fetch (&uc, uc);
+  ss = __sync_xor_and_fetch (&ss, uc);
+  us = __sync_xor_and_fetch (&us, uc);
+  si = __sync_xor_and_fetch (&si, uc);
+  ui = __sync_xor_and_fetch (&ui, uc);
+  sl = __sync_xor_and_fetch (&sl, uc);
+  ul = __sync_xor_and_fetch (&ul, uc);
+  sll = __sync_xor_and_fetch (&sll, uc);
+  ull = __sync_xor_and_fetch (&ull, uc);
+
+  sc = __sync_and_and_fetch (&sc, uc);
+  uc = __sync_and_and_fetch (&uc, uc);
+  ss = __sync_and_and_fetch (&ss, uc);
+  us = __sync_and_and_fetch (&us, uc);
+  si = __sync_and_and_fetch (&si, uc);
+  ui = __sync_and_and_fetch (&ui, uc);
+  sl = __sync_and_and_fetch (&sl, uc);
+  ul = __sync_and_and_fetch (&ul, uc);
+  sll = __sync_and_and_fetch (&sll, uc);
+  ull = __sync_and_and_fetch (&ull, uc);
+
+  sc = __sync_nand_and_fetch (&sc, uc);
+  uc = __sync_nand_and_fetch (&uc, uc);
+  ss = __sync_nand_and_fetch (&ss, uc);
+  us = __sync_nand_and_fetch (&us, uc);
+  si = __sync_nand_and_fetch (&si, uc);
+  ui = __sync_nand_and_fetch (&ui, uc);
+  sl = __sync_nand_and_fetch (&sl, uc);
+  ul = __sync_nand_and_fetch (&ul, uc);
+  sll = __sync_nand_and_fetch (&sll, uc);
+  ull = __sync_nand_and_fetch (&ull, uc);
+}
+
+void test_compare_and_swap (void)
+{
+  sc = __sync_val_compare_and_swap (&sc, uc, sc);
+  uc = __sync_val_compare_and_swap (&uc, uc, sc);
+  ss = __sync_val_compare_and_swap (&ss, uc, sc);
+  us = __sync_val_compare_and_swap (&us, uc, sc);
+  si = __sync_val_compare_and_swap (&si, uc, sc);
+  ui = __sync_val_compare_and_swap (&ui, uc, sc);
+  sl = __sync_val_compare_and_swap (&sl, uc, sc);
+  ul = __sync_val_compare_and_swap (&ul, uc, sc);
+  sll = __sync_val_compare_and_swap (&sll, uc, sc);
+  ull = __sync_val_compare_and_swap (&ull, uc, sc);
+
+  ui = __sync_bool_compare_and_swap (&sc, uc, sc);
+  ui = __sync_bool_compare_and_swap (&uc, uc, sc);
+  ui = __sync_bool_compare_and_swap (&ss, uc, sc);
+  ui = __sync_bool_compare_and_swap (&us, uc, sc);
+  ui = __sync_bool_compare_and_swap (&si, uc, sc);
+  ui = __sync_bool_compare_and_swap (&ui, uc, sc);
+  ui = __sync_bool_compare_and_swap (&sl, uc, sc);
+  ui = __sync_bool_compare_and_swap (&ul, uc, sc);
+  ui = __sync_bool_compare_and_swap (&sll, uc, sc);
+  ui = __sync_bool_compare_and_swap (&ull, uc, sc);
+}
+
+void test_lock (void)
+{
+  sc = __sync_lock_test_and_set (&sc, 1);
+  uc = __sync_lock_test_and_set (&uc, 1);
+  ss = __sync_lock_test_and_set (&ss, 1);
+  us = __sync_lock_test_and_set (&us, 1);
+  si = __sync_lock_test_and_set (&si, 1);
+  ui = __sync_lock_test_and_set (&ui, 1);
+  sl = __sync_lock_test_and_set (&sl, 1);
+  ul = __sync_lock_test_and_set (&ul, 1);
+  sll = __sync_lock_test_and_set (&sll, 1);
+  ull = __sync_lock_test_and_set (&ull, 1);
+
+  __sync_synchronize ();
+
+  __sync_lock_release (&sc);
+  __sync_lock_release (&uc);
+  __sync_lock_release (&ss);
+  __sync_lock_release (&us);
+  __sync_lock_release (&si);
+  __sync_lock_release (&ui);
+  __sync_lock_release (&sl);
+  __sync_lock_release (&ul);
+  __sync_lock_release (&sll);
+  __sync_lock_release (&ull);
+}
diff --git a/gcc/testsuite/gcc.dg/sync-1.c b/gcc/testsuite/gcc.dg/sync-1.c
new file mode 100644 (file)
index 0000000..f8cabe4
--- /dev/null
@@ -0,0 +1,40 @@
+/* Validate that the __sync builtins are overloaded properly.  */
+/* { dg-do compile } */
+/* { dg-options "-Werror" } */
+
+#define TEST1(TYPE, BUILTIN)           \
+void t_##TYPE##BUILTIN(TYPE *p)                \
+{                                      \
+  __typeof(BUILTIN(p, 1)) *pp;         \
+  pp = p;                              \
+}
+
+#define TEST2(BUILTIN)         \
+  TEST1(int, BUILTIN)          \
+  TEST1(long, BUILTIN)
+
+TEST2(__sync_fetch_and_add)
+TEST2(__sync_fetch_and_sub)
+TEST2(__sync_fetch_and_or)
+TEST2(__sync_fetch_and_and)
+TEST2(__sync_fetch_and_xor)
+TEST2(__sync_fetch_and_nand)
+
+TEST2(__sync_add_and_fetch)
+TEST2(__sync_sub_and_fetch)
+TEST2(__sync_or_and_fetch)
+TEST2(__sync_and_and_fetch)
+TEST2(__sync_xor_and_fetch)
+TEST2(__sync_nand_and_fetch)
+
+TEST2(__sync_lock_test_and_set)
+
+#define TEST3(TYPE)                                    \
+void t_##TYPE##__sync_val_compare_and_swap(TYPE *p)    \
+{                                                      \
+  __typeof(__sync_val_compare_and_swap(p, 1, 2)) *pp;  \
+  pp = p;                                              \
+}
+
+TEST3(int)
+TEST3(long)