blob: d1c7eb0cc020c0273fbaea4e12ccd53ef3431aaa [file]
/* PRU target specific passes
Copyright (C) 2017-2026 Free Software Foundation, Inc.
Dimitar Dimitrov <dimitar@dinux.eu>
This file is part of GCC.
GCC 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, or (at your
option) any later version.
GCC 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; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#define IN_TARGET_CODE 1
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "context.h"
#include "tm.h"
#include "alias.h"
#include "symtab.h"
#include "tree.h"
#include "diagnostic-core.h"
#include "function.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "gimple-walk.h"
#include "gimple-expr.h"
#include "cgraph.h"
#include "tree-pass.h"
#include "pru-protos.h"
namespace {
/* Scan the tree to ensure that the compiled code by GCC
conforms to the TI ABI specification. If GCC cannot
output a conforming code, raise an error. */
const pass_data pass_data_pru_tiabi_check =
{
SIMPLE_IPA_PASS, /* type */
"*pru_tiabi_check", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
/* Implementation class for the TI ABI compliance-check pass. */
class pass_pru_tiabi_check : public simple_ipa_opt_pass
{
public:
pass_pru_tiabi_check (gcc::context *ctxt)
: simple_ipa_opt_pass (pass_data_pru_tiabi_check, ctxt)
{}
/* opt_pass methods: */
virtual unsigned int execute (function *);
virtual bool gate (function *)
{
return pru_current_abi == PRU_ABI_TI;
}
}; // class pass_pru_tiabi_check
/* Issue an error diagnostic if the given TYPE is not compatible with
TI's ABI. */
static void
check_type_tiabi_compatibility (tree type, location_t loc,
hash_set<tree> *visited_nodes)
{
/* Do not visit the same type twice. */
if (visited_nodes->add (type))
return;
if (POINTER_TYPE_P (type))
{
/* TODO: All declarations and statements share the same object for a
function pointer tree type. So if there are several variables
with the same type (function pointer with same function signature),
only the first declaration would get a diagnostic. */
if (FUNC_OR_METHOD_TYPE_P (TREE_TYPE (type)))
{
location_t ptrloc = DECL_P (type) ? DECL_SOURCE_LOCATION (type) : loc;
error_at (ptrloc,
"function pointers not supported with %<-mabi=ti%> option");
}
else
check_type_tiabi_compatibility (TREE_TYPE (type), loc, visited_nodes);
}
else if (RECORD_OR_UNION_TYPE_P (type))
{
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL)
{
if (DECL_BIT_FIELD (field))
error_at (DECL_SOURCE_LOCATION (field),
"bit-fields not supported with %<-mabi=ti%> option");
check_type_tiabi_compatibility (TREE_TYPE (field),
DECL_SOURCE_LOCATION (field),
visited_nodes);
}
}
else if (TREE_CODE (type) == ARRAY_TYPE)
check_type_tiabi_compatibility (TREE_TYPE (type), loc, visited_nodes);
}
/* Check the GCC function declaration FNTYPE for TI ABI compatibility. */
static void
check_function_decl (tree fntype, location_t loc,
hash_set<tree> *visited_nodes)
{
/* Do not visit the same type twice. */
if (visited_nodes->add (fntype))
return;
/* GCC does not check if the RETURN VALUE pointer is NULL,
so do not allow GCC functions with large return values. */
if (!VOID_TYPE_P (TREE_TYPE (fntype))
&& pru_return_in_memory (TREE_TYPE (fntype), fntype))
error_at (loc,
"large return values not supported with %<-mabi=ti%> option");
/* Rest of checks for the returned type. */
check_type_tiabi_compatibility (TREE_TYPE (fntype), loc, visited_nodes);
/* Check this function's arguments. */
for (tree p = TYPE_ARG_TYPES (fntype); p; p = TREE_CHAIN (p))
check_type_tiabi_compatibility (TREE_VALUE (p), loc, visited_nodes);
}
/* Callback for walk_gimple_seq that checks TP tree for TI ABI compliance.
Note that TP would have been marked as visited before calling
this function. But the underlying type of TP may or may not have
been visited before. */
static tree
check_op_callback (tree *tp, int *, void *data)
{
struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
if (RECORD_OR_UNION_TYPE_P (*tp) || TREE_CODE (*tp) == ENUMERAL_TYPE)
{
/* Forward declarations have NULL tree type. Skip them. */
if (TREE_TYPE (*tp) == NULL)
return NULL;
}
/* TODO - why C++ leaves INTEGER_TYPE forward declarations around? */
if (TREE_TYPE (*tp) == NULL)
return NULL;
const tree type = TREE_TYPE (*tp);
/* Direct function calls are allowed, obviously. */
gcall *call = dyn_cast <gcall *> (gsi_stmt (wi->gsi));
if (call
&& tp == gimple_call_fn_ptr (call)
&& gimple_call_fndecl (call))
return NULL;
switch (TREE_CODE (type))
{
case FUNCTION_TYPE:
case METHOD_TYPE:
{
/* Has this function already been inspected? */
if (wi->pset->add (type))
return NULL;
/* Note: Do not enforce a small return value. It is safe to
call any TI ABI function from GCC, since GCC will
never pass NULL. */
/* Check arguments for function pointers. */
for (tree p = TYPE_ARG_TYPES (type); p; p = TREE_CHAIN (p))
check_type_tiabi_compatibility (TREE_VALUE (p),
gimple_location (wi->stmt),
wi->pset);
break;
}
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
{
/* walk_tree would not descend and visit field declarations.
So do it here. */
check_type_tiabi_compatibility (type,
gimple_location (wi->stmt),
wi->pset);
break;
}
default:
break;
}
return NULL;
}
/* Pass implementation. */
unsigned
pass_pru_tiabi_check::execute (function *)
{
struct cgraph_node *node;
hash_set<tree> visited_nodes;
symtab_node *snode;
FOR_EACH_SYMBOL (snode)
{
tree decl = snode->decl;
if (decl)
{
if (FUNC_OR_METHOD_TYPE_P (TREE_TYPE (decl)))
check_function_decl (TREE_TYPE (decl),
DECL_SOURCE_LOCATION (decl),
&visited_nodes);
else
check_type_tiabi_compatibility (TREE_TYPE (decl),
DECL_SOURCE_LOCATION (decl),
&visited_nodes);
}
}
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
function *fn = node->get_fun ();
gimple_stmt_iterator gsi;
basic_block bb;
FOR_EACH_BB_FN (bb, fn)
{
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
struct walk_stmt_info wi;
memset (&wi, 0, sizeof (wi));
wi.info = NULL;
wi.want_locations = true;
wi.pset = &visited_nodes;
/* Check the function body. */
walk_gimple_stmt (&gsi, NULL, check_op_callback, &wi);
}
}
}
return 0;
}
} // anon namespace
simple_ipa_opt_pass *
make_pru_tiabi_check (gcc::context *ctxt)
{
return new pass_pru_tiabi_check (ctxt);
}
namespace {
/* Scan the tree to ensure that the compiled code by GCC
conforms to the non-standard minimal runtime. */
const pass_data pass_data_pru_minrt_check =
{
GIMPLE_PASS, /* type */
"*pru_minrt_check", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
PROP_gimple_any, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
/* Implementation class for the minrt compliance-check pass. */
class pass_pru_minrt_check : public gimple_opt_pass
{
public:
pass_pru_minrt_check (gcc::context *ctxt)
: gimple_opt_pass (pass_data_pru_minrt_check, ctxt)
{}
/* opt_pass methods: */
virtual unsigned int execute (function *);
virtual bool gate (function *)
{
return TARGET_MINRT;
}
}; // class pass_pru_minrt_check
/* Pass implementation. */
unsigned
pass_pru_minrt_check::execute (function *fun)
{
const_tree fntype = TREE_TYPE (fun->decl);
if (id_equal (DECL_NAME (fun->decl), "main"))
{
/* Argument list always ends with VOID_TYPE, so subtract one
to get the number of function arguments. */
const unsigned num_args = list_length (TYPE_ARG_TYPES (fntype)) - 1;
if (num_args != 0)
error_at (DECL_SOURCE_LOCATION (fun->decl), "function %<main%> "
"must have no arguments when using the "
"%<-minrt%> option");
/* The required CFG analysis to detect when a functions would never
return is available only with -O1 and higher. */
if (optimize >= 1 && !TREE_THIS_VOLATILE (fun->decl))
error_at (DECL_SOURCE_LOCATION (fun->decl), "function %<main%> "
"must never return when using the "
"%<-minrt%> option");
}
return 0;
}
} // anon namespace
gimple_opt_pass *
make_pru_minrt_check (gcc::context *ctxt)
{
return new pass_pru_minrt_check (ctxt);
}