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
This commit is contained in:
parent
871ae77287
commit
48ae6c138c
140
gcc/ChangeLog
140
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>
|
||||
|
||||
|
@ -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)
|
||||
|
338
gcc/builtins.c
338
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;
|
||||
}
|
||||
|
199
gcc/builtins.def
199
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)
|
||||
|
171
gcc/c-common.c
171
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"
|
||||
|
@ -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 *);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
134
gcc/doc/md.texi
134
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
|
||||
|
24
gcc/expr.c
24
gcc/expr.c
@ -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: */
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
536
gcc/optabs.c
536
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"
|
||||
|
37
gcc/optabs.h
37
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,
|
||||
|
276
gcc/testsuite/gcc.c-torture/compile/sync-1.c
Normal file
276
gcc/testsuite/gcc.c-torture/compile/sync-1.c
Normal file
@ -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);
|
||||
}
|
40
gcc/testsuite/gcc.dg/sync-1.c
Normal file
40
gcc/testsuite/gcc.dg/sync-1.c
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user