Просмотр исходного кода

gcc: Add the `registered' attribute.

* gcc-plugin/src/starpu.c (registered_attribute_name): New variable.
  (handle_heap_allocated_attribute): Rewrite surrounding statements when
  VAR matches `registered_p'.
  (handle_registered_attribute, registered_p): New functions.
  (register_task_attributes): Register the `registered' attribute
  handler.

* gcc-plugin/tests/registered-errors.c, gcc-plugin/tests/registered.c:
  New files.
* gcc-plugin/tests/Makefile.am (gcc_tests): Add them.
* gcc-plugin/tests/mocks.h (starpu_vector_data_register): Allow
  `expected_register_arguments.pointer' to be NULL.

* gcc-plugin/examples/vector_scal/vector_scal.c (main): Use the
  `registered' attribute instead of the `register' pragma.

* doc/chapters/c-extensions.texi (Registered Data Buffers): Document the
  `registered' attribute.  Update `heap_allocated' example to use it.
* doc/chapters/basic-examples.texi (Vector Scaling Using the C
  Extension): Update example to use `registered'.
Ludovic Courtès лет назад: 12
Родитель
Сommit
91766051e7

+ 1 - 0
.gitignore

@@ -287,3 +287,4 @@ starpu.log
 /tests/datawizard/interfaces/copy_interfaces
 /gcc-plugin/tests/release
 /gcc-plugin/tests/opencl
+/gcc-plugin/tests/registered

+ 2 - 3
doc/chapters/basic-examples.texi

@@ -358,9 +358,8 @@ main (void)
 #define FACTOR 3.14
 
   @{
-    float vector[NX] __attribute__ ((heap_allocated));
-
-#pragma starpu register vector
+    float vector[NX]
+       __attribute__ ((heap_allocated, registered));
 
     size_t i;
     for (i = 0; i < NX; i++)

+ 13 - 7
doc/chapters/c-extensions.texi

@@ -295,6 +295,7 @@ The following pragmas are provided:
 @item #pragma starpu register @var{ptr} [@var{size}]
 Register @var{ptr} as a @var{size}-element buffer.  When @var{ptr} has
 an array type whose size is known, @var{size} may be omitted.
+Alternatively, the @code{registered} attribute can be used (see below.)
 
 @item #pragma starpu unregister @var{ptr}
 Unregister the previously-registered memory area pointed to by
@@ -311,11 +312,19 @@ making it available to the tasks.
 
 @end table
 
-Additionally, the @code{heap_allocated} variable attribute offers a
-simple way to allocate storage for arrays on the heap:
+Additionally, the following attributes offer a simple way to allocate
+and register storage for arrays:
 
 @table @code
 
+@item registered
+@cindex @code{registered} attribute
+This attributes applies to local variables with an array type.  Its
+effect is to automatically register the array's storage, as per
+@code{#pragma starpu register}.  The array is automatically unregistered
+when the variable's scope is left.  This attribute is typically used in
+conjunction with the @code{heap_allocated} attribute, described below.
+
 @item heap_allocated
 @cindex @code{heap_allocated} attribute
 This attributes applies to local variables with an array type.  Its
@@ -351,16 +360,13 @@ main (int argc, char *argv[])
 
   @{
     float matrix[nblocks][nblocks][size]
-      __attribute__ ((heap_allocated));
-
-#pragma starpu register matrix
+      __attribute__ ((heap_allocated, registered));
 
     cholesky (nblocks, size, matrix);
 
 #pragma starpu wait
-#pragma starpu unregister matrix
 
-  @}   /* MATRIX is automatically freed here.  */
+  @}   /* MATRIX is automatically unregistered & freed here.  */
 
 #pragma starpu shutdown
 

+ 1 - 5
gcc-plugin/examples/vector_scal/vector_scal.c

@@ -172,9 +172,7 @@ main (void)
 #define FACTOR 3.14
 
   {
-    float vector[NX] __attribute__ ((heap_allocated));
-
-#pragma starpu register vector
+    float vector[NX] __attribute__ ((heap_allocated, registered));
 
     size_t i;
     for (i = 0; i < NX; i++)
@@ -184,8 +182,6 @@ main (void)
 
 #pragma starpu wait
 
-#pragma starpu unregister vector
-
     valid = check (NX, vector, FACTOR);
 
   } /* VECTOR is automatically freed here.  */

+ 114 - 2
gcc-plugin/src/starpu.c

@@ -130,6 +130,7 @@ static const char task_attribute_name[] = "task";
 static const char task_implementation_attribute_name[] = "task_implementation";
 static const char output_attribute_name[] = "output";
 static const char heap_allocated_attribute_name[] = "heap_allocated";
+static const char registered_attribute_name[] = "registered";
 
 /* Names of attributes used internally.  */
 static const char task_codelet_attribute_name[] = ".codelet";
@@ -183,6 +184,7 @@ static bool implicit_cpu_task_implementation_p (const_tree fn);
 static int task_implementation_target_to_int (const_tree target);
 
 static bool heap_allocated_p (const_tree var_decl);
+static bool registered_p (const_tree var_decl);
 
 static tree declare_codelet (tree task_decl);
 
@@ -2002,6 +2004,7 @@ handle_heap_allocated_attribute (tree *node, tree name, tree args,
       /* Turn VAR into a pointer that feels like an array.  This is what's
 	 done for PARM_DECLs that have an array type.  */
 
+      location_t loc = DECL_SOURCE_LOCATION (var);
       tree array_type = TREE_TYPE (var);
       tree element_type = TREE_TYPE (array_type);
       tree pointer_type = build_pointer_type (element_type);
@@ -2025,7 +2028,6 @@ handle_heap_allocated_attribute (tree *node, tree name, tree args,
 				    build_addr (var, current_function_decl),
 				    TYPE_SIZE_UNIT (array_type));
       TREE_SIDE_EFFECTS (alloc) = true;
-      add_stmt (alloc);
 
       /* Add a destructor for VAR.  Instead of consing the `cleanup'
 	 attribute for VAR, directly use `push_cleanup'.  This guarantees
@@ -2037,7 +2039,99 @@ handle_heap_allocated_attribute (tree *node, tree name, tree args,
       static tree cleanup_decl;
       LOOKUP_STARPU_FUNCTION (cleanup_decl, "starpu_free");
 
-      push_cleanup (var, build_call_expr (cleanup_decl, 1, var), false);
+      if (registered_p (var))
+	{
+	  /* A `registered' attribute has already been processed, and thus a
+	     cleanup for it has been pushed.  However, we want that cleanup
+	     to appear before ours, and our allocation to appear before the
+	     registration, so swap them.  */
+	  tree_stmt_iterator it;
+	  tree parent, try_finally, registration;
+
+#ifdef stmt_list_stack
+	  parent = VEC_last (tree, stmt_list_stack);
+#else  /* 4.6 and before */
+	  parent = TREE_CHAIN (cur_stmt_list);
+#endif
+
+	  gcc_assert (parent != NULL_TREE
+		      && TREE_CODE (parent) == STATEMENT_LIST);
+
+	  it = tsi_last (parent);
+	  try_finally = tsi_stmt (it);
+	  gcc_assert (TREE_CODE (try_finally) == TRY_FINALLY_EXPR);
+
+	  tsi_prev (&it);
+	  registration =
+	    build_data_register_call (loc, var,
+				      array_type_element_count
+				       (loc, array_type));
+
+	  add_stmt (registration);
+	  *tsi_stmt_ptr (it) = alloc;
+
+	  push_cleanup (var, build_data_unregister_call (loc, var), false);
+	  TREE_OPERAND (try_finally, 1) = build_call_expr (cleanup_decl, 1, var);
+	}
+      else
+	{
+	  /* Push the allocation and cleanup in order.  */
+	  add_stmt (alloc);
+	  push_cleanup (var, build_call_expr (cleanup_decl, 1, var), false);
+	}
+
+      /* Keep the attribute.  */
+      *no_add_attrs = false;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle the `registered' attribute on variable *NODE.  */
+
+static tree
+handle_registered_attribute (tree *node, tree name, tree args,
+			     int flags, bool *no_add_attrs)
+{
+  location_t loc;
+  tree var = *node;
+
+  loc = DECL_SOURCE_LOCATION (var);
+
+  bool heap_p = heap_allocated_p (var);
+
+  /* When VAR has the `heap_allocated' attribute, we know it has a complete
+     array type.  */
+
+  if (heap_p
+      || automatic_array_variable_p (registered_attribute_name, var))
+    {
+      /* FIXME: This warning cannot be emitted here, because the
+	 `heap_allocated' attribute may be processed later.  */
+      /* if (!heap_p */
+      /* 	  && !MAIN_NAME_P (DECL_NAME (current_function_decl))) */
+      /* 	warning_at (loc, 0, "using an on-stack array as a task input " */
+      /* 		    "considered unsafe"); */
+
+      tree ptr_type, heap_attr =
+	lookup_attribute (heap_allocated_orig_type_attribute_name,
+			  DECL_ATTRIBUTES (var));
+
+      if (heap_attr != NULL_TREE)
+	/* PTR is `heap_allocated' so use its original array type to
+	   determine its size.  */
+	ptr_type = TREE_VALUE (heap_attr);
+      else
+	ptr_type = TREE_TYPE (var);
+
+      tree count = array_type_element_count (loc, ptr_type);
+
+      add_stmt (build_data_register_call (loc, var, count));
+
+      push_cleanup (var,
+		    build_data_unregister_call (DECL_SOURCE_LOCATION (var),
+						var),
+		    false);
     }
 
   return NULL_TREE;
