/* GCC-StarPU
   Copyright (C) 2011 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 .  */
/* Use extensions of the GNU C Library.  */
#define _GNU_SOURCE 1
#include 
int plugin_is_GPL_compatible;
/* #define ENABLE_TREE_CHECKING 1 */
#include 
#include 
#include 
#include 
#include 
#include 
#ifdef HAVE_C_FAMILY_C_COMMON_H
# include 
#elif HAVE_C_COMMON_H
# include 
#endif
#ifdef HAVE_C_FAMILY_C_PRAGMA_H
# include 
#elif HAVE_C_PRAGMA_H
# include 
#endif
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
/* Don't include the dreaded proprietary headers that we don't need anyway.
   In particular, this waives the obligation to reproduce their silly
   disclaimer.  */
#define STARPU_DONT_INCLUDE_CUDA_HEADERS
#include   /* for `STARPU_CPU' & co.  */
/* The name of this plug-in.  */
static const char plugin_name[] = "starpu";
/* Names of public attributes.  */
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";
/* Names of attributes used internally.  */
static const char task_codelet_attribute_name[] = ".codelet";
static const char task_implementation_list_attribute_name[] =
  ".task_implementation_list";
static const char task_implementation_wrapper_attribute_name[] =
  ".task_implementation_wrapper";
