diff options
author | Richard Henderson <rth@gcc.gnu.org> | 2005-04-14 16:37:47 -0700 |
---|---|---|
committer | Richard Henderson <rth@gcc.gnu.org> | 2005-04-14 16:37:47 -0700 |
commit | 48ae6c138ca30c4c5e876a0be47c9a0b5c8bf5c2 (patch) | |
tree | 1a1c7042dd3669b680fe5d1694acec62b0dfabc4 /gcc | |
parent | 871ae772871fdfbbb978c7e9f7ff68b03324644a (diff) | |
download | gcc-48ae6c138ca30c4c5e876a0be47c9a0b5c8bf5c2.zip gcc-48ae6c138ca30c4c5e876a0be47c9a0b5c8bf5c2.tar.gz gcc-48ae6c138ca30c4c5e876a0be47c9a0b5c8bf5c2.tar.bz2 |
re PR middle-end/14311 (builtins for atomic operations needed)
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
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 140 | ||||
-rw-r--r-- | gcc/builtin-types.def | 27 | ||||
-rw-r--r-- | gcc/builtins.c | 338 | ||||
-rw-r--r-- | gcc/builtins.def | 199 | ||||
-rw-r--r-- | gcc/c-common.c | 171 | ||||
-rw-r--r-- | gcc/c-common.h | 4 | ||||
-rw-r--r-- | gcc/c-typeck.c | 7 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 128 | ||||
-rw-r--r-- | gcc/doc/md.texi | 134 | ||||
-rw-r--r-- | gcc/expr.c | 24 | ||||
-rw-r--r-- | gcc/expr.h | 5 | ||||
-rw-r--r-- | gcc/genopinit.c | 25 | ||||
-rw-r--r-- | gcc/optabs.c | 536 | ||||
-rw-r--r-- | gcc/optabs.h | 37 | ||||
-rw-r--r-- | gcc/testsuite/gcc.c-torture/compile/sync-1.c | 276 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/sync-1.c | 40 |
16 files changed, 2057 insertions, 34 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 53aa00c..88c3a03 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -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 @@ -91,13 +177,13 @@ 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> @@ -489,29 +575,29 @@ 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> diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index d9b87df..4b5f353 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -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) diff --git a/gcc/builtins.c b/gcc/builtins.c index b7ddb74..1a54a06 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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); } + + +/* 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); +} /* 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; } diff --git a/gcc/builtins.def b/gcc/builtins.def index 51d87ca..134bf98 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -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) diff --git a/gcc/c-common.c b/gcc/c-common.c index 050a1eb..da6be4e 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -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; } + +/* 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" diff --git a/gcc/c-common.h b/gcc/c-common.h index d2b9a4a..cf6e88b 100644 --- a/gcc/c-common.h +++ b/gcc/c-common.h @@ -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 *); diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c index 8c39966..9c82bbe 100644 --- a/gcc/c-typeck.c +++ b/gcc/c-typeck.c @@ -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 diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 23b046d..5fc85a4 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -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 diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index 4600230..4deacfb 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -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 @@ -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 @@ -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); /* Functions from expmed.c: */ diff --git a/gcc/genopinit.c b/gcc/genopinit.c index da80f78..d58f881 100644 --- a/gcc/genopinit.c +++ b/gcc/genopinit.c @@ -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); diff --git a/gcc/optabs.c b/gcc/optabs.c index 6835a9a..47eec4b 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -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; } + + +/* 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" diff --git a/gcc/optabs.h b/gcc/optabs.h index dea332e..1426e57 100644 --- a/gcc/optabs.h +++ b/gcc/optabs.h @@ -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 index 0000000..1703aaf --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/compile/sync-1.c @@ -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 index 0000000..f8cabe4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/sync-1.c @@ -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) |