@@ -2252,6 +2346,17 @@ heap_allocated_p (const_tree var_decl)
 			   DECL_ATTRIBUTES (var_decl)) != NULL_TREE;
 }
 
+/* Return true when VAR_DECL has the `registered' attribute.  */
+
+static bool
+registered_p (const_tree var_decl)
+{
+  gcc_assert (TREE_CODE (var_decl) == VAR_DECL);
+
+  return lookup_attribute (registered_attribute_name,
+			   DECL_ATTRIBUTES (var_decl)) != NULL_TREE;
+}
+
 /* Return true if TYPE is `output'-qualified.  */
 
 static bool
@@ -2306,6 +2411,12 @@ register_task_attributes (void *gcc_data, void *user_data)
 #endif
     };
 
+  static const struct attribute_spec registered_attr =
+    {
+      registered_attribute_name, 0, 0, true, false, false,
+      handle_registered_attribute
+    };
+
   static const struct attribute_spec output_attr =
     {
       output_attribute_name, 0, 0, true, true, false,
@@ -2318,6 +2429,7 @@ register_task_attributes (void *gcc_data, void *user_data)
   register_attribute (&task_attr);
   register_attribute (&task_implementation_attr);
   register_attribute (&heap_allocated_attr);
+  register_attribute (&registered_attr);
   register_attribute (&output_attr);
 }
 

