/* GCC-StarPU
Copyright (C) 2012 Inria
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 . */
#include
/* We must include starpu.h here, otherwise gcc will complain about a poisoned
malloc in xmmintrin.h. */
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_C_FAMILY_C_COMMON_H
# include
#elif HAVE_C_COMMON_H
# include
#endif
#include
#include
#include
#include
/* Task-related functions. */
/* Name of public attributes. */
const char task_attribute_name[] = "task";
const char task_implementation_attribute_name[] = "task_implementation";
const char output_attribute_name[] = "output";
/* Names of attributes used internally. */
static const char task_codelet_attribute_name[] = ".codelet";
const char task_implementation_list_attribute_name[] =
".task_implementation_list";
const char task_implementation_wrapper_attribute_name[] =
".task_implementation_wrapper";
/* Names of data structures defined in . */
static const char codelet_struct_tag[] = "starpu_codelet";
/* Return true if DECL is a task. */
bool
task_p (const_tree decl)
{
return (TREE_CODE (decl) == FUNCTION_DECL &&
lookup_attribute (task_attribute_name,
DECL_ATTRIBUTES (decl)) != NULL_TREE);
}
/* Return true if DECL is a task implementation. */
bool
task_implementation_p (const_tree decl)
{
return (TREE_CODE (decl) == FUNCTION_DECL &&
lookup_attribute (task_implementation_attribute_name,
DECL_ATTRIBUTES (decl)) != NULL_TREE);
}
/* Return a value indicating where TASK_IMPL should execute (`STARPU_CPU',
`STARPU_CUDA', etc.). */
int
task_implementation_where (const_tree task_impl)
{
tree impl_attr, args, where;
gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
impl_attr = lookup_attribute (task_implementation_attribute_name,
DECL_ATTRIBUTES (task_impl));
gcc_assert (impl_attr != NULL_TREE);
args = TREE_VALUE (impl_attr);
where = TREE_VALUE (args);
return task_implementation_target_to_int (where);
}
/* Return the StarPU integer constant corresponding to string TARGET. */
int
task_implementation_target_to_int (const_tree target)
{
gcc_assert (TREE_CODE (target) == STRING_CST);
int where_int;
if (!strncmp (TREE_STRING_POINTER (target), "cpu",
TREE_STRING_LENGTH (target)))
where_int = STARPU_CPU;
else if (!strncmp (TREE_STRING_POINTER (target), "opencl",
TREE_STRING_LENGTH (target)))
where_int = STARPU_OPENCL;
else if (!strncmp (TREE_STRING_POINTER (target), "cuda",
TREE_STRING_LENGTH (target)))
where_int = STARPU_CUDA;
else
where_int = 0;
return where_int;
}
/* Return the task implemented by TASK_IMPL. */
tree
task_implementation_task (const_tree task_impl)
{
tree impl_attr, args, task;
gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
impl_attr = lookup_attribute (task_implementation_attribute_name,
DECL_ATTRIBUTES (task_impl));
gcc_assert (impl_attr != NULL_TREE);
args = TREE_VALUE (impl_attr);
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 declaration of the `struct starpu_codelet' variable associated with
TASK_DECL. */
tree
task_codelet_declaration (const_tree task_decl)
{
tree cl_attr;
cl_attr = lookup_attribute (task_codelet_attribute_name,
DECL_ATTRIBUTES (task_decl));
gcc_assert (cl_attr != NULL_TREE);
return TREE_VALUE (cl_attr);
}
/* Return the list of implementations of TASK_DECL. */
tree
task_implementation_list (const_tree task_decl)
{
tree attr;
attr = lookup_attribute (task_implementation_list_attribute_name,
DECL_ATTRIBUTES (task_decl));
return TREE_VALUE (attr);
}
/* Return the list of pointer parameter types of TASK_DECL. */
tree
task_pointer_parameter_types (const_tree task_decl)
{
return filter (pointer_type_p, TYPE_ARG_TYPES (TREE_TYPE (task_decl)));
}
/* Return a bitwise-or of the supported targets of TASK_DECL. */
int
task_where (const_tree task_decl)
{
gcc_assert (task_p (task_decl));
int where;
const_tree impl;
for (impl = task_implementation_list (task_decl), where = 0;
impl != NULL_TREE;
impl = TREE_CHAIN (impl))
where |= task_implementation_where (TREE_VALUE (impl));
return where;
}
/* Return the FUNCTION_DECL of the wrapper generated for TASK_IMPL. */
tree
task_implementation_wrapper (const_tree task_impl)
{
tree attr;
gcc_assert (TREE_CODE (task_impl) == FUNCTION_DECL);
attr = lookup_attribute (task_implementation_wrapper_attribute_name,
DECL_ATTRIBUTES (task_impl));
gcc_assert (attr != NULL_TREE);
return TREE_VALUE (attr);
}
tree
codelet_type (void)
{
/* XXX: Hack to allow the type declaration to be accessible at lower
time. */
static tree type_decl = NULL_TREE;
if (type_decl == NULL_TREE)
/* Lookup the `struct starpu_codelet' struct type. This should succeed since
we push early on. */
type_decl = type_decl_for_struct_tag (codelet_struct_tag);
return TREE_TYPE (type_decl);
}
/* Return the access mode for POINTER, a PARM_DECL of a task. */
enum starpu_data_access_mode
access_mode (const_tree type)
{
gcc_assert (POINTER_TYPE_P (type));
/* If TYPE points to a const-qualified type, then mark the data as
read-only; if is has the `output' attribute, then mark it as write-only;
otherwise default to read-write. */
return ((TYPE_QUALS (TREE_TYPE (type)) & TYPE_QUAL_CONST)
? STARPU_R
: (output_type_p (type) ? STARPU_W : STARPU_RW));
}
/* Return true if TYPE is `output'-qualified. */
bool
output_type_p (const_tree type)
{
return (lookup_attribute (output_attribute_name,
TYPE_ATTRIBUTES (type)) != NULL_TREE);
}
/* Code generation. */
/* Turn FN into a task, and push its associated codelet declaration. */
void
taskify_function (tree fn)
{
gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
/* Add a `task' attribute and an empty `task_implementation_list'
attribute. */
DECL_ATTRIBUTES (fn) =
tree_cons (get_identifier (task_implementation_list_attribute_name),
NULL_TREE,
tree_cons (get_identifier (task_attribute_name), NULL_TREE,
DECL_ATTRIBUTES (fn)));
/* 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);
}
/* Return a NODE_IDENTIFIER for the variable holding the `struct starpu_codelet'
structure associated with TASK_DECL. */
tree
build_codelet_identifier (tree task_decl)
{
static const char suffix[] = ".codelet";
tree id;
char *cl_name;
const char *task_name;
id = DECL_NAME (task_decl);
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);
}
/* Return a VAR_DECL that declares a `struct starpu_codelet' structure for
TASK_DECL. */
tree
build_codelet_declaration (tree task_decl)
{
tree name, cl_decl;
name = build_codelet_identifier (task_decl);
cl_decl = build_decl (DECL_SOURCE_LOCATION (task_decl),
VAR_DECL, name,
/* c_build_qualified_type (type, TYPE_QUAL_CONST) */
codelet_type ());
DECL_ARTIFICIAL (cl_decl) = true;
TREE_PUBLIC (cl_decl) = TREE_PUBLIC (task_decl);
TREE_STATIC (cl_decl) = false;
TREE_USED (cl_decl) = true;
DECL_EXTERNAL (cl_decl) = true;
DECL_CONTEXT (cl_decl) = NULL_TREE;
return cl_decl;
}
/* Return a `struct starpu_codelet' initializer for TASK_DECL. */
tree
build_codelet_initializer (tree task_decl)
{
tree fields;
fields = TYPE_FIELDS (codelet_type ());
gcc_assert (TREE_CODE (fields) == FIELD_DECL);
local_define (tree, lookup_field, (const char *name))
{
tree fdecl, fname;
fname = get_identifier (name);
for (fdecl = fields;
fdecl != NULL_TREE;
fdecl = TREE_CHAIN (fdecl))
{
if (DECL_NAME (fdecl) == fname)
return fdecl;
}
/* Field NAME wasn't found. */
gcc_assert (false);
};
local_define (tree, field_initializer, (const char *name, tree value))
{
tree field, init;
field = lookup_field (name);
init = make_node (TREE_LIST);
TREE_PURPOSE (init) = field;
TREE_CHAIN (init) = NULL_TREE;
if (TREE_CODE (TREE_TYPE (value)) != ARRAY_TYPE)
TREE_VALUE (init) = fold_convert (TREE_TYPE (field), value);
else
TREE_VALUE (init) = value;
return init;
};
local_define (tree, codelet_name, ())
{
const char *name = IDENTIFIER_POINTER (DECL_NAME (task_decl));
return build_string_literal (strlen (name) + 1, name);
};
local_define (tree, where_init, (tree impls))
{
tree impl;
int where_int = 0;
for (impl = impls;
impl != NULL_TREE;
impl = TREE_CHAIN (impl))
{
tree impl_decl;
impl_decl = TREE_VALUE (impl);
gcc_assert (TREE_CODE (impl_decl) == FUNCTION_DECL);
if (verbose_output_p)
/* List the implementations of TASK_DECL. */
inform (DECL_SOURCE_LOCATION (impl_decl),
" %qE", DECL_NAME (impl_decl));
where_int |= task_implementation_where (impl_decl);
}
return build_int_cstu (integer_type_node, where_int);
};
local_define (tree, implementation_pointers, (tree impls, int where))
{
size_t len;
tree impl, pointers;
for (impl = impls, pointers = NULL_TREE, len = 0;
impl != NULL_TREE;
impl = TREE_CHAIN (impl))
{
tree impl_decl;
impl_decl = TREE_VALUE (impl);
if (task_implementation_where (impl_decl) == where)
{
/* Return a pointer to the wrapper of IMPL_DECL. */
tree addr = build_addr (task_implementation_wrapper (impl_decl),
NULL_TREE);
pointers = tree_cons (size_int (len), addr, pointers);
len++;
if (len > STARPU_MAXIMPLEMENTATIONS)
error_at (DECL_SOURCE_LOCATION (impl_decl),
"maximum number of per-target task implementations "
"exceeded");
}
}
/* POINTERS must be null-terminated. */
pointers = tree_cons (size_int (len), build_zero_cst (ptr_type_node),
pointers);
len++;
/* Return an array initializer. */
tree index_type = build_index_type (size_int (list_length (pointers)));
return build_constructor_from_list (build_array_type (ptr_type_node,
index_type),
nreverse (pointers));
};
local_define (tree, pointer_arg_count, (void))
{
size_t len;
len = list_length (task_pointer_parameter_types (task_decl));
return build_int_cstu (integer_type_node, len);
};
local_define (tree, access_mode_array, (void))
{
const_tree type;
tree modes;
size_t index;
for (type = task_pointer_parameter_types (task_decl),
modes = NULL_TREE, index = 0;
type != NULL_TREE && index < STARPU_NMAXBUFS;
type = TREE_CHAIN (type), index++)
{
tree value = build_int_cst (integer_type_node,
access_mode (TREE_VALUE (type)));
modes = tree_cons (size_int (index), value, modes);
}
tree index_type = build_index_type (size_int (list_length (modes)));
return build_constructor_from_list (build_array_type (integer_type_node,
index_type),
nreverse (modes));
};
if (verbose_output_p)
inform (DECL_SOURCE_LOCATION (task_decl),
"implementations for task %qE:", DECL_NAME (task_decl));
tree impls, inits;
impls = task_implementation_list (task_decl);
inits =
chain_trees (field_initializer ("name", codelet_name ()),
field_initializer ("where", where_init (impls)),
field_initializer ("nbuffers", pointer_arg_count ()),
field_initializer ("modes", access_mode_array ()),
field_initializer ("cpu_funcs",
implementation_pointers (impls,
STARPU_CPU)),
field_initializer ("opencl_funcs",
implementation_pointers (impls,
STARPU_OPENCL)),
field_initializer ("cuda_funcs",
implementation_pointers (impls,
STARPU_CUDA)),
NULL_TREE);
return build_constructor_from_unsorted_list (codelet_type (), inits);
}
/* Return the VAR_DECL that defines a `struct starpu_codelet' structure for
TASK_DECL. The VAR_DECL is assumed to already exists, so it must not be
pushed again. */
tree
declare_codelet (tree task_decl)
{
/* Retrieve the declaration of the `struct starpu_codelet' object. */
tree cl_decl;
cl_decl = lookup_name (build_codelet_identifier (task_decl));
gcc_assert (cl_decl != NULL_TREE && TREE_CODE (cl_decl) == VAR_DECL);
/* Turn the codelet declaration into a definition. */
TREE_TYPE (cl_decl) = codelet_type ();
TREE_PUBLIC (cl_decl) = TREE_PUBLIC (task_decl);
return cl_decl;
}
/* Build the body of TASK_DECL, which will call `starpu_task_insert'. */
void
define_task (tree task_decl)
{
/* First of all, give TASK_DECL an argument list. */
DECL_ARGUMENTS (task_decl) = build_function_arguments (task_decl);
VEC(tree, gc) *args = NULL;
location_t loc = DECL_SOURCE_LOCATION (task_decl);
tree p, params = DECL_ARGUMENTS (task_decl);
/* The first argument will be a pointer to the codelet. */
VEC_safe_push (tree, gc, args,
build_addr (task_codelet_declaration (task_decl),
current_function_decl));
for (p = params; p != NULL_TREE; p = TREE_CHAIN (p))
{
gcc_assert (TREE_CODE (p) == PARM_DECL);
tree type = TREE_TYPE (p);
if (POINTER_TYPE_P (type))
{
/* A pointer: the arguments will be:
`STARPU_RW, ptr' or similar. */
VEC_safe_push (tree, gc, args,
build_int_cst (integer_type_node,
access_mode (type)));
VEC_safe_push (tree, gc, args, build_pointer_lookup (p));
}
else
{
/* A scalar: the arguments will be:
`STARPU_VALUE, &scalar, sizeof (scalar)'. */
mark_addressable (p);
VEC_safe_push (tree, gc, args,
build_int_cst (integer_type_node, STARPU_VALUE));
VEC_safe_push (tree, gc, args,
build_addr (p, current_function_decl));
VEC_safe_push (tree, gc, args,
size_in_bytes (type));
}
}
/* Push the terminating zero. */
VEC_safe_push (tree, gc, args,
build_int_cst (integer_type_node, 0));
/* Introduce a local variable to hold the error code. */
tree error_var = build_decl (loc, VAR_DECL,
create_tmp_var_name (".task_insert_error"),
integer_type_node);
DECL_CONTEXT (error_var) = task_decl;
DECL_ARTIFICIAL (error_var) = true;
/* Build this:
err = starpu_task_insert (...);
if (err != 0)
{ printf ...; abort (); }
*/
static tree task_insert_fn;
LOOKUP_STARPU_FUNCTION (task_insert_fn, "starpu_task_insert");
tree call = build_call_expr_loc_vec (loc, task_insert_fn, args);
tree assignment = build2 (INIT_EXPR, TREE_TYPE (error_var),
error_var, call);
tree name = DECL_NAME (task_decl);
tree cond = build3 (COND_EXPR, void_type_node,
build2 (NE_EXPR, boolean_type_node,
error_var, integer_zero_node),
build_error_statements (loc, error_var,
build_starpu_error_string,
"failed to insert task `%s'",
IDENTIFIER_POINTER (name)),
NULL_TREE);
tree stmts = NULL;
append_to_statement_list (assignment, &stmts);
append_to_statement_list (cond, &stmts);
tree bind = build3 (BIND_EXPR, void_type_node, error_var, stmts,
NULL_TREE);
/* Put it all together. */
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) =
build_decl (loc, RESULT_DECL, NULL_TREE, void_type_node);
DECL_CONTEXT (DECL_RESULT (task_decl)) = task_decl;
}
/* Add FN to the list of implementations of TASK_DECL. */
void
add_task_implementation (tree task_decl, tree fn, const_tree where)
{
location_t loc;
tree attr, impls;
attr = lookup_attribute (task_implementation_list_attribute_name,
DECL_ATTRIBUTES (task_decl));
gcc_assert (attr != NULL_TREE);
gcc_assert (TREE_CODE (where) == STRING_CST);
loc = DECL_SOURCE_LOCATION (fn);
impls = tree_cons (NULL_TREE, fn, TREE_VALUE (attr));
TREE_VALUE (attr) = impls;
TREE_USED (fn) = true;
/* 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);
else if (task_implementation_target_to_int (where) == STARPU_OPENCL)
{
local_define (void, validate, (tree t))
{
validate_opencl_argument_type (loc, t);
};
for_each (validate, TYPE_ARG_TYPES (TREE_TYPE (fn)));
}
}