/* Names of data structures defined in .  */
static const char codelet_struct_name[] = "starpu_codelet_gcc";
/* Cached function declarations.  */
static tree unpack_fn;
/* Forward declarations.  */
static tree build_codelet_declaration (tree task_decl);
static tree build_task_body (const_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 int task_implementation_target_to_int (const_tree target);
/* Lookup the StarPU function NAME in the global scope and store the result
   in VAR (this can't be done from `lower_starpu'.)  */
#define LOOKUP_STARPU_FUNCTION(var, name)				\
  if ((var) == NULL_TREE)						\
    {									\
      (var) = lookup_name (get_identifier (name));			\
      gcc_assert ((var) != NULL_TREE && TREE_CODE (var) == FUNCTION_DECL); \
    }
/* Useful code backported from GCC 4.6.  */
#if !HAVE_DECL_BUILD_CALL_EXPR_LOC_ARRAY
static tree
build_call_expr_loc_array (location_t loc, tree fndecl, int n, tree *argarray)
{
  tree fntype = TREE_TYPE (fndecl);
  tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
  return fold_builtin_call_array (loc, TREE_TYPE (fntype), fn, n, argarray);
}
#endif
#if !HAVE_DECL_BUILD_CALL_EXPR_LOC_VEC
static tree
build_call_expr_loc_vec (location_t loc, tree fndecl, VEC(tree,gc) *vec)
{
  return build_call_expr_loc_array (loc, fndecl, VEC_length (tree, vec),
				    VEC_address (tree, vec));
}
#endif
/* Helpers.  */
/* Build a reference to the INDEXth element of ARRAY.  `build_array_ref' is
   not exported, so we roll our own.
   FIXME: This version may not work for array types and doesn't do as much
   type-checking as `build_array_ref'.  */
static tree
array_ref (tree array, size_t index)
{
  gcc_assert (POINTER_TYPE_P (TREE_TYPE (array)));
  tree pointer_plus_offset =
    index > 0
    ? build_binary_op (UNKNOWN_LOCATION, PLUS_EXPR,
		       array,
		       build_int_cstu (integer_type_node, index),
		       0)
    : array;
  gcc_assert (POINTER_TYPE_P (TREE_TYPE (pointer_plus_offset)));
  return build_indirect_ref (UNKNOWN_LOCATION,
			     pointer_plus_offset,
			     RO_ARRAY_INDEXING);
}
/* Like `build_constructor_from_list', but sort VALS according to their
   offset in struct TYPE.  Inspired by `gnat_build_constructor'.  */
static tree
build_constructor_from_unsorted_list (tree type, tree vals)
{
  int compare_elmt_bitpos (const void *rt1, const void *rt2)
  {
    const constructor_elt *elmt1 = (constructor_elt *) rt1;
    const constructor_elt *elmt2 = (constructor_elt *) rt2;
    const_tree field1 = elmt1->index;
    const_tree field2 = elmt2->index;
    int ret
      = tree_int_cst_compare (bit_position (field1), bit_position (field2));
    return ret ? ret : (int) (DECL_UID (field1) - DECL_UID (field2));
  }
  tree t;
  VEC(constructor_elt,gc) *v = NULL;
  if (vals)
    {
      v = VEC_alloc (constructor_elt, gc, list_length (vals));
      for (t = vals; t; t = TREE_CHAIN (t))
	CONSTRUCTOR_APPEND_ELT (v, TREE_PURPOSE (t), TREE_VALUE (t));
    }
  /* Sort field initializers by field offset.  */
  VEC_qsort (constructor_elt, v, compare_elmt_bitpos);
  return build_constructor (type, v);
}
/* Return true if LST holds the void type.  */
bool
void_type_p (const_tree lst)
{
  gcc_assert (TREE_CODE (lst) == TREE_LIST);
  return VOID_TYPE_P (TREE_VALUE (lst));
}
/* Debugging helpers.  */
static tree build_printf (const char *, ...)
  __attribute__ ((format (printf, 1, 2)));
static tree
build_printf (const char *fmt, ...)
{
  tree call;
  char *str;
  va_list args;
  va_start (args, fmt);
  vasprintf (&str, fmt, args);
  call = build_call_expr (built_in_decls[BUILT_IN_PUTS], 1,
	 		  build_string_literal (strlen (str) + 1, str));
  free (str);
  va_end (args);
  return call;
}
static tree
build_hello_world (void)
{
  return build_printf ("Hello, StarPU!");
}
/* List and vector utilities, à la SRFI-1.  */
static tree chain_trees (tree t, ...)
  __attribute__ ((sentinel));
static tree
chain_trees (tree t, ...)
{
  va_list args;
  va_start (args, t);
  tree next, prev = t;
  for (prev = t, next = va_arg (args, tree);
       next != NULL_TREE;
       prev = next, next = va_arg (args, tree))
    TREE_CHAIN (prev) = next;
  va_end (args);
  return t;
}
static tree
filter (bool (*pred) (const_tree), tree t)
{
  tree result, lst;
  gcc_assert (TREE_CODE (t) == TREE_LIST);
  result = NULL_TREE;
  for (lst = t; lst != NULL_TREE; lst = TREE_CHAIN (lst))
    {
      if (pred (lst))
	result = tree_cons (TREE_PURPOSE (lst), TREE_VALUE (lst),
			    result);
    }
  return nreverse (result);
}
static tree
list_remove (bool (*pred) (const_tree), tree t)
{
  bool opposite (const_tree t)
  {
    return !pred (t);
  }
  return filter (opposite, t);
}
/* Map FUNC over chain T.  T does not have to be `TREE_LIST'; it can be a
   chain of arbitrary tree objects.  */
static tree
map (tree (*func) (const_tree), tree t)
{
  tree result, tail, lst;
  result = tail = NULL_TREE;
  for (lst = t; lst != NULL_TREE; lst = TREE_CHAIN (lst))
    {
      tree r = func (lst);
      if (tail != NULL_TREE)
	TREE_CHAIN (tail) = r;
      else
	result = r;
      tail = r;
    }
  return result;
}
static void
for_each (void (*func) (tree), tree t)
{
  tree lst;
  gcc_assert (TREE_CODE (t) == TREE_LIST);
  for (lst = t; lst != NULL_TREE; lst = TREE_CHAIN (lst))
    func (TREE_VALUE (lst));
}
/* Pragmas.  */
#define STARPU_PRAGMA_NAME_SPACE "starpu"
static void
handle_pragma_hello (struct cpp_reader *reader)
{
  add_stmt (build_hello_world ());
}
/* Process `#pragma starpu initialize'.
   TODO: Parse and initialize some of the fields of `starpu_conf'.  */
static void
handle_pragma_initialize (struct cpp_reader *reader)
{
  static tree init_fn;
  LOOKUP_STARPU_FUNCTION (init_fn, "starpu_init");
  /* Call `starpu_init (NULL)'.  */
  tree init = build_call_expr (init_fn, 1, build_zero_cst (ptr_type_node));
  add_stmt (init);
}
/* Process `#pragma starpu shutdown'.  */
static void
handle_pragma_shutdown (struct cpp_reader *reader)
{
  static tree shutdown_fn;
  LOOKUP_STARPU_FUNCTION (shutdown_fn, "starpu_shutdown");
  tree token;
  if (pragma_lex (&token) != CPP_EOF)
    error_at (cpp_peek_token (reader, 0)->src_loc,
	      "junk after % pragma");
  else
    /* Call `starpu_shutdown ()'.  */
    add_stmt (build_call_expr (shutdown_fn, 0));
}
static void
handle_pragma_wait (struct cpp_reader *reader)
{
  if (task_implementation_p (current_function_decl))
    {
      location_t loc;
      loc = cpp_peek_token (reader, 0)->src_loc;
      /* TODO: In the future we could generate a task for the continuation
	 and have it depend on what's before here.  */
      error_at (loc, "task implementation is not allowed to wait");
    }
  else
    {
      tree fndecl;
      fndecl = lookup_name (get_identifier ("starpu_task_wait_for_all"));
      gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL);
      add_stmt (build_call_expr (fndecl, 0));
    }
}
/* The minimal C expression parser.  */
extern int yyparse (location_t, const char *, tree *);
extern int yydebug;
/* Parse expressions from the CPP reader for PRAGMA, which is located at LOC.
   Return a TREE_LIST of C expressions.  */
