Forráskód Böngészése

gcc: Interpret the body of a task as its implicit CPU implementation.

* gcc-plugin/src/starpu.c (add_task_implementation): Mark FN as used.
  (handle_task_implementation_attribute): When TASK_DECL matches
  `implicit_cpu_task_implementation_p', get its actual task.
  (task_implementation_task): Likewise.
  (implicit_cpu_task_implementation_p, build_cpu_codelet_identifier):
  New functions.
  (handle_pre_genericize): Instead of raising an error when FN has a
  body, treat it as an implicit "cpu" implementation of the
  corresponding task, rename it, and build a new task declaration under
  its original name.
  After the `define_task' call, mark TASK as needed.
  (define_task): Mark TASK_DECL as artificial.
  (lower_starpu): Rewrite calls to CALLEE_DECL when it matches
  `implicit_cpu_task_implementation_p' and CALLER_DECL is not
  artificial.

* gcc-plugin/tests/base.c (my_task_with_body, my_task_with_body_opencl):
  New functions.
  (main): Call it.
* gcc-plugin/tests/lib-user.c (main): Use `signed char' instead of `char'.
* gcc-plugin/tests/my-lib.c (my_task_cpu): Rename to...
  (my_task): ... this.
  (my_task_opencl): Make public.
* gcc-plugin/tests/my-lib.h (my_task): Use `signed char' instead of
  `char'.  Update implementations accordingly.
  (my_task_cpu): Remove declaration.
  (my_task_opencl): New declaration.
* gcc-plugin/tests/pointer-tasks.c (my_pointer_task_cpu): Rename to...
  (my_pointer_task): ... this.
* gcc-plugin/tests/scalar-tasks.c (my_scalar_task_cpu): Rename to...
  (my_scalar_task): ... this.
* gcc-plugin/tests/task-errors.c (my_task_with_body): Remove.
  (my_task): Expect "none of the implementations" error.
Ludovic Courtès 13 éve
szülő
commit
fb8edd8ba9

+ 128 - 10
gcc-plugin/src/starpu.c

@@ -133,11 +133,15 @@ static tree unpack_fn, data_lookup_fn;
 /* Forward declarations.  */
 
 static tree build_codelet_declaration (tree task_decl);
+static tree build_cpu_codelet_identifier (const_tree task);
 static void define_task (tree task_decl);
 static tree build_pointer_lookup (tree pointer);
 
 static bool task_p (const_tree decl);
 static bool task_implementation_p (const_tree decl);
+static tree task_implementation_task (const_tree task_impl);
+static bool implicit_cpu_task_implementation_p (const_tree fn);
+
 
 static int task_implementation_target_to_int (const_tree target);
 
@@ -1165,7 +1169,7 @@ add_task_implementation (tree task_decl, tree fn, const_tree where)
   impls = tree_cons (NULL_TREE, fn, TREE_VALUE (attr));
   TREE_VALUE (attr) = impls;
 
-  TREE_USED (fn) = TREE_USED (task_decl);
+  TREE_USED (fn) = true;
 
   /* Check the `where' argument to raise a warning if needed.  */
   if (task_implementation_target_to_int (where) == 0)
@@ -1203,6 +1207,12 @@ handle_task_implementation_attribute (tree *node, tree name, tree args,
   where = TREE_VALUE (args);
   task_decl = TREE_VALUE (TREE_CHAIN (args));
 
+  if (implicit_cpu_task_implementation_p (task_decl))
+    /* TASK_DECL is actually a CPU implementation.  Implicit CPU task
+       implementations can lead to this situation, because the task is
+       renamed and modified to become a CPU implementation.  */
+    task_decl = task_implementation_task (task_decl);
+
   loc = DECL_SOURCE_LOCATION (fn);
 
   /* Get rid of the `task_implementation' attribute by default so that FN
@@ -1480,7 +1490,7 @@ task_where (const_tree task_decl)
 static tree
 task_implementation_task (const_tree task_impl)
 {
-  tree impl_attr, args;
+  tree impl_attr, args, task;
 
   gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
 
@@ -1490,7 +1500,13 @@ task_implementation_task (const_tree task_impl)
 
   args = TREE_VALUE (impl_attr);
 
-  return TREE_VALUE (TREE_CHAIN (args));
+  task = TREE_VALUE (TREE_CHAIN (args));
+  if (task_implementation_p (task))
+    /* TASK is an implicit CPU task implementation, so return its real
+       task.  */
+    return task_implementation_task (task);
+
+  return task;
 }
 
 /* Return the FUNCTION_DECL of the wrapper generated for TASK_IMPL.  */