+ 2 - 0
gcc-plugin/tests/Makefile.am

@@ -21,6 +21,8 @@ gcc_tests =					\
   output-pointer-errors.c			\
   register.c					\
   register-errors.c				\
+  registered.c					\
+  registered-errors.c				\
   acquire.c					\
   acquire-errors.c				\
   release.c					\

+ 9 - 1
gcc-plugin/tests/mocks.h

@@ -296,7 +296,15 @@ starpu_vector_data_register (starpu_data_handle_t *handle,
 			     uint32_t home_node, uintptr_t ptr,
 			     uint32_t count, size_t elemsize)
 {
-  assert ((void *) ptr == expected_register_arguments.pointer);
+  /* Sometimes tests cannot tell what the pointer will be (for instance, for
+     the `registered' attribute), and thus pass NULL as the expected
+     pointer.  */
+  if (expected_register_arguments.pointer != NULL)
+    assert ((void *) ptr == expected_register_arguments.pointer);
+  else
+    /* Allow users to check the pointer afterward.  */
+    expected_register_arguments.pointer = (void *) ptr;
+
   assert (count == expected_register_arguments.elements);
   assert (elemsize == expected_register_arguments.element_size);
 

+ 36 - 0
gcc-plugin/tests/registered-errors.c

@@ -0,0 +1,36 @@
+/* GCC-StarPU
+   Copyright (C) 2012 Institut National de Recherche en Informatique et Automatique
+
+   GCC-StarPU is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   GCC-StarPU is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC-StarPU.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* (instructions compile (cflags "-Wno-unused-variable")) */
+
+static int global[123]              /* (error "cannot be used") */
+  __attribute__ ((registered, used));
+
+extern int external[123]            /* (error "cannot be used") */
+  __attribute__ ((registered));
+
+void
+foo (size_t size)
+{
+  float scalar /* (error "must have an array type") */
+    __attribute__ ((registered));
+  float *ptr   /* (error "must have an array type") */
+    __attribute__ ((registered));
+  float incomp[]  /* (error "incomplete array type") */
+    __attribute__ ((registered));
+  float incomp2[size][3][]  /* (error "incomplete element type") */
+    __attribute__ ((registered));
+}

+ 128 - 0
gcc-plugin/tests/registered.c

@@ -0,0 +1,128 @@
+/* GCC-StarPU
+   Copyright (C) 2012 Institut National de Recherche en Informatique et Automatique
+
+   GCC-StarPU is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   GCC-StarPU is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC-StarPU.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#undef NDEBUG
+
+#include <mocks.h>
+#include <stdlib.h>
+
+static void
+test_vec (void)
+{
+  data_register_calls = data_unregister_calls = 0;
+  expected_register_arguments.pointer = NULL;
+  expected_register_arguments.elements = 123;
+  expected_register_arguments.element_size = sizeof (float);
+
+  float vec[123]		    /* FIXME: warning: "considered unsafe" */
+    __attribute__ ((registered));
+
+  assert (data_register_calls == 1);
+  assert (expected_register_arguments.pointer == vec);
+
+  expected_unregister_arguments.pointer = vec;
+}
+
+static void
+test_matrix (void)
+{
+  data_register_calls = data_unregister_calls = 0;
+  expected_register_arguments.pointer = NULL;
+  expected_register_arguments.elements = 123;
+  expected_register_arguments.element_size = 234 * sizeof (double);
+
+  double matrix[123][234]	     /* FIXME: warning "considered unsafe" */
+    __attribute__ ((registered));
+
+  assert (data_register_calls == 1);
+  assert (expected_register_arguments.pointer == matrix);
+
+  expected_unregister_arguments.pointer = matrix;
+}
+
+static void
+test_with_heap_alloc (void)
+{
+  data_register_calls = data_unregister_calls = 0;
+  malloc_calls = free_calls = 0;
+
+  expected_register_arguments.pointer = NULL;
+  expected_register_arguments.elements = 123;
+  expected_register_arguments.element_size =
+     234 * 77 * sizeof (int);
+  expected_malloc_argument =
+    expected_register_arguments.elements
+    * expected_register_arguments.element_size;
+
+  int matrix[123][234][77]
+    __attribute__ ((registered, heap_allocated));
+
+  assert (data_register_calls == 1);
+  assert (expected_register_arguments.pointer == matrix);
+  assert (malloc_calls == 1);
+
+  expected_unregister_arguments.pointer = matrix;
+  expected_free_argument = matrix;
+}
+
+/* Same as above, but with the attributes in reverse order.  */
+static void
+test_with_heap_alloc_reversed (void)
+{
+  data_register_calls = data_unregister_calls = 0;
+  malloc_calls = free_calls = 0;
+
+  expected_register_arguments.pointer = NULL;
+  expected_register_arguments.elements = 123;
+  expected_register_arguments.element_size =
+     234 * 77 * sizeof (int);
+  expected_malloc_argument =
+    expected_register_arguments.elements
+    * expected_register_arguments.element_size;
+
+  int matrix[123][234][77]
+    __attribute__ ((heap_allocated, registered));
+
+  assert (data_register_calls == 1);
+  assert (expected_register_arguments.pointer == matrix);
+  assert (malloc_calls == 1);
+
+  expected_unregister_arguments.pointer = matrix;
+  expected_free_argument = matrix;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+#pragma starpu initialize
+
+  test_vec ();
+  assert (data_unregister_calls == 1);
+
+  test_matrix ();
+  assert (data_unregister_calls == 1);
+
+  test_with_heap_alloc ();
+  assert (data_unregister_calls == 1);
+  assert (free_calls == 1);
+
+  test_with_heap_alloc_reversed ();
+  assert (data_unregister_calls == 1);
+  assert (free_calls == 1);
+
+  return EXIT_SUCCESS;
+}