static tree
read_pragma_expressions (const char *pragma, location_t loc)
{
  tree expr = NULL_TREE;
  if (yyparse (loc, pragma, &expr))
    /* Parse error or memory exhaustion.  */
    expr = NULL_TREE;
  return expr;
}
/* Process `#pragma starpu register VAR [COUNT]' and emit the corresponding
   `starpu_vector_data_register' call.  */
static void
handle_pragma_register (struct cpp_reader *reader)
{
  tree args, ptr, count_arg;
  location_t loc;
  loc = cpp_peek_token (reader, 0)->src_loc;
  args = read_pragma_expressions ("register", loc);
  if (args == NULL_TREE)
    /* Parse error, presumably already handled by the parser.  */
    return;
  /* First argument should be a pointer expression.  */
  ptr = TREE_VALUE (args);
  args = TREE_CHAIN (args);
  if (ptr == error_mark_node)
    return;
  if (!POINTER_TYPE_P (TREE_TYPE (ptr))
      && TREE_CODE (TREE_TYPE (ptr)) != ARRAY_TYPE)
    {
      error_at (loc, "%qE is neither a pointer nor an array", ptr);
      return;
    }
  TREE_USED (ptr) = true;
  if (DECL_P (ptr))
    DECL_READ_P (ptr) = true;
  if (TREE_CODE (TREE_TYPE (ptr)) == ARRAY_TYPE
      && !DECL_EXTERNAL (ptr)
      && !TREE_STATIC (ptr)
      && !MAIN_NAME_P (DECL_NAME (current_function_decl)))
    warning_at (loc, 0, "using an on-stack array as a task input "
		"considered unsafe");
  /* Determine the number of elements in the vector.  */
  tree count = NULL_TREE;
  if (TREE_CODE (TREE_TYPE (ptr)) == ARRAY_TYPE)
    {
      tree domain = TYPE_DOMAIN (TREE_TYPE (ptr));
      if (domain != NULL_TREE)
	{
	  count = build_binary_op (loc, MINUS_EXPR,
				   TYPE_MAX_VALUE (domain),
				   TYPE_MIN_VALUE (domain),
				   false);
	  count = build_binary_op (loc, PLUS_EXPR,
				   count,
				   build_int_cstu (integer_type_node, 1),
				   false);
	  count = fold_convert (size_type_node, count);
	}
    }
  /* Second argument is optional but should be an integer.  */
  count_arg = (args == NULL_TREE) ? NULL_TREE : TREE_VALUE (args);
  if (args != NULL_TREE)
    {
      args = TREE_CHAIN (args);
      TREE_CHAIN (count_arg) = NULL_TREE;
    }
  if (count_arg == NULL_TREE)
    {
      /* End of line reached: check whether the array size was
	 determined.  */
      if (count == NULL_TREE)
	{
	  error_at (loc, "cannot determine size of array %qE", ptr);
	  return;
	}
    }
  else if (count_arg == error_mark_node)
    /* COUNT_ARG could not be parsed and an error was already reported.  */
    return;
  else if (!INTEGRAL_TYPE_P (TREE_TYPE (count_arg)))
    {
      error_at (loc, "%qE is not an integer", count_arg);
      return;
    }
  else
    {
      TREE_USED (count_arg) = true;
      if (DECL_P (count_arg))
	DECL_READ_P (count_arg) = true;
      if (count != NULL_TREE)
	{
	  /* The number of elements of this array was already determined.  */
	  inform (loc,
		  "element count can be omitted for bounded array %qE",
		  ptr);
	  if (count_arg != NULL_TREE)
	    {
	      if (TREE_CODE (count_arg) == INTEGER_CST)
		{
		  if (!tree_int_cst_equal (count, count_arg))
		    error_at (loc, "specified element count differs "
			      "from actual size of array %qE",
			      ptr);
		}
	      else
		/* Using a variable to determine the array size whereas the
		   array size is actually known statically.  This looks like
		   unreasonable code, so error out.  */
		error_at (loc, "determining array size at run-time "
			  "although array size is known at compile-time");
	    }
	}
      else
	count = count_arg;
    }
  /* Any remaining args?  */
  if (args != NULL_TREE)
    error_at (loc, "junk after % pragma");
  /* If PTR is an array, take its address.  */
  tree pointer =
    POINTER_TYPE_P (TREE_TYPE (ptr))
    ? ptr
    : build_addr (ptr, current_function_decl);
  /* Introduce a local variable to hold the handle.  */
  tree handle_var = build_decl (loc, VAR_DECL, create_tmp_var_name (".handle"),
				ptr_type_node);
  DECL_CONTEXT (handle_var) = current_function_decl;
  DECL_ARTIFICIAL (handle_var) = true;
  DECL_INITIAL (handle_var) = NULL_TREE;
  tree register_fn =
    lookup_name (get_identifier ("starpu_vector_data_register"));
  /* Build `starpu_vector_data_register (&HANDLE_VAR, 0, POINTER,
                                         COUNT, sizeof *POINTER)'  */
  tree call =
    build_call_expr (register_fn, 5,
		     build_addr (handle_var, current_function_decl),
		     build_zero_cst (uintptr_type_node), /* home node */
		     pointer, count,
		     size_in_bytes (TREE_TYPE (TREE_TYPE (ptr))));
  tree bind;
  bind = build3 (BIND_EXPR, void_type_node, handle_var, call,
		 NULL_TREE);
  add_stmt (bind);
}
/* Process `#pragma starpu acquire VAR' and emit the corresponding
   `starpu_data_acquire' call.  */