@@ -1509,6 +1525,23 @@ task_implementation_wrapper (const_tree task_impl)
   return TREE_VALUE (attr);
 }
 
+/* Return true when FN is an implicit CPU task implementation.  */
+
+static bool
+implicit_cpu_task_implementation_p (const_tree fn)
+{
+  if (task_implementation_p (fn)
+      && task_implementation_where (fn) == STARPU_CPU)
+    {
+      /* XXX: Hackish heuristic.  */
+      const_tree cpu_id;
+      cpu_id = build_cpu_codelet_identifier (task_implementation_task (fn));
+      return cpu_id == DECL_NAME (fn);
+    }
+
+  return false;
+}
+
 /* Return true when VAR_DECL has the `heap_allocated' attribute.  */
 
 static bool
@@ -2088,6 +2121,27 @@ declare_codelet (tree task_decl)
   return cl_decl;
 }
 
+/* Return the identifier for an automatically-generated CPU codelet of
+   TASK.  */
+
+static tree
+build_cpu_codelet_identifier (const_tree task)
+{
+  static const char suffix[] = ".cpu_implementation";
+
+  tree id;
+  char *cl_name;
+  const char *task_name;
+
+  id = DECL_NAME (task);
+  task_name = IDENTIFIER_POINTER (id);
+
+  cl_name = (char *) alloca (IDENTIFIER_LENGTH (id) + strlen (suffix) + 1);
+  memcpy (cl_name, task_name, IDENTIFIER_LENGTH (id));
+  strcpy (&cl_name[IDENTIFIER_LENGTH (id)], suffix);
+
+  return get_identifier (cl_name);
+}
 
 static void
 handle_pre_genericize (void *gcc_data, void *user_data)
@@ -2097,10 +2151,56 @@ handle_pre_genericize (void *gcc_data, void *user_data)
   gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
 
   if (task_p (fn) && TREE_STATIC (fn))
-    /* The user defined a body for task FN, which is forbidden.  */
-    error_at (DECL_SOURCE_LOCATION (fn),
-	      "task %qE must not have a body", DECL_NAME (fn));
-  else if (task_implementation_p (fn))
+    {
+      /* The user defined a body for task FN, which we interpret as being the
+	 body of an implicit CPU task implementation for FN.  Thus, rename FN
+	 and turn it into the "cpu" implementation of a task that we create
+	 under FN's original name (this is easier than moving the body to a
+	 different function, which would require traversing the body to
+	 rewrite all references to FN to point to the new function.)  Later,
+	 `lower_starpu' rewrites calls to FN as calls to the newly created
+	 task.  */
+
+      tree task_name = DECL_NAME (fn);
+
+      tree cpu_impl = fn;
+      DECL_NAME (cpu_impl) = build_cpu_codelet_identifier (fn);
+      if (verbose_output_p)
+	inform (DECL_SOURCE_LOCATION (fn),
+		"implicit CPU implementation renamed from %qE to %qE",
+		task_name, DECL_NAME (cpu_impl));
+
+      tree task = build_decl (DECL_SOURCE_LOCATION (fn), FUNCTION_DECL,
+			      task_name, TREE_TYPE (fn));
+
+      TREE_PUBLIC (task) = TREE_PUBLIC (fn);
+      TREE_PUBLIC (cpu_impl) = false;
+
+      taskify_function (task);
+
+      /* Inherit the task implementation list from FN.  */
+      tree impls = lookup_attribute (task_implementation_list_attribute_name,
+				     DECL_ATTRIBUTES (fn));
+      gcc_assert (impls != NULL_TREE);
+      impls = TREE_VALUE (impls);
+
+      DECL_ATTRIBUTES (task) =
+	tree_cons (get_identifier (task_implementation_list_attribute_name),
+		   impls, DECL_ATTRIBUTES (task));
+
+      /* Make CPU_IMPL an implementation of FN.  */
+      DECL_ATTRIBUTES (cpu_impl) =
+	tree_cons (get_identifier (task_implementation_attribute_name),
+		   tree_cons (NULL_TREE, build_string (3, "cpu"),
+			      tree_cons (NULL_TREE, task, NULL_TREE)),
+		   NULL_TREE);
+
+      add_task_implementation (task, cpu_impl, build_string (3, "cpu"));
+
+      /* And now, process CPU_IMPL.  */
+    }
+
+  if (task_implementation_p (fn))
     {
       tree task = task_implementation_task (fn);
 
@@ -2142,6 +2242,7 @@ handle_pre_genericize (void *gcc_data, void *user_data)
 	  rest_of_decl_compilation (task, true, 0);
 	  allocate_struct_function (task, false);
 	  cgraph_finalize_function (task, false);
+	  cgraph_mark_needed_node (cgraph_get_node (task));
 	}
     }
 }
