This patch supplies __sync_mem_is_lock_free (size) and
__sync_mem_always_lock_free (size).
__sync_mem_always_lock_free requires a compile time constant, and
returns true if an object of the specified size will *always* generate
lock free instructions on the current architecture. Otherwise false is
returned.
__sync_mem_is_lock_free also returns true if instructions will always be
lock free, but if the answer is not true, it resolves to an external
call named '__sync_mem_is_lock_free' which will be supplied
externally. Presumably by whatever library or application is providing
the other external __sync_mem routines as documented in
http://gcc.gnu.org/wiki/Atomic/GCCMM/LIbrary
New tests, documentation are provided, bootstraps on
x86_64-unknown-linux-gnu and causes no new testsuite regressions.
Andrew
* optabs.h (DOI_sync_mem_always_lock_free): New.
(DOI_sync_mem_is_lock_free): New.
(sync_mem_always_lock_free_optab, sync_mem_is_lock_free_optab): New.
* builtins.c (fold_builtin_sync_mem_always_lock_free): New.
(expand_builtin_sync_mem_always_lock_free): New.
(fold_builtin_sync_mem_is_lock_free): New.
(expand_builtin_sync_mem_is_lock_free): New.
(expand_builtin): Handle BUILT_IN_SYNC_MEM_{is,always}_LOCK_FREE.
(fold_builtin_1): Handle BUILT_IN_SYNC_MEM_{is,always}_LOCK_FREE.
* sync-builtins.def: Add BUILT_IN_SYNC_MEM_{is,always}_LOCK_FREE.
* builtin-types.def: Add BT_FN_BOOL_SIZE type.
* fortran/types.def: Add BT_SIZE and BT_FN_BOOL_SIZE.
* doc/extend.texi: Add documentation.
* testsuite/gcc.dg/sync-mem-invalid.c: Test for invalid param.
* testsuite/gcc.dg/sync-mem-lockfree[-aux].c: New tests.
Index: optabs.h
===================================================================
*** optabs.h (revision 178916)
--- optabs.h (working copy)
*************** enum direct_optab_index
*** 708,713 ****
--- 708,715 ----
DOI_sync_mem_nand,
DOI_sync_mem_xor,
DOI_sync_mem_or,
+ DOI_sync_mem_always_lock_free,
+ DOI_sync_mem_is_lock_free,
DOI_sync_mem_thread_fence,
DOI_sync_mem_signal_fence,
*************** typedef struct direct_optab_d *direct_op
*** 801,806 ****
--- 803,812 ----
(&direct_optab_table[(int) DOI_sync_mem_xor])
#define sync_mem_or_optab \
(&direct_optab_table[(int) DOI_sync_mem_or])
+ #define sync_mem_always_lock_free_optab \
+ (&direct_optab_table[(int) DOI_sync_mem_always_lock_free])
+ #define sync_mem_is_lock_free_optab \
+ (&direct_optab_table[(int) DOI_sync_mem_is_lock_free])
#define sync_mem_thread_fence_optab \
(&direct_optab_table[(int) DOI_sync_mem_thread_fence])
#define sync_mem_signal_fence_optab \
Index: builtins.c
===================================================================
*** builtins.c (revision 179522)
--- builtins.c (working copy)
*************** expand_builtin_sync_mem_fetch_op (enum m
*** 5386,5391 ****
--- 5386,5472 ----
return expand_sync_mem_fetch_op (target, mem, val, code, model,
fetch_after);
}
+ /* Return true if size ARG is always lock free on this architecture. */
+ static tree
+ fold_builtin_sync_mem_always_lock_free (tree arg)
+ {
+ int size;
+ enum machine_mode mode;
+ enum insn_code icode;
+
+ if (TREE_CODE (arg) != INTEGER_CST)
+ return NULL_TREE;
+
+ /* Check if a compare_and_swap pattern exists for the mode which represents
+ the required size. The pattern is not allowed to fail, so the existence
+ of the pattern indicates support is present. */
+ size = INTVAL (expand_normal (arg)) * BITS_PER_UNIT;
+ mode = mode_for_size (size, MODE_INT, 0);
+ icode = direct_optab_handler (sync_compare_and_swap_optab, mode);
+
+ if (icode == CODE_FOR_nothing)
+ return integer_zero_node;
+
+ return integer_one_node;
+ }
+
+ /* Return true if the first argument to call EXP represents a size of
+ object than will always generate lock-free instructions on this target.
+ Otherwise return false. */
+ static rtx
+ expand_builtin_sync_mem_always_lock_free (tree exp)
+ {
+ tree size;
+ tree arg = CALL_EXPR_ARG (exp, 0);
+
+ if (TREE_CODE (arg) != INTEGER_CST)
+ {
+ error ("non-constant argument to __sync_mem_always_lock_free");
+ return const0_rtx;
+ }
+
+ size = fold_builtin_sync_mem_always_lock_free (arg);
+ if (size == integer_one_node)
+ return const1_rtx;
+ return const0_rtx;
+ }
+
+ /* Return a one or zero if it can be determined that size ARG is lock free on
+ this architecture. */
+ static tree
+ fold_builtin_sync_mem_is_lock_free (tree arg)
+ {
+ tree always = fold_builtin_sync_mem_always_lock_free (arg);
+
+ /* If it isnt always lock free, don't generate a result. */
+ if (always == integer_one_node)
+ return always;
+
+ return NULL_TREE;
+ }
+
+ /* Return one or zero if the first argument to call EXP represents a size of
+ object than can generate lock-free instructions on this target. */
+ static rtx
+ expand_builtin_sync_mem_is_lock_free (tree exp)
+ {
+ tree size;
+ tree arg = CALL_EXPR_ARG (exp, 0);
+
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (arg)))
+ {
+ error ("non-integer argument to __sync_mem_is_lock_free");
+ return NULL_RTX;
+ }
+
+ /* If the value is known at compile time, return the RTX for it. */
+ size = fold_builtin_sync_mem_is_lock_free (arg);
+ if (size == integer_one_node)
+ return const1_rtx;
+
+ return NULL_RTX;
+ }
+
/* This routine will either emit the mem_thread_fence pattern or issue a
sync_synchronize to generate a fence for memory model MEMMODEL. */
*************** expand_builtin (tree exp, rtx target, rt
*** 6430,6435 ****
--- 6511,6525 ----
return target;
break;
+ case BUILT_IN_SYNC_MEM_ALWAYS_LOCK_FREE:
+ return expand_builtin_sync_mem_always_lock_free (exp);
+
+ case BUILT_IN_SYNC_MEM_IS_LOCK_FREE:
+ target = expand_builtin_sync_mem_is_lock_free (exp);
+ if (target)
+ return target;
+ break;
+
case BUILT_IN_SYNC_MEM_THREAD_FENCE:
expand_builtin_sync_mem_thread_fence (exp);
return const0_rtx;
*************** fold_builtin_1 (location_t loc, tree fnd
*** 10269,10274 ****
--- 10359,10370 ----
return build_empty_stmt (loc);
break;
+ case BUILT_IN_SYNC_MEM_ALWAYS_LOCK_FREE:
+ return fold_builtin_sync_mem_always_lock_free (arg0);
+
+ case BUILT_IN_SYNC_MEM_IS_LOCK_FREE:
+ return fold_builtin_sync_mem_is_lock_free (arg0);
+
default:
break;
}
Index: sync-builtins.def
===================================================================
*** sync-builtins.def (revision 178916)
--- sync-builtins.def (working copy)
*************** DEF_SYNC_BUILTIN (BUILT_IN_SYNC_MEM_FETC
*** 564,569 ****
--- 564,578 ----
"__sync_mem_fetch_or_16",
BT_FN_I16_VPTR_I16_INT, ATTR_NOTHROW_LEAF_LIST)
+ DEF_SYNC_BUILTIN (BUILT_IN_SYNC_MEM_ALWAYS_LOCK_FREE,
+ "__sync_mem_always_lock_free",
+ BT_FN_BOOL_SIZE, ATTR_CONST_NOTHROW_LEAF_LIST)
+
+ DEF_SYNC_BUILTIN (BUILT_IN_SYNC_MEM_IS_LOCK_FREE,
+ "__sync_mem_is_lock_free",
+ BT_FN_BOOL_SIZE, ATTR_CONST_NOTHROW_LEAF_LIST)
+
+
DEF_SYNC_BUILTIN (BUILT_IN_SYNC_MEM_THREAD_FENCE,
"__sync_mem_thread_fence",
BT_FN_VOID_INT, ATTR_NOTHROW_LEAF_LIST)
Index: builtin-types.def
===================================================================
*** builtin-types.def (revision 178916)
--- builtin-types.def (working copy)
*************** DEF_FUNCTION_TYPE_1 (BT_FN_ULONG_ULONG,
*** 224,229 ****
--- 224,230 ----
DEF_FUNCTION_TYPE_1 (BT_FN_ULONGLONG_ULONGLONG, BT_ULONGLONG, BT_ULONGLONG)
DEF_FUNCTION_TYPE_1 (BT_FN_UINT32_UINT32, BT_UINT32, BT_UINT32)
DEF_FUNCTION_TYPE_1 (BT_FN_UINT64_UINT64, BT_UINT64, BT_UINT64)
+ DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_SIZE, BT_BOOL, BT_SIZE)
DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR)
Index: fortran/types.def
===================================================================
*** fortran/types.def (revision 178916)
--- fortran/types.def (working copy)
*************** DEF_PRIMITIVE_TYPE (BT_UINT, unsigned_ty
*** 57,62 ****
--- 57,63 ----
DEF_PRIMITIVE_TYPE (BT_LONG, long_integer_type_node)
DEF_PRIMITIVE_TYPE (BT_ULONGLONG, long_long_unsigned_type_node)
DEF_PRIMITIVE_TYPE (BT_WORD, (*lang_hooks.types.type_for_mode) (word_mode, 1))
+ DEF_PRIMITIVE_TYPE (BT_SIZE, size_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_FUNCTION_TYPE_1 (BT_FN_VOID_VPTR, BT
*** 89,94 ****
--- 90,96 ----
DEF_FUNCTION_TYPE_1 (BT_FN_UINT_UINT, BT_UINT, BT_UINT)
DEF_FUNCTION_TYPE_1 (BT_FN_PTR_PTR, BT_PTR, BT_PTR)
DEF_FUNCTION_TYPE_1 (BT_FN_VOID_INT, BT_VOID, BT_INT)
+ DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_SIZE, BT_BOOL, BT_SIZE)
DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR)
Index: doc/extend.texi
===================================================================
*** doc/extend.texi (revision 178916)
--- doc/extend.texi (working copy)
*************** handlers based in the same thread.
*** 6900,6905 ****
--- 6900,6925 ----
All memory orders are valid.
+ @item bool __sync_mem_always_lock_free (size_t size)
+ @findex __sync_mem_always_lock_free
+
+ This builtin returns true if objects of size bytes will always generate lock
+ free atomic instructions for the target architecture. Otherwise false is
+ returned.
+
+ size must resolve to a compile time constant.
+
+ @smallexample
+ if (_sync_mem_always_lock_free (sizeof (long long)))
+ @end smallexample
+
+ @item bool __sync_mem_is_lock_free (size_t size)
+ @findex __sync_mem_is_lock_free
+
+ This builtin returns true if objects of size bytes will always generate lock
+ free atomic instructions for the target architecture. If it is not known to
+ be lock free a call is made to a runtime routine named
__sync_mem_is_lock_free.
+
@end table
@node Object Size Checking
Index: testsuite/gcc.dg/sync-mem-invalid.c
===================================================================
*** testsuite/gcc.dg/sync-mem-invalid.c (revision 178916)
--- testsuite/gcc.dg/sync-mem-invalid.c (working copy)
***************
*** 4,9 ****
--- 4,10 ----
/* { dg-require-effective-target sync_int_long } */
int i;
+ size_t s;
main ()
{
*************** main ()
*** 16,19 ****
--- 17,22 ----
__sync_mem_store (&i, 1, __SYNC_MEM_CONSUME); /* { dg-error "invalid memory
model" } */
__sync_mem_store (&i, 1, __SYNC_MEM_ACQ_REL); /* { dg-error "invalid memory
model" } */
+ i = __sync_mem_always_lock_free (s); /* { dg-error "non-constant argument"
} */
+
}
Index: testsuite/gcc.dg/sync-mem-lockfree.c
===================================================================
*** testsuite/gcc.dg/sync-mem-lockfree.c (revision 0)
--- testsuite/gcc.dg/sync-mem-lockfree.c (revision 0)
***************
*** 0 ****
--- 1,120 ----
+ /* Test __sync_mem routines for existence and execution with each valid
+ memory model. */
+ /* { dg-options "-w" } */
+ /* { dg-do run } */
+ /* { dg-additional-sources "sync-mem-lockfree-aux.c" } */
+
+ /* Test that __sync_mem_{is,always}_lock_free builtins execute.
+ sync-mem-lockfree-aux.c supplies and external entry point for
+ __sync_mem_is_lock_free which always returns a 2. We can detect the
+ external routine was called if 2 is returned since that is not a valid
+ result normally. */
+
+ #include <stdlib.h>
+
+ extern void abort();
+
+ int r1, r2;
+
+ /* Test for consistency on sizes 1, 2, 4, 8, 16 and 32. */
+ main ()
+ {
+
+ r1 = __sync_mem_always_lock_free (sizeof(char));
+ r2 = __sync_mem_is_lock_free (sizeof(char));
+ /* If always lock free, then is_lock_free must also be true. */
+ if (r1)
+ {
+ if (r2 != 1)
+ abort ();
+ }
+ else
+ {
+ /* If it is not lock free, then the external routine must be called. */
+ if (r2 != 2)
+ abort ();
+ }
+
+ r1 = __sync_mem_always_lock_free (2);
+ r2 = __sync_mem_is_lock_free (2);
+ /* If always lock free, then is_lock_free must also be true. */
+ if (r1)
+ {
+ if (r2 != 1)
+ abort ();
+ }
+ else
+ {
+ /* If it is not lock free, then the external routine must be called. */
+ if (r2 != 2)
+ abort ();
+ }
+
+
+ r1 = __sync_mem_always_lock_free (4);
+ r2 = __sync_mem_is_lock_free (4); /* Try passing in a variable. */
+ /* If always lock free, then is_lock_free must also be true. */
+ if (r1)
+ {
+ if (r2 != 1)
+ abort ();
+ }
+ else
+ {
+ /* If it is not lock free, then the external routine must be called. */
+ if (r2 != 2)
+ abort ();
+ }
+
+
+ r1 = __sync_mem_always_lock_free (8);
+ r2 = __sync_mem_is_lock_free (8);
+ /* If always lock free, then is_lock_free must also be true. */
+ if (r1)
+ {
+ if (r2 != 1)
+ abort ();
+ }
+ else
+ {
+ /* If it is not lock free, then the external routine must be called. */
+ if (r2 != 2)
+ abort ();
+ }
+
+
+ r1 = __sync_mem_always_lock_free (16);
+ r2 = __sync_mem_is_lock_free (16);
+ /* If always lock free, then is_lock_free must also be true. */
+ if (r1)
+ {
+ if (r2 != 1)
+ abort ();
+ }
+ else
+ {
+ /* If it is not lock free, then the external routine must be called. */
+ if (r2 != 2)
+ abort ();
+ }
+
+
+ r1 = __sync_mem_always_lock_free (32);
+ r2 = __sync_mem_is_lock_free (32);
+ /* If always lock free, then is_lock_free must also be true. */
+ if (r1)
+ {
+ if (r2 != 1)
+ abort ();
+ }
+ else
+ {
+ /* If it is not lock free, then the external routine must be called. */
+ if (r2 != 2)
+ abort ();
+ }
+
+
+ return 0;
+ }
+
Index: testsuite/gcc.dg/sync-mem-lockfree-aux.c
===================================================================
*** testsuite/gcc.dg/sync-mem-lockfree-aux.c (revision 0)
--- testsuite/gcc.dg/sync-mem-lockfree-aux.c (revision 0)
***************
*** 0 ****
--- 1,17 ----
+ /* Test supply a __sync_mem_is_lock_free routine for lock-free tests. */
+ /* Just compile it on its own. */
+ /* { dg-do compile } */
+ /* { dg-options "-w" } */
+
+ /* Test that __sync_mem_{is,always}_lock_free builtins execute. */
+
+ #include <stdlib.h>
+
+ /* Supply a builtin external function which returns a non-standard value so
+ it can be detected that it was called. */
+ int
+ __sync_mem_is_lock_free (size_t s)
+ {
+ return 2;
+ }
+