static void
handle_pragma_acquire (struct cpp_reader *reader)
{
  static tree acquire_fn;
  LOOKUP_STARPU_FUNCTION (acquire_fn, "starpu_data_acquire");
  tree args, var;
  location_t loc;
  loc = cpp_peek_token (reader, 0)->src_loc;
  args = read_pragma_expressions ("acquire", loc);
  if (args == NULL_TREE)
    return;
  var = TREE_VALUE (args);
  if (var == error_mark_node)
    return;
  else if (TREE_CODE (TREE_TYPE (var)) != POINTER_TYPE
	   && TREE_CODE (TREE_TYPE (var)) != ARRAY_TYPE)
    {
      error_at (loc, "%qE is neither a pointer nor an array", var);
      return;
    }
  else if (TREE_CHAIN (var) != NULL_TREE)
    error_at (loc, "junk after % pragma");
  /* If VAR is an array, take its address.  */
  tree pointer =
    POINTER_TYPE_P (TREE_TYPE (var))
    ? var
    : build_addr (var, current_function_decl);
  /* Call `starpu_data_acquire (starpu_data_lookup (ptr), STARPU_RW)'.
     TODO: Support modes other than RW.  */
  add_stmt (build_call_expr (acquire_fn, 2,
			     build_pointer_lookup (pointer),
			     build_int_cst (integer_type_node, STARPU_RW)));
}
/* Process `#pragma starpu unregister VAR' and emit the corresponding
   `starpu_data_unregister' call.  */