@@ -2290,6 +2391,7 @@ define_task (tree task_decl)
   DECL_SAVED_TREE (task_decl) = bind;
   TREE_STATIC (task_decl) = true;
   DECL_EXTERNAL (task_decl) = false;
+  DECL_ARTIFICIAL (task_decl) = true;
   DECL_INITIAL (task_decl) =
     build_block (error_var, NULL_TREE, task_decl, NULL_TREE);
   DECL_RESULT (task_decl) =
@@ -2423,12 +2525,28 @@ lower_starpu (void)
     {
       gcc_assert (callee->callee != NULL);
 
-      tree callee_decl;
+      tree callee_decl, caller_decl;
 
       callee_decl = callee->callee->decl;
+      caller_decl = callee->caller->decl;
+
+      if (implicit_cpu_task_implementation_p (callee_decl)
+	  && !DECL_ARTIFICIAL (caller_decl))
+	{
+	  /* Rewrite the call to point to the actual task beneath
+	     CALLEE_DECL.  */
+	  callee_decl = task_implementation_task (callee_decl);
+	  if (verbose_output_p)
+	    inform (gimple_location (callee->call_stmt),
+		    "call to %qE rewritten as a call to task %qE",
+		    DECL_NAME (callee->callee->decl),
+		    DECL_NAME (callee_decl));
+
+	  gimple_call_set_fn (callee->call_stmt,
+			      build_addr (callee_decl, callee->caller->decl));
+	}
 
-      if (lookup_attribute (task_attribute_name,
-			    DECL_ATTRIBUTES (callee_decl)))
+      if (task_p (callee_decl))
 	{
 	  if (verbose_output_p)
 	    inform (gimple_location (callee->call_stmt),

+ 22 - 1
gcc-plugin/tests/base.c

@@ -72,7 +72,26 @@ my_other_task_opencl (int x)
   printf ("opencl\n");
 }
 
+
+/* Task with a body.  */
+
+static void my_task_with_body (int x) __attribute__ ((task));
+
+static void
+my_task_with_body (int x)
+{
+  /* This body is implicitly the "cpu" implementation of the task.  */
+  int y = x + 2;
+  printf ("body: %i\n", y - 2);
+}
 
+static void my_task_with_body_opencl (int x)
+  __attribute__ ((task_implementation ("opencl", my_task_with_body)));
+
+static void
+my_task_with_body_opencl (int x)
+{
+}
 
 
 int
@@ -124,7 +143,9 @@ main (int argc, char *argv[])
 
   my_other_task (42);
 
-  assert (tasks_submitted == 10);
+  my_task_with_body (42);
+
+  assert (tasks_submitted == 11);
 
 #pragma starpu shutdown
   assert (initialized == 0);

+ 3 - 2
gcc-plugin/tests/lib-user.c

@@ -1,5 +1,5 @@
 /* GCC-StarPU
-   Copyright (C) 2011 Institut National de Recherche en Informatique et Automatique
+   Copyright (C) 2011, 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
@@ -30,7 +30,8 @@ main (int argc, char *argv[])
 
   /* Align X so that the assumptions behind `dummy_pointer_to_handle'
      hold.  */
-  static const char x[] __attribute__ ((aligned (8))) = { 0, 1, 2, 3, 4, 5 };
+  static const signed char x[] __attribute__ ((aligned (8))) =
+    { 0, 1, 2, 3, 4, 5 };
 
   float y[sizeof x];
 

+ 5 - 7
gcc-plugin/tests/my-lib.c

@@ -1,5 +1,5 @@
 /* GCC-StarPU
-   Copyright (C) 2011 Institut National de Recherche en Informatique et Automatique
+   Copyright (C) 2011, 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
@@ -24,11 +24,9 @@
 /* Task implementations: one is `static', one is global.  The codelet
    wrapper, codelet, and task body should be instantiated in this file.  */
 
-static void my_task_opencl (char, const char *, float *, int)
-  __attribute__ ((task_implementation ("opencl", my_task)));
-
+/* The implicit CPU implementation of `my_task'.  */
 void
-my_task_cpu (char a, const char *p, float *q, int b)
+my_task (signed char a, const signed char *p, float *q, int b)
 {
   int i;
 
@@ -36,8 +34,8 @@ my_task_cpu (char a, const char *p, float *q, int b)
     *q = *p + a;
 }
 
-static void
-my_task_opencl (char a, const char *p, float *q, int b)
+void
+my_task_opencl (signed char a, const signed char *p, float *q, int b)
 {
   int i;
 

+ 4 - 4
gcc-plugin/tests/my-lib.h

@@ -1,5 +1,5 @@
 /* GCC-StarPU
-   Copyright (C) 2011 Institut National de Recherche en Informatique et Automatique
+   Copyright (C) 2011, 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
@@ -19,12 +19,12 @@
 #ifndef MY_LIB_H
 #define MY_LIB_H
 
-extern void my_task (char, const char *, float *, int)
+extern void my_task (signed char, const signed char *, float *, int)
   __attribute__ ((task));
 
 /* One of the implementations of MY_TASK.  Since it's `extern', this should
    not trigger generation of the codelet, wrapper, etc.  */
-extern void my_task_cpu (char, const char *, float *, int)
-  __attribute__ ((task_implementation ("cpu", my_task)));
+extern void my_task_opencl (signed char, const signed char *, float *, int)
+  __attribute__ ((task_implementation ("opencl", my_task)));
 
 #endif /* MY_LIB_H */

+ 3 - 5
gcc-plugin/tests/pointer-tasks.c

@@ -1,5 +1,5 @@
 /* GCC-StarPU
-   Copyright (C) 2011 Institut National de Recherche en Informatique et Automatique
+   Copyright (C) 2011, 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
@@ -28,17 +28,15 @@
 static void my_pointer_task (const int *x, char a, long long *y, int b)
   __attribute__ ((task));
 
-static void my_pointer_task_cpu (const int *x, char a, long long *y, int b)
-  __attribute__ ((task_implementation ("cpu", my_pointer_task), noinline));
-
 static int implementations_called;
 
 /* The input arguments.  */
 static const int pointer_arg1[] = { 42, 1, 2, 3, 4, 5 };
 static long long *pointer_arg2;
 
+/* CPU implementation of `my_pointer_task'.  */
 static void
-my_pointer_task_cpu (const int *x, char a, long long *y, int b)
+my_pointer_task (const int *x, char a, long long *y, int b)
 {
   implementations_called |= STARPU_CPU;
   assert (x == pointer_arg1);

+ 3 - 4
gcc-plugin/tests/scalar-tasks.c

@@ -1,5 +1,5 @@
 /* GCC-StarPU
-   Copyright (C) 2011 Institut National de Recherche en Informatique et Automatique
+   Copyright (C) 2011, 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
@@ -26,15 +26,14 @@
 
 static void my_scalar_task (int x, int y) __attribute__ ((task));
 
-static void my_scalar_task_cpu (int, int)
-  __attribute__ ((task_implementation ("cpu", my_scalar_task), noinline));
 static void my_scalar_task_opencl (int, int)
   __attribute__ ((task_implementation ("opencl", my_scalar_task), noinline));
 
 static int implementations_called;
 
+/* CPU implementation of `my_scalar_task'.  */
 static void
-my_scalar_task_cpu (int x, int y)
+my_scalar_task (int x, int y)
 {
   implementations_called |= STARPU_CPU;
   assert (x == 42);

+ 2 - 9
gcc-plugin/tests/task-errors.c

@@ -18,7 +18,8 @@
 
 extern void my_external_task (int foo, char *bar) __attribute__ ((task));
 
-static void my_task (int foo, char *bar) __attribute__ ((task));
+void my_task (int foo, char *bar) /* (error "none of the implementations") */
+  __attribute__ ((task));
 static void my_task_cpu (int foo, float *bar)    /* (error "type differs") */
   __attribute__ ((task_implementation ("cpu", my_task)));
 
@@ -48,9 +49,6 @@ static void my_task_wrong_task_arg (int foo, char *bar)   /* (error "not a funct
 static void my_task_wrong_target_arg (int foo, char *bar) /* (error "string constant expected") */
   __attribute__ ((task_implementation (123, my_task)));
 
-static void my_task_with_a_body (int foo, char *bar)
-  __attribute__ ((task, unused));
-
 extern int my_task_not_void (int foo) /* (error "return type") */
   __attribute__ ((task));
 
@@ -116,11 +114,6 @@ my_task_wrong_target_arg (int foo, char *bar)
 {
 }
 
-static void
-my_task_with_a_body (int foo, char *bar)  /* (error "must not have a body") */
-{
-}
-
 void
 my_task_that_invokes_task_cpu (int x, char *y)
 {