/* 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 . */
/* Use extensions of the GNU C Library. */
#define _GNU_SOURCE 1
#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
#include
#include
#include
#include
/* Return true if there exists a `starpu_vector_data_register' call for VAR
before GSI in its basic block. */
static bool
registration_in_bb_p (gimple_stmt_iterator gsi, tree var)
{
gcc_assert (SSA_VAR_P (var));
tree register_fn_name;
register_fn_name = get_identifier ("starpu_vector_data_register");
local_define (bool, registration_function_p, (const_tree obj))
{
/* TODO: Compare against the real fndecl. */
return (obj != NULL_TREE
&& TREE_CODE (obj) == FUNCTION_DECL
&& DECL_NAME (obj) == register_fn_name);
};
bool found;
for (found = false;
!gsi_end_p (gsi) && !found;
gsi_prev (&gsi))
{
gimple stmt;
stmt = gsi_stmt (gsi);
if (is_gimple_call (stmt))
{
tree fn = gimple_call_fndecl (stmt);
if (registration_function_p (fn))
{
tree arg = gimple_call_arg (stmt, 2);
if (is_gimple_address (arg))
arg = TREE_OPERAND (arg, 0);
if (((TREE_CODE (arg) == VAR_DECL
|| TREE_CODE (arg) == VAR_DECL)
&& refs_may_alias_p (arg, var))
/* Both VAR and ARG should be SSA names, otherwise, if ARG
is a VAR_DECL, `ptr_derefs_may_alias_p' will
conservatively assume that they may alias. */
|| (TREE_CODE (var) == SSA_NAME
&& TREE_CODE (arg) != VAR_DECL
&& ptr_derefs_may_alias_p (arg, var)))
{
if (verbose_output_p)
{
var = TREE_CODE (var) == SSA_NAME ? SSA_NAME_VAR (var) : var;
inform (gimple_location (stmt),
"found registration of variable %qE",
DECL_NAME (var));
}
found = true;
}
}
}
}
return found;
}
/* Return true if BB is dominated by a registration of VAR. */
static bool
dominated_by_registration (gimple_stmt_iterator gsi, tree var)
{
/* Is there a registration call for VAR in GSI's basic block? */
if (registration_in_bb_p (gsi, var))
return true;
edge e;
edge_iterator ei;
bool found = false;
/* If every incoming edge is dominated by a registration, then we're
fine.
FIXME: This triggers false positives when registration is done in a
loop, because there's always an edge through which no registration
happens--the edge corresponding to the case where the loop is not
entered. */
FOR_EACH_EDGE (e, ei, gsi_bb (gsi)->preds)
{
if (!dominated_by_registration (gsi_last_bb (e->src), var))
return false;
else
found = true;
}
return found;
}
/* Return true if NAME aliases a global variable or a PARM_DECL. Note that,
for the former, `ptr_deref_may_alias_global_p' is way too conservative,
hence this approach. */
static bool
ssa_name_aliases_global_or_parm_p (const_tree name)
{
gcc_assert (TREE_CODE (name) == SSA_NAME);
if (TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL)
return true;
else
{
gimple def_stmt;
def_stmt = SSA_NAME_DEF_STMT (name);
if (is_gimple_assign (def_stmt))
{
tree rhs = gimple_assign_rhs1 (def_stmt);
if (TREE_CODE (rhs) == VAR_DECL
&& (DECL_EXTERNAL (rhs) || TREE_STATIC (rhs)))
return true;
}
}
return false;
}
/* Validate the arguments passed to tasks in FN's body. */
static void
validate_task_invocations (tree fn)
{
gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
const struct cgraph_node *cgraph;
const struct cgraph_edge *callee;
cgraph = cgraph_get_node (fn);
/* When a definition of IMPL is available, check its callees. */
if (cgraph != NULL)
for (callee = cgraph->callees;
callee != NULL;
callee = callee->next_callee)
{
if (task_p (callee->callee->decl))
{
unsigned i;
gimple call_stmt = callee->call_stmt;
for (i = 0; i < gimple_call_num_args (call_stmt); i++)
{
tree arg = gimple_call_arg (call_stmt, i);
if (TREE_CODE (arg) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (arg, 0)) == VAR_DECL
&& (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 0)))
== ARRAY_TYPE))
/* This is a "pointer-to-array" of a variable, so what we
really care about is the variable itself. */
arg = TREE_OPERAND (arg, 0);
if ((POINTER_TYPE_P (TREE_TYPE (arg))
|| (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE))
&& ((TREE_CODE (arg) == VAR_DECL
&& !TREE_STATIC (arg)
&& !DECL_EXTERNAL (arg)
&& !TREE_NO_WARNING (arg))
|| (TREE_CODE (arg) == SSA_NAME
&& !ssa_name_aliases_global_or_parm_p (arg))))
{
if (!dominated_by_registration (gsi_for_stmt (call_stmt),
arg))
{
if (TREE_CODE (arg) == SSA_NAME)
{
tree var = SSA_NAME_VAR (arg);
if (DECL_NAME (var) != NULL)
arg = var;
/* TODO: Check whether we can get the original
variable name via ARG's DEF_STMT. */
}
if (TREE_CODE (arg) == VAR_DECL
&& DECL_NAME (arg) != NULL_TREE)
warning_at (gimple_location (call_stmt), 0,
"variable %qE may be used unregistered",
DECL_NAME (arg));
else
warning_at (gimple_location (call_stmt), 0,
"argument %i may be used unregistered",
i);
}
}
}
}
}
}
/* A pass to warn about possibly unregistered task arguments. */
static unsigned int
warn_starpu_unregistered (void)
{
tree fndecl;
fndecl = current_function_decl;
gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL);
if (!task_p (fndecl))
validate_task_invocations (fndecl);
return 0;
}
struct opt_pass pass_warn_starpu_unregistered =
{
designated_field_init (type, GIMPLE_PASS),
designated_field_init (name, "warn_starpu_unregistered"),
designated_field_init (gate, NULL),
designated_field_init (execute, warn_starpu_unregistered),
/* The rest is zeroed. */
};