static void
handle_pragma_unregister (struct cpp_reader *reader)
{
  static tree unregister_fn;
  LOOKUP_STARPU_FUNCTION (unregister_fn, "starpu_data_unregister");
  tree args, var;
  location_t loc;
  loc = cpp_peek_token (reader, 0)->src_loc;
  args = read_pragma_expressions ("unregister", loc);
  if (args == NULL_TREE)
    return;
  var = TREE_VALUE (args);
  if (var == error_mark_node)
    return;
  else if (TREE_CODE (TREE_TYPE (var)) != POINTER_TYPE
	   && TREE_CODE (TREE_TYPE (var)) != ARRAY_TYPE)
    {
      error_at (loc, "%qE is neither a pointer nor an array", var);
      return;
    }
  else if (TREE_CHAIN (args) != NULL_TREE)
    error_at (loc, "junk after % pragma");
  /* If VAR is an array, take its address.  */
  tree pointer =
    POINTER_TYPE_P (TREE_TYPE (var))
    ? var
    : build_addr (var, current_function_decl);
  /* Call `starpu_data_unregister (starpu_data_lookup (ptr))'.  */
  add_stmt (build_call_expr (unregister_fn, 1,
			     build_pointer_lookup (pointer)));
}
static void
register_pragmas (void *gcc_data, void *user_data)
{
  c_register_pragma (STARPU_PRAGMA_NAME_SPACE, "hello",
		     handle_pragma_hello);
  c_register_pragma_with_expansion (STARPU_PRAGMA_NAME_SPACE, "initialize",
				    handle_pragma_initialize);
  c_register_pragma (STARPU_PRAGMA_NAME_SPACE, "wait",
		     handle_pragma_wait);
  c_register_pragma_with_expansion (STARPU_PRAGMA_NAME_SPACE, "register",
				    handle_pragma_register);
  c_register_pragma_with_expansion (STARPU_PRAGMA_NAME_SPACE, "acquire",
				    handle_pragma_acquire);
  c_register_pragma_with_expansion (STARPU_PRAGMA_NAME_SPACE, "unregister",
				    handle_pragma_unregister);
  c_register_pragma (STARPU_PRAGMA_NAME_SPACE, "shutdown",
		     handle_pragma_shutdown);
}
/* Attributes.  */
/* Handle the `task' function attribute.  */
static tree
handle_task_attribute (tree *node, tree name, tree args,
		       int flags, bool *no_add_attrs)
{
  tree fn;
  fn = *node;
  /* Get rid of the `task' attribute by default so that FN isn't further
     processed when it's erroneous.  */
  *no_add_attrs = true;
  if (TREE_CODE (fn) != FUNCTION_DECL)
    error_at (DECL_SOURCE_LOCATION (fn),
	      "% attribute only applies to functions");
  else
    {
      if (!VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fn))))
	/* Raise an error but keep going to avoid spitting out too many
	   errors at the user's face.  */
	error_at (DECL_SOURCE_LOCATION (fn),
		  "task return type must be %");
      /* This is a function declaration for something local to this
	 translation unit, so add the `task' attribute to FN.  */
      *no_add_attrs = false;
      /* Add an empty `task_implementation_list' attribute.  */
      DECL_ATTRIBUTES (fn) =
	tree_cons (get_identifier (task_implementation_list_attribute_name),
		   NULL_TREE,
		   NULL_TREE);
      /* Push a declaration for the corresponding `struct starpu_codelet' object and
	 add it as an attribute of FN.  */
      tree cl = build_codelet_declaration (fn);
      DECL_ATTRIBUTES (fn) =
	tree_cons (get_identifier (task_codelet_attribute_name), cl,
		   DECL_ATTRIBUTES (fn));
      pushdecl (cl);
    }
  /* Lookup & cache function declarations for later reuse.  */
  LOOKUP_STARPU_FUNCTION (unpack_fn, "starpu_unpack_cl_args");
  return NULL_TREE;
}
/* Handle the `task_implementation (WHERE, TASK)' attribute.  WHERE is a
   string constant ("cpu", "cuda", etc.), and TASK is the identifier of a
   function declared with the `task' attribute.  */
