Newer
Older
/* A subroutine of gimplify_type_sizes to make sure that *EXPR_P,
a size or position, has had all of its SAVE_EXPRs evaluated.
We add any required statements to *STMT_P. */
void
gimplify_one_sizepos (tree *expr_p, gimple_seq *stmt_p)
{
tree expr = *expr_p;
Richard Henderson
committed
/* We don't do anything if the value isn't there, is constant, or contains
A PLACEHOLDER_EXPR. We also don't want to do anything if it's already
a VAR_DECL. If it's a VAR_DECL from another function, the gimplifier
will want to replace it with a new variable, but that will cause problems
if this type is from outside the function. It's OK to have that here. */
if (is_gimple_sizepos (expr))
return;
Richard Henderson
committed
*expr_p = unshare_expr (expr);
gimplify_expr (expr_p, stmt_p, NULL, is_gimple_val, fb_rvalue);
}
/* Gimplify the body of statements of FNDECL and return a GIMPLE_BIND node
containing the sequence of corresponding GIMPLE statements. If DO_PARMS
is true, also gimplify the parameters. */
gimplify_body (tree fndecl, bool do_parms)
{
location_t saved_location = input_location;
gimple_seq parm_stmts, seq;
gimple outer_stmt;
gbind *outer_bind;
struct cgraph_node *cgn;
timevar_push (TV_TREE_GIMPLIFY);
Steven Bosscher
committed
/* Initialize for optimize_insn_for_s{ize,peed}_p possibly called during
gimplification. */
default_rtl_profile ();
Andrew MacLeod
committed
push_gimplify_context ();
if (flag_openacc || flag_openmp)
{
gcc_assert (gimplify_omp_ctxp == NULL);
if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl)))
gimplify_omp_ctxp = new_omp_context (ORT_TARGET);
}
/* Unshare most shared trees in the body and in that of any nested functions.
It would seem we don't have to do this for nested functions because
they are supposed to be output and then the outer function gimplified
first, but the g++ front end doesn't always do it that way. */
unshare_body (fndecl);
unvisit_body (fndecl);
if (cgn && cgn->origin)
nonlocal_vlas = new hash_set<tree>;
/* Make sure input_location isn't set to something weird. */
input_location = DECL_SOURCE_LOCATION (fndecl);
Richard Henderson
committed
/* Resolve callee-copies. This has to be done before processing
the body so that DECL_VALUE_EXPR gets processed correctly. */
parm_stmts = do_parms ? gimplify_parameters () : NULL;
Richard Henderson
committed
/* Gimplify the function's body. */
seq = NULL;
gimplify_stmt (&DECL_SAVED_TREE (fndecl), &seq);
outer_stmt = gimple_seq_first_stmt (seq);
if (!outer_stmt)
outer_stmt = gimple_build_nop ();
gimplify_seq_add_stmt (&seq, outer_stmt);
/* The body must contain exactly one statement, a GIMPLE_BIND. If this is
not the case, wrap everything in a GIMPLE_BIND to make it so. */
if (gimple_code (outer_stmt) == GIMPLE_BIND
&& gimple_seq_first (seq) == gimple_seq_last (seq))
outer_bind = as_a <gbind *> (outer_stmt);
else
outer_bind = gimple_build_bind (NULL_TREE, seq, NULL);
DECL_SAVED_TREE (fndecl) = NULL_TREE;
Richard Henderson
committed
/* If we had callee-copies statements, insert them at the beginning
Richard Guenther
committed
of the function and clear DECL_VALUE_EXPR_P on the parameters. */
if (!gimple_seq_empty_p (parm_stmts))
Richard Henderson
committed
{
Richard Guenther
committed
tree parm;
gimplify_seq_add_seq (&parm_stmts, gimple_bind_body (outer_bind));
gimple_bind_set_body (outer_bind, parm_stmts);
Richard Guenther
committed
for (parm = DECL_ARGUMENTS (current_function_decl);
Richard Guenther
committed
if (DECL_HAS_VALUE_EXPR_P (parm))
{
DECL_HAS_VALUE_EXPR_P (parm) = 0;
DECL_IGNORED_P (parm) = 0;
}
Richard Henderson
committed
}
if (nonlocal_vlas)
{
if (nonlocal_vla_vars)
{
/* tree-nested.c may later on call declare_vars (..., true);
which relies on BLOCK_VARS chain to be the tail of the
gimple_bind_vars chain. Ensure we don't violate that
assumption. */
if (gimple_bind_block (outer_bind)
== DECL_INITIAL (current_function_decl))
declare_vars (nonlocal_vla_vars, outer_bind, true);
else
BLOCK_VARS (DECL_INITIAL (current_function_decl))
= chainon (BLOCK_VARS (DECL_INITIAL (current_function_decl)),
nonlocal_vla_vars);
nonlocal_vla_vars = NULL_TREE;
}
nonlocal_vlas = NULL;
}
if ((flag_openacc || flag_openmp || flag_openmp_simd)
&& gimplify_omp_ctxp)
{
delete_omp_context (gimplify_omp_ctxp);
gimplify_omp_ctxp = NULL;
}
pop_gimplify_context (outer_bind);
#ifdef ENABLE_CHECKING
verify_gimple_in_seq (gimple_bind_body (outer_bind));
#endif
timevar_pop (TV_TREE_GIMPLIFY);
input_location = saved_location;
return outer_bind;
Joseph Myers
committed
typedef char *char_p; /* For DEF_VEC_P. */
/* Return whether we should exclude FNDECL from instrumentation. */
static bool
flag_instrument_functions_exclude_p (tree fndecl)
{
Diego Novillo
committed
vec<char_p> *v;
Joseph Myers
committed
Diego Novillo
committed
v = (vec<char_p> *) flag_instrument_functions_exclude_functions;
if (v && v->length () > 0)
Joseph Myers
committed
{
const char *name;
int i;
char *s;
name = lang_hooks.decl_printable_name (fndecl, 0);
Diego Novillo
committed
FOR_EACH_VEC_ELT (*v, i, s)
Joseph Myers
committed
if (strstr (name, s) != NULL)
return true;
}
Diego Novillo
committed
v = (vec<char_p> *) flag_instrument_functions_exclude_files;
if (v && v->length () > 0)
Joseph Myers
committed
{
const char *name;
int i;
char *s;
name = DECL_SOURCE_FILE (fndecl);
Diego Novillo
committed
FOR_EACH_VEC_ELT (*v, i, s)
Joseph Myers
committed
if (strstr (name, s) != NULL)
return true;
}
return false;
}
/* Entry point to the gimplification pass. FNDECL is the FUNCTION_DECL
node for the function we want to gimplify.
Return the sequence of GIMPLE statements corresponding to the body
of FNDECL. */
void
gimplify_function_tree (tree fndecl)
{
tree parm, ret;
gimple_seq seq;
gcc_assert (!gimple_body (fndecl));
if (DECL_STRUCT_FUNCTION (fndecl))
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
else
push_struct_function (fndecl);
/* Tentatively set PROP_gimple_lva here, and reset it in gimplify_va_arg_expr
if necessary. */
cfun->curr_properties |= PROP_gimple_lva;
for (parm = DECL_ARGUMENTS (fndecl); parm ; parm = DECL_CHAIN (parm))
Richard Henderson
committed
{
/* Preliminarily mark non-addressed complex variables as eligible
for promotion to gimple registers. We'll transform their uses
as we find them. */
Andrew Pinski
committed
if ((TREE_CODE (TREE_TYPE (parm)) == COMPLEX_TYPE
|| TREE_CODE (TREE_TYPE (parm)) == VECTOR_TYPE)
Richard Henderson
committed
&& !TREE_THIS_VOLATILE (parm)
&& !needs_to_live_in_memory (parm))
Andrew Pinski
committed
DECL_GIMPLE_REG_P (parm) = 1;
Richard Henderson
committed
}
ret = DECL_RESULT (fndecl);
Andrew Pinski
committed
if ((TREE_CODE (TREE_TYPE (ret)) == COMPLEX_TYPE
|| TREE_CODE (TREE_TYPE (ret)) == VECTOR_TYPE)
Richard Henderson
committed
&& !needs_to_live_in_memory (ret))
Andrew Pinski
committed
DECL_GIMPLE_REG_P (ret) = 1;
Richard Henderson
committed
bind = gimplify_body (fndecl, true);
/* The tree body of the function is no longer needed, replace it
with the new GIMPLE body. */
gimple_seq_add_stmt (&seq, bind);
gimple_set_body (fndecl, seq);
/* If we're instrumenting function entry/exit, then prepend the call to
the entry hook and wrap the whole function in a TRY_FINALLY_EXPR to
catch the exit hook. */
/* ??? Add some way to ignore exceptions for this TFE. */
if (flag_instrument_function_entry_exit
&& !DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl)
&& !flag_instrument_functions_exclude_p (fndecl))
tree x;
gimple tf;
gimple_seq cleanup = NULL, body = NULL;
Michael Meissner
committed
x = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS);
Paul Wögerer
committed
call = gimple_build_call (x, 1, integer_zero_node);
tmp_var = create_tmp_var (ptr_type_node, "return_addr");
gimple_call_set_lhs (call, tmp_var);
gimplify_seq_add_stmt (&cleanup, call);
Michael Meissner
committed
x = builtin_decl_implicit (BUILT_IN_PROFILE_FUNC_EXIT);
call = gimple_build_call (x, 2,
build_fold_addr_expr (current_function_decl),
tmp_var);
gimplify_seq_add_stmt (&cleanup, call);
tf = gimple_build_try (seq, cleanup, GIMPLE_TRY_FINALLY);
Michael Meissner
committed
x = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS);
Paul Wögerer
committed
call = gimple_build_call (x, 1, integer_zero_node);
tmp_var = create_tmp_var (ptr_type_node, "return_addr");
gimple_call_set_lhs (call, tmp_var);
gimplify_seq_add_stmt (&body, call);
Michael Meissner
committed
x = builtin_decl_implicit (BUILT_IN_PROFILE_FUNC_ENTER);
call = gimple_build_call (x, 2,
build_fold_addr_expr (current_function_decl),
tmp_var);
gimplify_seq_add_stmt (&body, call);
gimplify_seq_add_stmt (&body, tf);
new_bind = gimple_build_bind (NULL, body, gimple_bind_block (bind));
/* Clear the block for BIND, since it is no longer directly inside
the function, but within a try block. */
gimple_bind_set_block (bind, NULL);
/* Replace the current function body with the body
wrapped in the try/finally TF. */
gimple_seq_add_stmt (&seq, new_bind);
gimple_set_body (fndecl, seq);
bind = new_bind;
}
Bernd Edlinger
committed
if ((flag_sanitize & SANITIZE_THREAD) != 0
&& !lookup_attribute ("no_sanitize_thread", DECL_ATTRIBUTES (fndecl)))
{
gcall *call = gimple_build_call_internal (IFN_TSAN_FUNC_EXIT, 0);
gimple tf = gimple_build_try (seq, call, GIMPLE_TRY_FINALLY);
gbind *new_bind = gimple_build_bind (NULL, tf, gimple_bind_block (bind));
/* Clear the block for BIND, since it is no longer directly inside
the function, but within a try block. */
gimple_bind_set_block (bind, NULL);
/* Replace the current function body with the body
wrapped in the try/finally TF. */
seq = NULL;
gimple_seq_add_stmt (&seq, new_bind);
gimple_set_body (fndecl, seq);
DECL_SAVED_TREE (fndecl) = NULL_TREE;
cfun->curr_properties |= PROP_gimple_any;
/* Return a dummy expression of type TYPE in order to keep going after an
error. */
static tree
dummy_object (tree type)
tree t = build_int_cst (build_pointer_type (type), 0);
return build2 (MEM_REF, type, t, t);
9322
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342
9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
/* Call the target expander for evaluating a va_arg call of VALIST
and TYPE. */
tree
gimplify_va_arg_internal (tree valist, tree type, location_t loc,
gimple_seq *pre_p, gimple_seq *post_p)
{
tree have_va_type = TREE_TYPE (valist);
tree cano_type = targetm.canonical_va_list_type (have_va_type);
if (cano_type != NULL_TREE)
have_va_type = cano_type;
/* Make it easier for the backends by protecting the valist argument
from multiple evaluations. */
if (TREE_CODE (have_va_type) == ARRAY_TYPE)
{
/* For this case, the backends will be expecting a pointer to
TREE_TYPE (abi), but it's possible we've
actually been given an array (an actual TARGET_FN_ABI_VA_LIST).
So fix it. */
if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
{
tree p1 = build_pointer_type (TREE_TYPE (have_va_type));
valist = fold_convert_loc (loc, p1,
build_fold_addr_expr_loc (loc, valist));
}
gimplify_expr (&valist, pre_p, post_p, is_gimple_val, fb_rvalue);
}
else
gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
return targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p);
}
/* Gimplify __builtin_va_arg, aka VA_ARG_EXPR, which is not really a
builtin function, but a very special sort of operator. */
enum gimplify_status
gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p,
gimple_seq *post_p ATTRIBUTE_UNUSED)
{
tree promoted_type, have_va_type;
tree valist = TREE_OPERAND (*expr_p, 0);
tree type = TREE_TYPE (*expr_p);
location_t loc = EXPR_LOCATION (*expr_p);
/* Verify that valist is of the proper type. */
have_va_type = TREE_TYPE (valist);
if (have_va_type == error_mark_node)
return GS_ERROR;
have_va_type = targetm.canonical_va_list_type (have_va_type);
if (have_va_type == NULL_TREE)
{
error_at (loc, "first argument to %<va_arg%> not of type %<va_list%>");
return GS_ERROR;
}
/* Generate a diagnostic for requesting data of a type that cannot
be passed through `...' due to type promotion at the call site. */
if ((promoted_type = lang_hooks.types.type_promotes_to (type))
!= type)
{
static bool gave_help;
bool warned;
/* Unfortunately, this is merely undefined, rather than a constraint
violation, so we cannot make this an error. If this call is never
executed, the program is still strictly conforming. */
warned = warning_at (loc, 0,
"%qT is promoted to %qT when passed through %<...%>",
type, promoted_type);
if (!gave_help && warned)
{
gave_help = true;
inform (loc, "(so you should pass %qT not %qT to %<va_arg%>)",
promoted_type, type);
}
/* We can, however, treat "undefined" any way we please.
Call abort to encourage the user to fix the program. */
if (warned)
inform (loc, "if this code is reached, the program will abort");
/* Before the abort, allow the evaluation of the va_list
expression to exit or longjmp. */
gimplify_and_add (valist, pre_p);
t = build_call_expr_loc (loc,
builtin_decl_implicit (BUILT_IN_TRAP), 0);
gimplify_and_add (t, pre_p);
/* This is dead code, but go ahead and finish so that the
mode of the result comes out right. */
*expr_p = dummy_object (type);
return GS_ALL_DONE;
/* Transform a VA_ARG_EXPR into an VA_ARG internal function. */
ap = build_fold_addr_expr_loc (loc, valist);
tag = build_int_cst (build_pointer_type (type), 0);
*expr_p = build_call_expr_internal_loc (loc, IFN_VA_ARG, type, 2, ap, tag);
/* Clear the tentatively set PROP_gimple_lva, to indicate that IFN_VA_ARG
needs to be expanded. */
cfun->curr_properties &= ~PROP_gimple_lva;
Andrew MacLeod
committed
/* Build a new GIMPLE_ASSIGN tuple and append it to the end of *SEQ_P.
DST/SRC are the destination and source respectively. You can pass
ungimplified trees in DST or SRC, in which case they will be
converted to a gimple operand if necessary.
This function returns the newly created GIMPLE_ASSIGN tuple. */
gimple
gimplify_assign (tree dst, tree src, gimple_seq *seq_p)
{
tree t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src);
gimplify_and_add (t, seq_p);
ggc_free (t);
return gimple_seq_last_stmt (*seq_p);
}
9450
9451
9452
9453
9454
9455
9456
9457
9458
9459
9460
9461
9462
9463
9464
9465
9466
9467
9468
9469
9470
9471
9472
9473
9474
9475
9476
9477
9478
inline hashval_t
gimplify_hasher::hash (const value_type *p)
{
tree t = p->val;
return iterative_hash_expr (t, 0);
}
inline bool
gimplify_hasher::equal (const value_type *p1, const compare_type *p2)
{
tree t1 = p1->val;
tree t2 = p2->val;
enum tree_code code = TREE_CODE (t1);
if (TREE_CODE (t2) != code
|| TREE_TYPE (t1) != TREE_TYPE (t2))
return false;
if (!operand_equal_p (t1, t2, 0))
return false;
#ifdef ENABLE_CHECKING
/* Only allow them to compare equal if they also hash equal; otherwise
results are nondeterminate, and we fail bootstrap comparison. */
gcc_assert (hash (p1) == hash (p2));
#endif
return true;
}