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;
+ }
+ 

Reply via email to