static tree
handle_task_implementation_attribute (tree *node, tree name, tree args,
				      int flags, bool *no_add_attrs)
{
  location_t loc;
  tree fn, where, task_decl;
  /* FIXME:TODO: To change the order to (TASK, WHERE):
	  tree cleanup_id = TREE_VALUE (TREE_VALUE (attr));
	  tree cleanup_decl = lookup_name (cleanup_id);
  */
  fn = *node;
  where = TREE_VALUE (args);
  task_decl = TREE_VALUE (TREE_CHAIN (args));
  loc = DECL_SOURCE_LOCATION (fn);
  /* Get rid of the `task_implementation' attribute by default so that FN
     isn't further processed when it's erroneous.  */
  *no_add_attrs = true;
  /* Mark FN as used to placate `-Wunused-function' when FN is erroneous
     anyway.  */
  TREE_USED (fn) = true;
  if (TREE_CODE (fn) != FUNCTION_DECL)
    error_at (loc,
	      "% attribute only applies to functions");
  else if (TREE_CODE (where) != STRING_CST)
    error_at (loc, "string constant expected "
	      "as the first % argument");
  else if (TREE_CODE (task_decl) != FUNCTION_DECL)
    error_at (loc, "%qE is not a function", task_decl);
  else if (lookup_attribute (task_attribute_name,
			DECL_ATTRIBUTES (task_decl)) == NULL_TREE)
    error_at (loc, "function %qE lacks the % attribute",
	      DECL_NAME (task_decl));
  else if (TYPE_CANONICAL (TREE_TYPE (fn))
	   != TYPE_CANONICAL (TREE_TYPE (task_decl)))
    error_at (loc, "type differs from that of task %qE",
	      DECL_NAME (task_decl));
  else
    {
      /* Add FN to the list of implementations of TASK_DECL.  */
      tree attr, impls;
      attr = lookup_attribute (task_implementation_list_attribute_name,
			       DECL_ATTRIBUTES (task_decl));
      impls = tree_cons (NULL_TREE, fn, TREE_VALUE (attr));
      TREE_VALUE (attr) = impls;
      TREE_USED (fn) = TREE_USED (task_decl);
      /* Check the `where' argument to raise a warning if needed.  */
      if (task_implementation_target_to_int (where) == 0)
	warning_at (loc, 0,
		    "unsupported target %E; task implementation won't be used",
		    where);
      /* Keep the attribute.  */
      *no_add_attrs = false;
    }
  return NULL_TREE;
}
/* Handle the `heap_allocated' attribute on variable *NODE.  */
static tree
handle_heap_allocated_attribute (tree *node, tree name, tree args,
				 int flags, bool *no_add_attrs)
{
  location_t loc;
  tree var = *node;
  loc = DECL_SOURCE_LOCATION (var);
  if (DECL_EXTERNAL (var))
    error_at (loc, "attribute % cannot be used "
	      "on external declarations");
  else if (TREE_PUBLIC (var) || TREE_STATIC (var))
    {
      error_at (loc, "attribute % cannot be used "
		"on global variables");
      TREE_TYPE (var) = error_mark_node;
    }
  else if (TREE_CODE (TREE_TYPE (var)) != ARRAY_TYPE)
    {
      error_at (loc, "variable %qE must have an array type",
		DECL_NAME (var));
      TREE_TYPE (var) = error_mark_node;
    }
  else if (TYPE_SIZE (TREE_TYPE (var)) == NULL_TREE)
    {
      error_at (loc, "variable %qE has an incomplete array type",
		DECL_NAME (var));
      TREE_TYPE (var) = error_mark_node;
    }
  else
    {
      tree array_type = TREE_TYPE (var);
      tree pointer_type = build_pointer_type (strip_array_types (array_type));
      /* We want VAR to feel like an array, but to really be a pointer.  So
	 the hack consists in keeping its array type, but giving it the
	 storage of a pointer.  (XXX) */
      DECL_SIZE (var) = TYPE_SIZE (pointer_type);
      DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (pointer_type);
      DECL_ALIGN (var) = TYPE_ALIGN (pointer_type);
      DECL_USER_ALIGN (var) = false;
      DECL_MODE (var) = TYPE_MODE (pointer_type);
      tree malloc_fn = lookup_name (get_identifier ("starpu_malloc"));
      gcc_assert (malloc_fn != NULL_TREE);
      tree alloc = build_call_expr (malloc_fn, 2,
				    build_addr (var, current_function_decl),
				    TYPE_SIZE_UNIT (array_type));
      TREE_SIDE_EFFECTS (alloc) = true;
      add_stmt (alloc);
      /* Add a destructor for VAR.
	 TODO: Provide a way to disable this.  */
      DECL_ATTRIBUTES (var) =
	tree_cons (get_identifier ("cleanup"),
		   lookup_name (get_identifier ("starpu_free")),
		   DECL_ATTRIBUTES (var));
    }
  return NULL_TREE;
}
/* Handle the `output' attribute on type *NODE, which should be the type of a
   PARM_DECL of a task or task implementation.  */
static tree
handle_output_attribute (tree *node, tree name, tree args,
			 int flags, bool *no_add_attrs)
{
  tree type = *node;
  gcc_assert (TYPE_P (type));
  if (!POINTER_TYPE_P (type) && TREE_CODE (type) != ARRAY_TYPE)
    error ("%